diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1a167e4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ +Copyright and related rights for this sample code are waived via CC0. Utilities +and libraries in the sample code that are externally maintained have their own +licenses; we recommend you read them, as their terms may differ from the sample +code. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/README.md b/README.md index 46cc8e6..1caae8c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The most important part of this example is the app.js file, which specifies the **If you want IE6-8 support**, clone this repo, but replace the jQuery file with the latest jQuery 1.x release. The jQuery 2 file used in this project does not work with those browsers. -###Project structure +### Project structure tools/ @@ -29,13 +29,13 @@ www/js/ - app/ - main.js -###How it's set up +### How it's set up The main file of this setup is www/js/app.js. It is loaded from app.html by this line: ```html ``` -App.js is mainly about configuration. The shim configuration specifies jQuery as a dependency for jQuery.alpha and jQUery.beta. Finally, our main code is loaded at the bottom of the file: +app.js is mainly about configuration. The shim configuration specifies jQuery as a dependency for jQuery.alpha and jQuery.beta. Finally, our main code is loaded at the bottom of the file: ```javascript requirejs.config({ @@ -64,11 +64,11 @@ define(["jquery", "jquery.alpha", "jquery.beta"], function($) { }); ``` -###How to see it in action +### How to see it in action Just serve up the www/ folder using any web server you'd like. To get you set up quickly, we've included a node.js static file server in tools/. Start the server by typing `node tools/server.js` from the command line, and then go to [localhost:8888/www/app.html](http://localhost:8888/www/app.html) in your browser. -###How to optimize it using r.js +### How to optimize it using r.js To use the optimizer, you need [node.js](http://nodejs.org) or Java 6 installed. These instructions assume Node is being used. See the [Optimization page](http://requirejs.org/docs/optimization.html) for more information. r.js and a build configuration is included in the tools/ folder. To build, navigate to tools/ and type `node r.js -o build.js`. You will find the built product in the www-build folder. If you serve that directory instead, you can see in the network panel of the web developer tools that the files aren't loaded separately any more. diff --git a/tools/r.js b/tools/r.js index 309ceaf..46f2864 100644 --- a/tools/r.js +++ b/tools/r.js @@ -1,7 +1,6 @@ /** - * @license r.js 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details + * @license r.js 2.3.6 Copyright jQuery Foundation and other contributors. + * Released under MIT license, http://github.com/requirejs/r.js/LICENSE */ /* @@ -20,7 +19,7 @@ var requirejs, require, define, xpcUtil; (function (console, args, readFileFunc) { var fileName, env, fs, vm, path, exec, rhinoContext, dir, nodeRequire, nodeDefine, exists, reqMain, loadedOptimizedLib, existsForNode, Cc, Ci, - version = '2.1.9', + version = '2.3.6', jsSuffixRegExp = /\.js$/, commandOption = '', useLibLoaded = {}, @@ -31,26 +30,49 @@ var requirejs, require, define, xpcUtil; readFile = typeof readFileFunc !== 'undefined' ? readFileFunc : null; function showHelp() { - console.log('See https://github.com/jrburke/r.js for usage.'); + console.log('See https://github.com/requirejs/r.js for usage.'); } - if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || - (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { - env = 'browser'; + if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { + env = 'node'; + + //Get the fs module via Node's require before it + //gets replaced. Used in require/node.js + fs = require('fs'); + vm = require('vm'); + path = require('path'); + //In Node 0.7+ existsSync is on fs. + existsForNode = fs.existsSync || path.existsSync; + + nodeRequire = require; + nodeDefine = define; + reqMain = require.main; + + //Temporarily hide require and define to allow require.js to define + //them. + require = undefined; + define = undefined; readFile = function (path) { return fs.readFileSync(path, 'utf8'); }; - exec = function (string) { - return eval(string); + exec = function (string, name) { + return vm.runInThisContext(this.requirejsVars.require.makeNodeWrapper(string), + name ? fs.realpathSync(name) : ''); }; - exists = function () { - console.log('x.js exists not applicable in browser env'); - return false; + exists = function (fileName) { + return existsForNode(fileName); }; + + fileName = process.argv[2]; + + if (fileName && fileName.indexOf('-') === 0) { + commandOption = fileName.substring(1); + fileName = process.argv[3]; + } } else if (typeof Packages !== 'undefined') { env = 'rhino'; @@ -61,12 +83,23 @@ var requirejs, require, define, xpcUtil; fileName = args[1]; } - //Set up execution context. - rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext(); + //Exec/readFile differs between Rhino and Nashorn. Rhino has an + //importPackage where Nashorn does not, so branch on that. This is a + //coarser check -- detecting readFile existence might also be enough for + //this spot. However, sticking with importPackage to keep it the same + //as other Rhino/Nashorn detection branches. + if (typeof importPackage !== 'undefined') { + rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext(); - exec = function (string, name) { - return rhinoContext.evaluateString(this, string, name, 0, null); - }; + exec = function (string, name) { + return rhinoContext.evaluateString(this, string, name, 0, null); + }; + } else { + exec = function (string, name) { + load({ script: string, name: name}); + }; + readFile = readFully; + } exists = function (fileName) { return (new java.io.File(fileName)).exists(); @@ -81,46 +114,23 @@ var requirejs, require, define, xpcUtil; } }; } - } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { - env = 'node'; - - //Get the fs module via Node's require before it - //gets replaced. Used in require/node.js - fs = require('fs'); - vm = require('vm'); - path = require('path'); - //In Node 0.7+ existsSync is on fs. - existsForNode = fs.existsSync || path.existsSync; - - nodeRequire = require; - nodeDefine = define; - reqMain = require.main; - - //Temporarily hide require and define to allow require.js to define - //them. - require = undefined; - define = undefined; + } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || + (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { + env = 'browser'; readFile = function (path) { return fs.readFileSync(path, 'utf8'); }; - exec = function (string, name) { - return vm.runInThisContext(this.requirejsVars.require.makeNodeWrapper(string), - name ? fs.realpathSync(name) : ''); + exec = function (string) { + return eval(string); }; - exists = function (fileName) { - return existsForNode(fileName); + exists = function () { + console.log('x.js exists not applicable in browser env'); + return false; }; - - fileName = process.argv[2]; - - if (fileName && fileName.indexOf('-') === 0) { - commandOption = fileName.substring(1); - fileName = process.argv[3]; - } } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) { env = 'xpconnect'; @@ -238,9 +248,8 @@ var requirejs, require, define, xpcUtil; } /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details + * @license RequireJS 2.3.6 Copyright jQuery Foundation and other contributors. + * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE */ //Not using strict: uneven strict support in browsers, #392, and causes //problems with requirejs.exec()/transpiler plugins that may not be strict. @@ -248,19 +257,17 @@ var requirejs, require, define, xpcUtil; /*global window, navigator, document, importScripts, setTimeout, opera */ -(function (global) { +(function (global, setTimeout) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.9', - commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, + version = '2.3.6', + commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, currDirRegExp = /^\.\//, op = Object.prototype, ostring = op.toString, hasOwn = op.hasOwnProperty, - ap = Array.prototype, - apsp = ap.splice, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', //PS3 indicates loaded and complete, but need to wait for complete @@ -277,6 +284,11 @@ var requirejs, require, define, xpcUtil; globalDefQueue = [], useInteractive = false; + //Could match something like ')//comment', do not lose the prefix to comment. + function commentReplace(match, singlePrefix) { + return singlePrefix || ''; + } + function isFunction(it) { return ostring.call(it) === '[object Function]'; } @@ -347,7 +359,10 @@ var requirejs, require, define, xpcUtil; if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { - if (deepStringMixin && typeof value !== 'string') { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + if (!target[prop]) { target[prop] = {}; } @@ -377,7 +392,7 @@ var requirejs, require, define, xpcUtil; throw err; } - //Allow getting a global that expressed in + //Allow getting a global that is expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { if (!value) { @@ -399,7 +414,7 @@ var requirejs, require, define, xpcUtil; * @returns {Error} */ function makeError(id, msg, err, requireModules) { - var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + var e = new Error(msg + '\nhttps://requirejs.org/docs/errors.html#' + id); e.requireType = id; e.requireModules = requireModules; if (err) { @@ -416,7 +431,7 @@ var requirejs, require, define, xpcUtil; if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { - //Do not overwrite and existing requirejs instance. + //Do not overwrite an existing requirejs instance. return; } cfg = requirejs; @@ -440,6 +455,7 @@ var requirejs, require, define, xpcUtil; waitSeconds: 7, baseUrl: './', paths: {}, + bundles: {}, pkgs: {}, shim: {}, config: {} @@ -453,6 +469,7 @@ var requirejs, require, define, xpcUtil; defQueue = [], defined = {}, urlFetched = {}, + bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; @@ -467,20 +484,19 @@ var requirejs, require, define, xpcUtil; */ function trimDots(ary) { var i, part; - for (i = 0; ary[i]; i += 1) { + for (i = 0; i < ary.length; i++) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { - if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; @@ -500,54 +516,45 @@ var requirejs, require, define, xpcUtil; * @returns {String} normalized name */ function normalize(name, baseName, applyMap) { - var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, - foundMap, foundI, foundStarMap, starI, - baseParts = baseName && baseName.split('/'), - normalizedBaseParts = baseParts, + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), map = config.map, starMap = map && map['*']; //Adjust any relative paths. - if (name && name.charAt(0) === '.') { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - if (getOwn(config.pkgs, baseName)) { - //If the baseName is a package name, then just treat it as one - //name to concat the name with. - normalizedBaseParts = baseParts = [baseName]; - } else { - //Convert baseName to array, and lop off the last part, - //so that . matches that 'directory' and not name of the baseName's - //module. For instance, baseName of 'one/two/three', maps to - //'one/two/three.js', but we want the directory, 'one/two' for - //this normalization. - normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); - } + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; - name = normalizedBaseParts.concat(name.split('/')); - trimDots(name); + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } - //Some use of packages may use a . path to reference the - //'main' module name, so normalize for that. - pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); - name = name.join('/'); - if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { - name = pkgName; - } - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); } + + trimDots(name); + name = name.join('/'); } //Apply map config if available. if (applyMap && map && (baseParts || starMap)) { nameParts = name.split('/'); - for (i = nameParts.length; i > 0; i -= 1) { + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join('/'); if (baseParts) { @@ -564,16 +571,12 @@ var requirejs, require, define, xpcUtil; //Match, update name to the new value. foundMap = mapValue; foundI = i; - break; + break outerLoop; } } } } - if (foundMap) { - break; - } - //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. @@ -594,7 +597,11 @@ var requirejs, require, define, xpcUtil; } } - return name; + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; } function removeScript(name) { @@ -616,7 +623,13 @@ var requirejs, require, define, xpcUtil; //retry pathConfig.shift(); context.require.undef(id); - context.require([id]); + + //Custom require that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + return true; } } @@ -676,13 +689,24 @@ var requirejs, require, define, xpcUtil; //Account for relative paths if there is a base name. if (name) { if (prefix) { - if (pluginModule && pluginModule.normalize) { + if (isNormalized) { + normalizedName = name; + } else if (pluginModule && pluginModule.normalize) { //Plugin is loaded, use its normalize method. normalizedName = pluginModule.normalize(name, function (name) { return normalize(name, parentName, applyMap); }); } else { - normalizedName = normalize(name, parentName, applyMap); + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; } } else { //A regular module. @@ -783,11 +807,13 @@ var requirejs, require, define, xpcUtil; function takeGlobalQueue() { //Push all the globalDefQueue items into the context's defQueue if (globalDefQueue.length) { - //Array splice in the values since the context code has a - //local var ref to defQueue, so cannot just reassign the one - //on context. - apsp.apply(defQueue, - [defQueue.length - 1, 0].concat(globalDefQueue)); + each(globalDefQueue, function(queueItem) { + var id = queueItem[0]; + if (typeof id === 'string') { + context.defQueueMap[id] = true; + } + defQueue.push(queueItem); + }); globalDefQueue = []; } } @@ -804,7 +830,7 @@ var requirejs, require, define, xpcUtil; mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { - return mod.exports; + return (defined[mod.map.id] = mod.exports); } else { return (mod.exports = defined[mod.map.id] = {}); } @@ -818,15 +844,9 @@ var requirejs, require, define, xpcUtil; id: mod.map.id, uri: mod.map.url, config: function () { - var c, - pkg = getOwn(config.pkgs, mod.map.id); - // For packages, only support config targeted - // at the main module. - c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) : - getOwn(config.config, mod.map.id); - return c || {}; + return getOwn(config.config, mod.map.id) || {}; }, - exports: defined[mod.map.id] + exports: mod.exports || (mod.exports = {}) }); } } @@ -867,7 +887,7 @@ var requirejs, require, define, xpcUtil; } function checkLoaded() { - var map, modId, err, usingPathFallback, + var err, usingPathFallback, waitInterval = config.waitSeconds * 1000, //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), @@ -885,8 +905,8 @@ var requirejs, require, define, xpcUtil; //Figure out the state of all the modules. eachProp(enabledRegistry, function (mod) { - map = mod.map; - modId = map.id; + var map = mod.map, + modId = map.id; //Skip things that are not enabled or in error state. if (!mod.enabled) { @@ -1080,7 +1100,10 @@ var requirejs, require, define, xpcUtil; factory = this.factory; if (!this.inited) { - this.fetch(); + // Only fetch if not already in the defQueue. + if (!hasProp(context.defQueueMap, id)) { + this.fetch(); + } } else if (this.error) { this.emit('error', this.error); } else if (!this.defining) { @@ -1109,17 +1132,14 @@ var requirejs, require, define, xpcUtil; exports = context.execCb(id, factory, depExports, exports); } - if (this.map.isDefine) { - //If setting exports via 'module' is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { cjsModule = this.module; - if (cjsModule && - cjsModule.exports !== undefined && - //Make sure it is not already the exports value - cjsModule.exports !== this.exports) { + if (cjsModule) { exports = cjsModule.exports; - } else if (exports === undefined && this.usingExports) { + } else if (this.usingExports) { //exports already set the defined value. exports = this.exports; } @@ -1143,7 +1163,11 @@ var requirejs, require, define, xpcUtil; defined[id] = exports; if (req.onResourceLoad) { - req.onResourceLoad(context, this.map, this.depMaps); + var resLoadMaps = []; + each(this.depMaps, function (depMap) { + resLoadMaps.push(depMap.normalizedMap || depMap); + }); + req.onResourceLoad(context, this.map, resLoadMaps); } } @@ -1179,6 +1203,7 @@ var requirejs, require, define, xpcUtil; on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, localRequire = context.makeRequire(map.parentMap, { @@ -1198,9 +1223,11 @@ var requirejs, require, define, xpcUtil; //prefix and name should already be normalized, no need //for applying map config again either. normalizedMap = makeModuleMap(map.prefix + '!' + name, - this.map.parentMap); + this.map.parentMap, + true); on(normalizedMap, 'defined', bind(this, function (value) { + this.map.normalizedMap = normalizedMap; this.init([], function () { return value; }, null, { enabled: true, ignore: true @@ -1224,6 +1251,14 @@ var requirejs, require, define, xpcUtil; return; } + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layer. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true @@ -1346,12 +1381,22 @@ var requirejs, require, define, xpcUtil; this.depCount += 1; on(depMap, 'defined', bind(this, function (depExports) { + if (this.undefed) { + return; + } this.defineDep(i, depExports); this.check(); })); if (this.errback) { on(depMap, 'error', bind(this, this.errback)); + } else if (this.events.error) { + // No direct errback on this module, but something + // else is listening for errors, so be sure to + // propagate the error correctly. + on(depMap, 'error', bind(this, function(err) { + this.emit('error', err); + })); } } @@ -1455,13 +1500,15 @@ var requirejs, require, define, xpcUtil; while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { - return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + + args[args.length - 1])); } else { //args are id, deps, factory. Should be normalized by the //define() function. callGetModule(args); } } + context.defQueueMap = {}; } context = { @@ -1471,6 +1518,7 @@ var requirejs, require, define, xpcUtil; defined: defined, urlFetched: urlFetched, defQueue: defQueue, + defQueueMap: {}, Module: Module, makeModuleMap: makeModuleMap, nextTick: req.nextTick, @@ -1488,31 +1536,46 @@ var requirejs, require, define, xpcUtil; } } - //Save off the paths and packages since they require special processing, + // Convert old style urlArgs string to a function. + if (typeof cfg.urlArgs === 'string') { + var urlArgs = cfg.urlArgs; + cfg.urlArgs = function(id, url) { + return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs; + }; + } + + //Save off the paths since they require special processing, //they are additive. - var pkgs = config.pkgs, - shim = config.shim, + var shim = config.shim, objs = { paths: true, + bundles: true, config: true, map: true }; eachProp(cfg, function (value, prop) { if (objs[prop]) { - if (prop === 'map') { - if (!config.map) { - config.map = {}; - } - mixin(config[prop], value, true, true); - } else { - mixin(config[prop], value, true); + if (!config[prop]) { + config[prop] = {}; } + mixin(config[prop], value, true, true); } else { config[prop] = value; } }); + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + //Merge shim if (cfg.shim) { eachProp(cfg.shim, function (value, id) { @@ -1533,29 +1596,25 @@ var requirejs, require, define, xpcUtil; //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { - var location; + var location, name; - pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj; + + name = pkgObj.name; location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } - //Create a brand new object on pkgs, since currentPackages can - //be passed in again, and config.pkgs is the internal transformed - //state for all package configs. - pkgs[pkgObj.name] = { - name: pkgObj.name, - location: location || pkgObj.name, - //Remove leading dot in main, so main paths are normalized, - //and remove any trailing .js, since different package - //envs have different conventions: some use a module name, - //some use a file name. - main: (pkgObj.main || 'main') - .replace(currDirRegExp, '') - .replace(jsSuffixRegExp, '') - }; + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); }); - - //Done with modifications, assing packages back to context config - config.pkgs = pkgs; } //If there are any "waiting to execute" modules in the registry, @@ -1566,7 +1625,7 @@ var requirejs, require, define, xpcUtil; //late to modify them, and ignore unnormalized ones //since they are transient. if (!mod.inited && !mod.map.unnormalized) { - mod.map = makeModuleMap(id); + mod.map = makeModuleMap(id, null, true); } }); @@ -1702,12 +1761,23 @@ var requirejs, require, define, xpcUtil; var map = makeModuleMap(id, relMap, true), mod = getOwn(registry, id); + mod.undefed = true; removeScript(id); delete defined[id]; delete urlFetched[map.url]; delete undefEvents[id]; + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if (args[0] === id) { + defQueue.splice(i, 1); + } + }); + delete context.defQueueMap[id]; + if (mod) { //Hold on to listeners in case the //module will be attempted to be reloaded @@ -1727,7 +1797,7 @@ var requirejs, require, define, xpcUtil; /** * Called to enable a module if it is still in the registry * awaiting enablement. A second arg, parent, the parent module, - * is passed in for context, when this method is overriden by + * is passed in for context, when this method is overridden by * the optimizer. Not shown here to keep code compact. */ enable: function (depMap) { @@ -1768,6 +1838,7 @@ var requirejs, require, define, xpcUtil; callGetModule(args); } + context.defQueueMap = {}; //Do this after the cycle of callGetModule in case the result //of those calls/init calls changes the registry. @@ -1801,8 +1872,19 @@ var requirejs, require, define, xpcUtil; * internal API, not a public one. Use toUrl for the public API. */ nameToUrl: function (moduleName, ext, skipExt) { - var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, - parentPath; + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } //If a colon is in the URL, it indicates a protocol is used and it is just //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) @@ -1816,7 +1898,6 @@ var requirejs, require, define, xpcUtil; } else { //A module that needs to be converted to a path. paths = config.paths; - pkgs = config.pkgs; syms = moduleName.split('/'); //For each module name segment, see if there is a path @@ -1824,7 +1905,7 @@ var requirejs, require, define, xpcUtil; //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); - pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, @@ -1834,28 +1915,17 @@ var requirejs, require, define, xpcUtil; } syms.splice(0, i, parentPath); break; - } else if (pkg) { - //If module name is just the package name, then looking - //for the main module. - if (moduleName === pkg.name) { - pkgPath = pkg.location + '/' + pkg.main; - } else { - pkgPath = pkg.location; - } - syms.splice(0, i, pkgPath); - break; } } //Join the path parts together, then figure out if baseUrl is needed. url = syms.join('/'); - url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); + url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js')); url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; } - return config.urlArgs ? url + - ((url.indexOf('?') === -1 ? '?' : '&') + - config.urlArgs) : url; + return config.urlArgs && !/^blob\:/.test(url) ? + url + config.urlArgs(moduleName, url) : url; }, //Delegates to req.load. Broken out as a separate function to @@ -1903,7 +1973,21 @@ var requirejs, require, define, xpcUtil; onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { - return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); + var parents = []; + eachProp(registry, function(value, key) { + if (key.indexOf('_@r') !== 0) { + each(value.depMaps, function(depMap) { + if (depMap.id === data.id) { + parents.push(key); + return true; + } + }); + } + }); + return onError(makeError('scripterror', 'Script error for "' + data.id + + (parents.length ? + '", needed by: ' + parents.join(', ') : + '"'), evt, [data.id])); } } }; @@ -2077,11 +2161,11 @@ var requirejs, require, define, xpcUtil; if (node.attachEvent && //Check if node.attachEvent is artificially added by custom script or //natively supported by browser - //read https://github.com/jrburke/requirejs/issues/187 + //read https://github.com/requirejs/requirejs/issues/187 //if we can NOT find [native code] then it must NOT natively supported. //in IE8, node.attachEvent does not have toString() //Note the test for "[native code" with no closing brace, see: - //https://github.com/jrburke/requirejs/issues/273 + //https://github.com/requirejs/requirejs/issues/273 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) { //Probably IE. IE (at least 6-8) do not fire @@ -2109,6 +2193,12 @@ var requirejs, require, define, xpcUtil; } node.src = url; + //Calling onNodeCreated after all properties on the node have been + //set, but before it is placed in the DOM. + if (config.onNodeCreated) { + config.onNodeCreated(node, config, moduleName, url); + } + //For some cache cases in IE 6-8, the script executes before the end //of the appendChild execution, so to tie an anonymous define //call to the module name (which is stored on the node), hold on @@ -2127,9 +2217,14 @@ var requirejs, require, define, xpcUtil; //In a web worker, use importScripts. This is not a very //efficient use of importScripts, importScripts will block until //its script is downloaded and evaluated. However, if web workers - //are in play, the expectation that a build has been done so that - //only one script needs to be loaded anyway. This may need to be - //reevaluated if other use cases become common. + //are in play, the expectation is that a build has been done so + //that only one script needs to be loaded anyway. This may need + //to be reevaluated if other use cases become common. + + // Post a task to the event loop to work around a bug in WebKit + // where the worker gets garbage-collected after calling + // importScripts(): https://webkit.org/b/153317 + setTimeout(function() {}, 0); importScripts(url); //Account for anonymous modules @@ -2175,8 +2270,10 @@ var requirejs, require, define, xpcUtil; //Preserve dataMain in case it is a path (i.e. contains '?') mainScript = dataMain; - //Set final baseUrl if there is not already an explicit one. - if (!cfg.baseUrl) { + //Set final baseUrl if there is not already an explicit one, + //but only do so if the data-main value is not a loader plugin + //module ID. + if (!cfg.baseUrl && mainScript.indexOf('!') === -1) { //Pull off the directory of data-main for use as the //baseUrl. src = mainScript.split('/'); @@ -2190,7 +2287,7 @@ var requirejs, require, define, xpcUtil; //like a module name. mainScript = mainScript.replace(jsSuffixRegExp, ''); - //If mainScript is still a path, fall back to dataMain + //If mainScript is still a path, fall back to dataMain if (req.jsExtRegExp.test(mainScript)) { mainScript = dataMain; } @@ -2237,7 +2334,7 @@ var requirejs, require, define, xpcUtil; if (callback.length) { callback .toString() - .replace(commentRegExp, '') + .replace(commentRegExp, commentReplace) .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); @@ -2269,14 +2366,18 @@ var requirejs, require, define, xpcUtil; //where the module name is not known until the script onload event //occurs. If no context, use the global queue, and get it processed //in the onscript load callback. - (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + if (context) { + context.defQueue.push([name, deps, callback]); + context.defQueueMap[name] = true; + } else { + globalDefQueue.push([name, deps, callback]); + } }; define.amd = { jQuery: true }; - /** * Executes the text. Normally just uses eval, but can be modified * to use a better, environment-specific call. Only used for transpiling @@ -2290,7 +2391,7 @@ var requirejs, require, define, xpcUtil; //Set up with config info. req(cfg); -}(this)); +}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout))); @@ -2301,18 +2402,17 @@ var requirejs, require, define, xpcUtil; }; if (env === 'browser') { - /** - * @license RequireJS rhino Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -//sloppy since eval enclosed with use strict causes problems if the source + //sloppy since eval enclosed with use strict causes problems if the source //text is not strict-compliant. /*jslint sloppy: true, evil: true */ /*global require, XMLHttpRequest */ (function () { + // Separate function to avoid eval pollution, same with arguments use. + function exec() { + eval(arguments[0]); + } + require.load = function (context, moduleName, url) { var xhr = new XMLHttpRequest(); @@ -2321,7 +2421,7 @@ var requirejs, require, define, xpcUtil; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { - eval(xhr.responseText); + exec(xhr.responseText); //Support anonymous modules. context.completeLoad(moduleName); @@ -2330,14 +2430,7 @@ var requirejs, require, define, xpcUtil; }; }()); } else if (env === 'rhino') { - /** - * @license RequireJS rhino Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/*jslint */ -/*global require: false, java: false, load: false */ + /*global require: false, java: false, load: false */ (function () { 'use strict'; @@ -2354,13 +2447,10 @@ var requirejs, require, define, xpcUtil; this.requirejsVars.nodeRequire = nodeRequire; require.nodeRequire = nodeRequire; - /** - * @license RequireJS node Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/*jslint regexp: false */ + //Explicity not strict since this file contains an eval call, and do not want +//to enforce strict on code evaluated that way. See +//https://github.com/requirejs/r.js/issues/774 +/*jslint regexp: false, sloppy: true*/ /*global require: false, define: false, requirejsVars: false, process: false */ /** @@ -2371,8 +2461,6 @@ var requirejs, require, define, xpcUtil; */ (function () { - 'use strict'; - var nodeReq = requirejsVars.nodeRequire, req = requirejsVars.require, def = requirejsVars.define, @@ -2437,7 +2525,7 @@ var requirejs, require, define, xpcUtil; //Break any cycles by requiring it normally, but this will //finish synchronously - require([moduleName]); + context.require([moduleName]); //The above calls are sync, so can do the next thing safely. ret = context.defined[moduleName]; @@ -2515,7 +2603,7 @@ var requirejs, require, define, xpcUtil; err.originalError = e; err.moduleName = originalName; err.requireModules = [moduleName]; - return context.onError(err); + throw err; } }); } @@ -2533,13 +2621,7 @@ var requirejs, require, define, xpcUtil; }()); } else if (env === 'xpconnect') { - /** - * @license RequireJS xpconnect Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/*jslint */ + /*jslint */ /*global require, load */ (function () { @@ -2568,13 +2650,7 @@ var requirejs, require, define, xpcUtil; * tasks. */ function loadLib() { - /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/*jslint strict: false */ + /*jslint strict: false */ /*global Packages: false, process: false, window: false, navigator: false, document: false, define: false */ @@ -2586,10 +2662,10 @@ var requirejs, require, define, xpcUtil; var pathRegExp = /(\/|^)env\/|\{env\}/, env = 'unknown'; - if (typeof Packages !== 'undefined') { - env = 'rhino'; - } else if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { + if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { env = 'node'; + } else if (typeof Packages !== 'undefined') { + env = 'rhino'; } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { env = 'browser'; @@ -2621,25 +2697,32 @@ var requirejs, require, define, xpcUtil; }); } }); -}());/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - +}()); /*jslint plusplus: true */ -/*global define */ +/*global define, java */ define('lang', function () { 'use strict'; - var lang, + var lang, isJavaObj, hasOwn = Object.prototype.hasOwnProperty; function hasProp(obj, prop) { return hasOwn.call(obj, prop); } + isJavaObj = function () { + return false; + }; + + //Rhino, but not Nashorn (detected by importPackage not existing) + //Can have some strange foreign objects. + if (typeof java !== 'undefined' && java.lang && java.lang.Object && typeof importPackage !== 'undefined') { + isJavaObj = function (obj) { + return obj instanceof java.lang.Object; + }; + } + lang = { backSlashRegExp: /\\/g, ostring: Object.prototype.toString, @@ -2701,6 +2784,31 @@ define('lang', function () { return dest; // Object }, + /** + * Does a deep mix of source into dest, where source values override + * dest values if a winner is needed. + * @param {Object} dest destination object that receives the mixed + * values. + * @param {Object} source source object contributing properties to mix + * in. + * @return {[Object]} returns dest object with the modification. + */ + deepMix: function(dest, source) { + lang.eachProp(source, function (value, prop) { + if (typeof value === 'object' && value && + !lang.isArray(value) && !lang.isFunction(value) && + !(value instanceof RegExp)) { + + if (!dest[prop]) { + dest[prop] = {}; + } + lang.deepMix(dest[prop], value); + } else { + dest[prop] = value; + } + }); + return dest; + }, /** * Does a type of deep copy. Do not give it anything fancy, best @@ -2708,16 +2816,17 @@ define('lang', function () { * JSON-serialized things, or has properties pointing to functions. * For non-array/object values, just returns the same object. * @param {Object} obj copy properties from this object - * @param {Object} [result] optional result object to use + * @param {Object} [ignoredProps] optional object whose own properties + * are keys that should be ignored. * @return {Object} */ - deeplikeCopy: function (obj) { + deeplikeCopy: function (obj, ignoredProps) { var type, result; if (lang.isArray(obj)) { result = []; obj.forEach(function(value) { - result.push(lang.deeplikeCopy(value)); + result.push(lang.deeplikeCopy(value, ignoredProps)); }); return result; } @@ -2725,14 +2834,16 @@ define('lang', function () { type = typeof obj; if (obj === null || obj === undefined || type === 'boolean' || type === 'string' || type === 'number' || lang.isFunction(obj) || - lang.isRegExp(obj)) { + lang.isRegExp(obj)|| isJavaObj(obj)) { return obj; } //Anything else is an object, hopefully. result = {}; lang.eachProp(obj, function(value, key) { - result[key] = lang.deeplikeCopy(value); + if (!ignoredProps || !hasProp(ignoredProps, key)) { + result[key] = lang.deeplikeCopy(value, ignoredProps); + } }); return result; }, @@ -2804,7 +2915,7 @@ define('lang', function () { return lang; }); /** - * prim 0.0.1 Copyright (c) 2012-2013, The Dojo Foundation All Rights Reserved. + * prim 0.0.1 Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/requirejs/prim for details */ @@ -2845,7 +2956,8 @@ var prim; function check(p) { if (hasProp(p, 'e') || hasProp(p, 'v')) { if (!prim.hideResolutionConflict) { - throw new Error('nope'); + throw new Error('Prim promise already resolved: ' + + JSON.stringify(p)); } return false; } @@ -2998,12 +3110,6 @@ var prim; } }()); if(env === 'browser') { -/** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3015,12 +3121,6 @@ define('browser/assert', function () { } if(env === 'node') { -/** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3032,12 +3132,6 @@ define('node/assert', ['assert'], function (assert) { } if(env === 'rhino') { -/** - * @license RequireJS Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3049,12 +3143,6 @@ define('rhino/assert', function () { } if(env === 'xpconnect') { -/** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3066,12 +3154,6 @@ define('xpconnect/assert', function () { } if(env === 'browser') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, process: false */ @@ -3083,12 +3165,6 @@ define('browser/args', function () { } if(env === 'node') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, process: false */ @@ -3107,12 +3183,6 @@ define('node/args', function () { } if(env === 'rhino') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, process: false */ @@ -3132,12 +3202,6 @@ define('rhino/args', function () { } if(env === 'xpconnect') { -/** - * @license Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define, xpconnectArgs */ @@ -3157,12 +3221,6 @@ define('xpconnect/args', function () { } if(env === 'browser') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, console: false */ @@ -3177,12 +3235,6 @@ define('browser/load', ['./file'], function (file) { } if(env === 'node') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, console: false */ @@ -3198,12 +3250,6 @@ define('node/load', ['fs'], function (fs) { } if(env === 'rhino') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3214,12 +3260,6 @@ define('rhino/load', function () { } if(env === 'xpconnect') { -/** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, load: false */ @@ -3230,12 +3270,6 @@ define('xpconnect/load', function () { } if(env === 'browser') { -/** - * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint sloppy: true, nomen: true */ /*global require, define, console, XMLHttpRequest, requirejs, location */ @@ -3408,12 +3442,6 @@ define('browser/file', ['prim'], function (prim) { } if(env === 'node') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint plusplus: false, octal:false, strict: false */ /*global define: false, process: false */ @@ -3544,7 +3572,11 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { } else if (stat.isDirectory() && (!file.exclusionRegExp || !file.exclusionRegExp.test(fileName))) { dirFiles = this.getFilteredFileList(filePath, regExpFilters, makeUnixPaths); - files.push.apply(files, dirFiles); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); } } } @@ -3672,7 +3704,7 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { //summary: deletes a file or directory if it exists. var files, i, stat; if (file.exists(fileName)) { - stat = fs.statSync(fileName); + stat = fs.lstatSync(fileName); if (stat.isDirectory()) { files = fs.readdirSync(fileName); for (i = 0; i < files.length; i++) { @@ -3697,7 +3729,7 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { for (i = 0; i < dirFileArray.length; i++) { fileName = dirFileArray[i]; filePath = path.join(startDir, fileName); - stat = fs.statSync(filePath); + stat = fs.lstatSync(filePath); if (stat.isDirectory()) { file.deleteEmptyDirs(filePath); } @@ -3718,11 +3750,6 @@ define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { } if(env === 'rhino') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ //Helper functions to deal with file I/O. /*jslint plusplus: false */ @@ -3818,7 +3845,11 @@ define('rhino/file', ['prim'], function (prim) { } else if (fileObj.isDirectory() && (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.getName()))) { dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true); - files.push.apply(files, dirFiles); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); } } } @@ -3956,7 +3987,13 @@ define('rhino/file', ['prim'], function (prim) { os = new java.io.BufferedWriter(outWriter); try { - os.write(fileContents); + //If in Nashorn, need to coerce the JS string to a Java string so that + //writer.write method dispatch correctly detects the type. + if (typeof importPackage !== 'undefined') { + os.write(fileContents); + } else { + os.write(new java.lang.String(fileContents)); + } } finally { os.close(); } @@ -4012,11 +4049,6 @@ define('rhino/file', ['prim'], function (prim) { } if(env === 'xpconnect') { -/** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ //Helper functions to deal with file I/O. /*jslint plusplus: false */ @@ -4124,7 +4156,11 @@ define('xpconnect/file', ['prim'], function (prim) { } else if (fileObj.isDirectory() && (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.leafName))) { dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true); - files.push.apply(files, dirFiles); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); } } } @@ -4332,12 +4368,6 @@ define('xpconnect/quit', function () { } if(env === 'browser') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, console: false */ @@ -4352,12 +4382,6 @@ define('browser/print', function () { } if(env === 'node') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, console: false */ @@ -4372,12 +4396,6 @@ define('node/print', function () { } if(env === 'rhino') { -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, print: false */ @@ -4388,12 +4406,6 @@ define('rhino/print', function () { } if(env === 'xpconnect') { -/** - * @license RequireJS Copyright (c) 2013, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false, print: false */ @@ -4402,12 +4414,6 @@ define('xpconnect/print', function () { }); } -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint nomen: false, strict: false */ /*global define: false */ @@ -4464,15229 +4470,17192 @@ define('logger', ['env!env/print'], function (print) { //so that the build does not attempt to inline some env modules, //like Node's fs and path. -/* - Copyright (C) 2012 Ariya Hidayat - Copyright (C) 2012 Mathias Bynens - Copyright (C) 2012 Joost-Wim Boekesteijn - Copyright (C) 2012 Kris Kowal - Copyright (C) 2012 Yusuke Suzuki - Copyright (C) 2012 Arpad Borsos - Copyright (C) 2011 Ariya Hidayat - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/*jslint bitwise:true plusplus:true */ -/*global esprima:true, define:true, exports:true, window: true, -throwError: true, createLiteral: true, generateStatement: true, -parseAssignmentExpression: true, parseBlock: true, parseExpression: true, -parseFunctionDeclaration: true, parseFunctionExpression: true, -parseFunctionSourceElements: true, parseVariableIdentifier: true, -parseLeftHandSideExpression: true, -parseStatement: true, parseSourceElement: true */ - -(function (root, factory) { - 'use strict'; +(function webpackUniversalModuleDefinition(root, factory) { +/* istanbul ignore next */ + if(typeof define === 'function' && define.amd) + define('esprima', [], factory); + else if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); +/* istanbul ignore next */ + else if(typeof exports === 'object') + exports["esprima"] = factory(); + else + root["esprima"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/* istanbul ignore if */ +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + /* + Copyright JS Foundation and other contributors, https://js.foundation/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + Object.defineProperty(exports, "__esModule", { value: true }); + var comment_handler_1 = __webpack_require__(1); + var jsx_parser_1 = __webpack_require__(3); + var parser_1 = __webpack_require__(8); + var tokenizer_1 = __webpack_require__(15); + function parse(code, options, delegate) { + var commentHandler = null; + var proxyDelegate = function (node, metadata) { + if (delegate) { + delegate(node, metadata); + } + if (commentHandler) { + commentHandler.visit(node, metadata); + } + }; + var parserDelegate = (typeof delegate === 'function') ? proxyDelegate : null; + var collectComment = false; + if (options) { + collectComment = (typeof options.comment === 'boolean' && options.comment); + var attachComment = (typeof options.attachComment === 'boolean' && options.attachComment); + if (collectComment || attachComment) { + commentHandler = new comment_handler_1.CommentHandler(); + commentHandler.attach = attachComment; + options.comment = true; + parserDelegate = proxyDelegate; + } + } + var isModule = false; + if (options && typeof options.sourceType === 'string') { + isModule = (options.sourceType === 'module'); + } + var parser; + if (options && typeof options.jsx === 'boolean' && options.jsx) { + parser = new jsx_parser_1.JSXParser(code, options, parserDelegate); + } + else { + parser = new parser_1.Parser(code, options, parserDelegate); + } + var program = isModule ? parser.parseModule() : parser.parseScript(); + var ast = program; + if (collectComment && commentHandler) { + ast.comments = commentHandler.comments; + } + if (parser.config.tokens) { + ast.tokens = parser.tokens; + } + if (parser.config.tolerant) { + ast.errors = parser.errorHandler.errors; + } + return ast; + } + exports.parse = parse; + function parseModule(code, options, delegate) { + var parsingOptions = options || {}; + parsingOptions.sourceType = 'module'; + return parse(code, parsingOptions, delegate); + } + exports.parseModule = parseModule; + function parseScript(code, options, delegate) { + var parsingOptions = options || {}; + parsingOptions.sourceType = 'script'; + return parse(code, parsingOptions, delegate); + } + exports.parseScript = parseScript; + function tokenize(code, options, delegate) { + var tokenizer = new tokenizer_1.Tokenizer(code, options); + var tokens; + tokens = []; + try { + while (true) { + var token = tokenizer.getNextToken(); + if (!token) { + break; + } + if (delegate) { + token = delegate(token); + } + tokens.push(token); + } + } + catch (e) { + tokenizer.errorHandler.tolerate(e); + } + if (tokenizer.errorHandler.tolerant) { + tokens.errors = tokenizer.errors(); + } + return tokens; + } + exports.tokenize = tokenize; + var syntax_1 = __webpack_require__(2); + exports.Syntax = syntax_1.Syntax; + // Sync with *.json manifests. + exports.version = '4.0.1'; + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var syntax_1 = __webpack_require__(2); + var CommentHandler = (function () { + function CommentHandler() { + this.attach = false; + this.comments = []; + this.stack = []; + this.leading = []; + this.trailing = []; + } + CommentHandler.prototype.insertInnerComments = function (node, metadata) { + // innnerComments for properties empty block + // `function a() {/** comments **\/}` + if (node.type === syntax_1.Syntax.BlockStatement && node.body.length === 0) { + var innerComments = []; + for (var i = this.leading.length - 1; i >= 0; --i) { + var entry = this.leading[i]; + if (metadata.end.offset >= entry.start) { + innerComments.unshift(entry.comment); + this.leading.splice(i, 1); + this.trailing.splice(i, 1); + } + } + if (innerComments.length) { + node.innerComments = innerComments; + } + } + }; + CommentHandler.prototype.findTrailingComments = function (metadata) { + var trailingComments = []; + if (this.trailing.length > 0) { + for (var i = this.trailing.length - 1; i >= 0; --i) { + var entry_1 = this.trailing[i]; + if (entry_1.start >= metadata.end.offset) { + trailingComments.unshift(entry_1.comment); + } + } + this.trailing.length = 0; + return trailingComments; + } + var entry = this.stack[this.stack.length - 1]; + if (entry && entry.node.trailingComments) { + var firstComment = entry.node.trailingComments[0]; + if (firstComment && firstComment.range[0] >= metadata.end.offset) { + trailingComments = entry.node.trailingComments; + delete entry.node.trailingComments; + } + } + return trailingComments; + }; + CommentHandler.prototype.findLeadingComments = function (metadata) { + var leadingComments = []; + var target; + while (this.stack.length > 0) { + var entry = this.stack[this.stack.length - 1]; + if (entry && entry.start >= metadata.start.offset) { + target = entry.node; + this.stack.pop(); + } + else { + break; + } + } + if (target) { + var count = target.leadingComments ? target.leadingComments.length : 0; + for (var i = count - 1; i >= 0; --i) { + var comment = target.leadingComments[i]; + if (comment.range[1] <= metadata.start.offset) { + leadingComments.unshift(comment); + target.leadingComments.splice(i, 1); + } + } + if (target.leadingComments && target.leadingComments.length === 0) { + delete target.leadingComments; + } + return leadingComments; + } + for (var i = this.leading.length - 1; i >= 0; --i) { + var entry = this.leading[i]; + if (entry.start <= metadata.start.offset) { + leadingComments.unshift(entry.comment); + this.leading.splice(i, 1); + } + } + return leadingComments; + }; + CommentHandler.prototype.visitNode = function (node, metadata) { + if (node.type === syntax_1.Syntax.Program && node.body.length > 0) { + return; + } + this.insertInnerComments(node, metadata); + var trailingComments = this.findTrailingComments(metadata); + var leadingComments = this.findLeadingComments(metadata); + if (leadingComments.length > 0) { + node.leadingComments = leadingComments; + } + if (trailingComments.length > 0) { + node.trailingComments = trailingComments; + } + this.stack.push({ + node: node, + start: metadata.start.offset + }); + }; + CommentHandler.prototype.visitComment = function (node, metadata) { + var type = (node.type[0] === 'L') ? 'Line' : 'Block'; + var comment = { + type: type, + value: node.value + }; + if (node.range) { + comment.range = node.range; + } + if (node.loc) { + comment.loc = node.loc; + } + this.comments.push(comment); + if (this.attach) { + var entry = { + comment: { + type: type, + value: node.value, + range: [metadata.start.offset, metadata.end.offset] + }, + start: metadata.start.offset + }; + if (node.loc) { + entry.comment.loc = node.loc; + } + node.type = type; + this.leading.push(entry); + this.trailing.push(entry); + } + }; + CommentHandler.prototype.visit = function (node, metadata) { + if (node.type === 'LineComment') { + this.visitComment(node, metadata); + } + else if (node.type === 'BlockComment') { + this.visitComment(node, metadata); + } + else if (this.attach) { + this.visitNode(node, metadata); + } + }; + return CommentHandler; + }()); + exports.CommentHandler = CommentHandler; + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Syntax = { + AssignmentExpression: 'AssignmentExpression', + AssignmentPattern: 'AssignmentPattern', + ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AwaitExpression: 'AwaitExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExportAllDeclaration: 'ExportAllDeclaration', + ExportDefaultDeclaration: 'ExportDefaultDeclaration', + ExportNamedDeclaration: 'ExportNamedDeclaration', + ExportSpecifier: 'ExportSpecifier', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForOfStatement: 'ForOfStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', + ImportSpecifier: 'ImportSpecifier', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + MetaProperty: 'MetaProperty', + MethodDefinition: 'MethodDefinition', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', + Program: 'Program', + Property: 'Property', + RestElement: 'RestElement', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SpreadElement: 'SpreadElement', + Super: 'Super', + SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' + }; + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; +/* istanbul ignore next */ + var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + var character_1 = __webpack_require__(4); + var JSXNode = __webpack_require__(5); + var jsx_syntax_1 = __webpack_require__(6); + var Node = __webpack_require__(7); + var parser_1 = __webpack_require__(8); + var token_1 = __webpack_require__(13); + var xhtml_entities_1 = __webpack_require__(14); + token_1.TokenName[100 /* Identifier */] = 'JSXIdentifier'; + token_1.TokenName[101 /* Text */] = 'JSXText'; + // Fully qualified element name, e.g. returns "svg:path" + function getQualifiedElementName(elementName) { + var qualifiedName; + switch (elementName.type) { + case jsx_syntax_1.JSXSyntax.JSXIdentifier: + var id = elementName; + qualifiedName = id.name; + break; + case jsx_syntax_1.JSXSyntax.JSXNamespacedName: + var ns = elementName; + qualifiedName = getQualifiedElementName(ns.namespace) + ':' + + getQualifiedElementName(ns.name); + break; + case jsx_syntax_1.JSXSyntax.JSXMemberExpression: + var expr = elementName; + qualifiedName = getQualifiedElementName(expr.object) + '.' + + getQualifiedElementName(expr.property); + break; + /* istanbul ignore next */ + default: + break; + } + return qualifiedName; + } + var JSXParser = (function (_super) { + __extends(JSXParser, _super); + function JSXParser(code, options, delegate) { + return _super.call(this, code, options, delegate) || this; + } + JSXParser.prototype.parsePrimaryExpression = function () { + return this.match('<') ? this.parseJSXRoot() : _super.prototype.parsePrimaryExpression.call(this); + }; + JSXParser.prototype.startJSX = function () { + // Unwind the scanner before the lookahead token. + this.scanner.index = this.startMarker.index; + this.scanner.lineNumber = this.startMarker.line; + this.scanner.lineStart = this.startMarker.index - this.startMarker.column; + }; + JSXParser.prototype.finishJSX = function () { + // Prime the next lookahead. + this.nextToken(); + }; + JSXParser.prototype.reenterJSX = function () { + this.startJSX(); + this.expectJSX('}'); + // Pop the closing '}' added from the lookahead. + if (this.config.tokens) { + this.tokens.pop(); + } + }; + JSXParser.prototype.createJSXNode = function () { + this.collectComments(); + return { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + }; + JSXParser.prototype.createJSXChildNode = function () { + return { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + }; + JSXParser.prototype.scanXHTMLEntity = function (quote) { + var result = '&'; + var valid = true; + var terminated = false; + var numeric = false; + var hex = false; + while (!this.scanner.eof() && valid && !terminated) { + var ch = this.scanner.source[this.scanner.index]; + if (ch === quote) { + break; + } + terminated = (ch === ';'); + result += ch; + ++this.scanner.index; + if (!terminated) { + switch (result.length) { + case 2: + // e.g. '{' + numeric = (ch === '#'); + break; + case 3: + if (numeric) { + // e.g. 'A' + hex = (ch === 'x'); + valid = hex || character_1.Character.isDecimalDigit(ch.charCodeAt(0)); + numeric = numeric && !hex; + } + break; + default: + valid = valid && !(numeric && !character_1.Character.isDecimalDigit(ch.charCodeAt(0))); + valid = valid && !(hex && !character_1.Character.isHexDigit(ch.charCodeAt(0))); + break; + } + } + } + if (valid && terminated && result.length > 2) { + // e.g. 'A' becomes just '#x41' + var str = result.substr(1, result.length - 2); + if (numeric && str.length > 1) { + result = String.fromCharCode(parseInt(str.substr(1), 10)); + } + else if (hex && str.length > 2) { + result = String.fromCharCode(parseInt('0' + str.substr(1), 16)); + } + else if (!numeric && !hex && xhtml_entities_1.XHTMLEntities[str]) { + result = xhtml_entities_1.XHTMLEntities[str]; + } + } + return result; + }; + // Scan the next JSX token. This replaces Scanner#lex when in JSX mode. + JSXParser.prototype.lexJSX = function () { + var cp = this.scanner.source.charCodeAt(this.scanner.index); + // < > / : = { } + if (cp === 60 || cp === 62 || cp === 47 || cp === 58 || cp === 61 || cp === 123 || cp === 125) { + var value = this.scanner.source[this.scanner.index++]; + return { + type: 7 /* Punctuator */, + value: value, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: this.scanner.index - 1, + end: this.scanner.index + }; + } + // " ' + if (cp === 34 || cp === 39) { + var start = this.scanner.index; + var quote = this.scanner.source[this.scanner.index++]; + var str = ''; + while (!this.scanner.eof()) { + var ch = this.scanner.source[this.scanner.index++]; + if (ch === quote) { + break; + } + else if (ch === '&') { + str += this.scanXHTMLEntity(quote); + } + else { + str += ch; + } + } + return { + type: 8 /* StringLiteral */, + value: str, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + // ... or . + if (cp === 46) { + var n1 = this.scanner.source.charCodeAt(this.scanner.index + 1); + var n2 = this.scanner.source.charCodeAt(this.scanner.index + 2); + var value = (n1 === 46 && n2 === 46) ? '...' : '.'; + var start = this.scanner.index; + this.scanner.index += value.length; + return { + type: 7 /* Punctuator */, + value: value, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + // ` + if (cp === 96) { + // Only placeholder, since it will be rescanned as a real assignment expression. + return { + type: 10 /* Template */, + value: '', + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: this.scanner.index, + end: this.scanner.index + }; + } + // Identifer can not contain backslash (char code 92). + if (character_1.Character.isIdentifierStart(cp) && (cp !== 92)) { + var start = this.scanner.index; + ++this.scanner.index; + while (!this.scanner.eof()) { + var ch = this.scanner.source.charCodeAt(this.scanner.index); + if (character_1.Character.isIdentifierPart(ch) && (ch !== 92)) { + ++this.scanner.index; + } + else if (ch === 45) { + // Hyphen (char code 45) can be part of an identifier. + ++this.scanner.index; + } + else { + break; + } + } + var id = this.scanner.source.slice(start, this.scanner.index); + return { + type: 100 /* Identifier */, + value: id, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + return this.scanner.lex(); + }; + JSXParser.prototype.nextJSXToken = function () { + this.collectComments(); + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + var token = this.lexJSX(); + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + if (this.config.tokens) { + this.tokens.push(this.convertToken(token)); + } + return token; + }; + JSXParser.prototype.nextJSXText = function () { + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + var start = this.scanner.index; + var text = ''; + while (!this.scanner.eof()) { + var ch = this.scanner.source[this.scanner.index]; + if (ch === '{' || ch === '<') { + break; + } + ++this.scanner.index; + text += ch; + if (character_1.Character.isLineTerminator(ch.charCodeAt(0))) { + ++this.scanner.lineNumber; + if (ch === '\r' && this.scanner.source[this.scanner.index] === '\n') { + ++this.scanner.index; + } + this.scanner.lineStart = this.scanner.index; + } + } + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + var token = { + type: 101 /* Text */, + value: text, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + if ((text.length > 0) && this.config.tokens) { + this.tokens.push(this.convertToken(token)); + } + return token; + }; + JSXParser.prototype.peekJSXToken = function () { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.lexJSX(); + this.scanner.restoreState(state); + return next; + }; + // Expect the next JSX token to match the specified punctuator. + // If not, an exception will be thrown. + JSXParser.prototype.expectJSX = function (value) { + var token = this.nextJSXToken(); + if (token.type !== 7 /* Punctuator */ || token.value !== value) { + this.throwUnexpectedToken(token); + } + }; + // Return true if the next JSX token matches the specified punctuator. + JSXParser.prototype.matchJSX = function (value) { + var next = this.peekJSXToken(); + return next.type === 7 /* Punctuator */ && next.value === value; + }; + JSXParser.prototype.parseJSXIdentifier = function () { + var node = this.createJSXNode(); + var token = this.nextJSXToken(); + if (token.type !== 100 /* Identifier */) { + this.throwUnexpectedToken(token); + } + return this.finalize(node, new JSXNode.JSXIdentifier(token.value)); + }; + JSXParser.prototype.parseJSXElementName = function () { + var node = this.createJSXNode(); + var elementName = this.parseJSXIdentifier(); + if (this.matchJSX(':')) { + var namespace = elementName; + this.expectJSX(':'); + var name_1 = this.parseJSXIdentifier(); + elementName = this.finalize(node, new JSXNode.JSXNamespacedName(namespace, name_1)); + } + else if (this.matchJSX('.')) { + while (this.matchJSX('.')) { + var object = elementName; + this.expectJSX('.'); + var property = this.parseJSXIdentifier(); + elementName = this.finalize(node, new JSXNode.JSXMemberExpression(object, property)); + } + } + return elementName; + }; + JSXParser.prototype.parseJSXAttributeName = function () { + var node = this.createJSXNode(); + var attributeName; + var identifier = this.parseJSXIdentifier(); + if (this.matchJSX(':')) { + var namespace = identifier; + this.expectJSX(':'); + var name_2 = this.parseJSXIdentifier(); + attributeName = this.finalize(node, new JSXNode.JSXNamespacedName(namespace, name_2)); + } + else { + attributeName = identifier; + } + return attributeName; + }; + JSXParser.prototype.parseJSXStringLiteralAttribute = function () { + var node = this.createJSXNode(); + var token = this.nextJSXToken(); + if (token.type !== 8 /* StringLiteral */) { + this.throwUnexpectedToken(token); + } + var raw = this.getTokenRaw(token); + return this.finalize(node, new Node.Literal(token.value, raw)); + }; + JSXParser.prototype.parseJSXExpressionAttribute = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + this.finishJSX(); + if (this.match('}')) { + this.tolerateError('JSX attributes must only be assigned a non-empty expression'); + } + var expression = this.parseAssignmentExpression(); + this.reenterJSX(); + return this.finalize(node, new JSXNode.JSXExpressionContainer(expression)); + }; + JSXParser.prototype.parseJSXAttributeValue = function () { + return this.matchJSX('{') ? this.parseJSXExpressionAttribute() : + this.matchJSX('<') ? this.parseJSXElement() : this.parseJSXStringLiteralAttribute(); + }; + JSXParser.prototype.parseJSXNameValueAttribute = function () { + var node = this.createJSXNode(); + var name = this.parseJSXAttributeName(); + var value = null; + if (this.matchJSX('=')) { + this.expectJSX('='); + value = this.parseJSXAttributeValue(); + } + return this.finalize(node, new JSXNode.JSXAttribute(name, value)); + }; + JSXParser.prototype.parseJSXSpreadAttribute = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + this.expectJSX('...'); + this.finishJSX(); + var argument = this.parseAssignmentExpression(); + this.reenterJSX(); + return this.finalize(node, new JSXNode.JSXSpreadAttribute(argument)); + }; + JSXParser.prototype.parseJSXAttributes = function () { + var attributes = []; + while (!this.matchJSX('/') && !this.matchJSX('>')) { + var attribute = this.matchJSX('{') ? this.parseJSXSpreadAttribute() : + this.parseJSXNameValueAttribute(); + attributes.push(attribute); + } + return attributes; + }; + JSXParser.prototype.parseJSXOpeningElement = function () { + var node = this.createJSXNode(); + this.expectJSX('<'); + var name = this.parseJSXElementName(); + var attributes = this.parseJSXAttributes(); + var selfClosing = this.matchJSX('/'); + if (selfClosing) { + this.expectJSX('/'); + } + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXOpeningElement(name, selfClosing, attributes)); + }; + JSXParser.prototype.parseJSXBoundaryElement = function () { + var node = this.createJSXNode(); + this.expectJSX('<'); + if (this.matchJSX('/')) { + this.expectJSX('/'); + var name_3 = this.parseJSXElementName(); + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXClosingElement(name_3)); + } + var name = this.parseJSXElementName(); + var attributes = this.parseJSXAttributes(); + var selfClosing = this.matchJSX('/'); + if (selfClosing) { + this.expectJSX('/'); + } + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXOpeningElement(name, selfClosing, attributes)); + }; + JSXParser.prototype.parseJSXEmptyExpression = function () { + var node = this.createJSXChildNode(); + this.collectComments(); + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + return this.finalize(node, new JSXNode.JSXEmptyExpression()); + }; + JSXParser.prototype.parseJSXExpressionContainer = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + var expression; + if (this.matchJSX('}')) { + expression = this.parseJSXEmptyExpression(); + this.expectJSX('}'); + } + else { + this.finishJSX(); + expression = this.parseAssignmentExpression(); + this.reenterJSX(); + } + return this.finalize(node, new JSXNode.JSXExpressionContainer(expression)); + }; + JSXParser.prototype.parseJSXChildren = function () { + var children = []; + while (!this.scanner.eof()) { + var node = this.createJSXChildNode(); + var token = this.nextJSXText(); + if (token.start < token.end) { + var raw = this.getTokenRaw(token); + var child = this.finalize(node, new JSXNode.JSXText(token.value, raw)); + children.push(child); + } + if (this.scanner.source[this.scanner.index] === '{') { + var container = this.parseJSXExpressionContainer(); + children.push(container); + } + else { + break; + } + } + return children; + }; + JSXParser.prototype.parseComplexJSXElement = function (el) { + var stack = []; + while (!this.scanner.eof()) { + el.children = el.children.concat(this.parseJSXChildren()); + var node = this.createJSXChildNode(); + var element = this.parseJSXBoundaryElement(); + if (element.type === jsx_syntax_1.JSXSyntax.JSXOpeningElement) { + var opening = element; + if (opening.selfClosing) { + var child = this.finalize(node, new JSXNode.JSXElement(opening, [], null)); + el.children.push(child); + } + else { + stack.push(el); + el = { node: node, opening: opening, closing: null, children: [] }; + } + } + if (element.type === jsx_syntax_1.JSXSyntax.JSXClosingElement) { + el.closing = element; + var open_1 = getQualifiedElementName(el.opening.name); + var close_1 = getQualifiedElementName(el.closing.name); + if (open_1 !== close_1) { + this.tolerateError('Expected corresponding JSX closing tag for %0', open_1); + } + if (stack.length > 0) { + var child = this.finalize(el.node, new JSXNode.JSXElement(el.opening, el.children, el.closing)); + el = stack[stack.length - 1]; + el.children.push(child); + stack.pop(); + } + else { + break; + } + } + } + return el; + }; + JSXParser.prototype.parseJSXElement = function () { + var node = this.createJSXNode(); + var opening = this.parseJSXOpeningElement(); + var children = []; + var closing = null; + if (!opening.selfClosing) { + var el = this.parseComplexJSXElement({ node: node, opening: opening, closing: closing, children: children }); + children = el.children; + closing = el.closing; + } + return this.finalize(node, new JSXNode.JSXElement(opening, children, closing)); + }; + JSXParser.prototype.parseJSXRoot = function () { + // Pop the opening '<' added from the lookahead. + if (this.config.tokens) { + this.tokens.pop(); + } + this.startJSX(); + var element = this.parseJSXElement(); + this.finishJSX(); + return element; + }; + JSXParser.prototype.isStartOfExpression = function () { + return _super.prototype.isStartOfExpression.call(this) || this.match('<'); + }; + return JSXParser; + }(parser_1.Parser)); + exports.JSXParser = JSXParser; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + // See also tools/generate-unicode-regex.js. + var Regex = { + // Unicode v8.0.0 NonAsciiIdentifierStart: + NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, + // Unicode v8.0.0 NonAsciiIdentifierPart: + NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ + }; + exports.Character = { + /* tslint:disable:no-bitwise */ + fromCodePoint: function (cp) { + return (cp < 0x10000) ? String.fromCharCode(cp) : + String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); + }, + // https://tc39.github.io/ecma262/#sec-white-space + isWhiteSpace: function (cp) { + return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) || + (cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0); + }, + // https://tc39.github.io/ecma262/#sec-line-terminators + isLineTerminator: function (cp) { + return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029); + }, + // https://tc39.github.io/ecma262/#sec-names-and-keywords + isIdentifierStart: function (cp) { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp === 0x5C) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(exports.Character.fromCodePoint(cp))); + }, + isIdentifierPart: function (cp) { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp >= 0x30 && cp <= 0x39) || + (cp === 0x5C) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(exports.Character.fromCodePoint(cp))); + }, + // https://tc39.github.io/ecma262/#sec-literals-numeric-literals + isDecimalDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x39); // 0..9 + }, + isHexDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x39) || + (cp >= 0x41 && cp <= 0x46) || + (cp >= 0x61 && cp <= 0x66); // a..f + }, + isOctalDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x37); // 0..7 + } + }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var jsx_syntax_1 = __webpack_require__(6); + /* tslint:disable:max-classes-per-file */ + var JSXClosingElement = (function () { + function JSXClosingElement(name) { + this.type = jsx_syntax_1.JSXSyntax.JSXClosingElement; + this.name = name; + } + return JSXClosingElement; + }()); + exports.JSXClosingElement = JSXClosingElement; + var JSXElement = (function () { + function JSXElement(openingElement, children, closingElement) { + this.type = jsx_syntax_1.JSXSyntax.JSXElement; + this.openingElement = openingElement; + this.children = children; + this.closingElement = closingElement; + } + return JSXElement; + }()); + exports.JSXElement = JSXElement; + var JSXEmptyExpression = (function () { + function JSXEmptyExpression() { + this.type = jsx_syntax_1.JSXSyntax.JSXEmptyExpression; + } + return JSXEmptyExpression; + }()); + exports.JSXEmptyExpression = JSXEmptyExpression; + var JSXExpressionContainer = (function () { + function JSXExpressionContainer(expression) { + this.type = jsx_syntax_1.JSXSyntax.JSXExpressionContainer; + this.expression = expression; + } + return JSXExpressionContainer; + }()); + exports.JSXExpressionContainer = JSXExpressionContainer; + var JSXIdentifier = (function () { + function JSXIdentifier(name) { + this.type = jsx_syntax_1.JSXSyntax.JSXIdentifier; + this.name = name; + } + return JSXIdentifier; + }()); + exports.JSXIdentifier = JSXIdentifier; + var JSXMemberExpression = (function () { + function JSXMemberExpression(object, property) { + this.type = jsx_syntax_1.JSXSyntax.JSXMemberExpression; + this.object = object; + this.property = property; + } + return JSXMemberExpression; + }()); + exports.JSXMemberExpression = JSXMemberExpression; + var JSXAttribute = (function () { + function JSXAttribute(name, value) { + this.type = jsx_syntax_1.JSXSyntax.JSXAttribute; + this.name = name; + this.value = value; + } + return JSXAttribute; + }()); + exports.JSXAttribute = JSXAttribute; + var JSXNamespacedName = (function () { + function JSXNamespacedName(namespace, name) { + this.type = jsx_syntax_1.JSXSyntax.JSXNamespacedName; + this.namespace = namespace; + this.name = name; + } + return JSXNamespacedName; + }()); + exports.JSXNamespacedName = JSXNamespacedName; + var JSXOpeningElement = (function () { + function JSXOpeningElement(name, selfClosing, attributes) { + this.type = jsx_syntax_1.JSXSyntax.JSXOpeningElement; + this.name = name; + this.selfClosing = selfClosing; + this.attributes = attributes; + } + return JSXOpeningElement; + }()); + exports.JSXOpeningElement = JSXOpeningElement; + var JSXSpreadAttribute = (function () { + function JSXSpreadAttribute(argument) { + this.type = jsx_syntax_1.JSXSyntax.JSXSpreadAttribute; + this.argument = argument; + } + return JSXSpreadAttribute; + }()); + exports.JSXSpreadAttribute = JSXSpreadAttribute; + var JSXText = (function () { + function JSXText(value, raw) { + this.type = jsx_syntax_1.JSXSyntax.JSXText; + this.value = value; + this.raw = raw; + } + return JSXText; + }()); + exports.JSXText = JSXText; + + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.JSXSyntax = { + JSXAttribute: 'JSXAttribute', + JSXClosingElement: 'JSXClosingElement', + JSXElement: 'JSXElement', + JSXEmptyExpression: 'JSXEmptyExpression', + JSXExpressionContainer: 'JSXExpressionContainer', + JSXIdentifier: 'JSXIdentifier', + JSXMemberExpression: 'JSXMemberExpression', + JSXNamespacedName: 'JSXNamespacedName', + JSXOpeningElement: 'JSXOpeningElement', + JSXSpreadAttribute: 'JSXSpreadAttribute', + JSXText: 'JSXText' + }; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var syntax_1 = __webpack_require__(2); + /* tslint:disable:max-classes-per-file */ + var ArrayExpression = (function () { + function ArrayExpression(elements) { + this.type = syntax_1.Syntax.ArrayExpression; + this.elements = elements; + } + return ArrayExpression; + }()); + exports.ArrayExpression = ArrayExpression; + var ArrayPattern = (function () { + function ArrayPattern(elements) { + this.type = syntax_1.Syntax.ArrayPattern; + this.elements = elements; + } + return ArrayPattern; + }()); + exports.ArrayPattern = ArrayPattern; + var ArrowFunctionExpression = (function () { + function ArrowFunctionExpression(params, body, expression) { + this.type = syntax_1.Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.body = body; + this.generator = false; + this.expression = expression; + this.async = false; + } + return ArrowFunctionExpression; + }()); + exports.ArrowFunctionExpression = ArrowFunctionExpression; + var AssignmentExpression = (function () { + function AssignmentExpression(operator, left, right) { + this.type = syntax_1.Syntax.AssignmentExpression; + this.operator = operator; + this.left = left; + this.right = right; + } + return AssignmentExpression; + }()); + exports.AssignmentExpression = AssignmentExpression; + var AssignmentPattern = (function () { + function AssignmentPattern(left, right) { + this.type = syntax_1.Syntax.AssignmentPattern; + this.left = left; + this.right = right; + } + return AssignmentPattern; + }()); + exports.AssignmentPattern = AssignmentPattern; + var AsyncArrowFunctionExpression = (function () { + function AsyncArrowFunctionExpression(params, body, expression) { + this.type = syntax_1.Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.body = body; + this.generator = false; + this.expression = expression; + this.async = true; + } + return AsyncArrowFunctionExpression; + }()); + exports.AsyncArrowFunctionExpression = AsyncArrowFunctionExpression; + var AsyncFunctionDeclaration = (function () { + function AsyncFunctionDeclaration(id, params, body) { + this.type = syntax_1.Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = false; + this.expression = false; + this.async = true; + } + return AsyncFunctionDeclaration; + }()); + exports.AsyncFunctionDeclaration = AsyncFunctionDeclaration; + var AsyncFunctionExpression = (function () { + function AsyncFunctionExpression(id, params, body) { + this.type = syntax_1.Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.body = body; + this.generator = false; + this.expression = false; + this.async = true; + } + return AsyncFunctionExpression; + }()); + exports.AsyncFunctionExpression = AsyncFunctionExpression; + var AwaitExpression = (function () { + function AwaitExpression(argument) { + this.type = syntax_1.Syntax.AwaitExpression; + this.argument = argument; + } + return AwaitExpression; + }()); + exports.AwaitExpression = AwaitExpression; + var BinaryExpression = (function () { + function BinaryExpression(operator, left, right) { + var logical = (operator === '||' || operator === '&&'); + this.type = logical ? syntax_1.Syntax.LogicalExpression : syntax_1.Syntax.BinaryExpression; + this.operator = operator; + this.left = left; + this.right = right; + } + return BinaryExpression; + }()); + exports.BinaryExpression = BinaryExpression; + var BlockStatement = (function () { + function BlockStatement(body) { + this.type = syntax_1.Syntax.BlockStatement; + this.body = body; + } + return BlockStatement; + }()); + exports.BlockStatement = BlockStatement; + var BreakStatement = (function () { + function BreakStatement(label) { + this.type = syntax_1.Syntax.BreakStatement; + this.label = label; + } + return BreakStatement; + }()); + exports.BreakStatement = BreakStatement; + var CallExpression = (function () { + function CallExpression(callee, args) { + this.type = syntax_1.Syntax.CallExpression; + this.callee = callee; + this.arguments = args; + } + return CallExpression; + }()); + exports.CallExpression = CallExpression; + var CatchClause = (function () { + function CatchClause(param, body) { + this.type = syntax_1.Syntax.CatchClause; + this.param = param; + this.body = body; + } + return CatchClause; + }()); + exports.CatchClause = CatchClause; + var ClassBody = (function () { + function ClassBody(body) { + this.type = syntax_1.Syntax.ClassBody; + this.body = body; + } + return ClassBody; + }()); + exports.ClassBody = ClassBody; + var ClassDeclaration = (function () { + function ClassDeclaration(id, superClass, body) { + this.type = syntax_1.Syntax.ClassDeclaration; + this.id = id; + this.superClass = superClass; + this.body = body; + } + return ClassDeclaration; + }()); + exports.ClassDeclaration = ClassDeclaration; + var ClassExpression = (function () { + function ClassExpression(id, superClass, body) { + this.type = syntax_1.Syntax.ClassExpression; + this.id = id; + this.superClass = superClass; + this.body = body; + } + return ClassExpression; + }()); + exports.ClassExpression = ClassExpression; + var ComputedMemberExpression = (function () { + function ComputedMemberExpression(object, property) { + this.type = syntax_1.Syntax.MemberExpression; + this.computed = true; + this.object = object; + this.property = property; + } + return ComputedMemberExpression; + }()); + exports.ComputedMemberExpression = ComputedMemberExpression; + var ConditionalExpression = (function () { + function ConditionalExpression(test, consequent, alternate) { + this.type = syntax_1.Syntax.ConditionalExpression; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + return ConditionalExpression; + }()); + exports.ConditionalExpression = ConditionalExpression; + var ContinueStatement = (function () { + function ContinueStatement(label) { + this.type = syntax_1.Syntax.ContinueStatement; + this.label = label; + } + return ContinueStatement; + }()); + exports.ContinueStatement = ContinueStatement; + var DebuggerStatement = (function () { + function DebuggerStatement() { + this.type = syntax_1.Syntax.DebuggerStatement; + } + return DebuggerStatement; + }()); + exports.DebuggerStatement = DebuggerStatement; + var Directive = (function () { + function Directive(expression, directive) { + this.type = syntax_1.Syntax.ExpressionStatement; + this.expression = expression; + this.directive = directive; + } + return Directive; + }()); + exports.Directive = Directive; + var DoWhileStatement = (function () { + function DoWhileStatement(body, test) { + this.type = syntax_1.Syntax.DoWhileStatement; + this.body = body; + this.test = test; + } + return DoWhileStatement; + }()); + exports.DoWhileStatement = DoWhileStatement; + var EmptyStatement = (function () { + function EmptyStatement() { + this.type = syntax_1.Syntax.EmptyStatement; + } + return EmptyStatement; + }()); + exports.EmptyStatement = EmptyStatement; + var ExportAllDeclaration = (function () { + function ExportAllDeclaration(source) { + this.type = syntax_1.Syntax.ExportAllDeclaration; + this.source = source; + } + return ExportAllDeclaration; + }()); + exports.ExportAllDeclaration = ExportAllDeclaration; + var ExportDefaultDeclaration = (function () { + function ExportDefaultDeclaration(declaration) { + this.type = syntax_1.Syntax.ExportDefaultDeclaration; + this.declaration = declaration; + } + return ExportDefaultDeclaration; + }()); + exports.ExportDefaultDeclaration = ExportDefaultDeclaration; + var ExportNamedDeclaration = (function () { + function ExportNamedDeclaration(declaration, specifiers, source) { + this.type = syntax_1.Syntax.ExportNamedDeclaration; + this.declaration = declaration; + this.specifiers = specifiers; + this.source = source; + } + return ExportNamedDeclaration; + }()); + exports.ExportNamedDeclaration = ExportNamedDeclaration; + var ExportSpecifier = (function () { + function ExportSpecifier(local, exported) { + this.type = syntax_1.Syntax.ExportSpecifier; + this.exported = exported; + this.local = local; + } + return ExportSpecifier; + }()); + exports.ExportSpecifier = ExportSpecifier; + var ExpressionStatement = (function () { + function ExpressionStatement(expression) { + this.type = syntax_1.Syntax.ExpressionStatement; + this.expression = expression; + } + return ExpressionStatement; + }()); + exports.ExpressionStatement = ExpressionStatement; + var ForInStatement = (function () { + function ForInStatement(left, right, body) { + this.type = syntax_1.Syntax.ForInStatement; + this.left = left; + this.right = right; + this.body = body; + this.each = false; + } + return ForInStatement; + }()); + exports.ForInStatement = ForInStatement; + var ForOfStatement = (function () { + function ForOfStatement(left, right, body) { + this.type = syntax_1.Syntax.ForOfStatement; + this.left = left; + this.right = right; + this.body = body; + } + return ForOfStatement; + }()); + exports.ForOfStatement = ForOfStatement; + var ForStatement = (function () { + function ForStatement(init, test, update, body) { + this.type = syntax_1.Syntax.ForStatement; + this.init = init; + this.test = test; + this.update = update; + this.body = body; + } + return ForStatement; + }()); + exports.ForStatement = ForStatement; + var FunctionDeclaration = (function () { + function FunctionDeclaration(id, params, body, generator) { + this.type = syntax_1.Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = false; + } + return FunctionDeclaration; + }()); + exports.FunctionDeclaration = FunctionDeclaration; + var FunctionExpression = (function () { + function FunctionExpression(id, params, body, generator) { + this.type = syntax_1.Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = false; + } + return FunctionExpression; + }()); + exports.FunctionExpression = FunctionExpression; + var Identifier = (function () { + function Identifier(name) { + this.type = syntax_1.Syntax.Identifier; + this.name = name; + } + return Identifier; + }()); + exports.Identifier = Identifier; + var IfStatement = (function () { + function IfStatement(test, consequent, alternate) { + this.type = syntax_1.Syntax.IfStatement; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + return IfStatement; + }()); + exports.IfStatement = IfStatement; + var ImportDeclaration = (function () { + function ImportDeclaration(specifiers, source) { + this.type = syntax_1.Syntax.ImportDeclaration; + this.specifiers = specifiers; + this.source = source; + } + return ImportDeclaration; + }()); + exports.ImportDeclaration = ImportDeclaration; + var ImportDefaultSpecifier = (function () { + function ImportDefaultSpecifier(local) { + this.type = syntax_1.Syntax.ImportDefaultSpecifier; + this.local = local; + } + return ImportDefaultSpecifier; + }()); + exports.ImportDefaultSpecifier = ImportDefaultSpecifier; + var ImportNamespaceSpecifier = (function () { + function ImportNamespaceSpecifier(local) { + this.type = syntax_1.Syntax.ImportNamespaceSpecifier; + this.local = local; + } + return ImportNamespaceSpecifier; + }()); + exports.ImportNamespaceSpecifier = ImportNamespaceSpecifier; + var ImportSpecifier = (function () { + function ImportSpecifier(local, imported) { + this.type = syntax_1.Syntax.ImportSpecifier; + this.local = local; + this.imported = imported; + } + return ImportSpecifier; + }()); + exports.ImportSpecifier = ImportSpecifier; + var LabeledStatement = (function () { + function LabeledStatement(label, body) { + this.type = syntax_1.Syntax.LabeledStatement; + this.label = label; + this.body = body; + } + return LabeledStatement; + }()); + exports.LabeledStatement = LabeledStatement; + var Literal = (function () { + function Literal(value, raw) { + this.type = syntax_1.Syntax.Literal; + this.value = value; + this.raw = raw; + } + return Literal; + }()); + exports.Literal = Literal; + var MetaProperty = (function () { + function MetaProperty(meta, property) { + this.type = syntax_1.Syntax.MetaProperty; + this.meta = meta; + this.property = property; + } + return MetaProperty; + }()); + exports.MetaProperty = MetaProperty; + var MethodDefinition = (function () { + function MethodDefinition(key, computed, value, kind, isStatic) { + this.type = syntax_1.Syntax.MethodDefinition; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.static = isStatic; + } + return MethodDefinition; + }()); + exports.MethodDefinition = MethodDefinition; + var Module = (function () { + function Module(body) { + this.type = syntax_1.Syntax.Program; + this.body = body; + this.sourceType = 'module'; + } + return Module; + }()); + exports.Module = Module; + var NewExpression = (function () { + function NewExpression(callee, args) { + this.type = syntax_1.Syntax.NewExpression; + this.callee = callee; + this.arguments = args; + } + return NewExpression; + }()); + exports.NewExpression = NewExpression; + var ObjectExpression = (function () { + function ObjectExpression(properties) { + this.type = syntax_1.Syntax.ObjectExpression; + this.properties = properties; + } + return ObjectExpression; + }()); + exports.ObjectExpression = ObjectExpression; + var ObjectPattern = (function () { + function ObjectPattern(properties) { + this.type = syntax_1.Syntax.ObjectPattern; + this.properties = properties; + } + return ObjectPattern; + }()); + exports.ObjectPattern = ObjectPattern; + var Property = (function () { + function Property(kind, key, computed, value, method, shorthand) { + this.type = syntax_1.Syntax.Property; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.method = method; + this.shorthand = shorthand; + } + return Property; + }()); + exports.Property = Property; + var RegexLiteral = (function () { + function RegexLiteral(value, raw, pattern, flags) { + this.type = syntax_1.Syntax.Literal; + this.value = value; + this.raw = raw; + this.regex = { pattern: pattern, flags: flags }; + } + return RegexLiteral; + }()); + exports.RegexLiteral = RegexLiteral; + var RestElement = (function () { + function RestElement(argument) { + this.type = syntax_1.Syntax.RestElement; + this.argument = argument; + } + return RestElement; + }()); + exports.RestElement = RestElement; + var ReturnStatement = (function () { + function ReturnStatement(argument) { + this.type = syntax_1.Syntax.ReturnStatement; + this.argument = argument; + } + return ReturnStatement; + }()); + exports.ReturnStatement = ReturnStatement; + var Script = (function () { + function Script(body) { + this.type = syntax_1.Syntax.Program; + this.body = body; + this.sourceType = 'script'; + } + return Script; + }()); + exports.Script = Script; + var SequenceExpression = (function () { + function SequenceExpression(expressions) { + this.type = syntax_1.Syntax.SequenceExpression; + this.expressions = expressions; + } + return SequenceExpression; + }()); + exports.SequenceExpression = SequenceExpression; + var SpreadElement = (function () { + function SpreadElement(argument) { + this.type = syntax_1.Syntax.SpreadElement; + this.argument = argument; + } + return SpreadElement; + }()); + exports.SpreadElement = SpreadElement; + var StaticMemberExpression = (function () { + function StaticMemberExpression(object, property) { + this.type = syntax_1.Syntax.MemberExpression; + this.computed = false; + this.object = object; + this.property = property; + } + return StaticMemberExpression; + }()); + exports.StaticMemberExpression = StaticMemberExpression; + var Super = (function () { + function Super() { + this.type = syntax_1.Syntax.Super; + } + return Super; + }()); + exports.Super = Super; + var SwitchCase = (function () { + function SwitchCase(test, consequent) { + this.type = syntax_1.Syntax.SwitchCase; + this.test = test; + this.consequent = consequent; + } + return SwitchCase; + }()); + exports.SwitchCase = SwitchCase; + var SwitchStatement = (function () { + function SwitchStatement(discriminant, cases) { + this.type = syntax_1.Syntax.SwitchStatement; + this.discriminant = discriminant; + this.cases = cases; + } + return SwitchStatement; + }()); + exports.SwitchStatement = SwitchStatement; + var TaggedTemplateExpression = (function () { + function TaggedTemplateExpression(tag, quasi) { + this.type = syntax_1.Syntax.TaggedTemplateExpression; + this.tag = tag; + this.quasi = quasi; + } + return TaggedTemplateExpression; + }()); + exports.TaggedTemplateExpression = TaggedTemplateExpression; + var TemplateElement = (function () { + function TemplateElement(value, tail) { + this.type = syntax_1.Syntax.TemplateElement; + this.value = value; + this.tail = tail; + } + return TemplateElement; + }()); + exports.TemplateElement = TemplateElement; + var TemplateLiteral = (function () { + function TemplateLiteral(quasis, expressions) { + this.type = syntax_1.Syntax.TemplateLiteral; + this.quasis = quasis; + this.expressions = expressions; + } + return TemplateLiteral; + }()); + exports.TemplateLiteral = TemplateLiteral; + var ThisExpression = (function () { + function ThisExpression() { + this.type = syntax_1.Syntax.ThisExpression; + } + return ThisExpression; + }()); + exports.ThisExpression = ThisExpression; + var ThrowStatement = (function () { + function ThrowStatement(argument) { + this.type = syntax_1.Syntax.ThrowStatement; + this.argument = argument; + } + return ThrowStatement; + }()); + exports.ThrowStatement = ThrowStatement; + var TryStatement = (function () { + function TryStatement(block, handler, finalizer) { + this.type = syntax_1.Syntax.TryStatement; + this.block = block; + this.handler = handler; + this.finalizer = finalizer; + } + return TryStatement; + }()); + exports.TryStatement = TryStatement; + var UnaryExpression = (function () { + function UnaryExpression(operator, argument) { + this.type = syntax_1.Syntax.UnaryExpression; + this.operator = operator; + this.argument = argument; + this.prefix = true; + } + return UnaryExpression; + }()); + exports.UnaryExpression = UnaryExpression; + var UpdateExpression = (function () { + function UpdateExpression(operator, argument, prefix) { + this.type = syntax_1.Syntax.UpdateExpression; + this.operator = operator; + this.argument = argument; + this.prefix = prefix; + } + return UpdateExpression; + }()); + exports.UpdateExpression = UpdateExpression; + var VariableDeclaration = (function () { + function VariableDeclaration(declarations, kind) { + this.type = syntax_1.Syntax.VariableDeclaration; + this.declarations = declarations; + this.kind = kind; + } + return VariableDeclaration; + }()); + exports.VariableDeclaration = VariableDeclaration; + var VariableDeclarator = (function () { + function VariableDeclarator(id, init) { + this.type = syntax_1.Syntax.VariableDeclarator; + this.id = id; + this.init = init; + } + return VariableDeclarator; + }()); + exports.VariableDeclarator = VariableDeclarator; + var WhileStatement = (function () { + function WhileStatement(test, body) { + this.type = syntax_1.Syntax.WhileStatement; + this.test = test; + this.body = body; + } + return WhileStatement; + }()); + exports.WhileStatement = WhileStatement; + var WithStatement = (function () { + function WithStatement(object, body) { + this.type = syntax_1.Syntax.WithStatement; + this.object = object; + this.body = body; + } + return WithStatement; + }()); + exports.WithStatement = WithStatement; + var YieldExpression = (function () { + function YieldExpression(argument, delegate) { + this.type = syntax_1.Syntax.YieldExpression; + this.argument = argument; + this.delegate = delegate; + } + return YieldExpression; + }()); + exports.YieldExpression = YieldExpression; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var assert_1 = __webpack_require__(9); + var error_handler_1 = __webpack_require__(10); + var messages_1 = __webpack_require__(11); + var Node = __webpack_require__(7); + var scanner_1 = __webpack_require__(12); + var syntax_1 = __webpack_require__(2); + var token_1 = __webpack_require__(13); + var ArrowParameterPlaceHolder = 'ArrowParameterPlaceHolder'; + var Parser = (function () { + function Parser(code, options, delegate) { + if (options === void 0) { options = {}; } + this.config = { + range: (typeof options.range === 'boolean') && options.range, + loc: (typeof options.loc === 'boolean') && options.loc, + source: null, + tokens: (typeof options.tokens === 'boolean') && options.tokens, + comment: (typeof options.comment === 'boolean') && options.comment, + tolerant: (typeof options.tolerant === 'boolean') && options.tolerant + }; + if (this.config.loc && options.source && options.source !== null) { + this.config.source = String(options.source); + } + this.delegate = delegate; + this.errorHandler = new error_handler_1.ErrorHandler(); + this.errorHandler.tolerant = this.config.tolerant; + this.scanner = new scanner_1.Scanner(code, this.errorHandler); + this.scanner.trackComment = this.config.comment; + this.operatorPrecedence = { + ')': 0, + ';': 0, + ',': 0, + '=': 0, + ']': 0, + '||': 1, + '&&': 2, + '|': 3, + '^': 4, + '&': 5, + '==': 6, + '!=': 6, + '===': 6, + '!==': 6, + '<': 7, + '>': 7, + '<=': 7, + '>=': 7, + '<<': 8, + '>>': 8, + '>>>': 8, + '+': 9, + '-': 9, + '*': 11, + '/': 11, + '%': 11 + }; + this.lookahead = { + type: 2 /* EOF */, + value: '', + lineNumber: this.scanner.lineNumber, + lineStart: 0, + start: 0, + end: 0 + }; + this.hasLineTerminator = false; + this.context = { + isModule: false, + await: false, + allowIn: true, + allowStrictDirective: true, + allowYield: true, + firstCoverInitializedNameError: null, + isAssignmentTarget: false, + isBindingElement: false, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + labelSet: {}, + strict: false + }; + this.tokens = []; + this.startMarker = { + index: 0, + line: this.scanner.lineNumber, + column: 0 + }; + this.lastMarker = { + index: 0, + line: this.scanner.lineNumber, + column: 0 + }; + this.nextToken(); + this.lastMarker = { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + } + Parser.prototype.throwError = function (messageFormat) { + var values = []; + for (var _i = 1; _i < arguments.length; _i++) { + values[_i - 1] = arguments[_i]; + } + var args = Array.prototype.slice.call(arguments, 1); + var msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { + assert_1.assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + }); + var index = this.lastMarker.index; + var line = this.lastMarker.line; + var column = this.lastMarker.column + 1; + throw this.errorHandler.createError(index, line, column, msg); + }; + Parser.prototype.tolerateError = function (messageFormat) { + var values = []; + for (var _i = 1; _i < arguments.length; _i++) { + values[_i - 1] = arguments[_i]; + } + var args = Array.prototype.slice.call(arguments, 1); + var msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { + assert_1.assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + }); + var index = this.lastMarker.index; + var line = this.scanner.lineNumber; + var column = this.lastMarker.column + 1; + this.errorHandler.tolerateError(index, line, column, msg); + }; + // Throw an exception because of the token. + Parser.prototype.unexpectedTokenError = function (token, message) { + var msg = message || messages_1.Messages.UnexpectedToken; + var value; + if (token) { + if (!message) { + msg = (token.type === 2 /* EOF */) ? messages_1.Messages.UnexpectedEOS : + (token.type === 3 /* Identifier */) ? messages_1.Messages.UnexpectedIdentifier : + (token.type === 6 /* NumericLiteral */) ? messages_1.Messages.UnexpectedNumber : + (token.type === 8 /* StringLiteral */) ? messages_1.Messages.UnexpectedString : + (token.type === 10 /* Template */) ? messages_1.Messages.UnexpectedTemplate : + messages_1.Messages.UnexpectedToken; + if (token.type === 4 /* Keyword */) { + if (this.scanner.isFutureReservedWord(token.value)) { + msg = messages_1.Messages.UnexpectedReserved; + } + else if (this.context.strict && this.scanner.isStrictModeReservedWord(token.value)) { + msg = messages_1.Messages.StrictReservedWord; + } + } + } + value = token.value; + } + else { + value = 'ILLEGAL'; + } + msg = msg.replace('%0', value); + if (token && typeof token.lineNumber === 'number') { + var index = token.start; + var line = token.lineNumber; + var lastMarkerLineStart = this.lastMarker.index - this.lastMarker.column; + var column = token.start - lastMarkerLineStart + 1; + return this.errorHandler.createError(index, line, column, msg); + } + else { + var index = this.lastMarker.index; + var line = this.lastMarker.line; + var column = this.lastMarker.column + 1; + return this.errorHandler.createError(index, line, column, msg); + } + }; + Parser.prototype.throwUnexpectedToken = function (token, message) { + throw this.unexpectedTokenError(token, message); + }; + Parser.prototype.tolerateUnexpectedToken = function (token, message) { + this.errorHandler.tolerate(this.unexpectedTokenError(token, message)); + }; + Parser.prototype.collectComments = function () { + if (!this.config.comment) { + this.scanner.scanComments(); + } + else { + var comments = this.scanner.scanComments(); + if (comments.length > 0 && this.delegate) { + for (var i = 0; i < comments.length; ++i) { + var e = comments[i]; + var node = void 0; + node = { + type: e.multiLine ? 'BlockComment' : 'LineComment', + value: this.scanner.source.slice(e.slice[0], e.slice[1]) + }; + if (this.config.range) { + node.range = e.range; + } + if (this.config.loc) { + node.loc = e.loc; + } + var metadata = { + start: { + line: e.loc.start.line, + column: e.loc.start.column, + offset: e.range[0] + }, + end: { + line: e.loc.end.line, + column: e.loc.end.column, + offset: e.range[1] + } + }; + this.delegate(node, metadata); + } + } + } + }; + // From internal representation to an external structure + Parser.prototype.getTokenRaw = function (token) { + return this.scanner.source.slice(token.start, token.end); + }; + Parser.prototype.convertToken = function (token) { + var t = { + type: token_1.TokenName[token.type], + value: this.getTokenRaw(token) + }; + if (this.config.range) { + t.range = [token.start, token.end]; + } + if (this.config.loc) { + t.loc = { + start: { + line: this.startMarker.line, + column: this.startMarker.column + }, + end: { + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + } + }; + } + if (token.type === 9 /* RegularExpression */) { + var pattern = token.pattern; + var flags = token.flags; + t.regex = { pattern: pattern, flags: flags }; + } + return t; + }; + Parser.prototype.nextToken = function () { + var token = this.lookahead; + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + this.collectComments(); + if (this.scanner.index !== this.startMarker.index) { + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + } + var next = this.scanner.lex(); + this.hasLineTerminator = (token.lineNumber !== next.lineNumber); + if (next && this.context.strict && next.type === 3 /* Identifier */) { + if (this.scanner.isStrictModeReservedWord(next.value)) { + next.type = 4 /* Keyword */; + } + } + this.lookahead = next; + if (this.config.tokens && next.type !== 2 /* EOF */) { + this.tokens.push(this.convertToken(next)); + } + return token; + }; + Parser.prototype.nextRegexToken = function () { + this.collectComments(); + var token = this.scanner.scanRegExp(); + if (this.config.tokens) { + // Pop the previous token, '/' or '/=' + // This is added from the lookahead token. + this.tokens.pop(); + this.tokens.push(this.convertToken(token)); + } + // Prime the next lookahead. + this.lookahead = token; + this.nextToken(); + return token; + }; + Parser.prototype.createNode = function () { + return { + index: this.startMarker.index, + line: this.startMarker.line, + column: this.startMarker.column + }; + }; + Parser.prototype.startNode = function (token, lastLineStart) { + if (lastLineStart === void 0) { lastLineStart = 0; } + var column = token.start - token.lineStart; + var line = token.lineNumber; + if (column < 0) { + column += lastLineStart; + line--; + } + return { + index: token.start, + line: line, + column: column + }; + }; + Parser.prototype.finalize = function (marker, node) { + if (this.config.range) { + node.range = [marker.index, this.lastMarker.index]; + } + if (this.config.loc) { + node.loc = { + start: { + line: marker.line, + column: marker.column, + }, + end: { + line: this.lastMarker.line, + column: this.lastMarker.column + } + }; + if (this.config.source) { + node.loc.source = this.config.source; + } + } + if (this.delegate) { + var metadata = { + start: { + line: marker.line, + column: marker.column, + offset: marker.index + }, + end: { + line: this.lastMarker.line, + column: this.lastMarker.column, + offset: this.lastMarker.index + } + }; + this.delegate(node, metadata); + } + return node; + }; + // Expect the next token to match the specified punctuator. + // If not, an exception will be thrown. + Parser.prototype.expect = function (value) { + var token = this.nextToken(); + if (token.type !== 7 /* Punctuator */ || token.value !== value) { + this.throwUnexpectedToken(token); + } + }; + // Quietly expect a comma when in tolerant mode, otherwise delegates to expect(). + Parser.prototype.expectCommaSeparator = function () { + if (this.config.tolerant) { + var token = this.lookahead; + if (token.type === 7 /* Punctuator */ && token.value === ',') { + this.nextToken(); + } + else if (token.type === 7 /* Punctuator */ && token.value === ';') { + this.nextToken(); + this.tolerateUnexpectedToken(token); + } + else { + this.tolerateUnexpectedToken(token, messages_1.Messages.UnexpectedToken); + } + } + else { + this.expect(','); + } + }; + // Expect the next token to match the specified keyword. + // If not, an exception will be thrown. + Parser.prototype.expectKeyword = function (keyword) { + var token = this.nextToken(); + if (token.type !== 4 /* Keyword */ || token.value !== keyword) { + this.throwUnexpectedToken(token); + } + }; + // Return true if the next token matches the specified punctuator. + Parser.prototype.match = function (value) { + return this.lookahead.type === 7 /* Punctuator */ && this.lookahead.value === value; + }; + // Return true if the next token matches the specified keyword + Parser.prototype.matchKeyword = function (keyword) { + return this.lookahead.type === 4 /* Keyword */ && this.lookahead.value === keyword; + }; + // Return true if the next token matches the specified contextual keyword + // (where an identifier is sometimes a keyword depending on the context) + Parser.prototype.matchContextualKeyword = function (keyword) { + return this.lookahead.type === 3 /* Identifier */ && this.lookahead.value === keyword; + }; + // Return true if the next token is an assignment operator + Parser.prototype.matchAssign = function () { + if (this.lookahead.type !== 7 /* Punctuator */) { + return false; + } + var op = this.lookahead.value; + return op === '=' || + op === '*=' || + op === '**=' || + op === '/=' || + op === '%=' || + op === '+=' || + op === '-=' || + op === '<<=' || + op === '>>=' || + op === '>>>=' || + op === '&=' || + op === '^=' || + op === '|='; + }; + // Cover grammar support. + // + // When an assignment expression position starts with an left parenthesis, the determination of the type + // of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) + // or the first comma. This situation also defers the determination of all the expressions nested in the pair. + // + // There are three productions that can be parsed in a parentheses pair that needs to be determined + // after the outermost pair is closed. They are: + // + // 1. AssignmentExpression + // 2. BindingElements + // 3. AssignmentTargets + // + // In order to avoid exponential backtracking, we use two flags to denote if the production can be + // binding element or assignment target. + // + // The three productions have the relationship: + // + // BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression + // + // with a single exception that CoverInitializedName when used directly in an Expression, generates + // an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the + // first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. + // + // isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not + // effect the current flags. This means the production the parser parses is only used as an expression. Therefore + // the CoverInitializedName check is conducted. + // + // inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates + // the flags outside of the parser. This means the production the parser parses is used as a part of a potential + // pattern. The CoverInitializedName check is deferred. + Parser.prototype.isolateCoverGrammar = function (parseFunction) { + var previousIsBindingElement = this.context.isBindingElement; + var previousIsAssignmentTarget = this.context.isAssignmentTarget; + var previousFirstCoverInitializedNameError = this.context.firstCoverInitializedNameError; + this.context.isBindingElement = true; + this.context.isAssignmentTarget = true; + this.context.firstCoverInitializedNameError = null; + var result = parseFunction.call(this); + if (this.context.firstCoverInitializedNameError !== null) { + this.throwUnexpectedToken(this.context.firstCoverInitializedNameError); + } + this.context.isBindingElement = previousIsBindingElement; + this.context.isAssignmentTarget = previousIsAssignmentTarget; + this.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError; + return result; + }; + Parser.prototype.inheritCoverGrammar = function (parseFunction) { + var previousIsBindingElement = this.context.isBindingElement; + var previousIsAssignmentTarget = this.context.isAssignmentTarget; + var previousFirstCoverInitializedNameError = this.context.firstCoverInitializedNameError; + this.context.isBindingElement = true; + this.context.isAssignmentTarget = true; + this.context.firstCoverInitializedNameError = null; + var result = parseFunction.call(this); + this.context.isBindingElement = this.context.isBindingElement && previousIsBindingElement; + this.context.isAssignmentTarget = this.context.isAssignmentTarget && previousIsAssignmentTarget; + this.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.context.firstCoverInitializedNameError; + return result; + }; + Parser.prototype.consumeSemicolon = function () { + if (this.match(';')) { + this.nextToken(); + } + else if (!this.hasLineTerminator) { + if (this.lookahead.type !== 2 /* EOF */ && !this.match('}')) { + this.throwUnexpectedToken(this.lookahead); + } + this.lastMarker.index = this.startMarker.index; + this.lastMarker.line = this.startMarker.line; + this.lastMarker.column = this.startMarker.column; + } + }; + // https://tc39.github.io/ecma262/#sec-primary-expression + Parser.prototype.parsePrimaryExpression = function () { + var node = this.createNode(); + var expr; + var token, raw; + switch (this.lookahead.type) { + case 3 /* Identifier */: + if ((this.context.isModule || this.context.await) && this.lookahead.value === 'await') { + this.tolerateUnexpectedToken(this.lookahead); + } + expr = this.matchAsyncFunction() ? this.parseFunctionExpression() : this.finalize(node, new Node.Identifier(this.nextToken().value)); + break; + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + if (this.context.strict && this.lookahead.octal) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.StrictOctalLiteral); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(token.value, raw)); + break; + case 1 /* BooleanLiteral */: + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(token.value === 'true', raw)); + break; + case 5 /* NullLiteral */: + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(null, raw)); + break; + case 10 /* Template */: + expr = this.parseTemplateLiteral(); + break; + case 7 /* Punctuator */: + switch (this.lookahead.value) { + case '(': + this.context.isBindingElement = false; + expr = this.inheritCoverGrammar(this.parseGroupExpression); + break; + case '[': + expr = this.inheritCoverGrammar(this.parseArrayInitializer); + break; + case '{': + expr = this.inheritCoverGrammar(this.parseObjectInitializer); + break; + case '/': + case '/=': + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + this.scanner.index = this.startMarker.index; + token = this.nextRegexToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.RegexLiteral(token.regex, raw, token.pattern, token.flags)); + break; + default: + expr = this.throwUnexpectedToken(this.nextToken()); + } + break; + case 4 /* Keyword */: + if (!this.context.strict && this.context.allowYield && this.matchKeyword('yield')) { + expr = this.parseIdentifierName(); + } + else if (!this.context.strict && this.matchKeyword('let')) { + expr = this.finalize(node, new Node.Identifier(this.nextToken().value)); + } + else { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + if (this.matchKeyword('function')) { + expr = this.parseFunctionExpression(); + } + else if (this.matchKeyword('this')) { + this.nextToken(); + expr = this.finalize(node, new Node.ThisExpression()); + } + else if (this.matchKeyword('class')) { + expr = this.parseClassExpression(); + } + else { + expr = this.throwUnexpectedToken(this.nextToken()); + } + } + break; + default: + expr = this.throwUnexpectedToken(this.nextToken()); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-array-initializer + Parser.prototype.parseSpreadElement = function () { + var node = this.createNode(); + this.expect('...'); + var arg = this.inheritCoverGrammar(this.parseAssignmentExpression); + return this.finalize(node, new Node.SpreadElement(arg)); + }; + Parser.prototype.parseArrayInitializer = function () { + var node = this.createNode(); + var elements = []; + this.expect('['); + while (!this.match(']')) { + if (this.match(',')) { + this.nextToken(); + elements.push(null); + } + else if (this.match('...')) { + var element = this.parseSpreadElement(); + if (!this.match(']')) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + this.expect(','); + } + elements.push(element); + } + else { + elements.push(this.inheritCoverGrammar(this.parseAssignmentExpression)); + if (!this.match(']')) { + this.expect(','); + } + } + } + this.expect(']'); + return this.finalize(node, new Node.ArrayExpression(elements)); + }; + // https://tc39.github.io/ecma262/#sec-object-initializer + Parser.prototype.parsePropertyMethod = function (params) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = params.simple; + var body = this.isolateCoverGrammar(this.parseFunctionSourceElements); + if (this.context.strict && params.firstRestricted) { + this.tolerateUnexpectedToken(params.firstRestricted, params.message); + } + if (this.context.strict && params.stricted) { + this.tolerateUnexpectedToken(params.stricted, params.message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + return body; + }; + Parser.prototype.parsePropertyMethodFunction = function () { + var isGenerator = false; + var node = this.createNode(); + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var params = this.parseFormalParameters(); + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + }; + Parser.prototype.parsePropertyMethodAsyncFunction = function () { + var node = this.createNode(); + var previousAllowYield = this.context.allowYield; + var previousAwait = this.context.await; + this.context.allowYield = false; + this.context.await = true; + var params = this.parseFormalParameters(); + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + this.context.await = previousAwait; + return this.finalize(node, new Node.AsyncFunctionExpression(null, params.params, method)); + }; + Parser.prototype.parseObjectPropertyKey = function () { + var node = this.createNode(); + var token = this.nextToken(); + var key; + switch (token.type) { + case 8 /* StringLiteral */: + case 6 /* NumericLiteral */: + if (this.context.strict && token.octal) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictOctalLiteral); + } + var raw = this.getTokenRaw(token); + key = this.finalize(node, new Node.Literal(token.value, raw)); + break; + case 3 /* Identifier */: + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 4 /* Keyword */: + key = this.finalize(node, new Node.Identifier(token.value)); + break; + case 7 /* Punctuator */: + if (token.value === '[') { + key = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.expect(']'); + } + else { + key = this.throwUnexpectedToken(token); + } + break; + default: + key = this.throwUnexpectedToken(token); + } + return key; + }; + Parser.prototype.isPropertyKey = function (key, value) { + return (key.type === syntax_1.Syntax.Identifier && key.name === value) || + (key.type === syntax_1.Syntax.Literal && key.value === value); + }; + Parser.prototype.parseObjectProperty = function (hasProto) { + var node = this.createNode(); + var token = this.lookahead; + var kind; + var key = null; + var value = null; + var computed = false; + var method = false; + var shorthand = false; + var isAsync = false; + if (token.type === 3 /* Identifier */) { + var id = token.value; + this.nextToken(); + computed = this.match('['); + isAsync = !this.hasLineTerminator && (id === 'async') && + !this.match(':') && !this.match('(') && !this.match('*') && !this.match(','); + key = isAsync ? this.parseObjectPropertyKey() : this.finalize(node, new Node.Identifier(id)); + } + else if (this.match('*')) { + this.nextToken(); + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + } + var lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead); + if (token.type === 3 /* Identifier */ && !isAsync && token.value === 'get' && lookaheadPropertyKey) { + kind = 'get'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.context.allowYield = false; + value = this.parseGetterMethod(); + } + else if (token.type === 3 /* Identifier */ && !isAsync && token.value === 'set' && lookaheadPropertyKey) { + kind = 'set'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseSetterMethod(); + } + else if (token.type === 7 /* Punctuator */ && token.value === '*' && lookaheadPropertyKey) { + kind = 'init'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseGeneratorMethod(); + method = true; + } + else { + if (!key) { + this.throwUnexpectedToken(this.lookahead); + } + kind = 'init'; + if (this.match(':') && !isAsync) { + if (!computed && this.isPropertyKey(key, '__proto__')) { + if (hasProto.value) { + this.tolerateError(messages_1.Messages.DuplicateProtoProperty); + } + hasProto.value = true; + } + this.nextToken(); + value = this.inheritCoverGrammar(this.parseAssignmentExpression); + } + else if (this.match('(')) { + value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction(); + method = true; + } + else if (token.type === 3 /* Identifier */) { + var id = this.finalize(node, new Node.Identifier(token.value)); + if (this.match('=')) { + this.context.firstCoverInitializedNameError = this.lookahead; + this.nextToken(); + shorthand = true; + var init = this.isolateCoverGrammar(this.parseAssignmentExpression); + value = this.finalize(node, new Node.AssignmentPattern(id, init)); + } + else { + shorthand = true; + value = id; + } + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + } + return this.finalize(node, new Node.Property(kind, key, computed, value, method, shorthand)); + }; + Parser.prototype.parseObjectInitializer = function () { + var node = this.createNode(); + this.expect('{'); + var properties = []; + var hasProto = { value: false }; + while (!this.match('}')) { + properties.push(this.parseObjectProperty(hasProto)); + if (!this.match('}')) { + this.expectCommaSeparator(); + } + } + this.expect('}'); + return this.finalize(node, new Node.ObjectExpression(properties)); + }; + // https://tc39.github.io/ecma262/#sec-template-literals + Parser.prototype.parseTemplateHead = function () { + assert_1.assert(this.lookahead.head, 'Template literal must start with a template head'); + var node = this.createNode(); + var token = this.nextToken(); + var raw = token.value; + var cooked = token.cooked; + return this.finalize(node, new Node.TemplateElement({ raw: raw, cooked: cooked }, token.tail)); + }; + Parser.prototype.parseTemplateElement = function () { + if (this.lookahead.type !== 10 /* Template */) { + this.throwUnexpectedToken(); + } + var node = this.createNode(); + var token = this.nextToken(); + var raw = token.value; + var cooked = token.cooked; + return this.finalize(node, new Node.TemplateElement({ raw: raw, cooked: cooked }, token.tail)); + }; + Parser.prototype.parseTemplateLiteral = function () { + var node = this.createNode(); + var expressions = []; + var quasis = []; + var quasi = this.parseTemplateHead(); + quasis.push(quasi); + while (!quasi.tail) { + expressions.push(this.parseExpression()); + quasi = this.parseTemplateElement(); + quasis.push(quasi); + } + return this.finalize(node, new Node.TemplateLiteral(quasis, expressions)); + }; + // https://tc39.github.io/ecma262/#sec-grouping-operator + Parser.prototype.reinterpretExpressionAsPattern = function (expr) { + switch (expr.type) { + case syntax_1.Syntax.Identifier: + case syntax_1.Syntax.MemberExpression: + case syntax_1.Syntax.RestElement: + case syntax_1.Syntax.AssignmentPattern: + break; + case syntax_1.Syntax.SpreadElement: + expr.type = syntax_1.Syntax.RestElement; + this.reinterpretExpressionAsPattern(expr.argument); + break; + case syntax_1.Syntax.ArrayExpression: + expr.type = syntax_1.Syntax.ArrayPattern; + for (var i = 0; i < expr.elements.length; i++) { + if (expr.elements[i] !== null) { + this.reinterpretExpressionAsPattern(expr.elements[i]); + } + } + break; + case syntax_1.Syntax.ObjectExpression: + expr.type = syntax_1.Syntax.ObjectPattern; + for (var i = 0; i < expr.properties.length; i++) { + this.reinterpretExpressionAsPattern(expr.properties[i].value); + } + break; + case syntax_1.Syntax.AssignmentExpression: + expr.type = syntax_1.Syntax.AssignmentPattern; + delete expr.operator; + this.reinterpretExpressionAsPattern(expr.left); + break; + default: + // Allow other node type for tolerant parsing. + break; + } + }; + Parser.prototype.parseGroupExpression = function () { + var expr; + this.expect('('); + if (this.match(')')) { + this.nextToken(); + if (!this.match('=>')) { + this.expect('=>'); + } + expr = { + type: ArrowParameterPlaceHolder, + params: [], + async: false + }; + } + else { + var startToken = this.lookahead; + var params = []; + if (this.match('...')) { + expr = this.parseRestElement(params); + this.expect(')'); + if (!this.match('=>')) { + this.expect('=>'); + } + expr = { + type: ArrowParameterPlaceHolder, + params: [expr], + async: false + }; + } + else { + var arrow = false; + this.context.isBindingElement = true; + expr = this.inheritCoverGrammar(this.parseAssignmentExpression); + if (this.match(',')) { + var expressions = []; + this.context.isAssignmentTarget = false; + expressions.push(expr); + while (this.lookahead.type !== 2 /* EOF */) { + if (!this.match(',')) { + break; + } + this.nextToken(); + if (this.match(')')) { + this.nextToken(); + for (var i = 0; i < expressions.length; i++) { + this.reinterpretExpressionAsPattern(expressions[i]); + } + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: expressions, + async: false + }; + } + else if (this.match('...')) { + if (!this.context.isBindingElement) { + this.throwUnexpectedToken(this.lookahead); + } + expressions.push(this.parseRestElement(params)); + this.expect(')'); + if (!this.match('=>')) { + this.expect('=>'); + } + this.context.isBindingElement = false; + for (var i = 0; i < expressions.length; i++) { + this.reinterpretExpressionAsPattern(expressions[i]); + } + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: expressions, + async: false + }; + } + else { + expressions.push(this.inheritCoverGrammar(this.parseAssignmentExpression)); + } + if (arrow) { + break; + } + } + if (!arrow) { + expr = this.finalize(this.startNode(startToken), new Node.SequenceExpression(expressions)); + } + } + if (!arrow) { + this.expect(')'); + if (this.match('=>')) { + if (expr.type === syntax_1.Syntax.Identifier && expr.name === 'yield') { + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: [expr], + async: false + }; + } + if (!arrow) { + if (!this.context.isBindingElement) { + this.throwUnexpectedToken(this.lookahead); + } + if (expr.type === syntax_1.Syntax.SequenceExpression) { + for (var i = 0; i < expr.expressions.length; i++) { + this.reinterpretExpressionAsPattern(expr.expressions[i]); + } + } + else { + this.reinterpretExpressionAsPattern(expr); + } + var parameters = (expr.type === syntax_1.Syntax.SequenceExpression ? expr.expressions : [expr]); + expr = { + type: ArrowParameterPlaceHolder, + params: parameters, + async: false + }; + } + } + this.context.isBindingElement = false; + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions + Parser.prototype.parseArguments = function () { + this.expect('('); + var args = []; + if (!this.match(')')) { + while (true) { + var expr = this.match('...') ? this.parseSpreadElement() : + this.isolateCoverGrammar(this.parseAssignmentExpression); + args.push(expr); + if (this.match(')')) { + break; + } + this.expectCommaSeparator(); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return args; + }; + Parser.prototype.isIdentifierName = function (token) { + return token.type === 3 /* Identifier */ || + token.type === 4 /* Keyword */ || + token.type === 1 /* BooleanLiteral */ || + token.type === 5 /* NullLiteral */; + }; + Parser.prototype.parseIdentifierName = function () { + var node = this.createNode(); + var token = this.nextToken(); + if (!this.isIdentifierName(token)) { + this.throwUnexpectedToken(token); + } + return this.finalize(node, new Node.Identifier(token.value)); + }; + Parser.prototype.parseNewExpression = function () { + var node = this.createNode(); + var id = this.parseIdentifierName(); + assert_1.assert(id.name === 'new', 'New expression must start with `new`'); + var expr; + if (this.match('.')) { + this.nextToken(); + if (this.lookahead.type === 3 /* Identifier */ && this.context.inFunctionBody && this.lookahead.value === 'target') { + var property = this.parseIdentifierName(); + expr = new Node.MetaProperty(id, property); + } + else { + this.throwUnexpectedToken(this.lookahead); + } + } + else { + var callee = this.isolateCoverGrammar(this.parseLeftHandSideExpression); + var args = this.match('(') ? this.parseArguments() : []; + expr = new Node.NewExpression(callee, args); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + return this.finalize(node, expr); + }; + Parser.prototype.parseAsyncArgument = function () { + var arg = this.parseAssignmentExpression(); + this.context.firstCoverInitializedNameError = null; + return arg; + }; + Parser.prototype.parseAsyncArguments = function () { + this.expect('('); + var args = []; + if (!this.match(')')) { + while (true) { + var expr = this.match('...') ? this.parseSpreadElement() : + this.isolateCoverGrammar(this.parseAsyncArgument); + args.push(expr); + if (this.match(')')) { + break; + } + this.expectCommaSeparator(); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return args; + }; + Parser.prototype.parseLeftHandSideExpressionAllowCall = function () { + var startToken = this.lookahead; + var maybeAsync = this.matchContextualKeyword('async'); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + var expr; + if (this.matchKeyword('super') && this.context.inFunctionBody) { + expr = this.createNode(); + this.nextToken(); + expr = this.finalize(expr, new Node.Super()); + if (!this.match('(') && !this.match('.') && !this.match('[')) { + this.throwUnexpectedToken(this.lookahead); + } + } + else { + expr = this.inheritCoverGrammar(this.matchKeyword('new') ? this.parseNewExpression : this.parsePrimaryExpression); + } + while (true) { + if (this.match('.')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('.'); + var property = this.parseIdentifierName(); + expr = this.finalize(this.startNode(startToken), new Node.StaticMemberExpression(expr, property)); + } + else if (this.match('(')) { + var asyncArrow = maybeAsync && (startToken.lineNumber === this.lookahead.lineNumber); + this.context.isBindingElement = false; + this.context.isAssignmentTarget = false; + var args = asyncArrow ? this.parseAsyncArguments() : this.parseArguments(); + expr = this.finalize(this.startNode(startToken), new Node.CallExpression(expr, args)); + if (asyncArrow && this.match('=>')) { + for (var i = 0; i < args.length; ++i) { + this.reinterpretExpressionAsPattern(args[i]); + } + expr = { + type: ArrowParameterPlaceHolder, + params: args, + async: true + }; + } + } + else if (this.match('[')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('['); + var property = this.isolateCoverGrammar(this.parseExpression); + this.expect(']'); + expr = this.finalize(this.startNode(startToken), new Node.ComputedMemberExpression(expr, property)); + } + else if (this.lookahead.type === 10 /* Template */ && this.lookahead.head) { + var quasi = this.parseTemplateLiteral(); + expr = this.finalize(this.startNode(startToken), new Node.TaggedTemplateExpression(expr, quasi)); + } + else { + break; + } + } + this.context.allowIn = previousAllowIn; + return expr; + }; + Parser.prototype.parseSuper = function () { + var node = this.createNode(); + this.expectKeyword('super'); + if (!this.match('[') && !this.match('.')) { + this.throwUnexpectedToken(this.lookahead); + } + return this.finalize(node, new Node.Super()); + }; + Parser.prototype.parseLeftHandSideExpression = function () { + assert_1.assert(this.context.allowIn, 'callee of new expression always allow in keyword.'); + var node = this.startNode(this.lookahead); + var expr = (this.matchKeyword('super') && this.context.inFunctionBody) ? this.parseSuper() : + this.inheritCoverGrammar(this.matchKeyword('new') ? this.parseNewExpression : this.parsePrimaryExpression); + while (true) { + if (this.match('[')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('['); + var property = this.isolateCoverGrammar(this.parseExpression); + this.expect(']'); + expr = this.finalize(node, new Node.ComputedMemberExpression(expr, property)); + } + else if (this.match('.')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('.'); + var property = this.parseIdentifierName(); + expr = this.finalize(node, new Node.StaticMemberExpression(expr, property)); + } + else if (this.lookahead.type === 10 /* Template */ && this.lookahead.head) { + var quasi = this.parseTemplateLiteral(); + expr = this.finalize(node, new Node.TaggedTemplateExpression(expr, quasi)); + } + else { + break; + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-update-expressions + Parser.prototype.parseUpdateExpression = function () { + var expr; + var startToken = this.lookahead; + if (this.match('++') || this.match('--')) { + var node = this.startNode(startToken); + var token = this.nextToken(); + expr = this.inheritCoverGrammar(this.parseUnaryExpression); + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier && this.scanner.isRestrictedWord(expr.name)) { + this.tolerateError(messages_1.Messages.StrictLHSPrefix); + } + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + var prefix = true; + expr = this.finalize(node, new Node.UpdateExpression(token.value, expr, prefix)); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else { + expr = this.inheritCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + if (!this.hasLineTerminator && this.lookahead.type === 7 /* Punctuator */) { + if (this.match('++') || this.match('--')) { + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier && this.scanner.isRestrictedWord(expr.name)) { + this.tolerateError(messages_1.Messages.StrictLHSPostfix); + } + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var operator = this.nextToken().value; + var prefix = false; + expr = this.finalize(this.startNode(startToken), new Node.UpdateExpression(operator, expr, prefix)); + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-unary-operators + Parser.prototype.parseAwaitExpression = function () { + var node = this.createNode(); + this.nextToken(); + var argument = this.parseUnaryExpression(); + return this.finalize(node, new Node.AwaitExpression(argument)); + }; + Parser.prototype.parseUnaryExpression = function () { + var expr; + if (this.match('+') || this.match('-') || this.match('~') || this.match('!') || + this.matchKeyword('delete') || this.matchKeyword('void') || this.matchKeyword('typeof')) { + var node = this.startNode(this.lookahead); + var token = this.nextToken(); + expr = this.inheritCoverGrammar(this.parseUnaryExpression); + expr = this.finalize(node, new Node.UnaryExpression(token.value, expr)); + if (this.context.strict && expr.operator === 'delete' && expr.argument.type === syntax_1.Syntax.Identifier) { + this.tolerateError(messages_1.Messages.StrictDelete); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else if (this.context.await && this.matchContextualKeyword('await')) { + expr = this.parseAwaitExpression(); + } + else { + expr = this.parseUpdateExpression(); + } + return expr; + }; + Parser.prototype.parseExponentiationExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseUnaryExpression); + if (expr.type !== syntax_1.Syntax.UnaryExpression && this.match('**')) { + this.nextToken(); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var left = expr; + var right = this.isolateCoverGrammar(this.parseExponentiationExpression); + expr = this.finalize(this.startNode(startToken), new Node.BinaryExpression('**', left, right)); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-exp-operator + // https://tc39.github.io/ecma262/#sec-multiplicative-operators + // https://tc39.github.io/ecma262/#sec-additive-operators + // https://tc39.github.io/ecma262/#sec-bitwise-shift-operators + // https://tc39.github.io/ecma262/#sec-relational-operators + // https://tc39.github.io/ecma262/#sec-equality-operators + // https://tc39.github.io/ecma262/#sec-binary-bitwise-operators + // https://tc39.github.io/ecma262/#sec-binary-logical-operators + Parser.prototype.binaryPrecedence = function (token) { + var op = token.value; + var precedence; + if (token.type === 7 /* Punctuator */) { + precedence = this.operatorPrecedence[op] || 0; + } + else if (token.type === 4 /* Keyword */) { + precedence = (op === 'instanceof' || (this.context.allowIn && op === 'in')) ? 7 : 0; + } + else { + precedence = 0; + } + return precedence; + }; + Parser.prototype.parseBinaryExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseExponentiationExpression); + var token = this.lookahead; + var prec = this.binaryPrecedence(token); + if (prec > 0) { + this.nextToken(); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var markers = [startToken, this.lookahead]; + var left = expr; + var right = this.isolateCoverGrammar(this.parseExponentiationExpression); + var stack = [left, token.value, right]; + var precedences = [prec]; + while (true) { + prec = this.binaryPrecedence(this.lookahead); + if (prec <= 0) { + break; + } + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= precedences[precedences.length - 1])) { + right = stack.pop(); + var operator = stack.pop(); + precedences.pop(); + left = stack.pop(); + markers.pop(); + var node = this.startNode(markers[markers.length - 1]); + stack.push(this.finalize(node, new Node.BinaryExpression(operator, left, right))); + } + // Shift. + stack.push(this.nextToken().value); + precedences.push(prec); + markers.push(this.lookahead); + stack.push(this.isolateCoverGrammar(this.parseExponentiationExpression)); + } + // Final reduce to clean-up the stack. + var i = stack.length - 1; + expr = stack[i]; + var lastMarker = markers.pop(); + while (i > 1) { + var marker = markers.pop(); + var lastLineStart = lastMarker && lastMarker.lineStart; + var node = this.startNode(marker, lastLineStart); + var operator = stack[i - 1]; + expr = this.finalize(node, new Node.BinaryExpression(operator, stack[i - 2], expr)); + i -= 2; + lastMarker = marker; + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-conditional-operator + Parser.prototype.parseConditionalExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseBinaryExpression); + if (this.match('?')) { + this.nextToken(); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + var consequent = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.context.allowIn = previousAllowIn; + this.expect(':'); + var alternate = this.isolateCoverGrammar(this.parseAssignmentExpression); + expr = this.finalize(this.startNode(startToken), new Node.ConditionalExpression(expr, consequent, alternate)); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-assignment-operators + Parser.prototype.checkPatternParam = function (options, param) { + switch (param.type) { + case syntax_1.Syntax.Identifier: + this.validateParam(options, param, param.name); + break; + case syntax_1.Syntax.RestElement: + this.checkPatternParam(options, param.argument); + break; + case syntax_1.Syntax.AssignmentPattern: + this.checkPatternParam(options, param.left); + break; + case syntax_1.Syntax.ArrayPattern: + for (var i = 0; i < param.elements.length; i++) { + if (param.elements[i] !== null) { + this.checkPatternParam(options, param.elements[i]); + } + } + break; + case syntax_1.Syntax.ObjectPattern: + for (var i = 0; i < param.properties.length; i++) { + this.checkPatternParam(options, param.properties[i].value); + } + break; + default: + break; + } + options.simple = options.simple && (param instanceof Node.Identifier); + }; + Parser.prototype.reinterpretAsCoverFormalsList = function (expr) { + var params = [expr]; + var options; + var asyncArrow = false; + switch (expr.type) { + case syntax_1.Syntax.Identifier: + break; + case ArrowParameterPlaceHolder: + params = expr.params; + asyncArrow = expr.async; + break; + default: + return null; + } + options = { + simple: true, + paramSet: {} + }; + for (var i = 0; i < params.length; ++i) { + var param = params[i]; + if (param.type === syntax_1.Syntax.AssignmentPattern) { + if (param.right.type === syntax_1.Syntax.YieldExpression) { + if (param.right.argument) { + this.throwUnexpectedToken(this.lookahead); + } + param.right.type = syntax_1.Syntax.Identifier; + param.right.name = 'yield'; + delete param.right.argument; + delete param.right.delegate; + } + } + else if (asyncArrow && param.type === syntax_1.Syntax.Identifier && param.name === 'await') { + this.throwUnexpectedToken(this.lookahead); + } + this.checkPatternParam(options, param); + params[i] = param; + } + if (this.context.strict || !this.context.allowYield) { + for (var i = 0; i < params.length; ++i) { + var param = params[i]; + if (param.type === syntax_1.Syntax.YieldExpression) { + this.throwUnexpectedToken(this.lookahead); + } + } + } + if (options.message === messages_1.Messages.StrictParamDupe) { + var token = this.context.strict ? options.stricted : options.firstRestricted; + this.throwUnexpectedToken(token, options.message); + } + return { + simple: options.simple, + params: params, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + }; + Parser.prototype.parseAssignmentExpression = function () { + var expr; + if (!this.context.allowYield && this.matchKeyword('yield')) { + expr = this.parseYieldExpression(); + } + else { + var startToken = this.lookahead; + var token = startToken; + expr = this.parseConditionalExpression(); + if (token.type === 3 /* Identifier */ && (token.lineNumber === this.lookahead.lineNumber) && token.value === 'async') { + if (this.lookahead.type === 3 /* Identifier */ || this.matchKeyword('yield')) { + var arg = this.parsePrimaryExpression(); + this.reinterpretExpressionAsPattern(arg); + expr = { + type: ArrowParameterPlaceHolder, + params: [arg], + async: true + }; + } + } + if (expr.type === ArrowParameterPlaceHolder || this.match('=>')) { + // https://tc39.github.io/ecma262/#sec-arrow-function-definitions + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var isAsync = expr.async; + var list = this.reinterpretAsCoverFormalsList(expr); + if (list) { + if (this.hasLineTerminator) { + this.tolerateUnexpectedToken(this.lookahead); + } + this.context.firstCoverInitializedNameError = null; + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = list.simple; + var previousAllowYield = this.context.allowYield; + var previousAwait = this.context.await; + this.context.allowYield = true; + this.context.await = isAsync; + var node = this.startNode(startToken); + this.expect('=>'); + var body = void 0; + if (this.match('{')) { + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + body = this.parseFunctionSourceElements(); + this.context.allowIn = previousAllowIn; + } + else { + body = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + var expression = body.type !== syntax_1.Syntax.BlockStatement; + if (this.context.strict && list.firstRestricted) { + this.throwUnexpectedToken(list.firstRestricted, list.message); + } + if (this.context.strict && list.stricted) { + this.tolerateUnexpectedToken(list.stricted, list.message); + } + expr = isAsync ? this.finalize(node, new Node.AsyncArrowFunctionExpression(list.params, body, expression)) : + this.finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression)); + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.allowYield = previousAllowYield; + this.context.await = previousAwait; + } + } + else { + if (this.matchAssign()) { + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier) { + var id = expr; + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictLHSAssignment); + } + if (this.scanner.isStrictModeReservedWord(id.name)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + } + if (!this.match('=')) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else { + this.reinterpretExpressionAsPattern(expr); + } + token = this.nextToken(); + var operator = token.value; + var right = this.isolateCoverGrammar(this.parseAssignmentExpression); + expr = this.finalize(this.startNode(startToken), new Node.AssignmentExpression(operator, expr, right)); + this.context.firstCoverInitializedNameError = null; + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-comma-operator + Parser.prototype.parseExpression = function () { + var startToken = this.lookahead; + var expr = this.isolateCoverGrammar(this.parseAssignmentExpression); + if (this.match(',')) { + var expressions = []; + expressions.push(expr); + while (this.lookahead.type !== 2 /* EOF */) { + if (!this.match(',')) { + break; + } + this.nextToken(); + expressions.push(this.isolateCoverGrammar(this.parseAssignmentExpression)); + } + expr = this.finalize(this.startNode(startToken), new Node.SequenceExpression(expressions)); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-block + Parser.prototype.parseStatementListItem = function () { + var statement; + this.context.isAssignmentTarget = true; + this.context.isBindingElement = true; + if (this.lookahead.type === 4 /* Keyword */) { + switch (this.lookahead.value) { + case 'export': + if (!this.context.isModule) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.IllegalExportDeclaration); + } + statement = this.parseExportDeclaration(); + break; + case 'import': + if (!this.context.isModule) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.IllegalImportDeclaration); + } + statement = this.parseImportDeclaration(); + break; + case 'const': + statement = this.parseLexicalDeclaration({ inFor: false }); + break; + case 'function': + statement = this.parseFunctionDeclaration(); + break; + case 'class': + statement = this.parseClassDeclaration(); + break; + case 'let': + statement = this.isLexicalDeclaration() ? this.parseLexicalDeclaration({ inFor: false }) : this.parseStatement(); + break; + default: + statement = this.parseStatement(); + break; + } + } + else { + statement = this.parseStatement(); + } + return statement; + }; + Parser.prototype.parseBlock = function () { + var node = this.createNode(); + this.expect('{'); + var block = []; + while (true) { + if (this.match('}')) { + break; + } + block.push(this.parseStatementListItem()); + } + this.expect('}'); + return this.finalize(node, new Node.BlockStatement(block)); + }; + // https://tc39.github.io/ecma262/#sec-let-and-const-declarations + Parser.prototype.parseLexicalBinding = function (kind, options) { + var node = this.createNode(); + var params = []; + var id = this.parsePattern(params, kind); + if (this.context.strict && id.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateError(messages_1.Messages.StrictVarName); + } + } + var init = null; + if (kind === 'const') { + if (!this.matchKeyword('in') && !this.matchContextualKeyword('of')) { + if (this.match('=')) { + this.nextToken(); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + else { + this.throwError(messages_1.Messages.DeclarationMissingInitializer, 'const'); + } + } + } + else if ((!options.inFor && id.type !== syntax_1.Syntax.Identifier) || this.match('=')) { + this.expect('='); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + return this.finalize(node, new Node.VariableDeclarator(id, init)); + }; + Parser.prototype.parseBindingList = function (kind, options) { + var list = [this.parseLexicalBinding(kind, options)]; + while (this.match(',')) { + this.nextToken(); + list.push(this.parseLexicalBinding(kind, options)); + } + return list; + }; + Parser.prototype.isLexicalDeclaration = function () { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.scanner.lex(); + this.scanner.restoreState(state); + return (next.type === 3 /* Identifier */) || + (next.type === 7 /* Punctuator */ && next.value === '[') || + (next.type === 7 /* Punctuator */ && next.value === '{') || + (next.type === 4 /* Keyword */ && next.value === 'let') || + (next.type === 4 /* Keyword */ && next.value === 'yield'); + }; + Parser.prototype.parseLexicalDeclaration = function (options) { + var node = this.createNode(); + var kind = this.nextToken().value; + assert_1.assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const'); + var declarations = this.parseBindingList(kind, options); + this.consumeSemicolon(); + return this.finalize(node, new Node.VariableDeclaration(declarations, kind)); + }; + // https://tc39.github.io/ecma262/#sec-destructuring-binding-patterns + Parser.prototype.parseBindingRestElement = function (params, kind) { + var node = this.createNode(); + this.expect('...'); + var arg = this.parsePattern(params, kind); + return this.finalize(node, new Node.RestElement(arg)); + }; + Parser.prototype.parseArrayPattern = function (params, kind) { + var node = this.createNode(); + this.expect('['); + var elements = []; + while (!this.match(']')) { + if (this.match(',')) { + this.nextToken(); + elements.push(null); + } + else { + if (this.match('...')) { + elements.push(this.parseBindingRestElement(params, kind)); + break; + } + else { + elements.push(this.parsePatternWithDefault(params, kind)); + } + if (!this.match(']')) { + this.expect(','); + } + } + } + this.expect(']'); + return this.finalize(node, new Node.ArrayPattern(elements)); + }; + Parser.prototype.parsePropertyPattern = function (params, kind) { + var node = this.createNode(); + var computed = false; + var shorthand = false; + var method = false; + var key; + var value; + if (this.lookahead.type === 3 /* Identifier */) { + var keyToken = this.lookahead; + key = this.parseVariableIdentifier(); + var init = this.finalize(node, new Node.Identifier(keyToken.value)); + if (this.match('=')) { + params.push(keyToken); + shorthand = true; + this.nextToken(); + var expr = this.parseAssignmentExpression(); + value = this.finalize(this.startNode(keyToken), new Node.AssignmentPattern(init, expr)); + } + else if (!this.match(':')) { + params.push(keyToken); + shorthand = true; + value = init; + } + else { + this.expect(':'); + value = this.parsePatternWithDefault(params, kind); + } + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.expect(':'); + value = this.parsePatternWithDefault(params, kind); + } + return this.finalize(node, new Node.Property('init', key, computed, value, method, shorthand)); + }; + Parser.prototype.parseObjectPattern = function (params, kind) { + var node = this.createNode(); + var properties = []; + this.expect('{'); + while (!this.match('}')) { + properties.push(this.parsePropertyPattern(params, kind)); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + return this.finalize(node, new Node.ObjectPattern(properties)); + }; + Parser.prototype.parsePattern = function (params, kind) { + var pattern; + if (this.match('[')) { + pattern = this.parseArrayPattern(params, kind); + } + else if (this.match('{')) { + pattern = this.parseObjectPattern(params, kind); + } + else { + if (this.matchKeyword('let') && (kind === 'const' || kind === 'let')) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.LetInLexicalBinding); + } + params.push(this.lookahead); + pattern = this.parseVariableIdentifier(kind); + } + return pattern; + }; + Parser.prototype.parsePatternWithDefault = function (params, kind) { + var startToken = this.lookahead; + var pattern = this.parsePattern(params, kind); + if (this.match('=')) { + this.nextToken(); + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var right = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.context.allowYield = previousAllowYield; + pattern = this.finalize(this.startNode(startToken), new Node.AssignmentPattern(pattern, right)); + } + return pattern; + }; + // https://tc39.github.io/ecma262/#sec-variable-statement + Parser.prototype.parseVariableIdentifier = function (kind) { + var node = this.createNode(); + var token = this.nextToken(); + if (token.type === 4 /* Keyword */ && token.value === 'yield') { + if (this.context.strict) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + else if (!this.context.allowYield) { + this.throwUnexpectedToken(token); + } + } + else if (token.type !== 3 /* Identifier */) { + if (this.context.strict && token.type === 4 /* Keyword */ && this.scanner.isStrictModeReservedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + else { + if (this.context.strict || token.value !== 'let' || kind !== 'var') { + this.throwUnexpectedToken(token); + } + } + } + else if ((this.context.isModule || this.context.await) && token.type === 3 /* Identifier */ && token.value === 'await') { + this.tolerateUnexpectedToken(token); + } + return this.finalize(node, new Node.Identifier(token.value)); + }; + Parser.prototype.parseVariableDeclaration = function (options) { + var node = this.createNode(); + var params = []; + var id = this.parsePattern(params, 'var'); + if (this.context.strict && id.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateError(messages_1.Messages.StrictVarName); + } + } + var init = null; + if (this.match('=')) { + this.nextToken(); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + else if (id.type !== syntax_1.Syntax.Identifier && !options.inFor) { + this.expect('='); + } + return this.finalize(node, new Node.VariableDeclarator(id, init)); + }; + Parser.prototype.parseVariableDeclarationList = function (options) { + var opt = { inFor: options.inFor }; + var list = []; + list.push(this.parseVariableDeclaration(opt)); + while (this.match(',')) { + this.nextToken(); + list.push(this.parseVariableDeclaration(opt)); + } + return list; + }; + Parser.prototype.parseVariableStatement = function () { + var node = this.createNode(); + this.expectKeyword('var'); + var declarations = this.parseVariableDeclarationList({ inFor: false }); + this.consumeSemicolon(); + return this.finalize(node, new Node.VariableDeclaration(declarations, 'var')); + }; + // https://tc39.github.io/ecma262/#sec-empty-statement + Parser.prototype.parseEmptyStatement = function () { + var node = this.createNode(); + this.expect(';'); + return this.finalize(node, new Node.EmptyStatement()); + }; + // https://tc39.github.io/ecma262/#sec-expression-statement + Parser.prototype.parseExpressionStatement = function () { + var node = this.createNode(); + var expr = this.parseExpression(); + this.consumeSemicolon(); + return this.finalize(node, new Node.ExpressionStatement(expr)); + }; + // https://tc39.github.io/ecma262/#sec-if-statement + Parser.prototype.parseIfClause = function () { + if (this.context.strict && this.matchKeyword('function')) { + this.tolerateError(messages_1.Messages.StrictFunction); + } + return this.parseStatement(); + }; + Parser.prototype.parseIfStatement = function () { + var node = this.createNode(); + var consequent; + var alternate = null; + this.expectKeyword('if'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + consequent = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + consequent = this.parseIfClause(); + if (this.matchKeyword('else')) { + this.nextToken(); + alternate = this.parseIfClause(); + } + } + return this.finalize(node, new Node.IfStatement(test, consequent, alternate)); + }; + // https://tc39.github.io/ecma262/#sec-do-while-statement + Parser.prototype.parseDoWhileStatement = function () { + var node = this.createNode(); + this.expectKeyword('do'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + var body = this.parseStatement(); + this.context.inIteration = previousInIteration; + this.expectKeyword('while'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + } + else { + this.expect(')'); + if (this.match(';')) { + this.nextToken(); + } + } + return this.finalize(node, new Node.DoWhileStatement(body, test)); + }; + // https://tc39.github.io/ecma262/#sec-while-statement + Parser.prototype.parseWhileStatement = function () { + var node = this.createNode(); + var body; + this.expectKeyword('while'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + body = this.parseStatement(); + this.context.inIteration = previousInIteration; + } + return this.finalize(node, new Node.WhileStatement(test, body)); + }; + // https://tc39.github.io/ecma262/#sec-for-statement + // https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements + Parser.prototype.parseForStatement = function () { + var init = null; + var test = null; + var update = null; + var forIn = true; + var left, right; + var node = this.createNode(); + this.expectKeyword('for'); + this.expect('('); + if (this.match(';')) { + this.nextToken(); + } + else { + if (this.matchKeyword('var')) { + init = this.createNode(); + this.nextToken(); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + var declarations = this.parseVariableDeclarationList({ inFor: true }); + this.context.allowIn = previousAllowIn; + if (declarations.length === 1 && this.matchKeyword('in')) { + var decl = declarations[0]; + if (decl.init && (decl.id.type === syntax_1.Syntax.ArrayPattern || decl.id.type === syntax_1.Syntax.ObjectPattern || this.context.strict)) { + this.tolerateError(messages_1.Messages.ForInOfLoopInitializer, 'for-in'); + } + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else if (declarations.length === 1 && declarations[0].init === null && this.matchContextualKeyword('of')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.nextToken(); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.expect(';'); + } + } + else if (this.matchKeyword('const') || this.matchKeyword('let')) { + init = this.createNode(); + var kind = this.nextToken().value; + if (!this.context.strict && this.lookahead.value === 'in') { + init = this.finalize(init, new Node.Identifier(kind)); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else { + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + var declarations = this.parseBindingList(kind, { inFor: true }); + this.context.allowIn = previousAllowIn; + if (declarations.length === 1 && declarations[0].init === null && this.matchKeyword('in')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else if (declarations.length === 1 && declarations[0].init === null && this.matchContextualKeyword('of')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + this.nextToken(); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + this.consumeSemicolon(); + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + } + } + } + else { + var initStartToken = this.lookahead; + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + init = this.inheritCoverGrammar(this.parseAssignmentExpression); + this.context.allowIn = previousAllowIn; + if (this.matchKeyword('in')) { + if (!this.context.isAssignmentTarget || init.type === syntax_1.Syntax.AssignmentExpression) { + this.tolerateError(messages_1.Messages.InvalidLHSInForIn); + } + this.nextToken(); + this.reinterpretExpressionAsPattern(init); + left = init; + right = this.parseExpression(); + init = null; + } + else if (this.matchContextualKeyword('of')) { + if (!this.context.isAssignmentTarget || init.type === syntax_1.Syntax.AssignmentExpression) { + this.tolerateError(messages_1.Messages.InvalidLHSInForLoop); + } + this.nextToken(); + this.reinterpretExpressionAsPattern(init); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + if (this.match(',')) { + var initSeq = [init]; + while (this.match(',')) { + this.nextToken(); + initSeq.push(this.isolateCoverGrammar(this.parseAssignmentExpression)); + } + init = this.finalize(this.startNode(initStartToken), new Node.SequenceExpression(initSeq)); + } + this.expect(';'); + } + } + } + if (typeof left === 'undefined') { + if (!this.match(';')) { + test = this.parseExpression(); + } + this.expect(';'); + if (!this.match(')')) { + update = this.parseExpression(); + } + } + var body; + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + body = this.isolateCoverGrammar(this.parseStatement); + this.context.inIteration = previousInIteration; + } + return (typeof left === 'undefined') ? + this.finalize(node, new Node.ForStatement(init, test, update, body)) : + forIn ? this.finalize(node, new Node.ForInStatement(left, right, body)) : + this.finalize(node, new Node.ForOfStatement(left, right, body)); + }; + // https://tc39.github.io/ecma262/#sec-continue-statement + Parser.prototype.parseContinueStatement = function () { + var node = this.createNode(); + this.expectKeyword('continue'); + var label = null; + if (this.lookahead.type === 3 /* Identifier */ && !this.hasLineTerminator) { + var id = this.parseVariableIdentifier(); + label = id; + var key = '$' + id.name; + if (!Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.UnknownLabel, id.name); + } + } + this.consumeSemicolon(); + if (label === null && !this.context.inIteration) { + this.throwError(messages_1.Messages.IllegalContinue); + } + return this.finalize(node, new Node.ContinueStatement(label)); + }; + // https://tc39.github.io/ecma262/#sec-break-statement + Parser.prototype.parseBreakStatement = function () { + var node = this.createNode(); + this.expectKeyword('break'); + var label = null; + if (this.lookahead.type === 3 /* Identifier */ && !this.hasLineTerminator) { + var id = this.parseVariableIdentifier(); + var key = '$' + id.name; + if (!Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.UnknownLabel, id.name); + } + label = id; + } + this.consumeSemicolon(); + if (label === null && !this.context.inIteration && !this.context.inSwitch) { + this.throwError(messages_1.Messages.IllegalBreak); + } + return this.finalize(node, new Node.BreakStatement(label)); + }; + // https://tc39.github.io/ecma262/#sec-return-statement + Parser.prototype.parseReturnStatement = function () { + if (!this.context.inFunctionBody) { + this.tolerateError(messages_1.Messages.IllegalReturn); + } + var node = this.createNode(); + this.expectKeyword('return'); + var hasArgument = (!this.match(';') && !this.match('}') && + !this.hasLineTerminator && this.lookahead.type !== 2 /* EOF */) || + this.lookahead.type === 8 /* StringLiteral */ || + this.lookahead.type === 10 /* Template */; + var argument = hasArgument ? this.parseExpression() : null; + this.consumeSemicolon(); + return this.finalize(node, new Node.ReturnStatement(argument)); + }; + // https://tc39.github.io/ecma262/#sec-with-statement + Parser.prototype.parseWithStatement = function () { + if (this.context.strict) { + this.tolerateError(messages_1.Messages.StrictModeWith); + } + var node = this.createNode(); + var body; + this.expectKeyword('with'); + this.expect('('); + var object = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + body = this.parseStatement(); + } + return this.finalize(node, new Node.WithStatement(object, body)); + }; + // https://tc39.github.io/ecma262/#sec-switch-statement + Parser.prototype.parseSwitchCase = function () { + var node = this.createNode(); + var test; + if (this.matchKeyword('default')) { + this.nextToken(); + test = null; + } + else { + this.expectKeyword('case'); + test = this.parseExpression(); + } + this.expect(':'); + var consequent = []; + while (true) { + if (this.match('}') || this.matchKeyword('default') || this.matchKeyword('case')) { + break; + } + consequent.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.SwitchCase(test, consequent)); + }; + Parser.prototype.parseSwitchStatement = function () { + var node = this.createNode(); + this.expectKeyword('switch'); + this.expect('('); + var discriminant = this.parseExpression(); + this.expect(')'); + var previousInSwitch = this.context.inSwitch; + this.context.inSwitch = true; + var cases = []; + var defaultFound = false; + this.expect('{'); + while (true) { + if (this.match('}')) { + break; + } + var clause = this.parseSwitchCase(); + if (clause.test === null) { + if (defaultFound) { + this.throwError(messages_1.Messages.MultipleDefaultsInSwitch); + } + defaultFound = true; + } + cases.push(clause); + } + this.expect('}'); + this.context.inSwitch = previousInSwitch; + return this.finalize(node, new Node.SwitchStatement(discriminant, cases)); + }; + // https://tc39.github.io/ecma262/#sec-labelled-statements + Parser.prototype.parseLabelledStatement = function () { + var node = this.createNode(); + var expr = this.parseExpression(); + var statement; + if ((expr.type === syntax_1.Syntax.Identifier) && this.match(':')) { + this.nextToken(); + var id = expr; + var key = '$' + id.name; + if (Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.Redeclaration, 'Label', id.name); + } + this.context.labelSet[key] = true; + var body = void 0; + if (this.matchKeyword('class')) { + this.tolerateUnexpectedToken(this.lookahead); + body = this.parseClassDeclaration(); + } + else if (this.matchKeyword('function')) { + var token = this.lookahead; + var declaration = this.parseFunctionDeclaration(); + if (this.context.strict) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunction); + } + else if (declaration.generator) { + this.tolerateUnexpectedToken(token, messages_1.Messages.GeneratorInLegacyContext); + } + body = declaration; + } + else { + body = this.parseStatement(); + } + delete this.context.labelSet[key]; + statement = new Node.LabeledStatement(id, body); + } + else { + this.consumeSemicolon(); + statement = new Node.ExpressionStatement(expr); + } + return this.finalize(node, statement); + }; + // https://tc39.github.io/ecma262/#sec-throw-statement + Parser.prototype.parseThrowStatement = function () { + var node = this.createNode(); + this.expectKeyword('throw'); + if (this.hasLineTerminator) { + this.throwError(messages_1.Messages.NewlineAfterThrow); + } + var argument = this.parseExpression(); + this.consumeSemicolon(); + return this.finalize(node, new Node.ThrowStatement(argument)); + }; + // https://tc39.github.io/ecma262/#sec-try-statement + Parser.prototype.parseCatchClause = function () { + var node = this.createNode(); + this.expectKeyword('catch'); + this.expect('('); + if (this.match(')')) { + this.throwUnexpectedToken(this.lookahead); + } + var params = []; + var param = this.parsePattern(params); + var paramMap = {}; + for (var i = 0; i < params.length; i++) { + var key = '$' + params[i].value; + if (Object.prototype.hasOwnProperty.call(paramMap, key)) { + this.tolerateError(messages_1.Messages.DuplicateBinding, params[i].value); + } + paramMap[key] = true; + } + if (this.context.strict && param.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(param.name)) { + this.tolerateError(messages_1.Messages.StrictCatchVariable); + } + } + this.expect(')'); + var body = this.parseBlock(); + return this.finalize(node, new Node.CatchClause(param, body)); + }; + Parser.prototype.parseFinallyClause = function () { + this.expectKeyword('finally'); + return this.parseBlock(); + }; + Parser.prototype.parseTryStatement = function () { + var node = this.createNode(); + this.expectKeyword('try'); + var block = this.parseBlock(); + var handler = this.matchKeyword('catch') ? this.parseCatchClause() : null; + var finalizer = this.matchKeyword('finally') ? this.parseFinallyClause() : null; + if (!handler && !finalizer) { + this.throwError(messages_1.Messages.NoCatchOrFinally); + } + return this.finalize(node, new Node.TryStatement(block, handler, finalizer)); + }; + // https://tc39.github.io/ecma262/#sec-debugger-statement + Parser.prototype.parseDebuggerStatement = function () { + var node = this.createNode(); + this.expectKeyword('debugger'); + this.consumeSemicolon(); + return this.finalize(node, new Node.DebuggerStatement()); + }; + // https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations + Parser.prototype.parseStatement = function () { + var statement; + switch (this.lookahead.type) { + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + case 10 /* Template */: + case 9 /* RegularExpression */: + statement = this.parseExpressionStatement(); + break; + case 7 /* Punctuator */: + var value = this.lookahead.value; + if (value === '{') { + statement = this.parseBlock(); + } + else if (value === '(') { + statement = this.parseExpressionStatement(); + } + else if (value === ';') { + statement = this.parseEmptyStatement(); + } + else { + statement = this.parseExpressionStatement(); + } + break; + case 3 /* Identifier */: + statement = this.matchAsyncFunction() ? this.parseFunctionDeclaration() : this.parseLabelledStatement(); + break; + case 4 /* Keyword */: + switch (this.lookahead.value) { + case 'break': + statement = this.parseBreakStatement(); + break; + case 'continue': + statement = this.parseContinueStatement(); + break; + case 'debugger': + statement = this.parseDebuggerStatement(); + break; + case 'do': + statement = this.parseDoWhileStatement(); + break; + case 'for': + statement = this.parseForStatement(); + break; + case 'function': + statement = this.parseFunctionDeclaration(); + break; + case 'if': + statement = this.parseIfStatement(); + break; + case 'return': + statement = this.parseReturnStatement(); + break; + case 'switch': + statement = this.parseSwitchStatement(); + break; + case 'throw': + statement = this.parseThrowStatement(); + break; + case 'try': + statement = this.parseTryStatement(); + break; + case 'var': + statement = this.parseVariableStatement(); + break; + case 'while': + statement = this.parseWhileStatement(); + break; + case 'with': + statement = this.parseWithStatement(); + break; + default: + statement = this.parseExpressionStatement(); + break; + } + break; + default: + statement = this.throwUnexpectedToken(this.lookahead); + } + return statement; + }; + // https://tc39.github.io/ecma262/#sec-function-definitions + Parser.prototype.parseFunctionSourceElements = function () { + var node = this.createNode(); + this.expect('{'); + var body = this.parseDirectivePrologues(); + var previousLabelSet = this.context.labelSet; + var previousInIteration = this.context.inIteration; + var previousInSwitch = this.context.inSwitch; + var previousInFunctionBody = this.context.inFunctionBody; + this.context.labelSet = {}; + this.context.inIteration = false; + this.context.inSwitch = false; + this.context.inFunctionBody = true; + while (this.lookahead.type !== 2 /* EOF */) { + if (this.match('}')) { + break; + } + body.push(this.parseStatementListItem()); + } + this.expect('}'); + this.context.labelSet = previousLabelSet; + this.context.inIteration = previousInIteration; + this.context.inSwitch = previousInSwitch; + this.context.inFunctionBody = previousInFunctionBody; + return this.finalize(node, new Node.BlockStatement(body)); + }; + Parser.prototype.validateParam = function (options, param, name) { + var key = '$' + name; + if (this.context.strict) { + if (this.scanner.isRestrictedWord(name)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamName; + } + if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamDupe; + } + } + else if (!options.firstRestricted) { + if (this.scanner.isRestrictedWord(name)) { + options.firstRestricted = param; + options.message = messages_1.Messages.StrictParamName; + } + else if (this.scanner.isStrictModeReservedWord(name)) { + options.firstRestricted = param; + options.message = messages_1.Messages.StrictReservedWord; + } + else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamDupe; + } + } + /* istanbul ignore next */ + if (typeof Object.defineProperty === 'function') { + Object.defineProperty(options.paramSet, key, { value: true, enumerable: true, writable: true, configurable: true }); + } + else { + options.paramSet[key] = true; + } + }; + Parser.prototype.parseRestElement = function (params) { + var node = this.createNode(); + this.expect('...'); + var arg = this.parsePattern(params); + if (this.match('=')) { + this.throwError(messages_1.Messages.DefaultRestParameter); + } + if (!this.match(')')) { + this.throwError(messages_1.Messages.ParameterAfterRestParameter); + } + return this.finalize(node, new Node.RestElement(arg)); + }; + Parser.prototype.parseFormalParameter = function (options) { + var params = []; + var param = this.match('...') ? this.parseRestElement(params) : this.parsePatternWithDefault(params); + for (var i = 0; i < params.length; i++) { + this.validateParam(options, params[i], params[i].value); + } + options.simple = options.simple && (param instanceof Node.Identifier); + options.params.push(param); + }; + Parser.prototype.parseFormalParameters = function (firstRestricted) { + var options; + options = { + simple: true, + params: [], + firstRestricted: firstRestricted + }; + this.expect('('); + if (!this.match(')')) { + options.paramSet = {}; + while (this.lookahead.type !== 2 /* EOF */) { + this.parseFormalParameter(options); + if (this.match(')')) { + break; + } + this.expect(','); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return { + simple: options.simple, + params: options.params, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + }; + Parser.prototype.matchAsyncFunction = function () { + var match = this.matchContextualKeyword('async'); + if (match) { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.scanner.lex(); + this.scanner.restoreState(state); + match = (state.lineNumber === next.lineNumber) && (next.type === 4 /* Keyword */) && (next.value === 'function'); + } + return match; + }; + Parser.prototype.parseFunctionDeclaration = function (identifierIsOptional) { + var node = this.createNode(); + var isAsync = this.matchContextualKeyword('async'); + if (isAsync) { + this.nextToken(); + } + this.expectKeyword('function'); + var isGenerator = isAsync ? false : this.match('*'); + if (isGenerator) { + this.nextToken(); + } + var message; + var id = null; + var firstRestricted = null; + if (!identifierIsOptional || !this.match('(')) { + var token = this.lookahead; + id = this.parseVariableIdentifier(); + if (this.context.strict) { + if (this.scanner.isRestrictedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunctionName); + } + } + else { + if (this.scanner.isRestrictedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictFunctionName; + } + else if (this.scanner.isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictReservedWord; + } + } + } + var previousAllowAwait = this.context.await; + var previousAllowYield = this.context.allowYield; + this.context.await = isAsync; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(firstRestricted); + var params = formalParameters.params; + var stricted = formalParameters.stricted; + firstRestricted = formalParameters.firstRestricted; + if (formalParameters.message) { + message = formalParameters.message; + } + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = formalParameters.simple; + var body = this.parseFunctionSourceElements(); + if (this.context.strict && firstRestricted) { + this.throwUnexpectedToken(firstRestricted, message); + } + if (this.context.strict && stricted) { + this.tolerateUnexpectedToken(stricted, message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.await = previousAllowAwait; + this.context.allowYield = previousAllowYield; + return isAsync ? this.finalize(node, new Node.AsyncFunctionDeclaration(id, params, body)) : + this.finalize(node, new Node.FunctionDeclaration(id, params, body, isGenerator)); + }; + Parser.prototype.parseFunctionExpression = function () { + var node = this.createNode(); + var isAsync = this.matchContextualKeyword('async'); + if (isAsync) { + this.nextToken(); + } + this.expectKeyword('function'); + var isGenerator = isAsync ? false : this.match('*'); + if (isGenerator) { + this.nextToken(); + } + var message; + var id = null; + var firstRestricted; + var previousAllowAwait = this.context.await; + var previousAllowYield = this.context.allowYield; + this.context.await = isAsync; + this.context.allowYield = !isGenerator; + if (!this.match('(')) { + var token = this.lookahead; + id = (!this.context.strict && !isGenerator && this.matchKeyword('yield')) ? this.parseIdentifierName() : this.parseVariableIdentifier(); + if (this.context.strict) { + if (this.scanner.isRestrictedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunctionName); + } + } + else { + if (this.scanner.isRestrictedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictFunctionName; + } + else if (this.scanner.isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictReservedWord; + } + } + } + var formalParameters = this.parseFormalParameters(firstRestricted); + var params = formalParameters.params; + var stricted = formalParameters.stricted; + firstRestricted = formalParameters.firstRestricted; + if (formalParameters.message) { + message = formalParameters.message; + } + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = formalParameters.simple; + var body = this.parseFunctionSourceElements(); + if (this.context.strict && firstRestricted) { + this.throwUnexpectedToken(firstRestricted, message); + } + if (this.context.strict && stricted) { + this.tolerateUnexpectedToken(stricted, message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.await = previousAllowAwait; + this.context.allowYield = previousAllowYield; + return isAsync ? this.finalize(node, new Node.AsyncFunctionExpression(id, params, body)) : + this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator)); + }; + // https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive + Parser.prototype.parseDirective = function () { + var token = this.lookahead; + var node = this.createNode(); + var expr = this.parseExpression(); + var directive = (expr.type === syntax_1.Syntax.Literal) ? this.getTokenRaw(token).slice(1, -1) : null; + this.consumeSemicolon(); + return this.finalize(node, directive ? new Node.Directive(expr, directive) : new Node.ExpressionStatement(expr)); + }; + Parser.prototype.parseDirectivePrologues = function () { + var firstRestricted = null; + var body = []; + while (true) { + var token = this.lookahead; + if (token.type !== 8 /* StringLiteral */) { + break; + } + var statement = this.parseDirective(); + body.push(statement); + var directive = statement.directive; + if (typeof directive !== 'string') { + break; + } + if (directive === 'use strict') { + this.context.strict = true; + if (firstRestricted) { + this.tolerateUnexpectedToken(firstRestricted, messages_1.Messages.StrictOctalLiteral); + } + if (!this.context.allowStrictDirective) { + this.tolerateUnexpectedToken(token, messages_1.Messages.IllegalLanguageModeDirective); + } + } + else { + if (!firstRestricted && token.octal) { + firstRestricted = token; + } + } + } + return body; + }; + // https://tc39.github.io/ecma262/#sec-method-definitions + Parser.prototype.qualifiedPropertyName = function (token) { + switch (token.type) { + case 3 /* Identifier */: + case 8 /* StringLiteral */: + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 4 /* Keyword */: + return true; + case 7 /* Punctuator */: + return token.value === '['; + default: + break; + } + return false; + }; + Parser.prototype.parseGetterMethod = function () { + var node = this.createNode(); + var isGenerator = false; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(); + if (formalParameters.params.length > 0) { + this.tolerateError(messages_1.Messages.BadGetterArity); + } + var method = this.parsePropertyMethod(formalParameters); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + }; + Parser.prototype.parseSetterMethod = function () { + var node = this.createNode(); + var isGenerator = false; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(); + if (formalParameters.params.length !== 1) { + this.tolerateError(messages_1.Messages.BadSetterArity); + } + else if (formalParameters.params[0] instanceof Node.RestElement) { + this.tolerateError(messages_1.Messages.BadSetterRestParameter); + } + var method = this.parsePropertyMethod(formalParameters); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + }; + Parser.prototype.parseGeneratorMethod = function () { + var node = this.createNode(); + var isGenerator = true; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var params = this.parseFormalParameters(); + this.context.allowYield = false; + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + }; + // https://tc39.github.io/ecma262/#sec-generator-function-definitions + Parser.prototype.isStartOfExpression = function () { + var start = true; + var value = this.lookahead.value; + switch (this.lookahead.type) { + case 7 /* Punctuator */: + start = (value === '[') || (value === '(') || (value === '{') || + (value === '+') || (value === '-') || + (value === '!') || (value === '~') || + (value === '++') || (value === '--') || + (value === '/') || (value === '/='); // regular expression literal + break; + case 4 /* Keyword */: + start = (value === 'class') || (value === 'delete') || + (value === 'function') || (value === 'let') || (value === 'new') || + (value === 'super') || (value === 'this') || (value === 'typeof') || + (value === 'void') || (value === 'yield'); + break; + default: + break; + } + return start; + }; + Parser.prototype.parseYieldExpression = function () { + var node = this.createNode(); + this.expectKeyword('yield'); + var argument = null; + var delegate = false; + if (!this.hasLineTerminator) { + var previousAllowYield = this.context.allowYield; + this.context.allowYield = false; + delegate = this.match('*'); + if (delegate) { + this.nextToken(); + argument = this.parseAssignmentExpression(); + } + else if (this.isStartOfExpression()) { + argument = this.parseAssignmentExpression(); + } + this.context.allowYield = previousAllowYield; + } + return this.finalize(node, new Node.YieldExpression(argument, delegate)); + }; + // https://tc39.github.io/ecma262/#sec-class-definitions + Parser.prototype.parseClassElement = function (hasConstructor) { + var token = this.lookahead; + var node = this.createNode(); + var kind = ''; + var key = null; + var value = null; + var computed = false; + var method = false; + var isStatic = false; + var isAsync = false; + if (this.match('*')) { + this.nextToken(); + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + var id = key; + if (id.name === 'static' && (this.qualifiedPropertyName(this.lookahead) || this.match('*'))) { + token = this.lookahead; + isStatic = true; + computed = this.match('['); + if (this.match('*')) { + this.nextToken(); + } + else { + key = this.parseObjectPropertyKey(); + } + } + if ((token.type === 3 /* Identifier */) && !this.hasLineTerminator && (token.value === 'async')) { + var punctuator = this.lookahead.value; + if (punctuator !== ':' && punctuator !== '(' && punctuator !== '*') { + isAsync = true; + token = this.lookahead; + key = this.parseObjectPropertyKey(); + if (token.type === 3 /* Identifier */ && token.value === 'constructor') { + this.tolerateUnexpectedToken(token, messages_1.Messages.ConstructorIsAsync); + } + } + } + } + var lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead); + if (token.type === 3 /* Identifier */) { + if (token.value === 'get' && lookaheadPropertyKey) { + kind = 'get'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.context.allowYield = false; + value = this.parseGetterMethod(); + } + else if (token.value === 'set' && lookaheadPropertyKey) { + kind = 'set'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseSetterMethod(); + } + } + else if (token.type === 7 /* Punctuator */ && token.value === '*' && lookaheadPropertyKey) { + kind = 'init'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseGeneratorMethod(); + method = true; + } + if (!kind && key && this.match('(')) { + kind = 'init'; + value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction(); + method = true; + } + if (!kind) { + this.throwUnexpectedToken(this.lookahead); + } + if (kind === 'init') { + kind = 'method'; + } + if (!computed) { + if (isStatic && this.isPropertyKey(key, 'prototype')) { + this.throwUnexpectedToken(token, messages_1.Messages.StaticPrototype); + } + if (!isStatic && this.isPropertyKey(key, 'constructor')) { + if (kind !== 'method' || !method || (value && value.generator)) { + this.throwUnexpectedToken(token, messages_1.Messages.ConstructorSpecialMethod); + } + if (hasConstructor.value) { + this.throwUnexpectedToken(token, messages_1.Messages.DuplicateConstructor); + } + else { + hasConstructor.value = true; + } + kind = 'constructor'; + } + } + return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic)); + }; + Parser.prototype.parseClassElementList = function () { + var body = []; + var hasConstructor = { value: false }; + this.expect('{'); + while (!this.match('}')) { + if (this.match(';')) { + this.nextToken(); + } + else { + body.push(this.parseClassElement(hasConstructor)); + } + } + this.expect('}'); + return body; + }; + Parser.prototype.parseClassBody = function () { + var node = this.createNode(); + var elementList = this.parseClassElementList(); + return this.finalize(node, new Node.ClassBody(elementList)); + }; + Parser.prototype.parseClassDeclaration = function (identifierIsOptional) { + var node = this.createNode(); + var previousStrict = this.context.strict; + this.context.strict = true; + this.expectKeyword('class'); + var id = (identifierIsOptional && (this.lookahead.type !== 3 /* Identifier */)) ? null : this.parseVariableIdentifier(); + var superClass = null; + if (this.matchKeyword('extends')) { + this.nextToken(); + superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + } + var classBody = this.parseClassBody(); + this.context.strict = previousStrict; + return this.finalize(node, new Node.ClassDeclaration(id, superClass, classBody)); + }; + Parser.prototype.parseClassExpression = function () { + var node = this.createNode(); + var previousStrict = this.context.strict; + this.context.strict = true; + this.expectKeyword('class'); + var id = (this.lookahead.type === 3 /* Identifier */) ? this.parseVariableIdentifier() : null; + var superClass = null; + if (this.matchKeyword('extends')) { + this.nextToken(); + superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + } + var classBody = this.parseClassBody(); + this.context.strict = previousStrict; + return this.finalize(node, new Node.ClassExpression(id, superClass, classBody)); + }; + // https://tc39.github.io/ecma262/#sec-scripts + // https://tc39.github.io/ecma262/#sec-modules + Parser.prototype.parseModule = function () { + this.context.strict = true; + this.context.isModule = true; + this.scanner.isModule = true; + var node = this.createNode(); + var body = this.parseDirectivePrologues(); + while (this.lookahead.type !== 2 /* EOF */) { + body.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.Module(body)); + }; + Parser.prototype.parseScript = function () { + var node = this.createNode(); + var body = this.parseDirectivePrologues(); + while (this.lookahead.type !== 2 /* EOF */) { + body.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.Script(body)); + }; + // https://tc39.github.io/ecma262/#sec-imports + Parser.prototype.parseModuleSpecifier = function () { + var node = this.createNode(); + if (this.lookahead.type !== 8 /* StringLiteral */) { + this.throwError(messages_1.Messages.InvalidModuleSpecifier); + } + var token = this.nextToken(); + var raw = this.getTokenRaw(token); + return this.finalize(node, new Node.Literal(token.value, raw)); + }; + // import {} ...; + Parser.prototype.parseImportSpecifier = function () { + var node = this.createNode(); + var imported; + var local; + if (this.lookahead.type === 3 /* Identifier */) { + imported = this.parseVariableIdentifier(); + local = imported; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + local = this.parseVariableIdentifier(); + } + } + else { + imported = this.parseIdentifierName(); + local = imported; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + local = this.parseVariableIdentifier(); + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + } + return this.finalize(node, new Node.ImportSpecifier(local, imported)); + }; + // {foo, bar as bas} + Parser.prototype.parseNamedImports = function () { + this.expect('{'); + var specifiers = []; + while (!this.match('}')) { + specifiers.push(this.parseImportSpecifier()); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + return specifiers; + }; + // import ...; + Parser.prototype.parseImportDefaultSpecifier = function () { + var node = this.createNode(); + var local = this.parseIdentifierName(); + return this.finalize(node, new Node.ImportDefaultSpecifier(local)); + }; + // import <* as foo> ...; + Parser.prototype.parseImportNamespaceSpecifier = function () { + var node = this.createNode(); + this.expect('*'); + if (!this.matchContextualKeyword('as')) { + this.throwError(messages_1.Messages.NoAsAfterImportNamespace); + } + this.nextToken(); + var local = this.parseIdentifierName(); + return this.finalize(node, new Node.ImportNamespaceSpecifier(local)); + }; + Parser.prototype.parseImportDeclaration = function () { + if (this.context.inFunctionBody) { + this.throwError(messages_1.Messages.IllegalImportDeclaration); + } + var node = this.createNode(); + this.expectKeyword('import'); + var src; + var specifiers = []; + if (this.lookahead.type === 8 /* StringLiteral */) { + // import 'foo'; + src = this.parseModuleSpecifier(); + } + else { + if (this.match('{')) { + // import {bar} + specifiers = specifiers.concat(this.parseNamedImports()); + } + else if (this.match('*')) { + // import * as foo + specifiers.push(this.parseImportNamespaceSpecifier()); + } + else if (this.isIdentifierName(this.lookahead) && !this.matchKeyword('default')) { + // import foo + specifiers.push(this.parseImportDefaultSpecifier()); + if (this.match(',')) { + this.nextToken(); + if (this.match('*')) { + // import foo, * as foo + specifiers.push(this.parseImportNamespaceSpecifier()); + } + else if (this.match('{')) { + // import foo, {bar} + specifiers = specifiers.concat(this.parseNamedImports()); + } + else { + this.throwUnexpectedToken(this.lookahead); + } + } + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + if (!this.matchContextualKeyword('from')) { + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + this.nextToken(); + src = this.parseModuleSpecifier(); + } + this.consumeSemicolon(); + return this.finalize(node, new Node.ImportDeclaration(specifiers, src)); + }; + // https://tc39.github.io/ecma262/#sec-exports + Parser.prototype.parseExportSpecifier = function () { + var node = this.createNode(); + var local = this.parseIdentifierName(); + var exported = local; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + exported = this.parseIdentifierName(); + } + return this.finalize(node, new Node.ExportSpecifier(local, exported)); + }; + Parser.prototype.parseExportDeclaration = function () { + if (this.context.inFunctionBody) { + this.throwError(messages_1.Messages.IllegalExportDeclaration); + } + var node = this.createNode(); + this.expectKeyword('export'); + var exportDeclaration; + if (this.matchKeyword('default')) { + // export default ... + this.nextToken(); + if (this.matchKeyword('function')) { + // export default function foo () {} + // export default function () {} + var declaration = this.parseFunctionDeclaration(true); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else if (this.matchKeyword('class')) { + // export default class foo {} + var declaration = this.parseClassDeclaration(true); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else if (this.matchContextualKeyword('async')) { + // export default async function f () {} + // export default async function () {} + // export default async x => x + var declaration = this.matchAsyncFunction() ? this.parseFunctionDeclaration(true) : this.parseAssignmentExpression(); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else { + if (this.matchContextualKeyword('from')) { + this.throwError(messages_1.Messages.UnexpectedToken, this.lookahead.value); + } + // export default {}; + // export default []; + // export default (1 + 2); + var declaration = this.match('{') ? this.parseObjectInitializer() : + this.match('[') ? this.parseArrayInitializer() : this.parseAssignmentExpression(); + this.consumeSemicolon(); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + } + else if (this.match('*')) { + // export * from 'foo'; + this.nextToken(); + if (!this.matchContextualKeyword('from')) { + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + this.nextToken(); + var src = this.parseModuleSpecifier(); + this.consumeSemicolon(); + exportDeclaration = this.finalize(node, new Node.ExportAllDeclaration(src)); + } + else if (this.lookahead.type === 4 /* Keyword */) { + // export var f = 1; + var declaration = void 0; + switch (this.lookahead.value) { + case 'let': + case 'const': + declaration = this.parseLexicalDeclaration({ inFor: false }); + break; + case 'var': + case 'class': + case 'function': + declaration = this.parseStatementListItem(); + break; + default: + this.throwUnexpectedToken(this.lookahead); + } + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null)); + } + else if (this.matchAsyncFunction()) { + var declaration = this.parseFunctionDeclaration(); + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null)); + } + else { + var specifiers = []; + var source = null; + var isExportFromIdentifier = false; + this.expect('{'); + while (!this.match('}')) { + isExportFromIdentifier = isExportFromIdentifier || this.matchKeyword('default'); + specifiers.push(this.parseExportSpecifier()); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + if (this.matchContextualKeyword('from')) { + // export {default} from 'foo'; + // export {foo} from 'foo'; + this.nextToken(); + source = this.parseModuleSpecifier(); + this.consumeSemicolon(); + } + else if (isExportFromIdentifier) { + // export {default}; // missing fromClause + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + else { + // export {foo}; + this.consumeSemicolon(); + } + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(null, specifiers, source)); + } + return exportDeclaration; + }; + return Parser; + }()); + exports.Parser = Parser; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + "use strict"; + // Ensure the condition is true, otherwise throw an error. + // This is only to have a better contract semantic, i.e. another safety net + // to catch a logic error. The condition shall be fulfilled in normal case. + // Do NOT use this to enforce a certain condition on any user input. + Object.defineProperty(exports, "__esModule", { value: true }); + function assert(condition, message) { + /* istanbul ignore if */ + if (!condition) { + throw new Error('ASSERT: ' + message); + } + } + exports.assert = assert; + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + "use strict"; + /* tslint:disable:max-classes-per-file */ + Object.defineProperty(exports, "__esModule", { value: true }); + var ErrorHandler = (function () { + function ErrorHandler() { + this.errors = []; + this.tolerant = false; + } + ErrorHandler.prototype.recordError = function (error) { + this.errors.push(error); + }; + ErrorHandler.prototype.tolerate = function (error) { + if (this.tolerant) { + this.recordError(error); + } + else { + throw error; + } + }; + ErrorHandler.prototype.constructError = function (msg, column) { + var error = new Error(msg); + try { + throw error; + } + catch (base) { + /* istanbul ignore else */ + if (Object.create && Object.defineProperty) { + error = Object.create(base); + Object.defineProperty(error, 'column', { value: column }); + } + } + /* istanbul ignore next */ + return error; + }; + ErrorHandler.prototype.createError = function (index, line, col, description) { + var msg = 'Line ' + line + ': ' + description; + var error = this.constructError(msg, col); + error.index = index; + error.lineNumber = line; + error.description = description; + return error; + }; + ErrorHandler.prototype.throwError = function (index, line, col, description) { + throw this.createError(index, line, col, description); + }; + ErrorHandler.prototype.tolerateError = function (index, line, col, description) { + var error = this.createError(index, line, col, description); + if (this.tolerant) { + this.recordError(error); + } + else { + throw error; + } + }; + return ErrorHandler; + }()); + exports.ErrorHandler = ErrorHandler; + + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + // Error messages should be identical to V8. + exports.Messages = { + BadGetterArity: 'Getter must not have any formal parameters', + BadSetterArity: 'Setter must have exactly one formal parameter', + BadSetterRestParameter: 'Setter function argument must not be a rest parameter', + ConstructorIsAsync: 'Class constructor may not be an async method', + ConstructorSpecialMethod: 'Class constructor may not be an accessor', + DeclarationMissingInitializer: 'Missing initializer in %0 declaration', + DefaultRestParameter: 'Unexpected token =', + DuplicateBinding: 'Duplicate binding %0', + DuplicateConstructor: 'A class may only have one constructor', + DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', + ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer', + GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts', + IllegalBreak: 'Illegal break statement', + IllegalContinue: 'Illegal continue statement', + IllegalExportDeclaration: 'Unexpected token', + IllegalImportDeclaration: 'Unexpected token', + IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list', + IllegalReturn: 'Illegal return statement', + InvalidEscapedReservedWord: 'Keyword must not contain escaped characters', + InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence', + InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInForIn: 'Invalid left-hand side in for-in', + InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', + InvalidModuleSpecifier: 'Unexpected token', + InvalidRegExp: 'Invalid regular expression', + LetInLexicalBinding: 'let is disallowed as a lexically bound name', + MissingFromClause: 'Unexpected token', + MultipleDefaultsInSwitch: 'More than one default clause in switch statement', + NewlineAfterThrow: 'Illegal newline after throw', + NoAsAfterImportNamespace: 'Unexpected token', + NoCatchOrFinally: 'Missing catch or finally after try', + ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', + Redeclaration: '%0 \'%1\' has already been declared', + StaticPrototype: 'Classes may not have static property named prototype', + StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', + StrictDelete: 'Delete of an unqualified identifier in strict mode.', + StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block', + StrictFunctionName: 'Function name may not be eval or arguments in strict mode', + StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', + StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + StrictModeWith: 'Strict mode code may not include a with statement', + StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', + StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', + StrictReservedWord: 'Use of future reserved word in strict mode', + StrictVarName: 'Variable name may not be eval or arguments in strict mode', + TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', + UnexpectedEOS: 'Unexpected end of input', + UnexpectedIdentifier: 'Unexpected identifier', + UnexpectedNumber: 'Unexpected number', + UnexpectedReserved: 'Unexpected reserved word', + UnexpectedString: 'Unexpected string', + UnexpectedTemplate: 'Unexpected quasi %0', + UnexpectedToken: 'Unexpected token %0', + UnexpectedTokenIllegal: 'Unexpected token ILLEGAL', + UnknownLabel: 'Undefined label \'%0\'', + UnterminatedRegExp: 'Invalid regular expression: missing /' + }; + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var assert_1 = __webpack_require__(9); + var character_1 = __webpack_require__(4); + var messages_1 = __webpack_require__(11); + function hexValue(ch) { + return '0123456789abcdef'.indexOf(ch.toLowerCase()); + } + function octalValue(ch) { + return '01234567'.indexOf(ch); + } + var Scanner = (function () { + function Scanner(code, handler) { + this.source = code; + this.errorHandler = handler; + this.trackComment = false; + this.isModule = false; + this.length = code.length; + this.index = 0; + this.lineNumber = (code.length > 0) ? 1 : 0; + this.lineStart = 0; + this.curlyStack = []; + } + Scanner.prototype.saveState = function () { + return { + index: this.index, + lineNumber: this.lineNumber, + lineStart: this.lineStart + }; + }; + Scanner.prototype.restoreState = function (state) { + this.index = state.index; + this.lineNumber = state.lineNumber; + this.lineStart = state.lineStart; + }; + Scanner.prototype.eof = function () { + return this.index >= this.length; + }; + Scanner.prototype.throwUnexpectedToken = function (message) { + if (message === void 0) { message = messages_1.Messages.UnexpectedTokenIllegal; } + return this.errorHandler.throwError(this.index, this.lineNumber, this.index - this.lineStart + 1, message); + }; + Scanner.prototype.tolerateUnexpectedToken = function (message) { + if (message === void 0) { message = messages_1.Messages.UnexpectedTokenIllegal; } + this.errorHandler.tolerateError(this.index, this.lineNumber, this.index - this.lineStart + 1, message); + }; + // https://tc39.github.io/ecma262/#sec-comments + Scanner.prototype.skipSingleLineComment = function (offset) { + var comments = []; + var start, loc; + if (this.trackComment) { + comments = []; + start = this.index - offset; + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart - offset + }, + end: {} + }; + } + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + ++this.index; + if (character_1.Character.isLineTerminator(ch)) { + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart - 1 + }; + var entry = { + multiLine: false, + slice: [start + offset, this.index - 1], + range: [start, this.index - 1], + loc: loc + }; + comments.push(entry); + } + if (ch === 13 && this.source.charCodeAt(this.index) === 10) { + ++this.index; + } + ++this.lineNumber; + this.lineStart = this.index; + return comments; + } + } + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: false, + slice: [start + offset, this.index], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + return comments; + }; + Scanner.prototype.skipMultiLineComment = function () { + var comments = []; + var start, loc; + if (this.trackComment) { + comments = []; + start = this.index - 2; + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart - 2 + }, + end: {} + }; + } + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + if (character_1.Character.isLineTerminator(ch)) { + if (ch === 0x0D && this.source.charCodeAt(this.index + 1) === 0x0A) { + ++this.index; + } + ++this.lineNumber; + ++this.index; + this.lineStart = this.index; + } + else if (ch === 0x2A) { + // Block comment ends with '*/'. + if (this.source.charCodeAt(this.index + 1) === 0x2F) { + this.index += 2; + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: true, + slice: [start + 2, this.index - 2], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + return comments; + } + ++this.index; + } + else { + ++this.index; + } + } + // Ran off the end of the file - the whole thing is a comment + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: true, + slice: [start + 2, this.index], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + this.tolerateUnexpectedToken(); + return comments; + }; + Scanner.prototype.scanComments = function () { + var comments; + if (this.trackComment) { + comments = []; + } + var start = (this.index === 0); + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + if (character_1.Character.isWhiteSpace(ch)) { + ++this.index; + } + else if (character_1.Character.isLineTerminator(ch)) { + ++this.index; + if (ch === 0x0D && this.source.charCodeAt(this.index) === 0x0A) { + ++this.index; + } + ++this.lineNumber; + this.lineStart = this.index; + start = true; + } + else if (ch === 0x2F) { + ch = this.source.charCodeAt(this.index + 1); + if (ch === 0x2F) { + this.index += 2; + var comment = this.skipSingleLineComment(2); + if (this.trackComment) { + comments = comments.concat(comment); + } + start = true; + } + else if (ch === 0x2A) { + this.index += 2; + var comment = this.skipMultiLineComment(); + if (this.trackComment) { + comments = comments.concat(comment); + } + } + else { + break; + } + } + else if (start && ch === 0x2D) { + // U+003E is '>' + if ((this.source.charCodeAt(this.index + 1) === 0x2D) && (this.source.charCodeAt(this.index + 2) === 0x3E)) { + // '-->' is a single-line comment + this.index += 3; + var comment = this.skipSingleLineComment(3); + if (this.trackComment) { + comments = comments.concat(comment); + } + } + else { + break; + } + } + else if (ch === 0x3C && !this.isModule) { + if (this.source.slice(this.index + 1, this.index + 4) === '!--') { + this.index += 4; // `") && S.newline_before) { + forward(3); + skip_line_comment("comment4"); + continue; + } + } + var ch = peek(); + if (!ch) return token("eof"); + var code = ch.charCodeAt(0); + switch (code) { + case 34: case 39: return read_string(ch); + case 46: return handle_dot(); + case 47: { + var tok = handle_slash(); + if (tok === next_token) continue; + return tok; + } + } + if (is_digit(code)) return read_num(); + if (PUNC_CHARS(ch)) return token("punc", next()); + if (OPERATOR_CHARS(ch)) return read_operator(); + if (code == 92 || is_identifier_start(code)) return read_word(); + break; } + parse_error("Unexpected character '" + ch + "'"); + }; - return expr; + next_token.context = function(nc) { + if (nc) S = nc; + return S; + }; + + next_token.add_directive = function(directive) { + S.directive_stack[S.directive_stack.length - 1].push(directive); + + if (S.directives[directive] === undefined) { + S.directives[directive] = 1; + } else { + S.directives[directive]++; + } } - function parseLeftHandSideExpressionAllowCall() { - var expr; + next_token.push_directives_stack = function() { + S.directive_stack.push([]); + } - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); + next_token.pop_directives_stack = function() { + var directives = S.directive_stack[S.directive_stack.length - 1]; - while (match('.') || match('[') || match('(')) { - if (match('(')) { - expr = { - type: Syntax.CallExpression, - callee: expr, - 'arguments': parseArguments() - }; - } else if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - } + for (var i = 0; i < directives.length; i++) { + S.directives[directives[i]]--; } - return expr; + S.directive_stack.pop(); } + next_token.has_directive = function(directive) { + return S.directives[directive] !== undefined && + S.directives[directive] > 0; + } - function parseLeftHandSideExpression() { - var expr; + return next_token; - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); +}; - while (match('.') || match('[')) { - if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - } - } +/* -----[ Parser (constants) ]----- */ - return expr; - } +var UNARY_PREFIX = makePredicate([ + "typeof", + "void", + "delete", + "--", + "++", + "!", + "~", + "-", + "+" +]); - // 11.3 Postfix Expressions +var UNARY_POSTFIX = makePredicate([ "--", "++" ]); - function parsePostfixExpression() { - var expr = parseLeftHandSideExpressionAllowCall(), token; +var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); - token = lookahead(); - if (token.type !== Token.Punctuator) { - return expr; +var PRECEDENCE = (function(a, ret){ + for (var i = 0; i < a.length; ++i) { + var b = a[i]; + for (var j = 0; j < b.length; ++j) { + ret[b[j]] = i + 1; } + } + return ret; +})( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ], + {} +); - if ((match('++') || match('--')) && !peekLineTerminator()) { - // 11.3.1, 11.3.2 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPostfix); - } - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); - } +var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); - expr = { - type: Syntax.UpdateExpression, - operator: lex().value, - argument: expr, - prefix: false - }; - } +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); - return expr; - } +/* -----[ Parser ]----- */ - // 11.4 Unary Operators +function parse($TEXT, options) { - function parseUnaryExpression() { - var token, expr; + options = defaults(options, { + bare_returns : false, + cli : false, + expression : false, + filename : null, + html5_comments : true, + shebang : true, + strict : false, + toplevel : null, + }); - token = lookahead(); - if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { - return parsePostfixExpression(); - } + var S = { + input : (typeof $TEXT == "string" + ? tokenizer($TEXT, options.filename, + options.html5_comments, options.shebang) + : $TEXT), + token : null, + prev : null, + peeked : null, + in_function : 0, + in_directives : true, + in_loop : 0, + labels : [] + }; - if (match('++') || match('--')) { - token = lex(); - expr = parseUnaryExpression(); - // 11.4.4, 11.4.5 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant({}, Messages.StrictLHSPrefix); - } + S.token = next(); - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); - } + function is(type, value) { + return is_token(S.token, type, value); + }; - expr = { - type: Syntax.UpdateExpression, - operator: token.value, - argument: expr, - prefix: true - }; - return expr; - } + function peek() { return S.peeked || (S.peeked = S.input()); }; - if (match('+') || match('-') || match('~') || match('!')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; - return expr; + function next() { + S.prev = S.token; + if (S.peeked) { + S.token = S.peeked; + S.peeked = null; + } else { + S.token = S.input(); } + S.in_directives = S.in_directives && ( + S.token.type == "string" || is("punc", ";") + ); + return S.token; + }; - if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; - if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { - throwErrorTolerant({}, Messages.StrictDelete); - } - return expr; - } + function prev() { + return S.prev; + }; - return parsePostfixExpression(); - } + function croak(msg, line, col, pos) { + var ctx = S.input.context(); + js_error(msg, + ctx.filename, + line != null ? line : ctx.tokline, + col != null ? col : ctx.tokcol, + pos != null ? pos : ctx.tokpos); + }; - // 11.5 Multiplicative Operators + function token_error(token, msg) { + croak(msg, token.line, token.col); + }; - function parseMultiplicativeExpression() { - var expr = parseUnaryExpression(); + function unexpected(token) { + if (token == null) + token = S.token; + token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); + }; - while (match('*') || match('/') || match('%')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseUnaryExpression() - }; + function expect_token(type, val) { + if (is(type, val)) { + return next(); } + token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); + }; - return expr; - } + function expect(punc) { return expect_token("punc", punc); }; - // 11.6 Additive Operators + function can_insert_semicolon() { + return !options.strict && ( + S.token.nlb || is("eof") || is("punc", "}") + ); + }; - function parseAdditiveExpression() { - var expr = parseMultiplicativeExpression(); + function semicolon(optional) { + if (is("punc", ";")) next(); + else if (!optional && !can_insert_semicolon()) unexpected(); + }; - while (match('+') || match('-')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseMultiplicativeExpression() - }; - } + function parenthesised() { + expect("("); + var exp = expression(true); + expect(")"); + return exp; + }; - return expr; - } + function embed_tokens(parser) { + return function() { + var start = S.token; + var expr = parser(); + var end = prev(); + expr.start = start; + expr.end = end; + return expr; + }; + }; - // 11.7 Bitwise Shift Operators + function handle_regexp() { + if (is("operator", "/") || is("operator", "/=")) { + S.peeked = null; + S.token = S.input(S.token.value.substr(1)); // force regexp + } + }; - function parseShiftExpression() { - var expr = parseAdditiveExpression(); + var statement = embed_tokens(function() { + handle_regexp(); + switch (S.token.type) { + case "string": + if (S.in_directives) { + var token = peek(); + if (S.token.raw.indexOf("\\") == -1 + && (token.nlb + || is_token(token, "eof") + || is_token(token, "punc", ";") + || is_token(token, "punc", "}"))) { + S.input.add_directive(S.token.value); + } else { + S.in_directives = false; + } + } + var dir = S.in_directives, stat = simple_statement(); + return dir ? new AST_Directive(stat.body) : stat; + case "num": + case "regexp": + case "operator": + case "atom": + return simple_statement(); - while (match('<<') || match('>>') || match('>>>')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseAdditiveExpression() - }; - } + case "name": + return is_token(peek(), "punc", ":") + ? labeled_statement() + : simple_statement(); - return expr; - } - // 11.8 Relational Operators + case "punc": + switch (S.token.value) { + case "{": + return new AST_BlockStatement({ + start : S.token, + body : block_(), + end : prev() + }); + case "[": + case "(": + return simple_statement(); + case ";": + S.in_directives = false; + next(); + return new AST_EmptyStatement(); + default: + unexpected(); + } - function parseRelationalExpression() { - var expr, previousAllowIn; + case "keyword": + switch (S.token.value) { + case "break": + next(); + return break_cont(AST_Break); - previousAllowIn = state.allowIn; - state.allowIn = true; + case "continue": + next(); + return break_cont(AST_Continue); - expr = parseShiftExpression(); + case "debugger": + next(); + semicolon(); + return new AST_Debugger(); - while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseShiftExpression() - }; - } + case "do": + next(); + var body = in_loop(statement); + expect_token("keyword", "while"); + var condition = parenthesised(); + semicolon(true); + return new AST_Do({ + body : body, + condition : condition + }); - state.allowIn = previousAllowIn; - return expr; - } + case "while": + next(); + return new AST_While({ + condition : parenthesised(), + body : in_loop(statement) + }); - // 11.9 Equality Operators + case "for": + next(); + return for_(); - function parseEqualityExpression() { - var expr = parseRelationalExpression(); + case "function": + next(); + return function_(AST_Defun); - while (match('==') || match('!=') || match('===') || match('!==')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseRelationalExpression() - }; - } + case "if": + next(); + return if_(); - return expr; - } + case "return": + if (S.in_function == 0 && !options.bare_returns) + croak("'return' outside of function"); + next(); + var value = null; + if (is("punc", ";")) { + next(); + } else if (!can_insert_semicolon()) { + value = expression(true); + semicolon(); + } + return new AST_Return({ + value: value + }); - // 11.10 Binary Bitwise Operators + case "switch": + next(); + return new AST_Switch({ + expression : parenthesised(), + body : in_loop(switch_body_) + }); - function parseBitwiseANDExpression() { - var expr = parseEqualityExpression(); + case "throw": + next(); + if (S.token.nlb) + croak("Illegal newline after 'throw'"); + var value = expression(true); + semicolon(); + return new AST_Throw({ + value: value + }); - while (match('&')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '&', - left: expr, - right: parseEqualityExpression() - }; - } + case "try": + next(); + return try_(); - return expr; - } + case "var": + next(); + var node = var_(); + semicolon(); + return node; - function parseBitwiseXORExpression() { - var expr = parseBitwiseANDExpression(); + case "const": + next(); + var node = const_(); + semicolon(); + return node; - while (match('^')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '^', - left: expr, - right: parseBitwiseANDExpression() - }; + case "with": + if (S.input.has_directive("use strict")) { + croak("Strict mode may not include a with statement"); + } + next(); + return new AST_With({ + expression : parenthesised(), + body : statement() + }); + } } + unexpected(); + }); - return expr; - } + function labeled_statement() { + var label = as_symbol(AST_Label); + if (find_if(function(l){ return l.name == label.name }, S.labels)) { + // ECMA-262, 12.12: An ECMAScript program is considered + // syntactically incorrect if it contains a + // LabelledStatement that is enclosed by a + // LabelledStatement with the same Identifier as label. + croak("Label " + label.name + " defined twice"); + } + expect(":"); + S.labels.push(label); + var stat = statement(); + S.labels.pop(); + if (!(stat instanceof AST_IterationStatement)) { + // check for `continue` that refers to this label. + // those should be reported as syntax errors. + // https://github.com/mishoo/UglifyJS2/issues/287 + label.references.forEach(function(ref){ + if (ref instanceof AST_Continue) { + ref = ref.label.start; + croak("Continue label `" + label.name + "` refers to non-IterationStatement.", + ref.line, ref.col, ref.pos); + } + }); + } + return new AST_LabeledStatement({ body: stat, label: label }); + }; - function parseBitwiseORExpression() { - var expr = parseBitwiseXORExpression(); + function simple_statement(tmp) { + return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); + }; - while (match('|')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '|', - left: expr, - right: parseBitwiseXORExpression() - }; + function break_cont(type) { + var label = null, ldef; + if (!can_insert_semicolon()) { + label = as_symbol(AST_LabelRef, true); + } + if (label != null) { + ldef = find_if(function(l){ return l.name == label.name }, S.labels); + if (!ldef) + croak("Undefined label " + label.name); + label.thedef = ldef; } + else if (S.in_loop == 0) + croak(type.TYPE + " not inside a loop or switch"); + semicolon(); + var stat = new type({ label: label }); + if (ldef) ldef.references.push(stat); + return stat; + }; - return expr; - } - - // 11.11 Binary Logical Operators - - function parseLogicalANDExpression() { - var expr = parseBitwiseORExpression(); - - while (match('&&')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '&&', - left: expr, - right: parseBitwiseORExpression() - }; - } - - return expr; - } - - function parseLogicalORExpression() { - var expr = parseLogicalANDExpression(); - - while (match('||')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '||', - left: expr, - right: parseLogicalANDExpression() - }; + function for_() { + expect("("); + var init = null; + if (!is("punc", ";")) { + init = is("keyword", "var") + ? (next(), var_(true)) + : expression(true, true); + if (is("operator", "in")) { + if (init instanceof AST_Var && init.definitions.length > 1) + croak("Only one variable declaration allowed in for..in loop"); + next(); + return for_in(init); + } } + return regular_for(init); + }; - return expr; - } - - // 11.12 Conditional Operator - - function parseConditionalExpression() { - var expr, previousAllowIn, consequent; + function regular_for(init) { + expect(";"); + var test = is("punc", ";") ? null : expression(true); + expect(";"); + var step = is("punc", ")") ? null : expression(true); + expect(")"); + return new AST_For({ + init : init, + condition : test, + step : step, + body : in_loop(statement) + }); + }; - expr = parseLogicalORExpression(); + function for_in(init) { + var lhs = init instanceof AST_Var ? init.definitions[0].name : null; + var obj = expression(true); + expect(")"); + return new AST_ForIn({ + init : init, + name : lhs, + object : obj, + body : in_loop(statement) + }); + }; - if (match('?')) { - lex(); - previousAllowIn = state.allowIn; - state.allowIn = true; - consequent = parseAssignmentExpression(); - state.allowIn = previousAllowIn; - expect(':'); + var function_ = function(ctor) { + var in_statement = ctor === AST_Defun; + var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; + if (in_statement && !name) + unexpected(); + expect("("); + return new ctor({ + name: name, + argnames: (function(first, a){ + while (!is("punc", ")")) { + if (first) first = false; else expect(","); + a.push(as_symbol(AST_SymbolFunarg)); + } + next(); + return a; + })(true, []), + body: (function(loop, labels){ + ++S.in_function; + S.in_directives = true; + S.input.push_directives_stack(); + S.in_loop = 0; + S.labels = []; + var a = block_(); + S.input.pop_directives_stack(); + --S.in_function; + S.in_loop = loop; + S.labels = labels; + return a; + })(S.in_loop, S.labels) + }); + }; - expr = { - type: Syntax.ConditionalExpression, - test: expr, - consequent: consequent, - alternate: parseAssignmentExpression() - }; + function if_() { + var cond = parenthesised(), body = statement(), belse = null; + if (is("keyword", "else")) { + next(); + belse = statement(); } + return new AST_If({ + condition : cond, + body : body, + alternative : belse + }); + }; - return expr; - } - - // 11.13 Assignment Operators - - function parseAssignmentExpression() { - var token, expr; - - token = lookahead(); - expr = parseConditionalExpression(); + function block_() { + expect("{"); + var a = []; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + a.push(statement()); + } + next(); + return a; + }; - if (matchAssign()) { - // LeftHandSideExpression - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + function switch_body_() { + expect("{"); + var a = [], cur = null, branch = null, tmp; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + if (is("keyword", "case")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Case({ + start : (tmp = S.token, next(), tmp), + expression : expression(true), + body : cur + }); + a.push(branch); + expect(":"); } - - // 11.13.1 - if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { - throwErrorTolerant(token, Messages.StrictLHSAssignment); + else if (is("keyword", "default")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Default({ + start : (tmp = S.token, next(), expect(":"), tmp), + body : cur + }); + a.push(branch); + } + else { + if (!cur) unexpected(); + cur.push(statement()); } - - expr = { - type: Syntax.AssignmentExpression, - operator: lex().value, - left: expr, - right: parseAssignmentExpression() - }; } + if (branch) branch.end = prev(); + next(); + return a; + }; - return expr; - } - - // 11.14 Comma Operator + function try_() { + var body = block_(), bcatch = null, bfinally = null; + if (is("keyword", "catch")) { + var start = S.token; + next(); + expect("("); + var name = as_symbol(AST_SymbolCatch); + expect(")"); + bcatch = new AST_Catch({ + start : start, + argname : name, + body : block_(), + end : prev() + }); + } + if (is("keyword", "finally")) { + var start = S.token; + next(); + bfinally = new AST_Finally({ + start : start, + body : block_(), + end : prev() + }); + } + if (!bcatch && !bfinally) + croak("Missing catch/finally blocks"); + return new AST_Try({ + body : body, + bcatch : bcatch, + bfinally : bfinally + }); + }; - function parseExpression() { - var expr = parseAssignmentExpression(); + function vardefs(no_in, in_const) { + var a = []; + for (;;) { + a.push(new AST_VarDef({ + start : S.token, + name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), + value : is("operator", "=") ? (next(), expression(false, no_in)) : null, + end : prev() + })); + if (!is("punc", ",")) + break; + next(); + } + return a; + }; - if (match(',')) { - expr = { - type: Syntax.SequenceExpression, - expressions: [ expr ] - }; + var var_ = function(no_in) { + return new AST_Var({ + start : prev(), + definitions : vardefs(no_in, false), + end : prev() + }); + }; - while (index < length) { - if (!match(',')) { - break; - } - lex(); - expr.expressions.push(parseAssignmentExpression()); - } + var const_ = function() { + return new AST_Const({ + start : prev(), + definitions : vardefs(false, true), + end : prev() + }); + }; + var new_ = function(allow_calls) { + var start = S.token; + expect_token("operator", "new"); + var newexp = expr_atom(false), args; + if (is("punc", "(")) { + next(); + args = expr_list(")"); + } else { + args = []; } - return expr; - } - - // 12.1 Block - - function parseStatementList() { - var list = [], - statement; + return subscripts(new AST_New({ + start : start, + expression : newexp, + args : args, + end : prev() + }), allow_calls); + }; - while (index < length) { - if (match('}')) { + function as_atom_node() { + var tok = S.token, ret; + switch (tok.type) { + case "name": + case "keyword": + ret = _make_symbol(AST_SymbolRef); + break; + case "num": + ret = new AST_Number({ start: tok, end: tok, value: tok.value }); + break; + case "string": + ret = new AST_String({ + start : tok, + end : tok, + value : tok.value, + quote : tok.quote + }); + break; + case "regexp": + ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); + break; + case "atom": + switch (tok.value) { + case "false": + ret = new AST_False({ start: tok, end: tok }); break; - } - statement = parseSourceElement(); - if (typeof statement === 'undefined') { + case "true": + ret = new AST_True({ start: tok, end: tok }); + break; + case "null": + ret = new AST_Null({ start: tok, end: tok }); break; } - list.push(statement); + break; + case "operator": + if (!is_identifier_string(tok.value)) { + croak("Invalid getter/setter name: " + tok.value, + tok.line, tok.col, tok.pos); + } + ret = _make_symbol(AST_SymbolRef); + break; } + next(); + return ret; + }; - return list; - } - - function parseBlock() { - var block; - - expect('{'); - - block = parseStatementList(); - - expect('}'); - - return { - type: Syntax.BlockStatement, - body: block - }; - } - - // 12.2 Variable Statement - - function parseVariableIdentifier() { - var token = lex(); - - if (token.type !== Token.Identifier) { - throwUnexpected(token); + var expr_atom = function(allow_calls) { + if (is("operator", "new")) { + return new_(allow_calls); } - - return { - type: Syntax.Identifier, - name: token.value - }; - } - - function parseVariableDeclaration(kind) { - var id = parseVariableIdentifier(), - init = null; - - // 12.2.1 - if (strict && isRestrictedWord(id.name)) { - throwErrorTolerant({}, Messages.StrictVarName); + var start = S.token; + if (is("punc")) { + switch (start.value) { + case "(": + next(); + var ex = expression(true); + ex.start = start; + ex.end = S.token; + expect(")"); + return subscripts(ex, allow_calls); + case "[": + return subscripts(array_(), allow_calls); + case "{": + return subscripts(object_(), allow_calls); + } + unexpected(); + } + if (is("keyword", "function")) { + next(); + var func = function_(AST_Function); + func.start = start; + func.end = prev(); + return subscripts(func, allow_calls); + } + if (ATOMIC_START_TOKEN[S.token.type]) { + return subscripts(as_atom_node(), allow_calls); } + unexpected(); + }; - if (kind === 'const') { - expect('='); - init = parseAssignmentExpression(); - } else if (match('=')) { - lex(); - init = parseAssignmentExpression(); + function expr_list(closing, allow_trailing_comma, allow_empty) { + var first = true, a = []; + while (!is("punc", closing)) { + if (first) first = false; else expect(","); + if (allow_trailing_comma && is("punc", closing)) break; + if (is("punc", ",") && allow_empty) { + a.push(new AST_Hole({ start: S.token, end: S.token })); + } else { + a.push(expression(false)); + } } + next(); + return a; + }; - return { - type: Syntax.VariableDeclarator, - id: id, - init: init - }; - } + var array_ = embed_tokens(function() { + expect("["); + return new AST_Array({ + elements: expr_list("]", !options.strict, true) + }); + }); - function parseVariableDeclarationList(kind) { - var list = []; + var create_accessor = embed_tokens(function() { + return function_(AST_Accessor); + }); - do { - list.push(parseVariableDeclaration(kind)); - if (!match(',')) { + var object_ = embed_tokens(function() { + expect("{"); + var first = true, a = []; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + if (!options.strict && is("punc", "}")) + // allow trailing comma break; + var start = S.token; + var type = start.type; + var name = as_property_name(); + if (type == "name" && !is("punc", ":")) { + var key = new AST_SymbolAccessor({ + start: S.token, + name: as_property_name(), + end: prev() + }); + if (name == "get") { + a.push(new AST_ObjectGetter({ + start : start, + key : key, + value : create_accessor(), + end : prev() + })); + continue; + } + if (name == "set") { + a.push(new AST_ObjectSetter({ + start : start, + key : key, + value : create_accessor(), + end : prev() + })); + continue; + } } - lex(); - } while (index < length); + expect(":"); + a.push(new AST_ObjectKeyVal({ + start : start, + quote : start.quote, + key : name, + value : expression(false), + end : prev() + })); + } + next(); + return new AST_Object({ properties: a }); + }); - return list; - } + function as_property_name() { + var tmp = S.token; + switch (tmp.type) { + case "operator": + if (!KEYWORDS(tmp.value)) unexpected(); + case "num": + case "string": + case "name": + case "keyword": + case "atom": + next(); + return tmp.value; + default: + unexpected(); + } + }; - function parseVariableStatement() { - var declarations; + function as_name() { + var tmp = S.token; + if (tmp.type != "name") unexpected(); + next(); + return tmp.value; + }; - expectKeyword('var'); + function _make_symbol(type) { + var name = S.token.value; + return new (name == "this" ? AST_This : type)({ + name : String(name), + start : S.token, + end : S.token + }); + }; - declarations = parseVariableDeclarationList(); + function as_symbol(type, noerror) { + if (!is("name")) { + if (!noerror) croak("Name expected"); + return null; + } + var sym = _make_symbol(type); + next(); + return sym; + }; - consumeSemicolon(); + var subscripts = function(expr, allow_calls) { + var start = expr.start; + if (is("punc", ".")) { + next(); + return subscripts(new AST_Dot({ + start : start, + expression : expr, + property : as_name(), + end : prev() + }), allow_calls); + } + if (is("punc", "[")) { + next(); + var prop = expression(true); + expect("]"); + return subscripts(new AST_Sub({ + start : start, + expression : expr, + property : prop, + end : prev() + }), allow_calls); + } + if (allow_calls && is("punc", "(")) { + next(); + return subscripts(new AST_Call({ + start : start, + expression : expr, + args : expr_list(")"), + end : prev() + }), true); + } + return expr; + }; - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: 'var' - }; - } + var maybe_unary = function(allow_calls) { + var start = S.token; + if (is("operator") && UNARY_PREFIX(start.value)) { + next(); + handle_regexp(); + var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls)); + ex.start = start; + ex.end = prev(); + return ex; + } + var val = expr_atom(allow_calls); + while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { + val = make_unary(AST_UnaryPostfix, S.token, val); + val.start = start; + val.end = S.token; + next(); + } + return val; + }; + + function make_unary(ctor, token, expr) { + var op = token.value; + if ((op == "++" || op == "--") && !is_assignable(expr)) + croak("Invalid use of " + op + " operator", token.line, token.col, token.pos); + return new ctor({ operator: op, expression: expr }); + }; - // kind may be `const` or `let` - // Both are experimental and not in the specification yet. - // see http://wiki.ecmascript.org/doku.php?id=harmony:const - // and http://wiki.ecmascript.org/doku.php?id=harmony:let - function parseConstLetDeclaration(kind) { - var declarations; + var expr_op = function(left, min_prec, no_in) { + var op = is("operator") ? S.token.value : null; + if (op == "in" && no_in) op = null; + var prec = op != null ? PRECEDENCE[op] : null; + if (prec != null && prec > min_prec) { + next(); + var right = expr_op(maybe_unary(true), prec, no_in); + return expr_op(new AST_Binary({ + start : left.start, + left : left, + operator : op, + right : right, + end : right.end + }), min_prec, no_in); + } + return left; + }; - expectKeyword(kind); + function expr_ops(no_in) { + return expr_op(maybe_unary(true), 0, no_in); + }; - declarations = parseVariableDeclarationList(kind); + var maybe_conditional = function(no_in) { + var start = S.token; + var expr = expr_ops(no_in); + if (is("operator", "?")) { + next(); + var yes = expression(false); + expect(":"); + return new AST_Conditional({ + start : start, + condition : expr, + consequent : yes, + alternative : expression(false, no_in), + end : prev() + }); + } + return expr; + }; - consumeSemicolon(); + function is_assignable(expr) { + if (options.cli) return true; + return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; + }; - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind - }; - } + var maybe_assign = function(no_in) { + var start = S.token; + var left = maybe_conditional(no_in), val = S.token.value; + if (is("operator") && ASSIGNMENT(val)) { + if (is_assignable(left)) { + next(); + return new AST_Assign({ + start : start, + left : left, + operator : val, + right : maybe_assign(no_in), + end : prev() + }); + } + croak("Invalid assignment"); + } + return left; + }; - // 12.3 Empty Statement + var expression = function(commas, no_in) { + var start = S.token; + var expr = maybe_assign(no_in); + if (commas && is("punc", ",")) { + next(); + return new AST_Seq({ + start : start, + car : expr, + cdr : expression(true, no_in), + end : peek() + }); + } + return expr; + }; - function parseEmptyStatement() { - expect(';'); + function in_loop(cont) { + ++S.in_loop; + var ret = cont(); + --S.in_loop; + return ret; + }; - return { - type: Syntax.EmptyStatement - }; + if (options.expression) { + return expression(true); } - // 12.4 Expression Statement + return (function(){ + var start = S.token; + var body = []; + S.input.push_directives_stack(); + while (!is("eof")) + body.push(statement()); + S.input.pop_directives_stack(); + var end = prev(); + var toplevel = options.toplevel; + if (toplevel) { + toplevel.body = toplevel.body.concat(body); + toplevel.end = end; + } else { + toplevel = new AST_Toplevel({ start: start, body: body, end: end }); + } + return toplevel; + })(); - function parseExpressionStatement() { - var expr = parseExpression(); +}; - consumeSemicolon(); +/*********************************************************************** - return { - type: Syntax.ExpressionStatement, - expression: expr - }; - } + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- - // 12.5 If statement + Author: Mihai Bazon + + http://mihai.bazon.net/blog - function parseIfStatement() { - var test, consequent, alternate; + Distributed under the BSD license: - expectKeyword('if'); + Copyright 2012 (c) Mihai Bazon - expect('('); + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - test = parseExpression(); + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. - expect(')'); + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. - consequent = parseStatement(); + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. - if (matchKeyword('else')) { - lex(); - alternate = parseStatement(); - } else { - alternate = null; - } + ***********************************************************************/ - return { - type: Syntax.IfStatement, - test: test, - consequent: consequent, - alternate: alternate - }; - } +"use strict"; - // 12.6 Iteration Statements +// Tree transformer helpers. - function parseDoWhileStatement() { - var body, test, oldInIteration; +function TreeTransformer(before, after) { + TreeWalker.call(this); + this.before = before; + this.after = after; +} +TreeTransformer.prototype = new TreeWalker; + +(function(undefined){ + + function _(node, descend) { + node.DEFMETHOD("transform", function(tw, in_list){ + var x, y; + tw.push(this); + if (tw.before) x = tw.before(this, descend, in_list); + if (x === undefined) { + if (!tw.after) { + x = this; + descend(x, tw); + } else { + tw.stack[tw.stack.length - 1] = x = this; + descend(x, tw); + y = tw.after(x, in_list); + if (y !== undefined) x = y; + } + } + tw.pop(this); + return x; + }); + }; - expectKeyword('do'); + function do_list(list, tw) { + return MAP(list, function(node){ + return node.transform(tw, true); + }); + }; - oldInIteration = state.inIteration; - state.inIteration = true; + _(AST_Node, noop); - body = parseStatement(); + _(AST_LabeledStatement, function(self, tw){ + self.label = self.label.transform(tw); + self.body = self.body.transform(tw); + }); - state.inIteration = oldInIteration; + _(AST_SimpleStatement, function(self, tw){ + self.body = self.body.transform(tw); + }); - expectKeyword('while'); + _(AST_Block, function(self, tw){ + self.body = do_list(self.body, tw); + }); - expect('('); + _(AST_DWLoop, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); + }); - test = parseExpression(); + _(AST_For, function(self, tw){ + if (self.init) self.init = self.init.transform(tw); + if (self.condition) self.condition = self.condition.transform(tw); + if (self.step) self.step = self.step.transform(tw); + self.body = self.body.transform(tw); + }); - expect(')'); + _(AST_ForIn, function(self, tw){ + self.init = self.init.transform(tw); + self.object = self.object.transform(tw); + self.body = self.body.transform(tw); + }); - if (match(';')) { - lex(); - } + _(AST_With, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = self.body.transform(tw); + }); - return { - type: Syntax.DoWhileStatement, - body: body, - test: test - }; - } + _(AST_Exit, function(self, tw){ + if (self.value) self.value = self.value.transform(tw); + }); - function parseWhileStatement() { - var test, body, oldInIteration; + _(AST_LoopControl, function(self, tw){ + if (self.label) self.label = self.label.transform(tw); + }); - expectKeyword('while'); + _(AST_If, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); + if (self.alternative) self.alternative = self.alternative.transform(tw); + }); - expect('('); + _(AST_Switch, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); - test = parseExpression(); + _(AST_Case, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); - expect(')'); + _(AST_Try, function(self, tw){ + self.body = do_list(self.body, tw); + if (self.bcatch) self.bcatch = self.bcatch.transform(tw); + if (self.bfinally) self.bfinally = self.bfinally.transform(tw); + }); - oldInIteration = state.inIteration; - state.inIteration = true; + _(AST_Catch, function(self, tw){ + self.argname = self.argname.transform(tw); + self.body = do_list(self.body, tw); + }); - body = parseStatement(); + _(AST_Definitions, function(self, tw){ + self.definitions = do_list(self.definitions, tw); + }); - state.inIteration = oldInIteration; + _(AST_VarDef, function(self, tw){ + self.name = self.name.transform(tw); + if (self.value) self.value = self.value.transform(tw); + }); - return { - type: Syntax.WhileStatement, - test: test, - body: body - }; - } + _(AST_Lambda, function(self, tw){ + if (self.name) self.name = self.name.transform(tw); + self.argnames = do_list(self.argnames, tw); + self.body = do_list(self.body, tw); + }); - function parseForVariableDeclaration() { - var token = lex(); + _(AST_Call, function(self, tw){ + self.expression = self.expression.transform(tw); + self.args = do_list(self.args, tw); + }); - return { - type: Syntax.VariableDeclaration, - declarations: parseVariableDeclarationList(), - kind: token.value - }; - } + _(AST_Seq, function(self, tw){ + self.car = self.car.transform(tw); + self.cdr = self.cdr.transform(tw); + }); - function parseForStatement() { - var init, test, update, left, right, body, oldInIteration; + _(AST_Dot, function(self, tw){ + self.expression = self.expression.transform(tw); + }); - init = test = update = null; + _(AST_Sub, function(self, tw){ + self.expression = self.expression.transform(tw); + self.property = self.property.transform(tw); + }); - expectKeyword('for'); + _(AST_Unary, function(self, tw){ + self.expression = self.expression.transform(tw); + }); - expect('('); + _(AST_Binary, function(self, tw){ + self.left = self.left.transform(tw); + self.right = self.right.transform(tw); + }); - if (match(';')) { - lex(); - } else { - if (matchKeyword('var') || matchKeyword('let')) { - state.allowIn = false; - init = parseForVariableDeclaration(); - state.allowIn = true; + _(AST_Conditional, function(self, tw){ + self.condition = self.condition.transform(tw); + self.consequent = self.consequent.transform(tw); + self.alternative = self.alternative.transform(tw); + }); - if (init.declarations.length === 1 && matchKeyword('in')) { - lex(); - left = init; - right = parseExpression(); - init = null; - } - } else { - state.allowIn = false; - init = parseExpression(); - state.allowIn = true; - - if (matchKeyword('in')) { - // LeftHandSideExpression - if (!isLeftHandSide(init)) { - throwErrorTolerant({}, Messages.InvalidLHSInForIn); - } + _(AST_Array, function(self, tw){ + self.elements = do_list(self.elements, tw); + }); - lex(); - left = init; - right = parseExpression(); - init = null; - } - } + _(AST_Object, function(self, tw){ + self.properties = do_list(self.properties, tw); + }); - if (typeof left === 'undefined') { - expect(';'); - } - } + _(AST_ObjectProperty, function(self, tw){ + self.value = self.value.transform(tw); + }); - if (typeof left === 'undefined') { +})(); - if (!match(';')) { - test = parseExpression(); - } - expect(';'); +/*********************************************************************** - if (!match(')')) { - update = parseExpression(); - } - } + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 - expect(')'); + -------------------------------- (C) --------------------------------- - oldInIteration = state.inIteration; - state.inIteration = true; + Author: Mihai Bazon + + http://mihai.bazon.net/blog - body = parseStatement(); + Distributed under the BSD license: - state.inIteration = oldInIteration; + Copyright 2012 (c) Mihai Bazon - if (typeof left === 'undefined') { - return { - type: Syntax.ForStatement, - init: init, - test: test, - update: update, - body: body - }; - } + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - return { - type: Syntax.ForInStatement, - left: left, - right: right, - body: body, - each: false - }; - } + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. - // 12.7 The continue statement + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. - function parseContinueStatement() { - var token, label = null; + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. - expectKeyword('continue'); + ***********************************************************************/ - // Optimize the most common form: 'continue;'. - if (source[index] === ';') { - lex(); +"use strict"; + +function SymbolDef(scope, index, orig) { + this.name = orig.name; + this.orig = [ orig ]; + this.scope = scope; + this.references = []; + this.global = false; + this.mangled_name = null; + this.undeclared = false; + this.index = index; + this.id = SymbolDef.next_id++; +}; - if (!state.inIteration) { - throwError({}, Messages.IllegalContinue); - } +SymbolDef.next_id = 1; - return { - type: Syntax.ContinueStatement, - label: null - }; - } +SymbolDef.prototype = { + unmangleable: function(options) { + if (!options) options = {}; - if (peekLineTerminator()) { - if (!state.inIteration) { - throwError({}, Messages.IllegalContinue); + return (this.global && !options.toplevel) + || this.undeclared + || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) + || (options.keep_fnames + && (this.orig[0] instanceof AST_SymbolLambda + || this.orig[0] instanceof AST_SymbolDefun)); + }, + mangle: function(options) { + var cache = options.cache && options.cache.props; + if (this.global && cache && cache.has(this.name)) { + this.mangled_name = cache.get(this.name); + } + else if (!this.mangled_name && !this.unmangleable(options)) { + var s = this.scope; + var sym = this.orig[0]; + if (!options.screw_ie8 && sym instanceof AST_SymbolLambda) + s = s.parent_scope; + var def; + if (this.defun && (def = this.defun.variables.get(this.name))) { + this.mangled_name = def.mangled_name || def.name; + } else + this.mangled_name = s.next_mangled(options, this); + if (this.global && cache) { + cache.set(this.name, this.mangled_name); } - - return { - type: Syntax.ContinueStatement, - label: null - }; } + } +}; - token = lookahead(); - if (token.type === Token.Identifier) { - label = parseVariableIdentifier(); +AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ + options = defaults(options, { + cache: null, + screw_ie8: true, + }); - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { - throwError({}, Messages.UnknownLabel, label.name); + // pass 1: setup scope chaining and handle definitions + var self = this; + var scope = self.parent_scope = null; + var labels = new Dictionary(); + var defun = null; + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_Catch) { + var save_scope = scope; + scope = new AST_Scope(node); + scope.init_scope_vars(save_scope); + descend(); + scope = save_scope; + return true; + } + if (node instanceof AST_Scope) { + node.init_scope_vars(scope); + var save_scope = scope; + var save_defun = defun; + var save_labels = labels; + defun = scope = node; + labels = new Dictionary(); + descend(); + scope = save_scope; + defun = save_defun; + labels = save_labels; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_LabeledStatement) { + var l = node.label; + if (labels.has(l.name)) { + throw new Error(string_template("Label {name} defined twice", l)); + } + labels.set(l.name, l); + descend(); + labels.del(l.name); + return true; // no descend again + } + if (node instanceof AST_With) { + for (var s = scope; s; s = s.parent_scope) + s.uses_with = true; + return; + } + if (node instanceof AST_Symbol) { + node.scope = scope; + } + if (node instanceof AST_Label) { + node.thedef = node; + node.references = []; + } + if (node instanceof AST_SymbolLambda) { + defun.def_function(node); + } + else if (node instanceof AST_SymbolDefun) { + // Careful here, the scope where this should be defined is + // the parent scope. The reason is that we enter a new + // scope when we encounter the AST_Defun node (which is + // instanceof AST_Scope) but we get to the symbol a bit + // later. + (node.scope = defun.parent_scope).def_function(node); + } + else if (node instanceof AST_SymbolVar + || node instanceof AST_SymbolConst) { + defun.def_variable(node); + if (defun !== scope) { + node.mark_enclosed(options); + var def = scope.find_variable(node); + if (node.thedef !== def) { + node.thedef = def; + node.reference(options); + } + } + } + else if (node instanceof AST_SymbolCatch) { + scope.def_variable(node).defun = defun; + } + else if (node instanceof AST_LabelRef) { + var sym = labels.get(node.name); + if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { + name: node.name, + line: node.start.line, + col: node.start.col + })); + node.thedef = sym; + } + }); + self.walk(tw); + + // pass 2: find back references and eval + var func = null; + var globals = self.globals = new Dictionary(); + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_Lambda) { + var prev_func = func; + func = node; + descend(); + func = prev_func; + return true; + } + if (node instanceof AST_LoopControl && node.label) { + node.label.thedef.references.push(node); + return true; + } + if (node instanceof AST_SymbolRef) { + var name = node.name; + if (name == "eval" && tw.parent() instanceof AST_Call) { + for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { + s.uses_eval = true; + } + } + var sym = node.scope.find_variable(name); + if (node.scope instanceof AST_Lambda && name == "arguments") { + node.scope.uses_arguments = true; } + if (!sym) { + sym = self.def_global(node); + } + node.thedef = sym; + node.reference(options); + return true; } + }); + self.walk(tw); - consumeSemicolon(); + // pass 3: fix up any scoping issue with IE8 + if (!options.screw_ie8) { + self.walk(new TreeWalker(function(node, descend) { + if (node instanceof AST_SymbolCatch) { + var name = node.name; + var refs = node.thedef.references; + var scope = node.thedef.defun; + var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); + refs.forEach(function(ref) { + ref.thedef = def; + ref.reference(options); + }); + node.thedef = def; + return true; + } + })); + } - if (label === null && !state.inIteration) { - throwError({}, Messages.IllegalContinue); - } + if (options.cache) { + this.cname = options.cache.cname; + } +}); - return { - type: Syntax.ContinueStatement, - label: label - }; +AST_Toplevel.DEFMETHOD("def_global", function(node){ + var globals = this.globals, name = node.name; + if (globals.has(name)) { + return globals.get(name); + } else { + var g = new SymbolDef(this, globals.size(), node); + g.undeclared = true; + g.global = true; + globals.set(name, g); + return g; } +}); - // 12.8 The break statement +AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){ + this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) + this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) + this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement + this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` + this.parent_scope = parent_scope; // the parent scope + this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes + this.cname = -1; // the current index for mangling functions/variables +}); - function parseBreakStatement() { - var token, label = null; - - expectKeyword('break'); - - // Optimize the most common form: 'break;'. - if (source[index] === ';') { - lex(); - - if (!(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } - - return { - type: Syntax.BreakStatement, - label: null - }; - } - - if (peekLineTerminator()) { - if (!(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } +AST_Lambda.DEFMETHOD("init_scope_vars", function(){ + AST_Scope.prototype.init_scope_vars.apply(this, arguments); + this.uses_arguments = false; + this.def_variable(new AST_SymbolVar({ + name: "arguments", + start: this.start, + end: this.end + })); +}); - return { - type: Syntax.BreakStatement, - label: null - }; +AST_Symbol.DEFMETHOD("mark_enclosed", function(options) { + var def = this.definition(); + var s = this.scope; + while (s) { + push_uniq(s.enclosed, def); + if (options.keep_fnames) { + s.functions.each(function(d) { + push_uniq(def.scope.enclosed, d); + }); } + if (s === def.scope) break; + s = s.parent_scope; + } +}); - token = lookahead(); - if (token.type === Token.Identifier) { - label = parseVariableIdentifier(); - - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { - throwError({}, Messages.UnknownLabel, label.name); - } - } +AST_Symbol.DEFMETHOD("reference", function(options) { + this.definition().references.push(this); + this.mark_enclosed(options); +}); - consumeSemicolon(); +AST_Scope.DEFMETHOD("find_variable", function(name){ + if (name instanceof AST_Symbol) name = name.name; + return this.variables.get(name) + || (this.parent_scope && this.parent_scope.find_variable(name)); +}); - if (label === null && !(state.inIteration || state.inSwitch)) { - throwError({}, Messages.IllegalBreak); - } +AST_Scope.DEFMETHOD("def_function", function(symbol){ + this.functions.set(symbol.name, this.def_variable(symbol)); +}); - return { - type: Syntax.BreakStatement, - label: label - }; +AST_Scope.DEFMETHOD("def_variable", function(symbol){ + var def; + if (!this.variables.has(symbol.name)) { + def = new SymbolDef(this, this.variables.size(), symbol); + this.variables.set(symbol.name, def); + def.global = !this.parent_scope; + } else { + def = this.variables.get(symbol.name); + def.orig.push(symbol); } + return symbol.thedef = def; +}); - // 12.9 The return statement - - function parseReturnStatement() { - var token, argument = null; - - expectKeyword('return'); +AST_Scope.DEFMETHOD("next_mangled", function(options){ + var ext = this.enclosed; + out: while (true) { + var m = base54(++this.cname); + if (!is_identifier(m)) continue; // skip over "do" - if (!state.inFunctionBody) { - throwErrorTolerant({}, Messages.IllegalReturn); - } + // https://github.com/mishoo/UglifyJS2/issues/242 -- do not + // shadow a name excepted from mangling. + if (options.except.indexOf(m) >= 0) continue; - // 'return' followed by a space and an identifier is very common. - if (source[index] === ' ') { - if (isIdentifierStart(source[index + 1])) { - argument = parseExpression(); - consumeSemicolon(); - return { - type: Syntax.ReturnStatement, - argument: argument - }; - } + // we must ensure that the mangled name does not shadow a name + // from some parent scope that is referenced in this or in + // inner scopes. + for (var i = ext.length; --i >= 0;) { + var sym = ext[i]; + var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); + if (m == name) continue out; } + return m; + } +}); - if (peekLineTerminator()) { - return { - type: Syntax.ReturnStatement, - argument: null - }; - } +AST_Function.DEFMETHOD("next_mangled", function(options, def){ + // #179, #326 + // in Safari strict mode, something like (function x(x){...}) is a syntax error; + // a function expression's argument cannot shadow the function expression's name - if (!match(';')) { - token = lookahead(); - if (!match('}') && token.type !== Token.EOF) { - argument = parseExpression(); - } - } + var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); - consumeSemicolon(); + // the function's mangled_name is null when keep_fnames is true + var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null; - return { - type: Syntax.ReturnStatement, - argument: argument - }; + while (true) { + var name = AST_Lambda.prototype.next_mangled.call(this, options, def); + if (!tricky_name || tricky_name != name) + return name; } +}); - // 12.10 The with statement +AST_Symbol.DEFMETHOD("unmangleable", function(options){ + return this.definition().unmangleable(options); +}); - function parseWithStatement() { - var object, body; +// labels are always mangleable +AST_Label.DEFMETHOD("unmangleable", function(){ + return false; +}); - if (strict) { - throwErrorTolerant({}, Messages.StrictModeWith); - } +AST_Symbol.DEFMETHOD("unreferenced", function(){ + return this.definition().references.length == 0 + && !(this.scope.uses_eval || this.scope.uses_with); +}); - expectKeyword('with'); +AST_Symbol.DEFMETHOD("undeclared", function(){ + return this.definition().undeclared; +}); - expect('('); +AST_LabelRef.DEFMETHOD("undeclared", function(){ + return false; +}); - object = parseExpression(); +AST_Label.DEFMETHOD("undeclared", function(){ + return false; +}); - expect(')'); +AST_Symbol.DEFMETHOD("definition", function(){ + return this.thedef; +}); - body = parseStatement(); +AST_Symbol.DEFMETHOD("global", function(){ + return this.definition().global; +}); - return { - type: Syntax.WithStatement, - object: object, - body: body - }; - } +AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ + return defaults(options, { + eval : false, + except : [], + keep_fnames : false, + screw_ie8 : true, + sort : false, // Ignored. Flag retained for backwards compatibility. + toplevel : false, + }); +}); - // 12.10 The swith statement +AST_Toplevel.DEFMETHOD("mangle_names", function(options){ + options = this._default_mangler_options(options); - function parseSwitchCase() { - var test, - consequent = [], - statement; + // Never mangle arguments + options.except.push('arguments'); - if (matchKeyword('default')) { - lex(); - test = null; - } else { - expectKeyword('case'); - test = parseExpression(); - } - expect(':'); + // We only need to mangle declaration nodes. Special logic wired + // into the code generator will display the mangled name if it's + // present (and for AST_SymbolRef-s it'll use the mangled name of + // the AST_SymbolDeclaration that it points to). + var lname = -1; + var to_mangle = []; - while (index < length) { - if (match('}') || matchKeyword('default') || matchKeyword('case')) { - break; - } - statement = parseStatement(); - if (typeof statement === 'undefined') { - break; + if (options.cache) { + this.globals.each(function(symbol){ + if (options.except.indexOf(symbol.name) < 0) { + to_mangle.push(symbol); } - consequent.push(statement); - } - - return { - type: Syntax.SwitchCase, - test: test, - consequent: consequent - }; + }); } - function parseSwitchStatement() { - var discriminant, cases, clause, oldInSwitch, defaultFound; - - expectKeyword('switch'); - - expect('('); - - discriminant = parseExpression(); - - expect(')'); + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_LabeledStatement) { + // lname is incremented when we get to the AST_Label + var save_nesting = lname; + descend(); + lname = save_nesting; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_Scope) { + var p = tw.parent(), a = []; + node.variables.each(function(symbol){ + if (options.except.indexOf(symbol.name) < 0) { + a.push(symbol); + } + }); + to_mangle.push.apply(to_mangle, a); + return; + } + if (node instanceof AST_Label) { + var name; + do name = base54(++lname); while (!is_identifier(name)); + node.mangled_name = name; + return true; + } + if (options.screw_ie8 && node instanceof AST_SymbolCatch) { + to_mangle.push(node.definition()); + return; + } + }); + this.walk(tw); + to_mangle.forEach(function(def){ def.mangle(options) }); - expect('{'); + if (options.cache) { + options.cache.cname = this.cname; + } +}); - cases = []; +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ + options = this._default_mangler_options(options); + var tw = new TreeWalker(function(node){ + if (node instanceof AST_Constant) + base54.consider(node.print_to_string()); + else if (node instanceof AST_Return) + base54.consider("return"); + else if (node instanceof AST_Throw) + base54.consider("throw"); + else if (node instanceof AST_Continue) + base54.consider("continue"); + else if (node instanceof AST_Break) + base54.consider("break"); + else if (node instanceof AST_Debugger) + base54.consider("debugger"); + else if (node instanceof AST_Directive) + base54.consider(node.value); + else if (node instanceof AST_While) + base54.consider("while"); + else if (node instanceof AST_Do) + base54.consider("do while"); + else if (node instanceof AST_If) { + base54.consider("if"); + if (node.alternative) base54.consider("else"); + } + else if (node instanceof AST_Var) + base54.consider("var"); + else if (node instanceof AST_Const) + base54.consider("const"); + else if (node instanceof AST_Lambda) + base54.consider("function"); + else if (node instanceof AST_For) + base54.consider("for"); + else if (node instanceof AST_ForIn) + base54.consider("for in"); + else if (node instanceof AST_Switch) + base54.consider("switch"); + else if (node instanceof AST_Case) + base54.consider("case"); + else if (node instanceof AST_Default) + base54.consider("default"); + else if (node instanceof AST_With) + base54.consider("with"); + else if (node instanceof AST_ObjectSetter) + base54.consider("set" + node.key); + else if (node instanceof AST_ObjectGetter) + base54.consider("get" + node.key); + else if (node instanceof AST_ObjectKeyVal) + base54.consider(node.key); + else if (node instanceof AST_New) + base54.consider("new"); + else if (node instanceof AST_This) + base54.consider("this"); + else if (node instanceof AST_Try) + base54.consider("try"); + else if (node instanceof AST_Catch) + base54.consider("catch"); + else if (node instanceof AST_Finally) + base54.consider("finally"); + else if (node instanceof AST_Symbol && node.unmangleable(options)) + base54.consider(node.name); + else if (node instanceof AST_Unary || node instanceof AST_Binary) + base54.consider(node.operator); + else if (node instanceof AST_Dot) + base54.consider(node.property); + }); + this.walk(tw); + base54.sort(); +}); - if (match('}')) { - lex(); - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; +var base54 = (function() { + var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; + var chars, frequency; + function reset() { + frequency = Object.create(null); + chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); + chars.forEach(function(ch){ frequency[ch] = 0 }); + } + base54.consider = function(str){ + for (var i = str.length; --i >= 0;) { + var code = str.charCodeAt(i); + if (code in frequency) ++frequency[code]; } + }; + base54.sort = function() { + chars = mergeSort(chars, function(a, b){ + if (is_digit(a) && !is_digit(b)) return 1; + if (is_digit(b) && !is_digit(a)) return -1; + return frequency[b] - frequency[a]; + }); + }; + base54.reset = reset; + reset(); + base54.get = function(){ return chars }; + base54.freq = function(){ return frequency }; + function base54(num) { + var ret = "", base = 54; + num++; + do { + num--; + ret += String.fromCharCode(chars[num % base]); + num = Math.floor(num / base); + base = 64; + } while (num > 0); + return ret; + }; + return base54; +})(); - oldInSwitch = state.inSwitch; - state.inSwitch = true; - defaultFound = false; - - while (index < length) { - if (match('}')) { - break; - } - clause = parseSwitchCase(); - if (clause.test === null) { - if (defaultFound) { - throwError({}, Messages.MultipleDefaultsInSwitch); - } - defaultFound = true; +AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ + options = defaults(options, { + assign_to_global : true, + eval : true, + func_arguments : true, + nested_defuns : true, + undeclared : false, // this makes a lot of noise + unreferenced : true, + }); + var tw = new TreeWalker(function(node){ + if (options.undeclared + && node instanceof AST_SymbolRef + && node.undeclared()) + { + // XXX: this also warns about JS standard names, + // i.e. Object, Array, parseInt etc. Should add a list of + // exceptions. + AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.assign_to_global) + { + var sym = null; + if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) + sym = node.left; + else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) + sym = node.init; + if (sym + && (sym.undeclared() + || (sym.global() && sym.scope !== sym.definition().scope))) { + AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { + msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", + name: sym.name, + file: sym.start.file, + line: sym.start.line, + col: sym.start.col + }); } - cases.push(clause); } + if (options.eval + && node instanceof AST_SymbolRef + && node.undeclared() + && node.name == "eval") { + AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); + } + if (options.unreferenced + && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) + && !(node instanceof AST_SymbolCatch) + && node.unreferenced()) { + AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { + type: node instanceof AST_Label ? "Label" : "Symbol", + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.func_arguments + && node instanceof AST_Lambda + && node.uses_arguments) { + AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { + name: node.name ? node.name.name : "anonymous", + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.nested_defuns + && node instanceof AST_Defun + && !(tw.parent() instanceof AST_Scope)) { + AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { + name: node.name.name, + type: tw.parent().TYPE, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + }); + this.walk(tw); +}); - state.inSwitch = oldInSwitch; - - expect('}'); +/*********************************************************************** - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; - } + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 - // 12.13 The throw statement + -------------------------------- (C) --------------------------------- - function parseThrowStatement() { - var argument; + Author: Mihai Bazon + + http://mihai.bazon.net/blog - expectKeyword('throw'); + Distributed under the BSD license: - if (peekLineTerminator()) { - throwError({}, Messages.NewlineAfterThrow); - } + Copyright 2012 (c) Mihai Bazon - argument = parseExpression(); + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - consumeSemicolon(); + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. - return { - type: Syntax.ThrowStatement, - argument: argument - }; - } + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. - // 12.14 The try statement + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. - function parseCatchClause() { - var param; + ***********************************************************************/ - expectKeyword('catch'); +"use strict"; - expect('('); - if (match(')')) { - throwUnexpected(lookahead()); - } +var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/; - param = parseVariableIdentifier(); - // 12.14.1 - if (strict && isRestrictedWord(param.name)) { - throwErrorTolerant({}, Messages.StrictCatchVariable); - } +function is_some_comments(comment) { + // multiline comment + return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value); +} - expect(')'); +function OutputStream(options) { - return { - type: Syntax.CatchClause, - param: param, - body: parseBlock() - }; + options = defaults(options, { + ascii_only : false, + beautify : false, + bracketize : false, + comments : false, + indent_level : 4, + indent_start : 0, + inline_script : true, + keep_quoted_props: false, + max_line_len : false, + preamble : null, + preserve_line : false, + quote_keys : false, + quote_style : 0, + screw_ie8 : true, + semicolons : true, + shebang : true, + source_map : null, + space_colon : true, + unescape_regexps : false, + width : 80, + wrap_iife : false, + }, true); + + // Convert comment option to RegExp if neccessary and set up comments filter + var comment_filter = return_false; // Default case, throw all comments away + if (options.comments) { + var comments = options.comments; + if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { + var regex_pos = options.comments.lastIndexOf("/"); + comments = new RegExp( + options.comments.substr(1, regex_pos - 1), + options.comments.substr(regex_pos + 1) + ); + } + if (comments instanceof RegExp) { + comment_filter = function(comment) { + return comment.type != "comment5" && comments.test(comment.value); + }; + } + else if (typeof comments === "function") { + comment_filter = function(comment) { + return comment.type != "comment5" && comments(this, comment); + }; + } + else if (comments === "some") { + comment_filter = is_some_comments; + } else { // NOTE includes "all" option + comment_filter = return_true; + } } - function parseTryStatement() { - var block, handlers = [], finalizer = null; - - expectKeyword('try'); + var indentation = 0; + var current_col = 0; + var current_line = 1; + var current_pos = 0; + var OUTPUT = ""; - block = parseBlock(); + function to_ascii(str, identifier) { + return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { + var code = ch.charCodeAt(0).toString(16); + if (code.length <= 2 && !identifier) { + while (code.length < 2) code = "0" + code; + return "\\x" + code; + } else { + while (code.length < 4) code = "0" + code; + return "\\u" + code; + } + }); + }; - if (matchKeyword('catch')) { - handlers.push(parseCatchClause()); + function make_string(str, quote) { + var dq = 0, sq = 0; + str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, + function(s, i){ + switch (s) { + case '"': ++dq; return '"'; + case "'": ++sq; return "'"; + case "\\": return "\\\\"; + case "\n": return "\\n"; + case "\r": return "\\r"; + case "\t": return "\\t"; + case "\b": return "\\b"; + case "\f": return "\\f"; + case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B"; + case "\u2028": return "\\u2028"; + case "\u2029": return "\\u2029"; + case "\ufeff": return "\\ufeff"; + case "\0": + return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; + } + return s; + }); + function quote_single() { + return "'" + str.replace(/\x27/g, "\\'") + "'"; + } + function quote_double() { + return '"' + str.replace(/\x22/g, '\\"') + '"'; + } + if (options.ascii_only) str = to_ascii(str); + switch (options.quote_style) { + case 1: + return quote_single(); + case 2: + return quote_double(); + case 3: + return quote == "'" ? quote_single() : quote_double(); + default: + return dq > sq ? quote_single() : quote_double(); } + }; - if (matchKeyword('finally')) { - lex(); - finalizer = parseBlock(); + function encode_string(str, quote) { + var ret = make_string(str, quote); + if (options.inline_script) { + ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); + ret = ret.replace(/\x3c!--/g, "\\x3c!--"); + ret = ret.replace(/--\x3e/g, "--\\x3e"); } + return ret; + }; - if (handlers.length === 0 && !finalizer) { - throwError({}, Messages.NoCatchOrFinally); - } + function make_name(name) { + name = name.toString(); + if (options.ascii_only) + name = to_ascii(name, true); + return name; + }; - return { - type: Syntax.TryStatement, - block: block, - guardedHandlers: [], - handlers: handlers, - finalizer: finalizer - }; - } + function make_indent(back) { + return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); + }; - // 12.15 The debugger statement + /* -----[ beautification/minification ]----- */ - function parseDebuggerStatement() { - expectKeyword('debugger'); + var might_need_space = false; + var might_need_semicolon = false; + var might_add_newline = 0; + var last = ""; - consumeSemicolon(); + var ensure_line_len = options.max_line_len ? function() { + if (current_col > options.max_line_len) { + if (might_add_newline) { + var left = OUTPUT.slice(0, might_add_newline); + var right = OUTPUT.slice(might_add_newline); + OUTPUT = left + "\n" + right; + current_line++; + current_pos++; + current_col = right.length; + } + if (current_col > options.max_line_len) { + AST_Node.warn("Output exceeds {max_line_len} characters", options); + } + } + might_add_newline = 0; + } : noop; - return { - type: Syntax.DebuggerStatement - }; - } + var requireSemicolonChars = makePredicate("( [ + * / - , ."); - // 12 Statements + function print(str) { + str = String(str); + var ch = str.charAt(0); + var prev = last.charAt(last.length - 1); + if (might_need_semicolon) { + might_need_semicolon = false; - function parseStatement() { - var token = lookahead(), - expr, - labeledBody; + if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") { + if (options.semicolons || requireSemicolonChars(ch)) { + OUTPUT += ";"; + current_col++; + current_pos++; + } else { + ensure_line_len(); + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; - if (token.type === Token.EOF) { - throwUnexpected(token); - } + if (/^\s+$/.test(str)) { + // reset the semicolon flag, since we didn't print one + // now and might still have to later + might_need_semicolon = true; + } + } - if (token.type === Token.Punctuator) { - switch (token.value) { - case ';': - return parseEmptyStatement(); - case '{': - return parseBlock(); - case '(': - return parseExpressionStatement(); - default: - break; + if (!options.beautify) + might_need_space = false; } } - if (token.type === Token.Keyword) { - switch (token.value) { - case 'break': - return parseBreakStatement(); - case 'continue': - return parseContinueStatement(); - case 'debugger': - return parseDebuggerStatement(); - case 'do': - return parseDoWhileStatement(); - case 'for': - return parseForStatement(); - case 'function': - return parseFunctionDeclaration(); - case 'if': - return parseIfStatement(); - case 'return': - return parseReturnStatement(); - case 'switch': - return parseSwitchStatement(); - case 'throw': - return parseThrowStatement(); - case 'try': - return parseTryStatement(); - case 'var': - return parseVariableStatement(); - case 'while': - return parseWhileStatement(); - case 'with': - return parseWithStatement(); - default: - break; + if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { + var target_line = stack[stack.length - 1].start.line; + while (current_line < target_line) { + ensure_line_len(); + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; + might_need_space = false; } } - expr = parseExpression(); - - // 12.12 Labelled Statements - if ((expr.type === Syntax.Identifier) && match(':')) { - lex(); - - if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) { - throwError({}, Messages.Redeclaration, 'Label', expr.name); - } + if (might_need_space) { + if ((is_identifier_char(prev) + && (is_identifier_char(ch) || ch == "\\")) + || (ch == "/" && ch == prev) + || ((ch == "+" || ch == "-") && ch == last)) + { + OUTPUT += " "; + current_col++; + current_pos++; + } + might_need_space = false; + } + OUTPUT += str; + current_pos += str.length; + var a = str.split(/\r?\n/), n = a.length - 1; + current_line += n; + current_col += a[0].length; + if (n > 0) { + ensure_line_len(); + current_col = a[n].length; + } + last = str; + }; - state.labelSet[expr.name] = true; - labeledBody = parseStatement(); - delete state.labelSet[expr.name]; + var space = options.beautify ? function() { + print(" "); + } : function() { + might_need_space = true; + }; - return { - type: Syntax.LabeledStatement, - label: expr, - body: labeledBody - }; + var indent = options.beautify ? function(half) { + if (options.beautify) { + print(make_indent(half ? 0.5 : 0)); } + } : noop; - consumeSemicolon(); + var with_indent = options.beautify ? function(col, cont) { + if (col === true) col = next_indent(); + var save_indentation = indentation; + indentation = col; + var ret = cont(); + indentation = save_indentation; + return ret; + } : function(col, cont) { return cont() }; + + var newline = options.beautify ? function() { + print("\n"); + } : options.max_line_len ? function() { + ensure_line_len(); + might_add_newline = OUTPUT.length; + } : noop; + + var semicolon = options.beautify ? function() { + print(";"); + } : function() { + might_need_semicolon = true; + }; - return { - type: Syntax.ExpressionStatement, - expression: expr - }; - } + function force_semicolon() { + might_need_semicolon = false; + print(";"); + }; - // 13 Function Definition + function next_indent() { + return indentation + options.indent_level; + }; - function parseFunctionSourceElements() { - var sourceElement, sourceElements = [], token, directive, firstRestricted, - oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody; + function with_block(cont) { + var ret; + print("{"); + newline(); + with_indent(next_indent(), function(){ + ret = cont(); + }); + indent(); + print("}"); + return ret; + }; - expect('{'); + function with_parens(cont) { + print("("); + //XXX: still nice to have that for argument lists + //var ret = with_indent(current_col, cont); + var ret = cont(); + print(")"); + return ret; + }; - while (index < length) { - token = lookahead(); - if (token.type !== Token.StringLiteral) { - break; - } + function with_square(cont) { + print("["); + //var ret = with_indent(current_col, cont); + var ret = cont(); + print("]"); + return ret; + }; - sourceElement = parseSourceElement(); - sourceElements.push(sourceElement); - if (sourceElement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } - } - } + function comma() { + print(","); + space(); + }; - oldLabelSet = state.labelSet; - oldInIteration = state.inIteration; - oldInSwitch = state.inSwitch; - oldInFunctionBody = state.inFunctionBody; + function colon() { + print(":"); + if (options.space_colon) space(); + }; - state.labelSet = {}; - state.inIteration = false; - state.inSwitch = false; - state.inFunctionBody = true; + var add_mapping = options.source_map ? function(token, name) { + try { + if (token) options.source_map.add( + token.file || "?", + current_line, current_col, + token.line, token.col, + (!name && token.type == "name") ? token.value : name + ); + } catch(ex) { + AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", { + file: token.file, + line: token.line, + col: token.col, + cline: current_line, + ccol: current_col, + name: name || "" + }) + } + } : noop; + + function get() { + if (might_add_newline) { + ensure_line_len(); + } + return OUTPUT; + }; - while (index < length) { - if (match('}')) { - break; - } - sourceElement = parseSourceElement(); - if (typeof sourceElement === 'undefined') { - break; - } - sourceElements.push(sourceElement); + var stack = []; + return { + get : get, + toString : get, + indent : indent, + indentation : function() { return indentation }, + current_width : function() { return current_col - indentation }, + should_break : function() { return options.width && this.current_width() >= options.width }, + newline : newline, + print : print, + space : space, + comma : comma, + colon : colon, + last : function() { return last }, + semicolon : semicolon, + force_semicolon : force_semicolon, + to_ascii : to_ascii, + print_name : function(name) { print(make_name(name)) }, + print_string : function(str, quote, escape_directive) { + var encoded = encode_string(str, quote); + if (escape_directive === true && encoded.indexOf("\\") === -1) { + // Insert semicolons to break directive prologue + if (!EXPECT_DIRECTIVE.test(OUTPUT)) { + force_semicolon(); + } + force_semicolon(); + } + print(encoded); + }, + encode_string : encode_string, + next_indent : next_indent, + with_indent : with_indent, + with_block : with_block, + with_parens : with_parens, + with_square : with_square, + add_mapping : add_mapping, + option : function(opt) { return options[opt] }, + comment_filter : comment_filter, + line : function() { return current_line }, + col : function() { return current_col }, + pos : function() { return current_pos }, + push_node : function(node) { stack.push(node) }, + pop_node : function() { return stack.pop() }, + parent : function(n) { + return stack[stack.length - 2 - (n || 0)]; } + }; - expect('}'); +}; - state.labelSet = oldLabelSet; - state.inIteration = oldInIteration; - state.inSwitch = oldInSwitch; - state.inFunctionBody = oldInFunctionBody; +/* -----[ code generators ]----- */ - return { - type: Syntax.BlockStatement, - body: sourceElements - }; - } +(function(){ - function parseFunctionDeclaration() { - var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet; + /* -----[ utils ]----- */ - expectKeyword('function'); - token = lookahead(); - id = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictFunctionName); - } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } - } - - expect('('); - - if (!match(')')) { - paramSet = {}; - while (index < length) { - token = lookahead(); - param = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - stricted = token; - message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - stricted = token; - message = Messages.StrictParamDupe; - } - } else if (!firstRestricted) { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - firstRestricted = token; - message = Messages.StrictParamDupe; - } - } - params.push(param); - paramSet[param.name] = true; - if (match(')')) { - break; - } - expect(','); - } - } + function DEFPRINT(nodetype, generator) { + nodetype.DEFMETHOD("_codegen", generator); + }; - expect(')'); + var use_asm = false; + var in_directive = false; - previousStrict = strict; - body = parseFunctionSourceElements(); - if (strict && firstRestricted) { - throwError(firstRestricted, message); + AST_Node.DEFMETHOD("print", function(stream, force_parens){ + var self = this, generator = self._codegen, prev_use_asm = use_asm; + if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) { + use_asm = true; } - if (strict && stricted) { - throwErrorTolerant(stricted, message); + function doit() { + self.add_comments(stream); + self.add_source_map(stream); + generator(self, stream); + } + stream.push_node(self); + if (force_parens || self.needs_parens(stream)) { + stream.with_parens(doit); + } else { + doit(); + } + stream.pop_node(); + if (self instanceof AST_Scope) { + use_asm = prev_use_asm; } - strict = previousStrict; + }); - return { - type: Syntax.FunctionDeclaration, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; - } + AST_Node.DEFMETHOD("print_to_string", function(options){ + var s = OutputStream(options); + if (!options) s._readonly = true; + this.print(s); + return s.get(); + }); - function parseFunctionExpression() { - var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet; + /* -----[ comments ]----- */ - expectKeyword('function'); + AST_Node.DEFMETHOD("add_comments", function(output){ + if (output._readonly) return; + var self = this; + var start = self.start; + if (start && !start._comments_dumped) { + start._comments_dumped = true; + var comments = start.comments_before || []; + + // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 + // and https://github.com/mishoo/UglifyJS2/issues/372 + if (self instanceof AST_Exit && self.value) { + self.value.walk(new TreeWalker(function(node){ + if (node.start && node.start.comments_before) { + comments = comments.concat(node.start.comments_before); + node.start.comments_before = []; + } + if (node instanceof AST_Function || + node instanceof AST_Array || + node instanceof AST_Object) + { + return true; // don't go inside. + } + })); + } - if (!match('(')) { - token = lookahead(); - id = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - throwErrorTolerant(token, Messages.StrictFunctionName); + if (output.pos() == 0) { + if (comments.length > 0 && output.option("shebang") && comments[0].type == "comment5") { + output.print("#!" + comments.shift().value + "\n"); + output.indent(); } - } else { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictFunctionName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; + var preamble = output.option("preamble"); + if (preamble) { + output.print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); } } - } - expect('('); + comments = comments.filter(output.comment_filter, self); - if (!match(')')) { - paramSet = {}; - while (index < length) { - token = lookahead(); - param = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - stricted = token; - message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - stricted = token; - message = Messages.StrictParamDupe; - } - } else if (!firstRestricted) { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - firstRestricted = token; - message = Messages.StrictParamDupe; - } + // Keep single line comments after nlb, after nlb + if (!output.option("beautify") && comments.length > 0 && + /comment[134]/.test(comments[0].type) && + output.col() !== 0 && comments[0].nlb) + { + output.print("\n"); + } + + comments.forEach(function(c){ + if (/comment[134]/.test(c.type)) { + output.print("//" + c.value + "\n"); + output.indent(); } - params.push(param); - paramSet[param.name] = true; - if (match(')')) { - break; + else if (c.type == "comment2") { + output.print("/*" + c.value + "*/"); + if (start.nlb) { + output.print("\n"); + output.indent(); + } else { + output.space(); + } } - expect(','); - } + }); } + }); - expect(')'); + /* -----[ PARENTHESES ]----- */ - previousStrict = strict; - body = parseFunctionSourceElements(); - if (strict && firstRestricted) { - throwError(firstRestricted, message); - } - if (strict && stricted) { - throwErrorTolerant(stricted, message); + function PARENS(nodetype, func) { + if (Array.isArray(nodetype)) { + nodetype.forEach(function(nodetype){ + PARENS(nodetype, func); + }); + } else { + nodetype.DEFMETHOD("needs_parens", func); } - strict = previousStrict; - - return { - type: Syntax.FunctionExpression, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; - } - - // 14 Program + }; - function parseSourceElement() { - var token = lookahead(); + PARENS(AST_Node, function(){ + return false; + }); - if (token.type === Token.Keyword) { - switch (token.value) { - case 'const': - case 'let': - return parseConstLetDeclaration(token.value); - case 'function': - return parseFunctionDeclaration(); - default: - return parseStatement(); - } + // a function expression needs parens around it when it's provably + // the first token to appear in a statement. + PARENS(AST_Function, function(output){ + if (first_in_statement(output)) { + return true; } - if (token.type !== Token.EOF) { - return parseStatement(); + if (output.option('wrap_iife')) { + var p = output.parent(); + return p instanceof AST_Call && p.expression === this; } - } - function parseSourceElements() { - var sourceElement, sourceElements = [], token, directive, firstRestricted; + return false; + }); - while (index < length) { - token = lookahead(); - if (token.type !== Token.StringLiteral) { - break; - } + // same goes for an object literal, because otherwise it would be + // interpreted as a block of code. + PARENS(AST_Object, function(output){ + return first_in_statement(output); + }); - sourceElement = parseSourceElement(); - sourceElements.push(sourceElement); - if (sourceElement.expression.type !== Syntax.Literal) { - // this is not directive - break; - } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); - if (directive === 'use strict') { - strict = true; - if (firstRestricted) { - throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); - } - } else { - if (!firstRestricted && token.octal) { - firstRestricted = token; - } + PARENS(AST_Unary, function(output){ + var p = output.parent(); + return p instanceof AST_PropAccess && p.expression === this + || p instanceof AST_Call && p.expression === this; + }); + + PARENS(AST_Seq, function(output){ + var p = output.parent(); + return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) + || p instanceof AST_Unary // !(foo, bar, baz) + || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8 + || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4 + || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 + || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] + || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 + || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) + * ==> 20 (side effect, set a := 10 and b := 20) */ + ; + }); + + PARENS(AST_Binary, function(output){ + var p = output.parent(); + // (foo && bar)() + if (p instanceof AST_Call && p.expression === this) + return true; + // typeof (foo && bar) + if (p instanceof AST_Unary) + return true; + // (foo && bar)["prop"], (foo && bar).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + // this deals with precedence: 3 * (2 + 1) + if (p instanceof AST_Binary) { + var po = p.operator, pp = PRECEDENCE[po]; + var so = this.operator, sp = PRECEDENCE[so]; + if (pp > sp + || (pp == sp + && this === p.right)) { + return true; } } + }); - while (index < length) { - sourceElement = parseSourceElement(); - if (typeof sourceElement === 'undefined') { - break; + PARENS(AST_PropAccess, function(output){ + var p = output.parent(); + if (p instanceof AST_New && p.expression === this) { + // i.e. new (foo.bar().baz) + // + // if there's one call into this subtree, then we need + // parens around it too, otherwise the call will be + // interpreted as passing the arguments to the upper New + // expression. + try { + this.walk(new TreeWalker(function(node){ + if (node instanceof AST_Call) throw p; + })); + } catch(ex) { + if (ex !== p) throw ex; + return true; } - sourceElements.push(sourceElement); } - return sourceElements; - } + }); - function parseProgram() { - var program; - strict = false; - program = { - type: Syntax.Program, - body: parseSourceElements() - }; - return program; - } + PARENS(AST_Call, function(output){ + var p = output.parent(), p1; + if (p instanceof AST_New && p.expression === this) + return true; - // The following functions are needed only when the option to preserve - // the comments is active. + // workaround for Safari bug. + // https://bugs.webkit.org/show_bug.cgi?id=123506 + return this.expression instanceof AST_Function + && p instanceof AST_PropAccess + && p.expression === this + && (p1 = output.parent(1)) instanceof AST_Assign + && p1.left === p; + }); - function addComment(type, value, start, end, loc) { - assert(typeof start === 'number', 'Comment must have valid position'); + PARENS(AST_New, function(output){ + var p = output.parent(); + if (!need_constructor_parens(this, output) + && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() + || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) + return true; + }); - // Because the way the actual token is scanned, often the comments - // (if any) are skipped twice during the lexical analysis. - // Thus, we need to skip adding a comment if the comment array already - // handled it. - if (extra.comments.length > 0) { - if (extra.comments[extra.comments.length - 1].range[1] > start) { - return; + PARENS(AST_Number, function(output){ + var p = output.parent(); + if (p instanceof AST_PropAccess && p.expression === this) { + var value = this.getValue(); + if (value < 0 || /^0/.test(make_num(value))) { + return true; } } + }); - extra.comments.push({ - type: type, - value: value, - range: [start, end], - loc: loc - }); - } + PARENS([ AST_Assign, AST_Conditional ], function (output){ + var p = output.parent(); + // !(a = false) → true + if (p instanceof AST_Unary) + return true; + // 1 + (a = 2) + 3 → 6, side effect setting a = 2 + if (p instanceof AST_Binary && !(p instanceof AST_Assign)) + return true; + // (a = func)() —or— new (a = Object)() + if (p instanceof AST_Call && p.expression === this) + return true; + // (a = foo) ? bar : baz + if (p instanceof AST_Conditional && p.condition === this) + return true; + // (a = foo)["prop"] —or— (a = foo).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + }); - function scanComment() { - var comment, ch, loc, start, blockComment, lineComment; + /* -----[ PRINTERS ]----- */ - comment = ''; - blockComment = false; - lineComment = false; + DEFPRINT(AST_Directive, function(self, output){ + output.print_string(self.value, self.quote); + output.semicolon(); + }); + DEFPRINT(AST_Debugger, function(self, output){ + output.print("debugger"); + output.semicolon(); + }); - while (index < length) { - ch = source[index]; + /* -----[ statements ]----- */ - if (lineComment) { - ch = source[index++]; - if (isLineTerminator(ch)) { - loc.end = { - line: lineNumber, - column: index - lineStart - 1 - }; - lineComment = false; - addComment('Line', comment, start, index - 1, loc); - if (ch === '\r' && source[index] === '\n') { - ++index; - } - ++lineNumber; - lineStart = index; - comment = ''; - } else if (index >= length) { - lineComment = false; - comment += ch; - loc.end = { - line: lineNumber, - column: length - lineStart - }; - addComment('Line', comment, start, length, loc); - } else { - comment += ch; - } - } else if (blockComment) { - if (isLineTerminator(ch)) { - if (ch === '\r' && source[index + 1] === '\n') { - ++index; - comment += '\r\n'; - } else { - comment += ch; - } - ++lineNumber; - ++index; - lineStart = index; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - ch = source[index++]; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - comment += ch; - if (ch === '*') { - ch = source[index]; - if (ch === '/') { - comment = comment.substr(0, comment.length - 1); - blockComment = false; - ++index; - loc.end = { - line: lineNumber, - column: index - lineStart - }; - addComment('Block', comment, start, index, loc); - comment = ''; - } - } - } - } else if (ch === '/') { - ch = source[index + 1]; - if (ch === '/') { - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; - start = index; - index += 2; - lineComment = true; - if (index >= length) { - loc.end = { - line: lineNumber, - column: index - lineStart - }; - lineComment = false; - addComment('Line', comment, start, index, loc); - } - } else if (ch === '*') { - start = index; - index += 2; - blockComment = true; - loc = { - start: { - line: lineNumber, - column: index - lineStart - 2 - } - }; - if (index >= length) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } else { - break; - } - } else if (isWhiteSpace(ch)) { - ++index; - } else if (isLineTerminator(ch)) { - ++index; - if (ch === '\r' && source[index] === '\n') { - ++index; + function display_body(body, is_toplevel, output, allow_directives) { + var last = body.length - 1; + in_directive = allow_directives; + body.forEach(function(stmt, i){ + if (in_directive === true && !(stmt instanceof AST_Directive || + stmt instanceof AST_EmptyStatement || + (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) + )) { + in_directive = false; + } + if (!(stmt instanceof AST_EmptyStatement)) { + output.indent(); + stmt.print(output); + if (!(i == last && is_toplevel)) { + output.newline(); + if (is_toplevel) output.newline(); } - ++lineNumber; - lineStart = index; - } else { - break; } - } - } + if (in_directive === true && + stmt instanceof AST_SimpleStatement && + stmt.body instanceof AST_String + ) { + in_directive = false; + } + }); + in_directive = false; + }; - function filterCommentLocation() { - var i, entry, comment, comments = []; + AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ + force_statement(this.body, output); + }); - for (i = 0; i < extra.comments.length; ++i) { - entry = extra.comments[i]; - comment = { - type: entry.type, - value: entry.value - }; - if (extra.range) { - comment.range = entry.range; + DEFPRINT(AST_Statement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + DEFPRINT(AST_Toplevel, function(self, output){ + display_body(self.body, true, output, true); + output.print(""); + }); + DEFPRINT(AST_LabeledStatement, function(self, output){ + self.label.print(output); + output.colon(); + self.body.print(output); + }); + DEFPRINT(AST_SimpleStatement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + function print_bracketed(body, output, allow_directives) { + if (body.length > 0) output.with_block(function(){ + display_body(body, false, output, allow_directives); + }); + else output.print("{}"); + }; + DEFPRINT(AST_BlockStatement, function(self, output){ + print_bracketed(self.body, output); + }); + DEFPRINT(AST_EmptyStatement, function(self, output){ + output.semicolon(); + }); + DEFPRINT(AST_Do, function(self, output){ + output.print("do"); + output.space(); + make_block(self.body, output); + output.space(); + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); + }); + output.semicolon(); + }); + DEFPRINT(AST_While, function(self, output){ + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_For, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + if (self.init) { + if (self.init instanceof AST_Definitions) { + self.init.print(output); + } else { + parenthesize_for_noin(self.init, output, true); + } + output.print(";"); + output.space(); + } else { + output.print(";"); } - if (extra.loc) { - comment.loc = entry.loc; + if (self.condition) { + self.condition.print(output); + output.print(";"); + output.space(); + } else { + output.print(";"); } - comments.push(comment); - } - - extra.comments = comments; - } - - function collectToken() { - var start, loc, token, range, value; - - skipComment(); - start = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart + if (self.step) { + self.step.print(output); } - }; - - token = extra.advance(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_ForIn, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + self.init.print(output); + output.space(); + output.print("in"); + output.space(); + self.object.print(output); + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_With, function(self, output){ + output.print("with"); + output.space(); + output.with_parens(function(){ + self.expression.print(output); + }); + output.space(); + self._do_print_body(output); + }); - if (token.type !== Token.EOF) { - range = [token.range[0], token.range[1]]; - value = sliceSource(token.range[0], token.range[1]); - extra.tokens.push({ - type: TokenName[token.type], - value: value, - range: range, - loc: loc - }); + /* -----[ functions ]----- */ + AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ + var self = this; + if (!nokeyword) { + output.print("function"); } + if (self.name) { + output.space(); + self.name.print(output); + } + output.with_parens(function(){ + self.argnames.forEach(function(arg, i){ + if (i) output.comma(); + arg.print(output); + }); + }); + output.space(); + print_bracketed(self.body, output, true); + }); + DEFPRINT(AST_Lambda, function(self, output){ + self._do_print(output); + }); - return token; - } - - function collectRegex() { - var pos, loc, regex, token; - - skipComment(); - - pos = index; - loc = { - start: { - line: lineNumber, - column: index - lineStart - } - }; + /* -----[ exits ]----- */ + AST_Exit.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.value) { + output.space(); + this.value.print(output); + } + output.semicolon(); + }); + DEFPRINT(AST_Return, function(self, output){ + self._do_print(output, "return"); + }); + DEFPRINT(AST_Throw, function(self, output){ + self._do_print(output, "throw"); + }); - regex = extra.scanRegExp(); - loc.end = { - line: lineNumber, - column: index - lineStart - }; + /* -----[ loop control ]----- */ + AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.label) { + output.space(); + this.label.print(output); + } + output.semicolon(); + }); + DEFPRINT(AST_Break, function(self, output){ + self._do_print(output, "break"); + }); + DEFPRINT(AST_Continue, function(self, output){ + self._do_print(output, "continue"); + }); - // Pop the previous token, which is likely '/' or '/=' - if (extra.tokens.length > 0) { - token = extra.tokens[extra.tokens.length - 1]; - if (token.range[0] === pos && token.type === 'Punctuator') { - if (token.value === '/' || token.value === '/=') { - extra.tokens.pop(); + /* -----[ if ]----- */ + function make_then(self, output) { + var b = self.body; + if (output.option("bracketize") + || !output.option("screw_ie8") && b instanceof AST_Do) + return make_block(b, output); + // The squeezer replaces "block"-s that contain only a single + // statement with the statement itself; technically, the AST + // is correct, but this can create problems when we output an + // IF having an ELSE clause where the THEN clause ends in an + // IF *without* an ELSE block (then the outer ELSE would refer + // to the inner IF). This function checks for this case and + // adds the block brackets if needed. + if (!b) return output.force_semicolon(); + while (true) { + if (b instanceof AST_If) { + if (!b.alternative) { + make_block(self.body, output); + return; } + b = b.alternative; } + else if (b instanceof AST_StatementWithBody) { + b = b.body; + } + else break; } - - extra.tokens.push({ - type: 'RegularExpression', - value: regex.literal, - range: [pos, index], - loc: loc + force_statement(self.body, output); + }; + DEFPRINT(AST_If, function(self, output){ + output.print("if"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); }); + output.space(); + if (self.alternative) { + make_then(self, output); + output.space(); + output.print("else"); + output.space(); + if (self.alternative instanceof AST_If) + self.alternative.print(output); + else + force_statement(self.alternative, output); + } else { + self._do_print_body(output); + } + }); - return regex; - } - - function filterTokenLocation() { - var i, entry, token, tokens = []; + /* -----[ switch ]----- */ + DEFPRINT(AST_Switch, function(self, output){ + output.print("switch"); + output.space(); + output.with_parens(function(){ + self.expression.print(output); + }); + output.space(); + var last = self.body.length - 1; + if (last < 0) output.print("{}"); + else output.with_block(function(){ + self.body.forEach(function(branch, i){ + output.indent(true); + branch.print(output); + if (i < last && branch.body.length > 0) + output.newline(); + }); + }); + }); + AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ + output.newline(); + this.body.forEach(function(stmt){ + output.indent(); + stmt.print(output); + output.newline(); + }); + }); + DEFPRINT(AST_Default, function(self, output){ + output.print("default:"); + self._do_print_body(output); + }); + DEFPRINT(AST_Case, function(self, output){ + output.print("case"); + output.space(); + self.expression.print(output); + output.print(":"); + self._do_print_body(output); + }); - for (i = 0; i < extra.tokens.length; ++i) { - entry = extra.tokens[i]; - token = { - type: entry.type, - value: entry.value - }; - if (extra.range) { - token.range = entry.range; - } - if (extra.loc) { - token.loc = entry.loc; - } - tokens.push(token); + /* -----[ exceptions ]----- */ + DEFPRINT(AST_Try, function(self, output){ + output.print("try"); + output.space(); + print_bracketed(self.body, output); + if (self.bcatch) { + output.space(); + self.bcatch.print(output); } + if (self.bfinally) { + output.space(); + self.bfinally.print(output); + } + }); + DEFPRINT(AST_Catch, function(self, output){ + output.print("catch"); + output.space(); + output.with_parens(function(){ + self.argname.print(output); + }); + output.space(); + print_bracketed(self.body, output); + }); + DEFPRINT(AST_Finally, function(self, output){ + output.print("finally"); + output.space(); + print_bracketed(self.body, output); + }); - extra.tokens = tokens; - } - - function createLiteral(token) { - return { - type: Syntax.Literal, - value: token.value - }; - } - - function createRawLiteral(token) { - return { - type: Syntax.Literal, - value: token.value, - raw: sliceSource(token.range[0], token.range[1]) - }; - } + /* -----[ var/const ]----- */ + AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + output.space(); + this.definitions.forEach(function(def, i){ + if (i) output.comma(); + def.print(output); + }); + var p = output.parent(); + var in_for = p instanceof AST_For || p instanceof AST_ForIn; + var avoid_semicolon = in_for && p.init === this; + if (!avoid_semicolon) + output.semicolon(); + }); + DEFPRINT(AST_Var, function(self, output){ + self._do_print(output, "var"); + }); + DEFPRINT(AST_Const, function(self, output){ + self._do_print(output, "const"); + }); - function createLocationMarker() { - var marker = {}; + function parenthesize_for_noin(node, output, noin) { + if (!noin) node.print(output); + else try { + // need to take some precautions here: + // https://github.com/mishoo/UglifyJS2/issues/60 + node.walk(new TreeWalker(function(node){ + if (node instanceof AST_Binary && node.operator == "in") + throw output; + })); + node.print(output); + } catch(ex) { + if (ex !== output) throw ex; + node.print(output, true); + } + }; - marker.range = [index, index]; - marker.loc = { - start: { - line: lineNumber, - column: index - lineStart - }, - end: { - line: lineNumber, - column: index - lineStart - } - }; + DEFPRINT(AST_VarDef, function(self, output){ + self.name.print(output); + if (self.value) { + output.space(); + output.print("="); + output.space(); + var p = output.parent(1); + var noin = p instanceof AST_For || p instanceof AST_ForIn; + parenthesize_for_noin(self.value, output, noin); + } + }); - marker.end = function () { - this.range[1] = index; - this.loc.end.line = lineNumber; - this.loc.end.column = index - lineStart; - }; + /* -----[ other expressions ]----- */ + DEFPRINT(AST_Call, function(self, output){ + self.expression.print(output); + if (self instanceof AST_New && !need_constructor_parens(self, output)) + return; + output.with_parens(function(){ + self.args.forEach(function(expr, i){ + if (i) output.comma(); + expr.print(output); + }); + }); + }); + DEFPRINT(AST_New, function(self, output){ + output.print("new"); + output.space(); + AST_Call.prototype._codegen(self, output); + }); - marker.applyGroup = function (node) { - if (extra.range) { - node.groupRange = [this.range[0], this.range[1]]; - } - if (extra.loc) { - node.groupLoc = { - start: { - line: this.loc.start.line, - column: this.loc.start.column - }, - end: { - line: this.loc.end.line, - column: this.loc.end.column - } - }; + AST_Seq.DEFMETHOD("_do_print", function(output){ + this.car.print(output); + if (this.cdr) { + output.comma(); + if (output.should_break()) { + output.newline(); + output.indent(); } - }; + this.cdr.print(output); + } + }); + DEFPRINT(AST_Seq, function(self, output){ + self._do_print(output); + // var p = output.parent(); + // if (p instanceof AST_Statement) { + // output.with_indent(output.next_indent(), function(){ + // self._do_print(output); + // }); + // } else { + // self._do_print(output); + // } + }); + DEFPRINT(AST_Dot, function(self, output){ + var expr = self.expression; + expr.print(output); + if (expr instanceof AST_Number && expr.getValue() >= 0) { + if (!/[xa-f.)]/i.test(output.last())) { + output.print("."); + } + } + output.print("."); + // the name after dot would be mapped about here. + output.add_mapping(self.end); + output.print_name(self.property); + }); + DEFPRINT(AST_Sub, function(self, output){ + self.expression.print(output); + output.print("["); + self.property.print(output); + output.print("]"); + }); + DEFPRINT(AST_UnaryPrefix, function(self, output){ + var op = self.operator; + output.print(op); + if (/^[a-z]/i.test(op) + || (/[+-]$/.test(op) + && self.expression instanceof AST_UnaryPrefix + && /^[+-]/.test(self.expression.operator))) { + output.space(); + } + self.expression.print(output); + }); + DEFPRINT(AST_UnaryPostfix, function(self, output){ + self.expression.print(output); + output.print(self.operator); + }); + DEFPRINT(AST_Binary, function(self, output){ + var op = self.operator; + self.left.print(output); + if (op[0] == ">" /* ">>" ">>>" ">" ">=" */ + && self.left instanceof AST_UnaryPostfix + && self.left.operator == "--") { + // space is mandatory to avoid outputting --> + output.print(" "); + } else { + // the space is optional depending on "beautify" + output.space(); + } + output.print(op); + if ((op == "<" || op == "<<") + && self.right instanceof AST_UnaryPrefix + && self.right.operator == "!" + && self.right.expression instanceof AST_UnaryPrefix + && self.right.expression.operator == "--") { + // space is mandatory to avoid outputting x -= 2 + self.operator = self.right.operator + "="; + self.right = self.right.right; } - if (self.body instanceof AST_EmptyStatement && self.alternative && self.alternative instanceof AST_SimpleStatement) { - return make_node(AST_SimpleStatement, self, { - body: make_node(AST_Binary, self, { - operator: "||", - left: self.condition, - right: self.alternative.body - }) - }).transform(compressor); + else if (self.right.right instanceof AST_SymbolRef + && self.right.right.name == self.left.name + && member(self.right.operator, ASSIGN_OPS_COMMUTATIVE) + && !self.right.left.has_side_effects(compressor)) { + // x = 2 & x ---> x &= 2 + self.operator = self.right.operator + "="; + self.right = self.right.left; } - if (self.body instanceof AST_Exit && self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) { - return make_node(self.body.CTOR, self, { - value: make_node(AST_Conditional, self, { - condition: self.condition, - consequent: self.body.value || make_node(AST_Undefined, self.body).optimize(compressor), - alternative: self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor) - }) - }).transform(compressor); + } + return self; + }); + + OPT(AST_Conditional, function(self, compressor){ + if (!compressor.option("conditionals")) return self; + if (self.condition instanceof AST_Seq) { + var car = self.condition.car; + self.condition = self.condition.cdr; + return AST_Seq.cons(car, self); + } + var cond = self.condition.evaluate(compressor); + if (cond !== self.condition) { + if (cond) { + compressor.warn("Condition always true [{file}:{line},{col}]", self.start); + return maintain_this_binding(compressor.parent(), self, self.consequent); + } else { + compressor.warn("Condition always false [{file}:{line},{col}]", self.start); + return maintain_this_binding(compressor.parent(), self, self.alternative); } - if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { - self.condition = make_node(AST_Binary, self.condition, { - operator: "&&", + } + var negated = cond.negate(compressor, first_in_statement(compressor)); + if (best_of(compressor, cond, negated) === negated) { + self = make_node(AST_Conditional, self, { + condition: negated, + consequent: self.alternative, + alternative: self.consequent + }); + } + var condition = self.condition; + var consequent = self.consequent; + var alternative = self.alternative; + // x?x:y --> x||y + if (condition instanceof AST_SymbolRef + && consequent instanceof AST_SymbolRef + && condition.definition() === consequent.definition()) { + return make_node(AST_Binary, self, { + operator: "||", + left: condition, + right: alternative + }); + } + // if (foo) exp = something; else exp = something_else; + // | + // v + // exp = foo ? something : something_else; + if (consequent instanceof AST_Assign + && alternative instanceof AST_Assign + && consequent.operator == alternative.operator + && consequent.left.equivalent_to(alternative.left) + && (!self.condition.has_side_effects(compressor) + || consequent.operator == "=" + && !consequent.left.has_side_effects(compressor))) { + return make_node(AST_Assign, self, { + operator: consequent.operator, + left: consequent.left, + right: make_node(AST_Conditional, self, { + condition: self.condition, + consequent: consequent.right, + alternative: alternative.right + }) + }); + } + // x ? y(a) : y(b) --> y(x ? a : b) + if (consequent instanceof AST_Call + && alternative.TYPE === consequent.TYPE + && consequent.args.length == 1 + && alternative.args.length == 1 + && consequent.expression.equivalent_to(alternative.expression) + && !consequent.expression.has_side_effects(compressor)) { + consequent.args[0] = make_node(AST_Conditional, self, { + condition: self.condition, + consequent: consequent.args[0], + alternative: alternative.args[0] + }); + return consequent; + } + // x?y?z:a:a --> x&&y?z:a + if (consequent instanceof AST_Conditional + && consequent.alternative.equivalent_to(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { left: self.condition, - right: self.body.condition - }).transform(compressor); - self.body = self.body.body; + operator: "&&", + right: consequent.condition + }), + consequent: consequent.consequent, + alternative: alternative + }); + } + // x ? y : y --> x, y + if (consequent.equivalent_to(alternative)) { + return make_node(AST_Seq, self, { + car: self.condition, + cdr: consequent + }).optimize(compressor); + } + + if (is_true(self.consequent)) { + if (is_false(self.alternative)) { + // c ? true : false ---> !!c + return booleanize(self.condition); } - if (aborts(self.body)) { - if (self.alternative) { - var alt = self.alternative; - self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, alt ] - }).transform(compressor); - } + // c ? true : x ---> !!c || x + return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(self.condition), + right: self.alternative + }); + } + if (is_false(self.consequent)) { + if (is_true(self.alternative)) { + // c ? false : true ---> !c + return booleanize(self.condition.negate(compressor)); } - if (aborts(self.alternative)) { - var body = self.body; - self.body = self.alternative; - self.condition = negated_is_best ? negated : self.condition.negate(compressor); - self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, body ] - }).transform(compressor); + // c ? false : x ---> !c && x + return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(self.condition.negate(compressor)), + right: self.alternative + }); + } + if (is_true(self.alternative)) { + // c ? x : true ---> !c || x + return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(self.condition.negate(compressor)), + right: self.consequent + }); + } + if (is_false(self.alternative)) { + // c ? x : false ---> !!c && x + return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(self.condition), + right: self.consequent + }); + } + + return self; + + function booleanize(node) { + if (node.is_boolean()) return node; + // !!expression + return make_node(AST_UnaryPrefix, node, { + operator: "!", + expression: node.negate(compressor) + }); + } + + // AST_True or !0 + function is_true(node) { + return node instanceof AST_True + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && !node.expression.value); + } + // AST_False or !1 + function is_false(node) { + return node instanceof AST_False + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && !!node.expression.value); + } + }); + + OPT(AST_Boolean, function(self, compressor){ + if (compressor.option("booleans")) { + var p = compressor.parent(); + if (p instanceof AST_Binary && (p.operator == "==" + || p.operator == "!=")) { + compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { + operator : p.operator, + value : self.value, + file : p.start.file, + line : p.start.line, + col : p.start.col, + }); + return make_node(AST_Number, self, { + value: +self.value + }); } - return self; - }); - OPT(AST_Switch, function(self, compressor) { - if (self.body.length == 0 && compressor.option("conditionals")) { - return make_node(AST_SimpleStatement, self, { - body: self.expression - }).transform(compressor); + return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_Number, self, { + value: 1 - self.value + }) + }); + } + return self; + }); + + OPT(AST_Sub, function(self, compressor){ + var prop = self.property; + if (prop instanceof AST_String && compressor.option("properties")) { + prop = prop.getValue(); + if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { + return make_node(AST_Dot, self, { + expression : self.expression, + property : prop + }).optimize(compressor); } - for (;;) { - var last_branch = self.body[self.body.length - 1]; - if (last_branch) { - var stat = last_branch.body[last_branch.body.length - 1]; - if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self) last_branch.body.pop(); - if (last_branch instanceof AST_Default && last_branch.body.length == 0) { - self.body.pop(); - continue; - } - } - break; + var v = parseFloat(prop); + if (!isNaN(v) && v.toString() == prop) { + self.property = make_node(AST_Number, self.property, { + value: v + }); } - var exp = self.expression.evaluate(compressor); - out: if (exp.length == 2) try { - self.expression = exp[0]; - if (!compressor.option("dead_code")) break out; - var value = exp[1]; - var in_if = false; - var in_block = false; - var started = false; - var stopped = false; - var ruined = false; - var tt = new TreeTransformer(function(node, descend, in_list) { - if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) { - return node; - } else if (node instanceof AST_Switch && node === self) { - node = node.clone(); - descend(node, this); - return ruined ? node : make_node(AST_BlockStatement, node, { - body: node.body.reduce(function(a, branch) { - return a.concat(branch.body); - }, []) - }).transform(compressor); - } else if (node instanceof AST_If || node instanceof AST_Try) { - var save = in_if; - in_if = !in_block; - descend(node, this); - in_if = save; - return node; - } else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) { - var save = in_block; - in_block = true; - descend(node, this); - in_block = save; - return node; - } else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) { - if (in_if) { - ruined = true; - return node; - } - if (in_block) return node; - stopped = true; - return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); - } else if (node instanceof AST_SwitchBranch && this.parent() === self) { - if (stopped) return MAP.skip; - if (node instanceof AST_Case) { - var exp = node.expression.evaluate(compressor); - if (exp.length < 2) { - throw self; - } - if (exp[1] === value || started) { - started = true; - if (aborts(node)) stopped = true; - descend(node, this); - return node; - } - return MAP.skip; - } - descend(node, this); - return node; - } + } + var ev = self.evaluate(compressor); + if (ev !== self) { + ev = make_node_from_constant(ev, self).optimize(compressor); + return best_of(compressor, ev, self); + } + return self; + }); + + OPT(AST_Dot, function(self, compressor){ + var def = self.resolve_defines(compressor); + if (def) { + return def.optimize(compressor); + } + var prop = self.property; + if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) { + return make_node(AST_Sub, self, { + expression : self.expression, + property : make_node(AST_String, self, { + value: prop + }) + }).optimize(compressor); + } + if (compressor.option("unsafe_proto") + && self.expression instanceof AST_Dot + && self.expression.property == "prototype") { + var exp = self.expression.expression; + if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) { + case "Array": + self.expression = make_node(AST_Array, self.expression, { + elements: [] + }); + break; + case "Object": + self.expression = make_node(AST_Object, self.expression, { + properties: [] + }); + break; + case "String": + self.expression = make_node(AST_String, self.expression, { + value: "" }); - tt.stack = compressor.stack.slice(); - self = self.transform(tt); - } catch (ex) { - if (ex !== self) throw ex; + break; + } + } + var ev = self.evaluate(compressor); + if (ev !== self) { + ev = make_node_from_constant(ev, self).optimize(compressor); + return best_of(compressor, ev, self); + } + return self; + }); + + function literals_in_boolean_context(self, compressor) { + if (compressor.option("booleans") && compressor.in_boolean_context()) { + return best_of(compressor, self, make_node(AST_Seq, self, { + car: self, + cdr: make_node(AST_True, self) + }).optimize(compressor)); + } + return self; + }; + OPT(AST_Array, literals_in_boolean_context); + OPT(AST_Object, literals_in_boolean_context); + OPT(AST_RegExp, literals_in_boolean_context); + + OPT(AST_Return, function(self, compressor){ + if (self.value && is_undefined(self.value, compressor)) { + self.value = null; + } + return self; + }); + + OPT(AST_VarDef, function(self, compressor){ + var defines = compressor.option("global_defs"); + if (defines && HOP(defines, self.name.name)) { + compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start); + } + return self; + }); + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +// a small wrapper around fitzgen's source-map library +function SourceMap(options) { + options = defaults(options, { + file : null, + root : null, + orig : null, + + orig_line_diff : 0, + dest_line_diff : 0, + }); + var generator = new MOZ_SourceMap.SourceMapGenerator({ + file : options.file, + sourceRoot : options.root + }); + var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); + + if (orig_map && Array.isArray(options.orig.sources)) { + orig_map._sources.toArray().forEach(function(source) { + var sourceContent = orig_map.sourceContentFor(source, true); + if (sourceContent) { + generator.setSourceContent(source, sourceContent); } - return self; - }); - OPT(AST_Case, function(self, compressor) { - self.body = tighten_body(self.body, compressor); - return self; - }); - OPT(AST_Try, function(self, compressor) { - self.body = tighten_body(self.body, compressor); - return self; }); - AST_Definitions.DEFMETHOD("remove_initializers", function() { - this.definitions.forEach(function(def) { - def.value = null; + } + + function add(source, gen_line, gen_col, orig_line, orig_col, name) { + if (orig_map) { + var info = orig_map.originalPositionFor({ + line: orig_line, + column: orig_col }); + if (info.source === null) { + return; + } + source = info.source; + orig_line = info.line; + orig_col = info.column; + name = info.name || name; + } + generator.addMapping({ + generated : { line: gen_line + options.dest_line_diff, column: gen_col }, + original : { line: orig_line + options.orig_line_diff, column: orig_col }, + source : source, + name : name }); - AST_Definitions.DEFMETHOD("to_assignments", function() { - var assignments = this.definitions.reduce(function(a, def) { - if (def.value) { - var name = make_node(AST_SymbolRef, def.name, def.name); - a.push(make_node(AST_Assign, def, { - operator: "=", - left: name, - right: def.value - })); - } - return a; - }, []); - if (assignments.length == 0) return null; - return AST_Seq.from_array(assignments); - }); - OPT(AST_Definitions, function(self, compressor) { - if (self.definitions.length == 0) return make_node(AST_EmptyStatement, self); - return self; - }); - OPT(AST_Function, function(self, compressor) { - self = AST_Lambda.prototype.optimize.call(self, compressor); - if (compressor.option("unused")) { - if (self.name && self.name.unreferenced()) { - self.name = null; + }; + return { + add : add, + get : function() { return generator }, + toString : function() { return JSON.stringify(generator.toJSON()); } + }; +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +(function(){ + + var normalize_directives = function(body) { + var in_directive = true; + + for (var i = 0; i < body.length; i++) { + if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) { + body[i] = new AST_Directive({ + start: body[i].start, + end: body[i].end, + value: body[i].body.value + }); + } else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) { + in_directive = false; + } + } + + return body; + }; + + var MOZ_TO_ME = { + Program: function(M) { + return new AST_Toplevel({ + start: my_start_token(M), + end: my_end_token(M), + body: normalize_directives(M.body.map(from_moz)) + }); + }, + FunctionDeclaration: function(M) { + return new AST_Defun({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + argnames: M.params.map(from_moz), + body: normalize_directives(from_moz(M.body).body) + }); + }, + FunctionExpression: function(M) { + return new AST_Function({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + argnames: M.params.map(from_moz), + body: normalize_directives(from_moz(M.body).body) + }); + }, + ExpressionStatement: function(M) { + return new AST_SimpleStatement({ + start: my_start_token(M), + end: my_end_token(M), + body: from_moz(M.expression) + }); + }, + TryStatement: function(M) { + var handlers = M.handlers || [M.handler]; + if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) { + throw new Error("Multiple catch clauses are not supported."); + } + return new AST_Try({ + start : my_start_token(M), + end : my_end_token(M), + body : from_moz(M.block).body, + bcatch : from_moz(handlers[0]), + bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null + }); + }, + Property: function(M) { + var key = M.key; + var args = { + start : my_start_token(key), + end : my_end_token(M.value), + key : key.type == "Identifier" ? key.name : key.value, + value : from_moz(M.value) + }; + if (M.kind == "init") return new AST_ObjectKeyVal(args); + args.key = new AST_SymbolAccessor({ + name: args.key + }); + args.value = new AST_Accessor(args.value); + if (M.kind == "get") return new AST_ObjectGetter(args); + if (M.kind == "set") return new AST_ObjectSetter(args); + }, + ArrayExpression: function(M) { + return new AST_Array({ + start : my_start_token(M), + end : my_end_token(M), + elements : M.elements.map(function(elem){ + return elem === null ? new AST_Hole() : from_moz(elem); + }) + }); + }, + ObjectExpression: function(M) { + return new AST_Object({ + start : my_start_token(M), + end : my_end_token(M), + properties : M.properties.map(function(prop){ + prop.type = "Property"; + return from_moz(prop) + }) + }); + }, + SequenceExpression: function(M) { + return AST_Seq.from_array(M.expressions.map(from_moz)); + }, + MemberExpression: function(M) { + return new (M.computed ? AST_Sub : AST_Dot)({ + start : my_start_token(M), + end : my_end_token(M), + property : M.computed ? from_moz(M.property) : M.property.name, + expression : from_moz(M.object) + }); + }, + SwitchCase: function(M) { + return new (M.test ? AST_Case : AST_Default)({ + start : my_start_token(M), + end : my_end_token(M), + expression : from_moz(M.test), + body : M.consequent.map(from_moz) + }); + }, + VariableDeclaration: function(M) { + return new (M.kind === "const" ? AST_Const : AST_Var)({ + start : my_start_token(M), + end : my_end_token(M), + definitions : M.declarations.map(from_moz) + }); + }, + Literal: function(M) { + var val = M.value, args = { + start : my_start_token(M), + end : my_end_token(M) + }; + if (val === null) return new AST_Null(args); + switch (typeof val) { + case "string": + args.value = val; + return new AST_String(args); + case "number": + args.value = val; + return new AST_Number(args); + case "boolean": + return new (val ? AST_True : AST_False)(args); + default: + var rx = M.regex; + if (rx && rx.pattern) { + // RegExpLiteral as per ESTree AST spec + args.value = new RegExp(rx.pattern, rx.flags).toString(); + } else { + // support legacy RegExp + args.value = M.regex && M.raw ? M.raw : val; } + return new AST_RegExp(args); } - return self; + }, + Identifier: function(M) { + var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; + return new ( p.type == "LabeledStatement" ? AST_Label + : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) + : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) + : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) + : p.type == "CatchClause" ? AST_SymbolCatch + : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef + : AST_SymbolRef)({ + start : my_start_token(M), + end : my_end_token(M), + name : M.name + }); + } + }; + + MOZ_TO_ME.UpdateExpression = + MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) { + var prefix = "prefix" in M ? M.prefix + : M.type == "UnaryExpression" ? true : false; + return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ + start : my_start_token(M), + end : my_end_token(M), + operator : M.operator, + expression : from_moz(M.argument) }); - OPT(AST_Call, function(self, compressor) { - if (compressor.option("unsafe")) { - var exp = self.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { - switch (exp.name) { - case "Array": - if (self.args.length != 1) { - return make_node(AST_Array, self, { - elements: self.args - }); - } - break; + }; - case "Object": - if (self.args.length == 0) { - return make_node(AST_Object, self, { - properties: [] - }); - } - break; + map("EmptyStatement", AST_EmptyStatement); + map("BlockStatement", AST_BlockStatement, "body@body"); + map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); + map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); + map("BreakStatement", AST_Break, "label>label"); + map("ContinueStatement", AST_Continue, "label>label"); + map("WithStatement", AST_With, "object>expression, body>body"); + map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); + map("ReturnStatement", AST_Return, "argument>value"); + map("ThrowStatement", AST_Throw, "argument>value"); + map("WhileStatement", AST_While, "test>condition, body>body"); + map("DoWhileStatement", AST_Do, "test>condition, body>body"); + map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); + map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); + map("DebuggerStatement", AST_Debugger); + map("VariableDeclarator", AST_VarDef, "id>name, init>value"); + map("CatchClause", AST_Catch, "param>argname, body%body"); + + map("ThisExpression", AST_This); + map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); + map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); + map("NewExpression", AST_New, "callee>expression, arguments@args"); + map("CallExpression", AST_Call, "callee>expression, arguments@args"); + + def_to_moz(AST_Toplevel, function To_Moz_Program(M) { + return to_moz_scope("Program", M); + }); - case "String": - if (self.args.length == 0) return make_node(AST_String, self, { - value: "" - }); - return make_node(AST_Binary, self, { - left: self.args[0], - operator: "+", - right: make_node(AST_String, self, { - value: "" - }) - }); + def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { + return { + type: "FunctionDeclaration", + id: to_moz(M.name), + params: M.argnames.map(to_moz), + body: to_moz_scope("BlockStatement", M) + } + }); - case "Function": - if (all(self.args, function(x) { - return x instanceof AST_String; - })) { - try { - var code = "(function(" + self.args.slice(0, -1).map(function(arg) { - return arg.value; - }).join(",") + "){" + self.args[self.args.length - 1].value + "})()"; - var ast = parse(code); - ast.figure_out_scope(); - var comp = new Compressor(compressor.options); - ast = ast.transform(comp); - ast.figure_out_scope(); - ast.mangle_names(); - var fun = ast.body[0].body.expression; - var args = fun.argnames.map(function(arg, i) { - return make_node(AST_String, self.args[i], { - value: arg.print_to_string() - }); - }); - var code = OutputStream(); - AST_BlockStatement.prototype._codegen.call(fun, fun, code); - code = code.toString().replace(/^\{|\}$/g, ""); - args.push(make_node(AST_String, self.args[self.args.length - 1], { - value: code - })); - self.args = args; - return self; - } catch (ex) { - if (ex instanceof JS_Parse_Error) { - compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start); - compressor.warn(ex.toString()); - } else { - console.log(ex); - } - } - } - break; - } - } else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) { - return make_node(AST_Binary, self, { - left: make_node(AST_String, self, { - value: "" - }), - operator: "+", - right: exp.expression - }).transform(compressor); - } - } - if (compressor.option("side_effects")) { - if (self.expression instanceof AST_Function && self.args.length == 0 && !AST_Block.prototype.has_side_effects.call(self.expression)) { - return make_node(AST_Undefined, self).transform(compressor); - } - } - return self; - }); - OPT(AST_New, function(self, compressor) { - if (compressor.option("unsafe")) { - var exp = self.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { - switch (exp.name) { - case "Object": - case "RegExp": - case "Function": - case "Error": - case "Array": - return make_node(AST_Call, self, self).transform(compressor); - } - } + def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) { + return { + type: "FunctionExpression", + id: to_moz(M.name), + params: M.argnames.map(to_moz), + body: to_moz_scope("BlockStatement", M) + } + }); + + def_to_moz(AST_Directive, function To_Moz_Directive(M) { + return { + type: "ExpressionStatement", + expression: { + type: "Literal", + value: M.value } - return self; - }); - OPT(AST_Seq, function(self, compressor) { - if (!compressor.option("side_effects")) return self; - if (!self.car.has_side_effects()) { - var p; - if (!(self.cdr instanceof AST_SymbolRef && self.cdr.name == "eval" && self.cdr.undeclared() && (p = compressor.parent()) instanceof AST_Call && p.expression === self)) { - return self.cdr; - } + }; + }); + + def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { + return { + type: "ExpressionStatement", + expression: to_moz(M.body) + }; + }); + + def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) { + return { + type: "SwitchCase", + test: to_moz(M.expression), + consequent: M.body.map(to_moz) + }; + }); + + def_to_moz(AST_Try, function To_Moz_TryStatement(M) { + return { + type: "TryStatement", + block: to_moz_block(M), + handler: to_moz(M.bcatch), + guardedHandlers: [], + finalizer: to_moz(M.bfinally) + }; + }); + + def_to_moz(AST_Catch, function To_Moz_CatchClause(M) { + return { + type: "CatchClause", + param: to_moz(M.argname), + guard: null, + body: to_moz_block(M) + }; + }); + + def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { + return { + type: "VariableDeclaration", + kind: M instanceof AST_Const ? "const" : "var", + declarations: M.definitions.map(to_moz) + }; + }); + + def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) { + return { + type: "SequenceExpression", + expressions: M.to_array().map(to_moz) + }; + }); + + def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { + var isComputed = M instanceof AST_Sub; + return { + type: "MemberExpression", + object: to_moz(M.expression), + computed: isComputed, + property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property} + }; + }); + + def_to_moz(AST_Unary, function To_Moz_Unary(M) { + return { + type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression", + operator: M.operator, + prefix: M instanceof AST_UnaryPrefix, + argument: to_moz(M.expression) + }; + }); + + def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { + return { + type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", + left: to_moz(M.left), + operator: M.operator, + right: to_moz(M.right) + }; + }); + + def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) { + return { + type: "ArrayExpression", + elements: M.elements.map(to_moz) + }; + }); + + def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { + return { + type: "ObjectExpression", + properties: M.properties.map(to_moz) + }; + }); + + def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { + var key = { + type: "Literal", + value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key + }; + var kind; + if (M instanceof AST_ObjectKeyVal) { + kind = "init"; + } else + if (M instanceof AST_ObjectGetter) { + kind = "get"; + } else + if (M instanceof AST_ObjectSetter) { + kind = "set"; + } + return { + type: "Property", + kind: kind, + key: key, + value: to_moz(M.value) + }; + }); + + def_to_moz(AST_Symbol, function To_Moz_Identifier(M) { + var def = M.definition(); + return { + type: "Identifier", + name: def ? def.mangled_name || def.name : M.name + }; + }); + + def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { + var value = M.value; + return { + type: "Literal", + value: value, + raw: value.toString(), + regex: { + pattern: value.source, + flags: value.toString().match(/[gimuy]*$/)[0] } - if (compressor.option("cascade")) { - if (self.car instanceof AST_Assign && !self.car.left.has_side_effects() && self.car.left.equivalent_to(self.cdr)) { - return self.car; - } - if (!self.car.has_side_effects() && !self.cdr.has_side_effects() && self.car.equivalent_to(self.cdr)) { - return self.car; + }; + }); + + def_to_moz(AST_Constant, function To_Moz_Literal(M) { + var value = M.value; + if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { + return { + type: "UnaryExpression", + operator: "-", + prefix: true, + argument: { + type: "Literal", + value: -value, + raw: M.start.raw } - } - return self; - }); - AST_Unary.DEFMETHOD("lift_sequences", function(compressor) { - if (compressor.option("sequences")) { - if (this.expression instanceof AST_Seq) { - var seq = this.expression; - var x = seq.to_array(); - this.expression = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; - } - } - return this; + }; + } + return { + type: "Literal", + value: value, + raw: M.start.raw + }; + }); + + def_to_moz(AST_Atom, function To_Moz_Atom(M) { + return { + type: "Identifier", + name: String(M.value) + }; + }); + + AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); + AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); + AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null }); + + AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); + AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); + + /* -----[ tools ]----- */ + + function raw_token(moznode) { + if (moznode.type == "Literal") { + return moznode.raw != null ? moznode.raw : moznode.value + ""; + } + } + + function my_start_token(moznode) { + var loc = moznode.loc, start = loc && loc.start; + var range = moznode.range; + return new AST_Token({ + file : loc && loc.source, + line : start && start.line, + col : start && start.column, + pos : range ? range[0] : moznode.start, + endline : start && start.line, + endcol : start && start.column, + endpos : range ? range[0] : moznode.start, + raw : raw_token(moznode), }); - OPT(AST_UnaryPostfix, function(self, compressor) { - return self.lift_sequences(compressor); + }; + + function my_end_token(moznode) { + var loc = moznode.loc, end = loc && loc.end; + var range = moznode.range; + return new AST_Token({ + file : loc && loc.source, + line : end && end.line, + col : end && end.column, + pos : range ? range[1] : moznode.end, + endline : end && end.line, + endcol : end && end.column, + endpos : range ? range[1] : moznode.end, + raw : raw_token(moznode), }); - OPT(AST_UnaryPrefix, function(self, compressor) { - self = self.lift_sequences(compressor); - var e = self.expression; - if (compressor.option("booleans") && compressor.in_boolean_context()) { - switch (self.operator) { - case "!": - if (e instanceof AST_UnaryPrefix && e.operator == "!") { - return e.expression; - } - break; + }; - case "typeof": - compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); - } - if (e instanceof AST_Binary && self.operator == "!") { - self = best_of(self, e.negate(compressor)); - } + function map(moztype, mytype, propmap) { + var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; + moz_to_me += "return new U2." + mytype.name + "({\n" + + "start: my_start_token(M),\n" + + "end: my_end_token(M)"; + + var me_to_moz = "function To_Moz_" + moztype + "(M){\n"; + me_to_moz += "return {\n" + + "type: " + JSON.stringify(moztype); + + if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ + var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); + if (!m) throw new Error("Can't understand property map: " + prop); + var moz = m[1], how = m[2], my = m[3]; + moz_to_me += ",\n" + my + ": "; + me_to_moz += ",\n" + moz + ": "; + switch (how) { + case "@": + moz_to_me += "M." + moz + ".map(from_moz)"; + me_to_moz += "M." + my + ".map(to_moz)"; + break; + case ">": + moz_to_me += "from_moz(M." + moz + ")"; + me_to_moz += "to_moz(M." + my + ")"; + break; + case "=": + moz_to_me += "M." + moz; + me_to_moz += "M." + my; + break; + case "%": + moz_to_me += "from_moz(M." + moz + ").body"; + me_to_moz += "to_moz_block(M)"; + break; + default: + throw new Error("Can't understand operator in propmap: " + prop); } - return self.evaluate(compressor)[0]; - }); - AST_Binary.DEFMETHOD("lift_sequences", function(compressor) { - if (compressor.option("sequences")) { - if (this.left instanceof AST_Seq) { - var seq = this.left; - var x = seq.to_array(); - this.left = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; - } - if (this.right instanceof AST_Seq && !(this.operator == "||" || this.operator == "&&") && !this.left.has_side_effects()) { - var seq = this.right; - var x = seq.to_array(); - this.right = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; - } - } - return this; }); - var commutativeOperators = makePredicate("== === != !== * & | ^"); - OPT(AST_Binary, function(self, compressor) { - var reverse = compressor.has_directive("use asm") ? noop : function(op, force) { - if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) { - if (op) self.operator = op; - var tmp = self.left; - self.left = self.right; - self.right = tmp; - } + + moz_to_me += "\n})\n}"; + me_to_moz += "\n}\n}"; + + //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); + //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); + //console.log(moz_to_me); + + moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( + exports, my_start_token, my_end_token, from_moz + ); + me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")( + to_moz, to_moz_block, to_moz_scope + ); + MOZ_TO_ME[moztype] = moz_to_me; + def_to_moz(mytype, me_to_moz); + }; + + var FROM_MOZ_STACK = null; + + function from_moz(node) { + FROM_MOZ_STACK.push(node); + var ret = node != null ? MOZ_TO_ME[node.type](node) : null; + FROM_MOZ_STACK.pop(); + return ret; + }; + + AST_Node.from_mozilla_ast = function(node){ + var save_stack = FROM_MOZ_STACK; + FROM_MOZ_STACK = []; + var ast = from_moz(node); + FROM_MOZ_STACK = save_stack; + return ast; + }; + + function set_moz_loc(mynode, moznode, myparent) { + var start = mynode.start; + var end = mynode.end; + if (start.pos != null && end.endpos != null) { + moznode.range = [start.pos, end.endpos]; + } + if (start.line) { + moznode.loc = { + start: {line: start.line, column: start.col}, + end: end.endline ? {line: end.endline, column: end.endcol} : null }; - if (commutativeOperators(self.operator)) { - if (self.right instanceof AST_Constant && !(self.left instanceof AST_Constant)) { - reverse(null, true); - } - } - self = self.lift_sequences(compressor); - if (compressor.option("comparisons")) switch (self.operator) { - case "===": - case "!==": - if (self.left.is_string(compressor) && self.right.is_string(compressor) || self.left.is_boolean() && self.right.is_boolean()) { - self.operator = self.operator.substr(0, 2); - } - - case "==": - case "!=": - if (self.left instanceof AST_String && self.left.value == "undefined" && self.right instanceof AST_UnaryPrefix && self.right.operator == "typeof" && compressor.option("unsafe")) { - if (!(self.right.expression instanceof AST_SymbolRef) || !self.right.expression.undeclared()) { - self.right = self.right.expression; - self.left = make_node(AST_Undefined, self.left).optimize(compressor); - if (self.operator.length == 2) self.operator += "="; - } - } - break; + if (start.file) { + moznode.loc.source = start.file; } - if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { - case "&&": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if (ll.length > 1 && !ll[1] || rr.length > 1 && !rr[1]) { - compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); - return make_node(AST_False, self); - } - if (ll.length > 1 && ll[1]) { - return rr[0]; - } - if (rr.length > 1 && rr[1]) { - return ll[0]; - } - break; + } + return moznode; + }; - case "||": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if (ll.length > 1 && ll[1] || rr.length > 1 && rr[1]) { - compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); - } - if (ll.length > 1 && !ll[1]) { - return rr[0]; - } - if (rr.length > 1 && !rr[1]) { - return ll[0]; - } - break; + function def_to_moz(mytype, handler) { + mytype.DEFMETHOD("to_mozilla_ast", function() { + return set_moz_loc(this, handler(this)); + }); + }; + + function to_moz(node) { + return node != null ? node.to_mozilla_ast() : null; + }; + + function to_moz_block(node) { + return { + type: "BlockStatement", + body: node.body.map(to_moz) + }; + }; + + function to_moz_scope(type, node) { + var body = node.body.map(to_moz); + if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) { + body.unshift(to_moz(new AST_EmptyStatement(node.body[0]))); + } + return { + type: type, + body: body + }; + }; +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function find_builtins() { + // NaN will be included due to Number.NaN + var a = [ + "null", + "true", + "false", + "Infinity", + "-Infinity", + "undefined", + ]; + [ Object, Array, Function, Number, + String, Boolean, Error, Math, + Date, RegExp + ].forEach(function(ctor){ + Object.getOwnPropertyNames(ctor).map(add); + if (ctor.prototype) { + Object.getOwnPropertyNames(ctor.prototype).map(add); + } + }); + function add(name) { + push_uniq(a, name); + } + return a; +} + +function mangle_properties(ast, options) { + options = defaults(options, { + cache: null, + debug: false, + ignore_quoted: false, + only_cache: false, + regex: null, + reserved: null, + }); + + var reserved = options.reserved; + if (reserved == null) + reserved = find_builtins(); + + var cache = options.cache; + if (cache == null) { + cache = { + cname: -1, + props: new Dictionary() + }; + } - case "+": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if (ll.length > 1 && ll[0] instanceof AST_String && ll[1] || rr.length > 1 && rr[0] instanceof AST_String && rr[1]) { - compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); - } - break; - } - var exp = self.evaluate(compressor); - if (exp.length > 1) { - if (best_of(exp[0], self) !== self) return exp[0]; - } - if (compressor.option("comparisons")) { - if (!(compressor.parent() instanceof AST_Binary) || compressor.parent() instanceof AST_Assign) { - var negated = make_node(AST_UnaryPrefix, self, { - operator: "!", - expression: self.negate(compressor) - }); - self = best_of(self, negated); - } - switch (self.operator) { - case "<": - reverse(">"); - break; + var regex = options.regex; + var ignore_quoted = options.ignore_quoted; - case "<=": - reverse(">="); - break; - } - } - if (self.operator == "+" && self.right instanceof AST_String && self.right.getValue() === "" && self.left instanceof AST_Binary && self.left.operator == "+" && self.left.is_string(compressor)) { - return self.left; - } - return self; - }); - OPT(AST_SymbolRef, function(self, compressor) { - if (self.undeclared()) { - var defines = compressor.option("global_defs"); - if (defines && defines.hasOwnProperty(self.name)) { - return make_node_from_constant(compressor, defines[self.name], self); - } - switch (self.name) { - case "undefined": - return make_node(AST_Undefined, self); + // note debug is either false (disabled), or a string of the debug suffix to use (enabled). + // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' + // the same as passing an empty string. + var debug = (options.debug !== false); + var debug_name_suffix; + if (debug) { + debug_name_suffix = (options.debug === true ? "" : options.debug); + } - case "NaN": - return make_node(AST_NaN, self); + var names_to_mangle = []; + var unmangleable = []; + var ignored = {}; - case "Infinity": - return make_node(AST_Infinity, self); - } - } - return self; - }); - OPT(AST_Undefined, function(self, compressor) { - if (compressor.option("unsafe")) { - var scope = compressor.find_parent(AST_Scope); - var undef = scope.find_variable("undefined"); - if (undef) { - var ref = make_node(AST_SymbolRef, self, { - name: "undefined", - scope: scope, - thedef: undef - }); - ref.reference(); - return ref; - } - } - return self; - }); - var ASSIGN_OPS = [ "+", "-", "/", "*", "%", ">>", "<<", ">>>", "|", "^", "&" ]; - OPT(AST_Assign, function(self, compressor) { - self = self.lift_sequences(compressor); - if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary && self.right.left instanceof AST_SymbolRef && self.right.left.name == self.left.name && member(self.right.operator, ASSIGN_OPS)) { - self.operator = self.right.operator + "="; - self.right = self.right.right; - } - return self; - }); - OPT(AST_Conditional, function(self, compressor) { - if (!compressor.option("conditionals")) return self; - if (self.condition instanceof AST_Seq) { - var car = self.condition.car; - self.condition = self.condition.cdr; - return AST_Seq.cons(car, self); - } - var cond = self.condition.evaluate(compressor); - if (cond.length > 1) { - if (cond[1]) { - compressor.warn("Condition always true [{file}:{line},{col}]", self.start); - return self.consequent; - } else { - compressor.warn("Condition always false [{file}:{line},{col}]", self.start); - return self.alternative; - } - } - var negated = cond[0].negate(compressor); - if (best_of(cond[0], negated) === negated) { - self = make_node(AST_Conditional, self, { - condition: negated, - consequent: self.alternative, - alternative: self.consequent - }); - } - var consequent = self.consequent; - var alternative = self.alternative; - if (consequent instanceof AST_Assign && alternative instanceof AST_Assign && consequent.operator == alternative.operator && consequent.left.equivalent_to(alternative.left)) { - self = make_node(AST_Assign, self, { - operator: consequent.operator, - left: consequent.left, - right: make_node(AST_Conditional, self, { - condition: self.condition, - consequent: consequent.right, - alternative: alternative.right - }) - }); - } - return self; - }); - OPT(AST_Boolean, function(self, compressor) { - if (compressor.option("booleans")) { - var p = compressor.parent(); - if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { - compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { - operator: p.operator, - value: self.value, - file: p.start.file, - line: p.start.line, - col: p.start.col - }); - return make_node(AST_Number, self, { - value: +self.value - }); - } - return make_node(AST_UnaryPrefix, self, { - operator: "!", - expression: make_node(AST_Number, self, { - value: 1 - self.value - }) - }); - } - return self; - }); - OPT(AST_Sub, function(self, compressor) { - var prop = self.property; - if (prop instanceof AST_String && compressor.option("properties")) { - prop = prop.getValue(); - if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { - return make_node(AST_Dot, self, { - expression: self.expression, - property: prop - }); - } - } - return self; - }); - function literals_in_boolean_context(self, compressor) { - if (compressor.option("booleans") && compressor.in_boolean_context()) { - return make_node(AST_True, self); - } - return self; + // step 1: find candidates to mangle + ast.walk(new TreeWalker(function(node){ + if (node instanceof AST_ObjectKeyVal) { + add(node.key, ignore_quoted && node.quote); } - OPT(AST_Array, literals_in_boolean_context); - OPT(AST_Object, literals_in_boolean_context); - OPT(AST_RegExp, literals_in_boolean_context); - })(); - "use strict"; - function SourceMap(options) { - options = defaults(options, { - file: null, - root: null, - orig: null - }); - var generator = new MOZ_SourceMap.SourceMapGenerator({ - file: options.file, - sourceRoot: options.root - }); - var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); - function add(source, gen_line, gen_col, orig_line, orig_col, name) { - if (orig_map) { - var info = orig_map.originalPositionFor({ - line: orig_line, - column: orig_col - }); - source = info.source; - orig_line = info.line; - orig_col = info.column; - name = info.name; - } - generator.addMapping({ - generated: { - line: gen_line, - column: gen_col - }, - original: { - line: orig_line, - column: orig_col - }, - source: source, - name: name - }); + else if (node instanceof AST_ObjectProperty) { + // setter or getter, since KeyVal is handled above + add(node.key.name); } - return { - add: add, - get: function() { - return generator; - }, - toString: function() { - return generator.toString(); - } - }; + else if (node instanceof AST_Dot) { + add(node.property); + } + else if (node instanceof AST_Sub) { + addStrings(node.property, ignore_quoted); + } + })); + + // step 2: transform the tree, renaming properties + return ast.transform(new TreeTransformer(function(node){ + if (node instanceof AST_ObjectKeyVal) { + if (!(ignore_quoted && node.quote)) + node.key = mangle(node.key); + } + else if (node instanceof AST_ObjectProperty) { + // setter or getter + node.key.name = mangle(node.key.name); + } + else if (node instanceof AST_Dot) { + node.property = mangle(node.property); + } + else if (node instanceof AST_Sub) { + if (!ignore_quoted) + node.property = mangleStrings(node.property); + } + // else if (node instanceof AST_String) { + // if (should_mangle(node.value)) { + // AST_Node.warn( + // "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", { + // file : node.start.file, + // line : node.start.line, + // col : node.start.col, + // prop : node.value + // } + // ); + // } + // } + })); + + // only function declarations after this line + + function can_mangle(name) { + if (unmangleable.indexOf(name) >= 0) return false; + if (reserved.indexOf(name) >= 0) return false; + if (options.only_cache) { + return cache.props.has(name); + } + if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; + return true; } - "use strict"; - (function() { - var MOZ_TO_ME = { - TryStatement: function(M) { - return new AST_Try({ - start: my_start_token(M), - end: my_end_token(M), - body: from_moz(M.block).body, - bcatch: from_moz(M.handlers[0]), - bfinally: M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null - }); - }, - CatchClause: function(M) { - return new AST_Catch({ - start: my_start_token(M), - end: my_end_token(M), - argname: from_moz(M.param), - body: from_moz(M.body).body - }); - }, - ObjectExpression: function(M) { - return new AST_Object({ - start: my_start_token(M), - end: my_end_token(M), - properties: M.properties.map(function(prop) { - var key = prop.key; - var name = key.type == "Identifier" ? key.name : key.value; - var args = { - start: my_start_token(key), - end: my_end_token(prop.value), - key: name, - value: from_moz(prop.value) - }; - switch (prop.kind) { - case "init": - return new AST_ObjectKeyVal(args); - case "set": - args.value.name = from_moz(key); - return new AST_ObjectSetter(args); + function should_mangle(name) { + if (ignore_quoted && name in ignored) return false; + if (regex && !regex.test(name)) return false; + if (reserved.indexOf(name) >= 0) return false; + return cache.props.has(name) + || names_to_mangle.indexOf(name) >= 0; + } - case "get": - args.value.name = from_moz(key); - return new AST_ObjectGetter(args); - } - }) - }); - }, - SequenceExpression: function(M) { - return AST_Seq.from_array(M.expressions.map(from_moz)); - }, - MemberExpression: function(M) { - return new (M.computed ? AST_Sub : AST_Dot)({ - start: my_start_token(M), - end: my_end_token(M), - property: M.computed ? from_moz(M.property) : M.property.name, - expression: from_moz(M.object) - }); - }, - SwitchCase: function(M) { - return new (M.test ? AST_Case : AST_Default)({ - start: my_start_token(M), - end: my_end_token(M), - expression: from_moz(M.test), - body: M.consequent.map(from_moz) - }); - }, - Literal: function(M) { - var val = M.value, args = { - start: my_start_token(M), - end: my_end_token(M) - }; - if (val === null) return new AST_Null(args); - switch (typeof val) { - case "string": - args.value = val; - return new AST_String(args); + function add(name, ignore) { + if (ignore) { + ignored[name] = true; + return; + } - case "number": - args.value = val; - return new AST_Number(args); + if (can_mangle(name)) + push_uniq(names_to_mangle, name); - case "boolean": - return new (val ? AST_True : AST_False)(args); + if (!should_mangle(name)) { + push_uniq(unmangleable, name); + } + } - default: - args.value = val; - return new AST_RegExp(args); + function mangle(name) { + if (!should_mangle(name)) { + return name; + } + + var mangled = cache.props.get(name); + if (!mangled) { + if (debug) { + // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. + var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; + + if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) { + mangled = debug_mangled; } - }, - UnaryExpression: From_Moz_Unary, - UpdateExpression: From_Moz_Unary, - Identifier: function(M) { - var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; - return new (M.name == "this" ? AST_This : p.type == "LabeledStatement" ? AST_Label : p.type == "VariableDeclarator" && p.id === M ? p.kind == "const" ? AST_SymbolConst : AST_SymbolVar : p.type == "FunctionExpression" ? p.id === M ? AST_SymbolLambda : AST_SymbolFunarg : p.type == "FunctionDeclaration" ? p.id === M ? AST_SymbolDefun : AST_SymbolFunarg : p.type == "CatchClause" ? AST_SymbolCatch : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef : AST_SymbolRef)({ - start: my_start_token(M), - end: my_end_token(M), - name: M.name - }); } - }; - function From_Moz_Unary(M) { - var prefix = "prefix" in M ? M.prefix : M.type == "UnaryExpression" ? true : false; - return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ - start: my_start_token(M), - end: my_end_token(M), - operator: M.operator, - expression: from_moz(M.argument) - }); - } - var ME_TO_MOZ = {}; - map("Node", AST_Node); - map("Program", AST_Toplevel, "body@body"); - map("Function", AST_Function, "id>name, params@argnames, body%body"); - map("EmptyStatement", AST_EmptyStatement); - map("BlockStatement", AST_BlockStatement, "body@body"); - map("ExpressionStatement", AST_SimpleStatement, "expression>body"); - map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); - map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); - map("BreakStatement", AST_Break, "label>label"); - map("ContinueStatement", AST_Continue, "label>label"); - map("WithStatement", AST_With, "object>expression, body>body"); - map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); - map("ReturnStatement", AST_Return, "argument>value"); - map("ThrowStatement", AST_Throw, "argument>value"); - map("WhileStatement", AST_While, "test>condition, body>body"); - map("DoWhileStatement", AST_Do, "test>condition, body>body"); - map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); - map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); - map("DebuggerStatement", AST_Debugger); - map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body"); - map("VariableDeclaration", AST_Var, "declarations@definitions"); - map("VariableDeclarator", AST_VarDef, "id>name, init>value"); - map("ThisExpression", AST_This); - map("ArrayExpression", AST_Array, "elements@elements"); - map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body"); - map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); - map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); - map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); - map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); - map("NewExpression", AST_New, "callee>expression, arguments@args"); - map("CallExpression", AST_Call, "callee>expression, arguments@args"); - function my_start_token(moznode) { - return new AST_Token({ - file: moznode.loc && moznode.loc.source, - line: moznode.loc && moznode.loc.start.line, - col: moznode.loc && moznode.loc.start.column, - pos: moznode.start, - endpos: moznode.start - }); - } - function my_end_token(moznode) { - return new AST_Token({ - file: moznode.loc && moznode.loc.source, - line: moznode.loc && moznode.loc.end.line, - col: moznode.loc && moznode.loc.end.column, - pos: moznode.end, - endpos: moznode.end - }); + + // either debug mode is off, or it is on and we could not use the mangled name + if (!mangled) { + // note can_mangle() does not check if the name collides with the 'ignored' set + // (filled with quoted properties when ignore_quoted set). Make sure we add this + // check so we don't collide with a quoted name. + do { + mangled = base54(++cache.cname); + } while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored)); + } + + cache.props.set(name, mangled); } - function map(moztype, mytype, propmap) { - var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; - moz_to_me += "return new mytype({\n" + "start: my_start_token(M),\n" + "end: my_end_token(M)"; - if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop) { - var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); - if (!m) throw new Error("Can't understand property map: " + prop); - var moz = "M." + m[1], how = m[2], my = m[3]; - moz_to_me += ",\n" + my + ": "; - if (how == "@") { - moz_to_me += moz + ".map(from_moz)"; - } else if (how == ">") { - moz_to_me += "from_moz(" + moz + ")"; - } else if (how == "=") { - moz_to_me += moz; - } else if (how == "%") { - moz_to_me += "from_moz(" + moz + ").body"; - } else throw new Error("Can't understand operator in propmap: " + prop); - }); - moz_to_me += "\n})}"; - moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(mytype, my_start_token, my_end_token, from_moz); - return MOZ_TO_ME[moztype] = moz_to_me; - } - var FROM_MOZ_STACK = null; - function from_moz(node) { - FROM_MOZ_STACK.push(node); - var ret = node != null ? MOZ_TO_ME[node.type](node) : null; - FROM_MOZ_STACK.pop(); - return ret; + return mangled; + } + + function addStrings(node, ignore) { + var out = {}; + try { + (function walk(node){ + node.walk(new TreeWalker(function(node){ + if (node instanceof AST_Seq) { + walk(node.cdr); + return true; + } + if (node instanceof AST_String) { + add(node.value, ignore); + return true; + } + if (node instanceof AST_Conditional) { + walk(node.consequent); + walk(node.alternative); + return true; + } + throw out; + })); + })(node); + } catch(ex) { + if (ex !== out) throw ex; } - AST_Node.from_mozilla_ast = function(node) { - var save_stack = FROM_MOZ_STACK; - FROM_MOZ_STACK = []; - var ast = from_moz(node); - FROM_MOZ_STACK = save_stack; - return ast; - }; - })(); - exports["array_to_hash"] = array_to_hash; - exports["slice"] = slice; - exports["characters"] = characters; - exports["member"] = member; - exports["find_if"] = find_if; - exports["repeat_string"] = repeat_string; - exports["DefaultsError"] = DefaultsError; - exports["defaults"] = defaults; - exports["merge"] = merge; - exports["noop"] = noop; - exports["MAP"] = MAP; - exports["push_uniq"] = push_uniq; - exports["string_template"] = string_template; - exports["remove"] = remove; - exports["mergeSort"] = mergeSort; - exports["set_difference"] = set_difference; - exports["set_intersection"] = set_intersection; - exports["makePredicate"] = makePredicate; - exports["all"] = all; - exports["Dictionary"] = Dictionary; - exports["DEFNODE"] = DEFNODE; - exports["AST_Token"] = AST_Token; - exports["AST_Node"] = AST_Node; - exports["AST_Statement"] = AST_Statement; - exports["AST_Debugger"] = AST_Debugger; - exports["AST_Directive"] = AST_Directive; - exports["AST_SimpleStatement"] = AST_SimpleStatement; - exports["walk_body"] = walk_body; - exports["AST_Block"] = AST_Block; - exports["AST_BlockStatement"] = AST_BlockStatement; - exports["AST_EmptyStatement"] = AST_EmptyStatement; - exports["AST_StatementWithBody"] = AST_StatementWithBody; - exports["AST_LabeledStatement"] = AST_LabeledStatement; - exports["AST_DWLoop"] = AST_DWLoop; - exports["AST_Do"] = AST_Do; - exports["AST_While"] = AST_While; - exports["AST_For"] = AST_For; - exports["AST_ForIn"] = AST_ForIn; - exports["AST_With"] = AST_With; - exports["AST_Scope"] = AST_Scope; - exports["AST_Toplevel"] = AST_Toplevel; - exports["AST_Lambda"] = AST_Lambda; - exports["AST_Accessor"] = AST_Accessor; - exports["AST_Function"] = AST_Function; - exports["AST_Defun"] = AST_Defun; - exports["AST_Jump"] = AST_Jump; - exports["AST_Exit"] = AST_Exit; - exports["AST_Return"] = AST_Return; - exports["AST_Throw"] = AST_Throw; - exports["AST_LoopControl"] = AST_LoopControl; - exports["AST_Break"] = AST_Break; - exports["AST_Continue"] = AST_Continue; - exports["AST_If"] = AST_If; - exports["AST_Switch"] = AST_Switch; - exports["AST_SwitchBranch"] = AST_SwitchBranch; - exports["AST_Default"] = AST_Default; - exports["AST_Case"] = AST_Case; - exports["AST_Try"] = AST_Try; - exports["AST_Catch"] = AST_Catch; - exports["AST_Finally"] = AST_Finally; - exports["AST_Definitions"] = AST_Definitions; - exports["AST_Var"] = AST_Var; - exports["AST_Const"] = AST_Const; - exports["AST_VarDef"] = AST_VarDef; - exports["AST_Call"] = AST_Call; - exports["AST_New"] = AST_New; - exports["AST_Seq"] = AST_Seq; - exports["AST_PropAccess"] = AST_PropAccess; - exports["AST_Dot"] = AST_Dot; - exports["AST_Sub"] = AST_Sub; - exports["AST_Unary"] = AST_Unary; - exports["AST_UnaryPrefix"] = AST_UnaryPrefix; - exports["AST_UnaryPostfix"] = AST_UnaryPostfix; - exports["AST_Binary"] = AST_Binary; - exports["AST_Conditional"] = AST_Conditional; - exports["AST_Assign"] = AST_Assign; - exports["AST_Array"] = AST_Array; - exports["AST_Object"] = AST_Object; - exports["AST_ObjectProperty"] = AST_ObjectProperty; - exports["AST_ObjectKeyVal"] = AST_ObjectKeyVal; - exports["AST_ObjectSetter"] = AST_ObjectSetter; - exports["AST_ObjectGetter"] = AST_ObjectGetter; - exports["AST_Symbol"] = AST_Symbol; - exports["AST_SymbolAccessor"] = AST_SymbolAccessor; - exports["AST_SymbolDeclaration"] = AST_SymbolDeclaration; - exports["AST_SymbolVar"] = AST_SymbolVar; - exports["AST_SymbolConst"] = AST_SymbolConst; - exports["AST_SymbolFunarg"] = AST_SymbolFunarg; - exports["AST_SymbolDefun"] = AST_SymbolDefun; - exports["AST_SymbolLambda"] = AST_SymbolLambda; - exports["AST_SymbolCatch"] = AST_SymbolCatch; - exports["AST_Label"] = AST_Label; - exports["AST_SymbolRef"] = AST_SymbolRef; - exports["AST_LabelRef"] = AST_LabelRef; - exports["AST_This"] = AST_This; - exports["AST_Constant"] = AST_Constant; - exports["AST_String"] = AST_String; - exports["AST_Number"] = AST_Number; - exports["AST_RegExp"] = AST_RegExp; - exports["AST_Atom"] = AST_Atom; - exports["AST_Null"] = AST_Null; - exports["AST_NaN"] = AST_NaN; - exports["AST_Undefined"] = AST_Undefined; - exports["AST_Hole"] = AST_Hole; - exports["AST_Infinity"] = AST_Infinity; - exports["AST_Boolean"] = AST_Boolean; - exports["AST_False"] = AST_False; - exports["AST_True"] = AST_True; - exports["TreeWalker"] = TreeWalker; - exports["KEYWORDS"] = KEYWORDS; - exports["KEYWORDS_ATOM"] = KEYWORDS_ATOM; - exports["RESERVED_WORDS"] = RESERVED_WORDS; - exports["KEYWORDS_BEFORE_EXPRESSION"] = KEYWORDS_BEFORE_EXPRESSION; - exports["OPERATOR_CHARS"] = OPERATOR_CHARS; - exports["RE_HEX_NUMBER"] = RE_HEX_NUMBER; - exports["RE_OCT_NUMBER"] = RE_OCT_NUMBER; - exports["RE_DEC_NUMBER"] = RE_DEC_NUMBER; - exports["OPERATORS"] = OPERATORS; - exports["WHITESPACE_CHARS"] = WHITESPACE_CHARS; - exports["PUNC_BEFORE_EXPRESSION"] = PUNC_BEFORE_EXPRESSION; - exports["PUNC_CHARS"] = PUNC_CHARS; - exports["REGEXP_MODIFIERS"] = REGEXP_MODIFIERS; - exports["UNICODE"] = UNICODE; - exports["is_letter"] = is_letter; - exports["is_digit"] = is_digit; - exports["is_alphanumeric_char"] = is_alphanumeric_char; - exports["is_unicode_combining_mark"] = is_unicode_combining_mark; - exports["is_unicode_connector_punctuation"] = is_unicode_connector_punctuation; - exports["is_identifier"] = is_identifier; - exports["is_identifier_start"] = is_identifier_start; - exports["is_identifier_char"] = is_identifier_char; - exports["is_identifier_string"] = is_identifier_string; - exports["parse_js_number"] = parse_js_number; - exports["JS_Parse_Error"] = JS_Parse_Error; - exports["js_error"] = js_error; - exports["is_token"] = is_token; - exports["EX_EOF"] = EX_EOF; - exports["tokenizer"] = tokenizer; - exports["UNARY_PREFIX"] = UNARY_PREFIX; - exports["UNARY_POSTFIX"] = UNARY_POSTFIX; - exports["ASSIGNMENT"] = ASSIGNMENT; - exports["PRECEDENCE"] = PRECEDENCE; - exports["STATEMENTS_WITH_LABELS"] = STATEMENTS_WITH_LABELS; - exports["ATOMIC_START_TOKEN"] = ATOMIC_START_TOKEN; - exports["parse"] = parse; - exports["TreeTransformer"] = TreeTransformer; - exports["SymbolDef"] = SymbolDef; - exports["base54"] = base54; - exports["OutputStream"] = OutputStream; - exports["Compressor"] = Compressor; - exports["SourceMap"] = SourceMap; -})({}, function() { - return exports; -}()); + } + + function mangleStrings(node) { + return node.transform(new TreeTransformer(function(node){ + if (node instanceof AST_Seq) { + node.cdr = mangleStrings(node.cdr); + } + else if (node instanceof AST_String) { + node.value = mangle(node.value); + } + else if (node instanceof AST_Conditional) { + node.consequent = mangleStrings(node.consequent); + node.alternative = mangleStrings(node.alternative); + } + return node; + })); + } -var UglifyJS = exports.UglifyJS; +} -UglifyJS.AST_Node.warn_function = function(txt) { - logger.error("uglifyjs2 WARN: " + txt); +exports["Compressor"] = Compressor; +exports["DefaultsError"] = DefaultsError; +exports["Dictionary"] = Dictionary; +exports["JS_Parse_Error"] = JS_Parse_Error; +exports["MAP"] = MAP; +exports["OutputStream"] = OutputStream; +exports["SourceMap"] = SourceMap; +exports["TreeTransformer"] = TreeTransformer; +exports["TreeWalker"] = TreeWalker; +exports["base54"] = base54; +exports["defaults"] = defaults; +exports["mangle_properties"] = mangle_properties; +exports["merge"] = merge; +exports["parse"] = parse; +exports["push_uniq"] = push_uniq; +exports["string_template"] = string_template; +exports["tokenizer"] = tokenizer; +exports["is_identifier"] = is_identifier; +exports["SymbolDef"] = SymbolDef; + +AST_Node.warn_function = function(txt) { logger.error("uglifyjs WARN: " + txt); }; + +exports.AST_Node.warn_function = function(txt) { + console.error("WARN: %s", txt); }; -//JRB: MODIFIED FROM UGLIFY SOURCE -//to take a name for the file, and then set toplevel.filename to be that name. +function read_source_map(code) { + var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code); + if (!match) { + exports.AST_Node.warn("inline source map not found"); + return null; + } + return JSON.parse(new Buffer(match[2], "base64")); +} + exports.minify = function(files, options, name) { - options = UglifyJS.defaults(options, { - outSourceMap : null, - sourceRoot : null, - inSourceMap : null, - fromString : false, - warnings : false, - mangle : {}, - output : null, - compress : {} + options = exports.defaults(options, { + compress : {}, + fromString : false, + inSourceMap : null, + mangle : {}, + mangleProperties : false, + nameCache : null, + outFileName : null, + output : null, + outSourceMap : null, + parse : {}, + sourceMapInline : false, + sourceMapUrl : null, + sourceRoot : null, + spidermonkey : false, + warnings : false, }); - if (typeof files == "string") - files = [ files ]; + exports.base54.reset(); - UglifyJS.base54.reset(); + var inMap = options.inSourceMap; + if (typeof inMap == "string" && inMap != "inline") { + inMap = JSON.parse(rjsFile.readFile(inMap, "utf8")); + } // 1. parse - var toplevel = null; - files.forEach(function(file){ - var code = options.fromString - ? file - : rjsFile.readFile(file, "utf8"); - toplevel = UglifyJS.parse(code, { - filename: options.fromString ? name : file, - toplevel: toplevel + var toplevel = null, + sourcesContent = {}; + + if (options.spidermonkey) { + if (inMap == "inline") { + throw new Error("inline source map only works with built-in parser"); + } + toplevel = exports.AST_Node.from_mozilla_ast(files); + } else { + var addFile = function(file, fileUrl) { + var code = options.fromString + ? file + : rjsFile.readFile(file, "utf8"); + if (inMap == "inline") { + inMap = read_source_map(code); + } + sourcesContent[fileUrl] = code; + toplevel = exports.parse(code, { + filename: fileUrl, + toplevel: toplevel, + bare_returns: options.parse ? options.parse.bare_returns : undefined + }); + } + if (!options.fromString) { + files = exports.simple_glob(files); + if (inMap == "inline" && files.length > 1) { + throw new Error("inline source map only works with singular input"); + } + } + [].concat(files).forEach(function (files, i) { + if (typeof files === 'string') { + addFile(files, options.fromString ? i : files); + } else { + for (var fileUrl in files) { + addFile(files[fileUrl], fileUrl); + } + } }); - }); + } + if (options.wrap) { + toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); + } // 2. compress if (options.compress) { var compress = { warnings: options.warnings }; - UglifyJS.merge(compress, options.compress); - toplevel.figure_out_scope(); - var sq = UglifyJS.Compressor(compress); - toplevel = toplevel.transform(sq); + exports.merge(compress, options.compress); + toplevel.figure_out_scope(options.mangle); + var sq = exports.Compressor(compress); + toplevel = sq.compress(toplevel); + } + + // 3. mangle properties + if (options.mangleProperties || options.nameCache) { + options.mangleProperties.cache = exports.readNameCache(options.nameCache, "props"); + toplevel = exports.mangle_properties(toplevel, options.mangleProperties); + exports.writeNameCache(options.nameCache, "props", options.mangleProperties.cache); } - // 3. mangle + // 4. mangle if (options.mangle) { - toplevel.figure_out_scope(); - toplevel.compute_char_frequency(); + toplevel.figure_out_scope(options.mangle); + toplevel.compute_char_frequency(options.mangle); toplevel.mangle_names(options.mangle); } - // 4. output - var inMap = options.inSourceMap; - var output = {}; - if (typeof options.inSourceMap == "string") { - inMap = rjsFile.readFile(options.inSourceMap, "utf8"); - } - if (options.outSourceMap) { - output.source_map = UglifyJS.SourceMap({ - file: options.outSourceMap, + // 5. output + var output = { max_line_len: 32000 }; + if (options.outSourceMap || options.sourceMapInline) { + output.source_map = exports.SourceMap({ + // prefer outFileName, otherwise use outSourceMap without .map suffix + file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null), orig: inMap, root: options.sourceRoot }); + if (options.sourceMapIncludeSources) { + for (var file in sourcesContent) { + if (sourcesContent.hasOwnProperty(file)) { + output.source_map.get().setSourceContent(file, sourcesContent[file]); + } + } + } + } if (options.output) { - UglifyJS.merge(output, options.output); + exports.merge(output, options.output); } - var stream = UglifyJS.OutputStream(output); + var stream = exports.OutputStream(output); toplevel.print(stream); + + + var source_map = output.source_map; + if (source_map) { + source_map = source_map + ""; + } + + var mappingUrlPrefix = "\n//# sourceMappingURL="; + if (options.sourceMapInline) { + stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64"); + } else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) { + stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap); + } + return { code : stream + "", - map : output.source_map + "" + map : source_map }; }; @@ -21087,11 +24827,11 @@ exports.minify = function(files, options, name) { // if (ctor.SUBCLASSES.length > 0) ret.sub = sub; // return ret; // } -// return doitem(UglifyJS.AST_Node).sub; +// return doitem(exports.AST_Node).sub; // } exports.describe_ast = function() { - var out = UglifyJS.OutputStream({ beautify: true }); + var out = exports.OutputStream({ beautify: true }); function doitem(ctor) { out.print("AST_" + ctor.TYPE); var props = ctor.SELF_PROPS.filter(function(prop){ @@ -21121,17 +24861,103 @@ exports.describe_ast = function() { }); } }; - doitem(UglifyJS.AST_Node); + doitem(exports.AST_Node); return out + ""; }; -}); -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ +function readReservedFile(filename, reserved) { + if (!reserved) { + reserved = { vars: [], props: [] }; + } + var data = rjsFile.readFile(filename, "utf8"); + data = JSON.parse(data); + if (data.vars) { + data.vars.forEach(function(name){ + exports.push_uniq(reserved.vars, name); + }); + } + if (data.props) { + data.props.forEach(function(name){ + exports.push_uniq(reserved.props, name); + }); + } + return reserved; +} + +exports.readReservedFile = readReservedFile; + +exports.readDefaultReservedFile = function(reserved) { + return readReservedFile(require.resolve("./domprops.json"), reserved); +}; + +exports.readNameCache = function(filename, key) { + var cache = null; + if (filename) { + try { + var cache = rjsFile.readFile(filename, "utf8"); + cache = JSON.parse(cache)[key]; + if (!cache) throw "init"; + cache.props = exports.Dictionary.fromObject(cache.props); + } catch(ex) { + cache = { + cname: -1, + props: new exports.Dictionary() + }; + } + } + return cache; +}; + +exports.writeNameCache = function(filename, key, cache) { + if (filename) { + var data; + try { + data = rjsFile.readFile(filename, "utf8"); + data = JSON.parse(data); + } catch(ex) { + data = {}; + } + data[key] = { + cname: cache.cname, + props: cache.props.toObject() + }; + rjsFile.writeFile(filename, JSON.stringify(data, null, 2), "utf8"); + } +}; + +// A file glob function that only supports "*" and "?" wildcards in the basename. +// Example: "foo/bar/*baz??.*.js" +// Argument `glob` may be a string or an array of strings. +// Returns an array of strings. Garbage in, garbage out. +exports.simple_glob = function simple_glob(glob) { + if (Array.isArray(glob)) { + return [].concat.apply([], glob.map(simple_glob)); + } + if (glob.match(/\*|\?/)) { + var dir = path.dirname(glob); + try { + var entries = fs.readdirSync(dir); + } catch (ex) {} + if (entries) { + var pattern = "^" + path.basename(glob) + .replace(/[.+^$[\]\\(){}]/g, "\\$&") + .replace(/\*/g, "[^/\\\\]*") + .replace(/\?/g, "[^/\\\\]") + "$"; + var mod = process.platform === "win32" ? "i" : ""; + var rx = new RegExp(pattern, mod); + var results = entries.filter(function(name) { + return rx.test(name); + }).map(function(name) { + return path.join(dir, name); + }); + if (results.length) return results; + } + } + return [ glob ]; +}; + +}); /*jslint plusplus: true */ /*global define: false */ @@ -21152,11 +24978,15 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { //This string is saved off because JSLint complains //about obj.arguments use, as 'reserved word' - var argPropName = 'arguments'; + var argPropName = 'arguments', + //Default object to use for "scope" checking for UMD identifiers. + emptyScope = {}, + mixin = lang.mixin, + hasProp = lang.hasProp; //From an esprima example for traversing its ast. function traverse(object, visitor) { - var key, child; + var child; if (!object) { return; @@ -21165,13 +24995,11 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { if (visitor.call(null, object) === false) { return false; } - for (key in object) { - if (object.hasOwnProperty(key)) { - child = object[key]; - if (typeof child === 'object' && child !== null) { - if (traverse(child, visitor) === false) { - return false; - } + for (var i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[keys[i]]; + if (typeof child === 'object' && child !== null) { + if (traverse(child, visitor) === false) { + return false; } } } @@ -21181,7 +25009,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { //stops that subtree analysis, not the rest of tree //visiting. function traverseBroad(object, visitor) { - var key, child; + var child; if (!object) { return; @@ -21190,12 +25018,10 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { if (visitor.call(null, object) === false) { return false; } - for (key in object) { - if (object.hasOwnProperty(key)) { - child = object[key]; - if (typeof child === 'object' && child !== null) { - traverse(child, visitor); - } + for (var i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[key]; + if (typeof child === 'object' && child !== null) { + traverseBroad(child, visitor); } } } @@ -21227,6 +25053,13 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { return deps.length ? deps : undefined; } + // Detects regular or arrow function expressions as the desired expression + // type. + function isFnExpression(node) { + return (node && (node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression')); + } + /** * Main parse function. Returns a string of any valid require or * define/require.def calls as part of one JavaScript source string. @@ -21254,7 +25087,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { needsDefine = true, astRoot = esprima.parse(fileContents); - parse.recurse(astRoot, function (callName, config, name, deps) { + parse.recurse(astRoot, function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) { if (!deps) { deps = []; } @@ -21274,6 +25107,10 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { }); } + if (callName === 'define' && factoryIdentifier && hasProp(fnExpScope, factoryIdentifier)) { + return factoryIdentifier; + } + //If define was found, no need to dive deeper, unless //the config explicitly wants to dig deeper. return !!options.findNestedDependencies; @@ -21316,6 +25153,7 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { parse.traverse = traverse; parse.traverseBroad = traverseBroad; + parse.isFnExpression = isFnExpression; /** * Handles parsing a file recursively for require calls. @@ -21323,14 +25161,18 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { * @param {Function} onMatch function to call on a parse match. * @param {Object} [options] This is normally the build config options if * it is passed. + * @param {Object} [fnExpScope] holds list of function expresssion + * argument identifiers, set up internally, not passed in */ - parse.recurse = function (object, onMatch, options) { + parse.recurse = function (object, onMatch, options, fnExpScope) { //Like traverse, but skips if branches that would not be processed //after has application that results in tests of true or false boolean //literal values. - var key, child, + var keys, child, result, i, params, param, tempObject, hasHas = options && options.has; + fnExpScope = fnExpScope || emptyScope; + if (!object) { return; } @@ -21341,23 +25183,70 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { object.test.type === 'Literal') { if (object.test.value) { //Take the if branch - this.recurse(object.consequent, onMatch, options); + this.recurse(object.consequent, onMatch, options, fnExpScope); } else { //Take the else branch - this.recurse(object.alternate, onMatch, options); + this.recurse(object.alternate, onMatch, options, fnExpScope); } } else { - if (this.parseNode(object, onMatch) === false) { + result = this.parseNode(object, onMatch, fnExpScope); + if (result === false) { return; + } else if (typeof result === 'string') { + return result; + } + + //Build up a "scope" object that informs nested recurse calls if + //the define call references an identifier that is likely a UMD + //wrapped function expression argument. + //Catch (function(a) {... wrappers + if (object.type === 'ExpressionStatement' && object.expression && + object.expression.type === 'CallExpression' && object.expression.callee && + isFnExpression(object.expression.callee)) { + tempObject = object.expression.callee; + } + // Catch !function(a) {... wrappers + if (object.type === 'UnaryExpression' && object.argument && + object.argument.type === 'CallExpression' && object.argument.callee && + isFnExpression(object.argument.callee)) { + tempObject = object.argument.callee; + } + if (tempObject && tempObject.params && tempObject.params.length) { + params = tempObject.params; + fnExpScope = mixin({}, fnExpScope, true); + for (i = 0; i < params.length; i++) { + param = params[i]; + if (param.type === 'Identifier') { + fnExpScope[param.name] = true; + } + } } - for (key in object) { - if (object.hasOwnProperty(key)) { - child = object[key]; - if (typeof child === 'object' && child !== null) { - this.recurse(child, onMatch, options); + + for (i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[keys[i]]; + if (typeof child === 'object' && child !== null) { + result = this.recurse(child, onMatch, options, fnExpScope); + if (typeof result === 'string' && hasProp(fnExpScope, result)) { + //The result was still in fnExpScope so break. Otherwise, + //was a return from a a tree that had a UMD definition, + //but now out of that scope so keep siblings. + break; } } } + + //Check for an identifier for a factory function identifier being + //passed in as a function expression, indicating a UMD-type of + //wrapping. + if (typeof result === 'string') { + if (hasProp(fnExpScope, result)) { + //result still in scope, keep jumping out indicating the + //identifier still in use. + return result; + } + + return; + } } }; @@ -21369,18 +25258,56 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { * @returns {Boolean} */ parse.definesRequire = function (fileName, fileContents) { - var found = false; + var foundDefine = false, + foundDefineAmd = false; + + traverse(esprima.parse(fileContents), function (node) { + // Look for a top level declaration of a define, like + // var requirejs, require, define, off Program body. + if (node.type === 'Program' && node.body && node.body.length) { + foundDefine = node.body.some(function(bodyNode) { + // var define + if (bodyNode.type === 'VariableDeclaration') { + var decls = bodyNode.declarations; + if (decls) { + var hasVarDefine = decls.some(function(declNode) { + return (declNode.type === 'VariableDeclarator' && + declNode.id && + declNode.id.type === 'Identifier' && + declNode.id.name === 'define'); + }); + if (hasVarDefine) { + return true; + } + } + } + + // function define() {} + if (bodyNode.type === 'FunctionDeclaration' && + bodyNode.id && + bodyNode.id.type === 'Identifier' && + bodyNode.id.name === 'define') { + return true; + } + + + + - traverse(esprima.parse(fileContents), function (node) { - if (parse.hasDefineAmd(node)) { - found = true; + + }); + } + + // Need define variable found first, before detecting define.amd. + if (foundDefine && parse.hasDefineAmd(node)) { + foundDefineAmd = true; //Stop traversal return false; } }); - return found; + return foundDefine && foundDefineAmd; }; /** @@ -21449,15 +25376,14 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { //Just the factory function passed to define arg0 = node[argPropName][0]; - if (arg0 && arg0.type === 'FunctionExpression') { + if (isFnExpression(arg0)) { match = arg0; return false; } //A string literal module ID followed by the factory function. arg1 = node[argPropName][1]; - if (arg0.type === 'Literal' && - arg1 && arg1.type === 'FunctionExpression') { + if (arg0.type === 'Literal' && isFnExpression(arg1)) { match = arg1; return false; } @@ -21711,6 +25637,33 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { return name; }; + /** + * Finds all the named define module IDs in a file. + */ + parse.getAllNamedDefines = function (fileContents, excludeMap) { + var names = []; + parse.recurse(esprima.parse(fileContents), + function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) { + if (callName === 'define' && name) { + if (!excludeMap.hasOwnProperty(name)) { + names.push(name); + } + } + + //If a UMD definition that points to a factory that is an Identifier, + //indicate processing should not traverse inside the UMD definition. + if (callName === 'define' && factoryIdentifier && hasProp(fnExpScope, factoryIdentifier)) { + return factoryIdentifier; + } + + //If define was found, no need to dive deeper, unless + //the config explicitly wants to dig deeper. + return true; + }, {}); + + return names; + }; + /** * Determines if define(), require({}|[]) or requirejs was called in the * file. Also finds out if define() is declared and if define.amd is called. @@ -21781,6 +25734,11 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { } else if (exp.left.object.name === 'exports' && exp.left.property) { type = 'exports'; + } else if (exp.left.object.type === 'MemberExpression' && + exp.left.object.object.name === 'module' && + exp.left.object.property.name === 'exports' && + exp.left.object.property.type === 'Identifier') { + type = 'moduleExports'; } } else if (node && node.type === 'CallExpression' && node.callee && @@ -21831,19 +25789,23 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { * @param {Function} onMatch a function to call when a match is found. * It is passed the match name, and the config, name, deps possible args. * The config, name and deps args are not normalized. + * @param {Object} fnExpScope an object whose keys are all function + * expression identifiers that should be in scope. Useful for UMD wrapper + * detection to avoid parsing more into the wrapped UMD code. * * @returns {String} a JS source string with the valid require/define call. * Otherwise null. */ - parse.parseNode = function (node, onMatch) { + parse.parseNode = function (node, onMatch, fnExpScope) { var name, deps, cjsDeps, arg, factory, exp, refsDefine, bodyNode, args = node && node[argPropName], - callName = parse.hasRequire(node); + callName = parse.hasRequire(node), + isUmd = false; if (callName === 'require' || callName === 'requirejs') { //A plain require/requirejs call arg = node[argPropName] && node[argPropName][0]; - if (arg.type !== 'ArrayExpression') { + if (arg && arg.type !== 'ArrayExpression') { if (arg.type === 'ObjectExpression') { //A config call, try the second arg. arg = node[argPropName][1]; @@ -21866,39 +25828,54 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { factory = deps; deps = name; name = null; - } else if (name.type === 'FunctionExpression') { + } else if (isFnExpression(name)) { //Just the factory, no name or deps factory = name; name = deps = null; + } else if (name.type === 'Identifier' && args.length === 1 && + hasProp(fnExpScope, name.name)) { + //define(e) where e is a UMD identifier for the factory + //function. + isUmd = true; + factory = name; + name = null; } else if (name.type !== 'Literal') { //An object literal, just null out name = deps = factory = null; } if (name && name.type === 'Literal' && deps) { - if (deps.type === 'FunctionExpression') { + if (isFnExpression(deps)) { //deps is the factory factory = deps; deps = null; } else if (deps.type === 'ObjectExpression') { //deps is object literal, null out deps = factory = null; - } else if (deps.type === 'Identifier' && args.length === 2) { - // define('id', factory) - deps = factory = null; + } else if (deps.type === 'Identifier') { + if (args.length === 2) { + //define('id', factory) + deps = factory = null; + } else if (args.length === 3 && isFnExpression(factory)) { + //define('id', depsIdentifier, factory) + //Since identifier, cannot know the deps, but do not + //error out, assume they are taken care of outside of + //static parsing. + deps = null; + } } } if (deps && deps.type === 'ArrayExpression') { deps = getValidDeps(deps); - } else if (factory && factory.type === 'FunctionExpression') { + } else if (isFnExpression(factory)) { //If no deps and a factory function, could be a commonjs sugar //wrapper, scan the function for dependencies. cjsDeps = parse.getAnonDepsFromNode(factory); if (cjsDeps.length) { deps = cjsDeps; } - } else if (deps || factory) { + } else if (deps || (factory && !isUmd)) { //Does not match the shape of an AMD call. return; } @@ -21908,9 +25885,11 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { name = name.value; } - return onMatch("define", null, name, deps, node); + return onMatch("define", null, name, deps, node, + (factory && factory.type === 'Identifier' ? factory.name : undefined), + fnExpScope); } else if (node.type === 'CallExpression' && node.callee && - node.callee.type === 'FunctionExpression' && + isFnExpression(node.callee) && node.callee.body && node.callee.body.body && node.callee.body.body.length === 1 && node.callee.body.body[0].type === 'IfStatement') { @@ -21935,7 +25914,8 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { }); if (refsDefine) { - return onMatch("define", null, null, null, exp.expression); + return onMatch("define", null, null, null, exp.expression, + exp.expression.arguments[0].name, fnExpScope); } } } @@ -22052,12 +26032,6 @@ define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { return parse; }); -/** - * @license Copyright (c) 2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*global define */ define('transform', [ './esprimaAdapter', './parse', 'logger', 'lang'], @@ -22085,7 +26059,16 @@ function (esprima, parse, logger, lang) { foundAnon, scanCount = 0, scanReset = false, - defineInfos = []; + defineInfos = [], + applySourceUrl = function (contents) { + if (options.useSourceUrl) { + contents = 'eval("' + lang.jsEscape(contents) + + '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') + + path + + '");\n'; + } + return contents; + }; try { astRoot = esprima.parse(contents, { @@ -22098,12 +26081,35 @@ function (esprima, parse, logger, lang) { } //Find the define calls and their position in the files. - parse.traverseBroad(astRoot, function (node) { + parse.traverse(astRoot, function (node) { var args, firstArg, firstArgLoc, factoryNode, - needsId, depAction, foundId, + needsId, depAction, foundId, init, sourceUrlData, range, namespaceExists = false; + // If a bundle script with a define declaration, do not + // parse any further at this level. Likely a built layer + // by some other tool. + if (node.type === 'VariableDeclarator' && + node.id && node.id.name === 'define' && + node.id.type === 'Identifier') { + init = node.init; + if (init && init.callee && + init.callee.type === 'CallExpression' && + init.callee.callee && + init.callee.callee.type === 'Identifier' && + init.callee.callee.name === 'require' && + init.callee.arguments && init.callee.arguments.length === 1 && + init.callee.arguments[0].type === 'Literal' && + init.callee.arguments[0].value && + init.callee.arguments[0].value.indexOf('amdefine') !== -1) { + // the var define = require('amdefine')(module) case, + // keep going in that case. + } else { + return false; + } + } + namespaceExists = namespace && node.type === 'CallExpression' && node.callee && node.callee.object && @@ -22129,7 +26135,7 @@ function (esprima, parse, logger, lang) { //to limit impact of false positives. needsId = true; depAction = 'empty'; - } else if (firstArg.type === 'FunctionExpression') { + } else if (parse.isFnExpression(firstArg)) { //define(function(){}) factoryNode = firstArg; needsId = true; @@ -22169,7 +26175,7 @@ function (esprima, parse, logger, lang) { //Already has an ID. needsId = false; if (args.length === 2 && - args[1].type === 'FunctionExpression') { + parse.isFnExpression(args[1])) { //Needs dependency scanning. factoryNode = args[1]; depAction = 'scan'; @@ -22228,8 +26234,9 @@ function (esprima, parse, logger, lang) { } }); + if (!defineInfos.length) { - return contents; + return applySourceUrl(contents); } //Reverse the matches, need to start from the bottom of @@ -22301,14 +26308,7 @@ function (esprima, parse, logger, lang) { contents = contentLines.join('\n'); - if (options.useSourceUrl) { - contents = 'eval("' + lang.jsEscape(contents) + - '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') + - path + - '");\n'; - } - - return contents; + return applySourceUrl(contents); }, /** @@ -22470,12 +26470,6 @@ function (esprima, parse, logger, lang) { return transform; }); -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint regexp: true, plusplus: true */ /*global define: false */ @@ -22503,21 +26497,23 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { var pragma = { conditionalRegExp: /(exclude|include)Start\s*\(\s*["'](\w+)["']\s*,(.*)\)/, - useStrictRegExp: /['"]use strict['"];/g, + useStrictRegExp: /(^|[^{]\r?\n)['"]use strict['"];/g, hasRegExp: /has\s*\(\s*['"]([^'"]+)['"]\s*\)/g, configRegExp: /(^|[^\.])(requirejs|require)(\.config)\s*\(/g, nsWrapRegExp: /\/\*requirejs namespace: true \*\//, apiDefRegExp: /var requirejs,\s*require,\s*define;/, - defineCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd/g, - defineStringCheckRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g, + defineCheckRegExp: /typeof(\s+|\s*\(\s*)define(\s*\))?\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd/g, + defineStringCheckRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g, defineTypeFirstCheckRegExp: /\s*["']function["']\s*==(=?)\s*typeof\s+define\s*&&\s*define\s*\.\s*amd/g, - defineJQueryRegExp: /typeof\s+define\s*===\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g, + defineJQueryRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g, defineHasRegExp: /typeof\s+define\s*==(=)?\s*['"]function['"]\s*&&\s*typeof\s+define\.amd\s*==(=)?\s*['"]object['"]\s*&&\s*define\.amd/g, - defineTernaryRegExp: /typeof\s+define\s*===\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/, - amdefineRegExp: /if\s*\(\s*typeof define\s*\!==\s*'function'\s*\)\s*\{\s*[^\{\}]+amdefine[^\{\}]+\}/g, + defineTernaryRegExp: /typeof\s+define\s*===?\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/, + defineExistsRegExp: /\s+typeof\s+define\s*!==?\s*['"]undefined["']\s*/, + defineExistsAndAmdRegExp: /typeof\s+define\s*!==?\s*['"]undefined["']\s*&&\s*define\s*\.\s*amd\s*/, + amdefineRegExp: /if\s*\(\s*typeof define\s*\!==\s*['"]function['"]\s*\)\s*\{\s*[^\{\}]+amdefine[^\{\}]+\}/g, removeStrict: function (contents, config) { - return config.useStrict ? contents : contents.replace(pragma.useStrictRegExp, ''); + return config.useStrict ? contents : contents.replace(pragma.useStrictRegExp, '$1'); }, namespace: function (fileContents, ns, onLifecycleName) { @@ -22540,6 +26536,10 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { fileContents = fileContents.replace(pragma.defineHasRegExp, "typeof " + ns + ".define === 'function' && typeof " + ns + ".define.amd === 'object' && " + ns + ".define.amd"); + //Namespace async.js define use: + fileContents = fileContents.replace(pragma.defineExistsAndAmdRegExp, + "typeof " + ns + ".define !== 'undefined' && " + ns + ".define.amd"); + //Namespace define checks. //Do these ones last, since they are a subset of the more specific //checks above. @@ -22549,6 +26549,8 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { "typeof " + ns + ".define === 'function' && " + ns + ".define['amd']"); fileContents = fileContents.replace(pragma.defineTypeFirstCheckRegExp, "'function' === typeof " + ns + ".define && " + ns + ".define.amd"); + fileContents = fileContents.replace(pragma.defineExistsRegExp, + "typeof " + ns + ".define !== 'undefined'"); //Check for require.js with the require/define definitions if (pragma.apiDefRegExp.test(fileContents) && @@ -22720,7 +26722,9 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { } //Strip amdefine use for node-shared modules. - fileContents = fileContents.replace(pragma.amdefineRegExp, ''); + if (!config.keepAmdefine) { + fileContents = fileContents.replace(pragma.amdefineRegExp, ''); + } //Do namespacing if (onLifecycleName === 'OnSave' && config.namespace) { @@ -22734,13 +26738,8 @@ define('pragma', ['parse', 'logger'], function (parse, logger) { return pragma; }); -if(env === 'browser') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ +if(env === 'browser') { /*jslint strict: false */ /*global define: false */ @@ -22749,12 +26748,6 @@ define('browser/optimize', {}); } if(env === 'node') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint strict: false */ /*global define: false */ @@ -22763,12 +26756,6 @@ define('node/optimize', {}); } if(env === 'rhino') { -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint sloppy: true, plusplus: true */ /*global define, java, Packages, com */ @@ -22809,8 +26796,20 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { //Bind to Closure compiler, but if it is not available, do not sweat it. try { + // Try older closure compiler that worked on Java 6 JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.JSSourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); - } catch (e) {} + } catch (e) { + try { + // Try for newer closure compiler that needs Java 7+ + JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.SourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); + } catch (e) { + try { + // Try Nashorn style + var stringClass = Java.type("java.lang.String").class; + JSSourceFilefromCode = Java.type("com.google.javascript.jscomp.SourceFile").class.getMethod("fromCode", [stringClass, stringClass]); + } catch (e) {} + } + } //Helper for closure compiler, because of weird Java-JavaScript interactions. function closurefromCode(filename, content) { @@ -22848,7 +26847,8 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { //Set up source input jsSourceFile = closurefromCode(String(fileName), String(fileContents)), sourceListArray = new java.util.ArrayList(), - options, option, FLAG_compilation_level, compiler, + externList = new java.util.ArrayList(), + options, option, FLAG_compilation_level, compiler, externExportsPath, Compiler = Packages.com.google.javascript.jscomp.Compiler, CommandLineRunner = Packages.com.google.javascript.jscomp.CommandLineRunner; @@ -22878,6 +26878,14 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { options.setSourceMapOutputPath(fileName + ".map"); } + //If we need to pass an externs file to Closure so that it does not create aliases + //for certain symbols, do so here. + externList.addAll(CommandLineRunner.getDefaultExterns()); + if (config.externExportsPath) { + externExportsPath = config.externExportsPath; + externList.add(jscomp.SourceFile.fromFile(externExportsPath)); + } + //Trigger the compiler Compiler.setLoggingLevel(Packages.java.util.logging.Level[config.loggingLevel || 'WARNING']); compiler = new Compiler(); @@ -22886,7 +26894,7 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { //accepting the getDefaultExterns return value (a List) also wants the sources as a List sourceListArray.add(jsSourceFile); - result = compiler.compile(CommandLineRunner.getDefaultExterns(), sourceListArray, options); + result = compiler.compile(externList, sourceListArray, options); if (result.success) { optimized = String(compiler.toSource()); @@ -22939,20 +26947,14 @@ define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { if(env === 'xpconnect') { define('xpconnect/optimize', {}); } -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint plusplus: true, nomen: true, regexp: true */ /*global define: false */ define('optimize', [ 'lang', 'logger', 'env!env/optimize', 'env!env/file', 'parse', - 'pragma', 'uglifyjs/index', 'uglifyjs2', + 'pragma', 'uglifyjs', 'source-map'], function (lang, logger, envOptimize, file, parse, - pragma, uglify, uglify2, + pragma, uglify, sourceMap) { 'use strict'; @@ -22960,8 +26962,10 @@ function (lang, logger, envOptimize, file, parse, cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/ig, cssCommentImportRegExp = /\/\*[^\*]*@import[^\*]*\*\//g, cssUrlRegExp = /\url\(\s*([^\)]+)\s*\)?/g, + protocolRegExp = /^\w+:/, SourceMapGenerator = sourceMap.SourceMapGenerator, - SourceMapConsumer =sourceMap.SourceMapConsumer; + SourceMapConsumer = sourceMap.SourceMapConsumer, + es5PlusGuidance = 'If the source uses ES2015 or later syntax, please pass "optimize: \'none\'" to r.js and use an ES2015+ compatible minifier after running r.js. The included UglifyJS only understands ES5 or earlier syntax.'; /** * If an URL from a CSS url value contains start/end quotes, remove them. @@ -22984,19 +26988,19 @@ function (lang, logger, envOptimize, file, parse, function fixCssUrlPaths(fileName, path, contents, cssPrefix) { return contents.replace(cssUrlRegExp, function (fullMatch, urlMatch) { - var colonIndex, parts, i, + var firstChar, hasProtocol, parts, i, fixedUrlMatch = cleanCssUrlQuotes(urlMatch); fixedUrlMatch = fixedUrlMatch.replace(lang.backSlashRegExp, "/"); - //Only do the work for relative URLs. Skip things that start with / or have + //Only do the work for relative URLs. Skip things that start with / or #, or have //a protocol. - colonIndex = fixedUrlMatch.indexOf(":"); - if (fixedUrlMatch.charAt(0) !== "/" && (colonIndex === -1 || colonIndex > fixedUrlMatch.indexOf("/"))) { + firstChar = fixedUrlMatch.charAt(0); + hasProtocol = protocolRegExp.test(fixedUrlMatch); + if (firstChar !== "/" && firstChar !== "#" && !hasProtocol) { //It is a relative URL, tack on the cssPrefix and path prefix urlMatch = cssPrefix + path + fixedUrlMatch; - - } else { + } else if (!hasProtocol) { logger.trace(fileName + "\n URL not a relative URL, skipping: " + urlMatch); } @@ -23180,6 +27184,7 @@ function (lang, logger, envOptimize, file, parse, optConfig = config[optimizerName] || {}; if (config.generateSourceMaps) { optConfig.generateSourceMaps = !!config.generateSourceMaps; + optConfig._buildSourceMap = config._buildSourceMap; } try { @@ -23192,11 +27197,19 @@ function (lang, logger, envOptimize, file, parse, } } + if (config.generateSourceMaps && licenseContents) { + optConfig.preamble = licenseContents; + licenseContents = ''; + } + fileContents = licenseContents + optFunc(fileName, fileContents, outFileName, keepLines, optConfig); + if (optConfig._buildSourceMap && optConfig._buildSourceMap !== config._buildSourceMap) { + config._buildSourceMap = optConfig._buildSourceMap; + } } catch (e) { if (config.throwWhen && config.throwWhen.optimize) { throw e; @@ -23204,6 +27217,10 @@ function (lang, logger, envOptimize, file, parse, logger.error(e); } } + } else { + if (config._buildSourceMap) { + config._buildSourceMap = null; + } } return fileContents; @@ -23267,6 +27284,20 @@ function (lang, logger, envOptimize, file, parse, fileContents = fileContents.replace(/(\r\n)+/g, "\r\n"); fileContents = fileContents.replace(/(\n)+/g, "\n"); } + //Remove unnecessary whitespace + if (config.optimizeCss.indexOf(".keepWhitespace") === -1) { + //Remove leading and trailing whitespace from lines + fileContents = fileContents.replace(/^[ \t]+/gm, ""); + fileContents = fileContents.replace(/[ \t]+$/gm, ""); + //Remove whitespace after semicolon, colon, curly brackets and commas + fileContents = fileContents.replace(/(;|:|\{|}|,)[ \t]+/g, "$1"); + //Remove whitespace before opening curly brackets + fileContents = fileContents.replace(/[ \t]+(\{)/g, "$1"); + //Truncate double whitespace + fileContents = fileContents.replace(/([ \t])+/g, "$1"); + //Remove empty lines + fileContents = fileContents.replace(/^[ \t]*[\r\n]/gm,''); + } } catch (e) { fileContents = originalFileContents; logger.error("Could not optimized CSS file: " + fileName + ", error: " + e); @@ -23327,40 +27358,6 @@ function (lang, logger, envOptimize, file, parse, optimizers: { uglify: function (fileName, fileContents, outFileName, keepLines, config) { - var parser = uglify.parser, - processor = uglify.uglify, - ast, errMessage, errMatch; - - config = config || {}; - - logger.trace("Uglifying file: " + fileName); - - try { - ast = parser.parse(fileContents, config.strict_semicolons); - if (config.no_mangle !== true) { - ast = processor.ast_mangle(ast, config); - } - ast = processor.ast_squeeze(ast, config); - - fileContents = processor.gen_code(ast, config); - - if (config.max_line_length) { - fileContents = processor.split_lines(fileContents, config.max_line_length); - } - - //Add trailing semicolon to match uglifyjs command line version - fileContents += ';'; - } catch (e) { - errMessage = e.toString(); - errMatch = /\nError(\r)?\n/.exec(errMessage); - if (errMatch) { - errMessage = errMessage.substring(0, errMatch.index); - } - throw new Error('Cannot uglify file: ' + fileName + '. Skipping it. Error is:\n' + errMessage); - } - return fileContents; - }, - uglify2: function (fileName, fileContents, outFileName, keepLines, config) { var result, existingMap, resultMap, finalMap, sourceIndex, uconfig = {}, existingMapPath = outFileName + '.map', @@ -23372,37 +27369,50 @@ function (lang, logger, envOptimize, file, parse, uconfig.fromString = true; - if (config.generateSourceMaps && outFileName) { - uconfig.outSourceMap = baseName; + if (config.preamble) { + uconfig.output = {preamble: config.preamble}; + } + - if (file.exists(existingMapPath)) { + if (config.generateSourceMaps && (outFileName || config._buildSourceMap)) { + uconfig.outSourceMap = baseName + '.map'; + + if (config._buildSourceMap) { + existingMap = JSON.parse(config._buildSourceMap); + uconfig.inSourceMap = existingMap; + } else if (file.exists(existingMapPath)) { uconfig.inSourceMap = existingMapPath; existingMap = JSON.parse(file.readFile(existingMapPath)); } } - logger.trace("Uglify2 file: " + fileName); + logger.trace("Uglify file: " + fileName); try { //var tempContents = fileContents.replace(/\/\/\# sourceMappingURL=.*$/, ''); - result = uglify2.minify(fileContents, uconfig, baseName + '.src.js'); + result = uglify.minify(fileContents, uconfig, baseName + '.src.js'); if (uconfig.outSourceMap && result.map) { resultMap = result.map; - if (existingMap) { - resultMap = JSON.parse(resultMap); - finalMap = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(resultMap)); - finalMap.applySourceMap(new SourceMapConsumer(existingMap)); - resultMap = finalMap.toString(); - } else { + if (!existingMap && !config._buildSourceMap) { file.saveFile(outFileName + '.src.js', fileContents); } - file.saveFile(outFileName + '.map', resultMap); - fileContents = result.code + "\n//# sourceMappingURL=" + baseName + ".map"; + + fileContents = result.code; + + if (config._buildSourceMap) { + config._buildSourceMap = resultMap; + } else { + file.saveFile(outFileName + '.map', resultMap); + } } else { fileContents = result.code; } } catch (e) { - throw new Error('Cannot uglify2 file: ' + fileName + '. Skipping it. Error is:\n' + e.toString()); + var errorString = e.toString(); + var isSyntaxError = /SyntaxError/.test(errorString); + throw new Error('Cannot uglify file: ' + fileName + + '. Skipping it. Error is:\n' + errorString + + (isSyntaxError ? '\n\n' + es5PlusGuidance : '')); } return fileContents; } @@ -23411,11 +27421,6 @@ function (lang, logger, envOptimize, file, parse, return optimize; }); -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ /* * This file patches require.js to communicate with the build system. */ @@ -23441,7 +27446,18 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c var allowRun = true, hasProp = lang.hasProp, falseProp = lang.falseProp, - getOwn = lang.getOwn; + getOwn = lang.getOwn, + // Used to strip out use strict from toString()'d functions for the + // shim config since they will explicitly want to not be bound by strict, + // but some envs, explicitly xpcshell, adds a use strict. + useStrictRegExp = /['"]use strict['"];/g, + //Absolute path if starts with /, \, or x: + absoluteUrlRegExp = /^[\/\\]|^\w:/; + + //Turn off throwing on resolution conflict, that was just an older prim + //idea about finding errors early, but does not comply with how promises + //should operate. + prim.hideResolutionConflict = true; //This method should be called when the patches to require should take hold. return function () { @@ -23503,8 +27519,10 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c }; function normalizeUrlWithBase(context, moduleName, url) { - //Adjust the URL if it was not transformed to use baseUrl. - if (require.jsExtRegExp.test(moduleName)) { + //Adjust the URL if it was not transformed to use baseUrl, but only + //if the URL is not already an absolute path. + if (require.jsExtRegExp.test(moduleName) && + !absoluteUrlRegExp.test(url)) { url = (context.config.dir || context.config.dirBaseUrl) + url; } return url; @@ -23534,18 +27552,47 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c //spit out strings that can be used in the stringified //build output. context.makeShimExports = function (value) { - function fn() { - return '(function (global) {\n' + - ' return function () {\n' + - ' var ret, fn;\n' + - (value.init ? - (' fn = ' + value.init.toString() + ';\n' + - ' ret = fn.apply(global, arguments);\n') : '') + - (value.exports ? - ' return ret || global.' + value.exports + ';\n' : - ' return ret;\n') + - ' };\n' + - '}(this))'; + var fn; + if (context.config.wrapShim) { + fn = function () { + var str = 'return '; + // If specifies an export that is just a global + // name, no dot for a `this.` and such, then also + // attach to the global, for `var a = {}` files + // where the function closure would hide that from + // the global object. + if (value.exports && value.exports.indexOf('.') === -1) { + str += 'root.' + value.exports + ' = '; + } + + if (value.init) { + str += '(' + value.init.toString() + .replace(useStrictRegExp, '') + '.apply(this, arguments))'; + } + if (value.init && value.exports) { + str += ' || '; + } + if (value.exports) { + str += value.exports; + } + str += ';'; + return str; + }; + } else { + fn = function () { + return '(function (global) {\n' + + ' return function () {\n' + + ' var ret, fn;\n' + + (value.init ? + (' fn = ' + value.init.toString() + .replace(useStrictRegExp, '') + ';\n' + + ' ret = fn.apply(global, arguments);\n') : '') + + (value.exports ? + ' return ret || global.' + value.exports + ';\n' : + ' return ret;\n') + + ' };\n' + + '}(this))'; + }; } return fn; @@ -23560,7 +27607,7 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c if (mod && !mod.defined) { if (parentId && getOwn(needFullExec, parentId)) { - needFullExec[id] = true; + needFullExec[id] = depMap; } } else if ((getOwn(needFullExec, id) && falseProp(fullExec, id)) || @@ -23746,7 +27793,8 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c lang.each(depMaps, lang.bind(this, function (depMap) { if (typeof depMap === 'string') { depMap = context.makeModuleMap(depMap, - (this.map.isDefine ? this.map : this.map.parentMap)); + (this.map.isDefine ? this.map : this.map.parentMap), + false, true); } if (!context.fullExec[depMap.id]) { @@ -23765,7 +27813,7 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c pluginMod = getOwn(context.registry, pluginId); context.plugins[pluginId] = true; - context.needFullExec[pluginId] = true; + context.needFullExec[pluginId] = map; //If the module is not waiting to finish being defined, //undef it and start over, to get full execution. @@ -23842,13 +27890,26 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c var id = map.id, url; + // Fix up any maps that need to be normalized as part of the fullExec + // plumbing for plugins to participate in the build. + if (context.plugins && lang.hasProp(context.plugins, id)) { + lang.eachProp(context.needFullExec, function(value, prop) { + // For plugin entries themselves, they do not have a map + // value in needFullExec, just a "true" entry. + if (value !== true && value.prefix === id && value.unnormalized) { + var map = context.makeModuleMap(value.originalName, value.parentMap); + context.needFullExec[map.id] = map; + } + }); + } + //If build needed a full execution, indicate it //has been done now. But only do it if the context is tracking //that. Only valid for the context used in a build, not for //other contexts being run, like for useLib, plain requirejs //use in node/rhino. if (context.needFullExec && getOwn(context.needFullExec, id)) { - context.fullExec[id] = true; + context.fullExec[id] = map; } //A plugin. @@ -23883,12 +27944,6 @@ define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'c }; }; }); -/** - * @license RequireJS Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint */ /*global define: false, console: false */ @@ -23987,20 +28042,14 @@ define('commonJs', ['env!env/file', 'parse'], function (file, parse) { return commonJs; }); -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - /*jslint plusplus: true, nomen: true, regexp: true */ -/*global define, requirejs */ +/*global define, requirejs, java, process, console */ define('build', function (require) { 'use strict'; - var build, buildBaseConfig, + var build, lang = require('lang'), prim = require('prim'), logger = require('logger'), @@ -24012,12 +28061,22 @@ define('build', function (require) { requirePatch = require('requirePatch'), env = require('env'), commonJs = require('commonJs'), - SourceMapGenerator = require('source-map/source-map-generator'), + SourceMapGenerator = require('source-map').SourceMapGenerator, hasProp = lang.hasProp, getOwn = lang.getOwn, falseProp = lang.falseProp, endsWithSemiColonRegExp = /;\s*$/, - resourceIsModuleIdRegExp = /^[\w\/\\\.]+$/; + endsWithSlashRegExp = /[\/\\]$/, + resourceIsModuleIdRegExp = /^[\w\/\\\.]+$/, + deepCopyProps = { + layer: true + }; + + //Deep copy a config object, but do not copy over the "layer" property, + //as it can be a deeply nested structure with a full requirejs context. + function copyConfig(obj) { + return lang.deeplikeCopy(obj, deepCopyProps); + } prim.nextTick = function (fn) { fn(); @@ -24046,22 +28105,27 @@ define('build', function (require) { } }; - buildBaseConfig = { - appDir: "", - pragmas: {}, - paths: {}, - optimize: "uglify", - optimizeCss: "standard.keepLines", - inlineText: true, - isBuild: true, - optimizeAllPluginResources: false, - findNestedDependencies: false, - preserveLicenseComments: true, - //By default, all files/directories are copied, unless - //they match this regexp, by default just excludes .folders - dirExclusionRegExp: file.dirExclusionRegExp, - _buildPathToModuleIndex: {} - }; + function makeBuildBaseConfig() { + return { + appDir: "", + pragmas: {}, + paths: {}, + optimize: "uglify", + optimizeCss: "standard.keepLines.keepWhitespace", + inlineText: true, + isBuild: true, + optimizeAllPluginResources: false, + findNestedDependencies: false, + preserveLicenseComments: true, + writeBuildTxt: true, + //Some builds can take a while, up the default limit. + waitSeconds: 30, + //By default, all files/directories are copied, unless + //they match this regexp, by default just excludes .folders + dirExclusionRegExp: file.dirExclusionRegExp, + _buildPathToModuleIndex: {} + }; + } /** * Some JS may not be valid if concatenated with other JS, in particular @@ -24083,6 +28147,13 @@ define('build', function (require) { return dirName; } + function endsWithNewLine(text) { + if (text.charAt(text.length - 1) !== "\n") { + text += "\n"; + } + return text; + } + //Method used by plugin writeFile calls, defined up here to avoid //jslint warning about "making a function in a loop". function makeWriteFile(namespace, layer) { @@ -24099,6 +28170,75 @@ define('build', function (require) { return writeFile; } + /** + * Appends singleContents to fileContents and returns the result. If a sourceMapGenerator + * is provided, adds singleContents to the source map. + * + * @param {string} fileContents - The file contents to which to append singleContents + * @param {string} singleContents - The additional contents to append to fileContents + * @param {string} path - An absolute path of a file whose name to use in the source map. + * The file need not actually exist if the code in singleContents is generated. + * @param {{out: ?string, baseUrl: ?string}} config - The build configuration object. + * @param {?{_buildPath: ?string}} module - An object with module information. + * @param {?SourceMapGenerator} sourceMapGenerator - An instance of Mozilla's SourceMapGenerator, + * or null if no source map is being generated. + * @returns {string} fileContents with singleContents appended + */ + function appendToFileContents(fileContents, singleContents, path, config, module, sourceMapGenerator) { + var refPath, sourceMapPath, resourcePath, pluginId, sourceMapLineNumber, lineCount, parts, i; + if (sourceMapGenerator) { + if (config.out) { + refPath = config.baseUrl; + } else if (module && module._buildPath) { + refPath = module._buildPath; + } else { + refPath = ""; + } + parts = path.split('!'); + if (parts.length === 1) { + //Not a plugin resource, fix the path + sourceMapPath = build.makeRelativeFilePath(refPath, path); + } else { + //Plugin resource. If it looks like just a plugin + //followed by a module ID, pull off the plugin + //and put it at the end of the name, otherwise + //just leave it alone. + pluginId = parts.shift(); + resourcePath = parts.join('!'); + if (resourceIsModuleIdRegExp.test(resourcePath)) { + sourceMapPath = build.makeRelativeFilePath(refPath, require.toUrl(resourcePath)) + + '!' + pluginId; + } else { + sourceMapPath = path; + } + } + + sourceMapLineNumber = fileContents.split('\n').length - 1; + lineCount = singleContents.split('\n').length; + for (i = 1; i <= lineCount; i += 1) { + sourceMapGenerator.addMapping({ + generated: { + line: sourceMapLineNumber + i, + column: 0 + }, + original: { + line: i, + column: 0 + }, + source: sourceMapPath + }); + } + + //Store the content of the original in the source + //map since other transforms later like minification + //can mess up translating back to the original + //source. + sourceMapGenerator.setSourceContent(sourceMapPath, singleContents); + } + fileContents += singleContents; + return fileContents; + } + /** * Main API entry point into the build. The args argument can either be * an array of arguments (like the onese passed on a command-line), @@ -24249,7 +28389,7 @@ define('build', function (require) { if (paths[prop].indexOf(config.baseUrl) === 0) { buildPaths[prop] = paths[prop].replace(config.baseUrl, config.dirBaseUrl); } else { - buildPaths[prop] = paths[prop] === 'empty:' ? 'empty:' : prop.replace(/\./g, "/"); + buildPaths[prop] = paths[prop] === 'empty:' ? 'empty:' : prop; //Make sure source path is fully formed with baseUrl, //if it is a relative URL. @@ -24333,7 +28473,21 @@ define('build', function (require) { modules.forEach(function (module) { if (module.name) { module._buildPath = buildContext.nameToUrl(module.name, null); - if (!module.create) { + + //If buildPath and sourcePath are the same, throw since this + //would result in modifying source. This condition can happen + //with some more tricky paths: config and appDir/baseUrl + //setting, which is a sign of incorrect config. + if (module._buildPath === module._sourcePath && + !config.allowSourceOverwrites) { + throw new Error('Module ID \'' + module.name + + '\' has a source path that is same as output path: ' + + module._sourcePath + + '. Stopping, config is malformed.'); + } + + // Copy the file, but only if it is not provided in rawText. + if (!module.create && (!config.rawText || !lang.hasProp(config.rawText, module.name))) { file.copyFile(module._sourcePath, module._buildPath); } } @@ -24348,7 +28502,7 @@ define('build', function (require) { buildFileContents += optimize.css(config.dir, config); } }).then(function() { - baseConfig = lang.deeplikeCopy(require.s.contexts._.config); + baseConfig = copyConfig(require.s.contexts._.config); }).then(function () { var actions = []; @@ -24455,17 +28609,36 @@ define('build', function (require) { })); } }).then(function () { - var moduleName; + var moduleName, outOrigSourceMap, + bundlesConfig = {}, + bundlesConfigOutFile = config.bundlesConfigOutFile; + if (modules) { //Now move the build layers to their final position. modules.forEach(function (module) { - var finalPath = module._buildPath; + var entryConfig, + finalPath = module._buildPath; + if (finalPath !== 'FUNCTION') { if (file.exists(finalPath)) { file.deleteFile(finalPath); } file.renameFile(finalPath + '-temp', finalPath); + //If bundles config should be written out, scan the + //built file for module IDs. Favor doing this reparse + //since tracking the IDs as the file is built has some + //edge cases around files that had more than one ID in + //them already, and likely loader plugin-written contents. + if (bundlesConfigOutFile) { + entryConfig = bundlesConfig[module.name] = []; + var bundleContents = file.readFile(finalPath); + var excludeMap = {}; + excludeMap[module.name] = true; + var parsedIds = parse.getAllNamedDefines(bundleContents, excludeMap); + entryConfig.push.apply(entryConfig, parsedIds); + } + //And finally, if removeCombined is specified, remove //any of the files that were used in this layer. //Be sure not to remove other build layers. @@ -24492,6 +28665,24 @@ define('build', function (require) { config.onModuleBundleComplete(module.onCompleteData); } }); + + //Write out bundles config, if it is wanted. + if (bundlesConfigOutFile) { + var text = file.readFile(bundlesConfigOutFile); + text = transform.modifyConfig(text, function (config) { + if (!config.bundles) { + config.bundles = {}; + } + + lang.eachProp(bundlesConfig, function (value, prop) { + config.bundles[prop] = value; + }); + + return config; + }); + + file.saveUtf8File(bundlesConfigOutFile, text); + } } //If removeCombined in play, remove any empty directories that @@ -24505,10 +28696,18 @@ define('build', function (require) { //Just need to worry about one JS file. fileName = config.modules[0]._buildPath; if (fileName === 'FUNCTION') { - config.modules[0]._buildText = optimize.js(fileName, + outOrigSourceMap = config.modules[0]._buildSourceMap; + config._buildSourceMap = outOrigSourceMap; + config.modules[0]._buildText = optimize.js((config.modules[0].name || + config.modules[0].include[0] || + fileName) + '.build.js', config.modules[0]._buildText, null, config); + if (config._buildSourceMap && config._buildSourceMap !== outOrigSourceMap) { + config.modules[0]._buildSourceMap = config._buildSourceMap; + config._buildSourceMap = null; + } } else { optimize.jsFile(fileName, null, fileName, config); } @@ -24650,7 +28849,9 @@ define('build', function (require) { //All module layers are done, write out the build.txt file. - file.saveUtf8File(config.dir + "build.txt", buildFileContents); + if (config.writeBuildTxt) { + file.saveUtf8File(config.dir + "build.txt", buildFileContents); + } } //If just have one CSS file to optimize, do that here. @@ -24659,7 +28860,7 @@ define('build', function (require) { } if (typeof config.out === 'function') { - config.out(config.modules[0]._buildText); + config.out(config.modules[0]._buildText, config.modules[0]._buildSourceMap); } //Print out what was built into which layers. @@ -24705,7 +28906,8 @@ define('build', function (require) { uglify2: true, closure: true, map: true, - throwWhen: true + throwWhen: true, + rawText: true }; build.hasDotPropMatch = function (prop) { @@ -24735,7 +28937,10 @@ define('build', function (require) { "excludeShallow": true, "insertRequire": true, "stubModules": true, - "deps": true + "deps": true, + "mainConfigFile": true, + "wrap.startFile": true, + "wrap.endFile": true }; for (i = 0; i < ary.length; i++) { @@ -24829,8 +29034,10 @@ define('build', function (require) { } } - build.makeAbsObject(["out", "cssIn"], config, absFilePath); + build.makeAbsObject((config.out === "stdout" ? ["cssIn"] : ["out", "cssIn"]), + config, absFilePath); build.makeAbsObject(["startFile", "endFile"], config.wrap, absFilePath); + build.makeAbsObject(["externExportsPath"], config.closure, absFilePath); }; /** @@ -24839,13 +29046,19 @@ define('build', function (require) { * make sure paths end in a trailing '/'. */ build.makeRelativeFilePath = function (refPath, targetPath) { - var i, dotLength, finalParts, length, + var i, dotLength, finalParts, length, targetParts, targetName, refParts = refPath.split('/'), - targetParts = targetPath.split('/'), - //Pull off file name - targetName = targetParts.pop(), + hasEndSlash = endsWithSlashRegExp.test(targetPath), dotParts = []; + targetPath = file.normalize(targetPath); + if (hasEndSlash && !endsWithSlashRegExp.test(targetPath)) { + targetPath += '/'; + } + targetParts = targetPath.split('/'); + //Pull off file name + targetName = targetParts.pop(); + //Also pop off the ref file name to make the matches against //targetParts equivalent. refParts.pop(); @@ -24883,18 +29096,43 @@ define('build', function (require) { * Mixes additional source config into target config, and merges some * nested config, like paths, correctly. */ - function mixConfig(target, source) { - var prop, value; + function mixConfig(target, source, skipArrays) { + var prop, value, isArray, targetValue; for (prop in source) { if (hasProp(source, prop)) { //If the value of the property is a plain object, then //allow a one-level-deep mixing of it. value = source[prop]; + isArray = lang.isArray(value); if (typeof value === 'object' && value && - !lang.isArray(value) && !lang.isFunction(value) && + !isArray && !lang.isFunction(value) && !lang.isRegExp(value)) { - target[prop] = lang.mixin({}, target[prop], value, true); + + // TODO: need to generalize this work, maybe also reuse + // the work done in requirejs configure, perhaps move to + // just a deep copy/merge overall. However, given the + // amount of observable change, wait for a dot release. + // This change is in relation to #645 + if (prop === 'map') { + if (!target.map) { + target.map = {}; + } + lang.deepMix(target.map, source.map); + } else { + target[prop] = lang.mixin({}, target[prop], value, true); + } + } else if (isArray) { + if (!skipArrays) { + // Some config, like packages, are arrays. For those, + // just merge the results. + targetValue = target[prop]; + if (lang.isArray(targetValue)) { + target[prop] = targetValue.concat(value); + } else { + target[prop] = value; + } + } } else { target[prop] = value; } @@ -24912,22 +29150,37 @@ define('build', function (require) { * Converts a wrap.startFile or endFile to be start/end as a string. * the startFile/endFile values can be arrays. */ - function flattenWrapFile(wrap, keyName, absFilePath) { - var keyFileName = keyName + 'File'; + function flattenWrapFile(config, keyName, absFilePath) { + var wrap = config.wrap, + keyFileName = keyName + 'File', + keyMapName = '__' + keyName + 'Map'; if (typeof wrap[keyName] !== 'string' && wrap[keyFileName]) { wrap[keyName] = ''; if (typeof wrap[keyFileName] === 'string') { wrap[keyFileName] = [wrap[keyFileName]]; } + wrap[keyMapName] = []; wrap[keyFileName].forEach(function (fileName) { - wrap[keyName] += (wrap[keyName] ? '\n' : '') + - file.readFile(build.makeAbsPath(fileName, absFilePath)); + var absPath = build.makeAbsPath(fileName, absFilePath), + fileText = endsWithNewLine(file.readFile(absPath)); + wrap[keyMapName].push(function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, fileText, absPath, cfg, null, sourceMapGenerator); + }); + wrap[keyName] += fileText; }); } else if (wrap[keyName] === null || wrap[keyName] === undefined) { //Allow missing one, just set to empty string. wrap[keyName] = ''; - } else if (typeof wrap[keyName] !== 'string') { + } else if (typeof wrap[keyName] === 'string') { + wrap[keyName] = endsWithNewLine(wrap[keyName]); + wrap[keyMapName] = [ + function (fileContents, cfg, sourceMapGenerator) { + var absPath = build.makeAbsPath("config-wrap-" + keyName + "-default.js", absFilePath); + return appendToFileContents(fileContents, wrap[keyName], absPath, cfg, null, sourceMapGenerator); + } + ]; + } else { throw new Error('wrap.' + keyName + ' or wrap.' + keyFileName + ' malformed'); } } @@ -24939,12 +29192,27 @@ define('build', function (require) { if (config.wrap === true) { //Use default values. config.wrap = { - start: '(function () {', - end: '}());' + start: '(function () {\n', + end: '}());', + __startMap: [ + function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, "(function () {\n", + build.makeAbsPath("config-wrap-start-default.js", + absFilePath), cfg, null, + sourceMapGenerator); + } + ], + __endMap: [ + function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, "}());", + build.makeAbsPath("config-wrap-end-default.js", absFilePath), + cfg, null, sourceMapGenerator); + } + ] }; } else { - flattenWrapFile(config.wrap, 'start', absFilePath); - flattenWrapFile(config.wrap, 'end', absFilePath); + flattenWrapFile(config, 'start', absFilePath); + flattenWrapFile(config, 'end', absFilePath); } } } catch (wrapError) { @@ -24965,8 +29233,10 @@ define('build', function (require) { */ build.createConfig = function (cfg) { /*jslint evil: true */ - var config = {}, buildFileContents, buildFileConfig, mainConfig, - mainConfigFile, mainConfigPath, buildFile, absFilePath; + var buildFileContents, buildFileConfig, mainConfig, + mainConfigFile, mainConfigPath, buildFile, absFilePath, + config = {}, + buildBaseConfig = makeBuildBaseConfig(); //Make sure all paths are relative to current directory. absFilePath = file.absPath('.'); @@ -24997,6 +29267,14 @@ define('build', function (require) { //Load build file options. buildFileContents = file.readFile(buildFile); try { + //Be a bit lenient in the file ending in a ; or ending with + //a //# sourceMappingUrl comment, mostly for compiled languages + //that create a config, like typescript. + buildFileContents = buildFileContents + .replace(/\/\/\#[^\n\r]+[\n\r]*$/, '') + .trim() + .replace(/;$/, ''); + buildFileConfig = eval("(" + buildFileContents + ")"); build.makeAbsConfig(buildFileConfig, absFilePath); @@ -25013,50 +29291,58 @@ define('build', function (require) { mainConfigFile = config.mainConfigFile || (buildFileConfig && buildFileConfig.mainConfigFile); if (mainConfigFile) { - mainConfigFile = build.makeAbsPath(mainConfigFile, absFilePath); - if (!file.exists(mainConfigFile)) { - throw new Error(mainConfigFile + ' does not exist.'); - } - try { - mainConfig = parse.findConfig(file.readFile(mainConfigFile)).config; - } catch (configError) { - throw new Error('The config in mainConfigFile ' + - mainConfigFile + - ' cannot be used because it cannot be evaluated' + - ' correctly while running in the optimizer. Try only' + - ' using a config that is also valid JSON, or do not use' + - ' mainConfigFile and instead copy the config values needed' + - ' into a build file or command line arguments given to the optimizer.\n' + - 'Source error from parsing: ' + mainConfigFile + ': ' + configError); + if (typeof mainConfigFile === 'string') { + mainConfigFile = [mainConfigFile]; } - if (mainConfig) { - mainConfigPath = mainConfigFile.substring(0, mainConfigFile.lastIndexOf('/')); - //Add in some existing config, like appDir, since they can be - //used inside the mainConfigFile -- paths and baseUrl are - //relative to them. - if (config.appDir && !mainConfig.appDir) { - mainConfig.appDir = config.appDir; + mainConfigFile.forEach(function (configFile) { + configFile = build.makeAbsPath(configFile, absFilePath); + if (!file.exists(configFile)) { + throw new Error(configFile + ' does not exist.'); } + try { + mainConfig = parse.findConfig(file.readFile(configFile)).config; + } catch (configError) { + throw new Error('The config in mainConfigFile ' + + configFile + + ' cannot be used because it cannot be evaluated' + + ' correctly while running in the optimizer. Try only' + + ' using a config that is also valid JSON, or do not use' + + ' mainConfigFile and instead copy the config values needed' + + ' into a build file or command line arguments given to the optimizer.\n' + + 'Source error from parsing: ' + configFile + ': ' + configError); + } + if (mainConfig) { + mainConfigPath = configFile.substring(0, configFile.lastIndexOf('/')); + + //Add in some existing config, like appDir, since they can be + //used inside the configFile -- paths and baseUrl are + //relative to them. + if (config.appDir && !mainConfig.appDir) { + mainConfig.appDir = config.appDir; + } - //If no baseUrl, then use the directory holding the main config. - if (!mainConfig.baseUrl) { - mainConfig.baseUrl = mainConfigPath; - } + //If no baseUrl, then use the directory holding the main config. + if (!mainConfig.baseUrl) { + mainConfig.baseUrl = mainConfigPath; + } - build.makeAbsConfig(mainConfig, mainConfigPath); - mixConfig(config, mainConfig); - } + build.makeAbsConfig(mainConfig, mainConfigPath); + mixConfig(config, mainConfig); + } + }); } //Mix in build file config, but only after mainConfig has been mixed in. + //Since this is a re-application, skip array merging. if (buildFileConfig) { - mixConfig(config, buildFileConfig); + mixConfig(config, buildFileConfig, true); } //Re-apply the override config values. Command line //args should take precedence over build file values. - mixConfig(config, cfg); + //Since this is a re-application, skip array merging. + mixConfig(config, cfg, true); //Fix paths to full paths so that they can be adjusted consistently //lately to be in the output area. @@ -25072,6 +29358,9 @@ define('build', function (require) { //Set final output dir if (hasProp(config, "baseUrl")) { if (config.appDir) { + if (!config.originalBaseUrl) { + throw new Error('Please set a baseUrl in the build config'); + } config.dirBaseUrl = build.makeAbsPath(config.originalBaseUrl, config.dir); } else { config.dirBaseUrl = config.dir || config.baseUrl; @@ -25081,6 +29370,29 @@ define('build', function (require) { config.dirBaseUrl = endsWithSlash(config.dirBaseUrl); } + if (config.bundlesConfigOutFile) { + if (!config.dir) { + throw new Error('bundlesConfigOutFile can only be used with optimizations ' + + 'that use "dir".'); + } + config.bundlesConfigOutFile = build.makeAbsPath(config.bundlesConfigOutFile, config.dir); + } + + //If out=stdout, write output to STDOUT instead of a file. + if (config.out && config.out === 'stdout') { + config.out = function (content) { + var e = env.get(); + if (e === 'rhino') { + var out = new java.io.PrintStream(java.lang.System.out, true, 'UTF-8'); + out.println(content); + } else if (e === 'node') { + process.stdout.write(content, 'utf8'); + } else { + console.log(content); + } + }; + } + //Check for errors in config if (config.main) { throw new Error('"main" passed as an option, but the ' + @@ -25118,24 +29430,30 @@ define('build', function (require) { } if (config.out && config.dir) { throw new Error('The "out" and "dir" options are incompatible.' + - ' Use "out" if you are targeting a single file for' + + ' Use "out" if you are targeting a single file' + ' for optimization, and "dir" if you want the appDir' + ' or baseUrl directories optimized.'); } + if (config.dir) { // Make sure the output dir is not set to a parent of the // source dir or the same dir, as it will result in source // code deletion. - if (config.dir === config.baseUrl || + if (!config.allowSourceOverwrites && (config.dir === config.baseUrl || config.dir === config.appDir || (config.baseUrl && build.makeRelativeFilePath(config.dir, config.baseUrl).indexOf('..') !== 0) || (config.appDir && - build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0)) { + build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0))) { throw new Error('"dir" is set to a parent or same directory as' + ' "appDir" or "baseUrl". This can result in' + - ' the deletion of source code. Stopping.'); + ' the deletion of source code. Stopping. If' + + ' you want to allow possible overwriting of' + + ' source code, set "allowSourceOverwrites"' + + ' to true in the build config, but do so at' + + ' your own risk. In that case, you may want' + + ' to also set "keepBuildDir" to true.'); } } @@ -25144,17 +29462,27 @@ define('build', function (require) { ' to insert in to a require([]) call.'); } + //Support older configs with uglify2 settings, but now that uglify1 has + //been removed, just translate it to 'uglify' settings. + if (config.optimize === 'uglify2') { + config.optimize = 'uglify'; + } + if (config.uglify2) { + config.uglify = config.uglify2; + delete config.uglify2; + } + if (config.generateSourceMaps) { - if (config.preserveLicenseComments && config.optimize !== 'none') { + if (config.preserveLicenseComments && !(config.optimize === 'none' || config.optimize === 'uglify')) { throw new Error('Cannot use preserveLicenseComments and ' + - 'generateSourceMaps together. Either explcitly set ' + - 'preserveLicenseComments to false (default is true) or ' + - 'turn off generateSourceMaps. If you want source maps with ' + - 'license comments, see: ' + + 'generateSourceMaps together, unless optimize is set ' + + 'to \'uglify\'. Either explicitly set preserveLicenseComments ' + + 'to false (default is true) or turn off generateSourceMaps. ' + + 'If you want source maps with license comments, see: ' + 'http://requirejs.org/docs/errors.html#sourcemapcomments'); } else if (config.optimize !== 'none' && config.optimize !== 'closure' && - config.optimize !== 'uglify2') { + config.optimize !== 'uglify') { //Allow optimize: none to pass, since it is useful when toggling //minification on and off to debug something, and it implicitly //works, since it does not need a source map. @@ -25213,10 +29541,15 @@ define('build', function (require) { config.cssPrefix = ''; } - //Cycle through modules and combine any local stubModules with - //global values. + //Cycle through modules and normalize if (config.modules && config.modules.length) { config.modules.forEach(function (mod) { + if (lang.isArray(mod) || typeof mod === 'string' || !mod) { + throw new Error('modules config item is malformed: it should' + + ' be an object with a \'name\' property.'); + } + + //Combine any local stubModules with global values. if (config.stubModules) { mod.stubModules = config.stubModules.concat(mod.stubModules || []); } @@ -25230,6 +29563,12 @@ define('build', function (require) { }); } + // Legacy command support, which allowed a single string ID + // for include. + if (typeof mod.include === 'string') { + mod.include = [mod.include]; + } + //Allow wrap config in overrides, but normalize it. if (mod.override) { normalizeWrapConfig(mod.override, absFilePath); @@ -25270,7 +29609,19 @@ define('build', function (require) { file.exclusionRegExp = config.dirExclusionRegExp; } + //Track the deps, but in a different key, so that they are not loaded + //as part of config seeding before all config is in play (#648). Was + //going to merge this in with "include", but include is added after + //the "name" target. To preserve what r.js has done previously, make + //sure "deps" comes before the "name". + if (config.deps) { + config._depsInclude = config.deps; + } + + //Remove things that may cause problems in the build. + //deps already merged above + delete config.deps; delete config.jQuery; delete config.enforceDefine; delete config.urlArgs; @@ -25342,12 +29693,13 @@ define('build', function (require) { //Put back basic config, use a fresh object for it. if (baseLoaderConfig) { - require(lang.deeplikeCopy(baseLoaderConfig)); + require(copyConfig(baseLoaderConfig)); } logger.trace("\nTracing dependencies for: " + (module.name || (typeof module.out === 'function' ? 'FUNCTION' : module.out))); - include = module.name && !module.create ? [module.name] : []; + include = config._depsInclude || []; + include = include.concat(module.name && !module.create ? [module.name] : []); if (module.include) { include = include.concat(module.include); } @@ -25357,7 +29709,7 @@ define('build', function (require) { if (baseLoaderConfig) { override = build.createOverrideConfig(baseLoaderConfig, module.override); } else { - override = lang.deeplikeCopy(module.override); + override = copyConfig(module.override); } require(override); } @@ -25386,7 +29738,7 @@ define('build', function (require) { var hasError = false; if (syncChecks[env.get()]) { try { - build.checkForErrors(context); + build.checkForErrors(context, layer); } catch (e) { hasError = true; deferred.reject(e); @@ -25406,25 +29758,25 @@ define('build', function (require) { // issue, the require never completes, need to check for errors // here. if (syncChecks[env.get()]) { - build.checkForErrors(context); + build.checkForErrors(context, layer); } return deferred.promise.then(function () { //Reset config if (module.override && baseLoaderConfig) { - require(lang.deeplikeCopy(baseLoaderConfig)); + require(copyConfig(baseLoaderConfig)); } - build.checkForErrors(context); + build.checkForErrors(context, layer); return layer; }); }; - build.checkForErrors = function (context) { + build.checkForErrors = function (context, layer) { //Check to see if it all loaded. If not, then throw, and give //a message on what is left. - var id, prop, mod, idParts, pluginId, + var id, prop, mod, idParts, pluginId, pluginResources, errMessage = '', failedPluginMap = {}, failedPluginIds = [], @@ -25437,6 +29789,11 @@ define('build', function (require) { registry = context.registry; function populateErrUrlMap(id, errUrl, skipNew) { + // Loader plugins do not have an errUrl, so skip them. + if (!errUrl) { + return; + } + if (!skipNew) { errIds.push(id); } @@ -25460,17 +29817,25 @@ define('build', function (require) { if (hasProp(registry, id) && id.indexOf('_@r') !== 0) { hasUndefined = true; mod = getOwn(registry, id); + idParts = id.split('!'); + pluginId = idParts[0]; if (id.indexOf('_unnormalized') === -1 && mod && mod.enabled) { populateErrUrlMap(id, mod.map.url); } //Look for plugins that did not call load() - idParts = id.split('!'); - pluginId = idParts[0]; - if (idParts.length > 1 && falseProp(failedPluginMap, pluginId)) { - failedPluginIds.push(pluginId); - failedPluginMap[pluginId] = true; + //But skip plugin IDs that were already inlined and called + //define() with a name. + if (!hasProp(layer.modulesWithNames, id) && idParts.length > 1) { + if (falseProp(failedPluginMap, pluginId)) { + failedPluginIds.push(pluginId); + } + pluginResources = failedPluginMap[pluginId]; + if (!pluginResources) { + pluginResources = failedPluginMap[pluginId] = []; + } + pluginResources.push(id + (mod.error ? ': ' + mod.error : '')); } } } @@ -25490,8 +29855,11 @@ define('build', function (require) { errMessage += 'Loader plugin' + (failedPluginIds.length === 1 ? '' : 's') + ' did not call ' + - 'the load callback in the build: ' + - failedPluginIds.join(', ') + '\n'; + 'the load callback in the build:\n' + + failedPluginIds.map(function (pluginId) { + var pluginResources = failedPluginMap[pluginId]; + return pluginId + ':\n ' + pluginResources.join('\n '); + }).join('\n') + '\n'; } errMessage += 'Module loading did not complete for: ' + errIds.join(', '); @@ -25511,8 +29879,8 @@ define('build', function (require) { }; build.createOverrideConfig = function (config, override) { - var cfg = lang.deeplikeCopy(config), - oride = lang.deeplikeCopy(override); + var cfg = copyConfig(config), + oride = copyConfig(override); lang.eachProp(oride, function (value, prop) { if (hasProp(build.objProps, prop)) { @@ -25549,13 +29917,14 @@ define('build', function (require) { buildFileContents = ''; return prim().start(function () { - var reqIndex, currContents, - moduleName, shim, packageConfig, nonPackageName, + var reqIndex, currContents, fileForSourceMap, + moduleName, shim, packageName, parts, builder, writeApi, namespace, namespaceWithDot, stubModulesByName, context = layer.context, onLayerEnds = [], - onLayerEndAdded = {}; + onLayerEndAdded = {}, + pkgsMainMap = {}; //Use override settings, particularly for pragmas //Do this before the var readings since it reads config values. @@ -25589,27 +29958,42 @@ define('build', function (require) { if (config.generateSourceMaps) { sourceMapBase = config.dir || config.baseUrl; - sourceMapGenerator = new SourceMapGenerator.SourceMapGenerator({ - file: module._buildPath.replace(sourceMapBase, '') + if (module._buildPath === 'FUNCTION') { + fileForSourceMap = (module.name || module.include[0] || 'FUNCTION') + '.build.js'; + } else if (config.out) { + fileForSourceMap = module._buildPath.split('/').pop(); + } else { + fileForSourceMap = module._buildPath.replace(sourceMapBase, ''); + } + sourceMapGenerator = new SourceMapGenerator({ + file: fileForSourceMap }); } + //Create a reverse lookup for packages main module IDs to their package + //names, useful for knowing when to write out define() package main ID + //adapters. + lang.eachProp(layer.context.config.pkgs, function(value, prop) { + pkgsMainMap[value] = prop; + }); + //Write the built module to disk, and build up the build output. fileContents = ""; + if (config.wrap && config.wrap.__startMap) { + config.wrap.__startMap.forEach(function (wrapFunction) { + fileContents = wrapFunction(fileContents, config, sourceMapGenerator); + }); + } + return prim.serial(layer.buildFilePaths.map(function (path) { return function () { - var lineCount, - singleContents = ''; + var singleContents = ''; moduleName = layer.buildFileToModule[path]; - //If the moduleName is for a package main, then update it to the - //real main value. - packageConfig = layer.context.config.pkgs && - getOwn(layer.context.config.pkgs, moduleName); - if (packageConfig) { - nonPackageName = moduleName; - moduleName += '/' + packageConfig.main; - } + + //If the moduleName is a package main, then hold on to the + //packageName in case an adapter needs to be written. + packageName = getOwn(pkgsMainMap, moduleName); return prim().start(function () { //Figure out if the module is a result of a build plugin, and if so, @@ -25638,7 +30022,11 @@ define('build', function (require) { singleContents = config.onBuildWrite(moduleName, path, singleContents); } }; - builder.write(parts.prefix, parts.name, writeApi); + + builder.write(parts.prefix, parts.name, writeApi, { + name: module.onCompleteData.name, + path: module.onCompleteData.path + }); } return; } else { @@ -25671,8 +30059,8 @@ define('build', function (require) { currContents = config.onBuildRead(moduleName, path, currContents); } - if (packageConfig) { - hasPackageName = (nonPackageName === parse.getNamedDefine(currContents)); + if (packageName) { + hasPackageName = (packageName === parse.getNamedDefine(currContents)); } if (namespace) { @@ -25683,10 +30071,10 @@ define('build', function (require) { useSourceUrl: config.useSourceUrl }); - if (packageConfig && !hasPackageName) { + if (packageName && !hasPackageName) { currContents = addSemiColon(currContents, config) + '\n'; currContents += namespaceWithDot + "define('" + - packageConfig.name + "', ['" + moduleName + + packageName + "', ['" + moduleName + "'], function (main) { return main; });\n"; } @@ -25696,13 +30084,11 @@ define('build', function (require) { //Semicolon is for files that are not well formed when //concatenated with other content. - singleContents += "\n" + addSemiColon(currContents, config); + singleContents += addSemiColon(currContents, config); }); } }).then(function () { - var refPath, pluginId, resourcePath, - sourceMapPath, sourceMapLineNumber, - shortPath = path.replace(config.dir, ""); + var shimDeps, shortPath = path.replace(config.dir, ""); module.onCompleteData.included.push(shortPath); buildFileContents += shortPath + "\n"; @@ -25712,70 +30098,51 @@ define('build', function (require) { //after the module is processed. //If we have a name, but no defined module, then add in the placeholder. if (moduleName && falseProp(layer.modulesWithNames, moduleName) && !config.skipModuleInsertion) { - shim = config.shim && (getOwn(config.shim, moduleName) || (packageConfig && getOwn(config.shim, nonPackageName))); + shim = config.shim && (getOwn(config.shim, moduleName) || (packageName && getOwn(config.shim, packageName))); if (shim) { - singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' + - (shim.deps && shim.deps.length ? - build.makeJsArrayString(shim.deps) + ', ' : '') + - (shim.exportsFn ? shim.exportsFn() : 'function(){}') + - ');\n'; + shimDeps = lang.isArray(shim) ? shim : shim.deps; + if (config.wrapShim) { + + singleContents = '(function(root) {\n' + + namespaceWithDot + 'define("' + moduleName + '", ' + + (shimDeps && shimDeps.length ? + build.makeJsArrayString(shimDeps) + ', ' : '[], ') + + 'function() {\n' + + ' return (function() {\n' + + singleContents + + // Start with a \n in case last line is a comment + // in the singleContents, like a sourceURL comment. + '\n' + (shim.exportsFn ? shim.exportsFn() : '') + + '\n' + + ' }).apply(root, arguments);\n' + + '});\n' + + '}(this));\n'; + } else { + singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' + + (shimDeps && shimDeps.length ? + build.makeJsArrayString(shimDeps) + ', ' : '') + + (shim.exportsFn ? shim.exportsFn() : 'function(){}') + + ');\n'; + } } else { singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", function(){});\n'; } } - //Add to the source map - if (sourceMapGenerator) { - refPath = config.out ? config.baseUrl : module._buildPath; - parts = path.split('!'); - if (parts.length === 1) { - //Not a plugin resource, fix the path - sourceMapPath = build.makeRelativeFilePath(refPath, path); - } else { - //Plugin resource. If it looks like just a plugin - //followed by a module ID, pull off the plugin - //and put it at the end of the name, otherwise - //just leave it alone. - pluginId = parts.shift(); - resourcePath = parts.join('!'); - if (resourceIsModuleIdRegExp.test(resourcePath)) { - sourceMapPath = build.makeRelativeFilePath(refPath, require.toUrl(resourcePath)) + - '!' + pluginId; - } else { - sourceMapPath = path; - } - } - - sourceMapLineNumber = fileContents.split('\n').length - 1; - lineCount = singleContents.split('\n').length; - for (var i = 1; i <= lineCount; i += 1) { - sourceMapGenerator.addMapping({ - generated: { - line: sourceMapLineNumber + i, - column: 0 - }, - original: { - line: i, - column: 0 - }, - source: sourceMapPath - }); - } - - //Store the content of the original in the source - //map since other transforms later like minification - //can mess up translating back to the original - //source. - sourceMapGenerator.setSourceContent(sourceMapPath, singleContents); - } + //Add line break at end of file, instead of at beginning, + //so source map line numbers stay correct, but still allow + //for some space separation between files in case ASI issues + //for concatenation would cause an error otherwise. + singleContents += '\n'; - //Add the file to the final contents - fileContents += singleContents; + //Add to the source map and to the final contents + fileContents = appendToFileContents(fileContents, singleContents, path, config, module, + sourceMapGenerator); }); }; })).then(function () { if (onLayerEnds.length) { - onLayerEnds.forEach(function (builder) { + onLayerEnds.forEach(function (builder, index) { var path; if (typeof module.out === 'string') { path = module.out; @@ -25783,7 +30150,9 @@ define('build', function (require) { path = module._buildPath; } builder.onLayerEnd(function (input) { - fileContents += "\n" + addSemiColon(input, config); + fileContents = + appendToFileContents(fileContents, "\n" + addSemiColon(input, config), + 'onLayerEnd' + index + '.js', config, module, sourceMapGenerator); }, { name: module.name, path: path @@ -25796,21 +30165,30 @@ define('build', function (require) { //a module definition for it in case the //built file is used with enforceDefine //(#432) - fileContents += '\n' + namespaceWithDot + 'define("' + module.name + '", function(){});\n'; + fileContents = + appendToFileContents(fileContents, '\n' + namespaceWithDot + 'define("' + module.name + + '", function(){});\n', 'module-create.js', config, module, + sourceMapGenerator); } //Add a require at the end to kick start module execution, if that //was desired. Usually this is only specified when using small shim //loaders like almond. if (module.insertRequire) { - fileContents += '\n' + namespaceWithDot + 'require(["' + module.insertRequire.join('", "') + '"]);\n'; + fileContents = + appendToFileContents(fileContents, '\n' + namespaceWithDot + 'require(["' + module.insertRequire.join('", "') + + '"]);\n', 'module-insertRequire.js', config, module, + sourceMapGenerator); } }); }).then(function () { + if (config.wrap && config.wrap.__endMap) { + config.wrap.__endMap.forEach(function (wrapFunction) { + fileContents = wrapFunction(fileContents, config, sourceMapGenerator); + }); + } return { - text: config.wrap ? - config.wrap.start + fileContents + config.wrap.end : - fileContents, + text: fileContents, buildText: buildFileContents, sourceMap: sourceMapGenerator ? JSON.stringify(sourceMapGenerator.toJSON(), null, ' ') : @@ -25982,13 +30360,7 @@ define('build', function (require) { //Do the optimizer work. loadLib(); - /** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/* + /* * Create a build.js file that has the build options you want and pass that * build file to this file to do the build. See example.build.js for more information. */ @@ -26016,7 +30388,7 @@ function (args, quit, logger, build) { } else if (commandOption === 'v') { console.log('r.js: ' + version + ', RequireJS: ' + this.requirejsVars.require.version + - ', UglifyJS2: 2.4.0, UglifyJS: 1.3.4'); + ', UglifyJS: 2.8.29'); } else if (commandOption === 'convert') { loadLib(); diff --git a/tools/server.js b/tools/server.js index 1c9e353..676ea7a 100644 --- a/tools/server.js +++ b/tools/server.js @@ -12,26 +12,36 @@ var http = require('http'), types = { 'html': 'text/html', 'js': 'application/javascript' - }; + }, + site = 'http://localhost:' + port; http.createServer(function (request, response) { var uri = url.parse(request.url).pathname, filename = path.join(__dirname, '..', uri); fs.exists(filename, function (exists) { - if (!exists) { - response.writeHead(404, {'Content-Type': 'text/plain'}); - response.write('404 Not Found\n'); - response.end(); - return; - } + if (!exists) { + response.writeHead(404, {'Content-Type': 'text/plain'}); + response.write('404 Not Found\n'); + response.end(); + return; + } - var type = filename.split('.'); - type = type[type.length - 1]; + if(!fs.lstatSync(filename).isDirectory()) { + var type = filename.split('.'); + type = type[type.length - 1]; - response.writeHead(200, { 'Content-Type': types[type] + '; charset=utf-8' }); - fs.createReadStream(filename).pipe(response); - }); + response.writeHead(200, { 'Content-Type': types[type] + '; charset=utf-8' }); + fs.createReadStream(filename).pipe(response); + } else { + /** + * if users visit the site such as http://localhost:8888 + * then lead them to http://localhost:8888/www/app.html + */ + response.writeHead(301, {'Location': site + '/www/app.html' }); + response.end(); + } + }); }).listen(parseInt(port, 10)); -console.log('Static file server running at\n => http://localhost:' + port + '/\nCTRL + C to shutdown'); +console.log('Static file server running at\n => ' + site + '/\nCTRL + C to shutdown'); diff --git a/www/js/lib/require.js b/www/js/lib/require.js index 2ce09b5..78490f9 100644 --- a/www/js/lib/require.js +++ b/www/js/lib/require.js @@ -1,7 +1,6 @@ /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details + * @license RequireJS 2.3.6 Copyright jQuery Foundation and other contributors. + * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE */ //Not using strict: uneven strict support in browsers, #392, and causes //problems with requirejs.exec()/transpiler plugins that may not be strict. @@ -9,19 +8,17 @@ /*global window, navigator, document, importScripts, setTimeout, opera */ var requirejs, require, define; -(function (global) { +(function (global, setTimeout) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.9', - commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, + version = '2.3.6', + commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, currDirRegExp = /^\.\//, op = Object.prototype, ostring = op.toString, hasOwn = op.hasOwnProperty, - ap = Array.prototype, - apsp = ap.splice, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', //PS3 indicates loaded and complete, but need to wait for complete @@ -38,6 +35,11 @@ var requirejs, require, define; globalDefQueue = [], useInteractive = false; + //Could match something like ')//comment', do not lose the prefix to comment. + function commentReplace(match, singlePrefix) { + return singlePrefix || ''; + } + function isFunction(it) { return ostring.call(it) === '[object Function]'; } @@ -108,7 +110,10 @@ var requirejs, require, define; if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { - if (deepStringMixin && typeof value !== 'string') { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + if (!target[prop]) { target[prop] = {}; } @@ -138,7 +143,7 @@ var requirejs, require, define; throw err; } - //Allow getting a global that expressed in + //Allow getting a global that is expressed in //dot notation, like 'a.b.c'. function getGlobal(value) { if (!value) { @@ -160,7 +165,7 @@ var requirejs, require, define; * @returns {Error} */ function makeError(id, msg, err, requireModules) { - var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + var e = new Error(msg + '\nhttps://requirejs.org/docs/errors.html#' + id); e.requireType = id; e.requireModules = requireModules; if (err) { @@ -177,7 +182,7 @@ var requirejs, require, define; if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { - //Do not overwrite and existing requirejs instance. + //Do not overwrite an existing requirejs instance. return; } cfg = requirejs; @@ -201,6 +206,7 @@ var requirejs, require, define; waitSeconds: 7, baseUrl: './', paths: {}, + bundles: {}, pkgs: {}, shim: {}, config: {} @@ -214,6 +220,7 @@ var requirejs, require, define; defQueue = [], defined = {}, urlFetched = {}, + bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; @@ -228,20 +235,19 @@ var requirejs, require, define; */ function trimDots(ary) { var i, part; - for (i = 0; ary[i]; i += 1) { + for (i = 0; i < ary.length; i++) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { - if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; @@ -261,54 +267,45 @@ var requirejs, require, define; * @returns {String} normalized name */ function normalize(name, baseName, applyMap) { - var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, - foundMap, foundI, foundStarMap, starI, - baseParts = baseName && baseName.split('/'), - normalizedBaseParts = baseParts, + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), map = config.map, starMap = map && map['*']; //Adjust any relative paths. - if (name && name.charAt(0) === '.') { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - if (getOwn(config.pkgs, baseName)) { - //If the baseName is a package name, then just treat it as one - //name to concat the name with. - normalizedBaseParts = baseParts = [baseName]; - } else { - //Convert baseName to array, and lop off the last part, - //so that . matches that 'directory' and not name of the baseName's - //module. For instance, baseName of 'one/two/three', maps to - //'one/two/three.js', but we want the directory, 'one/two' for - //this normalization. - normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); - } - - name = normalizedBaseParts.concat(name.split('/')); - trimDots(name); + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; + + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } - //Some use of packages may use a . path to reference the - //'main' module name, so normalize for that. - pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); - name = name.join('/'); - if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { - name = pkgName; - } - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); } + + trimDots(name); + name = name.join('/'); } //Apply map config if available. if (applyMap && map && (baseParts || starMap)) { nameParts = name.split('/'); - for (i = nameParts.length; i > 0; i -= 1) { + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join('/'); if (baseParts) { @@ -325,16 +322,12 @@ var requirejs, require, define; //Match, update name to the new value. foundMap = mapValue; foundI = i; - break; + break outerLoop; } } } } - if (foundMap) { - break; - } - //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. @@ -355,7 +348,11 @@ var requirejs, require, define; } } - return name; + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; } function removeScript(name) { @@ -377,7 +374,13 @@ var requirejs, require, define; //retry pathConfig.shift(); context.require.undef(id); - context.require([id]); + + //Custom require that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + return true; } } @@ -437,13 +440,24 @@ var requirejs, require, define; //Account for relative paths if there is a base name. if (name) { if (prefix) { - if (pluginModule && pluginModule.normalize) { + if (isNormalized) { + normalizedName = name; + } else if (pluginModule && pluginModule.normalize) { //Plugin is loaded, use its normalize method. normalizedName = pluginModule.normalize(name, function (name) { return normalize(name, parentName, applyMap); }); } else { - normalizedName = normalize(name, parentName, applyMap); + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; } } else { //A regular module. @@ -544,11 +558,13 @@ var requirejs, require, define; function takeGlobalQueue() { //Push all the globalDefQueue items into the context's defQueue if (globalDefQueue.length) { - //Array splice in the values since the context code has a - //local var ref to defQueue, so cannot just reassign the one - //on context. - apsp.apply(defQueue, - [defQueue.length - 1, 0].concat(globalDefQueue)); + each(globalDefQueue, function(queueItem) { + var id = queueItem[0]; + if (typeof id === 'string') { + context.defQueueMap[id] = true; + } + defQueue.push(queueItem); + }); globalDefQueue = []; } } @@ -565,7 +581,7 @@ var requirejs, require, define; mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { - return mod.exports; + return (defined[mod.map.id] = mod.exports); } else { return (mod.exports = defined[mod.map.id] = {}); } @@ -579,15 +595,9 @@ var requirejs, require, define; id: mod.map.id, uri: mod.map.url, config: function () { - var c, - pkg = getOwn(config.pkgs, mod.map.id); - // For packages, only support config targeted - // at the main module. - c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) : - getOwn(config.config, mod.map.id); - return c || {}; + return getOwn(config.config, mod.map.id) || {}; }, - exports: defined[mod.map.id] + exports: mod.exports || (mod.exports = {}) }); } } @@ -628,7 +638,7 @@ var requirejs, require, define; } function checkLoaded() { - var map, modId, err, usingPathFallback, + var err, usingPathFallback, waitInterval = config.waitSeconds * 1000, //It is possible to disable the wait interval by using waitSeconds of 0. expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), @@ -646,8 +656,8 @@ var requirejs, require, define; //Figure out the state of all the modules. eachProp(enabledRegistry, function (mod) { - map = mod.map; - modId = map.id; + var map = mod.map, + modId = map.id; //Skip things that are not enabled or in error state. if (!mod.enabled) { @@ -841,7 +851,10 @@ var requirejs, require, define; factory = this.factory; if (!this.inited) { - this.fetch(); + // Only fetch if not already in the defQueue. + if (!hasProp(context.defQueueMap, id)) { + this.fetch(); + } } else if (this.error) { this.emit('error', this.error); } else if (!this.defining) { @@ -870,17 +883,14 @@ var requirejs, require, define; exports = context.execCb(id, factory, depExports, exports); } - if (this.map.isDefine) { - //If setting exports via 'module' is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { cjsModule = this.module; - if (cjsModule && - cjsModule.exports !== undefined && - //Make sure it is not already the exports value - cjsModule.exports !== this.exports) { + if (cjsModule) { exports = cjsModule.exports; - } else if (exports === undefined && this.usingExports) { + } else if (this.usingExports) { //exports already set the defined value. exports = this.exports; } @@ -904,7 +914,11 @@ var requirejs, require, define; defined[id] = exports; if (req.onResourceLoad) { - req.onResourceLoad(context, this.map, this.depMaps); + var resLoadMaps = []; + each(this.depMaps, function (depMap) { + resLoadMaps.push(depMap.normalizedMap || depMap); + }); + req.onResourceLoad(context, this.map, resLoadMaps); } } @@ -940,6 +954,7 @@ var requirejs, require, define; on(pluginMap, 'defined', bind(this, function (plugin) { var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, localRequire = context.makeRequire(map.parentMap, { @@ -959,9 +974,11 @@ var requirejs, require, define; //prefix and name should already be normalized, no need //for applying map config again either. normalizedMap = makeModuleMap(map.prefix + '!' + name, - this.map.parentMap); + this.map.parentMap, + true); on(normalizedMap, 'defined', bind(this, function (value) { + this.map.normalizedMap = normalizedMap; this.init([], function () { return value; }, null, { enabled: true, ignore: true @@ -985,6 +1002,14 @@ var requirejs, require, define; return; } + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layer. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true @@ -1107,12 +1132,22 @@ var requirejs, require, define; this.depCount += 1; on(depMap, 'defined', bind(this, function (depExports) { + if (this.undefed) { + return; + } this.defineDep(i, depExports); this.check(); })); if (this.errback) { on(depMap, 'error', bind(this, this.errback)); + } else if (this.events.error) { + // No direct errback on this module, but something + // else is listening for errors, so be sure to + // propagate the error correctly. + on(depMap, 'error', bind(this, function(err) { + this.emit('error', err); + })); } } @@ -1216,13 +1251,15 @@ var requirejs, require, define; while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { - return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + + args[args.length - 1])); } else { //args are id, deps, factory. Should be normalized by the //define() function. callGetModule(args); } } + context.defQueueMap = {}; } context = { @@ -1232,6 +1269,7 @@ var requirejs, require, define; defined: defined, urlFetched: urlFetched, defQueue: defQueue, + defQueueMap: {}, Module: Module, makeModuleMap: makeModuleMap, nextTick: req.nextTick, @@ -1249,31 +1287,46 @@ var requirejs, require, define; } } - //Save off the paths and packages since they require special processing, + // Convert old style urlArgs string to a function. + if (typeof cfg.urlArgs === 'string') { + var urlArgs = cfg.urlArgs; + cfg.urlArgs = function(id, url) { + return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs; + }; + } + + //Save off the paths since they require special processing, //they are additive. - var pkgs = config.pkgs, - shim = config.shim, + var shim = config.shim, objs = { paths: true, + bundles: true, config: true, map: true }; eachProp(cfg, function (value, prop) { if (objs[prop]) { - if (prop === 'map') { - if (!config.map) { - config.map = {}; - } - mixin(config[prop], value, true, true); - } else { - mixin(config[prop], value, true); + if (!config[prop]) { + config[prop] = {}; } + mixin(config[prop], value, true, true); } else { config[prop] = value; } }); + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + //Merge shim if (cfg.shim) { eachProp(cfg.shim, function (value, id) { @@ -1294,29 +1347,25 @@ var requirejs, require, define; //Adjust packages if necessary. if (cfg.packages) { each(cfg.packages, function (pkgObj) { - var location; + var location, name; + + pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj; - pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + name = pkgObj.name; location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } - //Create a brand new object on pkgs, since currentPackages can - //be passed in again, and config.pkgs is the internal transformed - //state for all package configs. - pkgs[pkgObj.name] = { - name: pkgObj.name, - location: location || pkgObj.name, - //Remove leading dot in main, so main paths are normalized, - //and remove any trailing .js, since different package - //envs have different conventions: some use a module name, - //some use a file name. - main: (pkgObj.main || 'main') - .replace(currDirRegExp, '') - .replace(jsSuffixRegExp, '') - }; + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); }); - - //Done with modifications, assing packages back to context config - config.pkgs = pkgs; } //If there are any "waiting to execute" modules in the registry, @@ -1327,7 +1376,7 @@ var requirejs, require, define; //late to modify them, and ignore unnormalized ones //since they are transient. if (!mod.inited && !mod.map.unnormalized) { - mod.map = makeModuleMap(id); + mod.map = makeModuleMap(id, null, true); } }); @@ -1463,12 +1512,23 @@ var requirejs, require, define; var map = makeModuleMap(id, relMap, true), mod = getOwn(registry, id); + mod.undefed = true; removeScript(id); delete defined[id]; delete urlFetched[map.url]; delete undefEvents[id]; + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if (args[0] === id) { + defQueue.splice(i, 1); + } + }); + delete context.defQueueMap[id]; + if (mod) { //Hold on to listeners in case the //module will be attempted to be reloaded @@ -1488,7 +1548,7 @@ var requirejs, require, define; /** * Called to enable a module if it is still in the registry * awaiting enablement. A second arg, parent, the parent module, - * is passed in for context, when this method is overriden by + * is passed in for context, when this method is overridden by * the optimizer. Not shown here to keep code compact. */ enable: function (depMap) { @@ -1529,6 +1589,7 @@ var requirejs, require, define; callGetModule(args); } + context.defQueueMap = {}; //Do this after the cycle of callGetModule in case the result //of those calls/init calls changes the registry. @@ -1562,8 +1623,19 @@ var requirejs, require, define; * internal API, not a public one. Use toUrl for the public API. */ nameToUrl: function (moduleName, ext, skipExt) { - var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, - parentPath; + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } //If a colon is in the URL, it indicates a protocol is used and it is just //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) @@ -1577,7 +1649,6 @@ var requirejs, require, define; } else { //A module that needs to be converted to a path. paths = config.paths; - pkgs = config.pkgs; syms = moduleName.split('/'); //For each module name segment, see if there is a path @@ -1585,7 +1656,7 @@ var requirejs, require, define; //and work up from it. for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); - pkg = getOwn(pkgs, parentModule); + parentPath = getOwn(paths, parentModule); if (parentPath) { //If an array, it means there are a few choices, @@ -1595,28 +1666,17 @@ var requirejs, require, define; } syms.splice(0, i, parentPath); break; - } else if (pkg) { - //If module name is just the package name, then looking - //for the main module. - if (moduleName === pkg.name) { - pkgPath = pkg.location + '/' + pkg.main; - } else { - pkgPath = pkg.location; - } - syms.splice(0, i, pkgPath); - break; } } //Join the path parts together, then figure out if baseUrl is needed. url = syms.join('/'); - url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); + url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js')); url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; } - return config.urlArgs ? url + - ((url.indexOf('?') === -1 ? '?' : '&') + - config.urlArgs) : url; + return config.urlArgs && !/^blob\:/.test(url) ? + url + config.urlArgs(moduleName, url) : url; }, //Delegates to req.load. Broken out as a separate function to @@ -1664,7 +1724,21 @@ var requirejs, require, define; onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { - return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); + var parents = []; + eachProp(registry, function(value, key) { + if (key.indexOf('_@r') !== 0) { + each(value.depMaps, function(depMap) { + if (depMap.id === data.id) { + parents.push(key); + return true; + } + }); + } + }); + return onError(makeError('scripterror', 'Script error for "' + data.id + + (parents.length ? + '", needed by: ' + parents.join(', ') : + '"'), evt, [data.id])); } } }; @@ -1838,11 +1912,11 @@ var requirejs, require, define; if (node.attachEvent && //Check if node.attachEvent is artificially added by custom script or //natively supported by browser - //read https://github.com/jrburke/requirejs/issues/187 + //read https://github.com/requirejs/requirejs/issues/187 //if we can NOT find [native code] then it must NOT natively supported. //in IE8, node.attachEvent does not have toString() //Note the test for "[native code" with no closing brace, see: - //https://github.com/jrburke/requirejs/issues/273 + //https://github.com/requirejs/requirejs/issues/273 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) { //Probably IE. IE (at least 6-8) do not fire @@ -1870,6 +1944,12 @@ var requirejs, require, define; } node.src = url; + //Calling onNodeCreated after all properties on the node have been + //set, but before it is placed in the DOM. + if (config.onNodeCreated) { + config.onNodeCreated(node, config, moduleName, url); + } + //For some cache cases in IE 6-8, the script executes before the end //of the appendChild execution, so to tie an anonymous define //call to the module name (which is stored on the node), hold on @@ -1888,9 +1968,14 @@ var requirejs, require, define; //In a web worker, use importScripts. This is not a very //efficient use of importScripts, importScripts will block until //its script is downloaded and evaluated. However, if web workers - //are in play, the expectation that a build has been done so that - //only one script needs to be loaded anyway. This may need to be - //reevaluated if other use cases become common. + //are in play, the expectation is that a build has been done so + //that only one script needs to be loaded anyway. This may need + //to be reevaluated if other use cases become common. + + // Post a task to the event loop to work around a bug in WebKit + // where the worker gets garbage-collected after calling + // importScripts(): https://webkit.org/b/153317 + setTimeout(function() {}, 0); importScripts(url); //Account for anonymous modules @@ -1936,8 +2021,10 @@ var requirejs, require, define; //Preserve dataMain in case it is a path (i.e. contains '?') mainScript = dataMain; - //Set final baseUrl if there is not already an explicit one. - if (!cfg.baseUrl) { + //Set final baseUrl if there is not already an explicit one, + //but only do so if the data-main value is not a loader plugin + //module ID. + if (!cfg.baseUrl && mainScript.indexOf('!') === -1) { //Pull off the directory of data-main for use as the //baseUrl. src = mainScript.split('/'); @@ -1951,7 +2038,7 @@ var requirejs, require, define; //like a module name. mainScript = mainScript.replace(jsSuffixRegExp, ''); - //If mainScript is still a path, fall back to dataMain + //If mainScript is still a path, fall back to dataMain if (req.jsExtRegExp.test(mainScript)) { mainScript = dataMain; } @@ -1998,7 +2085,7 @@ var requirejs, require, define; if (callback.length) { callback .toString() - .replace(commentRegExp, '') + .replace(commentRegExp, commentReplace) .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); @@ -2030,14 +2117,18 @@ var requirejs, require, define; //where the module name is not known until the script onload event //occurs. If no context, use the global queue, and get it processed //in the onscript load callback. - (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + if (context) { + context.defQueue.push([name, deps, callback]); + context.defQueueMap[name] = true; + } else { + globalDefQueue.push([name, deps, callback]); + } }; define.amd = { jQuery: true }; - /** * Executes the text. Normally just uses eval, but can be modified * to use a better, environment-specific call. Only used for transpiling @@ -2051,4 +2142,4 @@ var requirejs, require, define; //Set up with config info. req(cfg); -}(this)); +}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));