diff --git a/package.json b/package.json index ec506313ad..b2d143d73a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "dependencies": { "acorn-jsx": "^1.0.0", "ast-types": "~0.7.0", + "babel-plugin-undefined-to-void": "^1.1.0", "bluebird": "^2.9.25", "chalk": "^1.0.0", "convert-source-map": "^1.1.0", diff --git a/packages/babel-cli/bin/babel-plugin b/packages/babel-cli/bin/babel-plugin new file mode 100755 index 0000000000..a8e9ea5e2b --- /dev/null +++ b/packages/babel-cli/bin/babel-plugin @@ -0,0 +1,117 @@ +#!/usr/bin/env node + +var readline = require("readline"); +var child = require("child_process"); +var path = require("path"); +var fs = require("fs"); + +function spawn(cmd, args, callback) { + console.log(">", cmd, args); + + var spawn = child.spawn(cmd, args, { stdio: "inherit" }); + + spawn.on("exit", function (code) { + if (code === 0) { + if (callback) callback(); + } else { + console.log("Killing..."); + process.exit(1); + } + }); +} + +function spawnMultiple(cmds) { + function next() { + var cmd = cmds.shift(); + if (cmd) { + spawn(cmd.command, cmd.args, next); + } else { + process.exit(); + } + } + + next(); +} + +function write(filename, content) { + console.log(filename); + fs.writeFileSync(filename, content); +} + +var BABEL_PLUGIN_PREFIX = "babel-plugin-"; + +var cmds = { + init: function () { + var name = path.basename(process.cwd()); + + if (name.indexOf(BABEL_PLUGIN_PREFIX) === 0) { + name = name.slice(BABEL_PLUGIN_PREFIX.length); + } + + write("package.json", JSON.stringify({ + name: BABEL_PLUGIN_PREFIX + name, + version: "1.0.0", + description: "", + license: "MIT", + main: "lib/index.js", + + devDependencies: { + babel: "^5.6.0" + }, + + peerDependencies: { + babel: "^5.6.0" + }, + + scripts: { + build: "babel-plugin build", + push: "babel-plugin publish", + test: "babel-plugin test" + }, + + keywords: ["babel-plugin"] + }, null, " ")); + + write(".npmignore", "node_modules\n*.log\nsrc"); + + write(".gitignore", "node_modules\n*.log\nlib"); + + fs.mkdirSync("src"); + write("src/index.js", ""); + }, + + build: function () { + spawn("babel", ["src", "--out-dir", "lib"]); + }, + + publish: function () { + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + var pkg = require(process.cwd() + "/package.json"); + console.log("Current verison:", pkg.version); + + rl.question("New version (enter nothing for patch): ", function (newVersion) { + newVersion = newVersion || "patch"; + + spawnMultiple([ + { command: "git", args: ["pull"] }, + { command: "git", args: ["push"] }, + { command: "babel-plugin", args: ["build"] }, + { command: "npm", args: ["version", newVersion] }, + { command: "npm", args: ["publish"] }, + { command: "git", args: ["push", "--follow-tags"] } + ]); + }); + } +}; + +var cmd = cmds[process.argv[2]]; +if (cmd) { + cmd(); +} else { + console.error("Unknown command:", cmd); + process.exit(1); +} diff --git a/packages/babel-cli/package.json b/packages/babel-cli/package.json index 048f398777..af82623f3a 100644 --- a/packages/babel-cli/package.json +++ b/packages/babel-cli/package.json @@ -23,6 +23,7 @@ "bin": { "babel": "./bin/babel/index.js", "babel-node": "./bin/babel-node", - "babel-external-helpers": "./bin/babel-external-helpers" + "babel-external-helpers": "./bin/babel-external-helpers", + "babel-plugin": "./bin/babel-plugin" } -} \ No newline at end of file +} diff --git a/src/babel/api/node.js b/src/babel/api/node.js index 01a05a0f67..77bfc1e24b 100644 --- a/src/babel/api/node.js +++ b/src/babel/api/node.js @@ -9,8 +9,9 @@ export { pipeline } from "../transformation"; export { canCompile } from "../util"; export { default as options } from "../transformation/file/options/config"; +export { default as Plugin } from "../transformation/plugin"; export { default as Transformer } from "../transformation/transformer"; -export { default as TransformerPipeline } from "../transformation/transformer-pipeline"; +export { default as Pipeline } from "../transformation/pipeline"; export { default as traverse } from "../traversal"; export { default as buildExternalHelpers } from "../tools/build-external-helpers"; export { version } from "../../../package"; diff --git a/src/babel/api/register/node.js b/src/babel/api/register/node.js index 7e7a99fec4..a48a7eeb16 100644 --- a/src/babel/api/register/node.js +++ b/src/babel/api/register/node.js @@ -105,7 +105,7 @@ if (process.env.running_under_istanbul) { if (istanbulMonkey[filename]) { delete istanbulMonkey[filename]; var code = compile(filename, { - auxiliaryComment: "istanbul ignore next" + auxiliaryCommentBefore: "istanbul ignore next" }); istanbulMonkey[filename] = true; return code; diff --git a/src/babel/messages.js b/src/babel/messages.js index a7ad0feafa..507112ddc2 100644 --- a/src/babel/messages.js +++ b/src/babel/messages.js @@ -34,10 +34,10 @@ export const MESSAGES = { pluginIllegalKind: "Illegal kind $1 for plugin $2", pluginIllegalPosition: "Illegal position $1 for plugin $2", pluginKeyCollision: "The plugin $1 collides with another of the same name", - pluginNotTransformer: "The plugin $1 didn't export a Transformer instance", + pluginNotTransformer: "The plugin $1 didn't export a Plugin instance", pluginUnknown: "Unknown plugin $1", - transformerNotFile: "Transformer $1 is resolving to a different Babel version to what is doing the actual transformation..." + pluginNotFile: "Plugin $1 is resolving to a different Babel version to what is doing the actual transformation..." }; export function get(key: String, ...args) { diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index e7b2d6d1ac..55937edb2a 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -4,7 +4,6 @@ import moduleFormatters from "../modules"; import PluginManager from "./plugin-manager"; import shebangRegex from "shebang-regex"; import NodePath from "../../traversal/path"; -import Transformer from "../transformer"; import isFunction from "lodash/lang/isFunction"; import isAbsolute from "path-is-absolute"; import resolveRc from "./options/resolve-rc"; @@ -14,13 +13,14 @@ import codeFrame from "../../helpers/code-frame"; import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; import traverse from "../../traversal"; -import Hub from "../../traversal/hub"; import assign from "lodash/object/assign"; import Logger from "./logger"; +import Plugin from "../plugin"; import parse from "../../helpers/parse"; import merge from "../../helpers/merge"; import slash from "slash"; import clone from "lodash/lang/clone"; +import Hub from "../../traversal/hub"; import * as util from "../../util"; import path from "path"; import * as t from "../../types"; @@ -246,7 +246,7 @@ export default class File { // build dependency graph for (let pass of (stack: Array)) { - for (var dep of (pass.transformer.dependencies: Array)) { + for (var dep of (pass.plugin.dependencies: Array)) { this.transformerDependencies[dep] = pass.key; } } @@ -263,7 +263,7 @@ export default class File { // been merged if (ignore.indexOf(pass) >= 0) continue; - var group = pass.transformer.metadata.group; + var group = pass.plugin.metadata.group; // can't merge if (!pass.canTransform() || !group) { @@ -273,7 +273,7 @@ export default class File { var mergeStack = []; for (let pass of (_stack: Array)) { - if (pass.transformer.metadata.group === group) { + if (pass.plugin.metadata.group === group) { mergeStack.push(pass); ignore.push(pass); } @@ -281,11 +281,11 @@ export default class File { var visitors = []; for (let pass of (mergeStack: Array)) { - visitors.push(pass.handlers); + visitors.push(pass.plugin.visitor); } var visitor = traverse.visitors.merge(visitors); - var mergeTransformer = new Transformer(group, visitor); - stack.push(mergeTransformer.buildPass(this)); + var mergePlugin = new Plugin(group, { visitor }); + stack.push(mergePlugin.buildPass(this)); } return stack; @@ -586,7 +586,7 @@ export default class File { call(key: string) { for (var pass of (this.uncollapsedTransformerStack: Array)) { - var fn = pass.transformer[key]; + var fn = pass.plugin[key]; if (fn) fn(this); } } diff --git a/src/babel/transformation/file/plugin-manager.js b/src/babel/transformation/file/plugin-manager.js index 3a70e0eee6..5a377f4089 100644 --- a/src/babel/transformation/file/plugin-manager.js +++ b/src/babel/transformation/file/plugin-manager.js @@ -1,4 +1,13 @@ -import * as node from "../../api/node"; +import Transformer from "../transformer"; +import Plugin from "../plugin"; +import * as types from "../../types"; + +var context = { + Transformer, + Plugin, + types +}; + import * as messages from "../../messages"; import * as util from "../../util"; @@ -11,7 +20,7 @@ export default class PluginManager { if (plugin.container === fn) return plugin.transformer; } - var transformer = fn(node); + var transformer = fn(context); PluginManager.memoisedPlugins.push({ container: fn, transformer: transformer @@ -55,7 +64,7 @@ export default class PluginManager { } // validate Transformer instance - if (!plugin.buildPass || plugin.constructor.name !== "Transformer") { + if (!plugin.buildPass || plugin.constructor.name !== "Plugin") { throw new TypeError(messages.get("pluginNotTransformer", name)); } diff --git a/src/babel/transformation/helpers/build-react-transformer.js b/src/babel/transformation/helpers/build-react-transformer.js index 441d1d0869..92c0052e1f 100644 --- a/src/babel/transformation/helpers/build-react-transformer.js +++ b/src/babel/transformation/helpers/build-react-transformer.js @@ -9,8 +9,10 @@ import esutils from "esutils"; import * as react from "./react"; import * as t from "../../types"; -export default function (exports, opts) { - exports.JSXIdentifier = function (node) { +export default function (opts) { + var visitor = {}; + + visitor.JSXIdentifier = function (node) { if (node.name === "this" && this.isReferenced()) { return t.thisExpression(); } else if (esutils.keyword.isIdentifierNameES6(node.name)) { @@ -20,22 +22,22 @@ export default function (exports, opts) { } }; - exports.JSXNamespacedName = function () { + visitor.JSXNamespacedName = function () { throw this.errorWithNode(messages.get("JSXNamespacedTags")); }; - exports.JSXMemberExpression = { + visitor.JSXMemberExpression = { exit(node) { node.computed = t.isLiteral(node.property); node.type = "MemberExpression"; } }; - exports.JSXExpressionContainer = function (node) { + visitor.JSXExpressionContainer = function (node) { return node.expression; }; - exports.JSXAttribute = { + visitor.JSXAttribute = { enter(node) { var value = node.value; if (t.isLiteral(value) && isString(value.value)) { @@ -49,7 +51,7 @@ export default function (exports, opts) { } }; - exports.JSXOpeningElement = { + visitor.JSXOpeningElement = { exit(node, parent, scope, file) { parent.children = react.buildChildren(parent); @@ -139,7 +141,7 @@ export default function (exports, opts) { return attribs; }; - exports.JSXElement = { + visitor.JSXElement = { exit(node) { var callExpr = node.openingElement; @@ -173,15 +175,15 @@ export default function (exports, opts) { } }; - exports.ExportDefaultDeclaration = function (node, parent, scope, file) { + visitor.ExportDefaultDeclaration = function (node, parent, scope, file) { if (react.isCreateClass(node.declaration)) { addDisplayName(file.opts.basename, node.declaration); } }; - exports.AssignmentExpression = - exports.Property = - exports.VariableDeclarator = function (node) { + visitor.AssignmentExpression = + visitor.Property = + visitor.VariableDeclarator = function (node) { var left, right; if (t.isAssignmentExpression(node)) { @@ -203,4 +205,6 @@ export default function (exports, opts) { addDisplayName(left.name, right); } }; + + return visitor; } diff --git a/src/babel/transformation/helpers/name-method.js b/src/babel/transformation/helpers/name-method.js index 0057123015..680fd26fd7 100644 --- a/src/babel/transformation/helpers/name-method.js +++ b/src/babel/transformation/helpers/name-method.js @@ -68,10 +68,10 @@ var visit = function (node, name, scope) { // check to see if we have a local binding of the id we're setting inside of // the function, this is important as there are caveats associated - var bindingInfo = scope.getOwnBinding(name); + var binding = scope.getOwnBinding(name); - if (bindingInfo) { - if (bindingInfo.kind === "param") { + if (binding) { + if (binding.kind === "param") { // safari will blow up in strict mode with code like: // // var t = function t(t) {}; @@ -133,8 +133,8 @@ export function bare(node, parent, scope) { id = parent.id; if (t.isIdentifier(id)) { - var bindingInfo = scope.parent.getBinding(id.name); - if (bindingInfo && bindingInfo.constant && scope.getBinding(id.name) === bindingInfo) { + var binding = scope.parent.getBinding(id.name); + if (binding && binding.constant && scope.getBinding(id.name) === binding) { // always going to reference this method node.id = id; return; diff --git a/src/babel/transformation/index.js b/src/babel/transformation/index.js index 11b0780fd1..d369f52ba9 100644 --- a/src/babel/transformation/index.js +++ b/src/babel/transformation/index.js @@ -1,4 +1,4 @@ -import Pipeline from "./transformer-pipeline"; +import Pipeline from "./pipeline"; var pipeline = new Pipeline; @@ -8,8 +8,11 @@ import transformers from "./transformers"; for (var key in transformers) { var transformer = transformers[key]; - var metadata = transformer.metadata = transformer.metadata || {}; - metadata.group = metadata.group || "builtin-basic"; + + if (typeof transformer === "object") { + var metadata = transformer.metadata = transformer.metadata || {}; + metadata.group = metadata.group || "builtin-basic"; + } } pipeline.addTransformers(transformers); diff --git a/src/babel/transformation/transformer-pipeline.js b/src/babel/transformation/pipeline.js similarity index 80% rename from src/babel/transformation/transformer-pipeline.js rename to src/babel/transformation/pipeline.js index d8c43abbd9..e929c1e7fe 100644 --- a/src/babel/transformation/transformer-pipeline.js +++ b/src/babel/transformation/pipeline.js @@ -1,10 +1,11 @@ -import Transformer from "./transformer"; +import PluginManager from "./file/plugin-manager"; import normalizeAst from "../helpers/normalize-ast"; +import Plugin from "./plugin"; import assign from "lodash/object/assign"; import object from "../helpers/object"; import File from "./file"; -export default class TransformerPipeline { +export default class Pipeline { constructor() { this.transformers = object(); this.namespaces = object(); @@ -20,7 +21,7 @@ export default class TransformerPipeline { return this; } - addTransformer(key, transformer) { + addTransformer(key, plugin) { if (this.transformers[key]) throw new Error(); // todo: error var namespace = key.split(".")[0]; @@ -28,7 +29,15 @@ export default class TransformerPipeline { this.namespaces[namespace].push(key); this.namespaces[key] = namespace; - this.transformers[key] = new Transformer(key, transformer); + if (typeof plugin === "function") { + plugin = PluginManager.memoisePluginContainer(plugin); + plugin.key = key; + plugin.metadata.optional = true; + } else { + plugin = new Plugin(key, plugin); + } + + this.transformers[key] = plugin; } addAliases(names) { @@ -46,17 +55,24 @@ export default class TransformerPipeline { return this; } - canTransform(transformer, fileOpts) { - if (transformer.metadata.plugin) return true; + canTransform(plugin, fileOpts) { + if (plugin.metadata.plugin) { + return true; + } for (var filter of (this.filters: Array)) { - var result = filter(transformer, fileOpts); + var result = filter(plugin, fileOpts); if (result != null) return result; } return true; } + analyze(code: string, opts?: Object = {}) { + opts.code = false; + return this.transform(code, opts); + } + pretransform(code: string, opts?: Object) { var file = new File(opts, this); return file.wrap(code, function () { diff --git a/src/babel/transformation/transformer-pass.js b/src/babel/transformation/plugin-pass.js similarity index 63% rename from src/babel/transformation/transformer-pass.js rename to src/babel/transformation/plugin-pass.js index 23ba74c34b..be860f1deb 100644 --- a/src/babel/transformation/transformer-pass.js +++ b/src/babel/transformation/plugin-pass.js @@ -7,14 +7,13 @@ import type File from "./file"; * AST and running it's parent transformers handlers over it. */ -export default class TransformerPass { - constructor(file: File, transformer: Transformer) { - this.transformer = transformer; - this.handlers = transformer.handlers; - this.file = file; - this.key = transformer.key; +export default class PluginPass { + constructor(file: File, plugin: Transformer) { + this.plugin = plugin; + this.file = file; + this.key = plugin.key; - if (this.canTransform() && transformer.metadata.experimental && !file.opts.experimental) { + if (this.canTransform() && plugin.metadata.experimental && !file.opts.experimental) { file.log.warn(`THE TRANSFORMER ${this.key} HAS BEEN MARKED AS EXPERIMENTAL AND IS WIP. USE AT YOUR OWN RISK. ` + "THIS WILL HIGHLY LIKELY BREAK YOUR CODE SO USE WITH **EXTREME** CAUTION. ENABLE THE " + "`experimental` OPTION TO IGNORE THIS WARNING."); @@ -23,13 +22,13 @@ export default class TransformerPass { canTransform(): boolean { return this.file.transformerDependencies[this.key] || - this.file.pipeline.canTransform(this.transformer, this.file.opts); + this.file.pipeline.canTransform(this.plugin, this.file.opts); } transform() { var file = this.file; file.log.debug(`Start transformer ${this.key}`); - traverse(file.ast, this.handlers, file.scope, file); + traverse(file.ast, this.plugin.visitor, file.scope, file); file.log.debug(`Finish transformer ${this.key}`); } } diff --git a/src/babel/transformation/plugin.js b/src/babel/transformation/plugin.js new file mode 100644 index 0000000000..d2c620df49 --- /dev/null +++ b/src/babel/transformation/plugin.js @@ -0,0 +1,55 @@ +import PluginPass from "./plugin-pass"; +import * as messages from "../messages"; +import isFunction from "lodash/lang/isFunction"; +import traverse from "../traversal"; +import assign from "lodash/object/assign"; +import clone from "lodash/lang/clone"; +import File from "./file"; + +export default class Plugin { + constructor(key: string, plugin: Object) { + plugin = assign({}, plugin); + + var take = function (key) { + var val = plugin[key]; + delete plugin[key]; + return val; + }; + + this.manipulateOptions = take("manipulateOptions"); + this.metadata = take("metadata") || {}; + this.dependencies = this.metadata.dependencies || []; + this.post = take("post"); + this.pre = take("pre"); + + // + + if (this.metadata.stage != null) { + this.metadata.optional = true; + } + + // + + this.visitor = this.normalize(clone(take("visitor")) || {}); + this.key = key; + } + + normalize(visitor: Object): Object { + if (isFunction(visitor)) { + visitor = { ast: visitor }; + } + + traverse.explode(visitor); + + return visitor; + } + + buildPass(file: File): PluginPass { + // validate Transformer instance + if (!(file instanceof File)) { + throw new TypeError(messages.get("pluginNotFile", this.key)); + } + + return new PluginPass(file, this); + } +} diff --git a/src/babel/transformation/transformer.js b/src/babel/transformation/transformer.js index 9ad9400cc4..2077d9ea81 100644 --- a/src/babel/transformation/transformer.js +++ b/src/babel/transformation/transformer.js @@ -1,82 +1,14 @@ -import TransformerPass from "./transformer-pass"; -import * as messages from "../messages"; -import isFunction from "lodash/lang/isFunction"; -import traverse from "../traversal"; -import isObject from "lodash/lang/isObject"; -import assign from "lodash/object/assign"; -import File from "./file"; -import each from "lodash/collection/each"; - -/** - * This is the class responsible for normalising a transformers handlers - * as well as constructing a `TransformerPass` that is responsible for - * actually running the transformer over the provided `File`. - */ +import Plugin from "./plugin"; export default class Transformer { - constructor(transformerKey: string, transformer: Object) { - transformer = assign({}, transformer); + constructor(key, obj) { + var plugin = {}; - var take = function (key) { - var val = transformer[key]; - delete transformer[key]; - return val; - }; + plugin.metadata = obj.metadata; + delete obj.metadata; - this.manipulateOptions = take("manipulateOptions"); - this.metadata = take("metadata") || {}; - this.dependencies = this.metadata.dependencies || []; - this.parser = take("parser"); - this.post = take("post"); - this.pre = take("pre"); + plugin.visitor = obj; - // - - if (this.metadata.stage != null) { - this.metadata.optional = true; - } - - // - - this.handlers = this.normalize(transformer); - this.key = transformerKey; - } - - normalize(transformer: Object): Object { - if (isFunction(transformer)) { - transformer = { ast: transformer }; - } - - traverse.explode(transformer); - - each(transformer, (fns, type) => { - // hidden property - if (type[0] === "_") { - this[type] = fns; - return; - } - - if (type === "enter" || type === "exit") return; - - if (isFunction(fns)) fns = { enter: fns }; - - if (!isObject(fns)) return; - - if (!fns.enter) fns.enter = function () { }; - if (!fns.exit) fns.exit = function () { }; - - transformer[type] = fns; - }); - - return transformer; - } - - buildPass(file: File): TransformerPass { - // validate Transformer instance - if (!(file instanceof File)) { - throw new TypeError(messages.get("transformerNotFile", this.key)); - } - - return new TransformerPass(file, this); + return new Plugin(key, plugin); } } diff --git a/src/babel/transformation/transformers/es3/member-expression-literals.js b/src/babel/transformation/transformers/es3/member-expression-literals.js index af026a5171..ec0ea35db2 100644 --- a/src/babel/transformation/transformers/es3/member-expression-literals.js +++ b/src/babel/transformation/transformers/es3/member-expression-literals.js @@ -4,13 +4,15 @@ export var metadata = { group: "builtin-trailing" }; -export var MemberExpression = { - exit(node) { - var prop = node.property; - if (!node.computed && t.isIdentifier(prop) && !t.isValidIdentifier(prop.name)) { - // foo.default -> foo["default"] - node.property = t.literal(prop.name); - node.computed = true; +export var visitor = { + MemberExpression: { + exit(node) { + var prop = node.property; + if (!node.computed && t.isIdentifier(prop) && !t.isValidIdentifier(prop.name)) { + // foo.default -> foo["default"] + node.property = t.literal(prop.name); + node.computed = true; + } } } }; diff --git a/src/babel/transformation/transformers/es3/property-literals.js b/src/babel/transformation/transformers/es3/property-literals.js index ffb13ddc64..ff2040b572 100644 --- a/src/babel/transformation/transformers/es3/property-literals.js +++ b/src/babel/transformation/transformers/es3/property-literals.js @@ -4,12 +4,14 @@ export var metadata = { group: "builtin-trailing" }; -export var Property = { - exit(node) { - var key = node.key; - if (!node.computed && t.isIdentifier(key) && !t.isValidIdentifier(key.name)) { - // default: "bar" -> "default": "bar" - node.key = t.literal(key.name); +export var visitor = { + Property: { + exit(node) { + var key = node.key; + if (!node.computed && t.isIdentifier(key) && !t.isValidIdentifier(key.name)) { + // default: "bar" -> "default": "bar" + node.key = t.literal(key.name); + } } } }; diff --git a/src/babel/transformation/transformers/es5/properties.mutators.js b/src/babel/transformation/transformers/es5/properties.mutators.js index 57e46dbfa5..953527df25 100644 --- a/src/babel/transformation/transformers/es5/properties.mutators.js +++ b/src/babel/transformation/transformers/es5/properties.mutators.js @@ -1,29 +1,31 @@ import * as defineMap from "../../helpers/define-map"; import * as t from "../../../types"; -export function ObjectExpression(node, parent, scope, file) { - var hasAny = false; - for (var prop of (node.properties: Array)) { - if (prop.kind === "get" || prop.kind === "set") { - hasAny = true; - break; +export var visitor = { + ObjectExpression(node, parent, scope, file) { + var hasAny = false; + for (var prop of (node.properties: Array)) { + if (prop.kind === "get" || prop.kind === "set") { + hasAny = true; + break; + } } + if (!hasAny) return; + + var mutatorMap = {}; + + node.properties = node.properties.filter(function (prop) { + if (prop.kind === "get" || prop.kind === "set") { + defineMap.push(mutatorMap, prop, prop.kind, file); + return false; + } else { + return true; + } + }); + + return t.callExpression( + t.memberExpression(t.identifier("Object"), t.identifier("defineProperties")), + [node, defineMap.toDefineObject(mutatorMap)] + ); } - if (!hasAny) return; - - var mutatorMap = {}; - - node.properties = node.properties.filter(function (prop) { - if (prop.kind === "get" || prop.kind === "set") { - defineMap.push(mutatorMap, prop, prop.kind, file); - return false; - } else { - return true; - } - }); - - return t.callExpression( - t.memberExpression(t.identifier("Object"), t.identifier("defineProperties")), - [node, defineMap.toDefineObject(mutatorMap)] - ); -} +}; diff --git a/src/babel/transformation/transformers/es6/arrow-functions.js b/src/babel/transformation/transformers/es6/arrow-functions.js index 39d93f829a..653cf6b78e 100644 --- a/src/babel/transformation/transformers/es6/arrow-functions.js +++ b/src/babel/transformation/transformers/es6/arrow-functions.js @@ -1,9 +1,11 @@ import * as t from "../../../types"; -export function ArrowFunctionExpression(node) { - t.ensureBlock(node); +export var visitor = { + ArrowFunctionExpression(node) { + t.ensureBlock(node); - node.expression = false; - node.type = "FunctionExpression"; - node.shadow = true; -} + node.expression = false; + node.type = "FunctionExpression"; + node.shadow = true; + } +}; diff --git a/src/babel/transformation/transformers/es6/block-scoping.js b/src/babel/transformation/transformers/es6/block-scoping.js index a4bdfda181..b134d5e855 100644 --- a/src/babel/transformation/transformers/es6/block-scoping.js +++ b/src/babel/transformation/transformers/es6/block-scoping.js @@ -44,47 +44,47 @@ export var metadata = { group: "builtin-advanced" }; -export function VariableDeclaration(node, parent, scope, file) { - if (!isLet(node, parent)) return; +export var visitor = { + VariableDeclaration(node, parent, scope, file) { + if (!isLet(node, parent)) return; - if (isLetInitable(node) && file.transformers["es6.spec.blockScoping"].canTransform()) { - var nodes = [node]; + if (isLetInitable(node) && file.transformers["es6.spec.blockScoping"].canTransform()) { + var nodes = [node]; - for (var i = 0; i < node.declarations.length; i++) { - var decl = node.declarations[i]; - if (decl.init) { - var assign = t.assignmentExpression("=", decl.id, decl.init); - assign._ignoreBlockScopingTDZ = true; - nodes.push(t.expressionStatement(assign)); + for (var i = 0; i < node.declarations.length; i++) { + var decl = node.declarations[i]; + if (decl.init) { + var assign = t.assignmentExpression("=", decl.id, decl.init); + assign._ignoreBlockScopingTDZ = true; + nodes.push(t.expressionStatement(assign)); + } + decl.init = file.addHelper("temporal-undefined"); } - decl.init = file.addHelper("temporal-undefined"); + + node._blockHoist = 2; + + return nodes; + } + }, + + Loop(node, parent, scope, file) { + var init = node.left || node.init; + if (isLet(init, node)) { + t.ensureBlock(node); + node.body._letDeclarators = [init]; } - node._blockHoist = 2; + var blockScoping = new BlockScoping(this, this.get("body"), parent, scope, file); + return blockScoping.run(); + }, - return nodes; + "BlockStatement|Program"(block, parent, scope, file) { + if (!t.isLoop(parent)) { + var blockScoping = new BlockScoping(null, this, parent, scope, file); + blockScoping.run(); + } } -} - -export function Loop(node, parent, scope, file) { - var init = node.left || node.init; - if (isLet(init, node)) { - t.ensureBlock(node); - node.body._letDeclarators = [init]; - } - - var blockScoping = new BlockScoping(this, this.get("body"), parent, scope, file); - return blockScoping.run(); -} - -export function BlockStatement(block, parent, scope, file) { - if (!t.isLoop(parent)) { - var blockScoping = new BlockScoping(null, this, parent, scope, file); - blockScoping.run(); - } -} - -export { BlockStatement as Program }; +}; function replace(node, parent, scope, remaps) { var remap = remaps[node.name]; diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index 223ba9834c..3647d1f5da 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -10,15 +10,17 @@ import * as t from "../../../types"; const PROPERTY_COLLISION_METHOD_NAME = "__initializeProperties"; -export function ClassDeclaration(node, parent, scope, file) { - return t.variableDeclaration("let", [ - t.variableDeclarator(node.id, t.toExpression(node)) - ]); -} +export var visitor = { + ClassDeclaration(node) { + return t.variableDeclaration("let", [ + t.variableDeclarator(node.id, t.toExpression(node)) + ]); + }, -export function ClassExpression(node, parent, scope, file) { - return new ClassTransformer(this, file).run(); -} + ClassExpression(node, parent, scope, file) { + return new ClassTransformer(this, file).run(); + } +}; var collectPropertyReferencesVisitor = { Identifier: { diff --git a/src/babel/transformation/transformers/es6/constants.js b/src/babel/transformation/transformers/es6/constants.js index 65599b4291..6355428cdc 100644 --- a/src/babel/transformation/transformers/es6/constants.js +++ b/src/babel/transformation/transformers/es6/constants.js @@ -1,18 +1,20 @@ import * as messages from "../../../messages"; -export function Scope(node, parent, scope) { - for (var name in scope.bindings) { - var binding = scope.bindings[name]; +export var visitor = { + Scope(node, parent, scope) { + for (var name in scope.bindings) { + var binding = scope.bindings[name]; - // not a constant - if (binding.kind !== "const" && binding.kind !== "module") continue; + // not a constant + if (binding.kind !== "const" && binding.kind !== "module") continue; - for (var violation of (binding.constantViolations: Array)) { - throw violation.errorWithNode(messages.get("readOnly", name)); + for (var violation of (binding.constantViolations: Array)) { + throw violation.errorWithNode(messages.get("readOnly", name)); + } } - } -} + }, -export function VariableDeclaration(node) { - if (node.kind === "const") node.kind = "let"; -} + VariableDeclaration(node) { + if (node.kind === "const") node.kind = "let"; + } +}; diff --git a/src/babel/transformation/transformers/es6/destructuring.js b/src/babel/transformation/transformers/es6/destructuring.js index 60db95a446..9596aa1891 100644 --- a/src/babel/transformation/transformers/es6/destructuring.js +++ b/src/babel/transformation/transformers/es6/destructuring.js @@ -5,145 +5,201 @@ export var metadata = { group: "builtin-advanced" }; -export function ForOfStatement(node, parent, scope, file) { - var left = node.left; +export var visitor = { + ForXStatement(node, parent, scope, file) { + var left = node.left; - if (t.isPattern(left)) { - // for ({ length: k } in { abc: 3 }); + if (t.isPattern(left)) { + // for ({ length: k } in { abc: 3 }); - var temp = scope.generateUidIdentifier("ref"); + var temp = scope.generateUidIdentifier("ref"); - node.left = t.variableDeclaration("var", [ - t.variableDeclarator(temp) + node.left = t.variableDeclaration("var", [ + t.variableDeclarator(temp) + ]); + + t.ensureBlock(node); + + node.body.body.unshift(t.variableDeclaration("var", [ + t.variableDeclarator(left, temp) + ])); + + return; + } + + if (!t.isVariableDeclaration(left)) return; + + var pattern = left.declarations[0].id; + if (!t.isPattern(pattern)) return; + + var key = scope.generateUidIdentifier("ref"); + node.left = t.variableDeclaration(left.kind, [ + t.variableDeclarator(key, null) ]); + var nodes = []; + + var destructuring = new DestructuringTransformer({ + kind: left.kind, + file: file, + scope: scope, + nodes: nodes + }); + + destructuring.init(pattern, key); + t.ensureBlock(node); - node.body.body.unshift(t.variableDeclaration("var", [ - t.variableDeclarator(left, temp) - ])); + var block = node.body; + block.body = nodes.concat(block.body); + }, - return; - } - - if (!t.isVariableDeclaration(left)) return; - - var pattern = left.declarations[0].id; - if (!t.isPattern(pattern)) return; - - var key = scope.generateUidIdentifier("ref"); - node.left = t.variableDeclaration(left.kind, [ - t.variableDeclarator(key, null) - ]); - - var nodes = []; - - var destructuring = new DestructuringTransformer({ - kind: left.kind, - file: file, - scope: scope, - nodes: nodes - }); - - destructuring.init(pattern, key); - - t.ensureBlock(node); - - var block = node.body; - block.body = nodes.concat(block.body); -} - -export { ForOfStatement as ForInStatement }; - -export function Func/*tion*/(node, parent, scope, file) { - var hasDestructuring = false; - for (let pattern of (node.params: Array)) { - if (t.isPattern(pattern)) { - hasDestructuring = true; - break; + Function(node, parent, scope, file) { + var hasDestructuring = false; + for (let pattern of (node.params: Array)) { + if (t.isPattern(pattern)) { + hasDestructuring = true; + break; + } } - } - if (!hasDestructuring) return; + if (!hasDestructuring) return; - var nodes = []; + var nodes = []; - for (var i = 0; i < node.params.length; i++) { - let pattern = node.params[i]; - if (!t.isPattern(pattern)) continue; + for (var i = 0; i < node.params.length; i++) { + let pattern = node.params[i]; + if (!t.isPattern(pattern)) continue; - var ref = node.params[i] = scope.generateUidIdentifier("ref"); - t.inherits(ref, pattern); + var ref = node.params[i] = scope.generateUidIdentifier("ref"); + t.inherits(ref, pattern); + + var destructuring = new DestructuringTransformer({ + blockHoist: node.params.length - i, + nodes: nodes, + scope: scope, + file: file, + kind: "let" + }); + + destructuring.init(pattern, ref); + } + + t.ensureBlock(node); + + var block = node.body; + block.body = nodes.concat(block.body); + }, + + CatchClause(node, parent, scope, file) { + var pattern = node.param; + if (!t.isPattern(pattern)) return; + + var ref = scope.generateUidIdentifier("ref"); + node.param = ref; + + var nodes = []; var destructuring = new DestructuringTransformer({ - blockHoist: node.params.length - i, - nodes: nodes, - scope: scope, - file: file, - kind: "let" + kind: "let", + file: file, + scope: scope, + nodes: nodes + }); + destructuring.init(pattern, ref); + + node.body.body = nodes.concat(node.body.body); + }, + + AssignmentExpression(node, parent, scope, file) { + if (!t.isPattern(node.left)) return; + + var nodes = []; + + var destructuring = new DestructuringTransformer({ + operator: node.operator, + file: file, + scope: scope, + nodes: nodes }); - destructuring.init(pattern, ref); - } + var ref; + if (this.isCompletionRecord() || !this.parentPath.isExpressionStatement()) { + ref = scope.generateUidIdentifierBasedOnNode(node.right, "ref"); - t.ensureBlock(node); + nodes.push(t.variableDeclaration("var", [ + t.variableDeclarator(ref, node.right) + ])); - var block = node.body; - block.body = nodes.concat(block.body); -} - -export function CatchClause(node, parent, scope, file) { - var pattern = node.param; - if (!t.isPattern(pattern)) return; - - var ref = scope.generateUidIdentifier("ref"); - node.param = ref; - - var nodes = []; - - var destructuring = new DestructuringTransformer({ - kind: "let", - file: file, - scope: scope, - nodes: nodes - }); - destructuring.init(pattern, ref); - - node.body.body = nodes.concat(node.body.body); -} - -export function AssignmentExpression(node, parent, scope, file) { - if (!t.isPattern(node.left)) return; - - var nodes = []; - - var destructuring = new DestructuringTransformer({ - operator: node.operator, - file: file, - scope: scope, - nodes: nodes - }); - - var ref; - if (this.isCompletionRecord() || !this.parentPath.isExpressionStatement()) { - ref = scope.generateUidIdentifierBasedOnNode(node.right, "ref"); - - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(ref, node.right) - ])); - - if (t.isArrayExpression(node.right)) { - destructuring.arrays[ref.name] = true; + if (t.isArrayExpression(node.right)) { + destructuring.arrays[ref.name] = true; + } } + + destructuring.init(node.left, ref || node.right); + + if (ref) { + nodes.push(t.expressionStatement(ref)); + } + + return nodes; + }, + + VariableDeclaration(node, parent, scope, file) { + if (t.isForXStatement(parent)) return; + if (!variableDeclarationHasPattern(node)) return; + + var nodes = []; + var declar; + + for (var i = 0; i < node.declarations.length; i++) { + declar = node.declarations[i]; + + var patternId = declar.init; + var pattern = declar.id; + + var destructuring = new DestructuringTransformer({ + nodes: nodes, + scope: scope, + kind: node.kind, + file: file + }); + + if (t.isPattern(pattern)) { + destructuring.init(pattern, patternId); + + if (+i !== node.declarations.length - 1) { + // we aren't the last declarator so let's just make the + // last transformed node inherit from us + t.inherits(nodes[nodes.length - 1], declar); + } + } else { + nodes.push(t.inherits(destructuring.buildVariableAssignment(declar.id, declar.init), declar)); + } + } + + if (!t.isProgram(parent) && !t.isBlockStatement(parent)) { + // https://github.com/babel/babel/issues/113 + // for (let [x] = [0]; false;) {} + + declar = null; + + for (i = 0; i < nodes.length; i++) { + node = nodes[i]; + declar = declar || t.variableDeclaration(node.kind, []); + + if (!t.isVariableDeclaration(node) && declar.kind !== node.kind) { + throw file.errorWithNode(node, messages.get("invalidParentForThisNode")); + } + + declar.declarations = declar.declarations.concat(node.declarations); + } + + return declar; + } + + return nodes; } - - destructuring.init(node.left, ref || node.right); - - if (ref) { - nodes.push(t.expressionStatement(ref)); - } - - return nodes; -} +}; function variableDeclarationHasPattern(node) { for (var i = 0; i < node.declarations.length; i++) { @@ -154,62 +210,6 @@ function variableDeclarationHasPattern(node) { return false; } -export function VariableDeclaration(node, parent, scope, file) { - if (t.isForXStatement(parent)) return; - if (!variableDeclarationHasPattern(node)) return; - - var nodes = []; - var declar; - - for (var i = 0; i < node.declarations.length; i++) { - declar = node.declarations[i]; - - var patternId = declar.init; - var pattern = declar.id; - - var destructuring = new DestructuringTransformer({ - nodes: nodes, - scope: scope, - kind: node.kind, - file: file - }); - - if (t.isPattern(pattern)) { - destructuring.init(pattern, patternId); - - if (+i !== node.declarations.length - 1) { - // we aren't the last declarator so let's just make the - // last transformed node inherit from us - t.inherits(nodes[nodes.length - 1], declar); - } - } else { - nodes.push(t.inherits(destructuring.buildVariableAssignment(declar.id, declar.init), declar)); - } - } - - if (!t.isProgram(parent) && !t.isBlockStatement(parent)) { - // https://github.com/babel/babel/issues/113 - // for (let [x] = [0]; false;) {} - - declar = null; - - for (i = 0; i < nodes.length; i++) { - node = nodes[i]; - declar = declar || t.variableDeclaration(node.kind, []); - - if (!t.isVariableDeclaration(node) && declar.kind !== node.kind) { - throw file.errorWithNode(node, messages.get("invalidParentForThisNode")); - } - - declar.declarations = declar.declarations.concat(node.declarations); - } - - return declar; - } - - return nodes; -} - function hasRest(pattern) { for (var i = 0; i < pattern.elements.length; i++) { if (t.isRestElement(pattern.elements[i])) { diff --git a/src/babel/transformation/transformers/es6/for-of.js b/src/babel/transformation/transformers/es6/for-of.js index a8deac42dc..feb87dd568 100644 --- a/src/babel/transformation/transformers/es6/for-of.js +++ b/src/babel/transformation/transformers/es6/for-of.js @@ -2,40 +2,42 @@ import * as messages from "../../../messages"; import * as util from "../../../util"; import * as t from "../../../types"; -export function ForOfStatement(node, parent, scope, file) { - if (this.get("right").isArrayExpression()) { - return _ForOfStatementArray.call(this, node, scope, file); +export var visitor = { + ForOfStatement(node, parent, scope, file) { + if (this.get("right").isArrayExpression()) { + return _ForOfStatementArray.call(this, node, scope, file); + } + + var callback = spec; + if (file.isLoose("es6.forOf")) callback = loose; + + var build = callback(node, parent, scope, file); + var declar = build.declar; + var loop = build.loop; + var block = loop.body; + + // ensure that it's a block so we can take all its statements + t.ensureBlock(node); + + // add the value declaration to the new loop body + if (declar) { + block.body.push(declar); + } + + // push the rest of the original loop body onto our new body + block.body = block.body.concat(node.body.body); + + t.inherits(loop, node); + t.inherits(loop.body, node.body); + + if (build.replaceParent) { + this.parentPath.replaceWithMultiple(build.node); + this.dangerouslyRemove(); + } else { + return build.node; + } } - - var callback = spec; - if (file.isLoose("es6.forOf")) callback = loose; - - var build = callback(node, parent, scope, file); - var declar = build.declar; - var loop = build.loop; - var block = loop.body; - - // ensure that it's a block so we can take all its statements - t.ensureBlock(node); - - // add the value declaration to the new loop body - if (declar) { - block.body.push(declar); - } - - // push the rest of the original loop body onto our new body - block.body = block.body.concat(node.body.body); - - t.inherits(loop, node); - t.inherits(loop.body, node.body); - - if (build.replaceParent) { - this.parentPath.replaceWithMultiple(build.node); - this.dangerouslyRemove(); - } else { - return build.node; - } -} +}; export function _ForOfStatementArray(node, scope, file) { var nodes = []; diff --git a/src/babel/transformation/transformers/es6/modules.js b/src/babel/transformation/transformers/es6/modules.js index 1dfe4314b0..e83a1454c6 100644 --- a/src/babel/transformation/transformers/es6/modules.js +++ b/src/babel/transformation/transformers/es6/modules.js @@ -12,64 +12,66 @@ export var metadata = { group: "builtin-modules" }; -export function ImportDeclaration(node, parent, scope, file) { - // flow type - if (node.isType) return; +export var visitor = { + ImportDeclaration(node, parent, scope, file) { + // flow type + if (node.isType) return; - var nodes = []; + var nodes = []; - if (node.specifiers.length) { - for (var specifier of (node.specifiers: Array)) { - file.moduleFormatter.importSpecifier(specifier, node, nodes, parent); - } - } else { - file.moduleFormatter.importDeclaration(node, nodes, parent); - } - - if (nodes.length === 1) { - // inherit `_blockHoist` - this is for `_blockHoist` in File.prototype.addImport - nodes[0]._blockHoist = node._blockHoist; - } - - return nodes; -} - -export function ExportAllDeclaration(node, parent, scope, file) { - var nodes = []; - file.moduleFormatter.exportAllDeclaration(node, nodes, parent); - keepBlockHoist(node, nodes); - return nodes; -} - -export function ExportDefaultDeclaration(node, parent, scope, file) { - var nodes = []; - file.moduleFormatter.exportDeclaration(node, nodes, parent); - keepBlockHoist(node, nodes); - return nodes; -} - -export function ExportNamedDeclaration(node, parent, scope, file) { - // flow type - if (this.get("declaration").isTypeAlias()) return; - - var nodes = []; - - if (node.declaration) { - // make sure variable exports have an initializer - // this is done here to avoid duplicating it in the module formatters - if (t.isVariableDeclaration(node.declaration)) { - var declar = node.declaration.declarations[0]; - declar.init = declar.init || t.identifier("undefined"); + if (node.specifiers.length) { + for (var specifier of (node.specifiers: Array)) { + file.moduleFormatter.importSpecifier(specifier, node, nodes, parent); + } + } else { + file.moduleFormatter.importDeclaration(node, nodes, parent); } + if (nodes.length === 1) { + // inherit `_blockHoist` - this is for `_blockHoist` in File.prototype.addImport + nodes[0]._blockHoist = node._blockHoist; + } + + return nodes; + }, + + ExportAllDeclaration(node, parent, scope, file) { + var nodes = []; + file.moduleFormatter.exportAllDeclaration(node, nodes, parent); + keepBlockHoist(node, nodes); + return nodes; + }, + + ExportDefaultDeclaration(node, parent, scope, file) { + var nodes = []; file.moduleFormatter.exportDeclaration(node, nodes, parent); - } else if (node.specifiers) { - for (let i = 0; i < node.specifiers.length; i++) { - file.moduleFormatter.exportSpecifier(node.specifiers[i], node, nodes, parent); + keepBlockHoist(node, nodes); + return nodes; + }, + + ExportNamedDeclaration(node, parent, scope, file) { + // flow type + if (this.get("declaration").isTypeAlias()) return; + + var nodes = []; + + if (node.declaration) { + // make sure variable exports have an initializer + // this is done here to avoid duplicating it in the module formatters + if (t.isVariableDeclaration(node.declaration)) { + var declar = node.declaration.declarations[0]; + declar.init = declar.init || t.identifier("undefined"); + } + + file.moduleFormatter.exportDeclaration(node, nodes, parent); + } else if (node.specifiers) { + for (let i = 0; i < node.specifiers.length; i++) { + file.moduleFormatter.exportSpecifier(node.specifiers[i], node, nodes, parent); + } } + + keepBlockHoist(node, nodes); + + return nodes; } - - keepBlockHoist(node, nodes); - - return nodes; -} +}; diff --git a/src/babel/transformation/transformers/es6/object-super.js b/src/babel/transformation/transformers/es6/object-super.js index 64e461c312..a63e05cc25 100644 --- a/src/babel/transformation/transformers/es6/object-super.js +++ b/src/babel/transformation/transformers/es6/object-super.js @@ -17,17 +17,19 @@ function Property(path, node, scope, getObjectRef, file) { replaceSupers.replace(); } -export function ObjectExpression(node, parent, scope, file) { - var objectRef; - var getObjectRef = () => objectRef = objectRef || scope.generateUidIdentifier("obj"); +export var visitor = { + ObjectExpression(node, parent, scope, file) { + var objectRef; + var getObjectRef = () => objectRef = objectRef || scope.generateUidIdentifier("obj"); - var propPaths = this.get("properties"); - for (var i = 0; i < node.properties.length; i++) { - Property(propPaths[i], node.properties[i], scope, getObjectRef, file); - } + var propPaths = this.get("properties"); + for (var i = 0; i < node.properties.length; i++) { + Property(propPaths[i], node.properties[i], scope, getObjectRef, file); + } - if (objectRef) { - scope.push({ id: objectRef }); - return t.assignmentExpression("=", objectRef, node); + if (objectRef) { + scope.push({ id: objectRef }); + return t.assignmentExpression("=", objectRef, node); + } } -} +}; diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 04f6f7391c..4e8322e713 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -22,97 +22,99 @@ var iifeVisitor = { } }; -export function Func/*tion*/(node, parent, scope, file) { - if (!hasDefaults(node)) return; +export var visitor = { + Function(node, parent, scope, file) { + if (!hasDefaults(node)) return; - // ensure it's a block, useful for arrow functions - t.ensureBlock(node); + // ensure it's a block, useful for arrow functions + t.ensureBlock(node); - var state = { - iife: false, - scope: scope - }; + var state = { + iife: false, + scope: scope + }; - var body = []; + var body = []; - // - var argsIdentifier = t.identifier("arguments"); - argsIdentifier._shadowedFunctionLiteral = true; + // + var argsIdentifier = t.identifier("arguments"); + argsIdentifier._shadowedFunctionLiteral = true; - // push a default parameter definition - function pushDefNode(left, right, i) { - var defNode; - if (exceedsLastNonDefault(i) || t.isPattern(left) || file.transformers["es6.spec.blockScoping"].canTransform()) { - defNode = util.template("default-parameter", { - VARIABLE_NAME: left, - DEFAULT_VALUE: right, - ARGUMENT_KEY: t.literal(i), - ARGUMENTS: argsIdentifier - }, true); - } else { - defNode = util.template("default-parameter-assign", { - VARIABLE_NAME: left, - DEFAULT_VALUE: right - }, true); - } - defNode._blockHoist = node.params.length - i; - body.push(defNode); - } - - // check if an index exceeds the functions arity - function exceedsLastNonDefault(i) { - return i + 1 > lastNonDefaultParam; - } - - // - var lastNonDefaultParam = getFunctionArity(node); - - // - var params = this.get("params"); - for (var i = 0; i < params.length; i++) { - var param = params[i]; - - if (!param.isAssignmentPattern()) { - if (!param.isIdentifier()) { - param.traverse(iifeVisitor, state); - } - - if (file.transformers["es6.spec.blockScoping"].canTransform() && param.isIdentifier()) { - pushDefNode(param.node, t.identifier("undefined"), i); - } - - continue; - } - - var left = param.get("left"); - var right = param.get("right"); - - if (exceedsLastNonDefault(i) || left.isPattern()) { - var placeholder = scope.generateUidIdentifier("x"); - placeholder._isDefaultPlaceholder = true; - node.params[i] = placeholder; - } else { - node.params[i] = left.node; - } - - if (!state.iife) { - if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) { - state.iife = true; + // push a default parameter definition + function pushDefNode(left, right, i) { + var defNode; + if (exceedsLastNonDefault(i) || t.isPattern(left) || file.transformers["es6.spec.blockScoping"].canTransform()) { + defNode = util.template("default-parameter", { + VARIABLE_NAME: left, + DEFAULT_VALUE: right, + ARGUMENT_KEY: t.literal(i), + ARGUMENTS: argsIdentifier + }, true); } else { - right.traverse(iifeVisitor, state); + defNode = util.template("default-parameter-assign", { + VARIABLE_NAME: left, + DEFAULT_VALUE: right + }, true); } + defNode._blockHoist = node.params.length - i; + body.push(defNode); } - pushDefNode(left.node, right.node, i); - } + // check if an index exceeds the functions arity + function exceedsLastNonDefault(i) { + return i + 1 > lastNonDefaultParam; + } - // we need to cut off all trailing default parameters - node.params = node.params.slice(0, lastNonDefaultParam); + // + var lastNonDefaultParam = getFunctionArity(node); - if (state.iife) { - body.push(callDelegate(node, scope)); - node.body = t.blockStatement(body); - } else { - node.body.body = body.concat(node.body.body); + // + var params = this.get("params"); + for (var i = 0; i < params.length; i++) { + var param = params[i]; + + if (!param.isAssignmentPattern()) { + if (!param.isIdentifier()) { + param.traverse(iifeVisitor, state); + } + + if (file.transformers["es6.spec.blockScoping"].canTransform() && param.isIdentifier()) { + pushDefNode(param.node, t.identifier("undefined"), i); + } + + continue; + } + + var left = param.get("left"); + var right = param.get("right"); + + if (exceedsLastNonDefault(i) || left.isPattern()) { + var placeholder = scope.generateUidIdentifier("x"); + placeholder._isDefaultPlaceholder = true; + node.params[i] = placeholder; + } else { + node.params[i] = left.node; + } + + if (!state.iife) { + if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) { + state.iife = true; + } else { + right.traverse(iifeVisitor, state); + } + } + + pushDefNode(left.node, right.node, i); + } + + // we need to cut off all trailing default parameters + node.params = node.params.slice(0, lastNonDefaultParam); + + if (state.iife) { + body.push(callDelegate(node, scope)); + node.body = t.blockStatement(body); + } else { + node.body.body = body.concat(node.body.body); + } } -} +}; diff --git a/src/babel/transformation/transformers/es6/parameters.rest.js b/src/babel/transformation/transformers/es6/parameters.rest.js index 5154b2aa32..3c1d8e044e 100644 --- a/src/babel/transformation/transformers/es6/parameters.rest.js +++ b/src/babel/transformation/transformers/es6/parameters.rest.js @@ -58,91 +58,93 @@ function optimizeMemberExpression(parent, offset) { } } -var hasRest = function (node) { +function hasRest(node) { return t.isRestElement(node.params[node.params.length - 1]); -}; - -export function Func/*tion*/(node, parent, scope, file) { - if (!hasRest(node)) return; - - var restParam = node.params.pop(); - var rest = restParam.argument; - - var argsId = t.identifier("arguments"); - - // otherwise `arguments` will be remapped in arrow functions - argsId._shadowedFunctionLiteral = true; - - // support patterns - if (t.isPattern(rest)) { - var pattern = rest; - rest = scope.generateUidIdentifier("ref"); - - var declar = t.variableDeclaration("let", pattern.elements.map(function (elem, index) { - var accessExpr = t.memberExpression(rest, t.literal(index), true); - return t.variableDeclarator(elem, accessExpr); - })); - node.body.body.unshift(declar); - } - - // check if rest is used in member expressions and optimise for those cases - - var state = { - outerBinding: scope.getBindingIdentifier(rest.name), - canOptimise: true, - candidates: [], - method: node, - name: rest.name - }; - - this.traverse(memberExpressionOptimisationVisitor, state); - - // we only have shorthands and there's no other references - if (state.canOptimise && state.candidates.length) { - for (var candidate of (state.candidates: Array)) { - candidate.replaceWith(argsId); - optimizeMemberExpression(candidate.parent, node.params.length); - } - return; - } - - // - - var start = t.literal(node.params.length); - var key = scope.generateUidIdentifier("key"); - var len = scope.generateUidIdentifier("len"); - - var arrKey = key; - var arrLen = len; - if (node.params.length) { - // 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) - ); - } - - var loop = util.template("rest", { - ARRAY_TYPE: restParam.typeAnnotation, - ARGUMENTS: argsId, - ARRAY_KEY: arrKey, - ARRAY_LEN: arrLen, - START: start, - ARRAY: rest, - KEY: key, - LEN: len - }); - loop._blockHoist = node.params.length + 1; - node.body.body.unshift(loop); } + +export var visitor = { + Function(node, parent, scope) { + if (!hasRest(node)) return; + + var restParam = node.params.pop(); + var rest = restParam.argument; + + var argsId = t.identifier("arguments"); + + // otherwise `arguments` will be remapped in arrow functions + argsId._shadowedFunctionLiteral = true; + + // support patterns + if (t.isPattern(rest)) { + var pattern = rest; + rest = scope.generateUidIdentifier("ref"); + + var declar = t.variableDeclaration("let", pattern.elements.map(function (elem, index) { + var accessExpr = t.memberExpression(rest, t.literal(index), true); + return t.variableDeclarator(elem, accessExpr); + })); + node.body.body.unshift(declar); + } + + // check if rest is used in member expressions and optimise for those cases + + var state = { + outerBinding: scope.getBindingIdentifier(rest.name), + canOptimise: true, + candidates: [], + method: node, + name: rest.name + }; + + this.traverse(memberExpressionOptimisationVisitor, state); + + // we only have shorthands and there's no other references + if (state.canOptimise && state.candidates.length) { + for (var candidate of (state.candidates: Array)) { + candidate.replaceWith(argsId); + optimizeMemberExpression(candidate.parent, node.params.length); + } + return; + } + + // + + var start = t.literal(node.params.length); + var key = scope.generateUidIdentifier("key"); + var len = scope.generateUidIdentifier("len"); + + var arrKey = key; + var arrLen = len; + if (node.params.length) { + // 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) + ); + } + + var loop = util.template("rest", { + ARRAY_TYPE: restParam.typeAnnotation, + ARGUMENTS: argsId, + ARRAY_KEY: arrKey, + ARRAY_LEN: arrLen, + START: start, + ARRAY: rest, + KEY: key, + LEN: len + }); + loop._blockHoist = node.params.length + 1; + node.body.body.unshift(loop); + } +}; diff --git a/src/babel/transformation/transformers/es6/properties.computed.js b/src/babel/transformation/transformers/es6/properties.computed.js index 92b8ff87ec..6ca1e54992 100644 --- a/src/babel/transformation/transformers/es6/properties.computed.js +++ b/src/babel/transformation/transformers/es6/properties.computed.js @@ -63,40 +63,42 @@ function spec(node, body, objId, initProps, file) { } } -export var ObjectExpression = { - exit(node, parent, scope, file) { - var hasComputed = false; +export var visitor = { + ObjectExpression: { + exit(node, parent, scope, file) { + var hasComputed = false; - for (var prop of (node.properties: Array)) { - hasComputed = t.isProperty(prop, { computed: true, kind: "init" }); - if (hasComputed) break; + for (var prop of (node.properties: Array)) { + hasComputed = t.isProperty(prop, { computed: true, kind: "init" }); + if (hasComputed) break; + } + + if (!hasComputed) return; + + var initProps = []; + var objId = scope.generateUidIdentifierBasedOnNode(parent); + + // + + var body = []; + + // + + var callback = spec; + if (file.isLoose("es6.properties.computed")) callback = loose; + + var result = callback(node, body, objId, initProps, file); + if (result) return result; + + // + + body.unshift(t.variableDeclaration("var", [ + t.variableDeclarator(objId, t.objectExpression(initProps)) + ])); + + body.push(t.expressionStatement(objId)); + + return body; } - - if (!hasComputed) return; - - var initProps = []; - var objId = scope.generateUidIdentifierBasedOnNode(parent); - - // - - var body = []; - - // - - var callback = spec; - if (file.isLoose("es6.properties.computed")) callback = loose; - - var result = callback(node, body, objId, initProps, file); - if (result) return result; - - // - - body.unshift(t.variableDeclaration("var", [ - t.variableDeclarator(objId, t.objectExpression(initProps)) - ])); - - body.push(t.expressionStatement(objId)); - - return body; } }; diff --git a/src/babel/transformation/transformers/es6/properties.shorthand.js b/src/babel/transformation/transformers/es6/properties.shorthand.js index e25f43c8ab..57b7210122 100644 --- a/src/babel/transformation/transformers/es6/properties.shorthand.js +++ b/src/babel/transformation/transformers/es6/properties.shorthand.js @@ -1,9 +1,11 @@ -export function Property(node) { - if (node.method) { - node.method = false; - } +export var visitor = { + Property(node) { + if (node.method) { + node.method = false; + } - if (node.shorthand) { - node.shorthand = false; + if (node.shorthand) { + node.shorthand = false; + } } -} +}; diff --git a/src/babel/transformation/transformers/es6/regex.sticky.js b/src/babel/transformation/transformers/es6/regex.sticky.js index e298e0501b..427b4c76a0 100644 --- a/src/babel/transformation/transformers/es6/regex.sticky.js +++ b/src/babel/transformation/transformers/es6/regex.sticky.js @@ -1,10 +1,12 @@ import * as regex from "../../helpers/regex"; import * as t from "../../../types"; -export function Literal(node) { - if (!regex.is(node, "y")) return; - return t.newExpression(t.identifier("RegExp"), [ - t.literal(node.regex.pattern), - t.literal(node.regex.flags) - ]); -} +export var visitor = { + Literal(node) { + if (!regex.is(node, "y")) return; + return t.newExpression(t.identifier("RegExp"), [ + t.literal(node.regex.pattern), + t.literal(node.regex.flags) + ]); + } +}; diff --git a/src/babel/transformation/transformers/es6/regex.unicode.js b/src/babel/transformation/transformers/es6/regex.unicode.js index a914f2b529..0b26277bb9 100644 --- a/src/babel/transformation/transformers/es6/regex.unicode.js +++ b/src/babel/transformation/transformers/es6/regex.unicode.js @@ -1,8 +1,10 @@ import rewritePattern from "regexpu/rewrite-pattern"; import * as regex from "../../helpers/regex"; -export function Literal(node) { - if (!regex.is(node, "u")) return; - node.regex.pattern = rewritePattern(node.regex.pattern, node.regex.flags); - regex.pullFlag(node, "u"); -} +export var visitor = { + Literal(node) { + if (!regex.is(node, "u")) return; + node.regex.pattern = rewritePattern(node.regex.pattern, node.regex.flags); + regex.pullFlag(node, "u"); + } +}; diff --git a/src/babel/transformation/transformers/es6/spec.block-scoping.js b/src/babel/transformation/transformers/es6/spec.block-scoping.js index 348e8e1e11..337757bc64 100644 --- a/src/babel/transformation/transformers/es6/spec.block-scoping.js +++ b/src/babel/transformation/transformers/es6/spec.block-scoping.js @@ -15,7 +15,7 @@ function references(node, scope, state) { return scope.getBindingIdentifier(node.name) === declared; } -var visitor = { +var refVisitor = { ReferencedIdentifier(node, parent, scope, state) { if (t.isFor(parent) && parent.left === node) return; @@ -62,16 +62,16 @@ export var metadata = { group: "builtin-advanced" }; -export var BlockStatement = { - exit(node, parent, scope, file) { - var letRefs = node._letReferences; - if (!letRefs) return; +export var visitor = { + "Program|Loop|BlockStatement": { + exit(node, parent, scope, file) { + var letRefs = node._letReferences; + if (!letRefs) return; - this.traverse(visitor, { - letRefs: letRefs, - file: file - }); + this.traverse(refVisitor, { + letRefs: letRefs, + file: file + }); + } } }; - -export { BlockStatement as Program, BlockStatement as Loop }; diff --git a/src/babel/transformation/transformers/es6/spec.symbols.js b/src/babel/transformation/transformers/es6/spec.symbols.js index 953b29a18f..8b228cd588 100644 --- a/src/babel/transformation/transformers/es6/spec.symbols.js +++ b/src/babel/transformation/transformers/es6/spec.symbols.js @@ -4,34 +4,34 @@ export var metadata = { optional: true }; -export function UnaryExpression(node, parent, scope, file) { - if (node._ignoreSpecSymbols) return; +export var visitor = { + UnaryExpression(node, parent, scope, file) { + if (node._ignoreSpecSymbols) return; - if (node.operator === "typeof") { - var call = t.callExpression(file.addHelper("typeof"), [node.argument]); - if (this.get("argument").isIdentifier()) { - var undefLiteral = t.literal("undefined"); - var unary = t.unaryExpression("typeof", node.argument); - unary._ignoreSpecSymbols = true; - return t.conditionalExpression( - t.binaryExpression("===", unary, undefLiteral), - undefLiteral, - call - ); - } else { - return call; + if (node.operator === "typeof") { + var call = t.callExpression(file.addHelper("typeof"), [node.argument]); + if (this.get("argument").isIdentifier()) { + var undefLiteral = t.literal("undefined"); + var unary = t.unaryExpression("typeof", node.argument); + unary._ignoreSpecSymbols = true; + return t.conditionalExpression( + t.binaryExpression("===", unary, undefLiteral), + undefLiteral, + call + ); + } else { + return call; + } } + }, + + BinaryExpression(node, parent, scope, file) { + if (node.operator === "instanceof") { + return t.callExpression(file.addHelper("instanceof"), [node.left, node.right]); + } + }, + + "VariableDeclaration|FunctionDeclaration"(node) { + if (node._generated) this.skip(); } -} - -export function BinaryExpression(node, parent, scope, file) { - if (node.operator === "instanceof") { - return t.callExpression(file.addHelper("instanceof"), [node.left, node.right]); - } -} - -export function VariableDeclaration(node) { - if (node._generated) this.skip(); -} - -export { VariableDeclaration as FunctionDeclaration }; +}; diff --git a/src/babel/transformation/transformers/es6/spec.template-literals.js b/src/babel/transformation/transformers/es6/spec.template-literals.js index 3a58c5a294..33f10ffa87 100644 --- a/src/babel/transformation/transformers/es6/spec.template-literals.js +++ b/src/babel/transformation/transformers/es6/spec.template-literals.js @@ -5,10 +5,12 @@ export var metadata = { group: "builtin-pre" }; -export function TemplateLiteral(node, parent, scope, file) { - if (t.isTaggedTemplateExpression(parent)) return; +export var visitor = { + TemplateLiteral(node, parent) { + if (t.isTaggedTemplateExpression(parent)) return; - for (var i = 0; i < node.expressions.length; i++) { - node.expressions[i] = t.callExpression(t.identifier("String"), [node.expressions[i]]); + for (var i = 0; i < node.expressions.length; i++) { + node.expressions[i] = t.callExpression(t.identifier("String"), [node.expressions[i]]); + } } -} +}; diff --git a/src/babel/transformation/transformers/es6/spread.js b/src/babel/transformation/transformers/es6/spread.js index a6ae3a05cc..6b7c456b17 100644 --- a/src/babel/transformation/transformers/es6/spread.js +++ b/src/babel/transformation/transformers/es6/spread.js @@ -43,76 +43,78 @@ function build(props, scope) { return nodes; } -export function ArrayExpression(node, parent, scope) { - var elements = node.elements; - if (!hasSpread(elements)) return; +export var visitor = { + ArrayExpression(node, parent, scope) { + var elements = node.elements; + if (!hasSpread(elements)) return; - var nodes = build(elements, scope); - var first = nodes.shift(); + var nodes = build(elements, scope); + var first = nodes.shift(); - if (!t.isArrayExpression(first)) { - nodes.unshift(first); - first = t.arrayExpression([]); - } - - return t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes); -} - -export function CallExpression(node, parent, scope) { - var args = node.arguments; - if (!hasSpread(args)) return; - - var contextLiteral = t.identifier("undefined"); - - node.arguments = []; - - var nodes; - if (args.length === 1 && args[0].argument.name === "arguments") { - nodes = [args[0].argument]; - } else { - nodes = build(args, scope); - } - - var first = nodes.shift(); - if (nodes.length) { - node.arguments.push(t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes)); - } else { - node.arguments.push(first); - } - - var callee = node.callee; - - if (this.get("callee").isMemberExpression()) { - var temp = scope.maybeGenerateMemoised(callee.object); - if (temp) { - callee.object = t.assignmentExpression("=", temp, callee.object); - contextLiteral = temp; - } else { - contextLiteral = callee.object; + if (!t.isArrayExpression(first)) { + nodes.unshift(first); + first = t.arrayExpression([]); } - t.appendToMemberExpression(callee, t.identifier("apply")); - } else { - node.callee = t.memberExpression(node.callee, t.identifier("apply")); + + return t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes); + }, + + CallExpression(node, parent, scope) { + var args = node.arguments; + if (!hasSpread(args)) return; + + var contextLiteral = t.identifier("undefined"); + + node.arguments = []; + + var nodes; + if (args.length === 1 && args[0].argument.name === "arguments") { + nodes = [args[0].argument]; + } else { + nodes = build(args, scope); + } + + var first = nodes.shift(); + if (nodes.length) { + node.arguments.push(t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes)); + } else { + node.arguments.push(first); + } + + var callee = node.callee; + + if (this.get("callee").isMemberExpression()) { + var temp = scope.maybeGenerateMemoised(callee.object); + if (temp) { + callee.object = t.assignmentExpression("=", temp, callee.object); + contextLiteral = temp; + } else { + contextLiteral = callee.object; + } + t.appendToMemberExpression(callee, t.identifier("apply")); + } else { + node.callee = t.memberExpression(node.callee, t.identifier("apply")); + } + + node.arguments.unshift(contextLiteral); + }, + + NewExpression(node, parent, scope, file) { + var args = node.arguments; + if (!hasSpread(args)) return; + + var nodes = build(args, scope); + + var context = t.arrayExpression([t.literal(null)]); + + args = t.callExpression(t.memberExpression(context, t.identifier("concat")), nodes); + + return t.newExpression( + t.callExpression( + t.memberExpression(file.addHelper("bind"), t.identifier("apply")), + [node.callee, args] + ), + [] + ); } - - node.arguments.unshift(contextLiteral); -} - -export function NewExpression(node, parent, scope, file) { - var args = node.arguments; - if (!hasSpread(args)) return; - - var nodes = build(args, scope); - - var context = t.arrayExpression([t.literal(null)]); - - args = t.callExpression(t.memberExpression(context, t.identifier("concat")), nodes); - - return t.newExpression( - t.callExpression( - t.memberExpression(file.addHelper("bind"), t.identifier("apply")), - [node.callee, args] - ), - [] - ); -} +}; diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 78eb809380..908daffe7c 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -9,11 +9,13 @@ export var metadata = { group: "builtin-trailing" }; -export function Func/*tion*/(node, parent, scope, file) { - if (node.generator || node.async) return; - var tailCall = new TailCallTransformer(this, scope, file); - tailCall.run(); -} +export var visitor = { + Function(node, parent, scope, file) { + if (node.generator || node.async) return; + var tailCall = new TailCallTransformer(this, scope, file); + tailCall.run(); + } +}; function returnBlock(expr) { return t.blockStatement([t.returnStatement(expr)]); diff --git a/src/babel/transformation/transformers/es6/template-literals.js b/src/babel/transformation/transformers/es6/template-literals.js index 5c770f7216..9facba8537 100644 --- a/src/babel/transformation/transformers/es6/template-literals.js +++ b/src/babel/transformation/transformers/es6/template-literals.js @@ -6,6 +6,10 @@ export var metadata = { group: "builtin-pre" }; +function isString(node) { + return t.isLiteral(node) && typeof node.value === "string"; +} + function buildBinaryExpression(left, right) { var node = t.binaryExpression("+", left, right); node._templateLiteralProduced = true; @@ -21,67 +25,65 @@ function crawl(path) { } } -export function TaggedTemplateExpression(node, parent, scope, file) { - var quasi = node.quasi; - var args = []; +export var visitor = { + TaggedTemplateExpression(node, parent, scope, file) { + var quasi = node.quasi; + var args = []; - var strings = []; - var raw = []; + var strings = []; + var raw = []; - for (var elem of (quasi.quasis: Array)) { - strings.push(t.literal(elem.value.cooked)); - raw.push(t.literal(elem.value.raw)); - } + for (var elem of (quasi.quasis: Array)) { + strings.push(t.literal(elem.value.cooked)); + raw.push(t.literal(elem.value.raw)); + } - strings = t.arrayExpression(strings); - raw = t.arrayExpression(raw); + strings = t.arrayExpression(strings); + raw = t.arrayExpression(raw); - var templateName = "tagged-template-literal"; - if (file.isLoose("es6.templateLiterals")) templateName += "-loose"; - args.push(t.callExpression(file.addHelper(templateName), [strings, raw])); + var templateName = "tagged-template-literal"; + if (file.isLoose("es6.templateLiterals")) templateName += "-loose"; + args.push(t.callExpression(file.addHelper(templateName), [strings, raw])); - args = args.concat(quasi.expressions); + args = args.concat(quasi.expressions); - return t.callExpression(node.tag, args); -} + return t.callExpression(node.tag, args); + }, -function isString(node) { - return t.isLiteral(node) && typeof node.value === "string"; -} + TemplateLiteral(node, parent, scope, file) { + var nodes = []; -export function TemplateLiteral(node, parent, scope, file) { - var nodes = []; + for (let elem of (node.quasis: Array)) { + nodes.push(t.literal(elem.value.cooked)); - for (let elem of (node.quasis: Array)) { - nodes.push(t.literal(elem.value.cooked)); + var expr = node.expressions.shift(); + if (expr) nodes.push(expr); + } - var expr = node.expressions.shift(); - if (expr) nodes.push(expr); - } + if (nodes.length > 1) { + // filter out empty string literals + nodes = nodes.filter(n => !t.isLiteral(n, { value: "" })); - if (nodes.length > 1) { - // filter out empty string literals - nodes = nodes.filter(n => !t.isLiteral(n, { value: "" })); + if (nodes.length === 1 && isString(nodes[0])) { + return nodes[0]; + } - if (nodes.length === 1 && isString(nodes[0])) { + // since `+` is left-to-right associative + // ensure the first node is a string if first/second isn't + if (!isString(nodes[0]) && !isString(nodes[1])) { + nodes.unshift(t.literal("")); + } + + var root = buildBinaryExpression(nodes.shift(), nodes.shift()); + + for (let node of (nodes: Array)) { + root = buildBinaryExpression(root, node); + } + + this.replaceWith(root); + //crawl(this); + } else { return nodes[0]; } - - // since `+` is left-to-right associative - // ensure the first node is a string if first/second isn't - if (!isString(nodes[0]) && !isString(nodes[1])) { - nodes.unshift(t.literal("")); - } - - var root = buildBinaryExpression(nodes.shift(), nodes.shift()); - - for (let node of (nodes: Array)) { - root = buildBinaryExpression(root, node); - } - - this.replaceWith(root); - //crawl(this); - } else { - return nodes[0]; } -} +}; diff --git a/src/babel/transformation/transformers/es7/comprehensions.js b/src/babel/transformation/transformers/es7/comprehensions.js index 65aac03875..4807341c1a 100644 --- a/src/babel/transformation/transformers/es7/comprehensions.js +++ b/src/babel/transformation/transformers/es7/comprehensions.js @@ -7,11 +7,13 @@ export var metadata = { stage: 0 }; -export function ComprehensionExpression(node, parent, scope, file) { - var callback = array; - if (node.generator) callback = generator; - return callback(node, parent, scope); -} +export var visitor = { + ComprehensionExpression(node, parent, scope) { + var callback = array; + if (node.generator) callback = generator; + return callback(node, parent, scope); + } +}; function generator(node) { var body = []; diff --git a/src/babel/transformation/transformers/es7/decorators.js b/src/babel/transformation/transformers/es7/decorators.js index d875317c56..2e56c01e32 100644 --- a/src/babel/transformation/transformers/es7/decorators.js +++ b/src/babel/transformation/transformers/es7/decorators.js @@ -8,35 +8,37 @@ export var metadata = { stage: 1 }; -export function ObjectExpression(node, parent, scope, file) { - var hasDecorators = false; - for (let i = 0; i < node.properties.length; i++) { - let prop = node.properties[i]; - if (prop.decorators) { - hasDecorators = true; - break; +export var visitor = { + ObjectExpression(node, parent, scope, file) { + var hasDecorators = false; + for (let i = 0; i < node.properties.length; i++) { + let prop = node.properties[i]; + if (prop.decorators) { + hasDecorators = true; + break; + } } - } - if (!hasDecorators) return; + if (!hasDecorators) return; - var mutatorMap = {}; + var mutatorMap = {}; - for (let i = 0; i < node.properties.length; i++) { - let prop = node.properties[i]; - if (prop.decorators) memoiseDecorators(prop.decorators, scope); + for (let i = 0; i < node.properties.length; i++) { + let prop = node.properties[i]; + if (prop.decorators) memoiseDecorators(prop.decorators, scope); - if (prop.kind === "init" && !prop.method) { - prop.kind = ""; - prop.value = t.functionExpression(null, [], t.blockStatement([ - t.returnStatement(prop.value) - ])); + if (prop.kind === "init" && !prop.method) { + prop.kind = ""; + prop.value = t.functionExpression(null, [], t.blockStatement([ + t.returnStatement(prop.value) + ])); + } + + defineMap.push(mutatorMap, prop, "initializer", file); } - defineMap.push(mutatorMap, prop, "initializer", file); + var obj = defineMap.toClassObject(mutatorMap); + obj = defineMap.toComputedObjectFromClass(obj); + return t.callExpression(file.addHelper("create-decorated-object"), [obj]); } - - var obj = defineMap.toClassObject(mutatorMap); - obj = defineMap.toComputedObjectFromClass(obj); - return t.callExpression(file.addHelper("create-decorated-object"), [obj]); -} +}; diff --git a/src/babel/transformation/transformers/es7/do-expressions.js b/src/babel/transformation/transformers/es7/do-expressions.js index efefe06ba2..7f0fae85b7 100644 --- a/src/babel/transformation/transformers/es7/do-expressions.js +++ b/src/babel/transformation/transformers/es7/do-expressions.js @@ -5,11 +5,13 @@ export var metadata = { stage: 0 }; -export function DoExpression(node) { - var body = node.body.body; - if (body.length) { - return body; - } else { - return t.identifier("undefined"); +export var visitor = { + DoExpression(node) { + var body = node.body.body; + if (body.length) { + return body; + } else { + return t.identifier("undefined"); + } } -} +}; diff --git a/src/babel/transformation/transformers/es7/exponentiation-operator.js b/src/babel/transformation/transformers/es7/exponentiation-operator.js index 13081f5c4a..1f1ad5d62c 100644 --- a/src/babel/transformation/transformers/es7/exponentiation-operator.js +++ b/src/babel/transformation/transformers/es7/exponentiation-operator.js @@ -9,20 +9,10 @@ export var metadata = { var MATH_POW = t.memberExpression(t.identifier("Math"), t.identifier("pow")); -var { - ExpressionStatement, - AssignmentExpression, - BinaryExpression -} = build({ +export var visitor = build({ operator: "**", build(left, right) { return t.callExpression(MATH_POW, [left, right]); } }); - -export { - ExpressionStatement, - AssignmentExpression, - BinaryExpression -}; diff --git a/src/babel/transformation/transformers/es7/export-extensions.js b/src/babel/transformation/transformers/es7/export-extensions.js index 77880f3a10..204aaad140 100644 --- a/src/babel/transformation/transformers/es7/export-extensions.js +++ b/src/babel/transformation/transformers/es7/export-extensions.js @@ -26,14 +26,16 @@ function build(node, nodes, scope) { build(node, nodes, scope); } -export function ExportNamedDeclaration(node, parent, scope) { - var nodes = []; - build(node, nodes, scope); - if (!nodes.length) return; +export var visitor = { + ExportNamedDeclaration(node, parent, scope) { + var nodes = []; + build(node, nodes, scope); + if (!nodes.length) return; - if (node.specifiers.length >= 1) { - nodes.push(node); + if (node.specifiers.length >= 1) { + nodes.push(node); + } + + return nodes; } - - return nodes; -} +}; diff --git a/src/babel/transformation/transformers/es7/function-bind.js b/src/babel/transformation/transformers/es7/function-bind.js index 7bd6f58a46..8c7729e03f 100644 --- a/src/babel/transformation/transformers/es7/function-bind.js +++ b/src/babel/transformation/transformers/es7/function-bind.js @@ -36,16 +36,18 @@ function inferBindContext(bind, scope) { return tempId; } -export function CallExpression(node, parent, scope, file) { - var bind = node.callee; - if (!t.isBindExpression(bind)) return; +export var visitor = { + CallExpression(node, parent, scope) { + var bind = node.callee; + if (!t.isBindExpression(bind)) return; - var context = inferBindContext(bind, scope); - node.callee = t.memberExpression(bind.callee, t.identifier("call")); - node.arguments.unshift(context); -} + var context = inferBindContext(bind, scope); + node.callee = t.memberExpression(bind.callee, t.identifier("call")); + node.arguments.unshift(context); + }, -export function BindExpression(node, parent, scope, file) { - var context = inferBindContext(node, scope); - return t.callExpression(t.memberExpression(node.callee, t.identifier("bind")), [context]); -} + BindExpression(node, parent, scope) { + var context = inferBindContext(node, scope); + return t.callExpression(t.memberExpression(node.callee, t.identifier("bind")), [context]); + } +}; diff --git a/src/babel/transformation/transformers/es7/object-rest-spread.js b/src/babel/transformation/transformers/es7/object-rest-spread.js index 7b9f448f9c..07e8f6d4b1 100644 --- a/src/babel/transformation/transformers/es7/object-rest-spread.js +++ b/src/babel/transformation/transformers/es7/object-rest-spread.js @@ -16,33 +16,35 @@ var hasSpread = function (node) { return false; }; -export function ObjectExpression(node, parent, scope, file) { - if (!hasSpread(node)) return; +export var visitor = { + ObjectExpression(node, parent, scope, file) { + if (!hasSpread(node)) return; - var args = []; - var props = []; + var args = []; + var props = []; - var push = function () { - if (!props.length) return; - args.push(t.objectExpression(props)); - props = []; - }; + var push = function () { + if (!props.length) return; + args.push(t.objectExpression(props)); + props = []; + }; - for (var i = 0; i < node.properties.length; i++) { - var prop = node.properties[i]; - if (t.isSpreadProperty(prop)) { - push(); - args.push(prop.argument); - } else { - props.push(prop); + for (var i = 0; i < node.properties.length; i++) { + var prop = node.properties[i]; + if (t.isSpreadProperty(prop)) { + push(); + args.push(prop.argument); + } else { + props.push(prop); + } } + + push(); + + if (!t.isObjectExpression(args[0])) { + args.unshift(t.objectExpression([])); + } + + return t.callExpression(file.addHelper("extends"), args); } - - push(); - - if (!t.isObjectExpression(args[0])) { - args.unshift(t.objectExpression([])); - } - - return t.callExpression(file.addHelper("extends"), args); -} +}; diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 903bd084b1..6c7fa65ae4 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -53,7 +53,7 @@ export default { "es7.doExpressions": require("./es7/do-expressions"), "es6.spec.symbols": require("./es6/spec.symbols"), "es7.functionBind": require("./es7/function-bind"), - "spec.undefinedToVoid": require("./spec/undefined-to-void"), + "spec.undefinedToVoid": require("babel-plugin-undefined-to-void"), //- builtin-advanced "es6.destructuring": require("./es6/destructuring"), diff --git a/src/babel/transformation/transformers/internal/block-hoist.js b/src/babel/transformation/transformers/internal/block-hoist.js index 934d21b1e2..9c3802fa8b 100644 --- a/src/babel/transformation/transformers/internal/block-hoist.js +++ b/src/babel/transformation/transformers/internal/block-hoist.js @@ -11,24 +11,24 @@ export var metadata = { // - 2 Priority over normal nodes // - 3 We want this to be at the **very** top -export var BlockStatement = { - exit(node) { - var hasChange = false; - for (var i = 0; i < node.body.length; i++) { - var bodyNode = node.body[i]; - if (bodyNode && bodyNode._blockHoist != null) hasChange = true; +export var visitor = { + Block: { + exit(node) { + var hasChange = false; + for (var i = 0; i < node.body.length; i++) { + var bodyNode = node.body[i]; + if (bodyNode && bodyNode._blockHoist != null) hasChange = true; + } + if (!hasChange) return; + + node.body = sortBy(node.body, function(bodyNode){ + var priority = bodyNode && bodyNode._blockHoist; + if (priority == null) priority = 1; + if (priority === true) priority = 2; + + // Higher priorities should move toward the top. + return -1 * priority; + }); } - if (!hasChange) return; - - node.body = sortBy(node.body, function(bodyNode){ - var priority = bodyNode && bodyNode._blockHoist; - if (priority == null) priority = 1; - if (priority === true) priority = 2; - - // Higher priorities should move toward the top. - return -1 * priority; - }); } }; - -export { BlockStatement as Program }; diff --git a/src/babel/transformation/transformers/internal/explode.js b/src/babel/transformation/transformers/internal/explode.js index 10a8091fdc..4bb6040a5d 100644 --- a/src/babel/transformation/transformers/internal/explode.js +++ b/src/babel/transformation/transformers/internal/explode.js @@ -25,9 +25,10 @@ function buildListClone(listKey, bindingKey, refKey) { }; } -export var Property = buildClone("value", "key", function (node) { - return t.isAssignmentPattern(node.value) && node.value.left === node.key; -}); - -export var ExportDeclaration = buildListClone("specifiers", "local", "exported"); -export var ImportDeclaration = buildListClone("specifiers", "local", "imported"); +export var visitor = { + Property: buildClone("value", "key", function (node) { + return t.isAssignmentPattern(node.value) && node.value.left === node.key; + }), + ExportDeclaration: buildListClone("specifiers", "local", "exported"), + ImportDeclaration: buildListClone("specifiers", "local", "imported") +}; diff --git a/src/babel/transformation/transformers/internal/hoist-directives.js b/src/babel/transformation/transformers/internal/hoist-directives.js index 939b23c00c..4a5cdaa23f 100644 --- a/src/babel/transformation/transformers/internal/hoist-directives.js +++ b/src/babel/transformation/transformers/internal/hoist-directives.js @@ -4,17 +4,17 @@ export var metadata = { group: "builtin-pre" }; -export var BlockStatement = { - exit(node) { - for (var i = 0; i < node.body.length; i++) { - var bodyNode = node.body[i]; - if (t.isExpressionStatement(bodyNode) && t.isLiteral(bodyNode.expression)) { - bodyNode._blockHoist = Infinity; - } else { - return; +export var visitor = { + Block: { + exit(node) { + for (var i = 0; i < node.body.length; i++) { + var bodyNode = node.body[i]; + if (t.isExpressionStatement(bodyNode) && t.isLiteral(bodyNode.expression)) { + bodyNode._blockHoist = Infinity; + } else { + return; + } } } } }; - -export { BlockStatement as Program }; diff --git a/src/babel/transformation/transformers/internal/module-formatter.js b/src/babel/transformation/transformers/internal/module-formatter.js index 7e1a2f2b86..d767376582 100644 --- a/src/babel/transformation/transformers/internal/module-formatter.js +++ b/src/babel/transformation/transformers/internal/module-formatter.js @@ -2,19 +2,21 @@ export var metadata = { group: "builtin-modules" }; -export var Program = { - exit(program, parent, scope, file) { - // ensure that these are at the top, just like normal imports - for (var node of (file.dynamicImports: Array)) { - node._blockHoist = 3; - } +export var visitor = { + Program: { + exit(program, parent, scope, file) { + // ensure that these are at the top, just like normal imports + for (var node of (file.dynamicImports: Array)) { + node._blockHoist = 3; + } - program.body = file.dynamicImports.concat(program.body); + program.body = file.dynamicImports.concat(program.body); - if (!file.transformers["es6.modules"].canTransform()) return; + if (!file.transformers["es6.modules"].canTransform()) return; - if (file.moduleFormatter.transform) { - file.moduleFormatter.transform(program); + if (file.moduleFormatter.transform) { + file.moduleFormatter.transform(program); + } } } }; diff --git a/src/babel/transformation/transformers/internal/modules.js b/src/babel/transformation/transformers/internal/modules.js index ab1e9fe699..bcaaff3f1a 100644 --- a/src/babel/transformation/transformers/internal/modules.js +++ b/src/babel/transformation/transformers/internal/modules.js @@ -15,93 +15,95 @@ function getDeclar(node) { return declar; } -export var metadata = { - group: "builtin-pre" -}; - -export function ExportDefaultDeclaration(node, parent, scope) { - var declar = node.declaration; - - if (t.isClassDeclaration(declar)) { - // export default class Foo {}; - let nodes = [getDeclar(node), node]; - node.declaration = declar.id; - return nodes; - } else if (t.isClassExpression(declar)) { - // export default class {}; - var temp = scope.generateUidIdentifier("default"); - node.declaration = t.variableDeclaration("var", [ - t.variableDeclarator(temp, declar) - ]); - - let nodes = [getDeclar(node), node]; - node.declaration = temp; - return nodes; - } else if (t.isFunctionDeclaration(declar)) { - // export default function Foo() {} - node._blockHoist = 2; - - let nodes = [getDeclar(node), node]; - node.declaration = declar.id; - return nodes; - } -} - function buildExportSpecifier(id) { return t.exportSpecifier(clone(id), clone(id)); } -export function ExportNamedDeclaration(node, parent, scope) { - var declar = node.declaration; +export var metadata = { + group: "builtin-pre" +}; - if (t.isClassDeclaration(declar)) { - // export class Foo {} - node.specifiers = [buildExportSpecifier(declar.id)]; +export var visitor = { + ExportDefaultDeclaration(node, parent, scope) { + var declar = node.declaration; - let nodes = [getDeclar(node), node]; - node.declaration = null; - return nodes; - } else if (t.isFunctionDeclaration(declar)) { - // export function Foo() {} - node.specifiers = [buildExportSpecifier(declar.id)]; - node._blockHoist = 2; + if (t.isClassDeclaration(declar)) { + // export default class Foo {}; + let nodes = [getDeclar(node), node]; + node.declaration = declar.id; + return nodes; + } else if (t.isClassExpression(declar)) { + // export default class {}; + var temp = scope.generateUidIdentifier("default"); + node.declaration = t.variableDeclaration("var", [ + t.variableDeclarator(temp, declar) + ]); - let nodes = [getDeclar(node), node]; - node.declaration = null; - return nodes; - } else if (t.isVariableDeclaration(declar)) { - // export var foo = "bar"; - var specifiers = []; - var bindings = this.get("declaration").getBindingIdentifiers(); - for (var key in bindings) { - specifiers.push(buildExportSpecifier(bindings[key])); + let nodes = [getDeclar(node), node]; + node.declaration = temp; + return nodes; + } else if (t.isFunctionDeclaration(declar)) { + // export default function Foo() {} + node._blockHoist = 2; + + let nodes = [getDeclar(node), node]; + node.declaration = declar.id; + return nodes; } - return [declar, t.exportNamedDeclaration(null, specifiers)]; - } -} - -export var Program = { - enter(node) { - var imports = []; - var rest = []; - - for (var i = 0; i < node.body.length; i++) { - var bodyNode = node.body[i]; - if (t.isImportDeclaration(bodyNode)) { - imports.push(bodyNode); - } else { - rest.push(bodyNode); - } - } - - node.body = imports.concat(rest); }, - exit(node, parent, scope, file) { - if (!file.transformers["es6.modules"].canTransform()) return; + ExportNamedDeclaration(node) { + var declar = node.declaration; - if (file.moduleFormatter.setup) { - file.moduleFormatter.setup(); + if (t.isClassDeclaration(declar)) { + // export class Foo {} + node.specifiers = [buildExportSpecifier(declar.id)]; + + let nodes = [getDeclar(node), node]; + node.declaration = null; + return nodes; + } else if (t.isFunctionDeclaration(declar)) { + // export function Foo() {} + node.specifiers = [buildExportSpecifier(declar.id)]; + node._blockHoist = 2; + + let nodes = [getDeclar(node), node]; + node.declaration = null; + return nodes; + } else if (t.isVariableDeclaration(declar)) { + // export var foo = "bar"; + var specifiers = []; + var bindings = this.get("declaration").getBindingIdentifiers(); + for (var key in bindings) { + specifiers.push(buildExportSpecifier(bindings[key])); + } + return [declar, t.exportNamedDeclaration(null, specifiers)]; + } + }, + + Program: { + enter(node) { + var imports = []; + var rest = []; + + for (var i = 0; i < node.body.length; i++) { + var bodyNode = node.body[i]; + if (t.isImportDeclaration(bodyNode)) { + imports.push(bodyNode); + } else { + rest.push(bodyNode); + } + } + + node.body = imports.concat(rest); + }, + + exit(node, parent, scope, file) { + if (!file.transformers["es6.modules"].canTransform()) return; + + if (file.moduleFormatter.setup) { + file.moduleFormatter.setup(); + } } } }; diff --git a/src/babel/transformation/transformers/internal/shadow-functions.js b/src/babel/transformation/transformers/internal/shadow-functions.js index c01469eb84..2c8fc030f3 100644 --- a/src/babel/transformation/transformers/internal/shadow-functions.js +++ b/src/babel/transformation/transformers/internal/shadow-functions.js @@ -22,12 +22,14 @@ function remap(path, key, create) { return id; } -export function ThisExpression() { - return remap(this, "this", () => t.thisExpression()); -} +export var visitor = { + ThisExpression() { + return remap(this, "this", () => t.thisExpression()); + }, -export function ReferencedIdentifier(node) { - if (node.name === "arguments" && !node._shadowedFunctionLiteral) { - return remap(this, "arguments", () => t.identifier("arguments")); + ReferencedIdentifier(node) { + if (node.name === "arguments" && !node._shadowedFunctionLiteral) { + return remap(this, "arguments", () => t.identifier("arguments")); + } } -} +}; diff --git a/src/babel/transformation/transformers/internal/validation.js b/src/babel/transformation/transformers/internal/validation.js index 0d974fe952..e09092209a 100644 --- a/src/babel/transformation/transformers/internal/validation.js +++ b/src/babel/transformation/transformers/internal/validation.js @@ -5,41 +5,41 @@ export var metadata = { group: "builtin-pre" }; -export function ForOfStatement(node, parent, scope, file) { - var left = node.left; - if (t.isVariableDeclaration(left)) { - var declar = left.declarations[0]; - if (declar.init) throw file.errorWithNode(declar, messages.get("noAssignmentsInForHead")); - } -} - -export { ForOfStatement as ForInStatement }; - -export function MethodDefinition(node) { - if (node.kind !== "constructor") { - // get constructor() {} - var isConstructor = !node.computed && t.isIdentifier(node.key, { name: "constructor" }); - - // get ["constructor"]() {} - isConstructor = isConstructor || t.isLiteral(node.key, { value: "constructor" }); - - if (isConstructor) { - throw this.errorWithNode(messages.get("classesIllegalConstructorKind")); +export var visitor = { + ForXStatement(node, parent, scope, file) { + var left = node.left; + if (t.isVariableDeclaration(left)) { + var declar = left.declarations[0]; + if (declar.init) throw file.errorWithNode(declar, messages.get("noAssignmentsInForHead")); } - } + }, - Property.apply(this, arguments); -} + MethodDefinition(node) { + if (node.kind !== "constructor") { + // get constructor() {} + var isConstructor = !node.computed && t.isIdentifier(node.key, { name: "constructor" }); -export function Property(node, parent, scope, file) { - if (node.kind === "set") { - if (node.value.params.length !== 1) { - throw file.errorWithNode(node.value, messages.get("settersInvalidParamLength")); + // get ["constructor"]() {} + isConstructor = isConstructor || t.isLiteral(node.key, { value: "constructor" }); + + if (isConstructor) { + throw this.errorWithNode(messages.get("classesIllegalConstructorKind")); + } } - var first = node.value.params[0]; - if (t.isRestElement(first)) { - throw file.errorWithNode(first, messages.get("settersNoRest")); + visitor.Property.apply(this, arguments); + }, + + Property(node, parent, scope, file) { + if (node.kind === "set") { + if (node.value.params.length !== 1) { + throw file.errorWithNode(node.value, messages.get("settersInvalidParamLength")); + } + + var first = node.value.params[0]; + if (t.isRestElement(first)) { + throw file.errorWithNode(first, messages.get("settersNoRest")); + } } } -} +}; diff --git a/src/babel/transformation/transformers/minification/constant-folding.js b/src/babel/transformation/transformers/minification/constant-folding.js index 480a18814e..c3d383529b 100644 --- a/src/babel/transformation/transformers/minification/constant-folding.js +++ b/src/babel/transformation/transformers/minification/constant-folding.js @@ -6,66 +6,68 @@ export var metadata = { experimental: true }; -export function AssignmentExpression() { - var left = this.get("left"); - if (!left.isIdentifier()) return; +export var visitor = { + AssignmentExpression() { + var left = this.get("left"); + if (!left.isIdentifier()) return; - var binding = this.scope.getBinding(left.node.name); - if (!binding || binding.hasDeoptValue) return; + var binding = this.scope.getBinding(left.node.name); + if (!binding || binding.hasDeoptValue) return; - var evaluated = this.get("right").evaluate(); - if (evaluated.confident) { - binding.setValue(evaluated.value); - } else { - binding.deoptValue(); - } -} - -export function IfStatement() { - var evaluated = this.get("test").evaluate(); - if (!evaluated.confident) { - // todo: deopt binding values for constant violations inside - return this.skip(); - } - - if (evaluated.value) { - this.skipKey("alternate"); - } else { - this.skipKey("consequent"); - } -} - -export var Scopable = { - enter() { - var funcScope = this.scope.getFunctionParent(); - - for (var name in this.scope.bindings) { - var binding = this.scope.bindings[name]; - var deopt = false; - - for (var path of (binding.constantViolations: Array)) { - var funcViolationScope = path.scope.getFunctionParent(); - if (funcViolationScope !== funcScope) { - deopt = true; - break; - } - } - - if (deopt) binding.deoptValue(); + var evaluated = this.get("right").evaluate(); + if (evaluated.confident) { + binding.setValue(evaluated.value); + } else { + binding.deoptValue(); } }, - exit() { - for (var name in this.scope.bindings) { - var binding = this.scope.bindings[name]; - binding.clearValue(); + IfStatement() { + var evaluated = this.get("test").evaluate(); + if (!evaluated.confident) { + // todo: deopt binding values for constant violations inside + return this.skip(); + } + + if (evaluated.value) { + this.skipKey("alternate"); + } else { + this.skipKey("consequent"); + } + }, + + Scopable: { + enter() { + var funcScope = this.scope.getFunctionParent(); + + for (var name in this.scope.bindings) { + var binding = this.scope.bindings[name]; + var deopt = false; + + for (var path of (binding.constantViolations: Array)) { + var funcViolationScope = path.scope.getFunctionParent(); + if (funcViolationScope !== funcScope) { + deopt = true; + break; + } + } + + if (deopt) binding.deoptValue(); + } + }, + + exit() { + for (var name in this.scope.bindings) { + var binding = this.scope.bindings[name]; + binding.clearValue(); + } + } + }, + + Expression: { + exit() { + var res = this.evaluate(); + if (res.confident) return t.valueToNode(res.value); } } }; - -export var Expression = { - exit() { - var res = this.evaluate(); - if (res.confident) return t.valueToNode(res.value); - } -}; diff --git a/src/babel/transformation/transformers/minification/dead-code-elimination.js b/src/babel/transformation/transformers/minification/dead-code-elimination.js index f09166c03b..af23dba7e6 100644 --- a/src/babel/transformation/transformers/minification/dead-code-elimination.js +++ b/src/babel/transformation/transformers/minification/dead-code-elimination.js @@ -23,132 +23,132 @@ export var metadata = { experimental: true }; -export function ReferencedIdentifier(node, parent, scope) { - var binding = scope.getBinding(node.name); - if (!binding || binding.references > 1 || !binding.constant) return; - if (binding.kind === "param" || binding.kind === "module") return; +export var visitor = { + ReferencedIdentifier(node, parent, scope) { + var binding = scope.getBinding(node.name); + if (!binding || binding.references > 1 || !binding.constant) return; + if (binding.kind === "param" || binding.kind === "module") return; - var replacement = binding.path.node; - if (t.isVariableDeclarator(replacement)) { - replacement = replacement.init; - } - if (!replacement) return; + var replacement = binding.path.node; + if (t.isVariableDeclarator(replacement)) { + replacement = replacement.init; + } + if (!replacement) return; - // ensure it's a "pure" type - if (!scope.isPure(replacement, true)) return; + // ensure it's a "pure" type + if (!scope.isPure(replacement, true)) return; - if (t.isClass(replacement) || t.isFunction(replacement)) { - // don't change this if it's in a different scope, this can be bad - // for performance since it may be inside a loop or deeply nested in - // hot code - if (binding.path.scope.parent !== scope) return; - } - - if (this.findParent((path) => path.node === replacement)) { - return; - } - - t.toExpression(replacement); - scope.removeBinding(node.name); - binding.path.dangerouslyRemove(); - return replacement; -} - -export function FunctionDeclaration(node, parent, scope) { - var bindingInfo = scope.getBinding(node.id.name); - if (bindingInfo && !bindingInfo.referenced) { - this.dangerouslyRemove(); - } -} - -export { FunctionDeclaration as ClassDeclaration }; - -export function VariableDeclarator(node, parent, scope) { - if (!t.isIdentifier(node.id) || !scope.isPure(node.init, true)) return; - FunctionDeclaration.apply(this, arguments); -} - -export function ConditionalExpression(node, parent, scope) { - var evaluateTest = this.get("test").evaluateTruthy(); - if (evaluateTest === true) { - return node.consequent; - } else if (evaluateTest === false) { - return node.alternate; - } -} - -export function BlockStatement(node) { - var paths = this.get("body"); - - var purge = false; - - for (var i = 0; i < paths.length; i++) { - let path = paths[i]; - - if (!purge && path.isCompletionStatement()) { - purge = true; - continue; + if (t.isClass(replacement) || t.isFunction(replacement)) { + // don't change this if it's in a different scope, this can be bad + // for performance since it may be inside a loop or deeply nested in + // hot code + if (binding.path.scope.parent !== scope) return; } - if (purge && !path.isFunctionDeclaration()) { - path.dangerouslyRemove(); + if (this.findParent((path) => path.node === replacement)) { + return; } - } -} -export var IfStatement = { - exit(node) { - var consequent = node.consequent; - var alternate = node.alternate; - var test = node.test; + t.toExpression(replacement); + scope.removeBinding(node.name); + binding.path.dangerouslyRemove(); + return replacement; + }, + "ClassDeclaration|FunctionDeclaration"(node, parent, scope) { + var binding = scope.getBinding(node.id.name); + if (binding && !binding.referenced) { + this.dangerouslyRemove(); + } + }, + + VariableDeclarator(node, parent, scope) { + if (!t.isIdentifier(node.id) || !scope.isPure(node.init, true)) return; + visitor["ClassDeclaration|FunctionDeclaration"].apply(this, arguments); + }, + + ConditionalExpression(node) { var evaluateTest = this.get("test").evaluateTruthy(); - - // we can check if a test will be truthy 100% and if so then we can inline - // the consequent and completely ignore the alternate - // - // if (true) { foo; } -> { foo; } - // if ("foo") { foo; } -> { foo; } - // - if (evaluateTest === true) { - return toStatements(consequent); + return node.consequent; + } else if (evaluateTest === false) { + return node.alternate; } + }, - // we can check if a test will be falsy 100% and if so we can inline the - // alternate if there is one and completely remove the consequent - // - // if ("") { bar; } else { foo; } -> { foo; } - // if ("") { bar; } -> - // + BlockStatement() { + var paths = this.get("body"); - if (evaluateTest === false) { - if (alternate) { - return toStatements(alternate); - } else { - return this.dangerouslyRemove(); + var purge = false; + + for (var i = 0; i < paths.length; i++) { + let path = paths[i]; + + if (!purge && path.isCompletionStatement()) { + purge = true; + continue; + } + + if (purge && !path.isFunctionDeclaration()) { + path.dangerouslyRemove(); } } + }, - // remove alternate blocks that are empty - // - // if (foo) { foo; } else {} -> if (foo) { foo; } - // + IfStatement: { + exit(node) { + var consequent = node.consequent; + var alternate = node.alternate; + var test = node.test; - if (t.isBlockStatement(alternate) && !alternate.body.length) { - alternate = node.alternate = null; - } + var evaluateTest = this.get("test").evaluateTruthy(); - // if the consequent block is empty turn alternate blocks into a consequent - // and flip the test - // - // if (foo) {} else { bar; } -> if (!foo) { bar; } - // + // we can check if a test will be truthy 100% and if so then we can inline + // the consequent and completely ignore the alternate + // + // if (true) { foo; } -> { foo; } + // if ("foo") { foo; } -> { foo; } + // - if (t.isBlockStatement(consequent) && !consequent.body.length && t.isBlockStatement(alternate) && alternate.body.length) { - node.consequent = node.alternate; - node.alternate = null; - node.test = t.unaryExpression("!", test, true); + if (evaluateTest === true) { + return toStatements(consequent); + } + + // we can check if a test will be falsy 100% and if so we can inline the + // alternate if there is one and completely remove the consequent + // + // if ("") { bar; } else { foo; } -> { foo; } + // if ("") { bar; } -> + // + + if (evaluateTest === false) { + if (alternate) { + return toStatements(alternate); + } else { + return this.dangerouslyRemove(); + } + } + + // remove alternate blocks that are empty + // + // if (foo) { foo; } else {} -> if (foo) { foo; } + // + + if (t.isBlockStatement(alternate) && !alternate.body.length) { + alternate = node.alternate = null; + } + + // if the consequent block is empty turn alternate blocks into a consequent + // and flip the test + // + // if (foo) {} else { bar; } -> if (!foo) { bar; } + // + + if (t.isBlockStatement(consequent) && !consequent.body.length && t.isBlockStatement(alternate) && alternate.body.length) { + node.consequent = node.alternate; + node.alternate = null; + node.test = t.unaryExpression("!", test, true); + } } } }; diff --git a/src/babel/transformation/transformers/minification/member-expression-literals.js b/src/babel/transformation/transformers/minification/member-expression-literals.js index 877a924b11..130875c894 100644 --- a/src/babel/transformation/transformers/minification/member-expression-literals.js +++ b/src/babel/transformation/transformers/minification/member-expression-literals.js @@ -5,13 +5,15 @@ export var metadata = { group: "builtin-trailing" }; -export var MemberExpression = { - exit(node) { - var prop = node.property; - if (node.computed && t.isLiteral(prop) && t.isValidIdentifier(prop.value)) { - // foo["bar"] => foo.bar - node.property = t.identifier(prop.value); - node.computed = false; +export var visitor = { + MemberExpression: { + exit(node) { + var prop = node.property; + if (node.computed && t.isLiteral(prop) && t.isValidIdentifier(prop.value)) { + // foo["bar"] => foo.bar + node.property = t.identifier(prop.value); + node.computed = false; + } } } }; diff --git a/src/babel/transformation/transformers/minification/property-literals.js b/src/babel/transformation/transformers/minification/property-literals.js index c6cf4e7538..28abb18434 100644 --- a/src/babel/transformation/transformers/minification/property-literals.js +++ b/src/babel/transformation/transformers/minification/property-literals.js @@ -5,13 +5,15 @@ export var metadata = { group: "builtin-trailing" }; -export var Property = { - exit(node) { - var key = node.key; - if (t.isLiteral(key) && t.isValidIdentifier(key.value)) { - // "foo": "bar" -> foo: "bar" - node.key = t.identifier(key.value); - node.computed = false; +export var visitor = { + Property: { + exit(node) { + var key = node.key; + if (t.isLiteral(key) && t.isValidIdentifier(key.value)) { + // "foo": "bar" -> foo: "bar" + node.key = t.identifier(key.value); + node.computed = false; + } } } }; diff --git a/src/babel/transformation/transformers/minification/remove-console.js b/src/babel/transformation/transformers/minification/remove-console.js index b8175bbc6a..125ad4ba5c 100644 --- a/src/babel/transformation/transformers/minification/remove-console.js +++ b/src/babel/transformation/transformers/minification/remove-console.js @@ -3,8 +3,10 @@ export var metadata = { group: "builtin-pre" }; -export function CallExpression(node, parent) { - if (this.get("callee").matchesPattern("console", true)) { - this.dangerouslyRemove(); +export var visitor = { + CallExpression() { + if (this.get("callee").matchesPattern("console", true)) { + this.dangerouslyRemove(); + } } -} +}; diff --git a/src/babel/transformation/transformers/minification/remove-debugger.js b/src/babel/transformation/transformers/minification/remove-debugger.js index 8735b18015..cde74857be 100644 --- a/src/babel/transformation/transformers/minification/remove-debugger.js +++ b/src/babel/transformation/transformers/minification/remove-debugger.js @@ -3,6 +3,8 @@ export var metadata = { group: "builtin-pre" }; -export function DebuggerStatement(node) { - this.dangerouslyRemove(); -} +export var visitor = { + DebuggerStatement() { + this.dangerouslyRemove(); + } +}; diff --git a/src/babel/transformation/transformers/optimisation/flow.for-of.js b/src/babel/transformation/transformers/optimisation/flow.for-of.js index 8401d330e0..b5eeba6c62 100644 --- a/src/babel/transformation/transformers/optimisation/flow.for-of.js +++ b/src/babel/transformation/transformers/optimisation/flow.for-of.js @@ -4,8 +4,10 @@ export var metadata = { optional: true }; -export function ForOfStatement(node, parent, scope, file) { - if (this.get("right").isGenericType("Array")) { - return _ForOfStatementArray.call(this, node, scope, file); +export var visitor = { + ForOfStatement(node, parent, scope, file) { + if (this.get("right").isGenericType("Array")) { + return _ForOfStatementArray.call(this, node, scope, file); + } } -} +}; diff --git a/src/babel/transformation/transformers/optimisation/react.constant-elements.js b/src/babel/transformation/transformers/optimisation/react.constant-elements.js index d0d862de3c..bb74456f57 100644 --- a/src/babel/transformation/transformers/optimisation/react.constant-elements.js +++ b/src/babel/transformation/transformers/optimisation/react.constant-elements.js @@ -26,15 +26,17 @@ var immutabilityVisitor = { } }; -export function JSXElement(node, parent, scope, file) { - if (node._hoisted) return; +export var visitor = { + JSXElement(node) { + if (node._hoisted) return; - var state = { isImmutable: true }; - this.traverse(immutabilityVisitor, state); + var state = { isImmutable: true }; + this.traverse(immutabilityVisitor, state); - if (state.isImmutable) { - this.hoist(); - } else { - node._hoisted = true; + if (state.isImmutable) { + this.hoist(); + } else { + node._hoisted = true; + } } -} +}; diff --git a/src/babel/transformation/transformers/optimisation/react.inline-elements.js b/src/babel/transformation/transformers/optimisation/react.inline-elements.js index 5c336788a2..ed1ecaaba0 100644 --- a/src/babel/transformation/transformers/optimisation/react.inline-elements.js +++ b/src/babel/transformation/transformers/optimisation/react.inline-elements.js @@ -18,57 +18,59 @@ function isJSXAttributeOfName(attr, name) { return t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: name }); } -export function JSXElement(node, parent, scope, file) { - // filter - var open = node.openingElement; - if (hasRefOrSpread(open.attributes)) return; +export var visitor = { + JSXElement(node, parent, scope, file) { + // filter + var open = node.openingElement; + if (hasRefOrSpread(open.attributes)) return; - // init - var isComponent = true; - var props = t.objectExpression([]); - var obj = t.objectExpression([]); - var key = t.literal(null); - var type = open.name; + // init + var isComponent = true; + var props = t.objectExpression([]); + var obj = t.objectExpression([]); + var key = t.literal(null); + var type = open.name; - if (t.isJSXIdentifier(type) && react.isCompatTag(type.name)) { - type = t.literal(type.name); - isComponent = false; - } - - function pushElemProp(key, value) { - pushProp(obj.properties, t.identifier(key), value); - } - - function pushProp(objProps, key, value) { - objProps.push(t.property("init", key, value)); - } - - // metadata - pushElemProp("type", type); - pushElemProp("ref", t.literal(null)); - - if (node.children.length) { - pushProp(props.properties, t.identifier("children"), t.arrayExpression(react.buildChildren(node))); - } - - // props - for (var i = 0; i < open.attributes.length; i++) { - var attr = open.attributes[i]; - if (isJSXAttributeOfName(attr, "key")) { - key = attr.value; - } else { - pushProp(props.properties, attr.name, attr.value || t.identifier("true")); + if (t.isJSXIdentifier(type) && react.isCompatTag(type.name)) { + type = t.literal(type.name); + isComponent = false; } + + function pushElemProp(key, value) { + pushProp(obj.properties, t.identifier(key), value); + } + + function pushProp(objProps, key, value) { + objProps.push(t.property("init", key, value)); + } + + // metadata + pushElemProp("type", type); + pushElemProp("ref", t.literal(null)); + + if (node.children.length) { + pushProp(props.properties, t.identifier("children"), t.arrayExpression(react.buildChildren(node))); + } + + // props + for (var i = 0; i < open.attributes.length; i++) { + var attr = open.attributes[i]; + if (isJSXAttributeOfName(attr, "key")) { + key = attr.value; + } else { + pushProp(props.properties, attr.name, attr.value || t.identifier("true")); + } + } + + if (isComponent) { + props = t.callExpression(file.addHelper("default-props"), [t.memberExpression(type, t.identifier("defaultProps")), props]); + } + + pushElemProp("props", props); + + // key + pushElemProp("key", key); + + return obj; } - - if (isComponent) { - props = t.callExpression(file.addHelper("default-props"), [t.memberExpression(type, t.identifier("defaultProps")), props]); - } - - pushElemProp("props", props); - - // key - pushElemProp("key", key); - - return obj; -} +}; diff --git a/src/babel/transformation/transformers/other/async-to-generator.js b/src/babel/transformation/transformers/other/async-to-generator.js index a3f9a1aaac..844bfd0691 100644 --- a/src/babel/transformation/transformers/other/async-to-generator.js +++ b/src/babel/transformation/transformers/other/async-to-generator.js @@ -7,8 +7,10 @@ export var metadata = { dependencies: ["es7.asyncFunctions", "es6.classes"] }; -export function Func/*tion*/(node, parent, scope, file) { - if (!node.async || node.generator) return; +export var visitor = { + Function(node, parent, scope, file) { + if (!node.async || node.generator) return; - return remapAsyncToGenerator(this, file.addHelper("async-to-generator")); -} + return remapAsyncToGenerator(this, file.addHelper("async-to-generator")); + } +}; diff --git a/src/babel/transformation/transformers/other/bluebird-coroutines.js b/src/babel/transformation/transformers/other/bluebird-coroutines.js index 65f3eda4b2..0feae5c2fb 100644 --- a/src/babel/transformation/transformers/other/bluebird-coroutines.js +++ b/src/babel/transformation/transformers/other/bluebird-coroutines.js @@ -10,11 +10,13 @@ export var metadata = { dependencies: ["es7.asyncFunctions", "es6.classes"] }; -export function Func/*tion*/(node, parent, scope, file) { - if (!node.async || node.generator) return; +export var visitor = { + Function(node, parent, scope, file) { + if (!node.async || node.generator) return; - return remapAsyncToGenerator( - this, - t.memberExpression(file.addImport("bluebird", null, "absolute"), t.identifier("coroutine")) - ); -} + return remapAsyncToGenerator( + this, + t.memberExpression(file.addImport("bluebird", null, "absolute"), t.identifier("coroutine")) + ); + } +}; diff --git a/src/babel/transformation/transformers/other/eval.js b/src/babel/transformation/transformers/other/eval.js index e1ad0e67b6..c946cebafb 100644 --- a/src/babel/transformation/transformers/other/eval.js +++ b/src/babel/transformation/transformers/other/eval.js @@ -6,16 +6,18 @@ export var metadata = { optional: true }; -export function CallExpression(node) { - if (this.get("callee").isIdentifier({ name: "eval" }) && node.arguments.length === 1) { - var evaluate = this.get("arguments")[0].evaluate(); - if (!evaluate.confident) return; +export var visitor = { + CallExpression(node) { + if (this.get("callee").isIdentifier({ name: "eval" }) && node.arguments.length === 1) { + var evaluate = this.get("arguments")[0].evaluate(); + if (!evaluate.confident) return; - var code = evaluate.value; - if (typeof code !== "string") return; + var code = evaluate.value; + if (typeof code !== "string") return; - var ast = parse(code); - traverse.removeProperties(ast); - return ast.program; + var ast = parse(code); + traverse.removeProperties(ast); + return ast.program; + } } -} +}; diff --git a/src/babel/transformation/transformers/other/flow.js b/src/babel/transformation/transformers/other/flow.js index 2cce1ec2e8..f058924a59 100644 --- a/src/babel/transformation/transformers/other/flow.js +++ b/src/babel/transformation/transformers/other/flow.js @@ -4,37 +4,39 @@ export var metadata = { group: "builtin-trailing" }; -export function Flow(node) { - this.dangerouslyRemove(); -} +export var visitor = { + Flow() { + this.dangerouslyRemove(); + }, -export function ClassProperty(node) { - node.typeAnnotation = null; - if (!node.value) this.dangerouslyRemove(); -} + ClassProperty(node) { + node.typeAnnotation = null; + if (!node.value) this.dangerouslyRemove(); + }, -export function Class(node) { - node.implements = null; -} + Class(node) { + node.implements = null; + }, -export function Func/*tion*/(node) { - for (var i = 0; i < node.params.length; i++) { - var param = node.params[i]; - param.optional = false; + Function(node) { + for (var i = 0; i < node.params.length; i++) { + var param = node.params[i]; + param.optional = false; + } + }, + + TypeCastExpression(node) { + do { + node = node.expression; + } while(t.isTypeCastExpression(node)); + return node; + }, + + ImportDeclaration(node) { + if (node.isType) this.dangerouslyRemove(); + }, + + ExportDeclaration() { + if (this.get("declaration").isTypeAlias()) this.dangerouslyRemove(); } -} - -export function TypeCastExpression(node) { - do { - node = node.expression; - } while(t.isTypeCastExpression(node)); - return node; -} - -export function ImportDeclaration(node) { - if (node.isType) this.dangerouslyRemove(); -} - -export function ExportDeclaration(node) { - if (this.get("declaration").isTypeAlias()) this.dangerouslyRemove(); -} +}; diff --git a/src/babel/transformation/transformers/other/jscript.js b/src/babel/transformation/transformers/other/jscript.js index 0ea9d1e315..61abafba2a 100644 --- a/src/babel/transformation/transformers/other/jscript.js +++ b/src/babel/transformation/transformers/other/jscript.js @@ -5,17 +5,19 @@ export var metadata = { optional: true }; -export var FunctionExpression = { - exit(node) { - if (!node.id) return; - node._ignoreUserWhitespace = true; +export var visitor = { + FunctionExpression: { + exit(node) { + if (!node.id) return; + node._ignoreUserWhitespace = true; - return t.callExpression( - t.functionExpression(null, [], t.blockStatement([ - t.toStatement(node), - t.returnStatement(node.id) - ])), - [] - ); + return t.callExpression( + t.functionExpression(null, [], t.blockStatement([ + t.toStatement(node), + t.returnStatement(node.id) + ])), + [] + ); + } } }; diff --git a/src/babel/transformation/transformers/other/react-compat.js b/src/babel/transformation/transformers/other/react-compat.js index 9119ec9860..e7068a7fb6 100644 --- a/src/babel/transformation/transformers/other/react-compat.js +++ b/src/babel/transformation/transformers/other/react-compat.js @@ -10,7 +10,7 @@ export var metadata = { group: "builtin-advanced" }; -require("../../helpers/build-react-transformer")(exports, { +export var visitor = require("../../helpers/build-react-transformer")({ pre(state) { state.callee = state.tagExpr; }, diff --git a/src/babel/transformation/transformers/other/react.js b/src/babel/transformation/transformers/other/react.js index 1cceee2fae..4b48568395 100644 --- a/src/babel/transformation/transformers/other/react.js +++ b/src/babel/transformation/transformers/other/react.js @@ -7,7 +7,24 @@ export var metadata = { group: "builtin-advanced" }; -export function Program(node, parent, scope, file) { +export var visitor = require("../../helpers/build-react-transformer")({ + pre(state) { + var tagName = state.tagName; + var args = state.args; + if (react.isCompatTag(tagName)) { + args.push(t.literal(tagName)); + } else { + args.push(state.tagExpr); + } + }, + + post(state, file) { + state.callee = file.get("jsxIdentifier"); + } +}); + + +visitor.Program = function (node, parent, scope, file) { var id = file.opts.jsxPragma; for (var i = 0; i < file.ast.comments.length; i++) { @@ -26,20 +43,4 @@ export function Program(node, parent, scope, file) { file.set("jsxIdentifier", id.split(".").map(t.identifier).reduce(function (object, property) { return t.memberExpression(object, property); })); -} - -require("../../helpers/build-react-transformer")(exports, { - pre(state) { - var tagName = state.tagName; - var args = state.args; - if (react.isCompatTag(tagName)) { - args.push(t.literal(tagName)); - } else { - args.push(state.tagExpr); - } - }, - - post(state, file) { - state.callee = file.get("jsxIdentifier"); - } -}); +}; diff --git a/src/babel/transformation/transformers/other/regenerator.js b/src/babel/transformation/transformers/other/regenerator.js index adc9252cdc..e49d585270 100644 --- a/src/babel/transformation/transformers/other/regenerator.js +++ b/src/babel/transformation/transformers/other/regenerator.js @@ -6,17 +6,19 @@ export var metadata = { group: "builtin-advanced" }; -export var Func/*tion*/ = { - exit(node) { - if (node.async || node.generator) { - // Although this code transforms only the subtree rooted at the given - // Function node, that node might contain other generator functions - // that will also be transformed. It might help performance to ignore - // nested functions, and rely on the traversal to visit them later, - // but that's a small optimization. Starting here instead of at the - // root of the AST is the key optimization, since huge async/generator - // functions are relatively rare. - regenerator.transform(convertNodePath(this)); +export var visitor = { + Function: { + exit(node) { + if (node.async || node.generator) { + // Although this code transforms only the subtree rooted at the given + // Function node, that node might contain other generator functions + // that will also be transformed. It might help performance to ignore + // nested functions, and rely on the traversal to visit them later, + // but that's a small optimization. Starting here instead of at the + // root of the AST is the key optimization, since huge async/generator + // functions are relatively rare. + regenerator.transform(convertNodePath(this)); + } } } }; diff --git a/src/babel/transformation/transformers/other/runtime/index.js b/src/babel/transformation/transformers/other/runtime/index.js index 9b31bb031a..74b94a932f 100644 --- a/src/babel/transformation/transformers/other/runtime/index.js +++ b/src/babel/transformation/transformers/other/runtime/index.js @@ -19,82 +19,84 @@ export function pre(file) { }); } -export function ReferencedIdentifier(node, parent, scope, file) { - if (node.name === "regeneratorRuntime") { - return file.get("regeneratorIdentifier"); - } +export var visitor = { + ReferencedIdentifier(node, parent, scope, file) { + if (node.name === "regeneratorRuntime") { + return file.get("regeneratorIdentifier"); + } - if (t.isMemberExpression(parent)) return; - if (!has(definitions.builtins, node.name)) return; - if (scope.getBindingIdentifier(node.name)) return; + if (t.isMemberExpression(parent)) return; + if (!has(definitions.builtins, node.name)) return; + if (scope.getBindingIdentifier(node.name)) return; - // Symbol() -> _core.Symbol(); new Promise -> new _core.Promise - var modulePath = definitions.builtins[node.name]; - return file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, node.name, "absoluteDefault"); -} - -export function CallExpression(node, parent, scope, file) { - // arr[Symbol.iterator]() -> _core.$for.getIterator(arr) - - if (node.arguments.length) return; - - var callee = node.callee; - if (!t.isMemberExpression(callee)) return; - if (!callee.computed) return; - if (!this.get("callee.property").matchesPattern("Symbol.iterator")) return; - - return t.callExpression(file.addImport(`${RUNTIME_MODULE_NAME}/core-js/get-iterator`, "getIterator", "absoluteDefault"), [callee.object]); -} - -export function BinaryExpression(node, parent, scope, file) { - // Symbol.iterator in arr -> core.$for.isIterable(arr) - - if (node.operator !== "in") return; - if (!this.get("left").matchesPattern("Symbol.iterator")) return; - - return t.callExpression( - file.addImport(`${RUNTIME_MODULE_NAME}/core-js/is-iterable`, "isIterable", "absoluteDefault"), - [node.right] - ); -} - -export var MemberExpression = { - enter(node, parent, scope, file) { - // Array.from -> _core.Array.from - - if (!this.isReferenced()) return; - - var obj = node.object; - var prop = node.property; - - if (!t.isReferenced(obj, node)) return; - - if (node.computed) return; - - if (!has(definitions.methods, obj.name)) return; - - var methods = definitions.methods[obj.name]; - if (!has(methods, prop.name)) return; - - if (scope.getBindingIdentifier(obj.name)) return; - - var modulePath = methods[prop.name]; - return file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, `${obj.name}$${prop.name}`, "absoluteDefault"); + // Symbol() -> _core.Symbol(); new Promise -> new _core.Promise + var modulePath = definitions.builtins[node.name]; + return file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, node.name, "absoluteDefault"); }, - exit(node, parent, scope, file) { - if (!this.isReferenced()) return; + CallExpression(node, parent, scope, file) { + // arr[Symbol.iterator]() -> _core.$for.getIterator(arr) - var prop = node.property; - var obj = node.object; + if (node.arguments.length) return; - if (!has(definitions.builtins, obj.name)) return; - if (scope.getBindingIdentifier(obj.name)) return; + var callee = node.callee; + if (!t.isMemberExpression(callee)) return; + if (!callee.computed) return; + if (!this.get("callee.property").matchesPattern("Symbol.iterator")) return; - var modulePath = definitions.builtins[obj.name]; - return t.memberExpression( - file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, `${obj.name}`, "absoluteDefault"), - prop + return t.callExpression(file.addImport(`${RUNTIME_MODULE_NAME}/core-js/get-iterator`, "getIterator", "absoluteDefault"), [callee.object]); + }, + + BinaryExpression(node, parent, scope, file) { + // Symbol.iterator in arr -> core.$for.isIterable(arr) + + if (node.operator !== "in") return; + if (!this.get("left").matchesPattern("Symbol.iterator")) return; + + return t.callExpression( + file.addImport(`${RUNTIME_MODULE_NAME}/core-js/is-iterable`, "isIterable", "absoluteDefault"), + [node.right] ); + }, + + MemberExpression: { + enter(node, parent, scope, file) { + // Array.from -> _core.Array.from + + if (!this.isReferenced()) return; + + var obj = node.object; + var prop = node.property; + + if (!t.isReferenced(obj, node)) return; + + if (node.computed) return; + + if (!has(definitions.methods, obj.name)) return; + + var methods = definitions.methods[obj.name]; + if (!has(methods, prop.name)) return; + + if (scope.getBindingIdentifier(obj.name)) return; + + var modulePath = methods[prop.name]; + return file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, `${obj.name}$${prop.name}`, "absoluteDefault"); + }, + + exit(node, parent, scope, file) { + if (!this.isReferenced()) return; + + var prop = node.property; + var obj = node.object; + + if (!has(definitions.builtins, obj.name)) return; + if (scope.getBindingIdentifier(obj.name)) return; + + var modulePath = definitions.builtins[obj.name]; + return t.memberExpression( + file.addImport(`${RUNTIME_MODULE_NAME}/core-js/${modulePath}`, `${obj.name}`, "absoluteDefault"), + prop + ); + } } }; diff --git a/src/babel/transformation/transformers/other/strict.js b/src/babel/transformation/transformers/other/strict.js index fb3a12685f..cf7347d619 100644 --- a/src/babel/transformation/transformers/other/strict.js +++ b/src/babel/transformation/transformers/other/strict.js @@ -6,27 +6,29 @@ export var metadata = { const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassExpression", "ClassDeclaration"]; -export var Program = { - enter(program) { - var first = program.body[0]; +export var visitor = { + Program: { + enter(program) { + var first = program.body[0]; - var directive; - if (t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" })) { - directive = first; - } else { - directive = t.expressionStatement(t.literal("use strict")); - this.unshiftContainer("body", directive); - if (first) { - directive.leadingComments = first.leadingComments; - first.leadingComments = []; + var directive; + if (t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" })) { + directive = first; + } else { + directive = t.expressionStatement(t.literal("use strict")); + this.unshiftContainer("body", directive); + if (first) { + directive.leadingComments = first.leadingComments; + first.leadingComments = []; + } } + directive._blockHoist = Infinity; + } + }, + + ThisExpression() { + if (!this.findParent((path) => !path.is("shadow") && THIS_BREAK_KEYS.indexOf(path.type) >= 0)) { + return t.identifier("undefined"); } - directive._blockHoist = Infinity; } }; - -export function ThisExpression() { - if (!this.findParent((path) => !path.is("shadow") && THIS_BREAK_KEYS.indexOf(path.type) >= 0)) { - return t.identifier("undefined"); - } -} diff --git a/src/babel/transformation/transformers/spec/block-scoped-functions.js b/src/babel/transformation/transformers/spec/block-scoped-functions.js index feae389005..a27a13a5a8 100644 --- a/src/babel/transformation/transformers/spec/block-scoped-functions.js +++ b/src/babel/transformation/transformers/spec/block-scoped-functions.js @@ -23,14 +23,16 @@ function statementList(key, path) { } } -export function BlockStatement(node, parent) { - if ((t.isFunction(parent) && parent.body === node) || t.isExportDeclaration(parent)) { - return; +export var visitor = { + BlockStatement(node, parent) { + if ((t.isFunction(parent) && parent.body === node) || t.isExportDeclaration(parent)) { + return; + } + + statementList("body", this); + }, + + SwitchCase() { + statementList("consequent", this); } - - statementList("body", this); -} - -export function SwitchCase() { - statementList("consequent", this); -} +}; diff --git a/src/babel/transformation/transformers/spec/function-name.js b/src/babel/transformation/transformers/spec/function-name.js index a6e97a079b..190d3db868 100644 --- a/src/babel/transformation/transformers/spec/function-name.js +++ b/src/babel/transformation/transformers/spec/function-name.js @@ -4,8 +4,8 @@ export var metadata = { group: "builtin-pre" }; -export var FunctionExpression = { - exit: bare +export var visitor = { + "ArrowFunctionExpression|FunctionExpression": { + exit: bare + } }; - -export { FunctionExpression as ArrowFunctionExpression }; diff --git a/src/babel/transformation/transformers/spec/proto-to-assign.js b/src/babel/transformation/transformers/spec/proto-to-assign.js index 6f1443f43c..5fc2e460b2 100644 --- a/src/babel/transformation/transformers/spec/proto-to-assign.js +++ b/src/babel/transformation/transformers/spec/proto-to-assign.js @@ -19,44 +19,46 @@ export var metadata = { optional: true }; -export function AssignmentExpression(node, parent, scope, file) { - if (!isProtoAssignmentExpression(node)) return; +export var visitor = { + AssignmentExpression(node, parent, scope, file) { + if (!isProtoAssignmentExpression(node)) return; - var nodes = []; - var left = node.left.object; - var temp = scope.maybeGenerateMemoised(left); + var nodes = []; + var left = node.left.object; + var temp = scope.maybeGenerateMemoised(left); - nodes.push(t.expressionStatement(t.assignmentExpression("=", temp, left))); - nodes.push(buildDefaultsCallExpression(node, temp, file)); - if (temp) nodes.push(temp); + nodes.push(t.expressionStatement(t.assignmentExpression("=", temp, left))); + nodes.push(buildDefaultsCallExpression(node, temp, file)); + if (temp) nodes.push(temp); - return nodes; -} + return nodes; + }, -export function ExpressionStatement(node, parent, scope, file) { - var expr = node.expression; - if (!t.isAssignmentExpression(expr, { operator: "=" })) return; + ExpressionStatement(node, parent, scope, file) { + var expr = node.expression; + if (!t.isAssignmentExpression(expr, { operator: "=" })) return; - if (isProtoAssignmentExpression(expr)) { - return buildDefaultsCallExpression(expr, expr.left.object, file); - } -} + if (isProtoAssignmentExpression(expr)) { + return buildDefaultsCallExpression(expr, expr.left.object, file); + } + }, -export function ObjectExpression(node, parent, scope, file) { - var proto; + ObjectExpression(node, parent, scope, file) { + var proto; - for (var i = 0; i < node.properties.length; i++) { - var prop = node.properties[i]; + for (var i = 0; i < node.properties.length; i++) { + var prop = node.properties[i]; - if (isProtoKey(prop)) { - proto = prop.value; - pull(node.properties, prop); + if (isProtoKey(prop)) { + proto = prop.value; + pull(node.properties, prop); + } + } + + if (proto) { + var args = [t.objectExpression([]), proto]; + if (node.properties.length) args.push(node); + return t.callExpression(file.addHelper("extends"), args); } } - - if (proto) { - var args = [t.objectExpression([]), proto]; - if (node.properties.length) args.push(node); - return t.callExpression(file.addHelper("extends"), args); - } -} +}; diff --git a/src/babel/transformation/transformers/spec/undefined-to-void.js b/src/babel/transformation/transformers/spec/undefined-to-void.js deleted file mode 100644 index 30b2e17da8..0000000000 --- a/src/babel/transformation/transformers/spec/undefined-to-void.js +++ /dev/null @@ -1,12 +0,0 @@ -import * as t from "../../../types"; - -export var metadata = { - optional: true, - react: true -}; - -export function Identifier(node, parent) { - if (node.name === "undefined" && this.isReferenced()) { - return t.unaryExpression("void", t.literal(0), true); - } -} diff --git a/src/babel/transformation/transformers/utility/inline-environment-variables.js b/src/babel/transformation/transformers/utility/inline-environment-variables.js index e23dbd3c1f..3e0c0f1e6c 100644 --- a/src/babel/transformation/transformers/utility/inline-environment-variables.js +++ b/src/babel/transformation/transformers/utility/inline-environment-variables.js @@ -7,11 +7,13 @@ export var metadata = { var match = t.buildMatchMemberExpression("process.env"); -export function MemberExpression(node) { - if (match(node.object)) { - var key = this.toComputedKey(); - if (t.isLiteral(key)) { - return t.valueToNode(process.env[key.value]); +export var visitor = { + MemberExpression(node) { + if (match(node.object)) { + var key = this.toComputedKey(); + if (t.isLiteral(key)) { + return t.valueToNode(process.env[key.value]); + } } } -} +}; diff --git a/src/babel/transformation/transformers/validation/react.js b/src/babel/transformation/transformers/validation/react.js index 536aff1768..cefa71e64f 100644 --- a/src/babel/transformation/transformers/validation/react.js +++ b/src/babel/transformation/transformers/validation/react.js @@ -13,12 +13,14 @@ function check(source, file) { } } -export function CallExpression(node, parent, scope, file) { - if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) { - check(node.arguments[0], file); - } -} +export var visitor = { + CallExpression(node, parent, scope, file) { + if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) { + check(node.arguments[0], file); + } + }, -export function ModuleDeclaration(node, parent, scope, file) { - check(node.source, file); -} + ModuleDeclaration(node, parent, scope, file) { + check(node.source, file); + } +}; diff --git a/src/babel/transformation/transformers/validation/undeclared-variable-check.js b/src/babel/transformation/transformers/validation/undeclared-variable-check.js index 3311dd4d2e..fa6da185a0 100644 --- a/src/babel/transformation/transformers/validation/undeclared-variable-check.js +++ b/src/babel/transformation/transformers/validation/undeclared-variable-check.js @@ -5,39 +5,41 @@ export var metadata = { optional: true }; -export function ReferencedIdentifier(node, parent, scope, file) { - var binding = scope.getBinding(node.name); - if (binding && binding.kind === "type" && !this.parentPath.isFlow()) { - throw this.errorWithNode(messages.get("undeclaredVariableType", node.name), ReferenceError); +export var visitor = { + ReferencedIdentifier(node, parent, scope) { + var binding = scope.getBinding(node.name); + if (binding && binding.kind === "type" && !this.parentPath.isFlow()) { + throw this.errorWithNode(messages.get("undeclaredVariableType", node.name), ReferenceError); + } + + if (scope.hasBinding(node.name)) return; + + // get the closest declaration to offer as a suggestion + // the variable name may have just been mistyped + + var bindings = scope.getAllBindings(); + + var closest; + var shortest = -1; + + for (var name in bindings) { + var distance = levenshtein(node.name, name); + if (distance <= 0 || distance > 3) continue; + if (distance <= shortest) continue; + + closest = name; + shortest = distance; + } + + var msg; + if (closest) { + msg = messages.get("undeclaredVariableSuggestion", node.name, closest); + } else { + msg = messages.get("undeclaredVariable", node.name); + } + + // + + throw this.errorWithNode(msg, ReferenceError); } - - if (scope.hasBinding(node.name)) return; - - // get the closest declaration to offer as a suggestion - // the variable name may have just been mistyped - - var bindings = scope.getAllBindings(); - - var closest; - var shortest = -1; - - for (var name in bindings) { - var distance = levenshtein(node.name, name); - if (distance <= 0 || distance > 3) continue; - if (distance <= shortest) continue; - - closest = name; - shortest = distance; - } - - var msg; - if (closest) { - msg = messages.get("undeclaredVariableSuggestion", node.name, closest); - } else { - msg = messages.get("undeclaredVariable", node.name); - } - - // - - throw this.errorWithNode(msg, ReferenceError); -} +}; diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index 806c9c3ae2..6a8a59898c 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -15,7 +15,6 @@ export default function traverse(parent, opts, scope, state, parentPath) { if (!opts) opts = {}; - visitors.verify(opts); visitors.explode(opts); // array of nodes diff --git a/src/babel/traversal/path/lib/hoister.js b/src/babel/traversal/path/lib/hoister.js index 9ee6ddd291..4623bda388 100644 --- a/src/babel/traversal/path/lib/hoister.js +++ b/src/babel/traversal/path/lib/hoister.js @@ -8,17 +8,17 @@ var referenceVisitor = { } // direct references that we need to track to hoist this to the highest scope we can - var bindingInfo = scope.getBinding(node.name); - if (!bindingInfo) return; + var binding = scope.getBinding(node.name); + if (!binding) return; // this binding isn't accessible from the parent scope so we can safely ignore it // eg. it's in a closure etc - if (bindingInfo !== state.scope.getBinding(node.name)) return; + if (binding !== state.scope.getBinding(node.name)) return; - if (bindingInfo.constant) { - state.bindings[node.name] = bindingInfo; + if (binding.constant) { + state.bindings[node.name] = binding; } else { - for (var violationPath of (bindingInfo.constantViolations: Array)) { + for (var violationPath of (binding.constantViolations: Array)) { state.breakOnScopePaths.push(violationPath.scope.path); } } diff --git a/src/babel/traversal/scope/index.js b/src/babel/traversal/scope/index.js index f8ad816fcd..7415a30f08 100644 --- a/src/babel/traversal/scope/index.js +++ b/src/babel/traversal/scope/index.js @@ -31,9 +31,9 @@ var collectorVisitor = { }, ReferencedIdentifier(node) { - var bindingInfo = this.scope.getBinding(node.name); - if (bindingInfo) { - bindingInfo.reference(); + var binding = this.scope.getBinding(node.name); + if (binding) { + binding.reference(this); } else { this.scope.getProgramParent().addGlobal(node); } diff --git a/src/babel/traversal/visitors.js b/src/babel/traversal/visitors.js index 931147ab7b..5be7ebe565 100644 --- a/src/babel/traversal/visitors.js +++ b/src/babel/traversal/visitors.js @@ -8,6 +8,24 @@ export function explode(visitor) { if (visitor._exploded) return visitor; visitor._exploded = true; + // normalise pipes + for (let nodeType in visitor) { + if (shouldIgnoreKey(nodeType)) continue; + + let parts = nodeType.split("|"); + if (parts.length === 1) continue; + + let fns = visitor[nodeType]; + delete visitor[nodeType]; + + for (let part of (parts: Array)) { + visitor[part] = fns; + } + } + + // verify data structure + verify(visitor); + // make sure there's no __esModule type since this is because we're using loose mode // and it sets __esModule to be enumerable on all modules :( delete visitor.__esModule; @@ -21,6 +39,7 @@ export function explode(visitor) { // ensure visitors are objects ensureEntranceObjects(visitor); + // ensure enter/exit callbacks are arrays ensureCallbackArrays(visitor); // add type wrappers diff --git a/src/babel/types/index.js b/src/babel/types/index.js index 1bed2fe9b9..91365a4315 100644 --- a/src/babel/types/index.js +++ b/src/babel/types/index.js @@ -312,7 +312,6 @@ export function inherits(child: Object, parent: Object): Object { toFastProperties(t); toFastProperties(t.VISITOR_KEYS); -exports.__esModule = true; assign(t, require("./retrievers")); assign(t, require("./validators")); assign(t, require("./converters")); diff --git a/test/core/path.js b/test/core/path.js index f53817fa14..17fcf5dbdb 100644 --- a/test/core/path.js +++ b/test/core/path.js @@ -1,7 +1,7 @@ -var Transformer = require("../../lib/babel/transformation/transformer"); -var transform = require("../../lib/babel/transformation"); -var babel = require("../../lib/babel/api/node"); -var chai = require("chai"); +var transform = require("../../lib/babel/transformation"); +var Plugin = require("../../lib/babel/transformation/plugin"); +var babel = require("../../lib/babel/api/node"); +var chai = require("chai"); suite("traversal path", function () { test("replaceWithSourceString", function () { @@ -9,7 +9,7 @@ suite("traversal path", function () { var actualCode = transform(expectCode, { blacklist: "strict", - plugins: [new Transformer("foobar", { + plugins: [new Plugin("foobar", { FunctionDeclaration: function () { return "console.whatever()"; }