diff --git a/CHANGELOG.md b/CHANGELOG.md index 010bca804f..179876d5b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,17 @@ +# 1.12.21 + + * Fix unneccesary let scoping replacement. + * Add `commonInterop` module formatter. Thanks [@Naddiseo](https://github.com/Naddiseo). + * Fix `return` outside of function body bug. Thanks [@brentburg](https://github.com/brentburg). + * Add more flexible option types. + # 1.12.20 * Append `sourceMappingURL` when using `bin/6to5` and output sourcemaps. # 1.12.19 - * Add `comments` option and `--remove-comments` flag. Thanks @[webpro](htps://github.com/webpro)! + * Add `comments` option and `--remove-comments` flag. Thanks [@webpro](htps://github.com/webpro). * Embed `regenerator`. # 1.12.18 @@ -13,7 +20,7 @@ # 1.12.17 - * Add `moduleName`, `sourceRoot` and `filenameRelative` options - thanks @[darvelo](https://github.com/darvelo)! + * Add `moduleName`, `sourceRoot` and `filenameRelative` options. Thanks [@darvelo](https://github.com/darvelo). * Traversal optimisations. # 1.12.16 diff --git a/doc/usage.md b/doc/usage.md index c27b25e5a2..02c845469b 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -115,10 +115,12 @@ to5.transformFile("filename.js", options, function (err, result) { sourceFileName: "filename", // The root from which all sources are relative + // Default: `moduleRoot` option. sourceRoot: "assets/scripts", // Optional prefix for the AMD module formatter that will be prepend to the // filename on module definitions + // Default: `sourceRoot` option. moduleRoot: "my-app", // Optionally replace all 6to5 helper declarations with a referenece to this diff --git a/lib/6to5/file.js b/lib/6to5/file.js index 18dc3fc86d..cf7daf2505 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -19,7 +19,7 @@ function File(opts) { } File.declarations = ["extends", "class-props", "slice", "apply-constructor", - "tagged-template-literal"]; + "tagged-template-literal", "interop-require"]; File.normaliseOptions = function (opts) { opts = _.cloneDeep(opts || {}); @@ -39,6 +39,17 @@ File.normaliseOptions = function (opts) { // normalise windows path separators to unix opts.filename = opts.filename.replace(/\\/g, "/"); + opts.blacklist = util.arrayify(opts.blacklist); + opts.whitelist = util.arrayify(opts.whitelist); + + _.defaults(opts, { + moduleRoot: opts.sourceRoot + }); + + _.defaults(opts, { + sourceRoot: opts.moduleRoot + }); + _.defaults(opts, { filenameRelative: opts.filename }); @@ -58,8 +69,8 @@ File.normaliseOptions = function (opts) { return opts; }; -File.prototype.getModuleFormatter = function (type, opts) { - var ModuleFormatter = transform.moduleFormatters[type]; +File.prototype.getModuleFormatter = function (type) { + var ModuleFormatter = _.isFunction(type) ? type : transform.moduleFormatters[type]; if (!ModuleFormatter) { var loc = util.resolve(type); diff --git a/lib/6to5/generation/generator.js b/lib/6to5/generation/generator.js index 82fce1f967..73511ec6a6 100644 --- a/lib/6to5/generation/generator.js +++ b/lib/6to5/generation/generator.js @@ -165,6 +165,8 @@ CodeGenerator.prototype.print = function (node, parent, opts) { }; CodeGenerator.prototype.printJoin = function (print, nodes, opts) { + if (!nodes || !nodes.length) return; + opts = opts || {}; var self = this; diff --git a/lib/6to5/generation/generators/types.js b/lib/6to5/generation/generators/types.js index 80c72b1a70..92b4b2509d 100644 --- a/lib/6to5/generation/generators/types.js +++ b/lib/6to5/generation/generators/types.js @@ -78,7 +78,7 @@ exports.Literal = function (node) { val = JSON.stringify(val); // escape unicode characters - val = val.replace(/[\u007f-\uffff]/g, function(c) { + val = val.replace(/[\u007f-\uffff]/g, function (c) { return "\\u" + ("0000" + c.charCodeAt(0).toString(16)).slice(-4); }); diff --git a/lib/6to5/index.js b/lib/6to5/index.js index 2585aab36d..924e08acb3 100644 --- a/lib/6to5/index.js +++ b/lib/6to5/index.js @@ -17,6 +17,9 @@ exports.polyfill = function () { exports.canCompile = util.canCompile; +// do not use this - this is for use by official maintained 6to5 plugins +exports._util = util; + exports.transform = transform; exports.transformFile = function (filename, opts, callback) { diff --git a/lib/6to5/register.js b/lib/6to5/register.js index d34f447533..30fc5e5a82 100644 --- a/lib/6to5/register.js +++ b/lib/6to5/register.js @@ -1,6 +1,7 @@ require("./polyfill"); var sourceMapSupport = require("source-map-support"); +var util = require("./util"); var to5 = require("./index"); var _ = require("lodash"); @@ -71,7 +72,8 @@ var loader = function (m, filename) { var result = to5.transformFileSync(filename, { whitelist: whitelist, blacklist: blacklist, - sourceMap: true + sourceMap: true, + modules: "commonInterop" }); maps[filename] = result.map; @@ -100,11 +102,11 @@ module.exports = function (opts) { if (_.isRegExp(opts)) opts = { ignore: opts }; if (opts.ignoreRegex != null) opts.ignore = opts.ignoreRegex; - if (opts.only != null) onlyRegex = opts.only; - if (opts.ignore != null) ignoreRegex = opts.ignore; + if (opts.only != null) onlyRegex = util.regexify(opts.only); + if (opts.ignore != null) ignoreRegex = util.regexify(opts.ignore); - if (opts.extensions) hookExtensions(opts.extensions); + if (opts.extensions) hookExtensions(util.arrayify(opts.extensions)); - if (opts.blacklist) blacklist = opts.blacklist; - if (opts.whitelist) whitelist = opts.whitelist; + if (opts.blacklist) blacklist = util.arrayify(opts.blacklist); + if (opts.whitelist) whitelist = util.arrayify(opts.whitelist); }; diff --git a/lib/6to5/templates/interop-require.js b/lib/6to5/templates/interop-require.js new file mode 100644 index 0000000000..b48ee8c30e --- /dev/null +++ b/lib/6to5/templates/interop-require.js @@ -0,0 +1,3 @@ +(function (obj) { + return obj && (obj["default"] || obj); +}) diff --git a/lib/6to5/transformation/modules/common-interop.js b/lib/6to5/transformation/modules/common-interop.js new file mode 100644 index 0000000000..97d64e828d --- /dev/null +++ b/lib/6to5/transformation/modules/common-interop.js @@ -0,0 +1,29 @@ +module.exports = CommonJSInteropFormatter; + +var CommonJSFormatter = require("./common"); +var util = require("../../util"); +var t = require("../../types"); + +function CommonJSInteropFormatter(file) { + this.file = file; +} + +util.inherits(CommonJSInteropFormatter, CommonJSFormatter); + +CommonJSInteropFormatter.prototype.importSpecifier = function (specifier, node, nodes) { + var variableName = t.getSpecifierName(specifier); + + // import foo from "foo"; + if (specifier.default) { + nodes.push(t.variableDeclaration("var", [ + t.variableDeclarator(variableName, + t.callExpression(this.file.addDeclaration("interop-require"), [util.template("require", { + MODULE_NAME: node.source.raw + })]) + ) + ])); + return; + } + + CommonJSFormatter.prototype.importSpecifier.apply(this, arguments); +}; diff --git a/lib/6to5/transformation/modules/umd.js b/lib/6to5/transformation/modules/umd.js index 84e4f8ecd4..93d9bafd78 100644 --- a/lib/6to5/transformation/modules/umd.js +++ b/lib/6to5/transformation/modules/umd.js @@ -5,10 +5,8 @@ var util = require("../../util"); var t = require("../../types"); var _ = require("lodash"); -function UMDFormatter(file, opts) { - this.file = file; - this.ids = {}; - this.insertModuleId = opts.amdModuleId; +function UMDFormatter() { + AMDFormatter.apply(this, arguments); } util.inherits(UMDFormatter, AMDFormatter); diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index cc467086ac..a0ba465c72 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -20,10 +20,11 @@ transform._ensureTransformerNames = function (type, keys) { transform.transformers = {}; transform.moduleFormatters = { - common: require("./modules/common"), - ignore: require("./modules/ignore"), - amd: require("./modules/amd"), - umd: require("./modules/umd") + common: require("./modules/common"), + commonInterop: require("./modules/common-interop"), + ignore: require("./modules/ignore"), + amd: require("./modules/amd"), + umd: require("./modules/umd") }; _.each({ diff --git a/lib/6to5/transformation/transformers/generators/emit/explode-expressions.js b/lib/6to5/transformation/transformers/generators/emit/explode-expressions.js new file mode 100644 index 0000000000..fe28897340 --- /dev/null +++ b/lib/6to5/transformation/transformers/generators/emit/explode-expressions.js @@ -0,0 +1,204 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * https://raw.github.com/facebook/regenerator/master/LICENSE file. An + * additional grant of patent rights can be found in the PATENTS file in + * the same directory. + */ + +var assert = require("assert"); +var loc = require("../util").loc; +var t = require("../../../../types"); + +exports.ParenthesizedExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(this.explodeExpression(path.get("expression"))); +}; + +exports.MemberExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.memberExpression( + this.explodeExpression(path.get("object")), + expr.computed ? explodeViaTempVar(null, path.get("property")) : expr.property, + expr.computed + )); +}; + +exports.CallExpression = function (expr, path, explodeViaTempVar, finish) { + var oldCalleePath = path.get("callee"); + var newCallee = this.explodeExpression(oldCalleePath); + + // If the callee was not previously a MemberExpression, then the + // CallExpression was "unqualified," meaning its `this` object should + // be the global object. If the exploded expression has become a + // MemberExpression, then we need to force it to be unqualified by + // using the (0, object.property)(...) trick; otherwise, it will + // receive the object of the MemberExpression as its `this` object. + if (!t.isMemberExpression(oldCalleePath.node) && t.isMemberExpression(newCallee)) { + newCallee = t.sequenceExpression([ + t.literal(0), + newCallee + ]); + } + + return finish(t.callExpression( + newCallee, + path.get("arguments").map(function (argPath) { + return explodeViaTempVar(null, argPath); + }) + )); +}; + +exports.NewExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.newExpression( + explodeViaTempVar(null, path.get("callee")), + path.get("arguments").map(function (argPath) { + return explodeViaTempVar(null, argPath); + }) + )); +}; + +exports.ObjectExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.objectExpression( + path.get("properties").map(function (propPath) { + return t.property( + propPath.value.kind, + propPath.value.key, + explodeViaTempVar(null, propPath.get("value")) + ); + }) + )); +}; + +exports.ArrayExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.arrayExpression( + path.get("elements").map(function (elemPath) { + return explodeViaTempVar(null, elemPath); + }) + )); +}; + +exports.SequenceExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { + var lastIndex = expr.expressions.length - 1; + var self = this; + var result; + + path.get("expressions").each(function (exprPath) { + if (exprPath.name === lastIndex) { + result = self.explodeExpression(exprPath, ignoreResult); + } else { + self.explodeExpression(exprPath, true); + } + }); + + return result; +}; + +exports.LogicalExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { + var after = loc(); + var result; + + if (!ignoreResult) { + result = this.makeTempVar(); + } + + var left = explodeViaTempVar(result, path.get("left")); + + if (expr.operator === "&&") { + this.jumpIfNot(left, after); + } else { + assert.strictEqual(expr.operator, "||"); + this.jumpIf(left, after); + } + + explodeViaTempVar(result, path.get("right"), ignoreResult); + + this.mark(after); + + return result; +}; + +exports.ConditionalExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { + var elseLoc = loc(); + var after = loc(); + var test = this.explodeExpression(path.get("test")); + var result; + + this.jumpIfNot(test, elseLoc); + + if (!ignoreResult) { + result = this.makeTempVar(); + } + + explodeViaTempVar(result, path.get("consequent"), ignoreResult); + this.jump(after); + + this.mark(elseLoc); + explodeViaTempVar(result, path.get("alternate"), ignoreResult); + + this.mark(after); + + return result; +}; + +exports.UnaryExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.unaryExpression( + expr.operator, + // Can't (and don't need to) break up the syntax of the argument. + // Think about delete a[b]. + this.explodeExpression(path.get("argument")), + !!expr.prefix + )); +}; + +exports.BinaryExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.binaryExpression( + expr.operator, + explodeViaTempVar(null, path.get("left")), + explodeViaTempVar(null, path.get("right")) + )); +}; + +exports.AssignmentExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.assignmentExpression( + expr.operator, + this.explodeExpression(path.get("left")), + this.explodeExpression(path.get("right")) + )); +}; + +exports.UpdateExpression = function (expr, path, explodeViaTempVar, finish) { + return finish(t.updateExpression( + expr.operator, + this.explodeExpression(path.get("argument")), + expr.prefix + )); +}; + +exports.YieldExpression = function (expr, path) { + var after = loc(); + var arg = expr.argument && this.explodeExpression(path.get("argument")); + var result; + + if (arg && expr.delegate) { + result = this.makeTempVar(); + + this.emit(t.returnStatement(t.callExpression( + this.contextProperty("delegateYield"), [ + arg, + t.literal(result.property.name), + after + ] + ))); + + this.mark(after); + + return result; + } + + this.emitAssign(this.contextProperty("next"), after); + this.emit(t.returnStatement(arg || null)); + this.mark(after); + + return this.contextProperty("sent"); +}; diff --git a/lib/6to5/transformation/transformers/generators/emit/explode-statements.js b/lib/6to5/transformation/transformers/generators/emit/explode-statements.js new file mode 100644 index 0000000000..9f73681883 --- /dev/null +++ b/lib/6to5/transformation/transformers/generators/emit/explode-statements.js @@ -0,0 +1,334 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * https://raw.github.com/facebook/regenerator/master/LICENSE file. An + * additional grant of patent rights can be found in the PATENTS file in + * the same directory. + */ + +var assert = require("assert"); +var types = require("ast-types"); +var leap = require("../leap"); +var util = require("../util"); +var t = require("../../../../types"); + +var runtimeKeysMethod = util.runtimeProperty("keys"); +var loc = util.loc; + +exports.ExpressionStatement = function (path) { + this.explodeExpression(path.get("expression"), true); +}; + +exports.LabeledStatement = function (path, stmt) { + this.explodeStatement(path.get("body"), stmt.label); +}; + +exports.WhileStatement = function (path, stmt, labelId) { + var before = loc(); + var after = loc(); + + this.mark(before); + this.jumpIfNot(this.explodeExpression(path.get("test")), after); + this.leapManager.withEntry( + new leap.LoopEntry(after, before, labelId), + function () { this.explodeStatement(path.get("body")); } + ); + this.jump(before); + this.mark(after); +}; + +exports.DoWhileStatement = function (path, stmt, labelId) { + var first = loc(); + var test = loc(); + var after = loc(); + + this.mark(first); + this.leapManager.withEntry( + new leap.LoopEntry(after, test, labelId), + function () { this.explode(path.get("body")); } + ); + this.mark(test); + this.jumpIf(this.explodeExpression(path.get("test")), first); + this.mark(after); +}; + +exports.ForStatement = function (path, stmt, labelId) { + var head = loc(); + var update = loc(); + var after = loc(); + + if (stmt.init) { + // We pass true here to indicate that if stmt.init is an expression + // then we do not care about its result. + this.explode(path.get("init"), true); + } + + this.mark(head); + + if (stmt.test) { + this.jumpIfNot(this.explodeExpression(path.get("test")), after); + } else { + // No test means continue unconditionally. + } + + this.leapManager.withEntry( + new leap.LoopEntry(after, update, labelId), + function () { this.explodeStatement(path.get("body")); } + ); + + this.mark(update); + + if (stmt.update) { + // We pass true here to indicate that if stmt.update is an + // expression then we do not care about its result. + this.explode(path.get("update"), true); + } + + this.jump(head); + + this.mark(after); +}; + +exports.ForInStatement = function (path, stmt, labelId) { + t.assertIdentifier(stmt.left); + + var head = loc(); + var after = loc(); + + var keyIterNextFn = this.makeTempVar(); + this.emitAssign( + keyIterNextFn, + t.callExpression( + runtimeKeysMethod, + [this.explodeExpression(path.get("right"))] + ) + ); + + this.mark(head); + + var keyInfoTmpVar = this.makeTempVar(); + this.jumpIf( + t.memberExpression( + t.assignmentExpression( + "=", + keyInfoTmpVar, + t.callExpression(keyIterNextFn, []) + ), + t.identifier("done"), + false + ), + after + ); + + this.emitAssign( + stmt.left, + t.memberExpression( + keyInfoTmpVar, + t.identifier("value"), + false + ) + ); + + this.leapManager.withEntry( + new leap.LoopEntry(after, head, labelId), + function () { this.explodeStatement(path.get("body")); } + ); + + this.jump(head); + + this.mark(after); +}; + +exports.BreakStatement = function (path, stmt) { + this.emitAbruptCompletion({ + type: "break", + target: this.leapManager.getBreakLoc(stmt.label) + }); +}; + +exports.ContinueStatement = function (path, stmt) { + this.emitAbruptCompletion({ + type: "continue", + target: this.leapManager.getContinueLoc(stmt.label) + }); +}; + +exports.SwitchStatement = function (path, stmt) { + // Always save the discriminant into a temporary variable in case the + // test expressions overwrite values like context.sent. + var disc = this.emitAssign( + this.makeTempVar(), + this.explodeExpression(path.get("discriminant")) + ); + + var after = loc(); + var defaultLoc = loc(); + var condition = defaultLoc; + var caseLocs = []; + var self = this; + + // If there are no cases, .cases might be undefined. + var cases = stmt.cases || []; + + for (var i = cases.length - 1; i >= 0; --i) { + var c = cases[i]; + t.assertSwitchCase(c); + + if (c.test) { + condition = t.conditionalExpression( + t.binaryExpression("===", disc, c.test), + caseLocs[i] = loc(), + condition + ); + } else { + caseLocs[i] = defaultLoc; + } + } + + this.jump(this.explodeExpression( + new types.NodePath(condition, path, "discriminant") + )); + + this.leapManager.withEntry( + new leap.SwitchEntry(after), + function () { + path.get("cases").each(function (casePath) { + var i = casePath.name; + + self.mark(caseLocs[i]); + + casePath.get("consequent").each( + self.explodeStatement, + self + ); + }); + } + ); + + this.mark(after); + if (defaultLoc.value === -1) { + this.mark(defaultLoc); + assert.strictEqual(after.value, defaultLoc.value); + } +}; + +exports.IfStatement = function (path, stmt) { + var elseLoc = stmt.alternate && loc(); + var after = loc(); + + this.jumpIfNot( + this.explodeExpression(path.get("test")), + elseLoc || after + ); + + this.explodeStatement(path.get("consequent")); + + if (elseLoc) { + this.jump(after); + this.mark(elseLoc); + this.explodeStatement(path.get("alternate")); + } + + this.mark(after); +}; + +exports.ReturnStatement = function (path) { + this.emitAbruptCompletion({ + type: "return", + value: this.explodeExpression(path.get("argument")) + }); +}; + +exports.TryStatement = function (path, stmt) { + var after = loc(); + var self = this; + + var handler = stmt.handler; + if (!handler && stmt.handlers) { + handler = stmt.handlers[0] || null; + } + + var catchLoc = handler && loc(); + var catchEntry = catchLoc && new leap.CatchEntry( + catchLoc, + handler.param + ); + + var finallyLoc = stmt.finalizer && loc(); + var finallyEntry = finallyLoc && new leap.FinallyEntry(finallyLoc); + + var tryEntry = new leap.TryEntry( + this.getUnmarkedCurrentLoc(), + catchEntry, + finallyEntry + ); + + this.tryEntries.push(tryEntry); + this.updateContextPrevLoc(tryEntry.firstLoc); + + this.leapManager.withEntry(tryEntry, function () { + this.explodeStatement(path.get("block")); + + if (catchLoc) { + if (finallyLoc) { + // If we have both a catch block and a finally block, then + // because we emit the catch block first, we need to jump over + // it to the finally block. + this.jump(finallyLoc); + + } else { + // If there is no finally block, then we need to jump over the + // catch block to the fall-through location. + this.jump(after); + } + + this.updateContextPrevLoc(self.mark(catchLoc)); + + var bodyPath = path.get("handler", "body"); + var safeParam = this.makeTempVar(); + this.clearPendingException(tryEntry.firstLoc, safeParam); + + var catchScope = bodyPath.scope; + var catchParamName = handler.param.name; + t.assertCatchClause(catchScope.node); + assert.strictEqual(catchScope.lookup(catchParamName), catchScope); + + types.visit(bodyPath, { + visitIdentifier: function (path) { + if (path.value.name === catchParamName && + path.scope.lookup(catchParamName) === catchScope) { + return safeParam; + } + this.traverse(path); + } + }); + + this.leapManager.withEntry(catchEntry, function () { + this.explodeStatement(bodyPath); + }); + } + + if (finallyLoc) { + this.updateContextPrevLoc(this.mark(finallyLoc)); + + this.leapManager.withEntry(finallyEntry, function () { + this.explodeStatement(path.get("finalizer")); + }); + + this.emit(t.callExpression( + this.contextProperty("finish"), + [finallyEntry.firstLoc] + )); + } + }); + + this.mark(after); +}; + +exports.ThrowStatement = function (path) { + this.emit(t.throwStatement( + this.explodeExpression(path.get("argument")) + )); +}; diff --git a/lib/6to5/transformation/transformers/generators/emit.js b/lib/6to5/transformation/transformers/generators/emit/index.js similarity index 51% rename from lib/6to5/transformation/transformers/generators/emit.js rename to lib/6to5/transformation/transformers/generators/emit/index.js index a628d5a686..8df1576721 100644 --- a/lib/6to5/transformation/transformers/generators/emit.js +++ b/lib/6to5/transformation/transformers/generators/emit/index.js @@ -10,20 +10,22 @@ exports.Emitter = Emitter; -var runtimeProperty = require("./util").runtimeProperty; -var assert = require("assert"); -var types = require("ast-types"); -var leap = require("./leap"); -var meta = require("./meta"); -var t = require("../../../types"); -var _ = require("lodash"); +var explodeExpressions = require("./explode-expressions"); +var explodeStatements = require("./explode-statements"); +var assert = require("assert"); +var types = require("ast-types"); +var leap = require("../leap"); +var meta = require("../meta"); +var util = require("../util"); +var t = require("../../../../types"); +var _ = require("lodash"); -var runtimeKeysMethod = runtimeProperty("keys"); -var n = types.namedTypes; +var loc = util.loc; +var n = types.namedTypes; function Emitter(contextId) { assert.ok(this instanceof Emitter); - n.Identifier.assert(contextId); + t.assertIdentifier(contextId); // In order to make sure the context object does not collide with // anything in the local scope, we might have to rename it, so we @@ -52,19 +54,10 @@ function Emitter(contextId) { this.leapManager = new leap.LeapManager(this); } -// Offsets into this.listing that could be used as targets for branches or -// jumps are represented as numeric Literal nodes. This representation has -// the amazingly convenient benefit of allowing the exact value of the -// location to be determined at any time, even after generating code that -// refers to the location. -function loc() { - return t.literal(-1); -} - // Sets the exact value of the given location to the offset of the next // Statement emitted. -Emitter.prototype.mark = function(loc) { - n.Literal.assert(loc); +Emitter.prototype.mark = function (loc) { + t.assertLiteral(loc); var index = this.listing.length; if (loc.value === -1) { loc.value = index; @@ -77,28 +70,28 @@ Emitter.prototype.mark = function(loc) { return loc; }; -Emitter.prototype.emit = function(node) { +Emitter.prototype.emit = function (node) { if (t.isExpression(node)) node = t.expressionStatement(node); - n.Statement.assert(node); + t.assertStatement(node); this.listing.push(node); }; // Shorthand for emitting assignment statements. This will come in handy // for assignments to temporary variables. -Emitter.prototype.emitAssign = function(lhs, rhs) { +Emitter.prototype.emitAssign = function (lhs, rhs) { this.emit(this.assign(lhs, rhs)); return lhs; }; // Shorthand for an assignment statement. -Emitter.prototype.assign = function(lhs, rhs) { +Emitter.prototype.assign = function (lhs, rhs) { return t.expressionStatement( t.assignmentExpression("=", lhs, rhs)); }; // Convenience function for generating expressions like context.next, // context.sent, and context.rval. -Emitter.prototype.contextProperty = function(name, computed) { +Emitter.prototype.contextProperty = function (name, computed) { return t.memberExpression( this.contextId, computed ? t.literal(name) : t.identifier(name), @@ -116,7 +109,7 @@ var volatileContextPropertyNames = { // A "volatile" context property is a MemberExpression like context.sent // that should probably be stored in a temporary variable when there's a // possibility the property will get overwritten. -Emitter.prototype.isVolatileContextProperty = function(expr) { +Emitter.prototype.isVolatileContextProperty = function (expr) { if (t.isMemberExpression(expr)) { if (expr.computed) { // If it's a computed property such as context[couldBeAnything], @@ -136,7 +129,7 @@ Emitter.prototype.isVolatileContextProperty = function(expr) { }; // Shorthand for setting context.rval and jumping to `context.stop()`. -Emitter.prototype.stop = function(rval) { +Emitter.prototype.stop = function (rval) { if (rval) { this.setReturnValue(rval); } @@ -144,8 +137,8 @@ Emitter.prototype.stop = function(rval) { this.jump(this.finalLoc); }; -Emitter.prototype.setReturnValue = function(valuePath) { - n.Expression.assert(valuePath.value); +Emitter.prototype.setReturnValue = function (valuePath) { + t.assertExpression(valuePath.value); this.emitAssign( this.contextProperty("rval"), @@ -153,8 +146,8 @@ Emitter.prototype.setReturnValue = function(valuePath) { ); }; -Emitter.prototype.clearPendingException = function(tryLoc, assignee) { - n.Literal.assert(tryLoc); +Emitter.prototype.clearPendingException = function (tryLoc, assignee) { + t.assertLiteral(tryLoc); var catchCall = t.callExpression( this.contextProperty("catch", true), @@ -170,15 +163,15 @@ Emitter.prototype.clearPendingException = function(tryLoc, assignee) { // Emits code for an unconditional jump to the given location, even if the // exact value of the location is not yet known. -Emitter.prototype.jump = function(toLoc) { +Emitter.prototype.jump = function (toLoc) { this.emitAssign(this.contextProperty("next"), toLoc); this.emit(t.breakStatement()); }; // Conditional jump. -Emitter.prototype.jumpIf = function(test, toLoc) { - n.Expression.assert(test); - n.Literal.assert(toLoc); +Emitter.prototype.jumpIf = function (test, toLoc) { + t.assertExpression(test); + t.assertLiteral(toLoc); this.emit(t.ifStatement( test, @@ -190,9 +183,9 @@ Emitter.prototype.jumpIf = function(test, toLoc) { }; // Conditional jump, with the condition negated. -Emitter.prototype.jumpIfNot = function(test, toLoc) { - n.Expression.assert(test); - n.Literal.assert(toLoc); +Emitter.prototype.jumpIfNot = function (test, toLoc) { + t.assertExpression(test); + t.assertLiteral(toLoc); var negatedTest; if (t.isUnaryExpression(test) && test.operator === "!") { @@ -217,11 +210,11 @@ Emitter.prototype.jumpIfNot = function(test, toLoc) { // other local variables, and since we just increment `nextTempId` // monotonically, uniqueness is assured. var nextTempId = 0; -Emitter.prototype.makeTempVar = function() { +Emitter.prototype.makeTempVar = function () { return this.contextProperty("t" + nextTempId++); }; -Emitter.prototype.getContextFunction = function(id) { +Emitter.prototype.getContextFunction = function (id) { var node = t.functionExpression( id || null, [this.contextId], @@ -244,7 +237,7 @@ Emitter.prototype.getContextFunction = function(id) { // // Each marked location in this.listing will correspond to one generated // case statement. -Emitter.prototype.getDispatchLoop = function() { +Emitter.prototype.getDispatchLoop = function () { var self = this; var cases = []; var current; @@ -253,7 +246,7 @@ Emitter.prototype.getDispatchLoop = function() { // case, we can skip the rest of the statements until the next case. var alreadyEnded = false; - self.listing.forEach(function(stmt, i) { + self.listing.forEach(function (stmt, i) { if (self.marked.hasOwnProperty(i)) { cases.push(t.switchCase(t.literal(i), current = [])); alreadyEnded = false; @@ -306,7 +299,7 @@ function isSwitchCaseEnder(stmt) { t.isThrowStatement(stmt); } -Emitter.prototype.getTryEntryList = function() { +Emitter.prototype.getTryEntryList = function () { if (this.tryEntries.length === 0) { // To avoid adding a needless [] to the majority of runtime.wrap // argument lists, force the caller to handle this case specially. @@ -316,7 +309,7 @@ Emitter.prototype.getTryEntryList = function() { var lastLocValue = 0; return t.arrayExpression( - this.tryEntries.map(function(tryEntry) { + this.tryEntries.map(function (tryEntry) { var thisLocValue = tryEntry.firstLoc.value; assert.ok(thisLocValue >= lastLocValue, "try entries out of order"); lastLocValue = thisLocValue; @@ -346,13 +339,13 @@ Emitter.prototype.getTryEntryList = function() { // No destructive modification of AST nodes. -Emitter.prototype.explode = function(path, ignoreResult) { +Emitter.prototype.explode = function (path, ignoreResult) { assert.ok(path instanceof types.NodePath); var node = path.value; var self = this; - n.Node.assert(node); + n.Node.check(node); if (t.isStatement(node)) return self.explodeStatement(path); @@ -389,17 +382,16 @@ function getDeclError(node) { JSON.stringify(node)); } -Emitter.prototype.explodeStatement = function(path, labelId) { +Emitter.prototype.explodeStatement = function (path, labelId) { assert.ok(path instanceof types.NodePath); var stmt = path.value; var self = this; - var after, head; - n.Statement.assert(stmt); + t.assertStatement(stmt); if (labelId) { - n.Identifier.assert(labelId); + t.assertIdentifier(labelId); } else { labelId = null; } @@ -423,338 +415,15 @@ Emitter.prototype.explodeStatement = function(path, labelId) { return; } - switch (stmt.type) { - case "ExpressionStatement": - self.explodeExpression(path.get("expression"), true); - break; - - case "LabeledStatement": - self.explodeStatement(path.get("body"), stmt.label); - break; - - case "WhileStatement": - var before = loc(); - after = loc(); - - self.mark(before); - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - self.leapManager.withEntry( - new leap.LoopEntry(after, before, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - self.jump(before); - self.mark(after); - - break; - - case "DoWhileStatement": - var first = loc(); - var test = loc(); - after = loc(); - - self.mark(first); - self.leapManager.withEntry( - new leap.LoopEntry(after, test, labelId), - function() { self.explode(path.get("body")); } - ); - self.mark(test); - self.jumpIf(self.explodeExpression(path.get("test")), first); - self.mark(after); - - break; - - case "ForStatement": - head = loc(); - var update = loc(); - after = loc(); - - if (stmt.init) { - // We pass true here to indicate that if stmt.init is an expression - // then we do not care about its result. - self.explode(path.get("init"), true); - } - - self.mark(head); - - if (stmt.test) { - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - } else { - // No test means continue unconditionally. - } - - self.leapManager.withEntry( - new leap.LoopEntry(after, update, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.mark(update); - - if (stmt.update) { - // We pass true here to indicate that if stmt.update is an - // expression then we do not care about its result. - self.explode(path.get("update"), true); - } - - self.jump(head); - - self.mark(after); - - break; - - case "ForInStatement": - n.Identifier.assert(stmt.left); - - head = loc(); - after = loc(); - - var keyIterNextFn = self.makeTempVar(); - self.emitAssign( - keyIterNextFn, - t.callExpression( - runtimeKeysMethod, - [self.explodeExpression(path.get("right"))] - ) - ); - - self.mark(head); - - var keyInfoTmpVar = self.makeTempVar(); - self.jumpIf( - t.memberExpression( - t.assignmentExpression( - "=", - keyInfoTmpVar, - t.callExpression(keyIterNextFn, []) - ), - t.identifier("done"), - false - ), - after - ); - - self.emitAssign( - stmt.left, - t.memberExpression( - keyInfoTmpVar, - t.identifier("value"), - false - ) - ); - - self.leapManager.withEntry( - new leap.LoopEntry(after, head, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.jump(head); - - self.mark(after); - - break; - - case "BreakStatement": - self.emitAbruptCompletion({ - type: "break", - target: self.leapManager.getBreakLoc(stmt.label) - }); - - break; - - case "ContinueStatement": - self.emitAbruptCompletion({ - type: "continue", - target: self.leapManager.getContinueLoc(stmt.label) - }); - - break; - - case "SwitchStatement": - // Always save the discriminant into a temporary variable in case the - // test expressions overwrite values like context.sent. - var disc = self.emitAssign( - self.makeTempVar(), - self.explodeExpression(path.get("discriminant")) - ); - - after = loc(); - var defaultLoc = loc(); - var condition = defaultLoc; - var caseLocs = []; - - // If there are no cases, .cases might be undefined. - var cases = stmt.cases || []; - - for (var i = cases.length - 1; i >= 0; --i) { - var c = cases[i]; - n.SwitchCase.assert(c); - - if (c.test) { - condition = t.conditionalExpression( - t.binaryExpression("===", disc, c.test), - caseLocs[i] = loc(), - condition - ); - } else { - caseLocs[i] = defaultLoc; - } - } - - self.jump(self.explodeExpression( - new types.NodePath(condition, path, "discriminant") - )); - - self.leapManager.withEntry( - new leap.SwitchEntry(after), - function() { - path.get("cases").each(function(casePath) { - var i = casePath.name; - - self.mark(caseLocs[i]); - - casePath.get("consequent").each( - self.explodeStatement, - self - ); - }); - } - ); - - self.mark(after); - if (defaultLoc.value === -1) { - self.mark(defaultLoc); - assert.strictEqual(after.value, defaultLoc.value); - } - - break; - - case "IfStatement": - var elseLoc = stmt.alternate && loc(); - after = loc(); - - self.jumpIfNot( - self.explodeExpression(path.get("test")), - elseLoc || after - ); - - self.explodeStatement(path.get("consequent")); - - if (elseLoc) { - self.jump(after); - self.mark(elseLoc); - self.explodeStatement(path.get("alternate")); - } - - self.mark(after); - - break; - - case "ReturnStatement": - self.emitAbruptCompletion({ - type: "return", - value: self.explodeExpression(path.get("argument")) - }); - - break; - - case "TryStatement": - after = loc(); - - var handler = stmt.handler; - if (!handler && stmt.handlers) { - handler = stmt.handlers[0] || null; - } - - var catchLoc = handler && loc(); - var catchEntry = catchLoc && new leap.CatchEntry( - catchLoc, - handler.param - ); - - var finallyLoc = stmt.finalizer && loc(); - var finallyEntry = finallyLoc && new leap.FinallyEntry(finallyLoc); - - var tryEntry = new leap.TryEntry( - self.getUnmarkedCurrentLoc(), - catchEntry, - finallyEntry - ); - - self.tryEntries.push(tryEntry); - self.updateContextPrevLoc(tryEntry.firstLoc); - - self.leapManager.withEntry(tryEntry, function() { - self.explodeStatement(path.get("block")); - - if (catchLoc) { - if (finallyLoc) { - // If we have both a catch block and a finally block, then - // because we emit the catch block first, we need to jump over - // it to the finally block. - self.jump(finallyLoc); - - } else { - // If there is no finally block, then we need to jump over the - // catch block to the fall-through location. - self.jump(after); - } - - self.updateContextPrevLoc(self.mark(catchLoc)); - - var bodyPath = path.get("handler", "body"); - var safeParam = self.makeTempVar(); - self.clearPendingException(tryEntry.firstLoc, safeParam); - - var catchScope = bodyPath.scope; - var catchParamName = handler.param.name; - n.CatchClause.assert(catchScope.node); - assert.strictEqual(catchScope.lookup(catchParamName), catchScope); - - types.visit(bodyPath, { - visitIdentifier: function(path) { - if (path.value.name === catchParamName && - path.scope.lookup(catchParamName) === catchScope) { - return safeParam; - } - this.traverse(path); - } - }); - - self.leapManager.withEntry(catchEntry, function() { - self.explodeStatement(bodyPath); - }); - } - - if (finallyLoc) { - self.updateContextPrevLoc(self.mark(finallyLoc)); - - self.leapManager.withEntry(finallyEntry, function() { - self.explodeStatement(path.get("finalizer")); - }); - - self.emit(t.callExpression( - self.contextProperty("finish"), - [finallyEntry.firstLoc] - )); - } - }); - - self.mark(after); - - break; - - case "ThrowStatement": - self.emit(t.throwStatement( - self.explodeExpression(path.get("argument")) - )); - - break; - - default: + var fn = explodeStatements[stmt.type]; + if (fn) { + fn.call(this, path, stmt, labelId); + } else { throw new Error("unknown Statement of type " + JSON.stringify(stmt.type)); } }; -Emitter.prototype.emitAbruptCompletion = function(record) { +Emitter.prototype.emitAbruptCompletion = function (record) { if (!isValidCompletion(record)) { assert.ok( false, @@ -770,11 +439,11 @@ Emitter.prototype.emitAbruptCompletion = function(record) { var abruptArgs = [t.literal(record.type)]; if (record.type === "break" || record.type === "continue") { - n.Literal.assert(record.target); + t.assertLiteral(record.target); abruptArgs[1] = record.target; } else if (record.type === "return" || record.type === "throw") { if (record.value) { - n.Expression.assert(record.value); + t.assertExpression(record.value); abruptArgs[1] = record.value; } } @@ -797,7 +466,7 @@ function isValidCompletion(record) { } if (type === "break" || type === "continue") { - return !_.has(record, "value") && t.isLiteral(record.target);; + return !_.has(record, "value") && t.isLiteral(record.target); } if (type === "return" || type === "throw") { @@ -807,7 +476,6 @@ function isValidCompletion(record) { return false; } - // Not all offsets into emitter.listing are potential jump targets. For // example, execution typically falls into the beginning of a try block // without jumping directly there. This method returns the current offset @@ -817,7 +485,7 @@ function isValidCompletion(record) { // statements). There's no logical harm in marking such locations as jump // targets, but minimizing the number of switch cases keeps the generated // code shorter. -Emitter.prototype.getUnmarkedCurrentLoc = function() { +Emitter.prototype.getUnmarkedCurrentLoc = function () { return t.literal(this.listing.length); }; @@ -831,9 +499,9 @@ Emitter.prototype.getUnmarkedCurrentLoc = function() { // would know the location of the current instruction with complete // precision at all times, but we don't have that luxury here, as it would // be costly and verbose to set context.prev before every statement. -Emitter.prototype.updateContextPrevLoc = function(loc) { +Emitter.prototype.updateContextPrevLoc = function (loc) { if (loc) { - n.Literal.assert(loc); + t.assertLiteral(loc); if (loc.value === -1) { // If an uninitialized location literal was passed in, set its value @@ -854,21 +522,20 @@ Emitter.prototype.updateContextPrevLoc = function(loc) { this.emitAssign(this.contextProperty("prev"), loc); }; -Emitter.prototype.explodeExpression = function(path, ignoreResult) { +Emitter.prototype.explodeExpression = function (path, ignoreResult) { assert.ok(path instanceof types.NodePath); var expr = path.value; if (expr) { - n.Expression.assert(expr); + t.assertExpression(expr); } else { return expr; } var self = this; - var result, after; // Used optionally by several cases below. function finish(expr) { - n.Expression.assert(expr); + t.assertExpression(expr); if (ignoreResult) { self.emit(expr); } else { @@ -935,180 +602,10 @@ Emitter.prototype.explodeExpression = function(path, ignoreResult) { // emitting the expression with all its side effects, and we should not // return a result. - switch (expr.type) { - case "ParenthesizedExpression": - return finish(self.explodeExpression(path.get("expression"))); - - case "MemberExpression": - return finish(t.memberExpression( - self.explodeExpression(path.get("object")), - expr.computed ? explodeViaTempVar(null, path.get("property")) : expr.property, - expr.computed - )); - - case "CallExpression": - var oldCalleePath = path.get("callee"); - var newCallee = self.explodeExpression(oldCalleePath); - - // If the callee was not previously a MemberExpression, then the - // CallExpression was "unqualified," meaning its `this` object should - // be the global object. If the exploded expression has become a - // MemberExpression, then we need to force it to be unqualified by - // using the (0, object.property)(...) trick; otherwise, it will - // receive the object of the MemberExpression as its `this` object. - if (!t.isMemberExpression(oldCalleePath.node) && t.isMemberExpression(newCallee)) { - newCallee = t.sequenceExpression([ - t.literal(0), - newCallee - ]); - } - - return finish(t.callExpression( - newCallee, - path.get("arguments").map(function(argPath) { - return explodeViaTempVar(null, argPath); - }) - )); - - case "NewExpression": - return finish(t.newExpression( - explodeViaTempVar(null, path.get("callee")), - path.get("arguments").map(function(argPath) { - return explodeViaTempVar(null, argPath); - }) - )); - - case "ObjectExpression": - return finish(t.objectExpression( - path.get("properties").map(function(propPath) { - return t.property( - propPath.value.kind, - propPath.value.key, - explodeViaTempVar(null, propPath.get("value")) - ); - }) - )); - - case "ArrayExpression": - return finish(t.arrayExpression( - path.get("elements").map(function(elemPath) { - return explodeViaTempVar(null, elemPath); - }) - )); - - case "SequenceExpression": - var lastIndex = expr.expressions.length - 1; - - path.get("expressions").each(function(exprPath) { - if (exprPath.name === lastIndex) { - result = self.explodeExpression(exprPath, ignoreResult); - } else { - self.explodeExpression(exprPath, true); - } - }); - - return result; - - case "LogicalExpression": - after = loc(); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - var left = explodeViaTempVar(result, path.get("left")); - - if (expr.operator === "&&") { - self.jumpIfNot(left, after); - } else { - assert.strictEqual(expr.operator, "||"); - self.jumpIf(left, after); - } - - explodeViaTempVar(result, path.get("right"), ignoreResult); - - self.mark(after); - - return result; - - case "ConditionalExpression": - var elseLoc = loc(); - after = loc(); - var test = self.explodeExpression(path.get("test")); - - self.jumpIfNot(test, elseLoc); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - explodeViaTempVar(result, path.get("consequent"), ignoreResult); - self.jump(after); - - self.mark(elseLoc); - explodeViaTempVar(result, path.get("alternate"), ignoreResult); - - self.mark(after); - - return result; - - case "UnaryExpression": - return finish(t.unaryExpression( - expr.operator, - // Can't (and don't need to) break up the syntax of the argument. - // Think about delete a[b]. - self.explodeExpression(path.get("argument")), - !!expr.prefix - )); - - case "BinaryExpression": - return finish(t.binaryExpression( - expr.operator, - explodeViaTempVar(null, path.get("left")), - explodeViaTempVar(null, path.get("right")) - )); - - case "AssignmentExpression": - return finish(t.assignmentExpression( - expr.operator, - self.explodeExpression(path.get("left")), - self.explodeExpression(path.get("right")) - )); - - case "UpdateExpression": - return finish(t.updateExpression( - expr.operator, - self.explodeExpression(path.get("argument")), - expr.prefix - )); - - case "YieldExpression": - after = loc(); - var arg = expr.argument && self.explodeExpression(path.get("argument")); - - if (arg && expr.delegate) { - result = self.makeTempVar(); - - self.emit(t.returnStatement(t.callExpression( - self.contextProperty("delegateYield"), [ - arg, - t.literal(result.property.name), - after - ] - ))); - - self.mark(after); - - return result; - } - - self.emitAssign(self.contextProperty("next"), after); - self.emit(t.returnStatement(arg || null)); - self.mark(after); - - return self.contextProperty("sent"); - - default: + var fn = explodeExpressions[expr.type]; + if (fn) { + return fn.call(this, expr, path, explodeViaTempVar, finish, ignoreResult); + } else { throw new Error("unknown Expression of type " + JSON.stringify(expr.type)); } }; diff --git a/lib/6to5/transformation/transformers/generators/hoist.js b/lib/6to5/transformation/transformers/generators/hoist.js index 03b97c79db..b9772466ba 100644 --- a/lib/6to5/transformation/transformers/generators/hoist.js +++ b/lib/6to5/transformation/transformers/generators/hoist.js @@ -13,23 +13,21 @@ var types = require("ast-types"); var t = require("../../../types"); var _ = require("lodash"); -var n = types.namedTypes; - // The hoist function takes a FunctionExpression or FunctionDeclaration // and replaces any Declaration nodes in its body with assignments, then // returns a VariableDeclaration containing just the names of the removed // declarations. -exports.hoist = function(funPath) { +exports.hoist = function (funPath) { assert.ok(funPath instanceof types.NodePath); - n.Function.assert(funPath.value); + t.assertFunction(funPath.value); var vars = {}; function varDeclToExpr(vdec, includeIdentifiers) { - n.VariableDeclaration.assert(vdec); + t.assertVariableDeclaration(vdec); var exprs = []; - vdec.declarations.forEach(function(dec) { + vdec.declarations.forEach(function (dec) { vars[dec.id.name] = dec.id; if (dec.init) { @@ -51,7 +49,7 @@ exports.hoist = function(funPath) { } types.visit(funPath.get("body"), { - visitVariableDeclaration: function(path) { + visitVariableDeclaration: function (path) { var expr = varDeclToExpr(path.value, false); if (expr === null) { path.replace(); @@ -66,7 +64,7 @@ exports.hoist = function(funPath) { return false; }, - visitForStatement: function(path) { + visitForStatement: function (path) { var init = path.value.init; if (t.isVariableDeclaration(init)) { path.get("init").replace(varDeclToExpr(init, false)); @@ -74,7 +72,7 @@ exports.hoist = function(funPath) { this.traverse(path); }, - visitForInStatement: function(path) { + visitForInStatement: function (path) { var left = path.value.left; if (t.isVariableDeclaration(left)) { path.get("left").replace(varDeclToExpr(left, true)); @@ -82,7 +80,7 @@ exports.hoist = function(funPath) { this.traverse(path); }, - visitFunctionDeclaration: function(path) { + visitFunctionDeclaration: function (path) { var node = path.value; vars[node.id.name] = node.id; @@ -120,14 +118,14 @@ exports.hoist = function(funPath) { return false; }, - visitFunctionExpression: function() { + visitFunctionExpression: function () { // Don't descend into nested function expressions. return false; } }); var paramNames = {}; - funPath.get("params").each(function(paramPath) { + funPath.get("params").each(function (paramPath) { var param = paramPath.value; if (t.isIdentifier(param)) { paramNames[param.name] = param; @@ -139,7 +137,7 @@ exports.hoist = function(funPath) { var declarations = []; - Object.keys(vars).forEach(function(name) { + Object.keys(vars).forEach(function (name) { if (!_.has(paramNames, name)) { declarations.push(t.variableDeclarator(vars[name], null)); } diff --git a/lib/6to5/transformation/transformers/generators/leap.js b/lib/6to5/transformation/transformers/generators/leap.js index b6704d8ce2..5cd289b501 100644 --- a/lib/6to5/transformation/transformers/generators/leap.js +++ b/lib/6to5/transformation/transformers/generators/leap.js @@ -17,11 +17,10 @@ exports.LoopEntry = LoopEntry; exports.TryEntry = TryEntry; var assert = require("assert"); -var types = require("ast-types"); var util = require("util"); +var t = require("../../../types"); var inherits = util.inherits; -var n = types.namedTypes; function Entry() { assert.ok(this instanceof Entry); @@ -30,7 +29,7 @@ function Entry() { function FunctionEntry(returnLoc) { Entry.call(this); - n.Literal.assert(returnLoc); + t.assertLiteral(returnLoc); this.returnLoc = returnLoc; } @@ -40,11 +39,11 @@ inherits(FunctionEntry, Entry); function LoopEntry(breakLoc, continueLoc, label) { Entry.call(this); - n.Literal.assert(breakLoc); - n.Literal.assert(continueLoc); + t.assertLiteral(breakLoc); + t.assertLiteral(continueLoc); if (label) { - n.Identifier.assert(label); + t.assertIdentifier(label); } else { label = null; } @@ -59,7 +58,7 @@ inherits(LoopEntry, Entry); function SwitchEntry(breakLoc) { Entry.call(this); - n.Literal.assert(breakLoc); + t.assertLiteral(breakLoc); this.breakLoc = breakLoc; } @@ -69,7 +68,7 @@ inherits(SwitchEntry, Entry); function TryEntry(firstLoc, catchEntry, finallyEntry) { Entry.call(this); - n.Literal.assert(firstLoc); + t.assertLiteral(firstLoc); if (catchEntry) { assert.ok(catchEntry instanceof CatchEntry); @@ -96,8 +95,8 @@ inherits(TryEntry, Entry); function CatchEntry(firstLoc, paramId) { Entry.call(this); - n.Literal.assert(firstLoc); - n.Identifier.assert(paramId); + t.assertLiteral(firstLoc); + t.assertIdentifier(paramId); this.firstLoc = firstLoc; this.paramId = paramId; @@ -108,7 +107,7 @@ inherits(CatchEntry, Entry); function FinallyEntry(firstLoc) { Entry.call(this); - n.Literal.assert(firstLoc); + t.assertLiteral(firstLoc); this.firstLoc = firstLoc; } @@ -125,7 +124,7 @@ function LeapManager(emitter) { this.entryStack = [new FunctionEntry(emitter.finalLoc)]; } -LeapManager.prototype.withEntry = function(entry, callback) { +LeapManager.prototype.withEntry = function (entry, callback) { assert.ok(entry instanceof Entry); this.entryStack.push(entry); try { @@ -136,7 +135,7 @@ LeapManager.prototype.withEntry = function(entry, callback) { } }; -LeapManager.prototype._findLeapLocation = function(property, label) { +LeapManager.prototype._findLeapLocation = function (property, label) { for (var i = this.entryStack.length - 1; i >= 0; --i) { var entry = this.entryStack[i]; var loc = entry[property]; @@ -155,10 +154,10 @@ LeapManager.prototype._findLeapLocation = function(property, label) { return null; }; -LeapManager.prototype.getBreakLoc = function(label) { +LeapManager.prototype.getBreakLoc = function (label) { return this._findLeapLocation("breakLoc", label); }; -LeapManager.prototype.getContinueLoc = function(label) { +LeapManager.prototype.getContinueLoc = function (label) { return this._findLeapLocation("continueLoc", label); }; diff --git a/lib/6to5/transformation/transformers/generators/meta.js b/lib/6to5/transformation/transformers/generators/meta.js index a755da8019..f7bb1248e6 100644 --- a/lib/6to5/transformation/transformers/generators/meta.js +++ b/lib/6to5/transformation/transformers/generators/meta.js @@ -11,7 +11,6 @@ var assert = require("assert"); var types = require("ast-types"); var m = require("private").makeAccessor(); -var t = require("../../../types"); var _ = require("lodash"); var isArray = types.builtInTypes.array; @@ -19,7 +18,7 @@ var n = types.namedTypes; function makePredicate(propertyName, knownTypes) { function onlyChildren(node) { - n.Node.assert(node); + n.Node.check(node); // Assume no side effects until we find out otherwise. var result = false; @@ -36,7 +35,7 @@ function makePredicate(propertyName, knownTypes) { return result; } - types.eachField(node, function(name, child) { + types.eachField(node, function (name, child) { check(child); }); @@ -44,7 +43,7 @@ function makePredicate(propertyName, knownTypes) { } function predicate(node) { - n.Node.assert(node); + n.Node.check(node); var meta = m(node); if (_.has(meta, propertyName)) return meta[propertyName]; diff --git a/lib/6to5/transformation/transformers/generators/runtime.js b/lib/6to5/transformation/transformers/generators/runtime.js index 5257c4b937..2adc60afdd 100644 --- a/lib/6to5/transformation/transformers/generators/runtime.js +++ b/lib/6to5/transformation/transformers/generators/runtime.js @@ -38,19 +38,19 @@ var GFName = "GeneratorFunction"; if (GF.name !== GFName) GF.name = GFName; if (GF.name !== GFName) throw new Error(GFName + " renamed?"); -runtime.isGeneratorFunction = function(genFun) { +runtime.isGeneratorFunction = function (genFun) { var ctor = genFun && genFun.constructor; return ctor ? GF.name === ctor.name : false; }; -runtime.mark = function(genFun) { +runtime.mark = function (genFun) { genFun.__proto__ = GFp; genFun.prototype = Object.create(Gp); return genFun; }; -runtime.async = function(innerFn, outerFn, self, tryList) { - return new Promise(function(resolve, reject) { +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"]); @@ -202,11 +202,11 @@ function Generator(innerFn, outerFn, self, tryList) { return generator; } -Gp[iteratorSymbol] = function() { +Gp[iteratorSymbol] = function () { return this; }; -Gp.toString = function() { +Gp.toString = function () { return "[object Generator]"; }; @@ -240,7 +240,7 @@ function Context(tryList) { this.reset(); } -runtime.keys = function(object) { +runtime.keys = function (object) { var keys = []; for (var key in object) { keys.push(key); @@ -293,7 +293,7 @@ runtime.values = values; Context.prototype = { constructor: Context, - reset: function() { + reset: function () { this.prev = 0; this.next = 0; this.sent = undefined; @@ -311,7 +311,7 @@ Context.prototype = { } }, - stop: function() { + stop: function () { this.done = true; var rootEntry = this.tryEntries[0]; @@ -323,7 +323,7 @@ Context.prototype = { return this.rval; }, - dispatchException: function(exception) { + dispatchException: function (exception) { if (this.done) { throw exception; } @@ -375,7 +375,7 @@ Context.prototype = { } }, - _findFinallyEntry: function(finallyLoc) { + _findFinallyEntry: function (finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && @@ -387,7 +387,7 @@ Context.prototype = { } }, - abrupt: function(type, arg) { + abrupt: function (type, arg) { var entry = this._findFinallyEntry(); var record = entry ? entry.completion : {}; @@ -403,7 +403,7 @@ Context.prototype = { return ContinueSentinel; }, - complete: function(record) { + complete: function (record) { if (record.type === "throw") { throw record.arg; } @@ -418,12 +418,12 @@ Context.prototype = { return ContinueSentinel; }, - finish: function(finallyLoc) { + finish: function (finallyLoc) { var entry = this._findFinallyEntry(finallyLoc); return this.complete(entry.completion); }, - "catch": function(tryLoc) { + "catch": function (tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { @@ -442,7 +442,7 @@ Context.prototype = { throw new Error("illegal catch attempt"); }, - delegateYield: function(iterable, resultName, nextLoc) { + delegateYield: function (iterable, resultName, nextLoc) { this.delegate = { iterator: values(iterable), resultName: resultName, diff --git a/lib/6to5/transformation/transformers/generators/util.js b/lib/6to5/transformation/transformers/generators/util.js index a6b5dc165f..99e7072a19 100644 --- a/lib/6to5/transformation/transformers/generators/util.js +++ b/lib/6to5/transformation/transformers/generators/util.js @@ -16,3 +16,13 @@ exports.runtimeProperty = function (name) { t.identifier(name) ); }; + +// Offsets into this.listing that could be used as targets for branches or +// jumps are represented as numeric Literal nodes. This representation has +// the amazingly convenient benefit of allowing the exact value of the +// location to be determined at any time, even after generating code that +// refers to the location. + +exports.loc = function () { + return t.literal(-1); +}; diff --git a/lib/6to5/transformation/transformers/generators/visit.js b/lib/6to5/transformation/transformers/generators/visit.js index e9c0781291..1c31a3e5a3 100644 --- a/lib/6to5/transformation/transformers/generators/visit.js +++ b/lib/6to5/transformation/transformers/generators/visit.js @@ -14,8 +14,6 @@ var hoist = require("./hoist").hoist; var types = require("ast-types"); var t = require("../../../types"); -var n = types.namedTypes; - var runtimeAsyncMethod = runtimeProperty("async"); var runtimeWrapMethod = runtimeProperty("wrap"); var runtimeMarkMethod = runtimeProperty("mark"); @@ -25,7 +23,7 @@ exports.transform = function transform(node) { }; var visitor = types.PathVisitor.fromMethodsObject({ - visitFunction: function(path) { + visitFunction: function (path) { // Calling this.traverse(path) first makes for a post-order traversal. this.traverse(path); @@ -179,7 +177,7 @@ var visitor = types.PathVisitor.fromMethodsObject({ bodyPath.push(varDecl); } else { - n.FunctionExpression.assert(node); + t.assertFunctionExpression(node); return t.callExpression(runtimeMarkMethod, [node]); } } @@ -187,7 +185,7 @@ var visitor = types.PathVisitor.fromMethodsObject({ function shouldNotHoistAbove(stmtPath) { var value = stmtPath.value; - n.Statement.assert(value); + t.assertStatement(value); // If the first statement is a "use strict" declaration, make sure to // insert hoisted declarations afterwards. @@ -210,11 +208,11 @@ function shouldNotHoistAbove(stmtPath) { } var awaitVisitor = types.PathVisitor.fromMethodsObject({ - visitFunction: function() { + visitFunction: function () { return false; // Don't descend into nested function scopes. }, - visitAwaitExpression: function(path) { + visitAwaitExpression: function (path) { // Convert await expressions to yield expressions. return t.yieldExpression(path.value.argument, false); } diff --git a/lib/6to5/transformation/transformers/let-scoping.js b/lib/6to5/transformation/transformers/let-scoping.js index 7998e9e362..a56d6af6b7 100644 --- a/lib/6to5/transformation/transformers/let-scoping.js +++ b/lib/6to5/transformation/transformers/let-scoping.js @@ -88,6 +88,9 @@ LetScoping.prototype.run = function () { this.info = this.getInfo(); + // remap all let references that exist in upper scopes to their uid + this.remap(); + // this is a block within a `Function` so we can safely leave it be if (t.isFunction(this.parent)) return this.noClosure(); @@ -138,23 +141,29 @@ LetScoping.prototype.run = function () { }; /** - * There are no let references accessed within a closure so we can just traverse - * through this block and replace all references that exist in a higher scope to - * their uids. + * There are no let references accessed within a closure so we can just turn the + * lets into vars. */ LetScoping.prototype.noClosure = function () { - var replacements = this.info.duplicates; - var declarators = this.info.declarators; - var block = this.block; + standardiseLets(this.info.declarators); +}; - standardiseLets(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 (_.isEmpty(replacements)) return; - var replace = function (node, parent) { + 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; }; @@ -260,7 +269,7 @@ LetScoping.prototype.checkFor = function () { return false; } - if (forParent && !node.label) { + if (forParent && node && !node.label) { if (t.isBreakStatement(node)) { has.hasBreak = true; replace = t.returnStatement(t.literal("break")); diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index fac701132a..3e13d640e0 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -2,14 +2,25 @@ var _ = require("lodash"); var t = exports; +var addAssert = function (type, is) { + 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.VISITOR_KEYS = require("./visitor-keys"); _.each(t.VISITOR_KEYS, function (keys, type) { - t["is" + type] = function (node, opts) { + var is = t["is" + type] = function (node, opts) { return node && node.type === type && t.shallowEqual(node, opts); }; + + addAssert(type, is); }); // @@ -43,17 +54,21 @@ _.each(t.ALIAS_KEYS, function (aliases, type) { _.each(_aliases, function (types, type) { t[type.toUpperCase() + "_TYPES"] = types; - t["is" + type] = function (node, opts) { + var is = t["is" + type] = function (node, opts) { return node && _.contains(types, node.type) && t.shallowEqual(node, opts); }; + + addAssert(type, is); }); // t.isExpression = function (node) { - return !t.isDeclaration(node); + return !t.isStatement(node); }; +addAssert("Expression", t.isExpression); + // t.shallowEqual = function (actual, expected) { diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 4425c28036..99551e5174 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -37,6 +37,21 @@ exports.list = function (val) { return val ? val.split(",") : []; }; +exports.regexify = function (val) { + if (!val) return new RegExp; + if (_.isArray(val)) val = val.join("|"); + if (_.isString(val)) return new RegExp(val || ""); + if (_.isRegExp(val)) return val; + throw new TypeError("illegal type for regexify"); +}; + +exports.arrayify = function (val) { + if (!val) return []; + if (_.isString(val)) return exports.list(val); + if (_.isArray(val)) return val; + throw new TypeError("illegal type for arrayify"); +}; + exports.getUid = function (parent, file) { var node; diff --git a/package.json b/package.json index 58bbc14e7f..7aeb3ca1ac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "6to5", "description": "Turn ES6 code into readable vanilla ES5 with source maps", - "version": "1.12.20", + "version": "1.12.21", "author": "Sebastian McKenzie ", "homepage": "https://github.com/6to5/6to5", "repository": { @@ -37,17 +37,17 @@ "dependencies": { "ast-types": "~0.6.0", "commander": "2.5.0", - "fs-readdir-recursive": "0.0.2", + "fs-readdir-recursive": "0.1.0", "lodash": "2.4.1", "mkdirp": "0.5.0", "es6-shim": "0.20.2", "es6-symbol": "0.1.1", "regexpu": "0.3.0", "source-map": "0.1.40", - "chokidar": "0.10.5", + "chokidar": "0.11.0", "source-map-support": "0.2.8", "esutils": "1.1.4", - "acorn-6to5": "https://github.com/6to5/acorn-6to5/archive/74d8e9bed20ba302d3504f53d0b1c649968959e1.tar.gz", + "acorn-6to5": "0.9.1-2", "estraverse": "^1.7.0", "private": "^0.1.6" }, diff --git a/test/fixtures/transformation/let-scoping/exec-block-scoped/exec.js b/test/fixtures/transformation/let-scoping/exec-block-scoped/exec.js index 8b1fd6db2b..d90256cbb2 100644 --- a/test/fixtures/transformation/let-scoping/exec-block-scoped/exec.js +++ b/test/fixtures/transformation/let-scoping/exec-block-scoped/exec.js @@ -2,5 +2,12 @@ let x = 1; { let x = 2; assert.equal(x, 2); + { + let x = 3; + assert.equal(x, 3); + + x++; + assert.equal(x, 4); + } } assert.equal(x, 1); diff --git a/test/fixtures/transformation/modules-common-interop/exports-default/actual.js b/test/fixtures/transformation/modules-common-interop/exports-default/actual.js new file mode 100644 index 0000000000..62923e5c15 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-default/actual.js @@ -0,0 +1,8 @@ +export default 42; +export default {}; +export default []; +export default foo; +export default function () {} +export default class {} +export default function foo () {} +export default class Foo {} diff --git a/test/fixtures/transformation/modules-common-interop/exports-default/expected.js b/test/fixtures/transformation/modules-common-interop/exports-default/expected.js new file mode 100644 index 0000000000..9a3b8194a0 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-default/expected.js @@ -0,0 +1,13 @@ +"use strict"; + +exports["default"] = 42; +exports["default"] = {}; +exports["default"] = []; +exports["default"] = foo; +exports["default"] = function () {}; +exports["default"] = function () {}; +function foo() {} +exports["default"] = foo; +var Foo = function Foo() {}; + +exports["default"] = Foo; diff --git a/test/fixtures/transformation/modules-common-interop/exports-from/actual.js b/test/fixtures/transformation/modules-common-interop/exports-from/actual.js new file mode 100644 index 0000000000..60857f6542 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-from/actual.js @@ -0,0 +1,6 @@ +export * from "foo"; +export {foo} from "foo"; +export {foo, bar} from "foo"; +export {foo as bar} from "foo"; +export {foo as default} from "foo"; +export {foo as default, bar} from "foo"; diff --git a/test/fixtures/transformation/modules-common-interop/exports-from/expected.js b/test/fixtures/transformation/modules-common-interop/exports-from/expected.js new file mode 100644 index 0000000000..feaeac94ee --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-from/expected.js @@ -0,0 +1,15 @@ +"use strict"; + +(function (obj) { + for (var i in obj) { + exports[i] = obj[i]; + } +})(require("foo")); + +exports.foo = require("foo").foo; +exports.foo = require("foo").foo; +exports.bar = require("foo").bar; +exports.bar = require("foo").foo; +exports["default"] = require("foo").foo; +exports["default"] = require("foo").foo; +exports.bar = require("foo").bar; diff --git a/test/fixtures/transformation/modules-common-interop/exports-named/actual.js b/test/fixtures/transformation/modules-common-interop/exports-named/actual.js new file mode 100644 index 0000000000..8515ace759 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-named/actual.js @@ -0,0 +1,5 @@ +export {foo}; +export {foo, bar}; +export {foo as bar}; +export {foo as default}; +export {foo as default, bar}; diff --git a/test/fixtures/transformation/modules-common-interop/exports-named/expected.js b/test/fixtures/transformation/modules-common-interop/exports-named/expected.js new file mode 100644 index 0000000000..ce378a6fb0 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-named/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +exports.foo = foo; +exports.foo = foo; +exports.bar = bar; +exports.bar = foo; +exports["default"] = foo; +exports["default"] = foo; +exports.bar = bar; diff --git a/test/fixtures/transformation/modules-common-interop/exports-variable/actual.js b/test/fixtures/transformation/modules-common-interop/exports-variable/actual.js new file mode 100644 index 0000000000..b4629cc731 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-variable/actual.js @@ -0,0 +1,8 @@ +export var foo = 1; +export var foo2 = function () {}; +export var foo3; +export let foo4 = 2; +export let foo5; +export const foo6 = 3; +export function foo7 () {} +export class foo8 {} diff --git a/test/fixtures/transformation/modules-common-interop/exports-variable/expected.js b/test/fixtures/transformation/modules-common-interop/exports-variable/expected.js new file mode 100644 index 0000000000..886cf61824 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/exports-variable/expected.js @@ -0,0 +1,13 @@ +"use strict"; + +exports.foo7 = foo7; +var foo = exports.foo = 1; +var foo2 = exports.foo2 = function () {}; +var foo3; +var foo4 = exports.foo4 = 2; +var foo5; +var foo6 = exports.foo6 = 3; +function foo7() {} +var foo8 = function foo8() {}; + +exports.foo8 = foo8; diff --git a/test/fixtures/transformation/modules-common-interop/hoist-function-exports/actual.js b/test/fixtures/transformation/modules-common-interop/hoist-function-exports/actual.js new file mode 100644 index 0000000000..3c40b7d1c1 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/hoist-function-exports/actual.js @@ -0,0 +1,11 @@ +import { isEven } from "./evens"; + +export function nextOdd(n) { + return isEven(n) ? n + 1 : n + 2; +} + +export var isOdd = (function (isEven) { + return function (n) { + return !isEven(n); + }; +})(isEven); diff --git a/test/fixtures/transformation/modules-common-interop/hoist-function-exports/expected.js b/test/fixtures/transformation/modules-common-interop/hoist-function-exports/expected.js new file mode 100644 index 0000000000..5a074e1496 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/hoist-function-exports/expected.js @@ -0,0 +1,13 @@ +"use strict"; + +exports.nextOdd = nextOdd; +var isEven = require("./evens").isEven; +function nextOdd(n) { + return isEven(n) ? n + 1 : n + 2; +} + +var isOdd = exports.isOdd = (function (isEven) { + return function (n) { + return !isEven(n); + }; +})(isEven); diff --git a/test/fixtures/transformation/modules-common-interop/imports-default/actual.js b/test/fixtures/transformation/modules-common-interop/imports-default/actual.js new file mode 100644 index 0000000000..e67418654c --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-default/actual.js @@ -0,0 +1,2 @@ +import foo from "foo"; +import {default as foo} from "foo"; diff --git a/test/fixtures/transformation/modules-common-interop/imports-default/expected.js b/test/fixtures/transformation/modules-common-interop/imports-default/expected.js new file mode 100644 index 0000000000..a93b2668f9 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-default/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +var _interopRequire = function (obj) { + return obj && (obj["default"] || obj); +}; + +var foo = _interopRequire(require("foo")); + +var foo = require("foo")["default"]; diff --git a/test/fixtures/transformation/modules-common-interop/imports-glob/actual.js b/test/fixtures/transformation/modules-common-interop/imports-glob/actual.js new file mode 100644 index 0000000000..e55c077500 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-glob/actual.js @@ -0,0 +1 @@ +import * as foo from "foo"; diff --git a/test/fixtures/transformation/modules-common-interop/imports-glob/expected.js b/test/fixtures/transformation/modules-common-interop/imports-glob/expected.js new file mode 100644 index 0000000000..e8c2d94a6a --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-glob/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +var foo = require("foo"); \ No newline at end of file diff --git a/test/fixtures/transformation/modules-common-interop/imports-mixing/actual.js b/test/fixtures/transformation/modules-common-interop/imports-mixing/actual.js new file mode 100644 index 0000000000..ef78c95b1c --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-mixing/actual.js @@ -0,0 +1 @@ +import foo, {baz as xyz} from "foo"; diff --git a/test/fixtures/transformation/modules-common-interop/imports-mixing/expected.js b/test/fixtures/transformation/modules-common-interop/imports-mixing/expected.js new file mode 100644 index 0000000000..09ea4cec86 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-mixing/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +var _interopRequire = function (obj) { + return obj && (obj["default"] || obj); +}; + +var foo = _interopRequire(require("foo")); + +var xyz = require("foo").baz; diff --git a/test/fixtures/transformation/modules-common-interop/imports-named/actual.js b/test/fixtures/transformation/modules-common-interop/imports-named/actual.js new file mode 100644 index 0000000000..83a766c62d --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-named/actual.js @@ -0,0 +1,4 @@ +import {bar} from "foo"; +import {bar, baz} from "foo"; +import {bar as baz} from "foo"; +import {bar as baz, xyz} from "foo"; diff --git a/test/fixtures/transformation/modules-common-interop/imports-named/expected.js b/test/fixtures/transformation/modules-common-interop/imports-named/expected.js new file mode 100644 index 0000000000..ed81790165 --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports-named/expected.js @@ -0,0 +1,8 @@ +"use strict"; + +var bar = require("foo").bar; +var bar = require("foo").bar; +var baz = require("foo").baz; +var baz = require("foo").bar; +var baz = require("foo").bar; +var xyz = require("foo").xyz; \ No newline at end of file diff --git a/test/fixtures/transformation/modules-common-interop/imports/actual.js b/test/fixtures/transformation/modules-common-interop/imports/actual.js new file mode 100644 index 0000000000..222b6885ac --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports/actual.js @@ -0,0 +1,3 @@ +import "foo"; +import "foo-bar"; +import "./directory/foo-bar"; diff --git a/test/fixtures/transformation/modules-common-interop/imports/expected.js b/test/fixtures/transformation/modules-common-interop/imports/expected.js new file mode 100644 index 0000000000..f1a139f51a --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/imports/expected.js @@ -0,0 +1,7 @@ +"use strict"; + +require("foo"); + +require("foo-bar"); + +require("./directory/foo-bar"); diff --git a/test/fixtures/transformation/modules-common-interop/options.json b/test/fixtures/transformation/modules-common-interop/options.json new file mode 100644 index 0000000000..d5e2792eed --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/options.json @@ -0,0 +1,3 @@ +{ + "modules": "commonInterop" +} diff --git a/test/fixtures/transformation/modules-common-interop/overview/actual.js b/test/fixtures/transformation/modules-common-interop/overview/actual.js new file mode 100644 index 0000000000..a77d4d5dfa --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/overview/actual.js @@ -0,0 +1,12 @@ +import "foo"; +import "foo-bar"; +import "./directory/foo-bar"; +import foo from "foo"; +import * as foo from "foo"; +import {bar} from "foo"; +import {foo as bar} from "foo"; + +export {test}; +export var test = 5; + +export default test; diff --git a/test/fixtures/transformation/modules-common-interop/overview/expected.js b/test/fixtures/transformation/modules-common-interop/overview/expected.js new file mode 100644 index 0000000000..208d3a3d3b --- /dev/null +++ b/test/fixtures/transformation/modules-common-interop/overview/expected.js @@ -0,0 +1,22 @@ +"use strict"; + +var _interopRequire = function (obj) { + return obj && (obj["default"] || obj); +}; + +require("foo"); + +require("foo-bar"); + +require("./directory/foo-bar"); + +var foo = _interopRequire(require("foo")); + +var foo = require("foo"); + +var bar = require("foo").bar; +var bar = require("foo").foo; +exports.test = test; +var test = exports.test = 5; + +exports["default"] = test;