diff --git a/.npmignore b/.npmignore index d8e51136b0..97d91a42da 100644 --- a/.npmignore +++ b/.npmignore @@ -10,3 +10,5 @@ dist tests.json CHANGELOG.md .package.json +coverage +vendor diff --git a/.travis.yml b/.travis.yml index 7981fb7440..b5e68b204e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: node_js node_js: - "0.10" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a099a9b89..caaccdebd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,86 @@ _Note: Gaps between patch versions are faulty/broken releases._ * `ignoreRegex` fallback has now been dropped from the require hook. `register(/foo/);`, `register({ ignoreRegex: /foo/ })` -> `register({ ignore: /foo/ })`. * Optional fast transformer backwards compatibility support has been removed. Use [loose mode](https://6to5.org/docs/usage/loose). +## 2.13.3 + + * **Internal** + * Add `coverage` and `vendor` to npmignore. + +## 2.13.2 + + * **Bug Fix** + * Fix `super` inside of nested functions. + * **Internal** + * Move let scoping TDZ into a separate transformer until it's more solid. + * Use `process.stdin.write` instead of `console.log` in `bin` to avoid sprintfification. + +## 2.13.1 + + * **New Feature** + * Temporal dead zone for block binding. + +## 2.13.0 + + * **New Feature** + * Block scoped functions. + * Add `--loose all` option. + * Add playground mallet operator thanks to [@jridgewell](https://github.com/jridgewell). + * **Bug Fix** + * Rewrote large parts of the let scoping transformer. Now passes all known tests. + * **Internal** + * Even more optimisations thanks to the awesome [@gaearon](https://github.com/gaearon). + * Upgrade `acorn-6to5`. + * Fixes exponentation operator thanks to [@charliesome](https://github.com/charliesome). + * Fix flow optional parameters with types. + * Upgrade `chokidar` to `0.12.6`. + * Upgrade `commander` to `2.6.0`. + * Upgrade `core-js` to `0.4.5`. + * Upgrade `estraverse` to `1.9.1`. + * Upgrade `jshint` to `2.5.11`. + * Upgrade `regenerator` to `0.8.9`. + * Upgrade `regexpu` to `1.0.0`. + * Upgrade `source-map` to `0.1.43`. + * Upgrade `source-map-support` to `0.2.9`. + * Upgrade `browserify` to `8.1.1`. + * Upgrade `chai` to `1.10.0`. + * Upgrade `istanbul` to `0.3.5`. + * Upgrade `mocha` to `2.1.0`. + * Upgrade `uglify-js` to `2.4.16`. + +## 2.12.6 + + * **Bug Fix** + * Add missing flow type traversal keys. + +## 2.12.5 + + * **Internal** + * Fix incorrect `for...in` loop still causing `ember-script` issues. + +## 2.12.4 + + * **Polish** + * Rest parameters now allocate the array before populating. + * **Internal** + * `for...in` loops have been changed to optimised `for` loops - better performance and no enumeration of protoype keys. + * Parts of the code generator have now been optimised thanks to [gaearon](https://github.com/gaearon). + +## 2.12.3 + + * **Spec Compliancy** + * Optional `typeof` transformer checks for `undefined` before passing it to the helper. + * Class methods are now named. + +## 2.12.2 + + * **Internal** + * Exclude nodes in function parameters and catch clauses from `isReferenced` check. + +## 2.12.1 + + * **Internal** + * Add `.jsx` to list of allowed extensions. + ## 2.12.0 * **Bug Fix** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad8ff721f5..15314b185c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,20 @@ # Contributing -Before contributing, please read the [code of conduct](https://github.com/6to5/6to5/blob/master/CODE_OF_CONDUCT.md). +Contributions are always welcome, no matter how large or small. Before contributing, please read the [code of conduct](https://github.com/6to5/6to5/blob/master/CODE_OF_CONDUCT.md). + +## Developing + +#### Workflow + +* Fork the repository +* Clone your fork and change directory to it (`git clone git@github.com:yourUserName/6to5.git && cd 6to5`) +* Install the project dependencies (`make bootstrap`) +* Link your forked clone (`npm link`) +* Develop your changes ensuring you're fetching updates from upstream often +* Ensure the test are passing (`make test`) +* Create new pull request explaining your proposed change or reference an issue in your commit message + +#### Code Standards * **General** * No ES6 syntax features or methods, exclusively ES5. diff --git a/Makefile b/Makefile index 1acfc5281f..5ceff1b52e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BROWSERIFY_CMD = node_modules/browserify/bin/cmd.js ISTANBUL_CMD = node_modules/istanbul/lib/cli.js cover -UGLIFY_CMD = node_modules/uglify-js/bin/uglifyjs --mangle sort +UGLIFY_CMD = node_modules/uglify-js/bin/uglifyjs +#UGLIFY_CMD = node_modules/uglify-js/bin/uglifyjs --mangle sort JSHINT_CMD = node_modules/jshint/bin/jshint MOCHA_CMD = node_modules/mocha/bin/_mocha diff --git a/NOTES.md b/NOTES.md index a973eaafdf..7219db2712 100644 --- a/NOTES.md +++ b/NOTES.md @@ -11,3 +11,4 @@ * Add autoindentation. * Move `super` transformation from classes into a separate transformer that also supports object expressions. * Remove fast transformer backwards compatibility. + * Rename let scoping transformer to block scoping. diff --git a/bin/6to5/dir.js b/bin/6to5/dir.js index 376d0bf571..962379e49b 100644 --- a/bin/6to5/dir.js +++ b/bin/6to5/dir.js @@ -1,9 +1,9 @@ -var chokidar = require("chokidar"); -var mkdirp = require("mkdirp"); -var path = require("path"); -var util = require("./util"); -var fs = require("fs"); -var _ = require("lodash"); +var outputFileSync = require("output-file-sync"); +var chokidar = require("chokidar"); +var path = require("path"); +var util = require("./util"); +var fs = require("fs"); +var _ = require("lodash"); module.exports = function (commander, filenames, opts) { if (commander.sourceMapsInline) { @@ -15,16 +15,13 @@ module.exports = function (commander, filenames, opts) { var data = util.compile(src, { sourceMapName: dest }); - var up = path.normalize(dest + "/.."); - mkdirp.sync(up); - if (commander.sourceMaps) { var mapLoc = dest + ".map"; data.code = util.addSourceMappingUrl(data.code, mapLoc); - fs.writeFileSync(mapLoc, JSON.stringify(data.map)); + outputFileSync(mapLoc, JSON.stringify(data.map)); } - fs.writeFileSync(dest, data.code); + outputFileSync(dest, data.code); console.log(src + " -> " + dest); }; diff --git a/bin/6to5/file.js b/bin/6to5/file.js index a06bf304ea..6a1dd9b7fc 100644 --- a/bin/6to5/file.js +++ b/bin/6to5/file.js @@ -28,7 +28,7 @@ module.exports = function (commander, filenames) { map.setSourceContent(filename, result.actual); consumer.eachMapping(function (mapping) { - map._mappings.push({ + map._mappings.add({ generatedLine: mapping.generatedLine + offset, generatedColumn: mapping.generatedColumn, originalLine: mapping.originalLine, @@ -63,7 +63,7 @@ module.exports = function (commander, filenames) { fs.writeFileSync(commander.outFile, result.code); } else { - console.log(result.code); + process.stdout.write(result.code + "\n"); } }; diff --git a/bin/6to5/index.js b/bin/6to5/index.js index 955671555d..6b1019ba4e 100755 --- a/bin/6to5/index.js +++ b/bin/6to5/index.js @@ -34,13 +34,10 @@ commander.on("--help", function(){ console.log(" " + title + ":"); console.log(); - var hasOptional = true; - _.each(_.keys(obj).sort(), function (key) { if (key[0] === "_") return; if (obj[key].optional) { - hasOptional = true; key = "[" + key + "]"; } diff --git a/bin/_6to5-node b/bin/_6to5-node index 5c5a5a0821..0887c31d96 100644 --- a/bin/_6to5-node +++ b/bin/_6to5-node @@ -89,8 +89,8 @@ function replEval(code, context, filename, callback) { var result; try { - if (/^\((.*?)\n\)$/.test(code)) { - code = code.slice(1, -2); // remove "(" and "\n)" + if (code[0] === "(" && code[code.length - 1] === ")") { + code = code.slice(1, -1); // remove "(" and ")" } console.log(JSON.stringify(code)); diff --git a/lib/6to5/browser.js b/lib/6to5/browser.js index ccf6d54ed4..18a7fd0f28 100644 --- a/lib/6to5/browser.js +++ b/lib/6to5/browser.js @@ -1,3 +1,5 @@ +"use strict"; + var transform = module.exports = require("./transformation/transform"); transform.version = require("../../package").version; diff --git a/lib/6to5/file.js b/lib/6to5/file.js index 2bc26ee611..7f42be0951 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = File; var SHEBANG_REGEX = /^\#\!.*/; @@ -78,6 +80,10 @@ File.normaliseOptions = function (opts) { opts.optional = util.arrayify(opts.optional); opts.loose = util.arrayify(opts.loose); + if (_.contains(opts.loose, "all")) { + opts.loose = Object.keys(transform.transformers); + } + _.defaults(opts, { moduleRoot: opts.sourceRoot }); @@ -317,7 +323,10 @@ File.prototype.generateUid = function (name, scope) { }; File.prototype.generateUidIdentifier = function (name, scope) { - return t.identifier(this.generateUid(name, scope)); + scope = scope || this.scope; + var id = t.identifier(this.generateUid(name, scope)); + scope.add(id); + return id; }; File.prototype._generateUid = function (name) { diff --git a/lib/6to5/generation/buffer.js b/lib/6to5/generation/buffer.js index c9b4e662fe..26e6bcc9aa 100644 --- a/lib/6to5/generation/buffer.js +++ b/lib/6to5/generation/buffer.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = Buffer; var util = require("../util"); @@ -67,32 +69,57 @@ Buffer.prototype.removeLast = function (cha) { Buffer.prototype.newline = function (i, removeLast) { if (this.format.compact) return; - - if (_.isBoolean(i)) { - removeLast = i; - i = null; - } + removeLast = removeLast || false; if (_.isNumber(i)) { if (this.endsWith("{\n")) i--; if (this.endsWith(util.repeat(i, "\n"))) return; - for (var j = 0; j < i; j++) { - this.newline(null, removeLast); + while (i--) { + this._newline(removeLast); } return; } + if (_.isBoolean(i)) { + removeLast = i; + } + + this._newline(removeLast); +}; + +Buffer.prototype._newline = function (removeLast) { if (removeLast && this.isLast("\n")) this.removeLast("\n"); this.removeLast(" "); // remove whitespace if last character was a newline - this.buf = this.buf.replace(/\n +$/, "\n"); - + this._removeSpacesAfterLastNewline(); this._push("\n"); }; +/** + * If buffer ends with a newline and some spaces after it, trim those spaces. + */ +Buffer.prototype._removeSpacesAfterLastNewline = function () { + var lastNewlineIndex = this.buf.lastIndexOf('\n'); + if (lastNewlineIndex === -1) + return; + + var index = this.buf.length - 1; + while (index > lastNewlineIndex) { + if (this.buf[index] !== ' ') { + break; + } + + index--; + } + + if (index === lastNewlineIndex) { + this.buf = this.buf.substring(0, index + 1); + } +}; + Buffer.prototype.push = function (str, noIndent) { if (this._indent && !noIndent && str !== "\n") { // we have an indent level and we aren't pushing a newline diff --git a/lib/6to5/generation/generator.js b/lib/6to5/generation/generator.js index 39f92e1491..778a590e74 100644 --- a/lib/6to5/generation/generator.js +++ b/lib/6to5/generation/generator.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = function (ast, opts, code) { var gen = new CodeGenerator(ast, opts, code); return gen.generate(); diff --git a/lib/6to5/generation/generators/base.js b/lib/6to5/generation/generators/base.js index 5d7eb6f412..a9e8bd51af 100644 --- a/lib/6to5/generation/generators/base.js +++ b/lib/6to5/generation/generators/base.js @@ -1,3 +1,5 @@ +"use strict"; + exports.File = function (node, print) { print(node.program); }; diff --git a/lib/6to5/generation/generators/classes.js b/lib/6to5/generation/generators/classes.js index f7df307f62..df4ecd557a 100644 --- a/lib/6to5/generation/generators/classes.js +++ b/lib/6to5/generation/generators/classes.js @@ -1,3 +1,5 @@ +"use strict"; + exports.ClassExpression = exports.ClassDeclaration = function (node, print) { this.push("class"); diff --git a/lib/6to5/generation/generators/comprehensions.js b/lib/6to5/generation/generators/comprehensions.js index 1fe81bbbe4..feeef3c2de 100644 --- a/lib/6to5/generation/generators/comprehensions.js +++ b/lib/6to5/generation/generators/comprehensions.js @@ -1,3 +1,5 @@ +"use strict"; + exports.ComprehensionBlock = function (node, print) { this.keyword("for"); this.push("("); diff --git a/lib/6to5/generation/generators/expressions.js b/lib/6to5/generation/generators/expressions.js index ebe3fd9b7c..5989e8f8d0 100644 --- a/lib/6to5/generation/generators/expressions.js +++ b/lib/6to5/generation/generators/expressions.js @@ -1,3 +1,5 @@ +"use strict"; + var util = require("../../util"); var t = require("../../types"); var _ = require("lodash"); diff --git a/lib/6to5/generation/generators/flow.js b/lib/6to5/generation/generators/flow.js index f96e45bde0..cc3d481d1a 100644 --- a/lib/6to5/generation/generators/flow.js +++ b/lib/6to5/generation/generators/flow.js @@ -1,3 +1,35 @@ -exports.ClassProperty = function () { - throw new Error("not implemented"); +"use strict"; + +exports.AnyTypeAnnotation = +exports.ArrayTypeAnnotation = +exports.BooleanTypeAnnotation = +exports.ClassProperty = +exports.DeclareClass = +exports.DeclareFunction = +exports.DeclareModule = +exports.DeclareVariable = +exports.FunctionTypeAnnotation = +exports.FunctionTypeParam = +exports.GenericTypeAnnotation = +exports.InterfaceExtends = +exports.InterfaceDeclaration = +exports.IntersectionTypeAnnotation = +exports.NullableTypeAnnotation = +exports.NumberTypeAnnotation = +exports.StringLiteralTypeAnnotation = +exports.StringTypeAnnotation = +exports.TupleTypeAnnotation = +exports.TypeofTypeAnnotation = +exports.TypeAlias = +exports.TypeAnnotation = +exports.TypeParameterDeclaration = +exports.TypeParameterInstantiation = +exports.ObjectTypeAnnotation = +exports.ObjectTypeCallProperty = +exports.ObjectTypeIndexer = +exports.ObjectTypeProperty = +exports.QualifiedTypeIdentifier = +exports.UnionTypeAnnotation = +exports.VoidTypeAnnotation = function () { + // todo: implement these once we have a `--keep-types` option }; diff --git a/lib/6to5/generation/generators/jsx.js b/lib/6to5/generation/generators/jsx.js index 5460660515..d372cbc74b 100644 --- a/lib/6to5/generation/generators/jsx.js +++ b/lib/6to5/generation/generators/jsx.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); diff --git a/lib/6to5/generation/generators/methods.js b/lib/6to5/generation/generators/methods.js index e147b20a06..40a4e52b89 100644 --- a/lib/6to5/generation/generators/methods.js +++ b/lib/6to5/generation/generators/methods.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports._params = function (node, print) { diff --git a/lib/6to5/generation/generators/modules.js b/lib/6to5/generation/generators/modules.js index 16f6a01d83..6e8a587efb 100644 --- a/lib/6to5/generation/generators/modules.js +++ b/lib/6to5/generation/generators/modules.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); diff --git a/lib/6to5/generation/generators/playground.js b/lib/6to5/generation/generators/playground.js index b97e2d6184..d11aa04ee6 100644 --- a/lib/6to5/generation/generators/playground.js +++ b/lib/6to5/generation/generators/playground.js @@ -1,3 +1,5 @@ +"use strict"; + var _ = require("lodash"); _.each(["BindMemberExpression", "BindFunctionExpression"], function (type) { diff --git a/lib/6to5/generation/generators/statements.js b/lib/6to5/generation/generators/statements.js index f3a6babeab..473ce5a18b 100644 --- a/lib/6to5/generation/generators/statements.js +++ b/lib/6to5/generation/generators/statements.js @@ -1,3 +1,5 @@ +"use strict"; + var util = require("../../util"); var t = require("../../types"); @@ -167,18 +169,19 @@ exports.DebuggerStatement = function () { exports.VariableDeclaration = function (node, print, parent) { this.push(node.kind + " "); - var inits = 0; - var noInits = 0; - for (var i in node.declarations) { - if (node.declarations[i].init) { - inits++; - } else { - noInits++; + var hasInits = false; + // don't add whitespace to loop heads + if (!t.isFor(parent)) { + for (var i = 0; i < node.declarations.length; i++) { + if (node.declarations[i].init) { + // has an init so let's split it up over multiple lines + hasInits = true; + } } } var sep = ","; - if (inits > noInits) { // more inits than noinits + if (hasInits) { sep += "\n" + util.repeat(node.kind.length + 1); } else { sep += " "; diff --git a/lib/6to5/generation/generators/template-literals.js b/lib/6to5/generation/generators/template-literals.js index b723a2b600..4bda97505f 100644 --- a/lib/6to5/generation/generators/template-literals.js +++ b/lib/6to5/generation/generators/template-literals.js @@ -1,3 +1,5 @@ +"use strict"; + var _ = require("lodash"); exports.TaggedTemplateExpression = function (node, print) { diff --git a/lib/6to5/generation/generators/types.js b/lib/6to5/generation/generators/types.js index 4a58375a8d..405fe31720 100644 --- a/lib/6to5/generation/generators/types.js +++ b/lib/6to5/generation/generators/types.js @@ -1,3 +1,5 @@ +"use strict"; + var _ = require("lodash"); exports.Identifier = function (node) { diff --git a/lib/6to5/generation/node/index.js b/lib/6to5/generation/node/index.js index 42daaafaec..ba6cc61f78 100644 --- a/lib/6to5/generation/node/index.js +++ b/lib/6to5/generation/node/index.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = Node; var whitespace = require("./whitespace"); @@ -13,7 +15,7 @@ var find = function (obj, node, parent) { for (var i = 0; i < types.length; i++) { var type = types[i]; - if (t["is" + type](node)) { + if (t.is(type, node)) { var fn = obj[type]; result = fn(node, parent); if (result != null) break; @@ -28,13 +30,11 @@ function Node(node, parent) { this.node = node; } -Node.prototype.isUserWhitespacable = function () { - return t.isUserWhitespacable(this.node); +Node.isUserWhitespacable = function (node) { + return t.isUserWhitespacable(node); }; -Node.prototype.needsWhitespace = function (type) { - var parent = this.parent; - var node = this.node; +Node.needsWhitespace = function (node, parent, type) { if (!node) return 0; if (t.isExpressionStatement(node)) { @@ -51,18 +51,15 @@ Node.prototype.needsWhitespace = function (type) { return lines || 0; }; -Node.prototype.needsWhitespaceBefore = function () { - return this.needsWhitespace("before"); +Node.needsWhitespaceBefore = function (node, parent) { + return Node.needsWhitespace(node, parent, "before"); }; -Node.prototype.needsWhitespaceAfter = function () { - return this.needsWhitespace("after"); +Node.needsWhitespaceAfter = function (node, parent) { + return Node.needsWhitespace(node, parent, "after"); }; -Node.prototype.needsParens = function () { - var parent = this.parent; - var node = this.node; - +Node.needsParens = function (node, parent) { if (!parent) return false; if (t.isNewExpression(parent) && parent.callee === node) { @@ -77,10 +74,7 @@ Node.prototype.needsParens = function () { return find(parens, node, parent); }; -Node.prototype.needsParensNoLineTerminator = function () { - var parent = this.parent; - var node = this.node; - +Node.needsParensNoLineTerminator = function (node, parent) { if (!parent) return false; // no comments @@ -100,17 +94,18 @@ Node.prototype.needsParensNoLineTerminator = function () { return false; }; -_.each(Node.prototype, function (fn, key) { - Node[key] = function (node, parent) { - var n = new Node(node, parent); - +_.each(Node, function (fn, key) { + Node.prototype[key] = function () { // Avoid leaking arguments to prevent deoptimization - var skipCount = 2; - var args = new Array(arguments.length - skipCount); + var args = new Array(arguments.length + 2); + + args[0] = this.node; + args[1] = this.parent; + for (var i = 0; i < args.length; i++) { - args[i] = arguments[i + 2]; + args[i + 2] = arguments[i]; } - return n[key].apply(n, args); + return Node[key].apply(null, args); }; }); diff --git a/lib/6to5/generation/node/parentheses.js b/lib/6to5/generation/node/parentheses.js index 5518086e27..391f8ffc5c 100644 --- a/lib/6to5/generation/node/parentheses.js +++ b/lib/6to5/generation/node/parentheses.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); diff --git a/lib/6to5/generation/node/whitespace.js b/lib/6to5/generation/node/whitespace.js index d1defd1cfe..8deca5a4b2 100644 --- a/lib/6to5/generation/node/whitespace.js +++ b/lib/6to5/generation/node/whitespace.js @@ -1,3 +1,5 @@ +"use strict"; + var _ = require("lodash"); var t = require("../../types"); diff --git a/lib/6to5/generation/position.js b/lib/6to5/generation/position.js index cae67ca356..df85e0a332 100644 --- a/lib/6to5/generation/position.js +++ b/lib/6to5/generation/position.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = Position; function Position() { diff --git a/lib/6to5/generation/source-map.js b/lib/6to5/generation/source-map.js index 34f5d9d0cd..2dc922f024 100644 --- a/lib/6to5/generation/source-map.js +++ b/lib/6to5/generation/source-map.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = SourceMap; var sourceMap = require("source-map"); diff --git a/lib/6to5/generation/whitespace.js b/lib/6to5/generation/whitespace.js index c7dda860da..30855472d4 100644 --- a/lib/6to5/generation/whitespace.js +++ b/lib/6to5/generation/whitespace.js @@ -1,10 +1,40 @@ +"use strict"; + module.exports = Whitespace; -var _ = require("lodash"); +var _ = require("lodash"); + +/** + * Returns `i`th number from `base`, continuing from 0 when `max` is reached. + * Useful for shifting `for` loop by a fixed number but going over all items. + * + * @param {Number} i Current index in the loop + * @param {Number} base Start index for which to return 0 + * @param {Number} max Array length + * @returns {Number} shiftedIndex + */ +function getLookupIndex(i, base, max) { + i += base; + + if (i >= max) + i -= max; + + return i; +} function Whitespace(tokens, comments) { this.tokens = _.sortBy(tokens.concat(comments), "start"); - this.used = []; + this.used = {}; + + // Profiling this code shows that while generator passes over it, indexes + // returned by `getNewlinesBefore` and `getNewlinesAfter` are always increasing. + + // We use this implementation detail for an optimization: instead of always + // starting to look from `this.tokens[0]`, we will start `for` loops from the + // previous successful match. We will enumerate all tokens—but the common + // case will be much faster. + + this._lastFoundIndex = 0; } Whitespace.prototype.getNewlinesBefore = function (node) { @@ -13,13 +43,17 @@ Whitespace.prototype.getNewlinesBefore = function (node) { var tokens = this.tokens; var token; - for (var i = 0; i < tokens.length; i++) { + for (var j = 0; j < tokens.length; j++) { + // optimize for forward traversal by shifting for loop index + var i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length); token = tokens[i]; // this is the token this node starts with if (node.start === token.start) { startToken = tokens[i - 1]; endToken = token; + + this._lastFoundIndex = i; break; } } @@ -33,13 +67,17 @@ Whitespace.prototype.getNewlinesAfter = function (node) { var tokens = this.tokens; var token; - for (var i = 0; i < tokens.length; i++) { + for (var j = 0; j < tokens.length; j++) { + // optimize for forward traversal by shifting for loop index + var i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length); token = tokens[i]; // this is the token this node ends with if (node.end === token.end) { startToken = token; endToken = tokens[i + 1]; + + this._lastFoundIndex = i; break; } } @@ -60,12 +98,11 @@ Whitespace.prototype.getNewlinesAfter = function (node) { Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) { var start = startToken ? startToken.loc.end.line : 1; var end = endToken.loc.start.line; - var lines = 0; for (var line = start; line < end; line++) { - if (!_.contains(this.used, line)) { - this.used.push(line); + if (typeof this.used[line] === 'undefined') { + this.used[line] = true; lines++; } } diff --git a/lib/6to5/index.js b/lib/6to5/index.js index 7e3bac2922..79a33a1f0b 100644 --- a/lib/6to5/index.js +++ b/lib/6to5/index.js @@ -1,3 +1,5 @@ +"use strict"; + var transform = require("./transformation/transform"); var util = require("./util"); var fs = require("fs"); diff --git a/lib/6to5/patch.js b/lib/6to5/patch.js index 76e65bd38f..ac1ff2670b 100644 --- a/lib/6to5/patch.js +++ b/lib/6to5/patch.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("./types"); var _ = require("lodash"); diff --git a/lib/6to5/polyfill.js b/lib/6to5/polyfill.js index c8d5124f2b..7a893bd69c 100644 --- a/lib/6to5/polyfill.js +++ b/lib/6to5/polyfill.js @@ -1,2 +1,4 @@ +"use strict"; + require("core-js/shim"); require("regenerator/runtime"); diff --git a/lib/6to5/register-browser.js b/lib/6to5/register-browser.js index 7f7ab7a552..bd18a5fbc9 100644 --- a/lib/6to5/register-browser.js +++ b/lib/6to5/register-browser.js @@ -1,3 +1,5 @@ +"use strict"; + // required to safely use 6to5/register within a browserify codebase module.exports = function () {}; diff --git a/lib/6to5/register.js b/lib/6to5/register.js index 76cf325df7..3643024506 100644 --- a/lib/6to5/register.js +++ b/lib/6to5/register.js @@ -1,3 +1,5 @@ +"use strict"; + require("./polyfill"); var sourceMapSupport = require("source-map-support"); @@ -23,49 +25,6 @@ sourceMapSupport.install({ // -var blacklist = []; - -var blacklistTest = function (transformer, code) { - try { - if (_.isFunction(code)) { - code(); - } else { - new Function(code); - } - blacklist.push(transformer); - } catch (err) { - if (err.name !== "SyntaxError") throw err; - } -}; - -blacklistTest("arrayComprehension", "var foo = [for (foo of bar) foo * foo];"); -blacklistTest("generatorComprehension", "var foo = (for (foo of bar) foo * foo)"); -blacklistTest("arrowFunctions", "var foo = x => x * x;"); -blacklistTest("classes", "class Foo {}"); -blacklistTest("computedPropertyNames", "var foo = { [foo]: bar };"); -blacklistTest("constants", function () { - try { - new Function("const foo = 'foo';\nfoo = 'wow';"); - } catch (err) { - return; // constants are supported - } - throw new SyntaxError; -}); -blacklistTest("defaultParamaters", "var foo = function (bar = 0) {};"); -blacklistTest("destructuring", "var { x, y } = { x: 0, y: 0 };"); -blacklistTest("forOf", "for (var foo of bar) {}"); -blacklistTest("generators", "function* foo() {}\nasync function bar() {}"); // generators/async functions delegated to same transformer -blacklistTest("letScoping", "let foo = 0;"); -blacklistTest("modules", 'import foo from "from";'); -blacklistTest("propertyMethodAssignment", "{ get foo() {} }"); -blacklistTest("propertyNameShorthand", "var foo = { x, y };"); -blacklistTest("restParameters", "function foo(...bar) {}"); -blacklistTest("spread", "foo(...bar);"); -blacklistTest("templateLiterals", "var foo = `foo`;"); -blacklistTest("unicodeRegex", function () { new RegExp("foo", "u"); }); - -// - registerCache.load(); var cache = registerCache.get(); @@ -99,7 +58,6 @@ var loader = function (m, filename) { result = result || to5.transformFileSync(filename, _.extend({ whitelist: whitelist, - blacklist: blacklist, sourceMap: true, ast: false }, transformOpts)); diff --git a/lib/6to5/runtime-generator.js b/lib/6to5/runtime-generator.js index 8ea86c5c99..fa44638215 100644 --- a/lib/6to5/runtime-generator.js +++ b/lib/6to5/runtime-generator.js @@ -1,3 +1,5 @@ +"use strict"; + var generator = require("./generation/generator"); var util = require("./util"); var File = require("./file"); diff --git a/lib/6to5/to-fast-properties.js b/lib/6to5/to-fast-properties.js new file mode 100644 index 0000000000..c82e9cbe4b --- /dev/null +++ b/lib/6to5/to-fast-properties.js @@ -0,0 +1,17 @@ +"use strict"; + +/** + * A trick from Bluebird to force V8 to use fast properties for an object. + * Read more: http://stackoverflow.com/questions/24987896/ + * + * Use %HasFastProperties(obj) and --allow-natives-syntax to check whether + * a particular object already has fast properties. + */ + +module.exports = function toFastProperties(obj) { + /*jshint -W027*/ + function f() {} + f.prototype = obj; + return f; + eval(obj); +}; diff --git a/lib/6to5/transformation/helpers/build-binary-assignment-operator-transformer.js b/lib/6to5/transformation/helpers/build-binary-assignment-operator-transformer.js new file mode 100644 index 0000000000..5822026ec6 --- /dev/null +++ b/lib/6to5/transformation/helpers/build-binary-assignment-operator-transformer.js @@ -0,0 +1,45 @@ +"use strict"; + +var explode = require("./explode-assignable-expression"); +var t = require("../../types"); + +module.exports = function (exports, opts) { + var isAssignment = function (node) { + return node.operator === opts.operator + "="; + }; + + var buildAssignment = function (left, right) { + return t.assignmentExpression("=", left, right); + }; + + exports.ExpressionStatement = function (node, parent, scope, context, file) { + var expr = node.expression; + if (!isAssignment(expr)) return; + + var nodes = []; + var exploded = explode(expr.left, nodes, file, scope, true); + + nodes.push(t.expressionStatement( + buildAssignment(exploded.ref, opts.build(exploded.uid, expr.right)) + )); + + return nodes; + }; + + exports.AssignmentExpression = function (node, parent, scope, context, file) { + if (t.isExpressionStatement(parent)) return; + if (!isAssignment(node)) return; + + var nodes = []; + var exploded = explode(node.left, nodes, file, scope); + nodes.push(opts.build(exploded.uid, node.right)); + nodes.push(exploded.ref); + + return t.toSequenceExpression(nodes, scope); + }; + + exports.BinaryExpression = function (node) { + if (node.operator !== opts.operator) return; + return opts.build(node.left, node.right); + }; +}; diff --git a/lib/6to5/transformation/helpers/build-comprehension.js b/lib/6to5/transformation/helpers/build-comprehension.js new file mode 100644 index 0000000000..d390b7afde --- /dev/null +++ b/lib/6to5/transformation/helpers/build-comprehension.js @@ -0,0 +1,25 @@ +"use strict"; + +var t = require("../../types"); + +module.exports = function build(node, buildBody) { + var self = node.blocks.shift(); + if (!self) return; + + var child = build(node, buildBody); + if (!child) { + // last item + child = buildBody(); + + // add a filter as this is our final stop + if (node.filter) { + child = t.ifStatement(node.filter, t.blockStatement([child])); + } + } + + return t.forOfStatement( + t.variableDeclaration("let", [t.variableDeclarator(self.left)]), + self.right, + t.blockStatement([child]) + ); +}; diff --git a/lib/6to5/transformation/helpers/build-conditional-assignment-operator-transformer.js b/lib/6to5/transformation/helpers/build-conditional-assignment-operator-transformer.js new file mode 100644 index 0000000000..9137b31b23 --- /dev/null +++ b/lib/6to5/transformation/helpers/build-conditional-assignment-operator-transformer.js @@ -0,0 +1,45 @@ +"use strict"; + +var explode = require("./explode-assignable-expression"); +var t = require("../../types"); + +module.exports = function (exports, opts) { + var buildAssignment = function (left, right) { + return t.assignmentExpression("=", left, right); + }; + + exports.ExpressionStatement = function (node, parent, scope, context, file) { + var expr = node.expression; + if (!opts.is(expr, file)) return; + + var nodes = []; + + var exploded = explode(expr.left, nodes, file, scope); + + nodes.push(t.ifStatement( + opts.build(exploded.uid, file), + t.expressionStatement(buildAssignment(exploded.ref, expr.right)) + )); + + return nodes; + }; + + exports.AssignmentExpression = function (node, parent, scope, context, file) { + if (t.isExpressionStatement(parent)) return; + if (!opts.is(node, file)) return; + + var nodes = []; + var exploded = explode(node.left, nodes, file, scope); + + nodes.push(t.logicalExpression( + "&&", + opts.build(exploded.uid, file), + buildAssignment(exploded.ref, node.right) + )); + + // todo: duplicate expression node + nodes.push(exploded.ref); + + return t.toSequenceExpression(nodes, scope); + }; +}; diff --git a/lib/6to5/transformation/helpers/explode-assignable-expression.js b/lib/6to5/transformation/helpers/explode-assignable-expression.js new file mode 100644 index 0000000000..9412f85390 --- /dev/null +++ b/lib/6to5/transformation/helpers/explode-assignable-expression.js @@ -0,0 +1,57 @@ +"use strict"; + +var t = require("../../types"); + +var getObjRef = function (node, nodes, file, scope) { + var ref; + if (t.isIdentifier(node)) { + ref = node; + } else if (t.isMemberExpression(node)) { + ref = node.object; + } else { + throw new Error("We can't explode this node type " + node.type); + } + + var temp = scope.generateUidBasedOnNode(ref, file); + nodes.push(t.variableDeclaration("var", [ + t.variableDeclarator(temp, ref) + ])); + return temp; +}; + +var getPropRef = function (node, nodes, file, scope) { + var prop = node.property; + var key = t.toComputedKey(node, prop); + if (t.isLiteral(key)) return key; + + var temp = scope.generateUidBasedOnNode(prop, file); + nodes.push(t.variableDeclaration("var", [ + t.variableDeclarator(temp, prop) + ])); + return temp; +}; + +module.exports = function (node, nodes, file, scope, allowedSingleIdent) { + var obj; + if (t.isIdentifier(node) && allowedSingleIdent) { + obj = node; + } else { + obj = getObjRef(node, nodes, file, scope); + } + + var ref, uid; + + if (t.isIdentifier(node)) { + ref = node; + uid = obj; + } else { + var prop = getPropRef(node, nodes, file, scope); + var computed = node.computed || t.isLiteral(prop); + uid = ref = t.memberExpression(obj, prop, computed); + } + + return { + uid: uid, + ref: ref + }; +}; diff --git a/lib/6to5/transformation/helpers/name-method.js b/lib/6to5/transformation/helpers/name-method.js new file mode 100644 index 0000000000..34ea75fbf1 --- /dev/null +++ b/lib/6to5/transformation/helpers/name-method.js @@ -0,0 +1,50 @@ +"use strict"; + +var traverse = require("../../traverse"); +var util = require("../../util"); +var t = require("../../types"); + +var traverser = { + enter: function (node, parent, scope, context, state) { + // check if this node is an identifier that matches the same as our function id + if (!t.isIdentifier(node, { name: state.id })) return; + + // check if this node is the one referenced + if (!t.isReferenced(node, parent)) return; + + // check that we don't have a local variable declared as that removes the need + // for the wrapper + var localDeclar = scope.get(state.id, true); + if (localDeclar !== state.outerDeclar) return; + + state.selfReference = true; + context.stop(); + } +}; + +exports.property = function (node, file, scope) { + var key = t.toComputedKey(node, node.key); + if (!t.isLiteral(key)) return node; // we can't set a function id with this + + var id = t.toIdentifier(key.value); + key = t.identifier(id); + + var state = { + id: id, + selfReference: false, + outerDeclar: scope.get(id, true), + }; + + traverse(node, traverser, scope, state); + + if (state.selfReference) { + node.value = util.template("property-method-assignment-wrapper", { + FUNCTION: node.value, + FUNCTION_ID: key, + FUNCTION_KEY: file.generateUidIdentifier(id, scope), + WRAPPER_KEY: file.generateUidIdentifier(id + "Wrapper", scope) + }); + } else { + node.value.id = key; + } +}; diff --git a/lib/6to5/transformation/helpers/remap-async-to-generator.js b/lib/6to5/transformation/helpers/remap-async-to-generator.js new file mode 100644 index 0000000000..7426862f71 --- /dev/null +++ b/lib/6to5/transformation/helpers/remap-async-to-generator.js @@ -0,0 +1,31 @@ +"use strict"; + +var traverse = require("../../traverse"); +var t = require("../../types"); + +module.exports = function (node, callId) { + node.async = false; + node.generator = true; + + traverse(node, { + enter: function (node, parent, scope, context) { + if (t.isFunction(node)) context.skip(); + + if (t.isAwaitExpression(node)) { + node.type = "YieldExpression"; + } + } + }); + + var call = t.callExpression(callId, [node]); + + if (t.isFunctionDeclaration(node)) { + var declar = t.variableDeclaration("var", [ + t.variableDeclarator(node.id, call) + ]); + declar._blockHoist = true; + return declar; + } else { + return call; + } +}; diff --git a/lib/6to5/transformation/helpers/replace-supers.js b/lib/6to5/transformation/helpers/replace-supers.js new file mode 100644 index 0000000000..2403e878cb --- /dev/null +++ b/lib/6to5/transformation/helpers/replace-supers.js @@ -0,0 +1,246 @@ +"use strict"; + +module.exports = ReplaceSupers; + +var traverse = require("../../traverse"); +var t = require("../../types"); + +/** + * Description + * + * @param {Object} methodNode + * @param {Object} className + * @param {Object} superName + * @param {Boolean} isLoose + * @param {File} file + */ + +function ReplaceSupers(methodNode, className, superName, isLoose, file) { + this.topLevelThisReference = null; + this.methodNode = methodNode; + this.className = className; + this.superName = superName; + this.isLoose = isLoose; + this.file = file; +} + +/** + * Gets a node representing the super class value of the named property. + * + * @example + * + * _get(Object.getPrototypeOf(CLASS.prototype), "METHOD", this) + * + * @param {Node} property + * @param {boolean} isStatic + * @param {boolean} isComputed + * + * @returns {Node} + */ + +ReplaceSupers.prototype.superProperty = function (property, isStatic, isComputed, thisExpression) { + return t.callExpression( + this.file.addHelper("get"), + [ + t.callExpression( + t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")), + [ + isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype")) + ] + ), + isComputed ? property : t.literal(property.name), + thisExpression + ] + ); +}; + +/** + * Description + * + * @param {Object} node + * @param {Object} id + * @param {Object} parent + * @returns {Object} + */ + +ReplaceSupers.prototype.looseSuperProperty = function (methodNode, id, parent) { + var methodName = methodNode.key; + var superName = this.superName || t.identifier("Function"); + + if (parent.property === id) { + return; + } else if (t.isCallExpression(parent, { callee: id })) { + // super(); -> ClassName.prototype.MethodName.call(this); + parent.arguments.unshift(t.thisExpression()); + + if (methodName.name === "constructor") { + // constructor() { super(); } + return t.memberExpression(superName, t.identifier("call")); + } else { + id = superName; + + // foo() { super(); } + if (!methodNode.static) { + id = t.memberExpression(id, t.identifier("prototype")); + } + + id = t.memberExpression(id, methodName, methodNode.computed); + return t.memberExpression(id, t.identifier("call")); + } + } else if (t.isMemberExpression(parent) && !methodNode.static) { + // super.test -> ClassName.prototype.test + return t.memberExpression(superName, t.identifier("prototype")); + } else { + return superName; + } +}; + +/** + * Description + */ + +ReplaceSupers.prototype.replace = function () { + this.traverseLevel(this.methodNode.value, true); +}; + +/** + * Description + * + * @param {Object} node + * @param {Boolean} topLevel + */ + +ReplaceSupers.prototype.traverseLevel = function (node, topLevel) { + var self = this; + + traverse(node, { + enter: function (node, parent, scope, context) { + if (t.isFunction(node) && !t.isArrowFunctionExpression(node)) { + // we need to call traverseLevel again so we're context aware + self.traverseLevel(node, false); + return context.skip(); + } + + if (t.isProperty(node, { method: true }) || t.isMethodDefinition(node)) { + // break on object methods + return context.skip(); + } + + var getThisReference = function () { + if (topLevel) { + // top level so `this` is the instance + return t.thisExpression(); + } else { + // not in the top level so we need to create a reference + return self.getThisReference(); + } + }; + + var callback = self.specHandle; + if (self.isLoose) callback = self.looseHandle; + return callback.call(self, getThisReference, node, parent); + } + }); +}; + +/** + * Description + */ + +ReplaceSupers.prototype.getThisReference = function () { + if (this.topLevelThisReference) { + return this.topLevelThisReference; + } else { + var ref = this.topLevelThisReference = this.file.generateUidIdentifier("this"); + this.methodNode.value.body.body.unshift(t.variableDeclaration("var", [ + t.variableDeclarator(this.topLevelThisReference, t.thisExpression()) + ])); + return ref; + } +}; + +/** + * Description + * + * @param {Function} getThisReference + * @param {Object} node + * @param {Object} parent + */ + +ReplaceSupers.prototype.looseHandle = function (getThisReference, node, parent) { + if (t.isIdentifier(node, { name: "super" })) { + return this.looseSuperProperty(this.methodNode, node, parent); + } else if (t.isCallExpression(node)) { + var callee = node.callee; + if (!t.isMemberExpression(callee)) return; + if (callee.object.name !== "super") return; + + // super.test(); -> ClassName.prototype.MethodName.call(this); + t.appendToMemberExpression(callee, t.identifier("call")); + node.arguments.unshift(getThisReference()); + } +}; + +/** + * Description + * + * @param {Function} getThisReference + * @param {Object} node + * @param {Object} parent + */ + +ReplaceSupers.prototype.specHandle = function (getThisReference, node, parent) { + var methodNode = this.methodNode; + var property; + var computed; + var args; + + if (t.isIdentifier(node, { name: "super" })) { + if (!(t.isMemberExpression(parent) && !parent.computed && parent.property === node)) { + throw this.file.errorWithNode(node, "illegal use of bare super"); + } + } else if (t.isCallExpression(node)) { + var callee = node.callee; + if (t.isIdentifier(callee, { name: "super" })) { + // super(); -> _get(Object.getPrototypeOf(ClassName), "MethodName", this).call(this); + property = methodNode.key; + computed = methodNode.computed; + args = node.arguments; + } else { + if (!t.isMemberExpression(callee)) return; + if (callee.object.name !== "super") return; + + // super.test(); -> _get(Object.getPrototypeOf(ClassName.prototype), "test", this).call(this); + property = callee.property; + computed = callee.computed; + args = node.arguments; + } + } else if (t.isMemberExpression(node)) { + if (!t.isIdentifier(node.object, { name: "super" })) return; + + // super.name; -> _get(Object.getPrototypeOf(ClassName.prototype), "name", this); + property = node.property; + computed = node.computed; + } + + if (!property) return; + + var thisReference = getThisReference(); + var superProperty = this.superProperty(property, methodNode.static, computed, thisReference); + if (args) { + if (args.length === 1 && t.isSpreadElement(args[0])) { + // super(...arguments); + return t.callExpression( + t.memberExpression(superProperty, t.identifier("apply")), + [thisReference, args[0].argument] + ); + } else { + return t.callExpression( + t.memberExpression(superProperty, t.identifier("call")), + [thisReference].concat(args) + ); + } + } else { + return superProperty; + } +}; diff --git a/lib/6to5/transformation/helpers/use-strict.js b/lib/6to5/transformation/helpers/use-strict.js new file mode 100644 index 0000000000..7709528a41 --- /dev/null +++ b/lib/6to5/transformation/helpers/use-strict.js @@ -0,0 +1,21 @@ +"use strict"; + +var t = require("../../types"); + +exports.has = function (node) { + var first = node.body[0]; + return t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" }); +}; + +exports.wrap = function (node, callback) { + var useStrictNode; + if (exports.has(node)) { + useStrictNode = node.body.shift(); + } + + callback(); + + if (useStrictNode) { + node.body.unshift(useStrictNode); + } +}; diff --git a/lib/6to5/transformation/modules/_default.js b/lib/6to5/transformation/modules/_default.js index 5e8ceb12a7..989d7b3f94 100644 --- a/lib/6to5/transformation/modules/_default.js +++ b/lib/6to5/transformation/modules/_default.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = DefaultFormatter; var traverse = require("../../traverse"); @@ -16,35 +18,51 @@ function DefaultFormatter(file) { //this.checkCollisions(); } +var exportsTraverser = { + enter: function (node, parent, scope, context, localExports) { + var declar = node && node.declaration; + if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) { + _.extend(localExports, t.getIds(declar, true)); + } + } +}; + DefaultFormatter.prototype.getLocalExports = function () { var localExports = {}; - - traverse(this.file.ast, { - enter: function (node) { - var declar = node && node.declaration; - if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) { - _.extend(localExports, t.getIds(declar, true)); - } - } - }); - + traverse(this.file.ast, exportsTraverser, null, localExports); return localExports; }; +var importsTraverser = { + enter: function (node, parent, scope, context, localImports) { + if (t.isImportDeclaration(node)) { + _.extend(localImports, t.getIds(node, true)); + } + } +}; + DefaultFormatter.prototype.getLocalImports = function () { var localImports = {}; - - traverse(this.file.ast, { - enter: function (node) { - if (t.isImportDeclaration(node)) { - _.extend(localImports, t.getIds(node, true)); - } - } - }); - + traverse(this.file.ast, importsTraverser, null, localImports); return localImports; }; +var collissionsTraverser = { + enter: function (node, parent, scope, context, check) { + if (t.isAssignmentExpression(node)) { + + var left = node.left; + if (t.isMemberExpression(left)) { + while (left.object) left = left.object; + } + + check(left); + } else if (t.isDeclaration(node)) { + _.each(t.getIds(node, true), check); + } + } +}; + DefaultFormatter.prototype.checkCollisions = function () { // todo: all check export collissions @@ -61,21 +79,7 @@ DefaultFormatter.prototype.checkCollisions = function () { } }; - traverse(file.ast, { - enter: function (node) { - if (t.isAssignmentExpression(node)) { - - var left = node.left; - if (t.isMemberExpression(left)) { - while (left.object) left = left.object; - } - - check(left); - } else if (t.isDeclaration(node)) { - _.each(t.getIds(node, true), check); - } - } - }); + traverse(file.ast, collissionsTraverser, null, check); }; DefaultFormatter.prototype.remapExportAssignment = function (node) { @@ -100,9 +104,9 @@ DefaultFormatter.prototype.remapAssignments = function () { }; traverse(this.file.ast, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, isLocalReference) { if (t.isUpdateExpression(node) && isLocalReference(node.argument, scope)) { - this.skip(); + context.skip(); // expand to long file assignment expression var assign = t.assignmentExpression(node.operator[0] + "=", node.argument, t.literal(1)); @@ -130,11 +134,11 @@ DefaultFormatter.prototype.remapAssignments = function () { } if (t.isAssignmentExpression(node) && isLocalReference(node.left, scope)) { - this.skip(); + context.skip(); return self.remapExportAssignment(node); } } - }); + }, null, isLocalReference); }; DefaultFormatter.prototype.getModuleName = function () { @@ -244,13 +248,13 @@ DefaultFormatter.prototype.exportDeclaration = function (node, nodes) { var assign; if (t.isVariableDeclaration(declar)) { - for (var i in declar.declarations) { + for (var i = 0; i < declar.declarations.length; i++) { var decl = declar.declarations[i]; decl.init = this._exportsAssign(decl.id, decl.init, node).expression; var newDeclar = t.variableDeclaration(declar.kind, [decl]); - if (i === "0") t.inherits(newDeclar, declar); + if (i === 0) t.inherits(newDeclar, declar); nodes.push(newDeclar); } } else { diff --git a/lib/6to5/transformation/modules/_strict.js b/lib/6to5/transformation/modules/_strict.js index 2531d64422..160f30b547 100644 --- a/lib/6to5/transformation/modules/_strict.js +++ b/lib/6to5/transformation/modules/_strict.js @@ -1,3 +1,5 @@ +"use strict"; + var util = require("../../util"); module.exports = function (Parent) { diff --git a/lib/6to5/transformation/modules/amd-strict.js b/lib/6to5/transformation/modules/amd-strict.js index 725d67325d..a055b94763 100644 --- a/lib/6to5/transformation/modules/amd-strict.js +++ b/lib/6to5/transformation/modules/amd-strict.js @@ -1 +1,3 @@ +"use strict"; + module.exports = require("./_strict")(require("./amd")); diff --git a/lib/6to5/transformation/modules/amd.js b/lib/6to5/transformation/modules/amd.js index f0aedd7e67..3940c9d18d 100644 --- a/lib/6to5/transformation/modules/amd.js +++ b/lib/6to5/transformation/modules/amd.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = AMDFormatter; var DefaultFormatter = require("./_default"); diff --git a/lib/6to5/transformation/modules/common-strict.js b/lib/6to5/transformation/modules/common-strict.js index 54608375de..bb7b5c944c 100644 --- a/lib/6to5/transformation/modules/common-strict.js +++ b/lib/6to5/transformation/modules/common-strict.js @@ -1 +1,3 @@ +"use strict"; + module.exports = require("./_strict")(require("./common")); diff --git a/lib/6to5/transformation/modules/common.js b/lib/6to5/transformation/modules/common.js index f62cb03883..8e6044f2e9 100644 --- a/lib/6to5/transformation/modules/common.js +++ b/lib/6to5/transformation/modules/common.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = CommonJSFormatter; var DefaultFormatter = require("./_default"); diff --git a/lib/6to5/transformation/modules/ignore.js b/lib/6to5/transformation/modules/ignore.js index 3753440eed..64c902afe7 100644 --- a/lib/6to5/transformation/modules/ignore.js +++ b/lib/6to5/transformation/modules/ignore.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = IgnoreFormatter; var t = require("../../types"); diff --git a/lib/6to5/transformation/modules/system.js b/lib/6to5/transformation/modules/system.js index d8a01218e3..85f64662a9 100644 --- a/lib/6to5/transformation/modules/system.js +++ b/lib/6to5/transformation/modules/system.js @@ -1,7 +1,9 @@ +"use strict"; + module.exports = SystemFormatter; var AMDFormatter = require("./amd"); -var useStrict = require("../transformers/use-strict"); +var useStrict = require("../helpers/use-strict"); var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); @@ -63,28 +65,31 @@ SystemFormatter.prototype.importSpecifier = function (specifier, node, nodes) { SystemFormatter.prototype.buildRunnerSetters = function (block, hoistDeclarators) { return t.arrayExpression(_.map(this.ids, function (uid, source) { - var nodes = []; + var state = { + nodes: [], + hoistDeclarators: hoistDeclarators + }; traverse(block, { - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node._importSource === source) { if (t.isVariableDeclaration(node)) { _.each(node.declarations, function (declar) { - hoistDeclarators.push(t.variableDeclarator(declar.id)); - nodes.push(t.expressionStatement( + state.hoistDeclarators.push(t.variableDeclarator(declar.id)); + state.nodes.push(t.expressionStatement( t.assignmentExpression("=", declar.id, declar.init) )); }); } else { - nodes.push(node); + state.nodes.push(node); } - this.remove(); + context.remove(); } } - }); + }, null, state); - return t.functionExpression(null, [uid], t.blockStatement(nodes)); + return t.functionExpression(null, [uid], t.blockStatement(state.nodes)); })); }; @@ -112,10 +117,10 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up all variable declarations traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, hoistDeclarators) { if (t.isFunction(node)) { // nothing inside is accessible - return this.skip(); + return context.skip(); } if (t.isVariableDeclaration(node)) { @@ -150,7 +155,8 @@ SystemFormatter.prototype.transform = function (ast) { return nodes; } } - }); + }, null, hoistDeclarators); + if (hoistDeclarators.length) { var hoistDeclar = t.variableDeclaration("var", hoistDeclarators); hoistDeclar._blockHoist = true; @@ -159,19 +165,19 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up function declarations for circular references traverse(block, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); + enter: function (node, parent, scope, context, handlerBody) { + if (t.isFunction(node)) context.skip(); if (t.isFunctionDeclaration(node) || node._blockHoist) { handlerBody.push(node); - this.remove(); + context.remove(); } } - }); + }, null, handlerBody); handlerBody.push(returnStatement); - if (useStrict._has(block)) { + if (useStrict.has(block)) { handlerBody.unshift(block.body.shift()); } diff --git a/lib/6to5/transformation/modules/umd-strict.js b/lib/6to5/transformation/modules/umd-strict.js index c14d6e4076..6104513e81 100644 --- a/lib/6to5/transformation/modules/umd-strict.js +++ b/lib/6to5/transformation/modules/umd-strict.js @@ -1 +1,3 @@ +"use strict"; + module.exports = require("./_strict")(require("./umd")); diff --git a/lib/6to5/transformation/modules/umd.js b/lib/6to5/transformation/modules/umd.js index d0122f4f68..f5c068bb64 100644 --- a/lib/6to5/transformation/modules/umd.js +++ b/lib/6to5/transformation/modules/umd.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = UMDFormatter; var AMDFormatter = require("./amd"); diff --git a/lib/6to5/transformation/templates/rest.js b/lib/6to5/transformation/templates/rest.js index f396f01117..c15ce8815d 100644 --- a/lib/6to5/transformation/templates/rest.js +++ b/lib/6to5/transformation/templates/rest.js @@ -1,3 +1,3 @@ -for (var KEY = START; KEY < ARGUMENTS.length; KEY++) { +for (var LEN = ARGUMENTS.length, ARRAY = Array(ARRAY_LEN), KEY = START; KEY < LEN; KEY++) { ARRAY[ARRAY_KEY] = ARGUMENTS[KEY]; } diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index a3399e7b97..a98b0fe820 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = transform; var Transformer = require("./transformer"); @@ -20,7 +22,7 @@ transform.fromAst = function (ast, code, opts) { }; transform._ensureTransformerNames = function (type, keys) { - for (var i in keys) { + for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (!_.has(transform.transformers, key)) { throw new ReferenceError("unknown transformer " + key + " specified in " + type); @@ -44,8 +46,10 @@ transform.moduleFormatters = { _.each({ specNoForInOfAssignment: require("./transformers/spec-no-for-in-of-assignment"), specSetters: require("./transformers/spec-setters"), + specBlockScopedFunctions: require("./transformers/spec-block-scoped-functions"), // playground + malletOperator: require("./transformers/playground-mallet-operator"), methodBinding: require("./transformers/playground-method-binding"), memoizationOperator: require("./transformers/playground-memoization-operator"), objectGetterMemoization: require("./transformers/playground-object-getter-memoization"), @@ -75,6 +79,7 @@ _.each({ constants: require("./transformers/es6-constants"), letScoping: require("./transformers/es6-let-scoping"), + blockScopingTDZ: require("./transformers/optional-block-scoping-tdz"), _blockHoist: require("./transformers/_block-hoist"), @@ -93,6 +98,7 @@ _.each({ typeofSymbol: require("./transformers/optional-typeof-symbol"), coreAliasing: require("./transformers/optional-core-aliasing"), undefinedToVoid: require("./transformers/optional-undefined-to-void"), + undeclaredVariableCheck: require("./transformers/optional-undeclared-variable-check"), // spec specPropertyLiterals: require("./transformers/spec-property-literals"), diff --git a/lib/6to5/transformation/transformer.js b/lib/6to5/transformation/transformer.js index d9587a38c0..d0a4e0dd0a 100644 --- a/lib/6to5/transformation/transformer.js +++ b/lib/6to5/transformation/transformer.js @@ -1,9 +1,30 @@ +"use strict"; + module.exports = Transformer; var traverse = require("../traverse"); var t = require("../types"); var _ = require("lodash"); +function noop() { } + +function enter(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.enter(node, parent, scope, context, args[0]); +} + +function exit(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.exit(node, parent, scope, context, args[0]); +} + +var traverseOpts = { + enter: enter, + exit: exit +}; + function Transformer(key, transformer, opts) { this.manipulateOptions = transformer.manipulateOptions; this.experimental = !!transformer.experimental; @@ -32,6 +53,9 @@ Transformer.prototype.normalise = function (transformer) { if (!_.isObject(fns)) return; + if (!fns.enter) fns.enter = noop; + if (!fns.exit) fns.exit = noop; + transformer[type] = fns; var aliases = t.FLIPPED_ALIAS_KEYS[type]; @@ -54,28 +78,8 @@ Transformer.prototype.astRun = function (file, key) { }; Transformer.prototype.transform = function (file) { - var transformer = this.transformer; - - var build = function (exit) { - return function (node, parent, scope) { - var fns = transformer[node.type]; - if (!fns) return; - - var fn = fns.enter; - if (exit) fn = fns.exit; - if (!fn) return; - - return fn(node, parent, file, scope); - }; - }; - this.astRun(file, "before"); - - traverse(file.ast, { - enter: build(), - exit: build(true) - }); - + traverse(file.ast, traverseOpts, null, [file, this.transformer]); this.astRun(file, "after"); }; diff --git a/lib/6to5/transformation/transformers/_alias-functions.js b/lib/6to5/transformation/transformers/_alias-functions.js index 5cf9003c84..836bf8f362 100644 --- a/lib/6to5/transformation/transformers/_alias-functions.js +++ b/lib/6to5/transformation/transformers/_alias-functions.js @@ -1,57 +1,64 @@ +"use strict"; + var traverse = require("../../traverse"); var t = require("../../types"); +var functionChildrenTraverser = { + enter: function (node, parent, scope, context, state) { + if (t.isFunction(node) && !node._aliasFunction) { + return context.skip(); + } + + if (node._ignoreAliasFunctions) return context.skip(); + + var getId; + + if (t.isIdentifier(node) && node.name === "arguments") { + getId = state.getArgumentsId; + } else if (t.isThisExpression(node)) { + getId = state.getThisId; + } else { + return; + } + + if (t.isReferenced(node, parent)) return getId(); + } +}; + +var functionTraverser = { + enter: function (node, parent, scope, context, state) { + if (!node._aliasFunction) { + if (t.isFunction(node)) { + // stop traversal of this node as it'll be hit again by this transformer + return context.skip(); + } else { + return; + } + } + + // traverse all child nodes of this function and find `arguments` and `this` + traverse(node, functionChildrenTraverser, null, state); + + return context.skip(); + } +}; + var go = function (getBody, node, file, scope) { var argumentsId; var thisId; - var getArgumentsId = function () { - return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); - }; - - var getThisId = function () { - return thisId = thisId || file.generateUidIdentifier("this", scope); + var state = { + getArgumentsId: function () { + return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); + }, + getThisId: function () { + return thisId = thisId || file.generateUidIdentifier("this", scope); + } }; // traverse the function and find all alias functions so we can alias // `arguments` and `this` if necessary - traverse(node, { - enter: function (node) { - if (!node._aliasFunction) { - if (t.isFunction(node)) { - // stop traversal of this node as it'll be hit again by this transformer - return this.skip(); - } else { - return; - } - } - - // traverse all child nodes of this function and find `arguments` and `this` - traverse(node, { - enter: function (node, parent) { - if (t.isFunction(node) && !node._aliasFunction) { - return this.skip(); - } - - if (node._ignoreAliasFunctions) return this.skip(); - - var getId; - - if (t.isIdentifier(node) && node.name === "arguments") { - getId = getArgumentsId; - } else if (t.isThisExpression(node)) { - getId = getThisId; - } else { - return; - } - - if (t.isReferenced(node, parent)) return getId(); - } - }); - - return this.skip(); - } - }); + traverse(node, functionTraverser, null, state); var body; @@ -71,14 +78,14 @@ var go = function (getBody, node, file, scope) { } }; -exports.Program = function (node, parent, file, scope) { +exports.Program = function (node, parent, scope, context, file) { go(function () { return node.body; }, node, file, scope); }; exports.FunctionDeclaration = -exports.FunctionExpression = function (node, parent, file, scope) { +exports.FunctionExpression = function (node, parent, scope, context, file) { go(function () { t.ensureBlock(node); return node.body.body; diff --git a/lib/6to5/transformation/transformers/_block-hoist.js b/lib/6to5/transformation/transformers/_block-hoist.js index e024a8a193..6e1ab91033 100644 --- a/lib/6to5/transformation/transformers/_block-hoist.js +++ b/lib/6to5/transformation/transformers/_block-hoist.js @@ -1,4 +1,6 @@ -var useStrict = require("./use-strict"); +"use strict"; + +var useStrict = require("../helpers/use-strict"); var _ = require("lodash"); // Priority: @@ -12,13 +14,13 @@ exports.BlockStatement = exports.Program = { exit: function (node) { var hasChange = false; - for (var i in node.body) { + for (var i = 0; i < node.body.length; i++) { var bodyNode = node.body[i]; if (bodyNode && bodyNode._blockHoist != null) hasChange = true; } if (!hasChange) return; - useStrict._wrap(node, function () { + useStrict.wrap(node, function () { var nodePriorities = _.groupBy(node.body, function (bodyNode) { var priority = bodyNode._blockHoist; if (priority == null) priority = 1; diff --git a/lib/6to5/transformation/transformers/_declarations.js b/lib/6to5/transformation/transformers/_declarations.js index 20fbfadb6e..07974b32a2 100644 --- a/lib/6to5/transformation/transformers/_declarations.js +++ b/lib/6to5/transformation/transformers/_declarations.js @@ -1,14 +1,18 @@ -var useStrict = require("./use-strict"); +"use strict"; + +var useStrict = require("../helpers/use-strict"); var t = require("../../types"); exports.secondPass = true; exports.BlockStatement = exports.Program = function (node) { + if (!node._declarations) return; + var kinds = {}; var kind; - useStrict._wrap(node, function () { + useStrict.wrap(node, function () { for (var i in node._declarations) { var declar = node._declarations[i]; diff --git a/lib/6to5/transformation/transformers/_module-formatter.js b/lib/6to5/transformation/transformers/_module-formatter.js index fb7c7d4a90..503543171e 100644 --- a/lib/6to5/transformation/transformers/_module-formatter.js +++ b/lib/6to5/transformation/transformers/_module-formatter.js @@ -1,3 +1,5 @@ +"use strict"; + var transform = require("../transform"); exports.ast = { diff --git a/lib/6to5/transformation/transformers/es6-arrow-functions.js b/lib/6to5/transformation/transformers/es6-arrow-functions.js index 40f9ab9906..e822b42707 100644 --- a/lib/6to5/transformation/transformers/es6-arrow-functions.js +++ b/lib/6to5/transformation/transformers/es6-arrow-functions.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports.ArrowFunctionExpression = function (node) { diff --git a/lib/6to5/transformation/transformers/es6-classes.js b/lib/6to5/transformation/transformers/es6-classes.js index c31d1787b9..e320d4634e 100644 --- a/lib/6to5/transformation/transformers/es6-classes.js +++ b/lib/6to5/transformation/transformers/es6-classes.js @@ -1,12 +1,27 @@ -var traverse = require("../../traverse"); -var util = require("../../util"); -var t = require("../../types"); +"use strict"; -exports.ClassDeclaration = function (node, parent, file, scope) { +var ReplaceSupers = require("../helpers/replace-supers"); +var nameMethod = require("../helpers/name-method"); +var util = require("../../util"); +var t = require("../../types"); + +exports.ClassDeclaration = function (node, parent, scope, context, file) { return new Class(node, file, scope, true).run(); }; -exports.ClassExpression = function (node, parent, file, scope) { +exports.ClassExpression = function (node, parent, scope, context, file) { + if (!node.id) { + if (t.isProperty(parent) && parent.value === node && !parent.computed && t.isIdentifier(parent.key)) { + // var o = { foo: class {} }; + node.id = parent.key; + } + + if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) { + // var foo = class {}; + node.id = parent.id; + } + } + return new Class(node, file, scope, false).run(); }; @@ -119,10 +134,11 @@ Class.prototype.buildBody = function () { var classBody = this.node.body.body; var body = this.body; - for (var i in classBody) { + for (var i = 0; i < classBody.length; i++) { var node = classBody[i]; if (t.isMethodDefinition(node)) { - this.replaceInstanceSuperReferences(node); + var replaceSupers = new ReplaceSupers(node, this.className, this.superName, this.isLoose, this.file); + replaceSupers.replace(); if (node.key.name === "constructor") { this.pushConstructor(node); @@ -179,15 +195,10 @@ Class.prototype.pushMethod = function (node) { var methodName = node.key; var kind = node.kind; - var mutatorMap = this.instanceMutatorMap; - if (node.static) { - this.hasStaticMutators = true; - mutatorMap = this.staticMutatorMap; - } else { - this.hasInstanceMutators = true; - } if (kind === "") { + nameMethod.property(node, this.file, this.scope); + if (this.isLoose) { // use assignments instead of define properties for loose classes @@ -204,194 +215,15 @@ Class.prototype.pushMethod = function (node) { kind = "value"; } - util.pushMutatorMap(mutatorMap, methodName, kind, node.computed, node); -}; - -/** - * Gets a node representing the super class value of the named property. - * - * @example - * - * _get(Object.getPrototypeOf(CLASS.prototype), "METHOD", this) - * - * @param {Node} property - * @param {boolean} isStatic - * @param {boolean} isComputed - * - * @returns {Node} - */ - -Class.prototype.superProperty = function (property, isStatic, isComputed, thisExpression) { - return t.callExpression( - this.file.addHelper("get"), - [ - t.callExpression( - t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")), - [ - isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype")) - ] - ), - isComputed ? property : t.literal(property.name), - thisExpression - ] - ); -}; - -/** - * Description - * - * @param {Object} node - * @param {Object} id - * @param {Object} parent - * @returns {Object} - */ - -Class.prototype.looseSuperProperty = function (methodNode, id, parent) { - var methodName = methodNode.key; - var superName = this.superName || t.identifier("Function"); - - if (parent.property === id) { - return; - } else if (t.isCallExpression(parent, { callee: id })) { - // super(); -> ClassName.prototype.MethodName.call(this); - parent.arguments.unshift(t.thisExpression()); - - if (methodName.name === "constructor") { - // constructor() { super(); } - return t.memberExpression(superName, t.identifier("call")); - } else { - id = superName; - - // foo() { super(); } - if (!methodNode.static) { - id = t.memberExpression(id, t.identifier("prototype")); - } - - id = t.memberExpression(id, methodName, methodNode.computed); - return t.memberExpression(id, t.identifier("call")); - } - } else if (t.isMemberExpression(parent) && !methodNode.static) { - // super.test -> ClassName.prototype.test - return t.memberExpression(superName, t.identifier("prototype")); + var mutatorMap = this.instanceMutatorMap; + if (node.static) { + this.hasStaticMutators = true; + mutatorMap = this.staticMutatorMap; } else { - return superName; - } -}; - -/** - * Replace all `super` references with a reference to our `superClass`. - * - * @param {Node} methodNode MethodDefinition - */ - -Class.prototype.replaceInstanceSuperReferences = function (methodNode) { - var method = methodNode.value; - var self = this; - - var topLevelThisReference; - - traverseLevel(method, true); - - if (topLevelThisReference) { - method.body.body.unshift(t.variableDeclaration("var", [ - t.variableDeclarator(topLevelThisReference, t.thisExpression()) - ])); + this.hasInstanceMutators = true; } - function traverseLevel(node, topLevel) { - traverse(node, { - enter: function (node, parent) { - if (t.isFunction(node) && !t.isArrowFunctionExpression(node)) { - // we need to call traverseLevel again so we're context aware - traverseLevel(node, false); - return this.skip(); - } - - var getThisReference = function () { - if (topLevel) { - // top level so `this` is the instance - return t.thisExpression(); - } else { - // not in the top level so we need to create a reference - return topLevelThisReference = topLevelThisReference || self.file.generateUidIdentifier("this"); - } - }; - - var callback = specHandle; - if (self.isLoose) callback = looseHandle; - return callback(getThisReference, node, parent); - } - }); - } - - function looseHandle(getThisReference, node, parent) { - if (t.isIdentifier(node, { name: "super" })) { - return self.looseSuperProperty(methodNode, node, parent); - } else if (t.isCallExpression(node)) { - var callee = node.callee; - if (!t.isMemberExpression(callee)) return; - if (callee.object.name !== "super") return; - - // super.test(); -> ClassName.prototype.MethodName.call(this); - t.appendToMemberExpression(callee, t.identifier("call")); - node.arguments.unshift(getThisReference()); - } - } - - function specHandle(getThisReference, node, parent) { - var property; - var computed; - var args; - - if (t.isIdentifier(node, { name: "super" })) { - if (!(t.isMemberExpression(parent) && !parent.computed && parent.property === node)) { - throw self.file.errorWithNode(node, "illegal use of bare super"); - } - } else if (t.isCallExpression(node)) { - var callee = node.callee; - if (t.isIdentifier(callee, { name: "super" })) { - // super(); -> _get(Object.getPrototypeOf(ClassName), "MethodName", this).call(this); - property = methodNode.key; - computed = methodNode.computed; - args = node.arguments; - } else { - if (!t.isMemberExpression(callee)) return; - if (callee.object.name !== "super") return; - - // super.test(); -> _get(Object.getPrototypeOf(ClassName.prototype), "test", this).call(this); - property = callee.property; - computed = callee.computed; - args = node.arguments; - } - } else if (t.isMemberExpression(node)) { - if (!t.isIdentifier(node.object, { name: "super" })) return; - - // super.name; -> _get(Object.getPrototypeOf(ClassName.prototype), "name", this); - property = node.property; - computed = node.computed; - } - - if (!property) return; - - var thisReference = getThisReference(); - var superProperty = self.superProperty(property, methodNode.static, computed, thisReference); - if (args) { - if (args.length === 1 && t.isSpreadElement(args[0])) { - // super(...arguments); - return t.callExpression( - t.memberExpression(superProperty, t.identifier("apply")), - [thisReference, args[0].argument] - ); - } else { - return t.callExpression( - t.memberExpression(superProperty, t.identifier("call")), - [thisReference].concat(args) - ); - } - } else { - return superProperty; - } - } + util.pushMutatorMap(mutatorMap, methodName, kind, node.computed, node); }; /** diff --git a/lib/6to5/transformation/transformers/es6-computed-property-names.js b/lib/6to5/transformation/transformers/es6-computed-property-names.js index 94d4bd9e86..9d08092830 100644 --- a/lib/6to5/transformation/transformers/es6-computed-property-names.js +++ b/lib/6to5/transformation/transformers/es6-computed-property-names.js @@ -1,9 +1,11 @@ +"use strict"; + var t = require("../../types"); -exports.ObjectExpression = function (node, parent, file, scope) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasComputed = false; - for (var i in node.properties) { + for (var i = 0; i < node.properties.length; i++) { hasComputed = t.isProperty(node.properties[i], { computed: true, kind: "init" }); if (hasComputed) break; } @@ -39,7 +41,7 @@ exports.ObjectExpression = function (node, parent, file, scope) { }; var loose = function (node, body, objId) { - for (var i in node.properties) { + for (var i = 0; i < node.properties.length; i++) { var prop = node.properties[i]; body.push(t.expressionStatement( @@ -58,7 +60,7 @@ var spec = function (node, body, objId, initProps, file) { // normalise key - for (var i in props) { + for (var i = 0; i < props.length; i++) { prop = props[i]; if (prop.kind !== "init") continue; @@ -73,7 +75,7 @@ var spec = function (node, body, objId, initProps, file) { var broken = false; - for (i in props) { + for (i = 0; i < props.length; i++) { prop = props[i]; if (prop.computed) { @@ -89,7 +91,7 @@ var spec = function (node, body, objId, initProps, file) { // add a simple assignment for all Symbol member expressions due to symbol polyfill limitations // otherwise use Object.defineProperty - for (i in props) { + for (i = 0; i < props.length; i++) { prop = props[i]; if (!prop) continue; diff --git a/lib/6to5/transformation/transformers/es6-constants.js b/lib/6to5/transformation/transformers/es6-constants.js index a3021188fb..7be895f0f2 100644 --- a/lib/6to5/transformation/transformers/es6-constants.js +++ b/lib/6to5/transformation/transformers/es6-constants.js @@ -1,3 +1,5 @@ +"use strict"; + var traverse = require("../../traverse"); var t = require("../../types"); var _ = require("lodash"); @@ -6,7 +8,7 @@ exports.Program = exports.BlockStatement = exports.ForInStatement = exports.ForOfStatement = -exports.ForStatement = function (node, parent, file) { +exports.ForStatement = function (node, parent, scope, context, file) { var hasConstants = false; var constants = {}; @@ -44,7 +46,7 @@ exports.ForStatement = function (node, parent, file) { } if (t.isVariableDeclaration(child, { kind: "const" })) { - for (var i in child.declarations) { + for (var i = 0; i < child.declarations.length; i++) { var declar = child.declarations[i]; var ids = getIds(declar); @@ -69,14 +71,19 @@ exports.ForStatement = function (node, parent, file) { if (!hasConstants) return; + var state = { + check: check, + getIds: getIds + }; + traverse(node, { - enter: function (child, parent, scope) { + enter: function (child, parent, scope, context, state) { if (child._ignoreConstant) return; if (t.isVariableDeclaration(child)) return; if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) { - check(parent, getIds(child), scope); + state.check(parent, state.getIds(child), scope); } } - }); + }, null, state); }; diff --git a/lib/6to5/transformation/transformers/es6-default-parameters.js b/lib/6to5/transformation/transformers/es6-default-parameters.js index db82d8e2d4..af1b163e5b 100644 --- a/lib/6to5/transformation/transformers/es6-default-parameters.js +++ b/lib/6to5/transformation/transformers/es6-default-parameters.js @@ -1,8 +1,10 @@ +"use strict"; + var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { if (!node.defaults || !node.defaults.length) return; t.ensureBlock(node); @@ -11,12 +13,11 @@ exports.Function = function (node, parent, file, scope) { }); var iife = false; - var i; var def; var checkTDZ = function (ids) { var check = function (node, parent) { - if (!t.isIdentifier(node) || !t.isReferenced(node, parent)) return; + if (!t.isReferencedIdentifier(node, parent)) return; if (ids.indexOf(node.name) >= 0) { throw file.errorWithNode(node, "Temporal dead zone - accessing a variable before it's initialized"); @@ -31,7 +32,7 @@ exports.Function = function (node, parent, file, scope) { traverse(def, { enter: check }); }; - for (i in node.defaults) { + for (var i = 0; i < node.defaults.length; i++) { def = node.defaults[i]; if (!def) continue; @@ -40,12 +41,12 @@ exports.Function = function (node, parent, file, scope) { // temporal dead zone check - here we prevent accessing of params that // are to the right - ie. uninitialized parameters var rightIds = ids.slice(i); - for (i in rightIds) { - checkTDZ(rightIds[i]); + for (var i2 = 0; i2 < rightIds.length; i2++) { + checkTDZ(rightIds[i2]); } // we're accessing a variable that's already defined within this function - var has = scope.get(param.name); + var has = scope.get(param.name, true); if (has && node.params.indexOf(has) < 0) { iife = true; } @@ -58,7 +59,7 @@ exports.Function = function (node, parent, file, scope) { var lastNonDefaultParam = 0; - for (i in node.defaults) { + for (i = 0; i < node.defaults.length; i++) { def = node.defaults[i]; if (!def) { lastNonDefaultParam = +i + 1; diff --git a/lib/6to5/transformation/transformers/es6-destructuring.js b/lib/6to5/transformation/transformers/es6-destructuring.js index e92e3194a5..3a3e27c4b5 100644 --- a/lib/6to5/transformation/transformers/es6-destructuring.js +++ b/lib/6to5/transformation/transformers/es6-destructuring.js @@ -1,3 +1,5 @@ +"use strict"; + // TODO: Clean up var t = require("../../types"); @@ -7,7 +9,7 @@ var buildVariableAssign = function (opts, id, init) { if (t.isMemberExpression(id)) op = "="; if (op) { - return t.expressionStatement(t.assignmentExpression("=", id, init)); + return t.expressionStatement(t.assignmentExpression(op, id, init)); } else { return t.variableDeclaration(opts.kind, [ t.variableDeclarator(id, init) @@ -46,12 +48,12 @@ var pushAssignmentPattern = function (opts, nodes, pattern, parentId) { }; var pushObjectPattern = function (opts, nodes, pattern, parentId) { - for (var i in pattern.properties) { + for (var i = 0; i < pattern.properties.length; i++) { var prop = pattern.properties[i]; if (t.isSpreadProperty(prop)) { // get all the keys that appear in this object before the current spread var keys = []; - for (var i2 in pattern.properties) { + for (var i2 = 0; i2 < pattern.properties.length; i2++) { var prop2 = pattern.properties[i2]; if (i2 >= i) break; @@ -88,7 +90,7 @@ var pushArrayPattern = function (opts, nodes, pattern, parentId) { var i; var hasSpreadElement = false; - for (i in pattern.elements) { + for (i = 0; i < pattern.elements.length; i++) { if (t.isSpreadElement(pattern.elements[i])) { hasSpreadElement = true; break; @@ -103,7 +105,7 @@ var pushArrayPattern = function (opts, nodes, pattern, parentId) { ])); parentId = _parentId; - for (i in pattern.elements) { + for (i = 0; i < pattern.elements.length; i++) { var elem = pattern.elements[i]; if (!elem) continue; @@ -148,7 +150,7 @@ var pushPattern = function (opts) { }; exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var declar = node.left; if (!t.isVariableDeclaration(declar)) return; @@ -174,7 +176,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { var nodes = []; var hasDestructuring = false; @@ -205,7 +207,7 @@ exports.Function = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.CatchClause = function (node, parent, file, scope) { +exports.CatchClause = function (node, parent, scope, context, file) { var pattern = node.param; if (!t.isPattern(pattern)) return; @@ -223,7 +225,7 @@ exports.CatchClause = function (node, parent, file, scope) { node.body.body = nodes.concat(node.body.body); }; -exports.ExpressionStatement = function (node, parent, file, scope) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (expr.type !== "AssignmentExpression") return; @@ -245,14 +247,13 @@ exports.ExpressionStatement = function (node, parent, file, scope) { return nodes; }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (parent.type === "ExpressionStatement") return; if (!t.isPattern(node.left)) return; - var tempName = file.generateUid("temp", scope); - var ref = t.identifier(tempName); + var ref = file.generateUidIdentifier("temp", scope); scope.push({ - key: tempName, + key: ref.name, id: ref }); @@ -270,7 +271,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes, scope); }; -exports.VariableDeclaration = function (node, parent, file, scope) { +exports.VariableDeclaration = function (node, parent, scope, context, file) { if (t.isForInStatement(parent) || t.isForOfStatement(parent)) return; var nodes = []; @@ -278,7 +279,7 @@ exports.VariableDeclaration = function (node, parent, file, scope) { var declar; var hasPattern = false; - for (i in node.declarations) { + for (i = 0; i < node.declarations.length; i++) { declar = node.declarations[i]; if (t.isPattern(declar.id)) { hasPattern = true; @@ -287,7 +288,7 @@ exports.VariableDeclaration = function (node, parent, file, scope) { } if (!hasPattern) return; - for (i in node.declarations) { + for (i = 0; i < node.declarations.length; i++) { declar = node.declarations[i]; var patternId = declar.init; @@ -317,7 +318,7 @@ exports.VariableDeclaration = function (node, parent, file, scope) { if (!t.isProgram(parent) && !t.isBlockStatement(parent)) { declar = null; - for (i in nodes) { + for (i = 0; i < nodes.length; i++) { node = nodes[i]; declar = declar || t.variableDeclaration(node.kind, []); diff --git a/lib/6to5/transformation/transformers/es6-for-of.js b/lib/6to5/transformation/transformers/es6-for-of.js index 61357cfbad..3eed21d15d 100644 --- a/lib/6to5/transformation/transformers/es6-for-of.js +++ b/lib/6to5/transformation/transformers/es6-for-of.js @@ -1,11 +1,13 @@ +"use strict"; + var util = require("../../util"); var t = require("../../types"); -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var callback = spec; if (file.isLoose("forOf")) callback = loose; - var build = callback(node, parent, file, scope); + var build = callback(node, parent, scope, context, file); var declar = build.declar; var loop = build.loop; var block = loop.body; @@ -31,7 +33,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { return loop; }; -var loose = function (node, parent, file, scope) { +var loose = function (node, parent, scope, context, file) { var left = node.left; var declar, id; @@ -63,7 +65,7 @@ var loose = function (node, parent, file, scope) { }; }; -var spec = function (node, parent, file, scope) { +var spec = function (node, parent, scope, context, file) { var left = node.left; var declar; diff --git a/lib/6to5/transformation/transformers/es6-generators.js b/lib/6to5/transformation/transformers/es6-generators.js index caeeec5ef6..6d8124b709 100644 --- a/lib/6to5/transformation/transformers/es6-generators.js +++ b/lib/6to5/transformation/transformers/es6-generators.js @@ -1,3 +1,5 @@ +"use strict"; + var regenerator = require("regenerator"); exports.ast = { diff --git a/lib/6to5/transformation/transformers/es6-let-scoping.js b/lib/6to5/transformation/transformers/es6-let-scoping.js index a95c1b3a30..4d0f13910e 100644 --- a/lib/6to5/transformation/transformers/es6-let-scoping.js +++ b/lib/6to5/transformation/transformers/es6-let-scoping.js @@ -1,3 +1,5 @@ +"use strict"; + var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); @@ -25,7 +27,7 @@ var isVar = function (node, parent) { }; var standardiseLets = function (declars) { - for (var i in declars) { + for (var i = 0; i < declars.length; i++) { delete declars[i]._let; } }; @@ -34,7 +36,7 @@ exports.VariableDeclaration = function (node, parent) { isLet(node, parent); }; -exports.Loop = function (node, parent, file, scope) { +exports.Loop = function (node, parent, scope, context, file) { var init = node.left || node.init; if (isLet(init, node)) { t.ensureBlock(node); @@ -46,7 +48,7 @@ exports.Loop = function (node, parent, file, scope) { node.label = parent.label; } - var letScoping = new LetScoping(node, node.body, parent, file, scope); + var letScoping = new LetScoping(node, node.body, parent, scope, file); letScoping.run(); if (node.label && !t.isLabeledStatement(parent)) { @@ -55,9 +57,10 @@ exports.Loop = function (node, parent, file, scope) { } }; -exports.BlockStatement = function (block, parent, file, scope) { +exports.Program = +exports.BlockStatement = function (block, parent, scope, context, file) { if (!t.isLoop(parent)) { - var letScoping = new LetScoping(false, block, parent, file, scope); + var letScoping = new LetScoping(false, block, parent, scope, file); letScoping.run(); } }; @@ -68,19 +71,21 @@ exports.BlockStatement = function (block, parent, file, scope) { * @param {Boolean|Node} loopParent * @param {Node} block * @param {Node} parent - * @param {File} file * @param {Scope} scope + * @param {File} file */ -function LetScoping(loopParent, block, parent, file, scope) { +function LetScoping(loopParent, block, parent, scope, file) { this.loopParent = loopParent; this.parent = parent; this.scope = scope; this.block = block; this.file = file; - this.letReferences = {}; - this.body = []; + this.outsideLetReferences = {}; + this.hasLetReferences = false; + this.letReferences = block._letReferences = {}; + this.body = []; } /** @@ -89,27 +94,94 @@ function LetScoping(loopParent, block, parent, file, scope) { LetScoping.prototype.run = function () { var block = this.block; - if (block._letDone) return; block._letDone = true; - this.info = this.getInfo(); + var needsClosure = this.getLetReferences(); - // remap all let references that exist in upper scopes to their uid - this.remap(); + // this is a block within a `Function/Program` so we can safely leave it be + if (t.isFunction(this.parent) || t.isProgram(this.block)) return; - // this is a block within a `Function` so we can safely leave it be - if (t.isFunction(this.parent)) return this.noClosure(); + // we can skip everything + if (!this.hasLetReferences) return; - // this block has no let references so let's clean up - if (!this.info.keys.length) return this.noClosure(); + if (needsClosure) { + this.needsClosure(); + } else { + this.remap(); + } +}; - // returns whether or not there are any outside let references within any - // functions - var referencesInClosure = this.getLetReferences(); +/** + * Description + */ - // no need for a closure so let's clean up - if (!referencesInClosure) return this.noClosure(); +LetScoping.prototype.remap = function () { + var letRefs = this.letReferences; + var scope = this.scope; + var file = this.file; + + // alright, so since we aren't wrapping this block in a closure + // we have to check if any of our let variables collide with + // those in upper scopes and then if they do, generate a uid + // for them and replace all references with it + var remaps = {}; + + for (var key in letRefs) { + // just an Identifier node we collected in `getLetReferences` + // this is the defining identifier of a declaration + var ref = letRefs[key]; + + if (scope.parentHas(key)) { + var uid = file.generateUidIdentifier(ref.name, scope).name; + ref.name = uid; + + remaps[key] = remaps[uid] = { + node: ref, + uid: uid + }; + } + } + + // + + var replace = function (node, parent, scope, context, remaps) { + if (!t.isReferencedIdentifier(node, parent)) return; + + var remap = remaps[node.name]; + if (!remap) return; + + var own = scope.get(node.name, true); + if (own === remap.node) { + node.name = remap.uid; + } else { + // scope already has it's own declaration that doesn't + // match the one we have a stored replacement for + if (context) context.skip(); + } + }; + + var traverseReplace = function (node, parent) { + replace(node, parent, scope, null, remaps); + traverse(node, { enter: replace }, scope, remaps); + }; + + var loopParent = this.loopParent; + if (loopParent) { + traverseReplace(loopParent.right, loopParent); + traverseReplace(loopParent.test, loopParent); + traverseReplace(loopParent.update, loopParent); + } + + traverse(this.block, { enter: replace }, scope, remaps); +}; + +/** + * Description + */ + +LetScoping.prototype.needsClosure = function () { + var block = this.block; // if we're inside of a for loop then we search to see if there are any // `break`s, `continue`s, `return`s etc @@ -118,22 +190,16 @@ LetScoping.prototype.run = function () { // hoist var references to retain scope this.hoistVarDeclarations(); - // set let references to plain var references - standardiseLets(this.info.declarators); - - // turn letReferences into an array - var letReferences = _.values(this.letReferences); + // turn outsideLetReferences into an array + var params = _.values(this.outsideLetReferences); // build the closure that we're going to wrap the block with - var fn = t.functionExpression(null, letReferences, t.blockStatement(block.body)); + var fn = t.functionExpression(null, params, t.blockStatement(block.body)); fn._aliasFunction = true; // replace the current block body with the one we're going to build block.body = this.body; - // change upper scope references with their uid if they have one - var params = this.getParams(letReferences); - // build a call and a unique id that we can assign the return value to var call = t.callExpression(fn, params); var ret = this.file.generateUidIdentifier("ret", this.scope); @@ -147,117 +213,78 @@ LetScoping.prototype.run = function () { this.build(ret, call); }; -/** - * There are no let references accessed within a closure so we can just turn the - * lets into vars. - */ - -LetScoping.prototype.noClosure = function () { - standardiseLets(this.info.declarators); -}; - -/** - * Traverse through block and replace all references that exist in a higher - * scope to their uids. - */ - -LetScoping.prototype.remap = function () { - var replacements = this.info.duplicates; - var block = this.block; - - if (!this.info.hasDuplicates) return; - - var replace = function (node, parent, scope) { - if (!t.isIdentifier(node)) return; - if (!t.isReferenced(node, parent)) return; - if (scope && scope.hasOwn(node.name)) return; - node.name = replacements[node.name] || node.name; - }; - - var traverseReplace = function (node, parent) { - replace(node, parent); - traverse(node, { enter: replace }); - }; - - var loopParent = this.loopParent; - if (loopParent) { - traverseReplace(loopParent.right, loopParent); - traverseReplace(loopParent.test, loopParent); - traverseReplace(loopParent.update, loopParent); - } - - traverse(block, { enter: replace }); -}; - /** * Description - * - * @returns {Object} */ -LetScoping.prototype.getInfo = function () { +LetScoping.prototype.getLetReferences = function () { var block = this.block; - var scope = this.scope; - var file = this.file; - var opts = { - // array of `Identifier` names of let variables that appear lexically out of - // this scope but should be accessible - eg. `ForOfStatement`.left - outsideKeys: [], - - // array of let `VariableDeclarator`s that are a part of this block - declarators: block._letDeclars || [], - - // object of duplicate ids and their aliases - if there's an `Identifier` - // name that's used in an upper scope we generate a unique id and replace - // all references with it - duplicates: {}, - - hasDuplicates: false, - - // array of `Identifier` names of let variables that are accessible within - // the current block - keys: [] - }; - - var duplicates = function (id, key) { - var has = scope.parentGet(key); - - if (has && has !== id) { - // there's a variable with this exact name in an upper scope so we need - // to generate a new name - opts.duplicates[key] = id.name = file.generateUid(key, scope); - opts.hasDuplicates = true; - } - }; - - var i; + var declarators = block._letDeclars || []; var declar; - for (i in opts.declarators) { - declar = opts.declarators[i]; - opts.declarators.push(declar); - - var keys = t.getIds(declar, true); - _.each(keys, duplicates); - keys = Object.keys(keys); - - opts.outsideKeys = opts.outsideKeys.concat(keys); - opts.keys = opts.keys.concat(keys); + // + for (var i = 0; i < declarators.length; i++) { + declar = declarators[i]; + _.extend(this.outsideLetReferences, t.getIds(declar, true)); } - for (i in block.body) { - declar = block.body[i]; - if (!isLet(declar, block)) continue; - - var declars = t.getIds(declar, true); - for (var key in declars) { - duplicates(declars[key], key); - opts.keys.push(key); + // + if (block.body) { + for (i = 0; i < block.body.length; i++) { + declar = block.body[i]; + if (isLet(declar, block)) { + declarators = declarators.concat(declar.declarations); + } } } - return opts; + // + for (i = 0; i < declarators.length; i++) { + declar = declarators[i]; + var keys = t.getIds(declar, true); + _.extend(this.letReferences, keys); + this.hasLetReferences = true; + } + + // no let references so we can just quit + if (!this.hasLetReferences) return; + + // set let references to plain var references + standardiseLets(declarators); + + var state = { + letReferences: this.letReferences, + closurify: false + }; + + // traverse through this block, stopping on functions and checking if they + // contain any local let references + traverse(this.block, { + enter: function (node, parent, scope, context, state) { + if (t.isFunction(node)) { + traverse(node, { + enter: function (node, parent) { + // not a direct reference + if (!t.isReferencedIdentifier(node, parent)) return; + + // this scope has a variable with the same name so it couldn't belong + // to our let scope + if (scope.hasOwn(node.name, true)) return; + + // not a part of our scope + if (!state.letReferences[node.name]) return; + + state.closurify = true; + } + }, scope, state); + + return context.skip(); + } + } + }, this.scope, state); + + return state.closurify; }; /** @@ -277,11 +304,11 @@ LetScoping.prototype.checkLoop = function () { }; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var replace; if (t.isFunction(node) || t.isLoop(node)) { - return this.skip(); + return context.skip(); } if (node && !node.label) { @@ -305,7 +332,7 @@ LetScoping.prototype.checkLoop = function () { if (replace) return t.inherits(replace, node); } - }); + }, this.scope, has); return has; }; @@ -316,9 +343,8 @@ LetScoping.prototype.checkLoop = function () { */ LetScoping.prototype.hoistVarDeclarations = function () { - var self = this; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context, self) { if (t.isForStatement(node)) { if (isVar(node.init, node)) { node.init = t.sequenceExpression(self.pushDeclar(node.init)); @@ -330,71 +356,10 @@ LetScoping.prototype.hoistVarDeclarations = function () { } else if (isVar(node, parent)) { return self.pushDeclar(node).map(t.expressionStatement); } else if (t.isFunction(node)) { - return this.skip(); + return context.skip(); } } - }); -}; - -/** - * Build up a parameter list that we'll call our closure wrapper with, replacing - * all duplicate ids with their uid. - * - * @param {Array} params - * @returns {Array} - */ - -LetScoping.prototype.getParams = function (params) { - var info = this.info; - params = _.cloneDeep(params); - _.each(params, function (param) { - param.name = info.duplicates[param.name] || param.name; - }); - return params; -}; - -/** - * Get all let references within this block. Stopping whenever we reach another - * block. - */ - -LetScoping.prototype.getLetReferences = function () { - var closurify = false; - var self = this; - - // traverse through this block, stopping on functions and checking if they - // contain any outside let references - traverse(this.block, { - enter: function (node, parent, scope) { - if (t.isFunction(node)) { - traverse(node, { - enter: function (node, parent) { - // not an identifier so we have no use - if (!t.isIdentifier(node)) return; - - // not a direct reference - if (!t.isReferenced(node, parent)) return; - - // this scope has a variable with the same name so it couldn't belong - // to our let scope - if (scope.hasOwn(node.name, true)) return; - - closurify = true; - - // this key doesn't appear just outside our scope - if (!_.contains(self.info.outsideKeys, node.name)) return; - - // push this badboy - self.letReferences[node.name] = node; - } - }); - - return this.skip(); - } - } - }); - - return closurify; + }, this.scope, this); }; /** @@ -412,7 +377,7 @@ LetScoping.prototype.pushDeclar = function (node) { var replace = []; - for (var i in node.declarations) { + for (var i = 0; i < node.declarations.length; i++) { var declar = node.declarations[i]; if (!declar.init) continue; diff --git a/lib/6to5/transformation/transformers/es6-modules.js b/lib/6to5/transformation/transformers/es6-modules.js index b8c1b4964c..b59b191323 100644 --- a/lib/6to5/transformation/transformers/es6-modules.js +++ b/lib/6to5/transformation/transformers/es6-modules.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports.ast = { @@ -6,11 +8,11 @@ exports.ast = { } }; -exports.ImportDeclaration = function (node, parent, file) { +exports.ImportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.specifiers.length) { - for (var i in node.specifiers) { + for (var i = 0; i < node.specifiers.length; i++) { file.moduleFormatter.importSpecifier(node.specifiers[i], node, nodes, parent); } } else { @@ -26,7 +28,7 @@ exports.ImportDeclaration = function (node, parent, file) { return nodes; }; -exports.ExportDeclaration = function (node, parent, file) { +exports.ExportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.declaration) { @@ -39,7 +41,7 @@ exports.ExportDeclaration = function (node, parent, file) { file.moduleFormatter.exportDeclaration(node, nodes, parent); } else { - for (var i in node.specifiers) { + for (var i = 0; i < node.specifiers.length; i++) { file.moduleFormatter.exportSpecifier(node.specifiers[i], node, nodes, parent); } } diff --git a/lib/6to5/transformation/transformers/es6-property-method-assignment.js b/lib/6to5/transformation/transformers/es6-property-method-assignment.js index b826f6977d..4a740e2f83 100644 --- a/lib/6to5/transformation/transformers/es6-property-method-assignment.js +++ b/lib/6to5/transformation/transformers/es6-property-method-assignment.js @@ -1,49 +1,15 @@ -var traverse = require("../../traverse"); -var util = require("../../util"); -var t = require("../../types"); +"use strict"; -exports.Property = function (node, parent, file, scope) { +var nameMethod = require("../helpers/name-method"); +var util = require("../../util"); +var t = require("../../types"); + +exports.Property = function (node, parent, scope, context, file) { if (!node.method) return; node.method = false; - var key = t.toComputedKey(node, node.key); - if (!t.isLiteral(key)) return; // we can't set a function id with this - - var id = t.toIdentifier(key.value); - key = t.identifier(id); - - var selfReference = false; - var outerDeclar = scope.get(id, true); - - traverse(node, { - enter: function (node, parent, scope) { - // check if this node is an identifier that matches the same as our function id - if (!t.isIdentifier(node, { name: id })) return; - - // check if this node is the one referenced - if (!t.isReferenced(node, parent)) return; - - // check that we don't have a local variable declared as that removes the need - // for the wrapper - var localDeclar = scope.get(id, true); - if (localDeclar !== outerDeclar) return; - - selfReference = true; - this.stop(); - } - }, scope); - - if (selfReference) { - node.value = util.template("property-method-assignment-wrapper", { - FUNCTION: node.value, - FUNCTION_ID: key, - FUNCTION_KEY: file.generateUidIdentifier(id, scope), - WRAPPER_KEY: file.generateUidIdentifier(id + "Wrapper", scope) - }); - } else { - node.value.id = key; - } + nameMethod.property(node, file, scope); }; exports.ObjectExpression = function (node) { diff --git a/lib/6to5/transformation/transformers/es6-property-name-shorthand.js b/lib/6to5/transformation/transformers/es6-property-name-shorthand.js index 8614d69c84..006fc9325b 100644 --- a/lib/6to5/transformation/transformers/es6-property-name-shorthand.js +++ b/lib/6to5/transformation/transformers/es6-property-name-shorthand.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); diff --git a/lib/6to5/transformation/transformers/es6-rest-parameters.js b/lib/6to5/transformation/transformers/es6-rest-parameters.js index 266b6f62f7..89bafc063a 100644 --- a/lib/6to5/transformation/transformers/es6-rest-parameters.js +++ b/lib/6to5/transformation/transformers/es6-rest-parameters.js @@ -1,36 +1,55 @@ +"use strict"; + var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.rest) return; var rest = node.rest; - delete node.rest; + node.rest = null; t.ensureBlock(node); var argsId = t.identifier("arguments"); + + // otherwise `arguments` will be remapped in arrow functions argsId._ignoreAliasFunctions = true; var start = t.literal(node.params.length); var key = file.generateUidIdentifier("key"); + var len = file.generateUidIdentifier("len"); var arrKey = key; + var arrLen = len; if (node.params.length) { - arrKey = t.binaryExpression("-", arrKey, start); + // this method has additional params, so we need to subtract + // the index of the current argument position from the + // position in the array that we want to populate + arrKey = t.binaryExpression("-", key, start); + + // we need to work out the size of the array that we're + // going to store all the rest parameters + // + // we need to add a check to avoid constructing the array + // with <0 if there are less arguments than params as it'll + // cause an error + arrLen = t.conditionalExpression( + t.binaryExpression(">", len, start), + t.binaryExpression("-", len, start), + t.literal(0) + ); } node.body.body.unshift( - t.variableDeclaration("var", [ - t.variableDeclarator(rest, t.arrayExpression([])) - ]), - util.template("rest", { ARGUMENTS: argsId, ARRAY_KEY: arrKey, + ARRAY_LEN: arrLen, START: start, ARRAY: rest, - KEY: key + KEY: key, + LEN: len, }) ); }; diff --git a/lib/6to5/transformation/transformers/es6-spread.js b/lib/6to5/transformation/transformers/es6-spread.js index 5a7b031ce1..fff975918c 100644 --- a/lib/6to5/transformation/transformers/es6-spread.js +++ b/lib/6to5/transformation/transformers/es6-spread.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); @@ -6,7 +8,7 @@ var getSpreadLiteral = function (spread, file) { }; var hasSpread = function (nodes) { - for (var i in nodes) { + for (var i = 0; i < nodes.length; i++) { if (t.isSpreadElement(nodes[i])) { return true; } @@ -25,7 +27,7 @@ var build = function (props, file) { _props = []; }; - for (var i in props) { + for (var i = 0; i < props.length; i++) { var prop = props[i]; if (t.isSpreadElement(prop)) { push(); @@ -40,7 +42,7 @@ var build = function (props, file) { return nodes; }; -exports.ArrayExpression = function (node, parent, file) { +exports.ArrayExpression = function (node, parent, scope, context, file) { var elements = node.elements; if (!hasSpread(elements)) return; @@ -55,7 +57,7 @@ exports.ArrayExpression = function (node, parent, file) { return t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; @@ -95,7 +97,7 @@ exports.CallExpression = function (node, parent, file, scope) { node.arguments.unshift(contextLiteral); }; -exports.NewExpression = function (node, parent, file) { +exports.NewExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; diff --git a/lib/6to5/transformation/transformers/es6-template-literals.js b/lib/6to5/transformation/transformers/es6-template-literals.js index 5e4f824cf3..682fbbe85b 100644 --- a/lib/6to5/transformation/transformers/es6-template-literals.js +++ b/lib/6to5/transformation/transformers/es6-template-literals.js @@ -1,17 +1,19 @@ +"use strict"; + var t = require("../../types"); var buildBinaryExpression = function (left, right) { return t.binaryExpression("+", left, right); }; -exports.TaggedTemplateExpression = function (node, parent, file) { +exports.TaggedTemplateExpression = function (node, parent, scope, context, file) { var args = []; var quasi = node.quasi; var strings = []; var raw = []; - for (var i in quasi.quasis) { + for (var i = 0; i < quasi.quasis.length; i++) { var elem = quasi.quasis[i]; strings.push(t.literal(elem.value.cooked)); raw.push(t.literal(elem.value.raw)); @@ -33,7 +35,7 @@ exports.TemplateLiteral = function (node) { var nodes = []; var i; - for (i in node.quasis) { + for (i = 0; i < node.quasis.length; i++) { var elem = node.quasis[i]; nodes.push(t.literal(elem.value.cooked)); @@ -49,7 +51,7 @@ exports.TemplateLiteral = function (node) { var root = buildBinaryExpression(nodes.shift(), nodes.shift()); - for (i in nodes) { + for (i = 0; i < nodes.length; i++) { root = buildBinaryExpression(root, nodes[i]); } diff --git a/lib/6to5/transformation/transformers/es6-unicode-regex.js b/lib/6to5/transformation/transformers/es6-unicode-regex.js index d85bdf4e3b..1c1cf36089 100644 --- a/lib/6to5/transformation/transformers/es6-unicode-regex.js +++ b/lib/6to5/transformation/transformers/es6-unicode-regex.js @@ -1,3 +1,5 @@ +"use strict"; + var rewritePattern = require("regexpu/rewrite-pattern"); var _ = require("lodash"); diff --git a/lib/6to5/transformation/transformers/es7-abstract-references.js b/lib/6to5/transformation/transformers/es7-abstract-references.js index 89bc7c05e4..f6c7eefb7e 100644 --- a/lib/6to5/transformation/transformers/es7-abstract-references.js +++ b/lib/6to5/transformation/transformers/es7-abstract-references.js @@ -1,3 +1,5 @@ +"use strict"; + // https://github.com/zenparsing/es-abstract-refs var util = require("../../util"); @@ -21,7 +23,7 @@ var container = function (parent, call, ret) { } }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { var left = node.left; if (!t.isVirtualPropertyExpression(left)) return; @@ -74,7 +76,7 @@ exports.UnaryExpression = function (node, parent) { return container(parent, call, t.literal(true)); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var callee = node.callee; if (!t.isVirtualPropertyExpression(callee)) return; diff --git a/lib/6to5/transformation/transformers/es7-array-comprehension.js b/lib/6to5/transformation/transformers/es7-array-comprehension.js index a988678d93..46b537f5f9 100644 --- a/lib/6to5/transformation/transformers/es7-array-comprehension.js +++ b/lib/6to5/transformation/transformers/es7-array-comprehension.js @@ -1,10 +1,13 @@ -var traverse = require("../../traverse"); -var util = require("../../util"); -var t = require("../../types"); +"use strict"; + +var buildComprehension = require("../helpers/build-comprehension"); +var traverse = require("../../traverse"); +var util = require("../../util"); +var t = require("../../types"); exports.experimental = true; -var build = function (node, parent, file, scope) { +var build = function (node, parent, scope, context, file) { var uid = scope.generateUidBasedOnNode(parent, file); var container = util.template("array-comprehension-container", { @@ -22,7 +25,7 @@ var build = function (node, parent, file, scope) { var returnStatement = body.pop(); - body.push(exports._build(node, function () { + body.push(buildComprehension(node, function () { return util.template("array-push", { STATEMENT: node.body, KEY: uid @@ -33,30 +36,8 @@ var build = function (node, parent, file, scope) { return container; }; -exports._build = function (node, buildBody) { - var self = node.blocks.shift(); - if (!self) return; - - var child = exports._build(node, buildBody); - if (!child) { - // last item - child = buildBody(); - - // add a filter as this is our final stop - if (node.filter) { - child = t.ifStatement(node.filter, t.blockStatement([child])); - } - } - - return t.forOfStatement( - t.variableDeclaration("let", [t.variableDeclarator(self.left)]), - self.right, - t.blockStatement([child]) - ); -}; - -exports.ComprehensionExpression = function (node, parent, file, scope) { +exports.ComprehensionExpression = function (node, parent, scope, context, file) { if (node.generator) return; - return build(node, parent, file, scope); + return build(node, parent, scope, context, file); }; diff --git a/lib/6to5/transformation/transformers/es7-exponentiation-operator.js b/lib/6to5/transformation/transformers/es7-exponentiation-operator.js index f5825967ee..9ef16713d8 100644 --- a/lib/6to5/transformation/transformers/es7-exponentiation-operator.js +++ b/lib/6to5/transformation/transformers/es7-exponentiation-operator.js @@ -1,18 +1,18 @@ +"use strict"; + // https://github.com/rwaldron/exponentiation-operator exports.experimental = true; -var t = require("../../types"); -var pow = t.memberExpression(t.identifier("Math"), t.identifier("pow")); +var build = require("../helpers/build-binary-assignment-operator-transformer"); +var t = require("../../types"); -exports.AssignmentExpression = function (node) { - if (node.operator !== "**=") return; - node.operator = "="; - node.right = t.callExpression(pow, [node.left, node.right]); -}; +var MATH_POW = t.memberExpression(t.identifier("Math"), t.identifier("pow")); -exports.BinaryExpression = function (node) { - if (node.operator !== "**") return; +build(exports, { + operator: "**", - return t.callExpression(pow, [node.left, node.right]); -}; + build: function (left, right) { + return t.callExpression(MATH_POW, [left, right]); + } +}); diff --git a/lib/6to5/transformation/transformers/es7-generator-comprehension.js b/lib/6to5/transformation/transformers/es7-generator-comprehension.js index 9d950fecd3..cd229e5eb3 100644 --- a/lib/6to5/transformation/transformers/es7-generator-comprehension.js +++ b/lib/6to5/transformation/transformers/es7-generator-comprehension.js @@ -1,4 +1,6 @@ -var arrayComprehension = require("./es7-array-comprehension"); +"use strict"; + +var buildComprehension = require("../helpers/build-comprehension"); var t = require("../../types"); exports.experimental = true; @@ -10,7 +12,7 @@ exports.ComprehensionExpression = function (node) { var container = t.functionExpression(null, [], t.blockStatement(body), true); container._aliasFunction = true; - body.push(arrayComprehension._build(node, function () { + body.push(buildComprehension(node, function () { return t.expressionStatement(t.yieldExpression(node.body)); })); diff --git a/lib/6to5/transformation/transformers/es7-object-spread.js b/lib/6to5/transformation/transformers/es7-object-spread.js index 81156bfb1f..87b32bff53 100644 --- a/lib/6to5/transformation/transformers/es7-object-spread.js +++ b/lib/6to5/transformation/transformers/es7-object-spread.js @@ -1,14 +1,16 @@ +"use strict"; + // https://github.com/sebmarkbage/ecmascript-rest-spread var t = require("../../types"); exports.experimental = true; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasSpread = false; var i; var prop; - for (i in node.properties) { + for (i = 0; i < node.properties.length; i++) { prop = node.properties[i]; if (t.isSpreadProperty(prop)) { hasSpread = true; @@ -26,7 +28,7 @@ exports.ObjectExpression = function (node, parent, file) { props = []; }; - for (i in node.properties) { + for (i = 0; i < node.properties.length; i++) { prop = node.properties[i]; if (t.isSpreadProperty(prop)) { push(); diff --git a/lib/6to5/transformation/transformers/optional-async-to-generator.js b/lib/6to5/transformation/transformers/optional-async-to-generator.js index 551c9f7c71..04f68f8cd9 100644 --- a/lib/6to5/transformation/transformers/optional-async-to-generator.js +++ b/lib/6to5/transformation/transformers/optional-async-to-generator.js @@ -1,11 +1,14 @@ -var bluebirdCoroutines = require("./optional-bluebird-coroutines"); +"use strict"; + +var remapAsyncToGenerator = require("../helpers/remap-async-to-generator"); +var bluebirdCoroutines = require("./optional-bluebird-coroutines"); exports.optional = true; exports.manipulateOptions = bluebirdCoroutines.manipulateOptions; -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; - return bluebirdCoroutines._Function(node, file.addHelper("async-to-generator")); + return remapAsyncToGenerator(node, file.addHelper("async-to-generator")); }; diff --git a/lib/6to5/transformation/transformers/optional-block-scoping-tdz.js b/lib/6to5/transformation/transformers/optional-block-scoping-tdz.js new file mode 100644 index 0000000000..27472f8bdf --- /dev/null +++ b/lib/6to5/transformation/transformers/optional-block-scoping-tdz.js @@ -0,0 +1,46 @@ +var traverse = require("../../traverse"); +var t = require("../../types"); + +exports.optional = true; + +exports.Loop = +exports.Program = +exports.BlockStatement = function (node, parent, scope, context, file) { + var letRefs = node._letReferences; + if (!letRefs) return; + + var state = { + letRefs: letRefs, + file: file + }; + + traverse(node, { + enter: function (node, parent, scope, context, state) { + if (!t.isReferencedIdentifier(node, parent)) return; + + var declared = state.letRefs[node.name]; + if (!declared) return; + + // declared node is different in this scope + if (scope.get(node.name, true) !== declared) return; + + var declaredLoc = declared.loc; + var referenceLoc = node.loc; + + if (!declaredLoc || !referenceLoc) return; + + // does this reference appear on a line before the declaration? + var before = referenceLoc.start.line < declaredLoc.start.line; + + if (referenceLoc.start.line === declaredLoc.start.line) { + // this reference appears on the same line + // check it appears before the declaration + before = referenceLoc.start.col < declaredLoc.start.col; + } + + if (before) { + throw state.file.errorWithNode(node, "Temporal dead zone - accessing a variable before it's initialized"); + } + } + }, scope, state); +}; diff --git a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js index cda714330c..1aa2a24863 100644 --- a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js +++ b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js @@ -1,5 +1,7 @@ -var traverse = require("../../traverse"); -var t = require("../../types"); +"use strict"; + +var remapAsyncToGenerator = require("../helpers/remap-async-to-generator"); +var t = require("../../types"); exports.manipulateOptions = function (opts) { opts.experimental = true; @@ -8,36 +10,12 @@ exports.manipulateOptions = function (opts) { exports.optional = true; -exports._Function = function (node, callId) { - node.async = false; - node.generator = true; - traverse(node, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); - - if (t.isAwaitExpression(node)) { - node.type = "YieldExpression"; - } - } - }); - - var call = t.callExpression(callId, [node]); - - if (t.isFunctionDeclaration(node)) { - var declar = t.variableDeclaration("var", [ - t.variableDeclarator(node.id, call) - ]); - declar._blockHoist = true; - return declar; - } else { - return call; - } -}; - -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; - var id = file.addImport("bluebird"); - return exports._Function(node, t.memberExpression(id, t.identifier("coroutine"))); + return remapAsyncToGenerator( + node, + t.memberExpression(file.addImport("bluebird"), t.identifier("coroutine")) + ); }; diff --git a/lib/6to5/transformation/transformers/optional-core-aliasing.js b/lib/6to5/transformation/transformers/optional-core-aliasing.js index 3610ae8f62..90bb4621a7 100644 --- a/lib/6to5/transformation/transformers/optional-core-aliasing.js +++ b/lib/6to5/transformation/transformers/optional-core-aliasing.js @@ -1,3 +1,5 @@ +"use strict"; + var traverse = require("../../traverse"); var util = require("../../util"); var core = require("core-js/library"); @@ -26,7 +28,7 @@ exports.ast = { exit: function (ast, file) { traverse(ast, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var prop; if (t.isMemberExpression(node) && t.isReferenced(node, parent)) { @@ -37,10 +39,10 @@ exports.ast = { if (!t.isReferenced(obj, node)) return; if (!node.computed && coreHas(obj) && _.has(core[obj.name], prop.name)) { - this.skip(); + context.skip(); return t.prependToMemberExpression(node, file._coreId); } - } else if (t.isIdentifier(node) && !t.isMemberExpression(parent) && t.isReferenced(node, parent) && _.contains(ALIASABLE_CONSTRUCTORS, node.name)) { + } else if (t.isReferencedIdentifier(node, parent) && !t.isMemberExpression(parent) && _.contains(ALIASABLE_CONSTRUCTORS, node.name)) { // Symbol() -> _core.Symbol(); new Promise -> new _core.Promise return t.memberExpression(file._coreId, node); } else if (t.isCallExpression(node)) { diff --git a/lib/6to5/transformation/transformers/optional-proto-to-assign.js b/lib/6to5/transformation/transformers/optional-proto-to-assign.js index b2682d1802..1533480b76 100644 --- a/lib/6to5/transformation/transformers/optional-proto-to-assign.js +++ b/lib/6to5/transformation/transformers/optional-proto-to-assign.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); var _ = require("lodash"); @@ -17,7 +19,7 @@ var buildDefaultsCallExpression = function (expr, ref, file) { exports.optional = true; exports.secondPass = true; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (t.isExpressionStatement(parent)) return; if (!isProtoAssignmentExpression(node)) return; @@ -32,7 +34,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes); }; -exports.ExpressionStatement = function (node, parent, file) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (!t.isAssignmentExpression(expr, { operator: "=" })) return; @@ -41,10 +43,10 @@ exports.ExpressionStatement = function (node, parent, file) { } }; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var proto; - for (var i in node.properties) { + for (var i = 0; i < node.properties.length; i++) { var prop = node.properties[i]; if (isProtoKey(prop)) { diff --git a/lib/6to5/transformation/transformers/optional-typeof-symbol.js b/lib/6to5/transformation/transformers/optional-typeof-symbol.js index 55be121b08..a3d938ccf8 100644 --- a/lib/6to5/transformation/transformers/optional-typeof-symbol.js +++ b/lib/6to5/transformation/transformers/optional-typeof-symbol.js @@ -1,9 +1,23 @@ +"use strict"; + var t = require("../../types"); exports.optional = true; -exports.UnaryExpression = function (node, parent, file) { +exports.UnaryExpression = function (node, parent, scope, context, file) { + context.skip(); + if (node.operator === "typeof") { - return t.callExpression(file.addHelper("typeof"), [node.argument]); + var call = t.callExpression(file.addHelper("typeof"), [node.argument]); + if (t.isIdentifier(node.argument)) { + var undefLiteral = t.literal("undefined"); + return t.conditionalExpression( + t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral), + undefLiteral, + call + ); + } else { + return call; + } } }; diff --git a/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js b/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js new file mode 100644 index 0000000000..6640c0217f --- /dev/null +++ b/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js @@ -0,0 +1,9 @@ +var t = require("../../types"); + +exports.optional = true; + +exports.Identifier = function (node, parent, scope, context, file) { + if (!scope.has(node.name, true)) { + throw file.errorWithNode(node, "Reference to undeclared variable"); + } +}; diff --git a/lib/6to5/transformation/transformers/optional-undefined-to-void.js b/lib/6to5/transformation/transformers/optional-undefined-to-void.js index 19875cd90a..f233becf74 100644 --- a/lib/6to5/transformation/transformers/optional-undefined-to-void.js +++ b/lib/6to5/transformation/transformers/optional-undefined-to-void.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports.optional = true; diff --git a/lib/6to5/transformation/transformers/playground-mallet-operator.js b/lib/6to5/transformation/transformers/playground-mallet-operator.js new file mode 100644 index 0000000000..5bd48d6046 --- /dev/null +++ b/lib/6to5/transformation/transformers/playground-mallet-operator.js @@ -0,0 +1,21 @@ +"use strict"; + +var build = require("../helpers/build-conditional-assignment-operator-transformer"); +var t = require("../../types"); + +build(exports, { + is: function (node, file) { + var is = t.isAssignmentExpression(node) && node.operator === "||="; + if (is) { + var left = node.left; + if (!t.isMemberExpression(left) && !t.isIdentifier(left)) { + throw file.errorWithNode(left, "Expected type MemeberExpression or Identifier"); + } + return true; + } + }, + + build: function (node) { + return t.unaryExpression("!", node, true); + } +}); diff --git a/lib/6to5/transformation/transformers/playground-memoization-operator.js b/lib/6to5/transformation/transformers/playground-memoization-operator.js index d8aad48236..0f41ffb3c6 100644 --- a/lib/6to5/transformation/transformers/playground-memoization-operator.js +++ b/lib/6to5/transformation/transformers/playground-memoization-operator.js @@ -1,86 +1,23 @@ -var t = require("../../types"); +"use strict"; -var isMemo = function (node) { - var is = t.isAssignmentExpression(node) && node.operator === "?="; - if (is) t.assertMemberExpression(node.left); - return is; -}; +var build = require("../helpers/build-conditional-assignment-operator-transformer"); +var t = require("../../types"); -var getPropRef = function (nodes, prop, file, scope) { - if (t.isIdentifier(prop)) { - return t.literal(prop.name); - } else { - var temp = scope.generateUidBasedOnNode(prop, file); - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(temp, prop) - ])); - return temp; +build(exports, { + is: function (node) { + var is = t.isAssignmentExpression(node) && node.operator === "?="; + if (is) t.assertMemberExpression(node.left); + return is; + }, + + build: function (node, file) { + return t.unaryExpression( + "!", + t.callExpression( + t.memberExpression(file.addHelper("has-own"), t.identifier("call")), + [node.object, node.property] + ), + true + ); } -}; - -var getObjRef = function (nodes, obj, file, scope) { - var temp = scope.generateUidBasedOnNode(obj, file); - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(temp, obj) - ])); - return temp; -}; - -var buildHasOwn = function (obj, prop, file) { - return t.unaryExpression( - "!", - t.callExpression( - t.memberExpression(file.addHelper("has-own"), t.identifier("call")), - [obj, prop] - ), - true - ); -}; - -var buildAbsoluteRef = function (left, obj, prop) { - var computed = left.computed || t.isLiteral(prop); - return t.memberExpression(obj, prop, computed); -}; - -var buildAssignment = function (expr, obj, prop) { - return t.assignmentExpression("=", buildAbsoluteRef(expr.left, obj, prop), expr.right); -}; - -exports.ExpressionStatement = function (node, parent, file, scope) { - var expr = node.expression; - if (!isMemo(expr)) return; - - var nodes = []; - - var left = expr.left; - var obj = getObjRef(nodes, left.object, file, scope); - var prop = getPropRef(nodes, left.property, file, scope); - - nodes.push(t.ifStatement( - buildHasOwn(obj, prop, file), - t.expressionStatement(buildAssignment(expr, obj, prop)) - )); - - return nodes; -}; - -exports.AssignmentExpression = function (node, parent, file, scope) { - if (t.isExpressionStatement(parent)) return; - if (!isMemo(node)) return; - - var nodes = []; - - var left = node.left; - var obj = getObjRef(nodes, left.object, file, scope); - var prop = getPropRef(nodes, left.property, file, scope); - - nodes.push(t.logicalExpression( - "&&", - buildHasOwn(obj, prop, file), - buildAssignment(node, obj, prop) - )); - - nodes.push(buildAbsoluteRef(left, obj, prop)); - - return t.toSequenceExpression(nodes, scope); -}; +}); diff --git a/lib/6to5/transformation/transformers/playground-method-binding.js b/lib/6to5/transformation/transformers/playground-method-binding.js index 6c6bd9e37b..69e901f628 100644 --- a/lib/6to5/transformation/transformers/playground-method-binding.js +++ b/lib/6to5/transformation/transformers/playground-method-binding.js @@ -1,6 +1,8 @@ +"use strict"; + var t = require("../../types"); -exports.BindMemberExpression = function (node, parent, file, scope) { +exports.BindMemberExpression = function (node, parent, scope, context, file) { var object = node.object; var prop = node.property; @@ -22,7 +24,7 @@ exports.BindMemberExpression = function (node, parent, file, scope) { } }; -exports.BindFunctionExpression = function (node, parent, file, scope) { +exports.BindFunctionExpression = function (node, parent, scope, context, file) { var buildCall = function (args) { var param = file.generateUidIdentifier("val", scope); return t.functionExpression(null, [param], t.blockStatement([ diff --git a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js index 9c3f0bb6d0..d5acc71105 100644 --- a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js +++ b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js @@ -1,8 +1,10 @@ +"use strict"; + var traverse = require("../../traverse"); var t = require("../../types"); exports.Property = -exports.MethodDefinition = function (node, parent, file) { +exports.MethodDefinition = function (node, parent, scope, context, file) { if (node.kind !== "memo") return; node.kind = "get"; diff --git a/lib/6to5/transformation/transformers/react.js b/lib/6to5/transformation/transformers/react.js index 341d6b1492..f906cead68 100644 --- a/lib/6to5/transformation/transformers/react.js +++ b/lib/6to5/transformation/transformers/react.js @@ -1,3 +1,5 @@ +"use strict"; + // Based upon the excellent jsx-transpiler by Ingvar Stepanyan (RReverser) // https://github.com/RReverser/jsx-transpiler @@ -15,7 +17,7 @@ exports.XJSIdentifier = function (node) { } }; -exports.XJSNamespacedName = function (node, parent, file) { +exports.XJSNamespacedName = function (node, parent, scope, context, file) { throw file.errorWithNode(node, "Namespace tags are not supported. ReactJSX is not XML."); }; @@ -37,12 +39,12 @@ exports.XJSAttribute = { } }; -var isTag = function(tagName) { - return (/^[a-z]|\-/).test(tagName); +var isCompatTag = function(tagName) { + return /^[a-z]|\-/.test(tagName); }; exports.XJSOpeningElement = { - exit: function (node, parent, file) { + exit: function (node, parent, scope, context, file) { var reactCompat = file.opts.reactCompat; var tagExpr = node.name; var args = []; @@ -55,7 +57,7 @@ exports.XJSOpeningElement = { } if (!reactCompat) { - if (tagName && isTag(tagName)) { + if (tagName && isCompatTag(tagName)) { args.push(t.literal(tagName)); } else { args.push(tagExpr); @@ -64,48 +66,7 @@ exports.XJSOpeningElement = { var attribs = node.attributes; if (attribs.length) { - var _props = []; - var objs = []; - - // so basically in order to support spread elements we - // loop over all the attributes, breaking on spreads - // we then push a new object containing all prior attributes - // to an array for later processing - - var pushProps = function () { - if (!_props.length) return; - - objs.push(t.objectExpression(_props)); - _props = []; - }; - - while (attribs.length) { - var prop = attribs.shift(); - if (t.isXJSSpreadAttribute(prop)) { - pushProps(); - objs.push(prop.argument); - } else { - _props.push(prop); - } - } - - pushProps(); - - if (objs.length === 1) { - // only one object - attribs = objs[0]; - } else { - // looks like we have multiple objects - if (!t.isObjectExpression(objs[0])) { - objs.unshift(t.objectExpression([])); - } - - // spread it - attribs = t.callExpression( - t.memberExpression(t.identifier("React"), t.identifier("__spread")), - objs - ); - } + attribs = buildXJSOpeningElementAttributes(attribs); } else { attribs = t.literal(null); } @@ -113,7 +74,7 @@ exports.XJSOpeningElement = { args.push(attribs); if (reactCompat) { - if (tagName && isTag(tagName)) { + if (tagName && isCompatTag(tagName)) { return t.callExpression( t.memberExpression( t.memberExpression(t.identifier("React"), t.identifier("DOM")), @@ -131,41 +92,64 @@ exports.XJSOpeningElement = { } }; +/** + * The logic for this is quite terse. It's because we need to + * support spread elements. We loop over all attributes, + * breaking on spreads, we then push a new object containg + * all prior attributes to an array for later processing. + */ + +var buildXJSOpeningElementAttributes = function (attribs) { + var _props = []; + var objs = []; + + var pushProps = function () { + if (!_props.length) return; + + objs.push(t.objectExpression(_props)); + _props = []; + }; + + while (attribs.length) { + var prop = attribs.shift(); + if (t.isXJSSpreadAttribute(prop)) { + pushProps(); + objs.push(prop.argument); + } else { + _props.push(prop); + } + } + + pushProps(); + + if (objs.length === 1) { + // only one object + attribs = objs[0]; + } else { + // looks like we have multiple objects + if (!t.isObjectExpression(objs[0])) { + objs.unshift(t.objectExpression([])); + } + + // spread it + attribs = t.callExpression( + t.memberExpression(t.identifier("React"), t.identifier("__spread")), + objs + ); + } + + return attribs; +}; + exports.XJSElement = { exit: function (node) { var callExpr = node.openingElement; - var i; - for (i in node.children) { + for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; if (t.isLiteral(child) && _.isString(child.value)) { - var lines = child.value.split(/\r\n|\n|\r/); - - for (i in lines) { - var line = lines[i]; - - var isFirstLine = i === "0"; - var isLastLine = +i === lines.length - 1; - - // replace rendered whitespace tabs with spaces - var trimmedLine = line.replace(/\t/g, " "); - - // trim whitespace touching a newline - if (!isFirstLine) { - trimmedLine = trimmedLine.replace(/^[ ]+/, ""); - } - - // trim whitespace touching an endline - if (!isLastLine) { - trimmedLine = trimmedLine.replace(/[ ]+$/, ""); - } - - if (trimmedLine) { - callExpr.arguments.push(t.literal(trimmedLine)); - } - } - + cleanXJSElementLiteralChild(child, callExpr.arguments); continue; } else if (t.isXJSEmptyExpression(child)) { continue; @@ -182,35 +166,69 @@ exports.XJSElement = { } }; +var cleanXJSElementLiteralChild = function (child, args) { + var lines = child.value.split(/\r\n|\n|\r/); + + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + + var isFirstLine = i === 0; + var isLastLine = i === lines.length - 1; + + // replace rendered whitespace tabs with spaces + var trimmedLine = line.replace(/\t/g, " "); + + // trim whitespace touching a newline + if (!isFirstLine) { + trimmedLine = trimmedLine.replace(/^[ ]+/, ""); + } + + // trim whitespace touching an endline + if (!isLastLine) { + trimmedLine = trimmedLine.replace(/[ ]+$/, ""); + } + + if (trimmedLine) { + args.push(t.literal(trimmedLine)); + } + } +}; + // display names -var addDisplayName = function (id, call) { - if (!call || !t.isCallExpression(call)) return; +var isCreateClass = function (call) { + if (!call || !t.isCallExpression(call)) return false; var callee = call.callee; - if (!t.isMemberExpression(callee)) return; + if (!t.isMemberExpression(callee)) return false; - // not React + // not React call member object var obj = callee.object; - if (!t.isIdentifier(obj, { name: "React" })) return; + if (!t.isIdentifier(obj, { name: "React" })) return false; - // not createClass + // not createClass call member property var prop = callee.property; - if (!t.isIdentifier(prop, { name: "createClass" })) return; + if (!t.isIdentifier(prop, { name: "createClass" })) return false; - // no arguments + // no call arguments var args = call.arguments; - if (args.length !== 1) return; + if (args.length !== 1) return false; - // not an object + // first call arg is not an object var first = args[0]; if (!t.isObjectExpression(first)) return; - var props = first.properties; + return true; +}; + +var addDisplayName = function (id, call) { + if (!isCreateClass(call)) return; + + var props = call.arguments[0].properties; var safe = true; - for (var i in props) { - prop = props[i]; + for (var i = 0; i < props.length; i++) { + var prop = props[i]; if (t.isIdentifier(prop.key, { name: "displayName" })) { safe = false; break; diff --git a/lib/6to5/transformation/transformers/spec-block-scoped-functions.js b/lib/6to5/transformation/transformers/spec-block-scoped-functions.js new file mode 100644 index 0000000000..087b881d9f --- /dev/null +++ b/lib/6to5/transformation/transformers/spec-block-scoped-functions.js @@ -0,0 +1,24 @@ +"use strict"; + +var t = require("../../types"); + +exports.FunctionDeclaration = function (node, parent) { + if (t.isProgram(parent) || t.isExportDeclaration(parent)) { + return; + } + + // this is to avoid triggering the TDZ detection + node.id.loc = null; + + var declar = t.variableDeclaration("let", [ + t.variableDeclarator(node.id, t.toExpression(node)) + ]); + + // hoist it up above everything else + declar._blockHoist = 2; + + // todo: name this + node.id = null; + + return declar; +}; diff --git a/lib/6to5/transformation/transformers/spec-member-expression-literals.js b/lib/6to5/transformation/transformers/spec-member-expression-literals.js index 6803865073..d4c318a996 100644 --- a/lib/6to5/transformation/transformers/spec-member-expression-literals.js +++ b/lib/6to5/transformation/transformers/spec-member-expression-literals.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports.MemberExpression = function (node) { diff --git a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js index 5af12108f1..01de184aaa 100644 --- a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js +++ b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js @@ -1,7 +1,9 @@ +"use strict"; + var t = require("../../types"); exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var left = node.left; if (t.isVariableDeclaration(left)) { var declar = left.declarations[0]; diff --git a/lib/6to5/transformation/transformers/spec-property-literals.js b/lib/6to5/transformation/transformers/spec-property-literals.js index f750fa7527..3473aa9f26 100644 --- a/lib/6to5/transformation/transformers/spec-property-literals.js +++ b/lib/6to5/transformation/transformers/spec-property-literals.js @@ -1,3 +1,5 @@ +"use strict"; + var t = require("../../types"); exports.Property = function (node) { diff --git a/lib/6to5/transformation/transformers/spec-setters.js b/lib/6to5/transformation/transformers/spec-setters.js index 30b404ed1e..e104022bbc 100644 --- a/lib/6to5/transformation/transformers/spec-setters.js +++ b/lib/6to5/transformation/transformers/spec-setters.js @@ -1,5 +1,7 @@ +"use strict"; + exports.MethodDefinition = -exports.Property = function (node, parent, file) { +exports.Property = function (node, parent, scope, context, file) { if (node.kind === "set" && node.value.params.length !== 1) { throw file.errorWithNode(node.value, "Setters must have only one parameter"); } diff --git a/lib/6to5/transformation/transformers/use-strict.js b/lib/6to5/transformation/transformers/use-strict.js index bd9fd9c651..e8b813d253 100644 --- a/lib/6to5/transformation/transformers/use-strict.js +++ b/lib/6to5/transformation/transformers/use-strict.js @@ -1,26 +1,11 @@ -var t = require("../../types"); +"use strict"; -exports._has = function (node) { - var first = node.body[0]; - return t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" }); -}; - -exports._wrap = function (node, callback) { - var useStrictNode; - if (exports._has(node)) { - useStrictNode = node.body.shift(); - } - - callback(); - - if (useStrictNode) { - node.body.unshift(useStrictNode); - } -}; +var useStrict = require("../helpers/use-strict"); +var t = require("../../types"); exports.ast = { exit: function (ast) { - if (!exports._has(ast.program)) { + if (!useStrict.has(ast.program)) { ast.program.body.unshift(t.expressionStatement(t.literal("use strict"))); } } diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 5667ce1f46..3c88178c1b 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -1,38 +1,47 @@ +"use strict"; + module.exports = traverse; +/* jshint maxparams:7 */ + var Scope = require("./scope"); var t = require("../types"); var _ = require("lodash"); -function TraversalContext(previousContext) { - this.didSkip = false; - this.didRemove = false; - this.didStop = false; - this.didFlatten = previousContext ? previousContext.didFlatten : false; +function noop() { } + +function TraversalContext() { + this.shouldFlatten = false; + this.shouldRemove = false; + this.shouldSkip = false; + this.shouldStop = false; } TraversalContext.prototype.flatten = function () { - this.didFlatten = true; + this.shouldFlatten = true; }; TraversalContext.prototype.remove = function () { - this.didRemove = true; - this.skip(); + this.shouldRemove = true; + this.shouldSkip = true; }; TraversalContext.prototype.skip = function () { - this.didSkip = true; + this.shouldSkip = true; }; TraversalContext.prototype.stop = function () { - this.didStop = true; - this.skip(); + this.shouldStop = true; + this.shouldSkip = true; }; -TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { - if (result === false) return node; - if (result == null) return node; +TraversalContext.prototype.reset = function () { + this.shouldRemove = false; + this.shouldSkip = false; + this.shouldStop = false; +}; +function replaceNode(obj, key, node, result) { var isArray = Array.isArray(result); // inherit comments from original node to the first replacement node @@ -41,7 +50,7 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { if (inheritTo) t.inheritsComments(inheritTo, node); // replace the node - node = obj[key] = result; + obj[key] = result; // we're replacing a statement or block node with an array of statements so we better // ensure that it's a block @@ -50,104 +59,138 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { } if (isArray) { - this.flatten(); + return true; + } +} + +TraversalContext.prototype.enterNode = function (obj, key, node, enter, parent, scope, state) { + var result = enter(node, parent, scope, this, state); + var flatten = false; + + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; + + if (flatten) { + this.shouldFlatten = true; + } + } + + if (this.shouldRemove) { + obj[key] = null; + this.shouldFlatten = true; } return node; }; -TraversalContext.prototype.visit = function (obj, key, opts, scope, parent) { +TraversalContext.prototype.exitNode = function (obj, key, node, exit, parent, scope, state) { + var result = exit(node, parent, scope, this, state); + var flatten = false; + + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; + + if (flatten) { + this.shouldFlatten = true; + } + } + + return node; +}; + +TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent, state) { + this.reset(); + var node = obj[key]; - if (!node) return; // type is blacklisted if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1) return; - var result; var ourScope = scope; - if (t.isScope(node)) ourScope = new Scope(node, scope); - - // enter - if (opts.enter) { - result = opts.enter.call(this, node, parent, ourScope); - node = this.maybeReplace(result, obj, key, node); - - if (this.didRemove) { - obj[key] = null; - this.flatten(); - } - - // stop traversal - if (this.didSkip) return; + if (t.isScope(node)) { + ourScope = new Scope(node, scope); } - // traverse node - traverse(node, opts, ourScope); + node = this.enterNode(obj, key, node, opts.enter, parent, ourScope, state); - // exit - if (opts.exit) { - result = opts.exit.call(this, node, parent, ourScope); - node = this.maybeReplace(result, obj, key, node); - } + if (this.shouldSkip) return this.shouldStop; + + traverseNode(node, opts, ourScope, state); + this.exitNode(obj, key, node, opts.exit, parent, ourScope, state); + + return this.shouldStop; }; -function traverse(parent, opts, scope) { - // falsy node - if (!parent) return; +TraversalContext.prototype.visit = function (node, key, opts, scope, state) { + var nodes = node[key]; + if (!nodes) return; - // array of nodes - if (Array.isArray(parent)) { - for (var i = 0; i < parent.length; i++) - traverse(parent[i], opts, scope); + if (!Array.isArray(nodes)) { + return this.visitNode(node, key, opts, scope, node, state); + } + + if (nodes.length === 0) { return; } - // unknown node type to traverse - var keys = t.VISITOR_KEYS[parent.type]; + for (var k = 0; k < nodes.length; k++) { + if (nodes[k] && this.visitNode(nodes, k, opts, scope, node, state)) + return true; + } + + if (this.shouldFlatten) { + node[key] = _.flatten(node[key]); + + if (key === "body") { + // we can safely compact this + node[key] = _.compact(node[key]); + } + } +}; + +function traverseNode(node, opts, scope, state) { + var keys = t.VISITOR_KEYS[node.type]; if (!keys) return; - opts = opts || {}; - var context = null; - + var context = new TraversalContext(); for (var j = 0; j < keys.length; j++) { - var key = keys[j]; - var nodes = parent[key]; - if (!nodes) continue; - - if (Array.isArray(nodes)) { - for (var k = 0; k < nodes.length; k++) { - context = new TraversalContext(context); - context.visit(nodes, k, opts, scope, parent); - if (context.didStop) return; - } - - if (context && context.didFlatten) { - parent[key] = _.flatten(parent[key]); - - if (key === "body") { - // we can safely compact this - parent[key] = _.compact(parent[key]); - } - } - } else { - context = new TraversalContext(context); - context.visit(parent, key, opts, scope, parent); - if (context.didStop) return; + if (context.visit(node, keys[j], opts, scope, state)) { + return; } } } +function traverse(parent, opts, scope, state) { + // falsy node + if (!parent) return; + + if (!opts) opts = {}; + if (!opts.enter) opts.enter = noop; + if (!opts.exit) opts.exit = noop; + + // array of nodes + if (Array.isArray(parent)) { + for (var i = 0; i < parent.length; i++) { + traverseNode(parent[i], opts, scope, state); + } + } else { + traverseNode(parent, opts, scope, state); + } +} + traverse.removeProperties = function (tree) { var clear = function (node) { - delete node._declarations; - delete node.extendedRange; - delete node._scopeInfo; - delete node.tokens; - delete node.range; - delete node.start; - delete node.end; - delete node.loc; - delete node.raw; + node._declarations = null; + node.extendedRange = null; + node._scopeInfo = null; + node.tokens = null; + node.range = null; + node.start = null; + node.end = null; + node.loc = null; + node.raw = null; clearComments(node.trailingComments); clearComments(node.leadingComments); @@ -166,23 +209,25 @@ traverse.removeProperties = function (tree) { traverse.hasType = function (tree, type, blacklistTypes) { blacklistTypes = [].concat(blacklistTypes || []); - var has = false; - // the node we're searching in is blacklisted if (_.contains(blacklistTypes, tree.type)) return false; // the type we're looking for is the same as the passed node if (tree.type === type) return true; + var state = { + has: false + }; + traverse(tree, { blacklist: blacklistTypes, - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node.type === type) { - has = true; - this.skip(); + state.has = true; + context.skip(); } } - }); + }, null, state); - return has; + return state.has; }; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index f02803c886..f100615f12 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -1,3 +1,5 @@ +"use strict"; + module.exports = Scope; var traverse = require("./index"); @@ -135,10 +137,8 @@ Scope.prototype.getInfo = function () { if (t.isFor(block)) { _.each(FOR_KEYS, function (key) { var node = block[key]; - if (t.isLet(node)) add(node); + if (t.isBlockScoped(node)) add(node); }); - - block = block.body; } // Program, BlockStatement - let variables @@ -146,7 +146,7 @@ Scope.prototype.getInfo = function () { if (t.isBlockStatement(block) || t.isProgram(block)) { _.each(block.body, function (node) { // check for non-var `VariableDeclaration`s - if (t.isLet(node)) add(node); + if (t.isBlockScoped(node)) add(node); }); } @@ -156,11 +156,22 @@ Scope.prototype.getInfo = function () { add(block.param); } + // ComprehensionExpression - blocks + + if (t.isComprehensionExpression(block)) { + add(block); + } + // Program, Function - var variables if (t.isProgram(block) || t.isFunction(block)) { + var state = { + blockId: block.id, + add: add + }; + traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { if (t.isFor(node)) { _.each(FOR_KEYS, function (key) { var declar = node[key]; @@ -170,22 +181,22 @@ Scope.prototype.getInfo = function () { // this block is a function so we'll stop since none of the variables // declared within are accessible - if (t.isFunction(node)) return this.skip(); + if (t.isFunction(node)) return context.skip(); // function identifier doesn't belong to this scope - if (block.id && node === block.id) return; + if (state.blockId && node === state.blockId) return; if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) { - add(node, true); + state.add(node, true); } // we've ran into a declaration! // we'll let the BlockStatement scope deal with `let` declarations unless - if (t.isDeclaration(node) && !t.isLet(node)) { - add(node); + if (t.isDeclaration(node) && !t.isBlockScoped(node)) { + state.add(node); } } - }, this); + }, this, state); } // Function - params, rest diff --git a/lib/6to5/types/alias-keys.json b/lib/6to5/types/alias-keys.json index e07ecc429d..cc80d7290c 100644 --- a/lib/6to5/types/alias-keys.json +++ b/lib/6to5/types/alias-keys.json @@ -16,6 +16,7 @@ "VariableDeclaration": ["Statement", "Declaration"], "ExportDeclaration": ["Statement", "Declaration"], "ImportDeclaration": ["Statement", "Declaration"], + "PrivateDeclaration": ["Statement", "Declaration"], "ArrowFunctionExpression": ["Scope", "Function", "Expression"], "FunctionDeclaration": ["Statement", "Declaration", "Scope", "Function"], @@ -52,7 +53,7 @@ "BindFunctionExpression": ["Expression"], "BindMemberExpression": ["Expression"], "CallExpression": ["Expression"], - "ComprehensionExpression": ["Expression"], + "ComprehensionExpression": ["Expression", "Scope"], "ConditionalExpression": ["Expression"], "Identifier": ["Expression"], "Literal": ["Expression"], diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index ef433cd5b1..8bc6cb5d75 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -1,5 +1,8 @@ -var esutils = require("esutils"); -var _ = require("lodash"); +"use strict"; + +var toFastProperties = require("../to-fast-properties"); +var esutils = require("esutils"); +var _ = require("lodash"); var t = exports; @@ -7,14 +10,26 @@ t.NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", // -var addAssert = function (type, is) { +/** + * Registers `is[Type]` and `assert[Type]` generated functions for a given `type`. + * Pass `skipAliasCheck` to force it to directly compare `node.type` with `type`. + * + * @param {String} type + * @param {Boolean?} skipAliasCheck + */ + +function registerType(type, skipAliasCheck) { + var is = t["is" + type] = function (node, opts) { + return t.is(type, node, opts, skipAliasCheck); + }; + t["assert" + type] = function (node, opts) { opts = opts || {}; if (!is(node, opts)) { throw new Error("Expected type " + JSON.stringify(type) + " with option " + JSON.stringify(opts)); } }; -}; +} t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"]; @@ -22,14 +37,60 @@ t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"]; t.VISITOR_KEYS = require("./visitor-keys"); -_.each(t.VISITOR_KEYS, function (keys, type) { - var is = t["is" + type] = function (node, opts) { - return node && node.type === type && t.shallowEqual(node, opts); - }; +t.ALIAS_KEYS = require("./alias-keys"); - addAssert(type, is); +t.FLIPPED_ALIAS_KEYS = {}; + +_.each(t.VISITOR_KEYS, function (keys, type) { + registerType(type, true); }); +_.each(t.ALIAS_KEYS, function (aliases, type) { + _.each(aliases, function (alias) { + var types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || []; + types.push(type); + }); +}); + +_.each(t.FLIPPED_ALIAS_KEYS, function (types, type) { + t[type.toUpperCase() + "_TYPES"] = types; + registerType(type, false); +}); + +/** + * Returns whether `node` is of given `type`. + * For better performance, use this instead of `is[Type]` when `type` is unknown. + * Optionally, pass `skipAliasCheck` to directly compare `node.type` with `type`. + * + * @param {String} type + * @param {Node} node + * @param {Object?} opts + * @param {Boolean?} skipAliasCheck + * @returns {Boolean} isOfType + */ + +t.is = function (type, node, opts, skipAliasCheck) { + if (!node) return; + + var typeMatches = (type === node.type); + + if (!typeMatches && !skipAliasCheck) { + var aliases = t.FLIPPED_ALIAS_KEYS[type]; + + if (typeof aliases !== 'undefined') + typeMatches = aliases.indexOf(node.type) > -1; + } + + if (!typeMatches) { + return false; + } + + if (typeof opts !== 'undefined') + return t.shallowEqual(node, opts); + + return true; +}; + // t.BUILDER_KEYS = _.defaults(require("./builder-keys"), t.VISITOR_KEYS); @@ -45,29 +106,6 @@ _.each(t.BUILDER_KEYS, function (keys, type) { }; }); -// - -t.ALIAS_KEYS = require("./alias-keys"); - -t.FLIPPED_ALIAS_KEYS = {}; - -_.each(t.ALIAS_KEYS, function (aliases, type) { - _.each(aliases, function (alias) { - var types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || []; - types.push(type); - }); -}); - -_.each(t.FLIPPED_ALIAS_KEYS, function (types, type) { - t[type.toUpperCase() + "_TYPES"] = types; - - var is = t["is" + type] = function (node, opts) { - return node && types.indexOf(node.type) >= 0 && t.shallowEqual(node, opts); - }; - - addAssert(type, is); -}); - /** * Description * @@ -139,17 +177,17 @@ t.toSequenceExpression = function (nodes, scope) { // t.shallowEqual = function (actual, expected) { - var same = true; + var keys = Object.keys(expected); + var key; - if (expected) { - _.each(expected, function (val, key) { - if (actual[key] !== val) { - return same = false; - } - }); + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + + if (actual[key] !== expected[key]) + return false; } - return same; + return true; }; /** @@ -193,6 +231,21 @@ t.isReferenced = function (node, parent) { // we're a property key and we aren't computed so we aren't referenced if (t.isProperty(parent) && parent.key === node && !parent.computed) return false; + if (t.isFunction(parent)) { + // we're a function param + if (_.contains(parent.params, node)) return false; + + // we're a rest parameter + if (parent.rest === node) return false; + } + + if (t.isMethodDefinition(parent) && parent.key === node && !parent.computed) { + return false; + } + + // we're a catch clause param + if (t.isCatchClause(parent) && parent.param === node) return false; + // we're a variable declarator id so we aren't referenced if (t.isVariableDeclarator(parent) && parent.id === node) return false; @@ -210,6 +263,18 @@ t.isReferenced = function (node, parent) { return false; }; +/** + * Description + * + * @param {Object} node + * @param {Object} parent + * @returns {Boolean} + */ + +t.isReferencedIdentifier = function (node, parent) { + return t.isIdentifier(node) && t.isReferenced(node, parent); +}; + /** * Description * @@ -392,7 +457,7 @@ t.getIds = function (node, map, ignoreTypes) { if (t.isIdentifier(id)) { ids[id.name] = id; } else if (nodeKeys) { - for (i in nodeKeys) { + for (i = 0; i < nodeKeys.length; i++) { key = nodeKeys[i]; if (id[key]) { search.push(id[key]); @@ -400,7 +465,7 @@ t.getIds = function (node, map, ignoreTypes) { } } } else if (arrKeys) { - for (i in arrKeys) { + for (i = 0; i < arrKeys.length; i++) { key = arrKeys[i]; search = search.concat(id[key] || []); } @@ -421,10 +486,13 @@ t.getIds.nodes = { ClassDeclaration: ["id"], MemeberExpression: ["object"], SpreadElement: ["argument"], - Property: ["value"] + Property: ["value"], + ComprehensionBlock: ["left"] }; t.getIds.arrays = { + PrivateDeclaration: ["declarations"], + ComprehensionExpression: ["blocks"], ExportDeclaration: ["specifiers", "declaration"], ImportDeclaration: ["specifiers"], VariableDeclaration: ["declarations"], @@ -443,6 +511,17 @@ t.isLet = function (node) { return t.isVariableDeclaration(node) && (node.kind !== "var" || node._let); }; +/** + * Description + * + * @param {Object} node + * @returns {Boolean} + */ + +t.isBlockScoped = function (node) { + return t.isFunctionDeclaration(node) || t.isLet(node); +}; + /** * Description * @@ -496,10 +575,10 @@ t.inheritsComments = function (child, parent) { */ t.inherits = function (child, parent) { - child.loc = parent.loc; - child.end = parent.end; child.range = parent.range; child.start = parent.start; + child.loc = parent.loc; + child.end = parent.end; t.inheritsComments(child, parent); return child; }; @@ -525,3 +604,6 @@ t.getSpecifierName = function (specifier) { t.isSpecifierDefault = function (specifier) { return t.isIdentifier(specifier.id) && specifier.id.name === "default"; }; + +toFastProperties(t); +toFastProperties(t.VISITOR_KEYS); diff --git a/lib/6to5/types/visitor-keys.json b/lib/6to5/types/visitor-keys.json index aac62dd391..125e91c922 100644 --- a/lib/6to5/types/visitor-keys.json +++ b/lib/6to5/types/visitor-keys.json @@ -15,7 +15,6 @@ "ClassBody": ["body"], "ClassDeclaration": ["id", "body", "superClass"], "ClassExpression": ["id", "body", "superClass"], - "ClassProperty": ["key"], "ComprehensionBlock": ["left", "right", "body"], "ComprehensionExpression": ["filter", "blocks", "body"], "ConditionalExpression": ["test", "consequent", "alternate"], @@ -68,6 +67,40 @@ "VirtualPropertyExpression": ["object", "property"], "WhileStatement": ["test", "body"], "WithStatement": ["object", "body"], + "YieldExpression": ["argument"], + + "AnyTypeAnnotation": [], + "ArrayTypeAnnotation": [], + "BooleanTypeAnnotation": [], + "ClassProperty": ["key"], + "DeclareClass": [], + "DeclareFunction": [], + "DeclareModule": [], + "DeclareVariable": [], + "FunctionTypeAnnotation": [], + "FunctionTypeParam": [], + "GenericTypeAnnotation": [], + "InterfaceExtends": [], + "InterfaceDeclaration": [], + "IntersectionTypeAnnotation": [], + "NullableTypeAnnotation": [], + "NumberTypeAnnotation": [], + "StringLiteralTypeAnnotation": [], + "StringTypeAnnotation": [], + "TupleTypeAnnotation": [], + "TypeofTypeAnnotation": [], + "TypeAlias": [], + "TypeAnnotation": [], + "TypeParameterDeclaration": [], + "TypeParameterInstantiation": [], + "ObjectTypeAnnotation": [], + "ObjectTypeCallProperty": [], + "ObjectTypeIndexer": [], + "ObjectTypeProperty": [], + "QualifiedTypeIdentifier": [], + "UnionTypeAnnotation": [], + "VoidTypeAnnotation": [], + "XJSAttribute": ["name", "value"], "XJSClosingElement": ["name"], "XJSElement": ["openingElement", "closingElement", "children"], @@ -77,6 +110,5 @@ "XJSMemberExpression": ["object", "property"], "XJSNamespacedName": ["namespace", "name"], "XJSOpeningElement": ["name", "attributes"], - "XJSSpreadAttribute": ["argument"], - "YieldExpression": ["argument"] + "XJSSpreadAttribute": ["argument"] } diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 27a790141c..ce200dbe57 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -1,3 +1,5 @@ +"use strict"; + require("./patch"); var estraverse = require("estraverse"); @@ -129,6 +131,14 @@ exports.buildDefineProperties = function (mutatorMap) { return objExpr; }; +var templateTraverser = { + enter: function (node, parent, scope, context, nodes) { + if (t.isIdentifier(node) && _.has(nodes, node.name)) { + return nodes[node.name]; + } + } +}; + exports.template = function (name, nodes, keepExpression) { var template = exports.templates[name]; if (!template) throw new ReferenceError("unknown template " + name); @@ -141,13 +151,7 @@ exports.template = function (name, nodes, keepExpression) { template = _.cloneDeep(template); if (!_.isEmpty(nodes)) { - traverse(template, { - enter: function (node) { - if (t.isIdentifier(node) && _.has(nodes, node.name)) { - return nodes[node.name]; - } - } - }); + traverse(template, templateTraverser, null, nodes); } var node = template.body[0]; diff --git a/package.json b/package.json index a76b3beabe..0bcce27ea4 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,10 @@ { "name": "6to5", "description": "Turn ES6 code into readable vanilla ES5 with source maps", - "version": "2.12.0", + "version": "2.13.3", "author": "Sebastian McKenzie ", - "homepage": "https://github.com/6to5/6to5", - "repository": { - "type": "git", - "url": "https://github.com/6to5/6to5.git" - }, - "bugs": { - "url": "https://github.com/6to5/6to5/issues" - }, + "homepage": "https://6to5.org/", + "repository": "6to5/6to5", "preferGlobal": true, "main": "lib/6to5/index.js", "bin": { @@ -39,34 +33,34 @@ "test": "make test" }, "dependencies": { - "acorn-6to5": "0.11.1-14", + "acorn-6to5": "0.11.1-18", "ast-types": "~0.6.1", - "chokidar": "0.11.1", - "commander": "2.5.0", - "core-js": "0.4.4", - "estraverse": "1.8.0", + "chokidar": "0.12.6", + "commander": "2.6.0", + "core-js": "0.4.5", + "estraverse": "1.9.1", "esutils": "1.1.6", "esvalid": "1.1.0", "fs-readdir-recursive": "0.1.0", - "jshint": "2.5.10", + "jshint": "2.5.11", "lodash": "2.4.1", - "mkdirp": "0.5.0", + "output-file-sync": "^1.1.0", "private": "0.1.6", - "regenerator": "0.8.3", - "regexpu": "0.3.0", + "regenerator": "0.8.9", + "regexpu": "1.0.0", "roadrunner": "1.0.4", - "source-map": "0.1.40", - "source-map-support": "0.2.8" + "source-map": "0.1.43", + "source-map-support": "0.2.9" }, "devDependencies": { - "browserify": "6.3.2", - "chai": "1.9.2", - "istanbul": "0.3.2", + "browserify": "8.1.1", + "chai": "1.10.0", + "istanbul": "0.3.5", "jshint-stylish": "1.0.0", "matcha": "0.6.0", - "mocha": "1.21.4", + "mocha": "2.1.0", "rimraf": "2.2.8", - "uglify-js": "2.4.15" + "uglify-js": "2.4.16" }, "optionalDependencies": { "kexec": "0.2.0" diff --git a/test/_transformation-helper.js b/test/_transformation-helper.js index db754ac9ec..c036440f9e 100644 --- a/test/_transformation-helper.js +++ b/test/_transformation-helper.js @@ -94,7 +94,12 @@ var run = function (task, done) { checkAst(result); actualCode = result.code; - chai.expect(actualCode).to.be.equal(expectCode, actual.loc + " !== " + expect.loc); + try { + chai.expect(actualCode).to.be.equal(expectCode, actual.loc + " !== " + expect.loc); + } catch (err) { + //require("fs").writeFileSync(expect.loc, actualCode); + throw err; + } } if (task.sourceMap) { diff --git a/test/bin.js b/test/bin.js index e8bd33b22e..91cb7c7716 100644 --- a/test/bin.js +++ b/test/bin.js @@ -1,15 +1,15 @@ if (process.env.running_under_istanbul) return; -var readdir = require("fs-readdir-recursive"); -var helper = require("./_helper"); -var assert = require("assert"); -var rimraf = require("rimraf"); -var mkdirp = require("mkdirp"); -var child = require("child_process"); -var path = require("path"); -var chai = require("chai"); -var fs = require("fs"); -var _ = require("lodash"); +var readdir = require("fs-readdir-recursive"); +var helper = require("./_helper"); +var assert = require("assert"); +var rimraf = require("rimraf"); +var outputFileSync = require("output-file-sync"); +var child = require("child_process"); +var path = require("path"); +var chai = require("chai"); +var fs = require("fs"); +var _ = require("lodash"); var fixtureLoc = __dirname + "/fixtures/bin"; var tmpLoc = __dirname + "/tmp"; @@ -27,10 +27,7 @@ var readDir = function (loc) { var saveInFiles = function (files) { _.each(files, function (content, filename) { - var up = path.normalize(filename + "/.."); - mkdirp.sync(up); - - fs.writeFileSync(filename, content); + outputFileSync(filename, content); }); }; diff --git a/test/fixtures/generation/types/VariableDeclaration-VariableDeclarator/expected.js b/test/fixtures/generation/types/VariableDeclaration-VariableDeclarator/expected.js index 86c14e5b1f..487fff2ae2 100644 --- a/test/fixtures/generation/types/VariableDeclaration-VariableDeclarator/expected.js +++ b/test/fixtures/generation/types/VariableDeclaration-VariableDeclarator/expected.js @@ -5,8 +5,10 @@ let foo = "foo"; var foo = "bar"; const foo = "foo"; -let foo, bar = "bar"; -var foo, bar = "bar"; +let foo, + bar = "bar"; +var foo, + bar = "bar"; let foo = "foo", bar = "bar"; diff --git a/test/fixtures/transformation/es6-classes-loose/accessing-super-class/expected.js b/test/fixtures/transformation/es6-classes-loose/accessing-super-class/expected.js index 6c9bd8d668..92710413da 100644 --- a/test/fixtures/transformation/es6-classes-loose/accessing-super-class/expected.js +++ b/test/fixtures/transformation/es6-classes-loose/accessing-super-class/expected.js @@ -1,11 +1,6 @@ "use strict"; var _slice = Array.prototype.slice; -var _prototypeProperties = function (child, staticProps, instanceProps) { - if (staticProps) Object.defineProperties(child, staticProps); - if (instanceProps) Object.defineProperties(child.prototype, instanceProps); -}; - var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); @@ -37,21 +32,19 @@ var Test = (function (Foo) { _inherits(Test, Foo); - Test.prototype.test = function () { + Test.prototype.test = function test() { var _Foo$prototype$test3, _Foo$prototype$test4; Foo.prototype.test.call(this); (_Foo$prototype$test3 = Foo.prototype.test).call.apply(_Foo$prototype$test3, [this].concat(_slice.call(arguments))); (_Foo$prototype$test4 = Foo.prototype.test).call.apply(_Foo$prototype$test4, [this, "test"].concat(_slice.call(arguments))); }; - Test.foo = function () { + Test.foo = function foo() { var _Foo$foo, _Foo$foo2; Foo.foo.call(this); (_Foo$foo = Foo.foo).call.apply(_Foo$foo, [this].concat(_slice.call(arguments))); (_Foo$foo2 = Foo.foo).call.apply(_Foo$foo2, [this, "test"].concat(_slice.call(arguments))); }; - _prototypeProperties(Test, {}, {}); - return Test; })(Foo); diff --git a/test/fixtures/transformation/es6-classes-loose/calling-super-properties/expected.js b/test/fixtures/transformation/es6-classes-loose/calling-super-properties/expected.js index 529795221c..065e6fc263 100644 --- a/test/fixtures/transformation/es6-classes-loose/calling-super-properties/expected.js +++ b/test/fixtures/transformation/es6-classes-loose/calling-super-properties/expected.js @@ -1,10 +1,5 @@ "use strict"; -var _prototypeProperties = function (child, staticProps, instanceProps) { - if (staticProps) Object.defineProperties(child, staticProps); - if (instanceProps) Object.defineProperties(child.prototype, instanceProps); -}; - var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); @@ -28,11 +23,9 @@ var Test = (function (Foo) { _inherits(Test, Foo); - Test.test = function () { + Test.test = function test() { return Foo.wow.call(this); }; - _prototypeProperties(Test, {}); - return Test; })(Foo); diff --git a/test/fixtures/transformation/es6-classes/accessing-super-class/expected.js b/test/fixtures/transformation/es6-classes/accessing-super-class/expected.js index a762c97203..e783b08f76 100644 --- a/test/fixtures/transformation/es6-classes/accessing-super-class/expected.js +++ b/test/fixtures/transformation/es6-classes/accessing-super-class/expected.js @@ -61,7 +61,7 @@ var Test = (function (Foo) { _prototypeProperties(Test, { foo: { - value: function () { + value: function foo() { var _get4; _get(Object.getPrototypeOf(Test), "foo", this).call(this); _get(Object.getPrototypeOf(Test), "foo", this).apply(this, arguments); @@ -73,7 +73,7 @@ var Test = (function (Foo) { } }, { test: { - value: function () { + value: function test() { var _get5; _get(Object.getPrototypeOf(Test.prototype), "test", this).call(this); _get(Object.getPrototypeOf(Test.prototype), "test", this).apply(this, arguments); diff --git a/test/fixtures/transformation/es6-classes/calling-super-properties/expected.js b/test/fixtures/transformation/es6-classes/calling-super-properties/expected.js index fc04a0bab4..b82d42c705 100644 --- a/test/fixtures/transformation/es6-classes/calling-super-properties/expected.js +++ b/test/fixtures/transformation/es6-classes/calling-super-properties/expected.js @@ -52,7 +52,7 @@ var Test = (function (Foo) { _prototypeProperties(Test, { test: { - value: function () { + value: function test() { return _get(Object.getPrototypeOf(Test), "wow", this).call(this); }, writable: true, diff --git a/test/fixtures/transformation/es6-classes/instance-method/expected.js b/test/fixtures/transformation/es6-classes/instance-method/expected.js index 8887b8d645..f632a93cba 100644 --- a/test/fixtures/transformation/es6-classes/instance-method/expected.js +++ b/test/fixtures/transformation/es6-classes/instance-method/expected.js @@ -10,7 +10,7 @@ var Test = (function () { _prototypeProperties(Test, null, { test: { - value: function () { + value: function test() { return 5 + 5; }, writable: true, diff --git a/test/fixtures/transformation/es6-classes/statement/expected.js b/test/fixtures/transformation/es6-classes/statement/expected.js index 1a10318ee2..ac555c8256 100644 --- a/test/fixtures/transformation/es6-classes/statement/expected.js +++ b/test/fixtures/transformation/es6-classes/statement/expected.js @@ -9,16 +9,16 @@ var BaseView = function BaseView() { this.autoRender = true; }; -var BaseView = function () { +var BaseView = function BaseView() { this.autoRender = true; }; var BaseView = (function () { - var _class2 = function () {}; + function BaseView() {} - _prototypeProperties(_class2, null, { + _prototypeProperties(BaseView, null, { foo: { - value: function () { + value: function foo() { this.autoRender = true; }, writable: true, @@ -27,5 +27,5 @@ var BaseView = (function () { } }); - return _class2; + return BaseView; })(); diff --git a/test/fixtures/transformation/es6-classes/static/expected.js b/test/fixtures/transformation/es6-classes/static/expected.js index 408d9a036a..b219f347c7 100644 --- a/test/fixtures/transformation/es6-classes/static/expected.js +++ b/test/fixtures/transformation/es6-classes/static/expected.js @@ -10,7 +10,7 @@ var A = (function () { _prototypeProperties(A, { a: { - value: function () {}, + value: function a() {}, writable: true, enumerable: true, configurable: true diff --git a/test/fixtures/transformation/es6-destructuring/parameters/expected.js b/test/fixtures/transformation/es6-destructuring/parameters/expected.js index a6a1d3dfe6..830eb15edc 100644 --- a/test/fixtures/transformation/es6-destructuring/parameters/expected.js +++ b/test/fixtures/transformation/es6-destructuring/parameters/expected.js @@ -32,16 +32,16 @@ function unpackObject(_ref2) { console.log(unpackObject({ title: "title", author: "author" })); var unpackArray = function (_ref3, _ref4) { - var _ref3 = _slicedToArray(_ref3, 3); + var _ref32 = _slicedToArray(_ref3, 3); - var a = _ref3[0]; - var b = _ref3[1]; - var c = _ref3[2]; - var _ref4 = _slicedToArray(_ref4, 3); + var a = _ref32[0]; + var b = _ref32[1]; + var c = _ref32[2]; + var _ref42 = _slicedToArray(_ref4, 3); - var x = _ref4[0]; - var y = _ref4[1]; - var z = _ref4[2]; + var x = _ref42[0]; + var y = _ref42[1]; + var z = _ref42[2]; return a + b + c; }; diff --git a/test/fixtures/transformation/es6-for-of-loose/identifier/expected.js b/test/fixtures/transformation/es6-for-of-loose/identifier/expected.js index 4a7ea3a6a5..cb83442b64 100644 --- a/test/fixtures/transformation/es6-for-of-loose/identifier/expected.js +++ b/test/fixtures/transformation/es6-for-of-loose/identifier/expected.js @@ -1,9 +1,6 @@ "use strict"; -for (var _iterator = arr, - _isArray = Array.isArray(_iterator), - _i = 0, - _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { +for (var _iterator = arr, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { if (_isArray) { if (_i >= _iterator.length) break; i = _iterator[_i++]; diff --git a/test/fixtures/transformation/es6-for-of-loose/let/expected.js b/test/fixtures/transformation/es6-for-of-loose/let/expected.js index 94848fe0c1..261cb6bf7b 100644 --- a/test/fixtures/transformation/es6-for-of-loose/let/expected.js +++ b/test/fixtures/transformation/es6-for-of-loose/let/expected.js @@ -1,9 +1,6 @@ "use strict"; -for (var _iterator = arr, - _isArray = Array.isArray(_iterator), - _i = 0, - _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { +for (var _iterator = arr, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var i = undefined; if (_isArray) { if (_i >= _iterator.length) break; diff --git a/test/fixtures/transformation/es6-for-of-loose/multiple/expected.js b/test/fixtures/transformation/es6-for-of-loose/multiple/expected.js index a20dee268b..46d8e3f0aa 100644 --- a/test/fixtures/transformation/es6-for-of-loose/multiple/expected.js +++ b/test/fixtures/transformation/es6-for-of-loose/multiple/expected.js @@ -1,9 +1,6 @@ "use strict"; -for (var _iterator = arr, - _isArray = Array.isArray(_iterator), - _i = 0, - _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { +for (var _iterator = arr, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var i; if (_isArray) { if (_i >= _iterator.length) break; @@ -15,10 +12,7 @@ for (var _iterator = arr, } } -for (var _iterator2 = numbers, - _isArray2 = Array.isArray(_iterator2), - _i2 = 0, - _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { +for (var _iterator2 = numbers, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var i; if (_isArray2) { if (_i2 >= _iterator2.length) break; diff --git a/test/fixtures/transformation/es6-for-of-loose/var/expected.js b/test/fixtures/transformation/es6-for-of-loose/var/expected.js index 3695bf3454..d5dd9060d5 100644 --- a/test/fixtures/transformation/es6-for-of-loose/var/expected.js +++ b/test/fixtures/transformation/es6-for-of-loose/var/expected.js @@ -1,9 +1,6 @@ "use strict"; -for (var _iterator = arr, - _isArray = Array.isArray(_iterator), - _i = 0, - _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { +for (var _iterator = arr, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var i; if (_isArray) { if (_i >= _iterator.length) break; diff --git a/test/fixtures/transformation/es6-generators/include-regenerator/actual.js b/test/fixtures/transformation/es6-generators/include-regenerator/actual.js deleted file mode 100644 index d82da75f43..0000000000 --- a/test/fixtures/transformation/es6-generators/include-regenerator/actual.js +++ /dev/null @@ -1 +0,0 @@ -function *x() {} diff --git a/test/fixtures/transformation/es6-generators/include-regenerator/exec.js b/test/fixtures/transformation/es6-generators/include-regenerator/exec.js new file mode 100644 index 0000000000..cea5770203 --- /dev/null +++ b/test/fixtures/transformation/es6-generators/include-regenerator/exec.js @@ -0,0 +1 @@ +assert.ok(typeof regeneratorRuntime !== "undefined"); diff --git a/test/fixtures/transformation/es6-generators/include-regenerator/expected.js b/test/fixtures/transformation/es6-generators/include-regenerator/expected.js deleted file mode 100644 index 3b1d9b0960..0000000000 --- a/test/fixtures/transformation/es6-generators/include-regenerator/expected.js +++ /dev/null @@ -1,400 +0,0 @@ -"use strict"; - -!(function () { - var hasOwn = Object.prototype.hasOwnProperty; - var undefined; - var iteratorSymbol = typeof Symbol === "function" && Symbol.iterator || "@@iterator"; - if (typeof regeneratorRuntime === "object") { - return; - } - var runtime = regeneratorRuntime = typeof exports === "undefined" ? {} : exports; - function wrap(innerFn, outerFn, self, tryList) { - return new Generator(innerFn, outerFn, self || null, tryList || []); - } - - runtime.wrap = wrap; - var GenStateSuspendedStart = "suspendedStart"; - - var GenStateSuspendedYield = "suspendedYield"; - - var GenStateExecuting = "executing"; - - var GenStateCompleted = "completed"; - - var ContinueSentinel = {}; - - function GeneratorFunction() {} - - function GeneratorFunctionPrototype() {} - - var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype; - GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; - GeneratorFunctionPrototype.constructor = GeneratorFunction; - GeneratorFunction.displayName = "GeneratorFunction"; - runtime.isGeneratorFunction = function (genFun) { - var ctor = typeof genFun === "function" && genFun.constructor; - return ctor ? ctor === GeneratorFunction || (ctor.displayName || ctor.name) === "GeneratorFunction" : false; - }; - - runtime.mark = function (genFun) { - genFun.__proto__ = GeneratorFunctionPrototype; - genFun.prototype = Object.create(Gp); - return genFun; - }; - - runtime.async = function (innerFn, outerFn, self, tryList) { - return new Promise(function (resolve, reject) { - var generator = wrap(innerFn, outerFn, self, tryList); - - var callNext = step.bind(generator.next); - - var callThrow = step.bind(generator["throw"]); - - function step(arg) { - try { - var info = this(arg); - - var value = info.value; - } catch (error) { - return reject(error); - } - if (info.done) { - resolve(value); - } else { - Promise.resolve(value).then(callNext, callThrow); - } - } - - callNext(); - }); - }; - - function Generator(innerFn, outerFn, self, tryList) { - var generator = outerFn ? Object.create(outerFn.prototype) : this; - var context = new Context(tryList); - var state = GenStateSuspendedStart; - function invoke(method, arg) { - if (state === GenStateExecuting) { - throw new Error("Generator is already running"); - } - if (state === GenStateCompleted) { - return doneResult(); - } - while (true) { - var delegate = context.delegate; - if (delegate) { - try { - var info = delegate.iterator[method](arg); - - method = "next"; - arg = undefined; - } catch (uncaught) { - context.delegate = null; - method = "throw"; - arg = uncaught; - continue; - } - if (info.done) { - context[delegate.resultName] = info.value; - context.next = delegate.nextLoc; - } else { - state = GenStateSuspendedYield; - return info; - } - context.delegate = null; - } - if (method === "next") { - if (state === GenStateSuspendedStart && typeof arg !== "undefined") { - throw new TypeError("attempt to send " + JSON.stringify(arg) + " to newborn generator"); - } - if (state === GenStateSuspendedYield) { - context.sent = arg; - } else { - delete context.sent; - } - } else if (method === "throw") { - if (state === GenStateSuspendedStart) { - state = GenStateCompleted; - throw arg; - } - if (context.dispatchException(arg)) { - method = "next"; - arg = undefined; - } - } else if (method === "return") { - context.abrupt("return", arg); - } - state = GenStateExecuting; - try { - var value = innerFn.call(self, context); - - state = context.done ? GenStateCompleted : GenStateSuspendedYield; - var info = { - value: value, - done: context.done - }; - - if (value === ContinueSentinel) { - if (context.delegate && method === "next") { - arg = undefined; - } - } else { - return info; - } - } catch (thrown) { - state = GenStateCompleted; - if (method === "next") { - context.dispatchException(thrown); - } else { - arg = thrown; - } - } - } - } - - generator.next = invoke.bind(generator, "next"); - generator["throw"] = invoke.bind(generator, "throw"); - generator["return"] = invoke.bind(generator, "return"); - return generator; - } - - Gp[iteratorSymbol] = function () { - return this; - }; - - Gp.toString = function () { - return "[object Generator]"; - }; - - function pushTryEntry(triple) { - var entry = { - tryLoc: triple[0] - }; - - if (1 in triple) { - entry.catchLoc = triple[1]; - } - if (2 in triple) { - entry.finallyLoc = triple[2]; - } - this.tryEntries.push(entry); - } - - function resetTryEntry(entry, i) { - var record = entry.completion || {}; - record.type = i === 0 ? "normal" : "return"; - delete record.arg; - entry.completion = record; - } - - function Context(tryList) { - this.tryEntries = [{ - tryLoc: "root" - }]; - tryList.forEach(pushTryEntry, this); - - this.reset(); - } - - runtime.keys = function (object) { - var keys = []; - - for (var key in object) { - keys.push(key); - } - - keys.reverse(); - - return function next() { - while (keys.length) { - var key = keys.pop(); - - if (key in object) { - next.value = key; - next.done = false; - return next; - } - } - next.done = true; - return next; - }; - }; - - function values(iterable) { - if (iterable) { - var iteratorMethod = iterable[iteratorSymbol]; - if (iteratorMethod) { - return iteratorMethod.call(iterable); - } - if (typeof iterable.next === "function") { - return iterable; - } - if (!isNaN(iterable.length)) { - var i = -1; - function next() { - while (++i < iterable.length) { - if (hasOwn.call(iterable, i)) { - next.value = iterable[i]; - next.done = false; - return next; - } - } - next.value = undefined; - next.done = true; - return next; - } - - return next.next = next; - } - } - return { - next: doneResult - }; - } - - runtime.values = values; - function doneResult() { - return { - value: undefined, - done: true - }; - } - - Context.prototype = { - constructor: Context, - reset: function () { - this.prev = 0; - this.next = 0; - this.sent = undefined; - this.done = false; - this.delegate = null; - this.tryEntries.forEach(resetTryEntry); - - for (var tempIndex = 0, tempName; hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20; ++tempIndex) { - this[tempName] = null; - } - }, - stop: function () { - this.done = true; - var rootEntry = this.tryEntries[0]; - var rootRecord = rootEntry.completion; - if (rootRecord.type === "throw") { - throw rootRecord.arg; - } - return this.rval; - }, - dispatchException: function (exception) { - if (this.done) { - throw exception; - } - var context = this; - function handle(loc, caught) { - record.type = "throw"; - record.arg = exception; - context.next = loc; - return !!caught; - } - - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - var record = entry.completion; - if (entry.tryLoc === "root") { - return handle("end"); - } - if (entry.tryLoc <= this.prev) { - var hasCatch = hasOwn.call(entry, "catchLoc"); - - var hasFinally = hasOwn.call(entry, "finallyLoc"); - - if (hasCatch && hasFinally) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } else if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - } else if (hasCatch) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } - } else if (hasFinally) { - if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - } else { - throw new Error("try statement without catch or finally"); - } - } - } - }, - _findFinallyEntry: function (finallyLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && (entry.finallyLoc === finallyLoc || this.prev < entry.finallyLoc)) { - return entry; - } - } - }, - abrupt: function (type, arg) { - var entry = this._findFinallyEntry(); - - var record = entry ? entry.completion : {}; - record.type = type; - record.arg = arg; - if (entry) { - this.next = entry.finallyLoc; - } else { - this.complete(record); - } - return ContinueSentinel; - }, - complete: function (record) { - if (record.type === "throw") { - throw record.arg; - } - if (record.type === "break" || record.type === "continue") { - this.next = record.arg; - } else if (record.type === "return") { - this.rval = record.arg; - this.next = "end"; - } - return ContinueSentinel; - }, - finish: function (finallyLoc) { - var entry = this._findFinallyEntry(finallyLoc); - - return this.complete(entry.completion); - }, - "catch": function (tryLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - if (entry.tryLoc === tryLoc) { - var record = entry.completion; - if (record.type === "throw") { - var thrown = record.arg; - resetTryEntry(entry, i); - } - return thrown; - } - } - - throw new Error("illegal catch attempt"); - }, - delegateYield: function (iterable, resultName, nextLoc) { - this.delegate = { - iterator: values(iterable), - resultName: resultName, - nextLoc: nextLoc - }; - return ContinueSentinel; - } - }; -})(); -var x = regeneratorRuntime.mark(function x() { - return regeneratorRuntime.wrap(function x$(context$1$0) { - while (1) switch (context$1$0.prev = context$1$0.next) { - case 0: - case "end": - return context$1$0.stop(); - } - }, x, this); -}); diff --git a/test/fixtures/transformation/es6-let-scoping/exec-block-scoped-2/exec.js b/test/fixtures/transformation/es6-let-scoping/exec-block-scoped-2/exec.js new file mode 100644 index 0000000000..e98bf47f5e --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/exec-block-scoped-2/exec.js @@ -0,0 +1,12 @@ +assert.equal(() => { + let sum = 0; + let a = 0; + { + let a = 10; + for (let i = 0; i < a; i++) { + let a = 1; + sum += (() => a)(); + } + } + return sum; +}(), 10); diff --git a/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/actual.js b/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/actual.js new file mode 100644 index 0000000000..34a3ccd844 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/actual.js @@ -0,0 +1,2 @@ +qux; +let qux = 456; diff --git a/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/options.json b/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/options.json new file mode 100644 index 0000000000..bcd3d8f6e2 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/temporal-dead-zone/options.json @@ -0,0 +1,4 @@ +{ + "throws": "Temporal dead zone - accessing a variable before it's initialized", + "optional": ["blockScopingTDZ"] +} diff --git a/test/fixtures/transformation/es6-rest-parameters/arrow-functions/expected.js b/test/fixtures/transformation/es6-rest-parameters/arrow-functions/expected.js index cb971ebc77..4b72d8d4cf 100644 --- a/test/fixtures/transformation/es6-rest-parameters/arrow-functions/expected.js +++ b/test/fixtures/transformation/es6-rest-parameters/arrow-functions/expected.js @@ -1,9 +1,7 @@ "use strict"; var concat = function () { - var arrs = []; - - for (var _key = 0; _key < arguments.length; _key++) { + for (var _len = arguments.length, arrs = Array(_len), _key = 0; _key < _len; _key++) { arrs[_key] = arguments[_key]; } }; diff --git a/test/fixtures/transformation/es6-rest-parameters/multiple/expected.js b/test/fixtures/transformation/es6-rest-parameters/multiple/expected.js index 56ff1c407f..2967aef64a 100644 --- a/test/fixtures/transformation/es6-rest-parameters/multiple/expected.js +++ b/test/fixtures/transformation/es6-rest-parameters/multiple/expected.js @@ -1,17 +1,13 @@ "use strict"; var t = function (f) { - var items = []; - - for (var _key = 1; _key < arguments.length; _key++) { + for (var _len = arguments.length, items = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { items[_key - 1] = arguments[_key]; } }; function t(f) { - var items = []; - - for (var _key2 = 1; _key2 < arguments.length; _key2++) { + for (var _len2 = arguments.length, items = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { items[_key2 - 1] = arguments[_key2]; } } diff --git a/test/fixtures/transformation/es6-rest-parameters/single/expected.js b/test/fixtures/transformation/es6-rest-parameters/single/expected.js index 316b3ec9a4..580c7706dd 100644 --- a/test/fixtures/transformation/es6-rest-parameters/single/expected.js +++ b/test/fixtures/transformation/es6-rest-parameters/single/expected.js @@ -1,17 +1,13 @@ "use strict"; var t = function () { - var items = []; - - for (var _key = 0; _key < arguments.length; _key++) { + for (var _len = arguments.length, items = Array(_len), _key = 0; _key < _len; _key++) { items[_key] = arguments[_key]; } }; function t() { - var items = []; - - for (var _key2 = 0; _key2 < arguments.length; _key2++) { + for (var _len2 = arguments.length, items = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { items[_key2] = arguments[_key2]; } } diff --git a/test/fixtures/transformation/es7-abstract-references/private/expected.js b/test/fixtures/transformation/es7-abstract-references/private/expected.js index d94a941e04..1af25539ca 100644 --- a/test/fixtures/transformation/es7-abstract-references/private/expected.js +++ b/test/fixtures/transformation/es7-abstract-references/private/expected.js @@ -16,7 +16,7 @@ var H = (function () { var J = new WeakMap(), K = new WeakMap(); var I = new WeakMap(); - var _class = function () {}; + function H() {} - return _class; + return H; })(); diff --git a/test/fixtures/transformation/es7-array-comprehension/array-expression-single-if/expected.js b/test/fixtures/transformation/es7-array-comprehension/array-expression-single-if/expected.js index 37496b18dc..0f3f625138 100644 --- a/test/fixtures/transformation/es7-array-comprehension/array-expression-single-if/expected.js +++ b/test/fixtures/transformation/es7-array-comprehension/array-expression-single-if/expected.js @@ -4,9 +4,9 @@ var arr = (function () { var _arr = []; for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { - var _i = _step.value; - if (_i > 1) { - _arr.push(_i * _i); + var i = _step.value; + if (i > 1) { + _arr.push(i * i); } } diff --git a/test/fixtures/transformation/es7-array-comprehension/multiple-if/expected.js b/test/fixtures/transformation/es7-array-comprehension/multiple-if/expected.js index c5ba3b5331..6b486b45f5 100644 --- a/test/fixtures/transformation/es7-array-comprehension/multiple-if/expected.js +++ b/test/fixtures/transformation/es7-array-comprehension/multiple-if/expected.js @@ -6,9 +6,9 @@ var seattlers = (function () { for (var _iterator = countries[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { var customers = _step.value; for (var _iterator2 = customers[Symbol.iterator](), _step2; !(_step2 = _iterator2.next()).done;) { - var _c = _step2.value; - if (_c.city == "Seattle") { - _seattlers.push({ name: _c.name, age: _c.age }); + var c = _step2.value; + if (c.city == "Seattle") { + _seattlers.push({ name: c.name, age: c.age }); } } } diff --git a/test/fixtures/transformation/es7-array-comprehension/single-if/expected.js b/test/fixtures/transformation/es7-array-comprehension/single-if/expected.js index 25915fc7f0..6e95f9cadc 100644 --- a/test/fixtures/transformation/es7-array-comprehension/single-if/expected.js +++ b/test/fixtures/transformation/es7-array-comprehension/single-if/expected.js @@ -4,9 +4,9 @@ var arr = (function () { var _arr = []; for (var _iterator = nums[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { - var _i = _step.value; - if (_i > 1) { - _arr.push(_i * _i); + var i = _step.value; + if (i > 1) { + _arr.push(i * i); } } diff --git a/test/fixtures/transformation/es7-exponentian-operator/comprehensive/exec.js b/test/fixtures/transformation/es7-exponentian-operator/comprehensive/exec.js new file mode 100644 index 0000000000..10dda4ed30 --- /dev/null +++ b/test/fixtures/transformation/es7-exponentian-operator/comprehensive/exec.js @@ -0,0 +1,21 @@ +assert.equal(8, 2 ** 3); +assert.equal(24, 3 * 2 ** 3); +var x = 2; +assert.equal(8, 2 ** ++x); +assert.equal(1, 2 ** -1 * 2); + +var calls = 0; +var q = {q: 3}; +var o = { + get p() { + calls++; + return q; + } +}; + +o.p.q **= 2; +assert.equal(1, calls); +assert.equal(9, o.p.q); + +assert.equal(512, 2 ** (3 ** 2)); +assert.equal(512, 2 ** 3 ** 2); diff --git a/test/fixtures/transformation/optional-typeof-symbol/basic/expected.js b/test/fixtures/transformation/optional-typeof-symbol/basic/expected.js index c33ed64571..a93f2923c6 100644 --- a/test/fixtures/transformation/optional-typeof-symbol/basic/expected.js +++ b/test/fixtures/transformation/optional-typeof-symbol/basic/expected.js @@ -5,4 +5,4 @@ var _typeof = function (obj) { }; var s = Symbol("s"); -assert.equal(_typeof(s), "symbol"); +assert.equal(typeof s === "undefined" ? "undefined" : _typeof(s), "symbol"); diff --git a/test/fixtures/transformation/optional-undeclared-variable-check/declared/exec.js b/test/fixtures/transformation/optional-undeclared-variable-check/declared/exec.js new file mode 100644 index 0000000000..e2721feb4a --- /dev/null +++ b/test/fixtures/transformation/optional-undeclared-variable-check/declared/exec.js @@ -0,0 +1,5 @@ +function foo() { + +} + +foo(); diff --git a/test/fixtures/transformation/optional-undeclared-variable-check/options.json b/test/fixtures/transformation/optional-undeclared-variable-check/options.json new file mode 100644 index 0000000000..535d39109b --- /dev/null +++ b/test/fixtures/transformation/optional-undeclared-variable-check/options.json @@ -0,0 +1,3 @@ +{ + "optional": ["undeclaredVariableCheck"] +} diff --git a/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/exec.js b/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/exec.js new file mode 100644 index 0000000000..a280f9a5cc --- /dev/null +++ b/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/exec.js @@ -0,0 +1 @@ +foo(); diff --git a/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/options.json b/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/options.json new file mode 100644 index 0000000000..a661ffc703 --- /dev/null +++ b/test/fixtures/transformation/optional-undeclared-variable-check/undeclared/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Reference to undeclared variable" +} diff --git a/test/fixtures/transformation/playground/mallet-operator/exec.js b/test/fixtures/transformation/playground/mallet-operator/exec.js new file mode 100644 index 0000000000..b784b844bd --- /dev/null +++ b/test/fixtures/transformation/playground/mallet-operator/exec.js @@ -0,0 +1,106 @@ +var obj = {}; +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = {}; +assert.equal(obj.x ||= 2, 2); + +obj = { x: 1 }; +obj.x ||= 2; +assert.equal(obj.x, 1); + +obj = { x: 1 }; +assert.equal(obj.x ||= 2, 1); + +obj = { x: undefined } +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = { x: undefined } +assert.equal(obj.x ||= 2, 2); + +obj = { x: null } +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = { x: null } +assert.equal(obj.x ||= 2, 2); + +obj = { x: false } +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = { x: false } +assert.equal(obj.x ||= 2, 2); + +obj = { x: '' } +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = { x: '' } +assert.equal(obj.x ||= 2, 2); + +obj = { x: 0 } +obj.x ||= 2; +assert.equal(obj.x, 2); + +obj = { x: 0 } +assert.equal(obj.x ||= 2, 2); + +obj = undefined; +obj ||= 2; +assert.equal(obj, 2); + +obj = undefined; +assert.equal(obj ||= 2 , 2); + +obj = 1; +obj ||= 2; +assert.equal(obj, 1); + +obj = 1; +assert.equal(obj ||= 2 , 1); + +obj = null; +obj ||= 2; +assert.equal(obj, 2); + +obj = null; +assert.equal(obj ||= 2 , 2); + +obj = false; +obj ||= 2; +assert.equal(obj, 2); + +obj = false; +assert.equal(obj ||= 2 , 2); + +obj = ''; +obj ||= 2; +assert.equal(obj, 2); + +obj = ''; +assert.equal(obj ||= 2 , 2); + +obj = 0; +obj ||= 2; +assert.equal(obj, 2); + +obj = 0; +assert.equal(obj ||= 2 , 2); + +var calls = 0; +var q = { q: 3 }; +var o = { + get p() { + calls++; + return q; + } +}; + +o.p.q ||= 2; +assert.equal(1, calls); +o.p.f ||= 2; +assert.equal(2, calls); +assert.equal(3, o.p.q); +assert.equal(2, o.p.f); diff --git a/test/fixtures/transformation/playground/memoization-assignment-operator/actual.js b/test/fixtures/transformation/playground/memoization-assignment-operator/actual.js deleted file mode 100644 index b2d1521e2c..0000000000 --- a/test/fixtures/transformation/playground/memoization-assignment-operator/actual.js +++ /dev/null @@ -1,13 +0,0 @@ -var obj = {}; - -obj.x ?= 2; - -console.log(obj.x ?= 2); - -obj[x()] ?= 2; - -console.log(obj[x()] ?= 2); - -obj[y()][x()] ?= 2; - -console.log(obj[y()][x()] ?= 2); diff --git a/test/fixtures/transformation/playground/memoization-assignment-operator/exec.js b/test/fixtures/transformation/playground/memoization-assignment-operator/exec.js index 3e528c641d..716aca45a7 100644 --- a/test/fixtures/transformation/playground/memoization-assignment-operator/exec.js +++ b/test/fixtures/transformation/playground/memoization-assignment-operator/exec.js @@ -18,3 +18,19 @@ assert.equal(obj.x, undefined); obj = { x: undefined } assert.equal(obj.x ?= 2, undefined); + +var calls = 0; +var q = { q: 3 }; +var o = { + get p() { + calls++; + return q; + } +}; + +o.p.q ?= 2; +assert.equal(1, calls); +o.p.f ?= 2; +assert.equal(2, calls); +assert.equal(3, o.p.q); +assert.equal(2, o.p.f); diff --git a/test/fixtures/transformation/playground/memoization-assignment-operator/expected.js b/test/fixtures/transformation/playground/memoization-assignment-operator/expected.js deleted file mode 100644 index fbb64ac4f8..0000000000 --- a/test/fixtures/transformation/playground/memoization-assignment-operator/expected.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -var _obj2, _obj4, _x2, _obj$y2, _x4; -var _hasOwn = Object.prototype.hasOwnProperty; -var obj = {}; - -var _obj = obj; -if (!_hasOwn.call(_obj, "x")) _obj.x = 2; - - -console.log((_obj2 = obj, !_hasOwn.call(_obj2, "x") && (_obj2.x = 2), _obj2.x)); - -var _obj3 = obj; -var _x = x(); - -if (!_hasOwn.call(_obj3, _x)) _obj3[_x] = 2; - - -console.log((_obj4 = obj, _x2 = x(), !_hasOwn.call(_obj4, _x2) && (_obj4[_x2] = 2), _obj4[_x2])); - -var _obj$y = obj[y()]; -var _x3 = x(); - -if (!_hasOwn.call(_obj$y, _x3)) _obj$y[_x3] = 2; - - -console.log((_obj$y2 = obj[y()], _x4 = x(), !_hasOwn.call(_obj$y2, _x4) && (_obj$y2[_x4] = 2), _obj$y2[_x4])); diff --git a/test/test262.js b/test/test262.js index 12e31a8927..b8b8f8cb3d 100644 --- a/test/test262.js +++ b/test/test262.js @@ -1,4 +1,4 @@ -if (process.env.SIMPLE_6TO5_TESTS || process.env.running_under_istanbul) return; +if (process.env.SIMPLE_6TO5_TESTS) return; var transform = require("../lib/6to5/transformation/transform"); var readdir = require("fs-readdir-recursive"); diff --git a/test/traceur.js b/test/traceur.js index e066effd17..2d2866c015 100644 --- a/test/traceur.js +++ b/test/traceur.js @@ -14,6 +14,7 @@ require("./_transformation-helper")({ loc: traceurLoc + "/test/feature", ignoreSuites: [ + // these are all internal traceur tests or non-standard features "ObjectMixin", "Annotations", "TestRunner", @@ -40,10 +41,13 @@ require("./_transformation-helper")({ "Syntax/UseStrictEscapeSequence", "Syntax/UseStrictLineContinuation", - // the spec for these doesn't define syntax (as far as i could tell) + // experimental es7 - the spec hasn't been finalised yet // these both fail because of filter between blocks "ArrayComprehension/Simple", - "GeneratorComprehension/Simple" + "GeneratorComprehension/Simple", + + // yield has been added as a keyword in ES6 so this test is actually incorrect + "Yield/YieldIdentifier" ] }, { optional: ["typeofSymbol"], diff --git a/vendor/traceur b/vendor/traceur index 0c3dad3ae2..ed432fb5d4 160000 --- a/vendor/traceur +++ b/vendor/traceur @@ -1 +1 @@ -Subproject commit 0c3dad3ae217139b1a6b4b986cef57669e7b49c6 +Subproject commit ed432fb5d4e7575b05b54f68226eede97509c05e