From 9cff51915d73364e7340b117f578742197e3647c Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Thu, 12 Mar 2015 14:35:55 +1100 Subject: [PATCH 01/24] switch scope tracking to a path based system --- src/babel/transformation/file.js | 15 +++++---- .../transformation/transformers/es6/for-of.js | 3 -- src/babel/traversal/path.js | 31 ++++++++++++------- src/babel/traversal/scope.js | 24 +++++++++----- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file.js index 900ffc3106..785f4ab4a4 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file.js @@ -1,6 +1,7 @@ import convertSourceMap from "convert-source-map"; import shebangRegex from "shebang-regex"; import isFunction from "lodash/lang/isFunction"; +import TraversalPath from "../traversal/path"; import sourceMap from "source-map"; import transform from "./index"; import generate from "../generation"; @@ -375,7 +376,7 @@ export default class File { this.usedHelpers[name] = true; var generator = this.get("helperGenerator"); - var runtime = this.get("helpersNamespace"); + var runtime = this.get("helpersNamespace"); if (generator) { return generator(name); } else if (runtime) { @@ -425,9 +426,11 @@ export default class File { transform(ast) { this.log.debug(); - this.ast = ast; + this.path = TraversalPath.get(null, null, ast, ast, "program", this); + this.scope = this.path.scope; + this.ast = ast; + this.lastStatements = t.getLastStatements(ast.program); - this.scope = new Scope(ast.program, ast, null, this); var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules); if (modFormatter.init && this.transformers["es6.modules"].canRun()) { @@ -481,14 +484,14 @@ export default class File { if (inputMap) { map.sources[0] = inputMap.file; - var inputMapConsumer = new sourceMap.SourceMapConsumer(inputMap); - var outputMapConsumer = new sourceMap.SourceMapConsumer(map); + var inputMapConsumer = new sourceMap.SourceMapConsumer(inputMap); + var outputMapConsumer = new sourceMap.SourceMapConsumer(map); var outputMapGenerator = sourceMap.SourceMapGenerator.fromSourceMap(outputMapConsumer); outputMapGenerator.applySourceMap(inputMapConsumer); var mergedMap = outputMapGenerator.toJSON(); mergedMap.sources = inputMap.sources - mergedMap.file = inputMap.file; + mergedMap.file = inputMap.file; return mergedMap; } diff --git a/src/babel/transformation/transformers/es6/for-of.js b/src/babel/transformation/transformers/es6/for-of.js index 28f7d98df0..9f7b1d9b04 100644 --- a/src/babel/transformation/transformers/es6/for-of.js +++ b/src/babel/transformation/transformers/es6/for-of.js @@ -29,9 +29,6 @@ export function ForOfStatement(node, parent, scope, file) { t.inherits(loop, node); - // todo: find out why this is necessary? #538 - loop._scopeInfo = node._scopeInfo; - if (build.replaceParent) { this.parentPath.node = build.node; } else { diff --git a/src/babel/traversal/path.js b/src/babel/traversal/path.js index fb5c204050..88bed83108 100644 --- a/src/babel/traversal/path.js +++ b/src/babel/traversal/path.js @@ -10,7 +10,7 @@ export default class TraversalPath { this.data = {}; } - static get(parentPath, context, parent, container, key) { + static get(parentPath: TraversalPath, context?: TraversalContext, parent, container, key, file?: File) { var targetNode = container[key]; var paths = container._paths ||= []; var path; @@ -28,17 +28,17 @@ export default class TraversalPath { paths.push(path); } - path.setContext(parentPath, context, key); + path.setContext(parentPath, context, key, file); return path; } - static getScope(node, parent, scope) { + static getScope(path: TraversalPath, scope: Scope, file?: File) { var ourScope = scope; // we're entering a new scope so let's construct it! - if (t.isScope(node, parent)) { - ourScope = new Scope(node, parent, scope); + if (path.isScope()) { + ourScope = new Scope(path, scope, file); } return ourScope; @@ -60,21 +60,24 @@ export default class TraversalPath { return this.data[key]; } - setScope() { - this.scope = TraversalPath.getScope(this.node, this.parent, this.context.scope); + setScope(file?) { + this.scope = TraversalPath.getScope(this, this.context && this.context.scope, file); } - setContext(parentPath, context, key) { + setContext(parentPath, context, key, file?) { this.shouldSkip = false; this.shouldStop = false; this.parentPath = parentPath || this.parentPath; - this.context = context; - this.state = context.state; - this.opts = context.opts; this.key = key; - this.setScope(); + if (context) { + this.context = context; + this.state = context.state; + this.opts = context.opts; + } + + this.setScope(file); } remove() { @@ -200,6 +203,10 @@ export default class TraversalPath { return TraversalPath.get(this, this.context, this.node, this.node, key); } + isScope() { + return t.isScope(this.node, this.parent); + } + isReferencedIdentifier(opts) { return t.isReferencedIdentifier(this.node, this.parent, opts); } diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index 5c006703b8..8aafda19ff 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -65,12 +65,20 @@ export default class Scope { * within. */ - constructor(block: Object, parentBlock: Object, parent?: Scope, file?: File) { + constructor(path: TraversalPath, parent?: Scope, file?: File) { + var cached = path.getData("scope"); + if (cached) { + return cached; + } else { + path.setData("scope", this); + } + this.parent = parent; this.file = parent ? parent.file : file; - this.parentBlock = parentBlock; - this.block = block; + this.parentBlock = path.parent; + this.block = path.node; + this.path = path; this.crawl(); } @@ -472,7 +480,7 @@ export default class Scope { */ recrawl() { - this.block._scopeInfo = null; + this.path.setData("scopeInfo", null); this.crawl(); } @@ -481,21 +489,21 @@ export default class Scope { */ crawl() { - var block = this.block; + var block = this.block; var i; // - var info = block._scopeInfo; + var info = this.path.getData("scopeInfo"); if (info) { extend(this, info); return; } - info = block._scopeInfo = { + info = this.path.setData("scopeInfo", { bindings: object(), globals: object() - }; + }); extend(this, info); From 40a111abbfe0bf221625f758e5090ebe82700ad0 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 13 Mar 2015 01:07:33 +1100 Subject: [PATCH 02/24] always resolve rc for absolute filenames --- bin/babel/util.js | 16 ++--- package.json | 1 + src/babel/api/node.js | 11 +-- src/babel/api/register/node.js | 5 +- .../{api/register => tools}/resolve-rc.js | 0 src/babel/transformation/file.js | 72 ++++++++++++++++--- src/babel/transformation/index.js | 2 +- src/babel/util.js | 16 +++-- 8 files changed, 91 insertions(+), 32 deletions(-) rename src/babel/{api/register => tools}/resolve-rc.js (100%) diff --git a/bin/babel/util.js b/bin/babel/util.js index 49c43de4e8..b353e153a8 100644 --- a/bin/babel/util.js +++ b/bin/babel/util.js @@ -1,11 +1,10 @@ -var resolveRc = require("../../lib/babel/api/register/resolve-rc"); -var readdir = require("fs-readdir-recursive"); -var index = require("./index"); -var babel = require("../../lib/babel/api/node"); -var util = require("../../lib/babel/util"); -var path = require("path"); -var fs = require("fs"); -var _ = require("lodash"); +var readdir = require("fs-readdir-recursive"); +var index = require("./index"); +var babel = require("../../lib/babel/api/node"); +var util = require("../../lib/babel/util"); +var path = require("path"); +var fs = require("fs"); +var _ = require("lodash"); exports.readdirFilter = function (filename) { return readdir(filename).filter(function (filename) { @@ -24,7 +23,6 @@ exports.addSourceMappingUrl = function (code, loc) { exports.transform = function (filename, code, opts) { opts = _.defaults(opts || {}, index.opts); opts.filename = filename; - resolveRc(filename, opts); var result = babel.transform(code, opts); result.filename = filename; diff --git a/package.json b/package.json index 6e2cd2721b..9240d6f6f6 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "leven": "^1.0.1", "line-numbers": "0.2.0", "lodash": "^3.2.0", + "minimatch": "^2.0.3", "output-file-sync": "^1.1.0", "path-is-absolute": "^1.0.0", "private": "^0.1.6", diff --git a/src/babel/api/node.js b/src/babel/api/node.js index df49f9e21d..fa2a7a7818 100644 --- a/src/babel/api/node.js +++ b/src/babel/api/node.js @@ -1,12 +1,13 @@ import isFunction from "lodash/lang/isFunction"; import transform from "../transformation"; +import * as util from "../util"; import fs from "fs"; -import * as util from "../util"; -export { util as _util }; +export { util }; export { canCompile } from "../util"; export { default as acorn } from "acorn-babel"; +export { default as Transformer } from "../transformation/transformer"; export { default as transform } from "../transformation"; export { default as traverse } from "../traversal"; export { default as buildExternalHelpers } from "../tools/build-external-helpers"; @@ -15,7 +16,7 @@ export { version } from "../../../package"; import * as t from "../types"; export { t as types }; -export function register(opts) { +export function register(opts?: Object) { var callback = require("./register/node"); if (opts != null) callback(opts); return callback; @@ -25,7 +26,7 @@ export function polyfill() { require("../polyfill"); } -export function transformFile(filename, opts, callback) { +export function transformFile(filename: string, opts?: Object, callback: Function) { if (isFunction(opts)) { callback = opts; opts = {}; @@ -48,7 +49,7 @@ export function transformFile(filename, opts, callback) { }); } -export function transformFileSync(filename, opts = {}) { +export function transformFileSync(filename: string, opts?: Object = {}) { opts.filename = filename; return transform(fs.readFileSync(filename), opts); } diff --git a/src/babel/api/register/node.js b/src/babel/api/register/node.js index f2c044af0c..beb06ebdb0 100644 --- a/src/babel/api/register/node.js +++ b/src/babel/api/register/node.js @@ -1,7 +1,7 @@ import "../../polyfill"; import sourceMapSupport from "source-map-support"; import * as registerCache from "./cache"; -import resolveRc from "./resolve-rc"; +import resolveRc from "../../tools/resolve-rc"; import extend from "lodash/object/extend"; import * as babel from "../node"; import each from "lodash/collection/each"; @@ -44,6 +44,9 @@ var compile = function (filename) { var result; var opts = extend({}, transformOpts); + + // this will be done when the file is transformed anyway but we need all + // the options so we can generate the cache key resolveRc(filename, opts); var cacheKey = `${filename}:${JSON.stringify(opts)}:${babel.version}`; diff --git a/src/babel/api/register/resolve-rc.js b/src/babel/tools/resolve-rc.js similarity index 100% rename from src/babel/api/register/resolve-rc.js rename to src/babel/tools/resolve-rc.js diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file.js index 785f4ab4a4..3b8b1abf43 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file.js @@ -1,12 +1,15 @@ import convertSourceMap from "convert-source-map"; import shebangRegex from "shebang-regex"; -import isFunction from "lodash/lang/isFunction"; import TraversalPath from "../traversal/path"; +import isFunction from "lodash/lang/isFunction"; +import isAbsolute from "path-is-absolute"; +import resolveRc from "../tools/resolve-rc"; import sourceMap from "source-map"; import transform from "./index"; import generate from "../generation"; import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; +import traverse from "../traversal"; import assign from "lodash/object/assign"; import Logger from "./logger"; import parse from "../helpers/parse"; @@ -114,13 +117,14 @@ export default class File { "sourceRoot", "moduleRoot", + "ignore", + "only", + // legacy "format", "reactCompat", // these are used by plugins - "ignore", - "only", "extensions", "accept" ]; @@ -128,6 +132,10 @@ export default class File { normalizeOptions(opts: Object) { opts = assign({}, opts); + if (opts.filename && isAbsolute(opts.filename)) { + opts = resolveRc(opts.filename, opts); + } + for (var key in opts) { if (key[0] !== "_" && File.validOptions.indexOf(key) < 0) { throw new ReferenceError(`Unknown option: ${key}`); @@ -154,6 +162,8 @@ export default class File { modules: "common", compact: "auto", loose: [], + ignore: [], + only: [], code: true, ast: true }); @@ -179,6 +189,8 @@ export default class File { opts.optional = util.arrayify(opts.optional); opts.compact = util.booleanify(opts.compact); opts.loose = util.arrayify(opts.loose); + opts.ignore = util.arrayify(opts.ignore, util.regexify); + opts.only = util.arrayify(opts.only, util.regexify); if (includes(opts.loose, "all") || includes(opts.loose, true)) { opts.loose = Object.keys(transform.transformers); @@ -214,11 +226,6 @@ export default class File { opts.optional = transform._ensureTransformerNames("optional", opts.optional); opts.loose = transform._ensureTransformerNames("loose", opts.loose); - if (opts.reactCompat) { - opts.optional.push("reactCompat"); - console.error("The reactCompat option has been moved into the optional transformer `reactCompat`"); - } - var ensureEnabled = function (key) { var namespace = transform.transformerNamespaces[key]; if (namespace === "es7") opts.experimental = true; @@ -409,7 +416,36 @@ export default class File { return this.parseShebang(code); } + shouldIgnore() { + var opts = this.opts; + + var filename = opts.filename; + var ignore = opts.ignore; + var only = opts.only; + + if (only.length) { + for (var i = 0; i < only.length; i++) { + if (only[i].test(filename)) return false; + } + return true; + } else if (ignore.length) { + for (var i = 0; i < ignore.length; i++) { + if (ignore[i].test(filename)) return true; + } + } + + return false; + } + parse(code: string) { + if (this.shouldIgnore()) { + return { + code: code, + map: null, + ast: null + }; + } + code = this.addCode(code); var opts = this.opts; @@ -423,13 +459,27 @@ export default class File { }); } - transform(ast) { - this.log.debug(); - + setAst(ast) { this.path = TraversalPath.get(null, null, ast, ast, "program", this); this.scope = this.path.scope; this.ast = ast; + traverse(ast, { + enter(node, parent, scope) { + if (this.isScope()) { + for (var key in scope.bindings) { + scope.bindings[key].setTypeAnnotation(); + } + } + } + }, this.scope); + } + + transform(ast) { + this.log.debug(); + + this.setAst(ast); + this.lastStatements = t.getLastStatements(ast.program); var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules); diff --git a/src/babel/transformation/index.js b/src/babel/transformation/index.js index 1c02c4ee2a..8d244aa51a 100644 --- a/src/babel/transformation/index.js +++ b/src/babel/transformation/index.js @@ -4,7 +4,7 @@ import object from "../helpers/object"; import File from "./file"; import each from "lodash/collection/each"; -export default function transform(code: code, opts?: Object) { +export default function transform(code: string, opts?: Object) { var file = new File(opts); return file.parse(code); } diff --git a/src/babel/util.js b/src/babel/util.js index f9a8184639..d3b271ecc1 100644 --- a/src/babel/util.js +++ b/src/babel/util.js @@ -4,6 +4,7 @@ import buildDebug from "debug/node"; import cloneDeep from "lodash/lang/cloneDeep"; import isBoolean from "lodash/lang/isBoolean"; import * as messages from "./messages"; +import minimatch from "minimatch"; import contains from "lodash/collection/contains"; import traverse from "./traversal"; import isString from "lodash/lang/isString"; @@ -43,16 +44,21 @@ export function list(val: string): Array { export function regexify(val: any): RegExp { if (!val) return new RegExp(/.^/); if (Array.isArray(val)) val = val.join("|"); - if (isString(val)) return new RegExp(val); + if (isString(val)) return minimatch.makeRe(val, { nocase: true }); if (isRegExp(val)) return val; throw new TypeError("illegal type for regexify"); } -export function arrayify(val: any): Array { +export function arrayify(val: any, mapFn?: Function): Array { if (!val) return []; - if (isBoolean(val)) return [val]; - if (isString(val)) return list(val); - if (Array.isArray(val)) return val; + if (isBoolean(val)) return arrayify([val], mapFn); + if (isString(val)) return arrayify(list(val), mapFn); + + if (Array.isArray(val)) { + if (mapFn) val = val.map(mapFn); + return val; + } + throw new TypeError("illegal type for arrayify"); } From b44ba25d11206fe40d1262d1651e7dbbe47a6c66 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 13 Mar 2015 01:08:46 +1100 Subject: [PATCH 03/24] separate binding logic from scope to a binding class, move binding type resolution to the path so it can be used on any expression - #653 --- .../transformation/templates/for-of-array.js | 1 + .../transformers/es6/destructuring.js | 2 +- .../transformation/transformers/es6/for-of.js | 44 +++ .../transformers/es6/parameters.rest.js | 10 +- .../transformers/es6/tail-call.js | 2 +- .../transformation/transformers/index.js | 2 +- src/babel/traversal/binding.js | 92 ++++++ src/babel/traversal/index.js | 1 - src/babel/traversal/path.js | 130 +++++++- src/babel/traversal/scope.js | 310 ++++++------------ src/babel/types/converters.js | 2 +- src/babel/types/index.js | 1 - 12 files changed, 372 insertions(+), 225 deletions(-) create mode 100644 src/babel/transformation/templates/for-of-array.js create mode 100644 src/babel/traversal/binding.js diff --git a/src/babel/transformation/templates/for-of-array.js b/src/babel/transformation/templates/for-of-array.js new file mode 100644 index 0000000000..7f27900c15 --- /dev/null +++ b/src/babel/transformation/templates/for-of-array.js @@ -0,0 +1 @@ +for (var KEY = 0; KEY < ARR.length; KEY++) BODY; diff --git a/src/babel/transformation/transformers/es6/destructuring.js b/src/babel/transformation/transformers/es6/destructuring.js index ea9e3bdaf3..7fecf07eef 100644 --- a/src/babel/transformation/transformers/es6/destructuring.js +++ b/src/babel/transformation/transformers/es6/destructuring.js @@ -432,7 +432,7 @@ class DestructuringTransformer { } else { arrayRef = this.scope.generateUidBasedOnNode(arrayRef); this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray)); - this.scope.assignTypeGeneric(arrayRef.name, "Array"); + this.getBinding(arrayRef.name).assignTypeGeneric("Array"); } // diff --git a/src/babel/transformation/transformers/es6/for-of.js b/src/babel/transformation/transformers/es6/for-of.js index 9f7b1d9b04..addd609af5 100644 --- a/src/babel/transformation/transformers/es6/for-of.js +++ b/src/babel/transformation/transformers/es6/for-of.js @@ -5,6 +5,12 @@ import * as t from "../../../types"; export var check = t.isForOfStatement; export function ForOfStatement(node, parent, scope, file) { + if (this.get("right").isTypeGeneric("Array")) { + return array(node, scope, file); + } + + // + var callback = spec; if (file.isLoose("es6.forOf")) callback = loose; @@ -36,6 +42,44 @@ export function ForOfStatement(node, parent, scope, file) { } } +var array = function (node, scope, file) { + var nodes = []; + var right = node.right; + + if (!t.isIdentifier(right) || !scope.hasBinding(right.name)) { + var uid = scope.generateUidIdentifier("arr"); + nodes.push(t.variableDeclaration("var", [ + t.variableDeclarator(uid, right) + ])); + right = uid; + } + + var iterationKey = scope.generateUidIdentifier("i"); + + var loop = util.template("for-of-array", { + BODY: node.body, + KEY: iterationKey, + ARR: right + }); + + t.inherits(loop, node); + t.ensureBlock(loop); + + var iterationValue = t.memberExpression(right, iterationKey, true); + + var left = node.left; + if (t.isVariableDeclaration(left)) { + left.declarations[0].init = iterationValue; + loop.body.body.unshift(left); + } else { + loop.body.body.unshift(t.expressionStatement(t.assignmentExpression("=", left, iterationValue))); + } + + nodes.push(loop); + + return nodes; +}; + var loose = function (node, parent, scope, file) { var left = node.left; var declar, id; diff --git a/src/babel/transformation/transformers/es6/parameters.rest.js b/src/babel/transformation/transformers/es6/parameters.rest.js index 14098fa0e3..e79af4418d 100644 --- a/src/babel/transformation/transformers/es6/parameters.rest.js +++ b/src/babel/transformation/transformers/es6/parameters.rest.js @@ -127,16 +127,14 @@ exports.Function = function (node, parent, scope, file) { ); } - scope.assignTypeGeneric(rest.name, "Array"); - var loop = util.template("rest", { ARGUMENTS: argsId, ARRAY_KEY: arrKey, ARRAY_LEN: arrLen, - START: start, - ARRAY: rest, - KEY: key, - LEN: len, + 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/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index e7439cdf5a..563ad8bd00 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -142,7 +142,7 @@ class TailCallTransformer { hasDeopt() { // check if the ownerId has been reassigned, if it has then it's not safe to // perform optimisations - var ownerIdInfo = this.scope.getBindingInfo(this.ownerId.name); + var ownerIdInfo = this.scope.getBinding(this.ownerId.name); return ownerIdInfo && ownerIdInfo.reassigned; } diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index e318ff6902..c1098afeb7 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -20,7 +20,6 @@ export default { "playground.objectGetterMemoization": require("./playground/object-getter-memoization"), reactCompat: require("./other/react-compat"), - flow: require("./other/flow"), react: require("./other/react"), _modules: require("./internal/modules"), @@ -110,5 +109,6 @@ export default { "utility.inlineExpressions": require("./utility/inline-expressions"), "utility.deadCodeElimination": require("./utility/dead-code-elimination"), + flow: require("./other/flow"), _cleanUp: require("./internal/cleanup") }; diff --git a/src/babel/traversal/binding.js b/src/babel/traversal/binding.js new file mode 100644 index 0000000000..95f45dace7 --- /dev/null +++ b/src/babel/traversal/binding.js @@ -0,0 +1,92 @@ +import * as t from "../types"; + +export default class Binding { + constructor({ identifier, scope, path, kind }) { + this.identifier = identifier; + this.reassigned = false; + this.scope = scope; + this.path = path; + this.kind = kind; + } + + /** + * Description + */ + + setTypeAnnotation() { + var typeInfo = this.path.getTypeAnnotation(); + this.typeAnnotationInferred = typeInfo.inferred; + this.typeAnnotation = typeInfo.annotation; + } + + /** + * Description + */ + + isTypeGeneric(): boolean { + return this.path.isTypeGeneric(...arguments); + } + + /** + * Description + */ + + assignTypeGeneric(type: Object, params?) { + var typeParams = null; + if (params) params = t.typeParameterInstantiation(params); + this.assignType(t.genericTypeAnnotation(t.identifier(type), typeParams)); + } + + /** + * Description + */ + + assignType(type: Object) { + this.typeAnnotation = type; + } + + /** + * Description + */ + + reassign() { + this.reassigned = true; + + if (this.typeAnnotationInferred) { + // destroy the inferred typeAnnotation + this.typeAnnotation = null; + } + } + + /** + * Description + */ + + getValueIfImmutable() { + // can't guarantee this value is the same + if (this.reassigned) return; + + var node = this.path.node; + if (t.isVariableDeclarator(node)) { + if (t.isIdentifier(node.id)) { + node = node.init; + } else { + // otherwise it's probably a destructuring like: + // var { foo } = "foo"; + return; + } + } + + if (t.isImmutable(node)) { + return node; + } + } + + /** + * Description + */ + + isCompatibleWithType(newType): boolean { + return false; + } +} diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index 86c94d9fbe..b2befe1823 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -42,7 +42,6 @@ traverse.node = function (node, opts, scope, state, parentPath) { function clearNode(node) { node._declarations = null; node.extendedRange = null; - node._scopeInfo = null; node._paths = null; node.tokens = null; node.range = null; diff --git a/src/babel/traversal/path.js b/src/babel/traversal/path.js index 88bed83108..67f527871b 100644 --- a/src/babel/traversal/path.js +++ b/src/babel/traversal/path.js @@ -1,3 +1,7 @@ +import isBoolean from "lodash/lang/isBoolean"; +import isNumber from "lodash/lang/isNumber"; +import isRegExp from "lodash/lang/isRegExp"; +import isString from "lodash/lang/isString"; import traverse from "./index"; import includes from "lodash/collection/includes"; import Scope from "./scope"; @@ -200,7 +204,104 @@ export default class TraversalPath { } get(key) { - return TraversalPath.get(this, this.context, this.node, this.node, key); + var node = this.node; + var container = node[key]; + if (Array.isArray(container)) { + return container.map((_, i) => { + return TraversalPath.get(this, this.context, node, container, i); + }); + } else { + return TraversalPath.get(this, this.context, node, node, key); + } + } + + has(key) { + return !!this.node[key]; + } + + getTypeAnnotation(): Object { + if (this.typeInfo) { + return this.typeInfo; + } + + var info = this.typeInfo = { + inferred: false, + annotation: null + }; + + var type = this.node.typeAnnotation; + + if (!type) { + info.inferred = true; + type = this.inferType(this); + } + + if (type) { + if (t.isTypeAnnotation(type)) type = type.typeAnnotation; + info.annotation = type; + } + + return info; + } + + resolve(): ?TraversalPath { + if (this.isVariableDeclarator()) { + return this.get("init").resolve(); + } else if (this.isIdentifier()) { + var binding = this.scope.getBinding(this.node.name); + if (!binding) return; + + if (binding.path === this) { + return this; + } else { + return binding.path.resolve();; + } + } else if (this.isMemberExpression()) { + var targetKey = t.toComputedKey(this.node); + if (!t.isLiteral(targetKey)) return; + var targetName = targetKey.value; + + var target = this.get("object").resolve(); + if (!target || !target.isObjectExpression()) return; + + var props = target.get("properties"); + for (var i = 0; i < props.length; i++) { + var prop = props[i]; + if (!prop.isProperty()) continue; + + var key = prop.get("key"); + if (key.isIdentifier({ name: targetName }) || key.isLiteral({ value: targetName })) { + return prop.get("value"); + } + } + } else { + return this; + } + } + + inferType(path: TraversalPath) { + path = path.resolve(); + if (!path) return; + + if (path.isRestElement() || path.isArrayExpression()) { + return t.genericTypeAnnotation(t.identifier("Array")); + } + + if (path.isObjectExpression()) { + return t.genericTypeAnnotation(t.identifier("Object")); + } + + if (path.isLiteral()) { + var value = path.node.value; + if (isString(value)) return t.stringTypeAnnotation(); + if (isNumber(value)) return t.numberTypeAnnotation(); + if (isBoolean(value)) return t.booleanTypeAnnotation(); + } + + if (path.isCallExpression()) { + var callee = path.get("callee").resolve(); + if (callee && callee.isFunction()) return callee.node.returnType; + } } isScope() { @@ -215,13 +316,40 @@ export default class TraversalPath { return t.isReferenced(this.node, this.parent); } + isBlockScoped() { + return t.isBlockScoped(this.node); + } + + isVar() { + return t.isVar(this.node); + } + isScope() { return t.isScope(this.node, this.parent); } + isTypeGeneric(genericName: string, hasTypeParameters?): boolean { + var type = this.getTypeAnnotation().annotation; + if (!type) return false; + + if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) { + return false; + } + + if (hasTypeParameters && !type.typeParameters) { + return false; + } + + return true; + } + getBindingIdentifiers() { return t.getBindingIdentifiers(this.node); } + + traverse(opts, state) { + traverse(this.node, opts, this.scope, state); + } } for (var i = 0; i < t.TYPES.length; i++) { diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index 8aafda19ff..e58da1aabe 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -2,6 +2,7 @@ import includes from "lodash/collection/includes"; import traverse from "./index"; import defaults from "lodash/object/defaults"; import * as messages from "../messages"; +import Binding from "./binding"; import globals from "globals"; import flatten from "lodash/array/flatten"; import extend from "lodash/object/extend"; @@ -12,27 +13,27 @@ import * as t from "../types"; var functionVariableVisitor = { enter(node, parent, scope, state) { if (t.isFor(node)) { - each(t.FOR_INIT_KEYS, function (key) { - var declar = node[key]; - if (t.isVar(declar)) state.scope.registerBinding("var", declar); + each(t.FOR_INIT_KEYS, (key) => { + var declar = this.get(key); + if (declar.isVar()) state.scope.registerBinding("var", declar); }); } // this block is a function so we'll stop since none of the variables // declared within are accessible - if (t.isFunction(node)) return this.skip(); + if (this.isFunction()) return this.skip(); // function identifier doesn't belong to this scope if (state.blockId && node === state.blockId) return; // delegate block scope handling to the `blockVariableVisitor` - if (t.isBlockScoped(node)) return; + if (this.isBlockScoped()) return; // this will be hit again once we traverse into it after this iteration - if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return; + if (this.isExportDeclaration() && t.isDeclaration(node.declaration)) return; // we've ran into a declaration! - if (t.isDeclaration(node)) state.scope.registerDeclaration(node); + if (this.isDeclaration()) state.scope.registerDeclaration(this); } }; @@ -42,16 +43,20 @@ var programReferenceVisitor = { state.addGlobal(node); } else if (t.isLabeledStatement(node)) { state.addGlobal(node); - } else if (t.isAssignmentExpression(node) || t.isUpdateExpression(node) || (t.isUnaryExpression(node) && node.operator === "delete")) { - scope.registerBindingReassignment(node); + } else if (t.isAssignmentExpression(node)) { + scope.registerBindingReassignment(this.get("left"), this.get("right")); + } else if (t.isUpdateExpression(node)) { + // TODO + } else if (t.isUnaryExpression(node) && node.operator === "delete") { + scope.registerBindingReassignment(this.get("left"), null); } } }; var blockVariableVisitor = { enter(node, parent, scope, state) { - if (t.isFunctionDeclaration(node) || t.isBlockScoped(node)) { - state.registerDeclaration(node); + if (this.isFunctionDeclaration() || this.isBlockScoped()) { + state.registerDeclaration(this); } else if (t.isScope(node, parent)) { this.skip(); } @@ -112,9 +117,7 @@ export default class Scope { */ generateUidIdentifier(name: string) { - var id = t.identifier(this.generateUid(name)); - this.getFunctionParent().registerBinding("uid", id); - return id; + return t.identifier(this.generateUid(name)); } /** @@ -129,7 +132,8 @@ export default class Scope { do { uid = this._generateUid(name, i); i++; - } while (this.hasBinding(uid) || this.hasGlobal(uid)); + } while (this.hasBinding(uid) || this.hasGlobal(uid) || this.hasUid(uid)); + this.uids[uid] = true; return uid; } @@ -139,6 +143,19 @@ export default class Scope { return `_${id}`; } + /** + * Description + */ + + hasUid(name): boolean { + var scope = this; + do { + if (scope.uids[name]) return true; + scope = scope.parent; + } while (scope); + return false; + } + /* * Description */ @@ -217,7 +234,7 @@ export default class Scope { rename(oldName: string, newName: string) { newName ||= this.generateUidIdentifier(oldName).name; - var info = this.getBindingInfo(oldName); + var info = this.getBinding(oldName); if (!info) return; var binding = info.identifier; @@ -246,102 +263,6 @@ export default class Scope { binding.name = newName; } - /** - * Description - */ - - inferType(node: Object) { - var target; - - if (t.isVariableDeclarator(node)) { - target = node.init; - } - - if (t.isArrayExpression(target)) { - return t.genericTypeAnnotation(t.identifier("Array")); - } - - if (t.isObjectExpression(target)) { - return; - } - - if (t.isLiteral(target)) { - return; - } - - if (t.isCallExpression(target) && t.isIdentifier(target.callee)) { - var funcInfo = this.getBindingInfo(target.callee.name); - if (funcInfo) { - var funcNode = funcInfo.node; - return !funcInfo.reassigned && t.isFunction(funcNode) && node.returnType; - } - } - - if (t.isIdentifier(target)) { - return; - } - } - - /** - * Description - */ - - isTypeGeneric(name: string, genericName: string) { - var info = this.getBindingInfo(name); - if (!info) return false; - - var type = info.typeAnnotation; - return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName }); - } - - /** - * Description - */ - - assignTypeGeneric(name: string, type: Object) { - this.assignType(name, t.genericTypeAnnotation(t.identifier(type))); - } - - /** - * Description - */ - - assignType(name: string, type: Object) { - var info = this.getBindingInfo(name); - if (!info) return; - - info.typeAnnotation = type; - } - - /** - * Description - */ - - getTypeAnnotation(id: Object, node: Object): Object { - var info = { - annotation: null, - inferred: false - }; - - var type; - - if (id.typeAnnotation) { - type = id.typeAnnotation; - } - - if (!type) { - info.inferred = true; - type = this.inferType(node); - } - - if (type) { - if (t.isTypeAnnotation(type)) type = type.typeAnnotation; - info.annotation = type; - } - - return info; - } - /** * Description */ @@ -349,8 +270,9 @@ export default class Scope { toArray(node: Object, i?: number) { var file = this.file; - if (t.isIdentifier(node) && this.isTypeGeneric(node.name, "Array")) { - return node; + if (t.isIdentifier(node)) { + var binding = this.getBinding(node.name); + if (binding && binding.isTypeGeneric("Array")) return node; } if (t.isArrayExpression(node)) { @@ -376,33 +298,21 @@ export default class Scope { * Description */ - refreshDeclaration(node: Object) { - if (t.isBlockScoped(node)) { - this.getBlockParent().registerDeclaration(node); - } else if (t.isVariableDeclaration(node, { kind: "var" })) { - this.getFunctionParent().registerDeclaration(node); - } else if (node === this.block) { - this.recrawl(); - } - } - - /** - * Description - */ - - registerDeclaration(node: Object) { + registerDeclaration(path: TraversalPath) { + var node = path.node; if (t.isFunctionDeclaration(node)) { - this.registerBinding("hoisted", node); + this.registerBinding("hoisted", path); } else if (t.isVariableDeclaration(node)) { - for (var i = 0; i < node.declarations.length; i++) { - this.registerBinding(node.kind, node.declarations[i]); + var declarations = path.get("declarations"); + for (var i = 0; i < declarations.length; i++) { + this.registerBinding(node.kind, declarations[i]); } } else if (t.isClassDeclaration(node)) { - this.registerBinding("let", node); + this.registerBinding("let", path); } else if (t.isImportDeclaration(node) || t.isExportDeclaration(node)) { - this.registerBinding("module", node); + this.registerBinding("module", path); } else { - this.registerBinding("unknown", node); + this.registerBinding("unknown", path); } } @@ -410,18 +320,16 @@ export default class Scope { * Description */ - registerBindingReassignment(node: Object) { - var ids = t.getBindingIdentifiers(node); + registerBindingReassignment(left: TraversalPath, right: TraversalPath) { + var ids = left.getBindingIdentifiers(); for (var name in ids) { - var info = this.getBindingInfo(name); - if (info) { - info.reassigned = true; - - if (info.typeAnnotationInferred) { - // destroy the inferred typeAnnotation - info.typeAnnotation = null; - } + var binding = this.getBinding(name); + if (!binding) continue; + if (right) { + var rightType = right.typeAnnotation; + if (rightType && binding.isCompatibleWithType(rightType)) continue; } + binding.reassign(); } } @@ -429,27 +337,22 @@ export default class Scope { * Description */ - registerBinding(kind: string, node: Object) { + registerBinding(kind: string, path: TraversalPath) { if (!kind) throw new ReferenceError("no `kind`"); - var ids = t.getBindingIdentifiers(node); + var ids = path.getBindingIdentifiers(); for (var name in ids) { var id = ids[name]; this.checkBlockScopedCollisions(kind, name, id); - var typeInfo = this.getTypeAnnotation(id, node); - - this.bindings[name] = { - typeAnnotationInferred: typeInfo.inferred, - typeAnnotation: typeInfo.annotation, - reassigned: false, - identifier: id, - scope: this, - node: node, - kind: kind - }; + this.bindings[name] = new Binding({ + identifier: id, + scope: this, + path: path, + kind: kind + }); } } @@ -489,91 +392,92 @@ export default class Scope { */ crawl() { - var block = this.block; + var path = this.path; var i; // - var info = this.path.getData("scopeInfo"); + var info = path.getData("scopeInfo"); if (info) { extend(this, info); return; } - info = this.path.setData("scopeInfo", { + info = path.setData("scopeInfo", { bindings: object(), - globals: object() + globals: object(), + uids: object() }); extend(this, info); // ForStatement - left, init - if (t.isLoop(block)) { + if (path.isLoop()) { for (i = 0; i < t.FOR_INIT_KEYS.length; i++) { - var node = block[t.FOR_INIT_KEYS[i]]; - if (t.isBlockScoped(node)) this.registerBinding("let", node); + var node = path.get(t.FOR_INIT_KEYS[i]); + if (node.isBlockScoped()) this.registerBinding("let", node); } - if (t.isBlockStatement(block.body)) { - block = block.body; - } + var body = path.get("body"); + if (body.isBlockStatement()) path = path.get("body"); } // FunctionExpression - id - if (t.isFunctionExpression(block) && block.id) { - if (!t.isProperty(this.parentBlock, { method: true })) { - this.registerBinding("var", block.id); + if (path.isFunctionExpression() && path.has("id")) { + if (!t.isProperty(path.parent, { method: true })) { + this.registerBinding("var", path.get("id")); } } // Class - if (t.isClass(block) && block.id) { - this.registerBinding("var", block.id); + if (path.isClass() && path.has("id")) { + this.registerBinding("var", path.get("id")); } // Function - params, rest - if (t.isFunction(block)) { - for (i = 0; i < block.params.length; i++) { - this.registerBinding("param", block.params[i]); + if (path.isFunction()) { + var params = path.get("params"); + for (i = 0; i < params.length; i++) { + this.registerBinding("param", params[i]); } - this.traverse(block.body, blockVariableVisitor, this); + this.traverse(path.get("body"), blockVariableVisitor, this); } // Program, BlockStatement, Function - let variables - if (t.isBlockStatement(block) || t.isProgram(block)) { - this.traverse(block, blockVariableVisitor, this); + if (path.isBlockStatement() || path.isProgram()) { + this.traverse(path.node, blockVariableVisitor, this); } // CatchClause - param - if (t.isCatchClause(block)) { - this.registerBinding("let", block.param); + if (path.isCatchClause()) { + this.registerBinding("let", path.get("param")); } // ComprehensionExpression - blocks - if (t.isComprehensionExpression(block)) { - this.registerBinding("let", block); + if (path.isComprehensionExpression()) { + this.registerBinding("let", path); } // Program, Function - var variables - if (t.isProgram(block) || t.isFunction(block)) { - this.traverse(block, functionVariableVisitor, { - blockId: block.id, + if (path.isProgram() || path.isFunction()) { + this.traverse(path.node, functionVariableVisitor, { + blockId: path.get("id").node, scope: this }); } // Program - if (t.isProgram(block)) { - this.traverse(block, programReferenceVisitor, this); + if (path.isProgram()) { + this.traverse(path.node, programReferenceVisitor, this); } } @@ -674,7 +578,7 @@ export default class Scope { * Description */ - getBindingInfo(name: string) { + getBinding(name: string) { var scope = this; do { @@ -696,7 +600,7 @@ export default class Scope { */ getBindingIdentifier(name: string) { - var info = this.getBindingInfo(name); + var info = this.getBinding(name); return info && info.identifier; } @@ -722,29 +626,11 @@ export default class Scope { */ getImmutableBindingValue(name: string) { - return this._immutableBindingInfoToValue(this.getBindingInfo(name)); + return this._immutableBindingInfoToValue(this.getBinding(name)); } - _immutableBindingInfoToValue(info) { - if (!info) return; - - // can't guarantee this value is the same - if (info.reassigned) return; - - var node = info.node; - if (t.isVariableDeclarator(node)) { - if (t.isIdentifier(node.id)) { - node = node.init; - } else { - // otherwise it's probably a destructuring like: - // var { foo } = "foo"; - return; - } - } - - if (t.isImmutable(node)) { - return node; - } + _immutableBindingInfoToValue(binding) { + if (binding) return binding.getValueIfImmutable(); } /** @@ -789,7 +675,7 @@ export default class Scope { */ removeBinding(name: string) { - var info = this.getBindingInfo(name); + var info = this.getBinding(name); if (info) info.scope.removeOwnBinding(name); } } diff --git a/src/babel/types/converters.js b/src/babel/types/converters.js index ea3a240ff3..429e9c45dd 100644 --- a/src/babel/types/converters.js +++ b/src/babel/types/converters.js @@ -10,7 +10,7 @@ import * as t from "./index"; * Description */ -export function toComputedKey(node: Object, key: Object = node.key): Object { +export function toComputedKey(node: Object, key: Object = node.key || node.property): Object { if (!node.computed) { if (t.isIdentifier(key)) key = t.literal(key.name); } diff --git a/src/babel/types/index.js b/src/babel/types/index.js index dfbdf9b07e..79293f0694 100644 --- a/src/babel/types/index.js +++ b/src/babel/types/index.js @@ -281,7 +281,6 @@ export function inheritsComments(child: Object, parent: Object): Object { export function inherits(child: Object, parent: Object): Object { child._declarations = parent._declarations; - child._scopeInfo = parent._scopeInfo; child.range = parent.range; child.start = parent.start; child.loc = parent.loc; From 41a825700580960c236aa7fff9a73e92a9045100 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 13 Mar 2015 01:09:04 +1100 Subject: [PATCH 04/24] various cleanup, remove redundant whitespace, realign object keys etc --- src/babel/generation/generators/modules.js | 8 +++++--- src/babel/transformation/helpers/name-method.js | 1 - src/babel/transformation/transformers/es6/classes.js | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/babel/generation/generators/modules.js b/src/babel/generation/generators/modules.js index b7241adb3a..2218e90486 100644 --- a/src/babel/generation/generators/modules.js +++ b/src/babel/generation/generators/modules.js @@ -66,8 +66,10 @@ export function ImportDeclaration(node, print) { if (specfiers && specfiers.length) { var foundImportSpecifier = false; - each(node.specifiers, (spec, i) => { - if (+i > 0) { + for (var i = 0; i < node.specifiers.length; i++) { + var spec = node.specifiers[i]; + + if (i > 0) { this.push(", "); } @@ -79,7 +81,7 @@ export function ImportDeclaration(node, print) { } print(spec); - }); + } if (foundImportSpecifier) { this.push(" }"); diff --git a/src/babel/transformation/helpers/name-method.js b/src/babel/transformation/helpers/name-method.js index de2d8f423d..d7f8d3b6f1 100644 --- a/src/babel/transformation/helpers/name-method.js +++ b/src/babel/transformation/helpers/name-method.js @@ -123,7 +123,6 @@ export function bare(node, parent, scope) { var name; if (t.isLiteral(id)) { - console.log(id); name = id.value; } else if (t.isIdentifier(id)) { name = id.name; diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index f0ccc38c5f..862e498d6f 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -250,8 +250,6 @@ class ClassTransformer { */ verifyConstructor(node: Object) { - return; // enable this for the next major - var state = { hasBareSuper: false, hasSuper: this.hasSuper, From c906bd3edc391cd2b0eeffa260e3611a772a789c Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 01:00:02 +1100 Subject: [PATCH 05/24] start migration of core from nodes to paths --- src/babel/transformation/file.js | 4 +- .../helpers/build-react-transformer.js | 2 +- .../transformation/helpers/replace-supers.js | 2 +- src/babel/transformation/modules/_default.js | 14 +- src/babel/transformation/transformer-pass.js | 3 +- .../transformers/es6/block-scoping-tdz.js | 2 +- .../transformers/es6/block-scoping.js | 4 +- .../transformers/es6/classes.js | 35 ++-- .../transformers/es6/constants.js | 2 +- .../transformation/transformers/es6/for-of.js | 10 +- .../transformers/es6/modules.js | 2 +- .../transformers/es6/parameters.default.js | 27 ++-- .../transformers/es6/parameters.rest.js | 4 +- .../transformation/transformers/es6/spread.js | 4 +- .../transformers/es6/symbols.js | 2 +- .../transformers/es6/tail-call.js | 19 +-- .../transformation/transformers/index.js | 1 + .../transformers/internal/alias-functions.js | 14 +- .../transformers/internal/modules.js | 2 +- .../transformers/optimisation/flow.for-of.js | 11 ++ .../transformation/transformers/other/flow.js | 2 +- .../transformers/other/runtime.js | 8 +- .../playground/object-getter-memoization.js | 5 +- .../utility/dead-code-elimination.js | 5 +- .../utility/inline-expressions.js | 2 +- .../transformers/utility/remove-console.js | 4 +- .../transformers/validation/react.js | 2 +- src/babel/traversal/binding.js | 24 --- src/babel/traversal/index.js | 8 +- .../path/evaluation.js} | 62 +++---- .../traversal/{path.js => path/index.js} | 151 ++++++++++++++---- src/babel/traversal/scope.js | 26 +-- src/babel/types/index.js | 1 - src/babel/types/visitor-keys.json | 2 +- .../array-expression-single/actual.js | 2 +- .../array-expression-single/expected.js | 4 +- .../es7-comprehensions/array-this/actual.js | 2 +- .../es7-comprehensions/array-this/expected.js | 4 +- 38 files changed, 273 insertions(+), 205 deletions(-) create mode 100644 src/babel/transformation/transformers/optimisation/flow.for-of.js rename src/babel/{types/evaluators.js => traversal/path/evaluation.js} (65%) rename src/babel/traversal/{path.js => path/index.js} (68%) diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file.js index 3b8b1abf43..0cd16c2aa5 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file.js @@ -464,7 +464,7 @@ export default class File { this.scope = this.path.scope; this.ast = ast; - traverse(ast, { + this.path.traverse({ enter(node, parent, scope) { if (this.isScope()) { for (var key in scope.bindings) { @@ -472,7 +472,7 @@ export default class File { } } } - }, this.scope); + }); } transform(ast) { diff --git a/src/babel/transformation/helpers/build-react-transformer.js b/src/babel/transformation/helpers/build-react-transformer.js index 02b9ba1ffe..ccef9d819b 100644 --- a/src/babel/transformation/helpers/build-react-transformer.js +++ b/src/babel/transformation/helpers/build-react-transformer.js @@ -27,7 +27,7 @@ export default function (exports, opts) { }; exports.JSXNamespacedName = function (node, parent, scope, file) { - throw file.errorWithNode(node, messages.get("JSXNamespacedTags")); + throw this.errorWithNode(messages.get("JSXNamespacedTags")); }; exports.JSXMemberExpression = { diff --git a/src/babel/transformation/helpers/replace-supers.js b/src/babel/transformation/helpers/replace-supers.js index e60cbabcb4..0796a21300 100644 --- a/src/babel/transformation/helpers/replace-supers.js +++ b/src/babel/transformation/helpers/replace-supers.js @@ -219,7 +219,7 @@ export default class ReplaceSupers { var thisReference; if (isIllegalBareSuper(node, parent)) { - throw this.file.errorWithNode(node, messages.get("classesIllegalBareSuper")); + throw this.errorWithNode(messages.get("classesIllegalBareSuper")); } if (t.isCallExpression(node)) { diff --git a/src/babel/transformation/modules/_default.js b/src/babel/transformation/modules/_default.js index a60d3b7507..2158b8525f 100644 --- a/src/babel/transformation/modules/_default.js +++ b/src/babel/transformation/modules/_default.js @@ -45,7 +45,7 @@ var importsVisitor = { ImportDeclaration: { enter(node, parent, scope, formatter) { formatter.hasLocalImports = true; - extend(formatter.localImports, t.getBindingIdentifiers(node)); + extend(formatter.localImports, this.getBindingIdentifiers()); formatter.bumpImportOccurences(node); } } @@ -54,11 +54,11 @@ var importsVisitor = { var exportsVisitor = { ExportDeclaration: { enter(node, parent, scope, formatter) { - var declar = node.declaration; + var declar = this.get("declaration"); formatter.hasLocalImports = true; - if (declar && t.isStatement(declar)) { - extend(formatter.localExports, t.getBindingIdentifiers(declar)); + if (declar.isStatement()) { + extend(formatter.localExports, declar.getBindingIdentifiers()); } if (!node.default) { @@ -105,16 +105,16 @@ export default class DefaultFormatter { } getLocalExports() { - this.file.scope.traverse(this.file.ast, exportsVisitor, this); + this.file.path.traverse(exportsVisitor, this); } getLocalImports() { - this.file.scope.traverse(this.file.ast, importsVisitor, this); + this.file.path.traverse(importsVisitor, this); } remapAssignments() { if (this.hasLocalImports) { - this.file.scope.traverse(this.file.ast, remapVisitor, this); + this.file.path.traverse(remapVisitor, this); } } diff --git a/src/babel/transformation/transformer-pass.js b/src/babel/transformation/transformer-pass.js index 3beac40bc8..dd8fab0431 100644 --- a/src/babel/transformation/transformer-pass.js +++ b/src/babel/transformation/transformer-pass.js @@ -1,4 +1,5 @@ import includes from "lodash/collection/includes"; +import traverse from "../traversal"; /** * This class is responsible for traversing over the provided `File`s @@ -59,7 +60,7 @@ export default class TransformerPass { file.log.debug(`Running transformer ${this.transformer.key}`); - file.scope.traverse(file.ast, this.handlers, file); + traverse(file.ast, this.handlers, file.scope, file); this.ran = true; } diff --git a/src/babel/transformation/transformers/es6/block-scoping-tdz.js b/src/babel/transformation/transformers/es6/block-scoping-tdz.js index d18333a192..0eee6bc324 100644 --- a/src/babel/transformation/transformers/es6/block-scoping-tdz.js +++ b/src/babel/transformation/transformers/es6/block-scoping-tdz.js @@ -32,7 +32,7 @@ export function BlockStatement(node, parent, scope, file) { var letRefs = node._letReferences; if (!letRefs) return; - scope.traverse(node, visitor, { + this.traverse(visitor, { letRefs: letRefs, file: file }); diff --git a/src/babel/transformation/transformers/es6/block-scoping.js b/src/babel/transformation/transformers/es6/block-scoping.js index 79eb0380c7..ee7e87402f 100644 --- a/src/babel/transformation/transformers/es6/block-scoping.js +++ b/src/babel/transformation/transformers/es6/block-scoping.js @@ -111,7 +111,7 @@ function traverseReplace(node, parent, scope, remaps) { var letReferenceBlockVisitor = { enter(node, parent, scope, state) { if (this.isFunction()) { - scope.traverse(node, letReferenceFunctionVisitor, state); + this.traverse(letReferenceFunctionVisitor, state); return this.skip(); } } @@ -173,7 +173,7 @@ var loopVisitor = { if (this.isLoop()) { state.ignoreLabeless = true; - scope.traverse(node, loopVisitor, state); + this.traverse(loopVisitor, state); state.ignoreLabeless = false; } diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index 862e498d6f..bf66ef32f1 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -15,7 +15,7 @@ export function ClassDeclaration(node, parent, scope, file) { } export function ClassExpression(node, parent, scope, file) { - return new ClassTransformer(node, parent, scope, file).run(); + return new ClassTransformer(this, file).run(); } var verifyConstructorVisitor = traverse.explode({ @@ -37,7 +37,7 @@ var verifyConstructorVisitor = traverse.explode({ state.hasBareSuper = true; if (!state.hasSuper) { - throw state.file.errorWithNode(node, "super call is only allowed in derived constructor"); + throw this.errorWithNode("super call is only allowed in derived constructor"); } } } @@ -46,7 +46,7 @@ var verifyConstructorVisitor = traverse.explode({ ThisExpression: { enter(node, parent, scope, state) { if (state.hasSuper && !state.hasBareSuper) { - throw state.file.errorWithNode(node, "'this' is not allowed before super()"); + throw this.errorWithNode("'this' is not allowed before super()"); } } } @@ -58,10 +58,11 @@ class ClassTransformer { * Description */ - constructor(node: Object, parent: Object, scope: Scope, file: File) { - this.parent = parent; - this.scope = scope; - this.node = node; + constructor(path: TraversalPath, file: File) { + this.parent = path.parent; + this.scope = path.scope; + this.node = path.node; + this.path = path; this.file = file; this.hasInstanceMutators = false; @@ -71,11 +72,11 @@ class ClassTransformer { this.staticMutatorMap = {}; this.hasConstructor = false; - this.className = node.id; - this.classRef = node.id || scope.generateUidIdentifier("class"); + this.className = this.node.id; + this.classRef = this.node.id || this.scope.generateUidIdentifier("class"); - this.superName = node.superClass || t.identifier("Function"); - this.hasSuper = !!node.superClass; + this.superName = this.node.superClass || t.identifier("Function"); + this.hasSuper = !!this.node.superClass; this.isLoose = file.isLoose("es6.classes"); } @@ -174,11 +175,13 @@ class ClassTransformer { var classBody = this.node.body.body; var body = this.body; + var classBodyPaths = this.path.get("body").get("body"); + for (var i = 0; i < classBody.length; i++) { var node = classBody[i]; if (t.isMethodDefinition(node)) { var isConstructor = (!node.computed && t.isIdentifier(node.key, { name: "constructor" })) || t.isLiteral(node.key, { value: "constructor" }); - if (isConstructor) this.verifyConstructor(node); + if (isConstructor) this.verifyConstructor(classBodyPaths[i]); var replaceSupers = new ReplaceSupers({ methodNode: node, @@ -206,7 +209,7 @@ class ClassTransformer { } // we have no constructor, we have a super, and the super doesn't appear to be falsy - if (!this.hasConstructor && this.hasSuper && t.evaluateTruthy(superName, this.scope) !== false) { + if (!this.hasConstructor && this.hasSuper) { // todo: t.evaluateTruthy(superName, this.scope) !== false var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; constructor.body.body.push(util.template(helperName, { @@ -249,17 +252,17 @@ class ClassTransformer { * Description */ - verifyConstructor(node: Object) { + verifyConstructor(path: TraversalPath) { var state = { hasBareSuper: false, hasSuper: this.hasSuper, file: this.file }; - traverse(node, verifyConstructorVisitor, this.scope, state); + path.traverse(verifyConstructorVisitor, state); if (!state.hasBareSuper && this.hasSuper) { - throw this.file.errorWithNode(node, "Derived constructor must call super()"); + throw path.errorWithNode("Derived constructor must call super()"); } } diff --git a/src/babel/transformation/transformers/es6/constants.js b/src/babel/transformation/transformers/es6/constants.js index fc29e8c916..4a4e5c95d2 100644 --- a/src/babel/transformation/transformers/es6/constants.js +++ b/src/babel/transformation/transformers/es6/constants.js @@ -37,7 +37,7 @@ var visitor = { }; export function Scopable(node, parent, scope, file) { - scope.traverse(node, visitor, { + this.traverse(visitor, { constants: scope.getAllBindingsOfKind("const"), file: file }); diff --git a/src/babel/transformation/transformers/es6/for-of.js b/src/babel/transformation/transformers/es6/for-of.js index addd609af5..42c57d5859 100644 --- a/src/babel/transformation/transformers/es6/for-of.js +++ b/src/babel/transformation/transformers/es6/for-of.js @@ -5,12 +5,10 @@ import * as t from "../../../types"; export var check = t.isForOfStatement; export function ForOfStatement(node, parent, scope, file) { - if (this.get("right").isTypeGeneric("Array")) { - return array(node, scope, file); + if (this.get("right").isArrayExpression()) { + return _ForOfStatementArray.call(this, node, scope, file); } - // - var callback = spec; if (file.isLoose("es6.forOf")) callback = loose; @@ -42,7 +40,7 @@ export function ForOfStatement(node, parent, scope, file) { } } -var array = function (node, scope, file) { +export function _ForOfStatementArray(node, scope, file) { var nodes = []; var right = node.right; @@ -78,7 +76,7 @@ var array = function (node, scope, file) { nodes.push(loop); return nodes; -}; +} var loose = function (node, parent, scope, file) { var left = node.left; diff --git a/src/babel/transformation/transformers/es6/modules.js b/src/babel/transformation/transformers/es6/modules.js index 0eab992dfe..cb583622d6 100644 --- a/src/babel/transformation/transformers/es6/modules.js +++ b/src/babel/transformation/transformers/es6/modules.js @@ -26,7 +26,7 @@ export function ImportDeclaration(node, parent, scope, file) { export function ExportDeclaration(node, parent, scope, file) { // flow type - if (t.isTypeAlias(node.declaration)) return; + if (this.get("declaration").isTypeAlias()) return; var nodes = []; var i; diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 9e525c87d3..82dd26e20e 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -49,41 +49,42 @@ exports.Function = function (node, parent, scope, file) { body.push(defNode); }; - for (var i = 0; i < node.params.length; i++) { - var param = node.params[i]; + var params = this.get("params"); + for (var i = 0; i < params.length; i++) { + var param = params[i]; - if (!t.isAssignmentPattern(param)) { - if (!t.isRestElement(param)) { + if (!param.isAssignmentPattern()) { + if (!param.isRestElement()) { lastNonDefaultParam = i + 1; } - if (!t.isIdentifier(param)) { - scope.traverse(param, iifeVisitor, state); + if (!param.isIdentifier()) { + param.traverse(iifeVisitor, state); } - if (file.transformers["es6.blockScopingTDZ"].canRun() && t.isIdentifier(param)) { - pushDefNode(param, t.identifier("undefined"), i); + if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) { + pushDefNode(param.node, t.identifier("undefined"), i); } continue; } - var left = param.left; - var right = param.right; + var left = param.get("left"); + var right = param.get("right"); var placeholder = scope.generateUidIdentifier("x"); placeholder._isDefaultPlaceholder = true; node.params[i] = placeholder; if (!state.iife) { - if (t.isIdentifier(right) && scope.hasOwnBinding(right.name)) { + if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) { state.iife = true; } else { - scope.traverse(right, iifeVisitor, state); + right.traverse(iifeVisitor, state); } } - pushDefNode(left, right, i); + pushDefNode(left.node, right.node, i); } // we need to cut off all trailing default parameters diff --git a/src/babel/transformation/transformers/es6/parameters.rest.js b/src/babel/transformation/transformers/es6/parameters.rest.js index e79af4418d..d0f967a8f7 100644 --- a/src/babel/transformation/transformers/es6/parameters.rest.js +++ b/src/babel/transformation/transformers/es6/parameters.rest.js @@ -15,7 +15,7 @@ var memberExpressionOptimisationVisitor = { // to the wrong function if (this.isFunctionDeclaration() || this.isFunctionExpression()) { state.noOptimise = true; - scope.traverse(node, memberExpressionOptimisationVisitor, state); + this.traverse(memberExpressionOptimisationVisitor, state); state.noOptimise = false; return this.skip(); } @@ -88,7 +88,7 @@ exports.Function = function (node, parent, scope, file) { name: rest.name }; - scope.traverse(node, memberExpressionOptimisationVisitor, state); + this.traverse(memberExpressionOptimisationVisitor, state); // we only have shorthands and there's no other references if (state.canOptimise && state.candidates.length) { diff --git a/src/babel/transformation/transformers/es6/spread.js b/src/babel/transformation/transformers/es6/spread.js index 4fe2b7d238..693553ded9 100644 --- a/src/babel/transformation/transformers/es6/spread.js +++ b/src/babel/transformation/transformers/es6/spread.js @@ -81,7 +81,7 @@ export function CallExpression(node, parent, scope) { var callee = node.callee; - if (t.isMemberExpression(callee)) { + if (this.get("callee").isMemberExpression()) { var temp = scope.generateTempBasedOnNode(callee.object); if (temp) { callee.object = t.assignmentExpression("=", temp, callee.object); @@ -101,7 +101,7 @@ export function NewExpression(node, parent, scope, file) { var args = node.arguments; if (!hasSpread(args)) return; - var nativeType = t.isIdentifier(node.callee) && includes(t.NATIVE_TYPE_NAMES, node.callee.name); + var nativeType = this.get("callee").isIdentifier() && includes(t.NATIVE_TYPE_NAMES, node.callee.name); var nodes = build(args, scope); diff --git a/src/babel/transformation/transformers/es6/symbols.js b/src/babel/transformation/transformers/es6/symbols.js index fd3fe51b7e..03f7ab491f 100644 --- a/src/babel/transformation/transformers/es6/symbols.js +++ b/src/babel/transformation/transformers/es6/symbols.js @@ -7,7 +7,7 @@ export function UnaryExpression(node, parent, scope, file) { if (node.operator === "typeof") { var call = t.callExpression(file.addHelper("typeof"), [node.argument]); - if (t.isIdentifier(node.argument)) { + if (this.get("argument").isIdentifier()) { var undefLiteral = t.literal("undefined"); return t.conditionalExpression( t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral), diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 563ad8bd00..122f456f2a 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -6,7 +6,7 @@ import map from "lodash/collection/map"; import * as t from "../../../types"; exports.Function = function (node, parent, scope, file) { - var tailCall = new TailCallTransformer(node, scope, file); + var tailCall = new TailCallTransformer(this, scope, file); tailCall.run(); }; @@ -18,11 +18,11 @@ function returnBlock(expr) { var firstPass = { enter(node, parent, scope, state) { if (this.isIfStatement()) { - if (t.isReturnStatement(node.alternate)) { + if (this.get("alternate").isReturnStatement()) { t.ensureBlock(node, "alternate"); } - if (t.isReturnStatement(node.consequent)) { + if (this.get("consequent").isReturnStatement()) { t.ensureBlock(node, "consequent"); } } else if (this.isReturnStatement()) { @@ -85,17 +85,18 @@ var thirdPass = { }; class TailCallTransformer { - constructor(node, scope, file) { + constructor(path, scope, file) { this.hasTailRecursion = false; this.needsArguments = false; this.setsArguments = false; this.needsThis = false; - this.ownerId = node.id; + this.ownerId = path.node.id; this.vars = []; this.scope = scope; + this.path = path; this.file = file; - this.node = node; + this.node = path.node; } getArgumentsId() { @@ -156,7 +157,7 @@ class TailCallTransformer { if (!ownerId) return; // traverse the function and look for tail recursion - scope.traverse(node, firstPass, this); + this.path.traverse(firstPass, this); if (!this.hasTailRecursion) return; @@ -167,10 +168,10 @@ class TailCallTransformer { // - scope.traverse(node, secondPass, this); + this.path.traverse(secondPass, this); if (!this.needsThis || !this.needsArguments) { - scope.traverse(node, thirdPass, this); + this.path.traverse(thirdPass, this); } var body = t.ensureBlock(node).body; diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index c1098afeb7..9b008d23c6 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -44,6 +44,7 @@ export default { // needs to be before `_aliasFunction` due to define property closure "es6.properties.computed": require("./es6/properties.computed"), + "optimisation.es6.forOf": require("./optimisation/flow.for-of"), "es6.forOf": require("./es6/for-of"), "es6.regex.sticky": require("./es6/regex.sticky"), diff --git a/src/babel/transformation/transformers/internal/alias-functions.js b/src/babel/transformation/transformers/internal/alias-functions.js index b76e19ae50..649edd0c38 100644 --- a/src/babel/transformation/transformers/internal/alias-functions.js +++ b/src/babel/transformation/transformers/internal/alias-functions.js @@ -34,13 +34,13 @@ var functionVisitor = { } // traverse all child nodes of this function and find `arguments` and `this` - scope.traverse(node, functionChildrenVisitor, state); + this.traverse(functionChildrenVisitor, state); return this.skip(); } }; -var go = function (getBody, node, scope) { +function aliasFunction(getBody, path, scope) { var argumentsId; var thisId; @@ -56,7 +56,7 @@ var go = function (getBody, node, scope) { // traverse the function and find all alias functions so we can alias // `arguments` and `this` if necessary - scope.traverse(node, functionVisitor, state); + path.traverse(functionVisitor, state); var body; @@ -77,16 +77,16 @@ var go = function (getBody, node, scope) { }; export function Program(node, parent, scope) { - go(function () { + aliasFunction(function () { return node.body; - }, node, scope); + }, this, scope); }; export function FunctionDeclaration(node, parent, scope) { - go(function () { + aliasFunction(function () { t.ensureBlock(node); return node.body.body; - }, node, scope); + }, this, scope); } export { FunctionDeclaration as FunctionExpression }; diff --git a/src/babel/transformation/transformers/internal/modules.js b/src/babel/transformation/transformers/internal/modules.js index 7ef5bc5302..69de42f4d5 100644 --- a/src/babel/transformation/transformers/internal/modules.js +++ b/src/babel/transformation/transformers/internal/modules.js @@ -59,7 +59,7 @@ export function ExportDeclaration(node, parent, scope) { } else if (t.isVariableDeclaration(declar)) { // export var foo = "bar"; var specifiers = []; - var bindings = t.getBindingIdentifiers(declar); + var bindings = this.get("declaration").getBindingIdentifiers(); for (var key in bindings) { var id = bindings[key]; specifiers.push(t.exportSpecifier(id, id)); diff --git a/src/babel/transformation/transformers/optimisation/flow.for-of.js b/src/babel/transformation/transformers/optimisation/flow.for-of.js new file mode 100644 index 0000000000..5e0237d700 --- /dev/null +++ b/src/babel/transformation/transformers/optimisation/flow.for-of.js @@ -0,0 +1,11 @@ +import { _ForOfStatementArray } from "../es6/for-of"; +import * as t from "../../../types"; + +export var check = t.isForOfStatement; +export var optional = true; + +export function ForOfStatement(node, parent, scope, file) { + if (this.get("right").isTypeGeneric("Array")) { + return _ForOfStatementArray.call(this, node, scope, file); + } +} diff --git a/src/babel/transformation/transformers/other/flow.js b/src/babel/transformation/transformers/other/flow.js index 4f8126f9f9..bb6bf4c393 100644 --- a/src/babel/transformation/transformers/other/flow.js +++ b/src/babel/transformation/transformers/other/flow.js @@ -29,5 +29,5 @@ export function ImportDeclaration(node) { } export function ExportDeclaration(node) { - if (t.isTypeAlias(node.declaration)) this.remove(); + if (this.get("declaration").isTypeAlias()) this.remove(); } diff --git a/src/babel/transformation/transformers/other/runtime.js b/src/babel/transformation/transformers/other/runtime.js index a137603e8c..ec2de99541 100644 --- a/src/babel/transformation/transformers/other/runtime.js +++ b/src/babel/transformation/transformers/other/runtime.js @@ -4,7 +4,7 @@ import core from "core-js/library"; import has from "lodash/object/has"; import * as t from "../../../types"; -var isSymboliterator = t.buildMatchMemberExpression("Symbol.iterator"); +var isSymbolIterator = t.buildMatchMemberExpression("Symbol.iterator"); var coreHas = function (node) { return node.name !== "_" && has(core, node.name); @@ -47,7 +47,7 @@ var astVisitor = { if (!callee.computed) return false; prop = callee.property; - if (!isSymboliterator(prop)) return false; + if (!isSymbolIterator(prop)) return false; return util.template("corejs-iterator", { CORE_ID: file.get("coreIdentifier"), @@ -59,7 +59,7 @@ var astVisitor = { if (node.operator !== "in") return; var left = node.left; - if (!isSymboliterator(left)) return; + if (!isSymbolIterator(left)) return; return util.template("corejs-is-iterator", { CORE_ID: file.get("coreIdentifier"), @@ -76,7 +76,7 @@ export function manipulateOptions(opts) { } export function Program(node, parent, scope, file) { - scope.traverse(node, astVisitor, file); + this.traverse(astVisitor, file); } export function pre(file) { diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js index af5ffe8c4c..5cfaf9c61a 100644 --- a/src/babel/transformation/transformers/playground/object-getter-memoization.js +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -20,8 +20,7 @@ export function MethodDefinition(node, parent, scope, file) { if (node.kind !== "memo") return; node.kind = "get"; - var value = node.value; - t.ensureBlock(value); + t.ensureBlock(node.value); var key = node.key; @@ -34,7 +33,7 @@ export function MethodDefinition(node, parent, scope, file) { file: file }; - scope.traverse(value, visitor, state); + this.get("value").traverse(visitor, state); return node; } diff --git a/src/babel/transformation/transformers/utility/dead-code-elimination.js b/src/babel/transformation/transformers/utility/dead-code-elimination.js index 3dd7252ae3..bda19f668d 100644 --- a/src/babel/transformation/transformers/utility/dead-code-elimination.js +++ b/src/babel/transformation/transformers/utility/dead-code-elimination.js @@ -20,7 +20,7 @@ function toStatements(node) { export var optional = true; export function ConditionalExpression(node, parent, scope) { - var evaluateTest = t.evaluateTruthy(node.test, scope); + var evaluateTest = this.get("test").evaluateTruthy(); if (evaluateTest === true) { return node.consequent; } else if (evaluateTest === false) { @@ -32,9 +32,8 @@ export var IfStatement = { exit(node, parent, scope) { var consequent = node.consequent; var alternate = node.alternate; - var test = node.test; - var evaluateTest = t.evaluateTruthy(test, scope); + 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 diff --git a/src/babel/transformation/transformers/utility/inline-expressions.js b/src/babel/transformation/transformers/utility/inline-expressions.js index 9b77422502..f1be106014 100644 --- a/src/babel/transformation/transformers/utility/inline-expressions.js +++ b/src/babel/transformation/transformers/utility/inline-expressions.js @@ -3,7 +3,7 @@ import * as t from "../../../types"; export var optional = true; export function Expression(node, parent, scope) { - var res = t.evaluate(node, scope); + var res = this.evaluate(); if (res.confident) return t.valueToNode(res.value); } diff --git a/src/babel/transformation/transformers/utility/remove-console.js b/src/babel/transformation/transformers/utility/remove-console.js index 791b17c9f4..9fe1f491a9 100644 --- a/src/babel/transformation/transformers/utility/remove-console.js +++ b/src/babel/transformation/transformers/utility/remove-console.js @@ -1,11 +1,9 @@ import * as t from "../../../types"; -var isConsole = t.buildMatchMemberExpression("console", true); - export var optional = true; export function CallExpression(node, parent) { - if (isConsole(node.callee)) { + if (this.get("callee").matchesPattern("console", true)) { if (t.isExpressionStatement(parent)) { this.parentPath.remove(); } else { diff --git a/src/babel/transformation/transformers/validation/react.js b/src/babel/transformation/transformers/validation/react.js index f8cbc58ffb..536aff1768 100644 --- a/src/babel/transformation/transformers/validation/react.js +++ b/src/babel/transformation/transformers/validation/react.js @@ -14,7 +14,7 @@ function check(source, file) { } export function CallExpression(node, parent, scope, file) { - if (t.isIdentifier(node.callee, { name: "require" }) && node.arguments.length === 1) { + if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) { check(node.arguments[0], file); } } diff --git a/src/babel/traversal/binding.js b/src/babel/traversal/binding.js index 95f45dace7..fe4d29a096 100644 --- a/src/babel/traversal/binding.js +++ b/src/babel/traversal/binding.js @@ -58,30 +58,6 @@ export default class Binding { } } - /** - * Description - */ - - getValueIfImmutable() { - // can't guarantee this value is the same - if (this.reassigned) return; - - var node = this.path.node; - if (t.isVariableDeclarator(node)) { - if (t.isIdentifier(node.id)) { - node = node.init; - } else { - // otherwise it's probably a destructuring like: - // var { foo } = "foo"; - return; - } - } - - if (t.isImmutable(node)) { - return node; - } - } - /** * Description */ diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index b2befe1823..c36162f7df 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -1,10 +1,8 @@ -module.exports = traverse; - import TraversalContext from "./context"; import includes from "lodash/collection/includes"; import * as t from "../types"; -function traverse(parent, opts, scope, state) { +export default function traverse(parent, opts, scope, state, parentPath) { if (!parent) return; if (!opts.noScope && !scope) { @@ -20,10 +18,10 @@ function traverse(parent, opts, scope, state) { // array of nodes if (Array.isArray(parent)) { for (var i = 0; i < parent.length; i++) { - traverse.node(parent[i], opts, scope, state); + traverse.node(parent[i], opts, scope, state, parentPath); } } else { - traverse.node(parent, opts, scope, state); + traverse.node(parent, opts, scope, state, parentPath); } } diff --git a/src/babel/types/evaluators.js b/src/babel/traversal/path/evaluation.js similarity index 65% rename from src/babel/types/evaluators.js rename to src/babel/traversal/path/evaluation.js index 163de2581e..2070ab5d2b 100644 --- a/src/babel/types/evaluators.js +++ b/src/babel/traversal/path/evaluation.js @@ -1,5 +1,3 @@ -import * as t from "./index"; - /** * Walk the input `node` and statically evaluate if it's truthy. * @@ -18,15 +16,15 @@ import * as t from "./index"; * */ -export function evaluateTruthy(node: Object, scope: Scope): boolean { - var res = evaluate(node, scope); +export function evaluateTruthy(): boolean { + var res = this.evaluate(); if (res.confident) return !!res.value; } /** * Walk the input `node` and statically evaluate it. * - * Returns an pbject in the form `{ confident, value }`. `confident` indicates + * Returns an object in the form `{ confident, value }`. `confident` indicates * whether or not we had to drop out of evaluating the expression because of * hitting an unknown node that we couldn't confidently find the value of. * @@ -38,24 +36,27 @@ export function evaluateTruthy(node: Object, scope: Scope): boolean { * */ -export function evaluate(node: Object, scope: Scope): { confident: boolean; value: any } { +export function evaluate(): { confident: boolean; value: any } { var confident = true; - var value = evaluate(node); + var value = evaluate(this); if (!confident) value = undefined; return { confident: confident, value: value }; - function evaluate(node) { + function evaluate(path) { if (!confident) return; - if (t.isSequenceExpression(node)) { - return evaluate(node.expressions[node.expressions.length - 1]); + var node = path.node; + + if (path.isSequenceExpression()) { + var exprs = path.get("expressions"); + return evaluate(exprs[exprs.length - 1]); } - if (t.isLiteral(node)) { + if (path.isLiteral()) { if (node.regex && node.value === null) { // we have a regex and we can't represent it natively } else { @@ -63,24 +64,29 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isConditionalExpression(node)) { - if (evaluate(node.test)) { - return evaluate(node.consequent); + if (path.isConditionalExpression()) { + if (evaluate(path.get("test"))) { + return evaluate(path.get("consequent")); } else { - return evaluate(node.alternate); + return evaluate(path.get("alternate")); } } - if (t.isIdentifier(node)) { - if (node.name === "undefined") { - return undefined; + if (path.isIdentifier({ name: "undefined" })) { + return undefined; + } + + if (path.isIdentifier() || path.isMemberExpression()) { + path = path.resolve(); + if (path) { + return evaluate(path); } else { - return evaluate(scope.getImmutableBindingValue(node.name)); + return confident = false; } } - if (t.isUnaryExpression(node, { prefix: true })) { - var arg = evaluate(node.argument); + if (path.isUnaryExpression({ prefix: true })) { + var arg = evaluate(path.get("argument")); switch (node.operator) { case "void": return undefined; case "!": return !arg; @@ -89,13 +95,13 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isArrayExpression(node) || t.isObjectExpression(node)) { + if (path.isArrayExpression() || path.isObjectExpression()) { // we could evaluate these but it's probably impractical and not very useful } - if (t.isLogicalExpression(node)) { - let left = evaluate(node.left); - let right = evaluate(node.right); + if (path.isLogicalExpression()) { + let left = evaluate(path.get("left")); + let right = evaluate(path.get("right")); switch (node.operator) { case "||": return left || right; @@ -103,9 +109,9 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isBinaryExpression(node)) { - let left = evaluate(node.left); - let right = evaluate(node.right); + if (path.isBinaryExpression()) { + let left = evaluate(path.get("left")); + let right = evaluate(path.get("right")); switch (node.operator) { case "-": return left - right; diff --git a/src/babel/traversal/path.js b/src/babel/traversal/path/index.js similarity index 68% rename from src/babel/traversal/path.js rename to src/babel/traversal/path/index.js index 67f527871b..d7fc18a77a 100644 --- a/src/babel/traversal/path.js +++ b/src/babel/traversal/path/index.js @@ -2,10 +2,11 @@ import isBoolean from "lodash/lang/isBoolean"; import isNumber from "lodash/lang/isNumber"; import isRegExp from "lodash/lang/isRegExp"; import isString from "lodash/lang/isString"; -import traverse from "./index"; +import traverse from "../index"; import includes from "lodash/collection/includes"; -import Scope from "./scope"; -import * as t from "../types"; +import assign from "lodash/object/assign"; +import Scope from "../scope"; +import * as t from "../../types"; export default class TraversalPath { constructor(parent, container) { @@ -112,6 +113,13 @@ export default class TraversalPath { this._refresh(node, [node]); } + errorWithNode(msg, Error = SyntaxError) { + var loc = this.node.loc.start; + var err = new Error(`Line ${loc.line}: ${msg}`); + err.loc = loc; + return err; + } + get node() { return this.container[this.key]; } @@ -133,9 +141,6 @@ export default class TraversalPath { // potentially create new scope this.setScope(); - // refresh scope with new/removed bindings - this._refresh(oldNode, replacements); - var file = this.scope && this.scope.file; if (file) { for (var i = 0; i < replacements.length; i++) { @@ -170,12 +175,12 @@ export default class TraversalPath { } } - isBlacklisted() { + isBlacklisted(): boolean { var blacklist = this.opts.blacklist; return blacklist && blacklist.indexOf(this.node.type) > -1; } - visit() { + visit(): boolean { if (this.isBlacklisted()) return false; this.call("enter"); @@ -215,11 +220,22 @@ export default class TraversalPath { } } - has(key) { + has(key): boolean { return !!this.node[key]; } - getTypeAnnotation(): Object { + is(key): boolean { + return this.has(key); + } + + isnt(key): boolean { + return !this.has(key); + } + + getTypeAnnotation(): { + inferred: boolean; + annotation: ?Object; + } { if (this.typeInfo) { return this.typeInfo; } @@ -246,10 +262,15 @@ export default class TraversalPath { resolve(): ?TraversalPath { if (this.isVariableDeclarator()) { - return this.get("init").resolve(); + if (this.get("id").isIdentifier()) { + return this.get("init").resolve(); + } else { + // otherwise it's a request for a destructuring declarator and i'm not + // ready to resolve those just yet + } } else if (this.isIdentifier()) { var binding = this.scope.getBinding(this.node.name); - if (!binding) return; + if (!binding || binding.reassigned) return; if (binding.path === this) { return this; @@ -270,27 +291,44 @@ export default class TraversalPath { if (!prop.isProperty()) continue; var key = prop.get("key"); - if (key.isIdentifier({ name: targetName }) || key.isLiteral({ value: targetName })) { - return prop.get("value"); - } + + // { foo: obj } + var match = prop.isnt("computed") && key.isIdentifier({ name: targetName }); + + // { "foo": "obj" } or { ["foo"]: "obj" } + match ||= key.isLiteral({ value: targetName }); + + if (match) return prop.get("value"); } } else { return this; } } - inferType(path: TraversalPath) { + inferType(path: TraversalPath): ?Object { path = path.resolve(); if (!path) return; - if (path.isRestElement() || path.isArrayExpression()) { + if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) { return t.genericTypeAnnotation(t.identifier("Array")); } + if (path.parentPath.isTypeCastExpression()) { + return path.parentPath.node.typeAnnotation; + } + + if (path.isTypeCastExpression()) { + return path.node.typeAnnotation; + } + if (path.isObjectExpression()) { return t.genericTypeAnnotation(t.identifier("Object")); } + if (path.isFunction()) { + return t.identifier("Function"); + } + if (path.isLiteral()) { var value = path.node.value; if (isString(value)) return t.stringTypeAnnotation(); @@ -304,39 +342,44 @@ export default class TraversalPath { } } - isScope() { + isScope(): boolean { return t.isScope(this.node, this.parent); } - isReferencedIdentifier(opts) { + isReferencedIdentifier(opts): boolean { return t.isReferencedIdentifier(this.node, this.parent, opts); } - isReferenced() { + isReferenced(): boolean { return t.isReferenced(this.node, this.parent); } - isBlockScoped() { + isBlockScoped(): boolean { return t.isBlockScoped(this.node); } - isVar() { + isVar(): boolean { return t.isVar(this.node); } - isScope() { + isScope(): boolean { return t.isScope(this.node, this.parent); } - isTypeGeneric(genericName: string, hasTypeParameters?): boolean { - var type = this.getTypeAnnotation().annotation; + isTypeGeneric(genericName: string, opts = {}): boolean { + var typeInfo = this.getTypeAnnotation(); + var type = typeInfo.annotation; if (!type) return false; + if (type.inferred && opts.inference === false) { + return false; + } + if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) { return false; } - if (hasTypeParameters && !type.typeParameters) { + if (opts.requireTypeParameters && !type.typeParameters) { return false; } @@ -348,10 +391,64 @@ export default class TraversalPath { } traverse(opts, state) { - traverse(this.node, opts, this.scope, state); + traverse(this.node, opts, this.scope, state, this); + } + + /** + * Match the current node if it matches the provided `pattern`. + * + * For example, given the match `React.createClass` it would match the + * parsed nodes of `React.createClass` and `React["createClass"]`. + */ + + matchesPattern(pattern: string, allowPartial?: boolean): boolean { + var parts = pattern.split("."); + + // not a member expression + if (!this.isMemberExpression()) return false; + + var search = [this.node]; + var i = 0; + + while (search.length) { + var node = search.shift(); + + if (allowPartial && i === parts.length) { + return true; + } + + if (t.isIdentifier(node)) { + // this part doesn't match + if (parts[i] !== node.name) return false; + } else if (t.isLiteral(node)) { + // this part doesn't match + if (parts[i] !== node.value) return false; + } else if (t.isMemberExpression(node)) { + if (node.computed && !t.isLiteral(node.property)) { + // we can't deal with this + return false; + } else { + search.push(node.object); + search.push(node.property); + continue; + } + } else { + // we can't deal with this + return false; + } + + // too many parts + if (++i > parts.length) { + return false; + } + } + + return true; } } +assign(TraversalPath.prototype, require("./evaluation")); + for (var i = 0; i < t.TYPES.length; i++) { let type = t.TYPES[i]; let typeKey = `is${type}`; diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index e58da1aabe..24cd0c6b8d 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -75,7 +75,7 @@ export default class Scope { if (cached) { return cached; } else { - path.setData("scope", this); + //path.setData("scope", this); } this.parent = parent; @@ -245,7 +245,7 @@ export default class Scope { if (t.isReferencedIdentifier(node, parent) && node.name === oldName) { node.name = newName; } else if (t.isDeclaration(node)) { - var ids = t.getBindingIdentifiers(node); + var ids = this.getBindingIdentifiers(); for (var name in ids) { if (name === oldName) ids[name].name = newName; } @@ -272,7 +272,7 @@ export default class Scope { if (t.isIdentifier(node)) { var binding = this.getBinding(node.name); - if (binding && binding.isTypeGeneric("Array")) return node; + if (binding && binding.isTypeGeneric("Array", { inference: false })) return node; } if (t.isArrayExpression(node)) { @@ -613,26 +613,6 @@ export default class Scope { return binding && binding.identifier; } - /** - * Description - */ - - getOwnImmutableBindingValue(name: string) { - return this._immutableBindingInfoToValue(this.getOwnBindingInfo(name)); - } - - /** - * Description - */ - - getImmutableBindingValue(name: string) { - return this._immutableBindingInfoToValue(this.getBinding(name)); - } - - _immutableBindingInfoToValue(binding) { - if (binding) return binding.getValueIfImmutable(); - } - /** * Description */ diff --git a/src/babel/types/index.js b/src/babel/types/index.js index 79293f0694..b82f1b380a 100644 --- a/src/babel/types/index.js +++ b/src/babel/types/index.js @@ -297,7 +297,6 @@ toFastProperties(t); toFastProperties(t.VISITOR_KEYS); exports.__esModule = true; -assign(t, require("./evaluators")); assign(t, require("./retrievers")); assign(t, require("./validators")); assign(t, require("./converters")); diff --git a/src/babel/types/visitor-keys.json b/src/babel/types/visitor-keys.json index e3ad4a629e..b30d9f0106 100644 --- a/src/babel/types/visitor-keys.json +++ b/src/babel/types/visitor-keys.json @@ -93,7 +93,7 @@ "TypeofTypeAnnotation": ["argument"], "TypeAlias": ["id", "typeParameters", "right"], "TypeAnnotation": ["typeAnnotation"], - "TypeCastExpression": ["expression"], + "TypeCastExpression": ["expression", "typeAnnotation"], "TypeParameterDeclaration": ["params"], "TypeParameterInstantiation": ["params"], "ObjectTypeAnnotation": ["key", "value"], diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js index 2087ae351b..9dda19048b 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js @@ -1 +1 @@ -var arr = [for (i of [1, 2, 3]) i * i]; +var arr = [for (i of nums) i * i]; diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js index 2ee858e713..10acf74be9 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js @@ -7,7 +7,7 @@ var arr = (function () { var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var i = _step.value; _arr.push(i * i); @@ -28,4 +28,4 @@ var arr = (function () { } return _arr; -})(); \ No newline at end of file +})(); diff --git a/test/fixtures/transformation/es7-comprehensions/array-this/actual.js b/test/fixtures/transformation/es7-comprehensions/array-this/actual.js index 355ed48188..5ae580c2d3 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-this/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/array-this/actual.js @@ -1,5 +1,5 @@ function add() { - return [for (i of [1, 2, 3]) i * this.multiplier]; + return [for (i of nums) i * this.multiplier]; } add.call({ multiplier: 5 }); diff --git a/test/fixtures/transformation/es7-comprehensions/array-this/expected.js b/test/fixtures/transformation/es7-comprehensions/array-this/expected.js index 5f7f52ab97..4ca4eeaee8 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-this/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-this/expected.js @@ -10,7 +10,7 @@ function add() { var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var i = _step.value; _ref.push(i * _this.multiplier); @@ -34,4 +34,4 @@ function add() { })(); } -add.call({ multiplier: 5 }); \ No newline at end of file +add.call({ multiplier: 5 }); From 12664c6cd64e8a4905fffb2eb562c233a1c4e1df Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 01:48:46 +1100 Subject: [PATCH 06/24] start migration of core from nodes to paths --- src/babel/api/register/cache.js | 2 +- src/babel/generation/node/parentheses.js | 2 +- src/babel/messages.js | 4 +- src/babel/transformation/file.js | 4 +- .../helpers/build-react-transformer.js | 2 +- .../transformation/helpers/replace-supers.js | 2 +- src/babel/transformation/modules/_default.js | 14 +- src/babel/transformation/transformer-pass.js | 3 +- .../transformers/es6/block-scoping-tdz.js | 2 +- .../transformers/es6/block-scoping.js | 4 +- .../transformers/es6/classes.js | 35 ++-- .../transformers/es6/constants.js | 2 +- .../transformation/transformers/es6/for-of.js | 10 +- .../transformers/es6/modules.js | 2 +- .../transformers/es6/parameters.default.js | 27 +-- .../transformers/es6/parameters.rest.js | 4 +- .../transformation/transformers/es6/spread.js | 4 +- .../transformers/es6/symbols.js | 2 +- .../transformers/es6/tail-call.js | 19 ++- .../transformation/transformers/index.js | 1 + .../transformers/internal/alias-functions.js | 14 +- .../transformers/internal/modules.js | 2 +- .../transformers/optimisation/flow.for-of.js | 11 ++ .../transformation/transformers/other/flow.js | 2 +- .../transformers/other/runtime.js | 8 +- .../playground/object-getter-memoization.js | 5 +- .../utility/dead-code-elimination.js | 5 +- .../utility/inline-environment-variables.js | 2 +- .../utility/inline-expressions.js | 2 +- .../transformers/utility/remove-console.js | 4 +- .../transformers/validation/react.js | 2 +- src/babel/traversal/binding.js | 24 --- src/babel/traversal/index.js | 8 +- src/babel/traversal/path/conversion.js | 24 +++ .../path/evaluation.js} | 62 +++---- .../traversal/{path.js => path/index.js} | 154 ++++++++++++++---- src/babel/traversal/scope.js | 49 +----- src/babel/types/index.js | 19 +-- src/babel/types/visitor-keys.json | 2 +- .../array-expression-single/actual.js | 2 +- .../array-expression-single/expected.js | 4 +- .../es7-comprehensions/array-this/actual.js | 2 +- .../es7-comprehensions/array-this/expected.js | 4 +- 43 files changed, 319 insertions(+), 237 deletions(-) create mode 100644 src/babel/transformation/transformers/optimisation/flow.for-of.js create mode 100644 src/babel/traversal/path/conversion.js rename src/babel/{types/evaluators.js => traversal/path/evaluation.js} (65%) rename src/babel/traversal/{path.js => path/index.js} (68%) diff --git a/src/babel/api/register/cache.js b/src/babel/api/register/cache.js index 6f7bc54029..dd724ac74a 100644 --- a/src/babel/api/register/cache.js +++ b/src/babel/api/register/cache.js @@ -2,7 +2,7 @@ import path from "path"; import os from "os"; import fs from "fs"; -var FILENAME = process.env.BABEL_CACHE_PATH || path.join(os.tmpdir(), "babel.json"); +const FILENAME = process.env.BABEL_CACHE_PATH || path.join(os.tmpdir(), "babel.json"); var data = {}; export function save() { diff --git a/src/babel/generation/node/parentheses.js b/src/babel/generation/node/parentheses.js index e429dca474..d3b4302f2d 100644 --- a/src/babel/generation/node/parentheses.js +++ b/src/babel/generation/node/parentheses.js @@ -1,7 +1,7 @@ import each from "lodash/collection/each"; import * as t from "../../types"; -var PRECEDENCE = {}; +const PRECEDENCE = {}; each([ ["||"], diff --git a/src/babel/messages.js b/src/babel/messages.js index f97e84c969..6a4952dcf3 100644 --- a/src/babel/messages.js +++ b/src/babel/messages.js @@ -1,6 +1,6 @@ import * as util from "util"; -export var messages = { +export const MESSAGES = { tailCallReassignmentDeopt: "Function reference has been reassigned so it's probably be dereferenced so we can't optimise this with confidence", JSXNamespacedTags: "Namespace tags are not supported. ReactJSX is not XML.", classesIllegalBareSuper: "Illegal use of bare super", @@ -25,7 +25,7 @@ export var messages = { }; export function get(key: String, ...args) { - var msg = messages[key]; + var msg = MESSAGES[key]; if (!msg) throw new ReferenceError(`Unknown message ${JSON.stringify(key)}`); args = parseArgs(args); diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file.js index 3b8b1abf43..0cd16c2aa5 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file.js @@ -464,7 +464,7 @@ export default class File { this.scope = this.path.scope; this.ast = ast; - traverse(ast, { + this.path.traverse({ enter(node, parent, scope) { if (this.isScope()) { for (var key in scope.bindings) { @@ -472,7 +472,7 @@ export default class File { } } } - }, this.scope); + }); } transform(ast) { diff --git a/src/babel/transformation/helpers/build-react-transformer.js b/src/babel/transformation/helpers/build-react-transformer.js index 02b9ba1ffe..ccef9d819b 100644 --- a/src/babel/transformation/helpers/build-react-transformer.js +++ b/src/babel/transformation/helpers/build-react-transformer.js @@ -27,7 +27,7 @@ export default function (exports, opts) { }; exports.JSXNamespacedName = function (node, parent, scope, file) { - throw file.errorWithNode(node, messages.get("JSXNamespacedTags")); + throw this.errorWithNode(messages.get("JSXNamespacedTags")); }; exports.JSXMemberExpression = { diff --git a/src/babel/transformation/helpers/replace-supers.js b/src/babel/transformation/helpers/replace-supers.js index e60cbabcb4..0796a21300 100644 --- a/src/babel/transformation/helpers/replace-supers.js +++ b/src/babel/transformation/helpers/replace-supers.js @@ -219,7 +219,7 @@ export default class ReplaceSupers { var thisReference; if (isIllegalBareSuper(node, parent)) { - throw this.file.errorWithNode(node, messages.get("classesIllegalBareSuper")); + throw this.errorWithNode(messages.get("classesIllegalBareSuper")); } if (t.isCallExpression(node)) { diff --git a/src/babel/transformation/modules/_default.js b/src/babel/transformation/modules/_default.js index a60d3b7507..2158b8525f 100644 --- a/src/babel/transformation/modules/_default.js +++ b/src/babel/transformation/modules/_default.js @@ -45,7 +45,7 @@ var importsVisitor = { ImportDeclaration: { enter(node, parent, scope, formatter) { formatter.hasLocalImports = true; - extend(formatter.localImports, t.getBindingIdentifiers(node)); + extend(formatter.localImports, this.getBindingIdentifiers()); formatter.bumpImportOccurences(node); } } @@ -54,11 +54,11 @@ var importsVisitor = { var exportsVisitor = { ExportDeclaration: { enter(node, parent, scope, formatter) { - var declar = node.declaration; + var declar = this.get("declaration"); formatter.hasLocalImports = true; - if (declar && t.isStatement(declar)) { - extend(formatter.localExports, t.getBindingIdentifiers(declar)); + if (declar.isStatement()) { + extend(formatter.localExports, declar.getBindingIdentifiers()); } if (!node.default) { @@ -105,16 +105,16 @@ export default class DefaultFormatter { } getLocalExports() { - this.file.scope.traverse(this.file.ast, exportsVisitor, this); + this.file.path.traverse(exportsVisitor, this); } getLocalImports() { - this.file.scope.traverse(this.file.ast, importsVisitor, this); + this.file.path.traverse(importsVisitor, this); } remapAssignments() { if (this.hasLocalImports) { - this.file.scope.traverse(this.file.ast, remapVisitor, this); + this.file.path.traverse(remapVisitor, this); } } diff --git a/src/babel/transformation/transformer-pass.js b/src/babel/transformation/transformer-pass.js index 3beac40bc8..dd8fab0431 100644 --- a/src/babel/transformation/transformer-pass.js +++ b/src/babel/transformation/transformer-pass.js @@ -1,4 +1,5 @@ import includes from "lodash/collection/includes"; +import traverse from "../traversal"; /** * This class is responsible for traversing over the provided `File`s @@ -59,7 +60,7 @@ export default class TransformerPass { file.log.debug(`Running transformer ${this.transformer.key}`); - file.scope.traverse(file.ast, this.handlers, file); + traverse(file.ast, this.handlers, file.scope, file); this.ran = true; } diff --git a/src/babel/transformation/transformers/es6/block-scoping-tdz.js b/src/babel/transformation/transformers/es6/block-scoping-tdz.js index d18333a192..0eee6bc324 100644 --- a/src/babel/transformation/transformers/es6/block-scoping-tdz.js +++ b/src/babel/transformation/transformers/es6/block-scoping-tdz.js @@ -32,7 +32,7 @@ export function BlockStatement(node, parent, scope, file) { var letRefs = node._letReferences; if (!letRefs) return; - scope.traverse(node, visitor, { + this.traverse(visitor, { letRefs: letRefs, file: file }); diff --git a/src/babel/transformation/transformers/es6/block-scoping.js b/src/babel/transformation/transformers/es6/block-scoping.js index 79eb0380c7..ee7e87402f 100644 --- a/src/babel/transformation/transformers/es6/block-scoping.js +++ b/src/babel/transformation/transformers/es6/block-scoping.js @@ -111,7 +111,7 @@ function traverseReplace(node, parent, scope, remaps) { var letReferenceBlockVisitor = { enter(node, parent, scope, state) { if (this.isFunction()) { - scope.traverse(node, letReferenceFunctionVisitor, state); + this.traverse(letReferenceFunctionVisitor, state); return this.skip(); } } @@ -173,7 +173,7 @@ var loopVisitor = { if (this.isLoop()) { state.ignoreLabeless = true; - scope.traverse(node, loopVisitor, state); + this.traverse(loopVisitor, state); state.ignoreLabeless = false; } diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index 862e498d6f..bf66ef32f1 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -15,7 +15,7 @@ export function ClassDeclaration(node, parent, scope, file) { } export function ClassExpression(node, parent, scope, file) { - return new ClassTransformer(node, parent, scope, file).run(); + return new ClassTransformer(this, file).run(); } var verifyConstructorVisitor = traverse.explode({ @@ -37,7 +37,7 @@ var verifyConstructorVisitor = traverse.explode({ state.hasBareSuper = true; if (!state.hasSuper) { - throw state.file.errorWithNode(node, "super call is only allowed in derived constructor"); + throw this.errorWithNode("super call is only allowed in derived constructor"); } } } @@ -46,7 +46,7 @@ var verifyConstructorVisitor = traverse.explode({ ThisExpression: { enter(node, parent, scope, state) { if (state.hasSuper && !state.hasBareSuper) { - throw state.file.errorWithNode(node, "'this' is not allowed before super()"); + throw this.errorWithNode("'this' is not allowed before super()"); } } } @@ -58,10 +58,11 @@ class ClassTransformer { * Description */ - constructor(node: Object, parent: Object, scope: Scope, file: File) { - this.parent = parent; - this.scope = scope; - this.node = node; + constructor(path: TraversalPath, file: File) { + this.parent = path.parent; + this.scope = path.scope; + this.node = path.node; + this.path = path; this.file = file; this.hasInstanceMutators = false; @@ -71,11 +72,11 @@ class ClassTransformer { this.staticMutatorMap = {}; this.hasConstructor = false; - this.className = node.id; - this.classRef = node.id || scope.generateUidIdentifier("class"); + this.className = this.node.id; + this.classRef = this.node.id || this.scope.generateUidIdentifier("class"); - this.superName = node.superClass || t.identifier("Function"); - this.hasSuper = !!node.superClass; + this.superName = this.node.superClass || t.identifier("Function"); + this.hasSuper = !!this.node.superClass; this.isLoose = file.isLoose("es6.classes"); } @@ -174,11 +175,13 @@ class ClassTransformer { var classBody = this.node.body.body; var body = this.body; + var classBodyPaths = this.path.get("body").get("body"); + for (var i = 0; i < classBody.length; i++) { var node = classBody[i]; if (t.isMethodDefinition(node)) { var isConstructor = (!node.computed && t.isIdentifier(node.key, { name: "constructor" })) || t.isLiteral(node.key, { value: "constructor" }); - if (isConstructor) this.verifyConstructor(node); + if (isConstructor) this.verifyConstructor(classBodyPaths[i]); var replaceSupers = new ReplaceSupers({ methodNode: node, @@ -206,7 +209,7 @@ class ClassTransformer { } // we have no constructor, we have a super, and the super doesn't appear to be falsy - if (!this.hasConstructor && this.hasSuper && t.evaluateTruthy(superName, this.scope) !== false) { + if (!this.hasConstructor && this.hasSuper) { // todo: t.evaluateTruthy(superName, this.scope) !== false var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; constructor.body.body.push(util.template(helperName, { @@ -249,17 +252,17 @@ class ClassTransformer { * Description */ - verifyConstructor(node: Object) { + verifyConstructor(path: TraversalPath) { var state = { hasBareSuper: false, hasSuper: this.hasSuper, file: this.file }; - traverse(node, verifyConstructorVisitor, this.scope, state); + path.traverse(verifyConstructorVisitor, state); if (!state.hasBareSuper && this.hasSuper) { - throw this.file.errorWithNode(node, "Derived constructor must call super()"); + throw path.errorWithNode("Derived constructor must call super()"); } } diff --git a/src/babel/transformation/transformers/es6/constants.js b/src/babel/transformation/transformers/es6/constants.js index fc29e8c916..4a4e5c95d2 100644 --- a/src/babel/transformation/transformers/es6/constants.js +++ b/src/babel/transformation/transformers/es6/constants.js @@ -37,7 +37,7 @@ var visitor = { }; export function Scopable(node, parent, scope, file) { - scope.traverse(node, visitor, { + this.traverse(visitor, { constants: scope.getAllBindingsOfKind("const"), file: file }); diff --git a/src/babel/transformation/transformers/es6/for-of.js b/src/babel/transformation/transformers/es6/for-of.js index addd609af5..42c57d5859 100644 --- a/src/babel/transformation/transformers/es6/for-of.js +++ b/src/babel/transformation/transformers/es6/for-of.js @@ -5,12 +5,10 @@ import * as t from "../../../types"; export var check = t.isForOfStatement; export function ForOfStatement(node, parent, scope, file) { - if (this.get("right").isTypeGeneric("Array")) { - return array(node, scope, file); + if (this.get("right").isArrayExpression()) { + return _ForOfStatementArray.call(this, node, scope, file); } - // - var callback = spec; if (file.isLoose("es6.forOf")) callback = loose; @@ -42,7 +40,7 @@ export function ForOfStatement(node, parent, scope, file) { } } -var array = function (node, scope, file) { +export function _ForOfStatementArray(node, scope, file) { var nodes = []; var right = node.right; @@ -78,7 +76,7 @@ var array = function (node, scope, file) { nodes.push(loop); return nodes; -}; +} var loose = function (node, parent, scope, file) { var left = node.left; diff --git a/src/babel/transformation/transformers/es6/modules.js b/src/babel/transformation/transformers/es6/modules.js index 0eab992dfe..cb583622d6 100644 --- a/src/babel/transformation/transformers/es6/modules.js +++ b/src/babel/transformation/transformers/es6/modules.js @@ -26,7 +26,7 @@ export function ImportDeclaration(node, parent, scope, file) { export function ExportDeclaration(node, parent, scope, file) { // flow type - if (t.isTypeAlias(node.declaration)) return; + if (this.get("declaration").isTypeAlias()) return; var nodes = []; var i; diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 9e525c87d3..82dd26e20e 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -49,41 +49,42 @@ exports.Function = function (node, parent, scope, file) { body.push(defNode); }; - for (var i = 0; i < node.params.length; i++) { - var param = node.params[i]; + var params = this.get("params"); + for (var i = 0; i < params.length; i++) { + var param = params[i]; - if (!t.isAssignmentPattern(param)) { - if (!t.isRestElement(param)) { + if (!param.isAssignmentPattern()) { + if (!param.isRestElement()) { lastNonDefaultParam = i + 1; } - if (!t.isIdentifier(param)) { - scope.traverse(param, iifeVisitor, state); + if (!param.isIdentifier()) { + param.traverse(iifeVisitor, state); } - if (file.transformers["es6.blockScopingTDZ"].canRun() && t.isIdentifier(param)) { - pushDefNode(param, t.identifier("undefined"), i); + if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) { + pushDefNode(param.node, t.identifier("undefined"), i); } continue; } - var left = param.left; - var right = param.right; + var left = param.get("left"); + var right = param.get("right"); var placeholder = scope.generateUidIdentifier("x"); placeholder._isDefaultPlaceholder = true; node.params[i] = placeholder; if (!state.iife) { - if (t.isIdentifier(right) && scope.hasOwnBinding(right.name)) { + if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) { state.iife = true; } else { - scope.traverse(right, iifeVisitor, state); + right.traverse(iifeVisitor, state); } } - pushDefNode(left, right, i); + pushDefNode(left.node, right.node, i); } // we need to cut off all trailing default parameters diff --git a/src/babel/transformation/transformers/es6/parameters.rest.js b/src/babel/transformation/transformers/es6/parameters.rest.js index e79af4418d..d0f967a8f7 100644 --- a/src/babel/transformation/transformers/es6/parameters.rest.js +++ b/src/babel/transformation/transformers/es6/parameters.rest.js @@ -15,7 +15,7 @@ var memberExpressionOptimisationVisitor = { // to the wrong function if (this.isFunctionDeclaration() || this.isFunctionExpression()) { state.noOptimise = true; - scope.traverse(node, memberExpressionOptimisationVisitor, state); + this.traverse(memberExpressionOptimisationVisitor, state); state.noOptimise = false; return this.skip(); } @@ -88,7 +88,7 @@ exports.Function = function (node, parent, scope, file) { name: rest.name }; - scope.traverse(node, memberExpressionOptimisationVisitor, state); + this.traverse(memberExpressionOptimisationVisitor, state); // we only have shorthands and there's no other references if (state.canOptimise && state.candidates.length) { diff --git a/src/babel/transformation/transformers/es6/spread.js b/src/babel/transformation/transformers/es6/spread.js index 4fe2b7d238..693553ded9 100644 --- a/src/babel/transformation/transformers/es6/spread.js +++ b/src/babel/transformation/transformers/es6/spread.js @@ -81,7 +81,7 @@ export function CallExpression(node, parent, scope) { var callee = node.callee; - if (t.isMemberExpression(callee)) { + if (this.get("callee").isMemberExpression()) { var temp = scope.generateTempBasedOnNode(callee.object); if (temp) { callee.object = t.assignmentExpression("=", temp, callee.object); @@ -101,7 +101,7 @@ export function NewExpression(node, parent, scope, file) { var args = node.arguments; if (!hasSpread(args)) return; - var nativeType = t.isIdentifier(node.callee) && includes(t.NATIVE_TYPE_NAMES, node.callee.name); + var nativeType = this.get("callee").isIdentifier() && includes(t.NATIVE_TYPE_NAMES, node.callee.name); var nodes = build(args, scope); diff --git a/src/babel/transformation/transformers/es6/symbols.js b/src/babel/transformation/transformers/es6/symbols.js index fd3fe51b7e..03f7ab491f 100644 --- a/src/babel/transformation/transformers/es6/symbols.js +++ b/src/babel/transformation/transformers/es6/symbols.js @@ -7,7 +7,7 @@ export function UnaryExpression(node, parent, scope, file) { if (node.operator === "typeof") { var call = t.callExpression(file.addHelper("typeof"), [node.argument]); - if (t.isIdentifier(node.argument)) { + if (this.get("argument").isIdentifier()) { var undefLiteral = t.literal("undefined"); return t.conditionalExpression( t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral), diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 563ad8bd00..122f456f2a 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -6,7 +6,7 @@ import map from "lodash/collection/map"; import * as t from "../../../types"; exports.Function = function (node, parent, scope, file) { - var tailCall = new TailCallTransformer(node, scope, file); + var tailCall = new TailCallTransformer(this, scope, file); tailCall.run(); }; @@ -18,11 +18,11 @@ function returnBlock(expr) { var firstPass = { enter(node, parent, scope, state) { if (this.isIfStatement()) { - if (t.isReturnStatement(node.alternate)) { + if (this.get("alternate").isReturnStatement()) { t.ensureBlock(node, "alternate"); } - if (t.isReturnStatement(node.consequent)) { + if (this.get("consequent").isReturnStatement()) { t.ensureBlock(node, "consequent"); } } else if (this.isReturnStatement()) { @@ -85,17 +85,18 @@ var thirdPass = { }; class TailCallTransformer { - constructor(node, scope, file) { + constructor(path, scope, file) { this.hasTailRecursion = false; this.needsArguments = false; this.setsArguments = false; this.needsThis = false; - this.ownerId = node.id; + this.ownerId = path.node.id; this.vars = []; this.scope = scope; + this.path = path; this.file = file; - this.node = node; + this.node = path.node; } getArgumentsId() { @@ -156,7 +157,7 @@ class TailCallTransformer { if (!ownerId) return; // traverse the function and look for tail recursion - scope.traverse(node, firstPass, this); + this.path.traverse(firstPass, this); if (!this.hasTailRecursion) return; @@ -167,10 +168,10 @@ class TailCallTransformer { // - scope.traverse(node, secondPass, this); + this.path.traverse(secondPass, this); if (!this.needsThis || !this.needsArguments) { - scope.traverse(node, thirdPass, this); + this.path.traverse(thirdPass, this); } var body = t.ensureBlock(node).body; diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index c1098afeb7..9b008d23c6 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -44,6 +44,7 @@ export default { // needs to be before `_aliasFunction` due to define property closure "es6.properties.computed": require("./es6/properties.computed"), + "optimisation.es6.forOf": require("./optimisation/flow.for-of"), "es6.forOf": require("./es6/for-of"), "es6.regex.sticky": require("./es6/regex.sticky"), diff --git a/src/babel/transformation/transformers/internal/alias-functions.js b/src/babel/transformation/transformers/internal/alias-functions.js index b76e19ae50..649edd0c38 100644 --- a/src/babel/transformation/transformers/internal/alias-functions.js +++ b/src/babel/transformation/transformers/internal/alias-functions.js @@ -34,13 +34,13 @@ var functionVisitor = { } // traverse all child nodes of this function and find `arguments` and `this` - scope.traverse(node, functionChildrenVisitor, state); + this.traverse(functionChildrenVisitor, state); return this.skip(); } }; -var go = function (getBody, node, scope) { +function aliasFunction(getBody, path, scope) { var argumentsId; var thisId; @@ -56,7 +56,7 @@ var go = function (getBody, node, scope) { // traverse the function and find all alias functions so we can alias // `arguments` and `this` if necessary - scope.traverse(node, functionVisitor, state); + path.traverse(functionVisitor, state); var body; @@ -77,16 +77,16 @@ var go = function (getBody, node, scope) { }; export function Program(node, parent, scope) { - go(function () { + aliasFunction(function () { return node.body; - }, node, scope); + }, this, scope); }; export function FunctionDeclaration(node, parent, scope) { - go(function () { + aliasFunction(function () { t.ensureBlock(node); return node.body.body; - }, node, scope); + }, this, scope); } export { FunctionDeclaration as FunctionExpression }; diff --git a/src/babel/transformation/transformers/internal/modules.js b/src/babel/transformation/transformers/internal/modules.js index 7ef5bc5302..69de42f4d5 100644 --- a/src/babel/transformation/transformers/internal/modules.js +++ b/src/babel/transformation/transformers/internal/modules.js @@ -59,7 +59,7 @@ export function ExportDeclaration(node, parent, scope) { } else if (t.isVariableDeclaration(declar)) { // export var foo = "bar"; var specifiers = []; - var bindings = t.getBindingIdentifiers(declar); + var bindings = this.get("declaration").getBindingIdentifiers(); for (var key in bindings) { var id = bindings[key]; specifiers.push(t.exportSpecifier(id, id)); diff --git a/src/babel/transformation/transformers/optimisation/flow.for-of.js b/src/babel/transformation/transformers/optimisation/flow.for-of.js new file mode 100644 index 0000000000..5e0237d700 --- /dev/null +++ b/src/babel/transformation/transformers/optimisation/flow.for-of.js @@ -0,0 +1,11 @@ +import { _ForOfStatementArray } from "../es6/for-of"; +import * as t from "../../../types"; + +export var check = t.isForOfStatement; +export var optional = true; + +export function ForOfStatement(node, parent, scope, file) { + if (this.get("right").isTypeGeneric("Array")) { + return _ForOfStatementArray.call(this, node, scope, file); + } +} diff --git a/src/babel/transformation/transformers/other/flow.js b/src/babel/transformation/transformers/other/flow.js index 4f8126f9f9..bb6bf4c393 100644 --- a/src/babel/transformation/transformers/other/flow.js +++ b/src/babel/transformation/transformers/other/flow.js @@ -29,5 +29,5 @@ export function ImportDeclaration(node) { } export function ExportDeclaration(node) { - if (t.isTypeAlias(node.declaration)) this.remove(); + if (this.get("declaration").isTypeAlias()) this.remove(); } diff --git a/src/babel/transformation/transformers/other/runtime.js b/src/babel/transformation/transformers/other/runtime.js index a137603e8c..ec2de99541 100644 --- a/src/babel/transformation/transformers/other/runtime.js +++ b/src/babel/transformation/transformers/other/runtime.js @@ -4,7 +4,7 @@ import core from "core-js/library"; import has from "lodash/object/has"; import * as t from "../../../types"; -var isSymboliterator = t.buildMatchMemberExpression("Symbol.iterator"); +var isSymbolIterator = t.buildMatchMemberExpression("Symbol.iterator"); var coreHas = function (node) { return node.name !== "_" && has(core, node.name); @@ -47,7 +47,7 @@ var astVisitor = { if (!callee.computed) return false; prop = callee.property; - if (!isSymboliterator(prop)) return false; + if (!isSymbolIterator(prop)) return false; return util.template("corejs-iterator", { CORE_ID: file.get("coreIdentifier"), @@ -59,7 +59,7 @@ var astVisitor = { if (node.operator !== "in") return; var left = node.left; - if (!isSymboliterator(left)) return; + if (!isSymbolIterator(left)) return; return util.template("corejs-is-iterator", { CORE_ID: file.get("coreIdentifier"), @@ -76,7 +76,7 @@ export function manipulateOptions(opts) { } export function Program(node, parent, scope, file) { - scope.traverse(node, astVisitor, file); + this.traverse(astVisitor, file); } export function pre(file) { diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js index af5ffe8c4c..5cfaf9c61a 100644 --- a/src/babel/transformation/transformers/playground/object-getter-memoization.js +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -20,8 +20,7 @@ export function MethodDefinition(node, parent, scope, file) { if (node.kind !== "memo") return; node.kind = "get"; - var value = node.value; - t.ensureBlock(value); + t.ensureBlock(node.value); var key = node.key; @@ -34,7 +33,7 @@ export function MethodDefinition(node, parent, scope, file) { file: file }; - scope.traverse(value, visitor, state); + this.get("value").traverse(visitor, state); return node; } diff --git a/src/babel/transformation/transformers/utility/dead-code-elimination.js b/src/babel/transformation/transformers/utility/dead-code-elimination.js index 3dd7252ae3..bda19f668d 100644 --- a/src/babel/transformation/transformers/utility/dead-code-elimination.js +++ b/src/babel/transformation/transformers/utility/dead-code-elimination.js @@ -20,7 +20,7 @@ function toStatements(node) { export var optional = true; export function ConditionalExpression(node, parent, scope) { - var evaluateTest = t.evaluateTruthy(node.test, scope); + var evaluateTest = this.get("test").evaluateTruthy(); if (evaluateTest === true) { return node.consequent; } else if (evaluateTest === false) { @@ -32,9 +32,8 @@ export var IfStatement = { exit(node, parent, scope) { var consequent = node.consequent; var alternate = node.alternate; - var test = node.test; - var evaluateTest = t.evaluateTruthy(test, scope); + 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 diff --git a/src/babel/transformation/transformers/utility/inline-environment-variables.js b/src/babel/transformation/transformers/utility/inline-environment-variables.js index 5c62f53a6e..5e2cabb55c 100644 --- a/src/babel/transformation/transformers/utility/inline-environment-variables.js +++ b/src/babel/transformation/transformers/utility/inline-environment-variables.js @@ -6,7 +6,7 @@ var match = t.buildMatchMemberExpression("process.env"); export function MemberExpression(node) { if (match(node.object)) { - var key = t.toComputedKey(node, node.property); + var key = this.toComputedKey(); if (t.isLiteral(key)) { return t.valueToNode(process.env[key.value]); } diff --git a/src/babel/transformation/transformers/utility/inline-expressions.js b/src/babel/transformation/transformers/utility/inline-expressions.js index 9b77422502..f1be106014 100644 --- a/src/babel/transformation/transformers/utility/inline-expressions.js +++ b/src/babel/transformation/transformers/utility/inline-expressions.js @@ -3,7 +3,7 @@ import * as t from "../../../types"; export var optional = true; export function Expression(node, parent, scope) { - var res = t.evaluate(node, scope); + var res = this.evaluate(); if (res.confident) return t.valueToNode(res.value); } diff --git a/src/babel/transformation/transformers/utility/remove-console.js b/src/babel/transformation/transformers/utility/remove-console.js index 791b17c9f4..9fe1f491a9 100644 --- a/src/babel/transformation/transformers/utility/remove-console.js +++ b/src/babel/transformation/transformers/utility/remove-console.js @@ -1,11 +1,9 @@ import * as t from "../../../types"; -var isConsole = t.buildMatchMemberExpression("console", true); - export var optional = true; export function CallExpression(node, parent) { - if (isConsole(node.callee)) { + if (this.get("callee").matchesPattern("console", true)) { if (t.isExpressionStatement(parent)) { this.parentPath.remove(); } else { diff --git a/src/babel/transformation/transformers/validation/react.js b/src/babel/transformation/transformers/validation/react.js index f8cbc58ffb..536aff1768 100644 --- a/src/babel/transformation/transformers/validation/react.js +++ b/src/babel/transformation/transformers/validation/react.js @@ -14,7 +14,7 @@ function check(source, file) { } export function CallExpression(node, parent, scope, file) { - if (t.isIdentifier(node.callee, { name: "require" }) && node.arguments.length === 1) { + if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) { check(node.arguments[0], file); } } diff --git a/src/babel/traversal/binding.js b/src/babel/traversal/binding.js index 95f45dace7..fe4d29a096 100644 --- a/src/babel/traversal/binding.js +++ b/src/babel/traversal/binding.js @@ -58,30 +58,6 @@ export default class Binding { } } - /** - * Description - */ - - getValueIfImmutable() { - // can't guarantee this value is the same - if (this.reassigned) return; - - var node = this.path.node; - if (t.isVariableDeclarator(node)) { - if (t.isIdentifier(node.id)) { - node = node.init; - } else { - // otherwise it's probably a destructuring like: - // var { foo } = "foo"; - return; - } - } - - if (t.isImmutable(node)) { - return node; - } - } - /** * Description */ diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index b2befe1823..c36162f7df 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -1,10 +1,8 @@ -module.exports = traverse; - import TraversalContext from "./context"; import includes from "lodash/collection/includes"; import * as t from "../types"; -function traverse(parent, opts, scope, state) { +export default function traverse(parent, opts, scope, state, parentPath) { if (!parent) return; if (!opts.noScope && !scope) { @@ -20,10 +18,10 @@ function traverse(parent, opts, scope, state) { // array of nodes if (Array.isArray(parent)) { for (var i = 0; i < parent.length; i++) { - traverse.node(parent[i], opts, scope, state); + traverse.node(parent[i], opts, scope, state, parentPath); } } else { - traverse.node(parent, opts, scope, state); + traverse.node(parent, opts, scope, state, parentPath); } } diff --git a/src/babel/traversal/path/conversion.js b/src/babel/traversal/path/conversion.js new file mode 100644 index 0000000000..e0271db76d --- /dev/null +++ b/src/babel/traversal/path/conversion.js @@ -0,0 +1,24 @@ +import * as t from "./index"; + +/** + * Description + */ + +export function toComputedKey(): Object { + var node = this.node; + + var key; + if (this.isMemberExpression()) { + key = node.property; + } else if (this.isProperty()) { + key = node.key; + } else { + throw new ReferenceError("todo"); + } + + if (!node.computed) { + if (t.isIdentifier(key)) key = t.literal(key.name); + } + + return key; +} diff --git a/src/babel/types/evaluators.js b/src/babel/traversal/path/evaluation.js similarity index 65% rename from src/babel/types/evaluators.js rename to src/babel/traversal/path/evaluation.js index 163de2581e..2070ab5d2b 100644 --- a/src/babel/types/evaluators.js +++ b/src/babel/traversal/path/evaluation.js @@ -1,5 +1,3 @@ -import * as t from "./index"; - /** * Walk the input `node` and statically evaluate if it's truthy. * @@ -18,15 +16,15 @@ import * as t from "./index"; * */ -export function evaluateTruthy(node: Object, scope: Scope): boolean { - var res = evaluate(node, scope); +export function evaluateTruthy(): boolean { + var res = this.evaluate(); if (res.confident) return !!res.value; } /** * Walk the input `node` and statically evaluate it. * - * Returns an pbject in the form `{ confident, value }`. `confident` indicates + * Returns an object in the form `{ confident, value }`. `confident` indicates * whether or not we had to drop out of evaluating the expression because of * hitting an unknown node that we couldn't confidently find the value of. * @@ -38,24 +36,27 @@ export function evaluateTruthy(node: Object, scope: Scope): boolean { * */ -export function evaluate(node: Object, scope: Scope): { confident: boolean; value: any } { +export function evaluate(): { confident: boolean; value: any } { var confident = true; - var value = evaluate(node); + var value = evaluate(this); if (!confident) value = undefined; return { confident: confident, value: value }; - function evaluate(node) { + function evaluate(path) { if (!confident) return; - if (t.isSequenceExpression(node)) { - return evaluate(node.expressions[node.expressions.length - 1]); + var node = path.node; + + if (path.isSequenceExpression()) { + var exprs = path.get("expressions"); + return evaluate(exprs[exprs.length - 1]); } - if (t.isLiteral(node)) { + if (path.isLiteral()) { if (node.regex && node.value === null) { // we have a regex and we can't represent it natively } else { @@ -63,24 +64,29 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isConditionalExpression(node)) { - if (evaluate(node.test)) { - return evaluate(node.consequent); + if (path.isConditionalExpression()) { + if (evaluate(path.get("test"))) { + return evaluate(path.get("consequent")); } else { - return evaluate(node.alternate); + return evaluate(path.get("alternate")); } } - if (t.isIdentifier(node)) { - if (node.name === "undefined") { - return undefined; + if (path.isIdentifier({ name: "undefined" })) { + return undefined; + } + + if (path.isIdentifier() || path.isMemberExpression()) { + path = path.resolve(); + if (path) { + return evaluate(path); } else { - return evaluate(scope.getImmutableBindingValue(node.name)); + return confident = false; } } - if (t.isUnaryExpression(node, { prefix: true })) { - var arg = evaluate(node.argument); + if (path.isUnaryExpression({ prefix: true })) { + var arg = evaluate(path.get("argument")); switch (node.operator) { case "void": return undefined; case "!": return !arg; @@ -89,13 +95,13 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isArrayExpression(node) || t.isObjectExpression(node)) { + if (path.isArrayExpression() || path.isObjectExpression()) { // we could evaluate these but it's probably impractical and not very useful } - if (t.isLogicalExpression(node)) { - let left = evaluate(node.left); - let right = evaluate(node.right); + if (path.isLogicalExpression()) { + let left = evaluate(path.get("left")); + let right = evaluate(path.get("right")); switch (node.operator) { case "||": return left || right; @@ -103,9 +109,9 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu } } - if (t.isBinaryExpression(node)) { - let left = evaluate(node.left); - let right = evaluate(node.right); + if (path.isBinaryExpression()) { + let left = evaluate(path.get("left")); + let right = evaluate(path.get("right")); switch (node.operator) { case "-": return left - right; diff --git a/src/babel/traversal/path.js b/src/babel/traversal/path/index.js similarity index 68% rename from src/babel/traversal/path.js rename to src/babel/traversal/path/index.js index 67f527871b..c6891e07b6 100644 --- a/src/babel/traversal/path.js +++ b/src/babel/traversal/path/index.js @@ -2,10 +2,11 @@ import isBoolean from "lodash/lang/isBoolean"; import isNumber from "lodash/lang/isNumber"; import isRegExp from "lodash/lang/isRegExp"; import isString from "lodash/lang/isString"; -import traverse from "./index"; +import traverse from "../index"; import includes from "lodash/collection/includes"; -import Scope from "./scope"; -import * as t from "../types"; +import assign from "lodash/object/assign"; +import Scope from "../scope"; +import * as t from "../../types"; export default class TraversalPath { constructor(parent, container) { @@ -112,6 +113,13 @@ export default class TraversalPath { this._refresh(node, [node]); } + errorWithNode(msg, Error = SyntaxError) { + var loc = this.node.loc.start; + var err = new Error(`Line ${loc.line}: ${msg}`); + err.loc = loc; + return err; + } + get node() { return this.container[this.key]; } @@ -133,9 +141,6 @@ export default class TraversalPath { // potentially create new scope this.setScope(); - // refresh scope with new/removed bindings - this._refresh(oldNode, replacements); - var file = this.scope && this.scope.file; if (file) { for (var i = 0; i < replacements.length; i++) { @@ -170,12 +175,12 @@ export default class TraversalPath { } } - isBlacklisted() { + isBlacklisted(): boolean { var blacklist = this.opts.blacklist; return blacklist && blacklist.indexOf(this.node.type) > -1; } - visit() { + visit(): boolean { if (this.isBlacklisted()) return false; this.call("enter"); @@ -215,11 +220,22 @@ export default class TraversalPath { } } - has(key) { + has(key): boolean { return !!this.node[key]; } - getTypeAnnotation(): Object { + is(key): boolean { + return this.has(key); + } + + isnt(key): boolean { + return !this.has(key); + } + + getTypeAnnotation(): { + inferred: boolean; + annotation: ?Object; + } { if (this.typeInfo) { return this.typeInfo; } @@ -246,10 +262,15 @@ export default class TraversalPath { resolve(): ?TraversalPath { if (this.isVariableDeclarator()) { - return this.get("init").resolve(); + if (this.get("id").isIdentifier()) { + return this.get("init").resolve(); + } else { + // otherwise it's a request for a destructuring declarator and i'm not + // ready to resolve those just yet + } } else if (this.isIdentifier()) { var binding = this.scope.getBinding(this.node.name); - if (!binding) return; + if (!binding || binding.reassigned) return; if (binding.path === this) { return this; @@ -257,7 +278,7 @@ export default class TraversalPath { return binding.path.resolve();; } } else if (this.isMemberExpression()) { - var targetKey = t.toComputedKey(this.node); + var targetKey = this.toComputedKey(); if (!t.isLiteral(targetKey)) return; var targetName = targetKey.value; @@ -270,27 +291,44 @@ export default class TraversalPath { if (!prop.isProperty()) continue; var key = prop.get("key"); - if (key.isIdentifier({ name: targetName }) || key.isLiteral({ value: targetName })) { - return prop.get("value"); - } + + // { foo: obj } + var match = prop.isnt("computed") && key.isIdentifier({ name: targetName }); + + // { "foo": "obj" } or { ["foo"]: "obj" } + match ||= key.isLiteral({ value: targetName }); + + if (match) return prop.get("value"); } } else { return this; } } - inferType(path: TraversalPath) { + inferType(path: TraversalPath): ?Object { path = path.resolve(); if (!path) return; - if (path.isRestElement() || path.isArrayExpression()) { + if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) { return t.genericTypeAnnotation(t.identifier("Array")); } + if (path.parentPath.isTypeCastExpression()) { + return path.parentPath.node.typeAnnotation; + } + + if (path.isTypeCastExpression()) { + return path.node.typeAnnotation; + } + if (path.isObjectExpression()) { return t.genericTypeAnnotation(t.identifier("Object")); } + if (path.isFunction()) { + return t.identifier("Function"); + } + if (path.isLiteral()) { var value = path.node.value; if (isString(value)) return t.stringTypeAnnotation(); @@ -304,39 +342,44 @@ export default class TraversalPath { } } - isScope() { + isScope(): boolean { return t.isScope(this.node, this.parent); } - isReferencedIdentifier(opts) { + isReferencedIdentifier(opts): boolean { return t.isReferencedIdentifier(this.node, this.parent, opts); } - isReferenced() { + isReferenced(): boolean { return t.isReferenced(this.node, this.parent); } - isBlockScoped() { + isBlockScoped(): boolean { return t.isBlockScoped(this.node); } - isVar() { + isVar(): boolean { return t.isVar(this.node); } - isScope() { + isScope(): boolean { return t.isScope(this.node, this.parent); } - isTypeGeneric(genericName: string, hasTypeParameters?): boolean { - var type = this.getTypeAnnotation().annotation; + isTypeGeneric(genericName: string, opts = {}): boolean { + var typeInfo = this.getTypeAnnotation(); + var type = typeInfo.annotation; if (!type) return false; + if (type.inferred && opts.inference === false) { + return false; + } + if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) { return false; } - if (hasTypeParameters && !type.typeParameters) { + if (opts.requireTypeParameters && !type.typeParameters) { return false; } @@ -348,10 +391,65 @@ export default class TraversalPath { } traverse(opts, state) { - traverse(this.node, opts, this.scope, state); + traverse(this.node, opts, this.scope, state, this); + } + + /** + * Match the current node if it matches the provided `pattern`. + * + * For example, given the match `React.createClass` it would match the + * parsed nodes of `React.createClass` and `React["createClass"]`. + */ + + matchesPattern(pattern: string, allowPartial?: boolean): boolean { + var parts = pattern.split("."); + + // not a member expression + if (!this.isMemberExpression()) return false; + + var search = [this.node]; + var i = 0; + + while (search.length) { + var node = search.shift(); + + if (allowPartial && i === parts.length) { + return true; + } + + if (t.isIdentifier(node)) { + // this part doesn't match + if (parts[i] !== node.name) return false; + } else if (t.isLiteral(node)) { + // this part doesn't match + if (parts[i] !== node.value) return false; + } else if (t.isMemberExpression(node)) { + if (node.computed && !t.isLiteral(node.property)) { + // we can't deal with this + return false; + } else { + search.push(node.object); + search.push(node.property); + continue; + } + } else { + // we can't deal with this + return false; + } + + // too many parts + if (++i > parts.length) { + return false; + } + } + + return true; } } +assign(TraversalPath.prototype, require("./evaluation")); +assign(TraversalPath.prototype, require("./conversion")); + for (var i = 0; i < t.TYPES.length; i++) { let type = t.TYPES[i]; let typeKey = `is${type}`; diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index e58da1aabe..9409ea78cc 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -71,13 +71,6 @@ export default class Scope { */ constructor(path: TraversalPath, parent?: Scope, file?: File) { - var cached = path.getData("scope"); - if (cached) { - return cached; - } else { - path.setData("scope", this); - } - this.parent = parent; this.file = parent ? parent.file : file; @@ -245,7 +238,7 @@ export default class Scope { if (t.isReferencedIdentifier(node, parent) && node.name === oldName) { node.name = newName; } else if (t.isDeclaration(node)) { - var ids = t.getBindingIdentifiers(node); + var ids = this.getBindingIdentifiers(); for (var name in ids) { if (name === oldName) ids[name].name = newName; } @@ -272,7 +265,7 @@ export default class Scope { if (t.isIdentifier(node)) { var binding = this.getBinding(node.name); - if (binding && binding.isTypeGeneric("Array")) return node; + if (binding && binding.isTypeGeneric("Array", { inference: false })) return node; } if (t.isArrayExpression(node)) { @@ -393,15 +386,11 @@ export default class Scope { crawl() { var path = this.path; - var i; // var info = path.getData("scopeInfo"); - if (info) { - extend(this, info); - return; - } + if (info) return extend(this, info); info = path.setData("scopeInfo", { bindings: object(), @@ -414,7 +403,7 @@ export default class Scope { // ForStatement - left, init if (path.isLoop()) { - for (i = 0; i < t.FOR_INIT_KEYS.length; i++) { + for (let i = 0; i < t.FOR_INIT_KEYS.length; i++) { var node = path.get(t.FOR_INIT_KEYS[i]); if (node.isBlockScoped()) this.registerBinding("let", node); } @@ -441,16 +430,16 @@ export default class Scope { if (path.isFunction()) { var params = path.get("params"); - for (i = 0; i < params.length; i++) { + for (let i = 0; i < params.length; i++) { this.registerBinding("param", params[i]); } - this.traverse(path.get("body"), blockVariableVisitor, this); + path.get("body").traverse(blockVariableVisitor, this); } // Program, BlockStatement, Function - let variables if (path.isBlockStatement() || path.isProgram()) { - this.traverse(path.node, blockVariableVisitor, this); + path.traverse(blockVariableVisitor, this); } // CatchClause - param @@ -468,7 +457,7 @@ export default class Scope { // Program, Function - var variables if (path.isProgram() || path.isFunction()) { - this.traverse(path.node, functionVariableVisitor, { + path.traverse(functionVariableVisitor, { blockId: path.get("id").node, scope: this }); @@ -477,7 +466,7 @@ export default class Scope { // Program if (path.isProgram()) { - this.traverse(path.node, programReferenceVisitor, this); + path.traverse(programReferenceVisitor, this); } } @@ -613,26 +602,6 @@ export default class Scope { return binding && binding.identifier; } - /** - * Description - */ - - getOwnImmutableBindingValue(name: string) { - return this._immutableBindingInfoToValue(this.getOwnBindingInfo(name)); - } - - /** - * Description - */ - - getImmutableBindingValue(name: string) { - return this._immutableBindingInfoToValue(this.getBinding(name)); - } - - _immutableBindingInfoToValue(binding) { - if (binding) return binding.getValueIfImmutable(); - } - /** * Description */ diff --git a/src/babel/types/index.js b/src/babel/types/index.js index 79293f0694..a82b86cfc3 100644 --- a/src/babel/types/index.js +++ b/src/babel/types/index.js @@ -24,15 +24,15 @@ function registerType(type: string, skipAliasCheck?: boolean) { }; } -export var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"]; -export var NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", "String"]; -export var FLATTENABLE_KEYS = ["body", "expressions"]; -export var FOR_INIT_KEYS = ["left", "init"]; -export var COMMENT_KEYS = ["leadingComments", "trailingComments"]; +export const STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"]; +export const NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", "String"]; +export const FLATTENABLE_KEYS = ["body", "expressions"]; +export const FOR_INIT_KEYS = ["left", "init"]; +export const COMMENT_KEYS = ["leadingComments", "trailingComments"]; -export var VISITOR_KEYS = require("./visitor-keys"); -export var BUILDER_KEYS = require("./builder-keys"); -export var ALIAS_KEYS = require("./alias-keys"); +export const VISITOR_KEYS = require("./visitor-keys"); +export const BUILDER_KEYS = require("./builder-keys"); +export const ALIAS_KEYS = require("./alias-keys"); t.FLIPPED_ALIAS_KEYS = {}; @@ -52,7 +52,7 @@ each(t.FLIPPED_ALIAS_KEYS, function (types, type) { registerType(type, false); }); -export var TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_ALIAS_KEYS)); +export const TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_ALIAS_KEYS)); /** * Returns whether `node` is of given `type`. @@ -297,7 +297,6 @@ toFastProperties(t); toFastProperties(t.VISITOR_KEYS); exports.__esModule = true; -assign(t, require("./evaluators")); assign(t, require("./retrievers")); assign(t, require("./validators")); assign(t, require("./converters")); diff --git a/src/babel/types/visitor-keys.json b/src/babel/types/visitor-keys.json index e3ad4a629e..b30d9f0106 100644 --- a/src/babel/types/visitor-keys.json +++ b/src/babel/types/visitor-keys.json @@ -93,7 +93,7 @@ "TypeofTypeAnnotation": ["argument"], "TypeAlias": ["id", "typeParameters", "right"], "TypeAnnotation": ["typeAnnotation"], - "TypeCastExpression": ["expression"], + "TypeCastExpression": ["expression", "typeAnnotation"], "TypeParameterDeclaration": ["params"], "TypeParameterInstantiation": ["params"], "ObjectTypeAnnotation": ["key", "value"], diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js index 2087ae351b..9dda19048b 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js @@ -1 +1 @@ -var arr = [for (i of [1, 2, 3]) i * i]; +var arr = [for (i of nums) i * i]; diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js index 2ee858e713..10acf74be9 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js @@ -7,7 +7,7 @@ var arr = (function () { var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var i = _step.value; _arr.push(i * i); @@ -28,4 +28,4 @@ var arr = (function () { } return _arr; -})(); \ No newline at end of file +})(); diff --git a/test/fixtures/transformation/es7-comprehensions/array-this/actual.js b/test/fixtures/transformation/es7-comprehensions/array-this/actual.js index 355ed48188..5ae580c2d3 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-this/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/array-this/actual.js @@ -1,5 +1,5 @@ function add() { - return [for (i of [1, 2, 3]) i * this.multiplier]; + return [for (i of nums) i * this.multiplier]; } add.call({ multiplier: 5 }); diff --git a/test/fixtures/transformation/es7-comprehensions/array-this/expected.js b/test/fixtures/transformation/es7-comprehensions/array-this/expected.js index 5f7f52ab97..4ca4eeaee8 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-this/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-this/expected.js @@ -10,7 +10,7 @@ function add() { var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var i = _step.value; _ref.push(i * _this.multiplier); @@ -34,4 +34,4 @@ function add() { })(); } -add.call({ multiplier: 5 }); \ No newline at end of file +add.call({ multiplier: 5 }); From f4ce216d1c18cbdad92e1e95446d3eb6f76f5925 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:00:41 +1100 Subject: [PATCH 07/24] remove es7.abstractReferences, playground.methodBinding and playground.objectGetterMemoisation --- .../transformers/es7/abstract-references.js | 111 ------------------ .../transformation/transformers/index.js | 3 - .../transformers/playground/method-binding.js | 43 ------- .../playground/object-getter-memoization.js | 43 ------- .../es7-abstract-references/call/actual.js | 6 - .../es7-abstract-references/call/expected.js | 10 -- .../es7-abstract-references/delete/actual.js | 3 - .../delete/expected.js | 5 - .../es7-abstract-references/get/actual.js | 1 - .../es7-abstract-references/get/expected.js | 3 - .../es7-abstract-references/options.json | 3 - .../es7-abstract-references/private/actual.js | 12 -- .../private/expected.js | 30 ----- .../es7-abstract-references/set/actual.js | 3 - .../es7-abstract-references/set/expected.js | 5 - .../playground/method-binding/actual.js | 13 -- .../playground/method-binding/exec.js | 30 ----- .../playground/method-binding/expected.js | 23 ---- .../playground/method-binding/options.json | 3 - .../object-getter-memoization/actual.js | 19 --- .../object-getter-memoization/expected.js | 36 ------ 21 files changed, 405 deletions(-) delete mode 100644 src/babel/transformation/transformers/es7/abstract-references.js delete mode 100644 src/babel/transformation/transformers/playground/method-binding.js delete mode 100644 src/babel/transformation/transformers/playground/object-getter-memoization.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/call/actual.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/call/expected.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/delete/actual.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/delete/expected.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/get/actual.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/get/expected.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/options.json delete mode 100644 test/fixtures/transformation/es7-abstract-references/private/actual.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/private/expected.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/set/actual.js delete mode 100644 test/fixtures/transformation/es7-abstract-references/set/expected.js delete mode 100644 test/fixtures/transformation/playground/method-binding/actual.js delete mode 100644 test/fixtures/transformation/playground/method-binding/exec.js delete mode 100644 test/fixtures/transformation/playground/method-binding/expected.js delete mode 100644 test/fixtures/transformation/playground/method-binding/options.json delete mode 100644 test/fixtures/transformation/playground/object-getter-memoization/actual.js delete mode 100644 test/fixtures/transformation/playground/object-getter-memoization/expected.js diff --git a/src/babel/transformation/transformers/es7/abstract-references.js b/src/babel/transformation/transformers/es7/abstract-references.js deleted file mode 100644 index 7713b41444..0000000000 --- a/src/babel/transformation/transformers/es7/abstract-references.js +++ /dev/null @@ -1,111 +0,0 @@ -// https://github.com/zenparsing/es-abstract-refs - -import * as util from "../../../util"; -import * as t from "../../../types"; - -export var experimental = true; - -var container = function (parent, call, ret, file) { - if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) { - // we don't need to worry about return values - return call; - } else { - var exprs = []; - if (t.isSequenceExpression(call)) { - exprs = call.expressions; - } else { - exprs.push(call); - } - exprs.push(ret); - return t.sequenceExpression(exprs); - } -}; - -export function AssignmentExpression(node, parent, scope, file) { - var left = node.left; - if (!t.isVirtualPropertyExpression(left)) return; - - var value = node.right; - var temp; - - // we need to return `node.right` - if (!t.isExpressionStatement(parent)) { - temp = scope.generateTempBasedOnNode(node.right); - if (temp) value = temp; - } - - if (node.operator !== "=") { - value = t.binaryExpression( - node.operator[0], - util.template("abstract-expression-get", { - PROPERTY: node.property, - OBJECT: node.object - }), - value - ); - } - - var call = util.template("abstract-expression-set", { - PROPERTY: left.property, - OBJECT: left.object, - VALUE: value - }); - - if (temp) { - call = t.sequenceExpression([ - t.assignmentExpression("=", temp, node.right), - call - ]); - } - - return container(parent, call, value, file); -} - -export function UnaryExpression(node, parent, scope, file) { - var arg = node.argument; - if (!t.isVirtualPropertyExpression(arg)) return; - if (node.operator !== "delete") return; - - var call = util.template("abstract-expression-delete", { - PROPERTY: arg.property, - OBJECT: arg.object - }); - - return container(parent, call, t.literal(true), file); -} - -export function CallExpression(node, parent, scope) { - var callee = node.callee; - if (!t.isVirtualPropertyExpression(callee)) return; - - var temp = scope.generateTempBasedOnNode(callee.object); - - var call = util.template("abstract-expression-call", { - PROPERTY: callee.property, - OBJECT: temp || callee.object - }); - - call.arguments = call.arguments.concat(node.arguments); - - if (temp) { - return t.sequenceExpression([ - t.assignmentExpression("=", temp, callee.object), - call - ]); - } else { - return call; - } -} - -export function VirtualPropertyExpression(node) { - return util.template("abstract-expression-get", { - PROPERTY: node.property, - OBJECT: node.object - }); -} - -export function PrivateDeclaration(node) { - return t.variableDeclaration("const", node.declarations.map(function (id) { - return t.variableDeclarator(id, t.newExpression(t.identifier("WeakMap"), [])); - })); -} diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 9b008d23c6..7a24cfd438 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -15,9 +15,7 @@ export default { "es6.arrowFunctions": require("./es6/arrow-functions"), "playground.malletOperator": require("./playground/mallet-operator"), - "playground.methodBinding": require("./playground/method-binding"), "playground.memoizationOperator": require("./playground/memoization-operator"), - "playground.objectGetterMemoization": require("./playground/object-getter-memoization"), reactCompat: require("./other/react-compat"), react: require("./other/react"), @@ -49,7 +47,6 @@ export default { "es6.regex.sticky": require("./es6/regex.sticky"), "es6.regex.unicode": require("./es6/regex.unicode"), - "es7.abstractReferences": require("./es7/abstract-references"), "es6.constants": require("./es6/constants"), diff --git a/src/babel/transformation/transformers/playground/method-binding.js b/src/babel/transformation/transformers/playground/method-binding.js deleted file mode 100644 index 5d4ddff3c7..0000000000 --- a/src/babel/transformation/transformers/playground/method-binding.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as t from "../../../types"; - -export var playground = true; - -export function BindMemberExpression(node, parent, scope) { - var object = node.object; - var prop = node.property; - - var temp = scope.generateTempBasedOnNode(node.object); - if (temp) object = temp; - - var call = t.callExpression( - t.memberExpression(t.memberExpression(object, prop), t.identifier("bind")), - [object, ...node.arguments] - ); - - if (temp) { - return t.sequenceExpression([ - t.assignmentExpression("=", temp, node.object), - call - ]); - } else { - return call; - } -} - -export function BindFunctionExpression(node, parent, scope) { - var buildCall = function (args) { - var param = scope.generateUidIdentifier("val"); - return t.functionExpression(null, [param], t.blockStatement([ - t.returnStatement(t.callExpression(t.memberExpression(param, node.callee), args)) - ])); - }; - - var temp = scope.generateTemp("args"); - - return t.sequenceExpression([ - t.assignmentExpression("=", temp, t.arrayExpression(node.arguments)), - buildCall(node.arguments.map(function (node, i) { - return t.memberExpression(temp, t.literal(i), true); - })) - ]); -} diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js deleted file mode 100644 index dbbd3a065e..0000000000 --- a/src/babel/transformation/transformers/playground/object-getter-memoization.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as t from "../../../types"; - -export var playground = true; - -var visitor = { - enter(node, parent, scope, state) { - if (this.isFunction()) return this.skip(); - - if (this.isReturnStatement() && node.argument) { - node.argument = t.memberExpression(t.callExpression(state.file.addHelper("define-property"), [ - t.thisExpression(), - state.key, - node.argument - ]), state.key, true); - } - } -}; - -export function MethodDefinition(node, parent, scope, file) { - if (node.kind !== "memo") return; - node.kind = "get"; - - console.error("Object getter memoization is deprecated and will be removed in 5.0.0"); - - t.ensureBlock(node.value); - - var key = node.key; - - if (t.isIdentifier(key) && !node.computed) { - key = t.literal(key.name); - } - - var state = { - key: key, - file: file - }; - - this.get("value").traverse(visitor, state); - - return node; -} - -export { MethodDefinition as Property }; diff --git a/test/fixtures/transformation/es7-abstract-references/call/actual.js b/test/fixtures/transformation/es7-abstract-references/call/actual.js deleted file mode 100644 index 51355b9a69..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/call/actual.js +++ /dev/null @@ -1,6 +0,0 @@ -foo::bar(); -foo::bar("arg"); - -var test = "test"; -test::bar(); -test::bar("arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/call/expected.js b/test/fixtures/transformation/es7-abstract-references/call/expected.js deleted file mode 100644 index 55bdecc949..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/call/expected.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -var _foo, _foo2; - -_foo = foo, bar[Symbol.referenceGet](_foo).call(_foo); -_foo2 = foo, bar[Symbol.referenceGet](_foo2).call(_foo2, "arg"); - -var test = "test"; -bar[Symbol.referenceGet](test).call(test); -bar[Symbol.referenceGet](test).call(test, "arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/delete/actual.js b/test/fixtures/transformation/es7-abstract-references/delete/actual.js deleted file mode 100644 index af9f86c23c..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/delete/actual.js +++ /dev/null @@ -1,3 +0,0 @@ -delete foo::bar; - -if (delete foo::bar) {} diff --git a/test/fixtures/transformation/es7-abstract-references/delete/expected.js b/test/fixtures/transformation/es7-abstract-references/delete/expected.js deleted file mode 100644 index 489c0b7863..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/delete/expected.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -bar[Symbol.referenceDelete](foo); - -if ((bar[Symbol.referenceDelete](foo), true)) {} diff --git a/test/fixtures/transformation/es7-abstract-references/get/actual.js b/test/fixtures/transformation/es7-abstract-references/get/actual.js deleted file mode 100644 index d6a275fb29..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/get/actual.js +++ /dev/null @@ -1 +0,0 @@ -foo::bar; diff --git a/test/fixtures/transformation/es7-abstract-references/get/expected.js b/test/fixtures/transformation/es7-abstract-references/get/expected.js deleted file mode 100644 index 70419d5d74..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/get/expected.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -bar[Symbol.referenceGet](foo); diff --git a/test/fixtures/transformation/es7-abstract-references/options.json b/test/fixtures/transformation/es7-abstract-references/options.json deleted file mode 100644 index 252f473a73..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "experimental": true -} diff --git a/test/fixtures/transformation/es7-abstract-references/private/actual.js b/test/fixtures/transformation/es7-abstract-references/private/actual.js deleted file mode 100644 index 9643ff19c2..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/private/actual.js +++ /dev/null @@ -1,12 +0,0 @@ -private A; -private B, C; - -class D { - private E; - private F, G; -} - -var H = class { - private I; - private J, K; -}; diff --git a/test/fixtures/transformation/es7-abstract-references/private/expected.js b/test/fixtures/transformation/es7-abstract-references/private/expected.js deleted file mode 100644 index 53ef631e1d..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/private/expected.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; - -var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; - -var A = new WeakMap(); -var B = new WeakMap(), - C = new WeakMap(); - -var D = (function () { - var F = new WeakMap(), - G = new WeakMap(); - var E = new WeakMap(); - - function D() { - _classCallCheck(this, D); - } - - return D; -})(); - -var H = (function () { - var _class = function H() { - _classCallCheck(this, _class); - }; - - var J = new WeakMap(), - K = new WeakMap(); - var I = new WeakMap(); - return _class; -})(); \ No newline at end of file diff --git a/test/fixtures/transformation/es7-abstract-references/set/actual.js b/test/fixtures/transformation/es7-abstract-references/set/actual.js deleted file mode 100644 index 8abf694209..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/set/actual.js +++ /dev/null @@ -1,3 +0,0 @@ -var baz = "foo"; -foo::bar = baz; -if (foo::bar = baz) {} diff --git a/test/fixtures/transformation/es7-abstract-references/set/expected.js b/test/fixtures/transformation/es7-abstract-references/set/expected.js deleted file mode 100644 index be9e6ef9f3..0000000000 --- a/test/fixtures/transformation/es7-abstract-references/set/expected.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -var baz = "foo"; -bar[Symbol.referenceSet](foo, baz); -if ((bar[Symbol.referenceSet](foo, baz), baz)) {} diff --git a/test/fixtures/transformation/playground/method-binding/actual.js b/test/fixtures/transformation/playground/method-binding/actual.js deleted file mode 100644 index 12185e1a7c..0000000000 --- a/test/fixtures/transformation/playground/method-binding/actual.js +++ /dev/null @@ -1,13 +0,0 @@ -var fn = obj#method; -var fn = obj#method("foob"); -var fn = obj[foo]#method; -var fn = obj.foo#method; -var fn = obj[foo()]#method; - -["foo", "bar"].map(#toUpperCase); -[1.1234, 23.53245, 3].map(#toFixed(2)); - -var get = function () { - return 2; -}; -[1.1234, 23.53245, 3].map(#toFixed(get())); diff --git a/test/fixtures/transformation/playground/method-binding/exec.js b/test/fixtures/transformation/playground/method-binding/exec.js deleted file mode 100644 index b83e4b49aa..0000000000 --- a/test/fixtures/transformation/playground/method-binding/exec.js +++ /dev/null @@ -1,30 +0,0 @@ -var obj = { - foo: "foo", - bar: "bar", - getFoo: function () { - return this.foo; - }, - getBar: function (arg) { - return arg + " " + this.bar; - }, - getZoo: function (a, b) { - return this.foo + " " + this.bar + " " + a + " " + b; - } -}; - -var foo = obj#getFoo; -assert.equal(foo(), "foo"); - -var bar = obj#getBar("foo"); -assert.equal(bar(), "foo bar"); - -var zoo = obj#getZoo("foo"); -assert.equal(zoo("bar"), "foo bar foo bar"); - -assert.deepEqual(["foo", "bar"].map(#toUpperCase), ["FOO", "BAR"]); -assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(2)), ["1.12", "23.53", "3.00"]); - -var get = function () { - return 2; -} -assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(get())), ["1.12", "23.53", "3.00"]); diff --git a/test/fixtures/transformation/playground/method-binding/expected.js b/test/fixtures/transformation/playground/method-binding/expected.js deleted file mode 100644 index 6f76e45d89..0000000000 --- a/test/fixtures/transformation/playground/method-binding/expected.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; - -var _obj, _obj2, _obj$foo, _obj$foo2, _obj$foo3, _args, _args2, _args3; - -var fn = (_obj = obj, _obj.method.bind(_obj)); -var fn = (_obj2 = obj, _obj2.method.bind(_obj2, "foob")); -var fn = (_obj$foo = obj[foo], _obj$foo.method.bind(_obj$foo)); -var fn = (_obj$foo2 = obj.foo, _obj$foo2.method.bind(_obj$foo2)); -var fn = (_obj$foo3 = obj[foo()], _obj$foo3.method.bind(_obj$foo3)); - -["foo", "bar"].map((_args = [], function (_val) { - return _val.toUpperCase(); -})); -[1.1234, 23.53245, 3].map((_args2 = [2], function (_val2) { - return _val2.toFixed(_args2[0]); -})); - -var get = function get() { - return 2; -}; -[1.1234, 23.53245, 3].map((_args3 = [get()], function (_val3) { - return _val3.toFixed(_args3[0]); -})); diff --git a/test/fixtures/transformation/playground/method-binding/options.json b/test/fixtures/transformation/playground/method-binding/options.json deleted file mode 100644 index b8b90ecb11..0000000000 --- a/test/fixtures/transformation/playground/method-binding/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "blacklist": ["es6.tailCall"] -} diff --git a/test/fixtures/transformation/playground/object-getter-memoization/actual.js b/test/fixtures/transformation/playground/object-getter-memoization/actual.js deleted file mode 100644 index 9b0d317c04..0000000000 --- a/test/fixtures/transformation/playground/object-getter-memoization/actual.js +++ /dev/null @@ -1,19 +0,0 @@ -class Foo { - memo bar() { - return complex(); - } - - memo [bar]() { - return complex(); - } -} - -var foo = { - memo bar() { - return complex(); - }, - - memo [bar]() { - return complex(); - } -}; diff --git a/test/fixtures/transformation/playground/object-getter-memoization/expected.js b/test/fixtures/transformation/playground/object-getter-memoization/expected.js deleted file mode 100644 index 214f9235f5..0000000000 --- a/test/fixtures/transformation/playground/object-getter-memoization/expected.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; - -var Foo = (function () { - function Foo() { - babelHelpers.classCallCheck(this, Foo); - } - - babelHelpers.createComputedClass(Foo, [{ - key: "bar", - get: function () { - return babelHelpers.defineProperty(this, "bar", complex()).bar; - } - }, { - key: bar, - get: function () { - return babelHelpers.defineProperty(this, bar, complex())[bar]; - } - }]); - return Foo; -})(); - -var foo = Object.defineProperties({}, babelHelpers.defineProperty({ - bar: { - get: function () { - return babelHelpers.defineProperty(this, "bar", complex()).bar; - }, - configurable: true, - enumerable: true - } -}, bar, { - get: function () { - return babelHelpers.defineProperty(this, bar, complex())[bar]; - }, - configurable: true, - enumerable: true -})); \ No newline at end of file From 9e61a307d40451940c2a5c425b8d7b480459d001 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:34:02 +1100 Subject: [PATCH 08/24] fix scope traversal --- src/babel/traversal/scope.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index c653187a42..fc9fd8dfcb 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -440,13 +440,13 @@ export default class Scope { for (let i = 0; i < params.length; i++) { this.registerBinding("param", params[i]); } - path.get("body").traverse(blockVariableVisitor, this); + this.traverse(path.get("body").node, blockVariableVisitor, this); } // Program, BlockStatement, Function - let variables if (path.isBlockStatement() || path.isProgram()) { - path.traverse(blockVariableVisitor, this); + this.traverse(path.node, blockVariableVisitor, this); } // CatchClause - param @@ -464,7 +464,7 @@ export default class Scope { // Program, Function - var variables if (path.isProgram() || path.isFunction()) { - path.traverse(functionVariableVisitor, { + this.traverse(path.node, functionVariableVisitor, { blockId: path.get("id").node, scope: this }); @@ -473,7 +473,7 @@ export default class Scope { // Program if (path.isProgram()) { - path.traverse(programReferenceVisitor, this); + this.traverse(path.node, programReferenceVisitor, this); } } From a1ab8fa0c12090df5ee0d1d25c302d1e0d6a65df Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:57:39 +1100 Subject: [PATCH 09/24] remove playground.memoisationOperator deprecation message --- .../transformers/playground/memoization-operator.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/babel/transformation/transformers/playground/memoization-operator.js b/src/babel/transformation/transformers/playground/memoization-operator.js index f8d55d854c..7c63ad102a 100644 --- a/src/babel/transformation/transformers/playground/memoization-operator.js +++ b/src/babel/transformation/transformers/playground/memoization-operator.js @@ -11,7 +11,6 @@ build(exports, { }, build(node, file) { - console.error("The memoization operator is deprecated and will be removed in 5.0.0"); return t.unaryExpression( "!", t.callExpression( From 547ecc2f73928ab40ee03d7ef21af2aaf7464137 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:57:53 +1100 Subject: [PATCH 10/24] update tests to reflect new for-of behaviour --- .../es6-block-scoping/hoisting/actual.js | 2 +- .../es6-block-scoping/hoisting/expected.js | 4 +-- .../es7-comprehensions/arguments/actual.js | 2 +- .../es7-comprehensions/arguments/expected.js | 4 +-- .../array-expression-single-if/expected.js | 29 ++++--------------- .../array-expression-single/actual.js | 2 +- .../array-expression-single/expected.js | 25 +++------------- 7 files changed, 17 insertions(+), 51 deletions(-) diff --git a/test/fixtures/transformation/es6-block-scoping/hoisting/actual.js b/test/fixtures/transformation/es6-block-scoping/hoisting/actual.js index 8baf9f54a2..03757cd57d 100644 --- a/test/fixtures/transformation/es6-block-scoping/hoisting/actual.js +++ b/test/fixtures/transformation/es6-block-scoping/hoisting/actual.js @@ -1,4 +1,4 @@ -for (let i of [1, 2, 3]) { +for (let i of nums) { var x = 5; fns.push(function () { return i * x; diff --git a/test/fixtures/transformation/es6-block-scoping/hoisting/expected.js b/test/fixtures/transformation/es6-block-scoping/hoisting/expected.js index f8cec2e342..661e28888b 100644 --- a/test/fixtures/transformation/es6-block-scoping/hoisting/expected.js +++ b/test/fixtures/transformation/es6-block-scoping/hoisting/expected.js @@ -5,7 +5,7 @@ var _didIteratorError = false; var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var x; (function () { @@ -30,4 +30,4 @@ try { throw _iteratorError; } } -} \ No newline at end of file +} diff --git a/test/fixtures/transformation/es7-comprehensions/arguments/actual.js b/test/fixtures/transformation/es7-comprehensions/arguments/actual.js index 95f9a4df57..5b07664e61 100644 --- a/test/fixtures/transformation/es7-comprehensions/arguments/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/arguments/actual.js @@ -1,5 +1,5 @@ function add() { - return [for (i of [1, 2, 3]) i * arguments[0]]; + return [for (i of nums) i * arguments[0]]; } add(5); diff --git a/test/fixtures/transformation/es7-comprehensions/arguments/expected.js b/test/fixtures/transformation/es7-comprehensions/arguments/expected.js index 610d5ef84b..ea21a12ae3 100644 --- a/test/fixtures/transformation/es7-comprehensions/arguments/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/arguments/expected.js @@ -10,7 +10,7 @@ function add() { var _iteratorError = undefined; try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var i = _step.value; _ref.push(i * _arguments[0]); @@ -34,4 +34,4 @@ function add() { })(); } -add(5); \ No newline at end of file +add(5); diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single-if/expected.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single-if/expected.js index e0815299e2..f36d1c6137 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single-if/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single-if/expected.js @@ -2,32 +2,15 @@ var arr = (function () { var _arr = []; - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; + var _arr2 = [1, 2, 3]; - try { - for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var i = _step.value; + for (var _i = 0; _i < _arr2.length; _i++) { + var i = _arr2[_i]; - if (i > 1) { - _arr.push(i * i); - } - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator["return"]) { - _iterator["return"](); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } + if (i > 1) { + _arr.push(i * i); } } return _arr; -})(); \ No newline at end of file +})(); diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js index 9dda19048b..2087ae351b 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/actual.js @@ -1 +1 @@ -var arr = [for (i of nums) i * i]; +var arr = [for (i of [1, 2, 3]) i * i]; diff --git a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js index 10acf74be9..0758f1ede6 100644 --- a/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js +++ b/test/fixtures/transformation/es7-comprehensions/array-expression-single/expected.js @@ -2,29 +2,12 @@ var arr = (function () { var _arr = []; - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; + var _arr2 = [1, 2, 3]; - try { - for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var i = _step.value; + for (var _i = 0; _i < _arr2.length; _i++) { + var i = _arr2[_i]; - _arr.push(i * i); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator["return"]) { - _iterator["return"](); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } + _arr.push(i * i); } return _arr; From 056b7fc63463c40e67b5522980a2e6a9dad176d2 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:58:02 +1100 Subject: [PATCH 11/24] hoist uids to their parent function --- src/babel/traversal/scope.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index fc9fd8dfcb..4f4619add1 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -133,7 +133,7 @@ export default class Scope { uid = this._generateUid(name, i); i++; } while (this.hasBinding(uid) || this.hasGlobal(uid) || this.hasUid(uid)); - this.uids[uid] = true; + this.getFunctionParent().uids[uid] = true; return uid; } From 1f9f57a2a6526c2b438308bd76d1ca0ef331d59c Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 03:58:18 +1100 Subject: [PATCH 12/24] use paths in replace supers helper --- .../transformation/helpers/replace-supers.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/babel/transformation/helpers/replace-supers.js b/src/babel/transformation/helpers/replace-supers.js index 0796a21300..aa80001ff6 100644 --- a/src/babel/transformation/helpers/replace-supers.js +++ b/src/babel/transformation/helpers/replace-supers.js @@ -39,7 +39,7 @@ var visitor = { var callback = self.specHandle; if (self.isLoose) callback = self.looseHandle; - return callback.call(self, getThisReference, node, parent); + return callback.call(self, this, getThisReference); } }; @@ -191,11 +191,12 @@ export default class ReplaceSupers { * Description */ - looseHandle(getThisReference: Function, node: Object, parent: Object) { - if (t.isIdentifier(node, { name: "super" })) { + looseHandle(path: TraversalPath, getThisReference: Function) { + var node = path.node; + if (path.isIdentifier({ name: "super" })) { this.hasSuper = true; - return this.getLooseSuperProperty(node, parent); - } else if (t.isCallExpression(node)) { + return this.getLooseSuperProperty(node, path.parent); + } else if (path.isCallExpression()) { var callee = node.callee; if (!t.isMemberExpression(callee)) return; if (callee.object.name !== "super") return; @@ -211,15 +212,18 @@ export default class ReplaceSupers { * Description */ - specHandle(getThisReference: Function, node: Object, parent: Object) { + specHandle(path: TraversalPath, getThisReference: Function) { var methodNode = this.methodNode; var property; var computed; var args; var thisReference; + var parent = path.parent; + var node = path.node; + if (isIllegalBareSuper(node, parent)) { - throw this.errorWithNode(messages.get("classesIllegalBareSuper")); + throw path.errorWithNode(messages.get("classesIllegalBareSuper")); } if (t.isCallExpression(node)) { From 080a844457fc5c091ca8ee342af72568e985b1e1 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 04:44:51 +1100 Subject: [PATCH 13/24] rename "reassigned" to "constant" to reflect purpose of the property better --- src/babel/transformation/transformers/es6/classes.js | 2 +- .../transformation/transformers/es6/destructuring.js | 2 +- src/babel/transformation/transformers/es6/tail-call.js | 2 +- src/babel/traversal/binding.js | 4 ++-- src/babel/traversal/path/conversion.js | 2 +- src/babel/traversal/path/index.js | 8 +++++--- src/babel/traversal/scope.js | 8 ++++---- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index bf66ef32f1..edcb986330 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -209,7 +209,7 @@ class ClassTransformer { } // we have no constructor, we have a super, and the super doesn't appear to be falsy - if (!this.hasConstructor && this.hasSuper) { // todo: t.evaluateTruthy(superName, this.scope) !== false + if (!this.hasConstructor && this.hasSuper && this.path.get("superClass").evaluateTruthy() !== false) { var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; constructor.body.body.push(util.template(helperName, { diff --git a/src/babel/transformation/transformers/es6/destructuring.js b/src/babel/transformation/transformers/es6/destructuring.js index 7fecf07eef..a27e70c907 100644 --- a/src/babel/transformation/transformers/es6/destructuring.js +++ b/src/babel/transformation/transformers/es6/destructuring.js @@ -432,7 +432,7 @@ class DestructuringTransformer { } else { arrayRef = this.scope.generateUidBasedOnNode(arrayRef); this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray)); - this.getBinding(arrayRef.name).assignTypeGeneric("Array"); + //this.getBinding(arrayRef.name).assignTypeGeneric("Array"); } // diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 122f456f2a..c0efe08ec5 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -144,7 +144,7 @@ class TailCallTransformer { // check if the ownerId has been reassigned, if it has then it's not safe to // perform optimisations var ownerIdInfo = this.scope.getBinding(this.ownerId.name); - return ownerIdInfo && ownerIdInfo.reassigned; + return ownerIdInfo && !ownerIdInfo.constant; } run() { diff --git a/src/babel/traversal/binding.js b/src/babel/traversal/binding.js index fe4d29a096..ee2cdddf0a 100644 --- a/src/babel/traversal/binding.js +++ b/src/babel/traversal/binding.js @@ -3,7 +3,7 @@ import * as t from "../types"; export default class Binding { constructor({ identifier, scope, path, kind }) { this.identifier = identifier; - this.reassigned = false; + this.constant = true; this.scope = scope; this.path = path; this.kind = kind; @@ -50,7 +50,7 @@ export default class Binding { */ reassign() { - this.reassigned = true; + this.constant = false; if (this.typeAnnotationInferred) { // destroy the inferred typeAnnotation diff --git a/src/babel/traversal/path/conversion.js b/src/babel/traversal/path/conversion.js index e0271db76d..24293c7d8b 100644 --- a/src/babel/traversal/path/conversion.js +++ b/src/babel/traversal/path/conversion.js @@ -1,4 +1,4 @@ -import * as t from "./index"; +import * as t from "../../types"; /** * Description diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index c6891e07b6..f5970b7cf4 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -61,8 +61,10 @@ export default class TraversalPath { return this.data[key] = val; } - getData(key) { - return this.data[key]; + getData(key, def) { + var val = this.data[key]; + if (!val && def) val = this.data[key] = def; + return val; } setScope(file?) { @@ -270,7 +272,7 @@ export default class TraversalPath { } } else if (this.isIdentifier()) { var binding = this.scope.getBinding(this.node.name); - if (!binding || binding.reassigned) return; + if (!binding || !binding.constant) return; if (binding.path === this) { return this; diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index 4f4619add1..e7f8ea1791 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -44,11 +44,11 @@ var programReferenceVisitor = { } else if (t.isLabeledStatement(node)) { state.addGlobal(node); } else if (t.isAssignmentExpression(node)) { - scope.registerBindingReassignment(this.get("left"), this.get("right")); + scope.registerConstantViolation(this.get("left"), this.get("right")); } else if (t.isUpdateExpression(node)) { - // TODO + scope.registerConstantViolation(this.get("argument"), null); } else if (t.isUnaryExpression(node) && node.operator === "delete") { - scope.registerBindingReassignment(this.get("left"), null); + scope.registerConstantViolation(this.get("left"), null); } } }; @@ -320,7 +320,7 @@ export default class Scope { * Description */ - registerBindingReassignment(left: TraversalPath, right: TraversalPath) { + registerConstantViolation(left: TraversalPath, right: TraversalPath) { var ids = left.getBindingIdentifiers(); for (var name in ids) { var binding = this.getBinding(name); From 9676056f0863bddbb67b774ea0a4035a028e4ed0 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 04:50:23 +1100 Subject: [PATCH 14/24] add note about member expression resolution --- src/babel/transformation/transformers/es6/classes.js | 2 +- src/babel/traversal/path/index.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index edcb986330..46126e45dd 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -209,7 +209,7 @@ class ClassTransformer { } // we have no constructor, we have a super, and the super doesn't appear to be falsy - if (!this.hasConstructor && this.hasSuper && this.path.get("superClass").evaluateTruthy() !== false) { + if (!this.hasConstructor && this.hasSuper) { var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; constructor.body.body.push(util.template(helperName, { diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index f5970b7cf4..64fed848f0 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -277,9 +277,12 @@ export default class TraversalPath { if (binding.path === this) { return this; } else { - return binding.path.resolve();; + return binding.path.resolve(); } } else if (this.isMemberExpression()) { + // this is dangerous, as non-direct target assignments will mutate it's state + // making this resolution inaccurate + var targetKey = this.toComputedKey(); if (!t.isLiteral(targetKey)) return; var targetName = targetKey.value; From 5ee9b3c37e04469605157802191e15746a6d3fd3 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 14 Mar 2015 04:50:46 +1100 Subject: [PATCH 15/24] remove irrelevant test --- test/fixtures/transformation/es6-spread/known-array/actual.js | 2 -- .../transformation/es6-spread/known-array/expected.js | 4 ---- 2 files changed, 6 deletions(-) delete mode 100644 test/fixtures/transformation/es6-spread/known-array/actual.js delete mode 100644 test/fixtures/transformation/es6-spread/known-array/expected.js diff --git a/test/fixtures/transformation/es6-spread/known-array/actual.js b/test/fixtures/transformation/es6-spread/known-array/actual.js deleted file mode 100644 index af3c9258a8..0000000000 --- a/test/fixtures/transformation/es6-spread/known-array/actual.js +++ /dev/null @@ -1,2 +0,0 @@ -var arr: Array = bar; -[...arr]; diff --git a/test/fixtures/transformation/es6-spread/known-array/expected.js b/test/fixtures/transformation/es6-spread/known-array/expected.js deleted file mode 100644 index cba7214bfd..0000000000 --- a/test/fixtures/transformation/es6-spread/known-array/expected.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict"; - -var arr = bar; -[].concat(arr); From a8cedf813bf12994bd8bc8afeab192671bd90c12 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:02:35 +1100 Subject: [PATCH 16/24] Revert "remove es7.abstractReferences, playground.methodBinding and playground.objectGetterMemoisation" This reverts commit f4ce216d1c18cbdad92e1e95446d3eb6f76f5925. --- .../transformers/es7/abstract-references.js | 111 ++++++++++++++++++ .../transformation/transformers/index.js | 3 + .../transformers/playground/method-binding.js | 43 +++++++ .../playground/object-getter-memoization.js | 43 +++++++ .../es7-abstract-references/call/actual.js | 6 + .../es7-abstract-references/call/expected.js | 10 ++ .../es7-abstract-references/delete/actual.js | 3 + .../delete/expected.js | 5 + .../es7-abstract-references/get/actual.js | 1 + .../es7-abstract-references/get/expected.js | 3 + .../es7-abstract-references/options.json | 3 + .../es7-abstract-references/private/actual.js | 12 ++ .../private/expected.js | 30 +++++ .../es7-abstract-references/set/actual.js | 3 + .../es7-abstract-references/set/expected.js | 5 + .../playground/method-binding/actual.js | 13 ++ .../playground/method-binding/exec.js | 30 +++++ .../playground/method-binding/expected.js | 23 ++++ .../playground/method-binding/options.json | 3 + .../object-getter-memoization/actual.js | 19 +++ .../object-getter-memoization/expected.js | 36 ++++++ 21 files changed, 405 insertions(+) create mode 100644 src/babel/transformation/transformers/es7/abstract-references.js create mode 100644 src/babel/transformation/transformers/playground/method-binding.js create mode 100644 src/babel/transformation/transformers/playground/object-getter-memoization.js create mode 100644 test/fixtures/transformation/es7-abstract-references/call/actual.js create mode 100644 test/fixtures/transformation/es7-abstract-references/call/expected.js create mode 100644 test/fixtures/transformation/es7-abstract-references/delete/actual.js create mode 100644 test/fixtures/transformation/es7-abstract-references/delete/expected.js create mode 100644 test/fixtures/transformation/es7-abstract-references/get/actual.js create mode 100644 test/fixtures/transformation/es7-abstract-references/get/expected.js create mode 100644 test/fixtures/transformation/es7-abstract-references/options.json create mode 100644 test/fixtures/transformation/es7-abstract-references/private/actual.js create mode 100644 test/fixtures/transformation/es7-abstract-references/private/expected.js create mode 100644 test/fixtures/transformation/es7-abstract-references/set/actual.js create mode 100644 test/fixtures/transformation/es7-abstract-references/set/expected.js create mode 100644 test/fixtures/transformation/playground/method-binding/actual.js create mode 100644 test/fixtures/transformation/playground/method-binding/exec.js create mode 100644 test/fixtures/transformation/playground/method-binding/expected.js create mode 100644 test/fixtures/transformation/playground/method-binding/options.json create mode 100644 test/fixtures/transformation/playground/object-getter-memoization/actual.js create mode 100644 test/fixtures/transformation/playground/object-getter-memoization/expected.js diff --git a/src/babel/transformation/transformers/es7/abstract-references.js b/src/babel/transformation/transformers/es7/abstract-references.js new file mode 100644 index 0000000000..7713b41444 --- /dev/null +++ b/src/babel/transformation/transformers/es7/abstract-references.js @@ -0,0 +1,111 @@ +// https://github.com/zenparsing/es-abstract-refs + +import * as util from "../../../util"; +import * as t from "../../../types"; + +export var experimental = true; + +var container = function (parent, call, ret, file) { + if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) { + // we don't need to worry about return values + return call; + } else { + var exprs = []; + if (t.isSequenceExpression(call)) { + exprs = call.expressions; + } else { + exprs.push(call); + } + exprs.push(ret); + return t.sequenceExpression(exprs); + } +}; + +export function AssignmentExpression(node, parent, scope, file) { + var left = node.left; + if (!t.isVirtualPropertyExpression(left)) return; + + var value = node.right; + var temp; + + // we need to return `node.right` + if (!t.isExpressionStatement(parent)) { + temp = scope.generateTempBasedOnNode(node.right); + if (temp) value = temp; + } + + if (node.operator !== "=") { + value = t.binaryExpression( + node.operator[0], + util.template("abstract-expression-get", { + PROPERTY: node.property, + OBJECT: node.object + }), + value + ); + } + + var call = util.template("abstract-expression-set", { + PROPERTY: left.property, + OBJECT: left.object, + VALUE: value + }); + + if (temp) { + call = t.sequenceExpression([ + t.assignmentExpression("=", temp, node.right), + call + ]); + } + + return container(parent, call, value, file); +} + +export function UnaryExpression(node, parent, scope, file) { + var arg = node.argument; + if (!t.isVirtualPropertyExpression(arg)) return; + if (node.operator !== "delete") return; + + var call = util.template("abstract-expression-delete", { + PROPERTY: arg.property, + OBJECT: arg.object + }); + + return container(parent, call, t.literal(true), file); +} + +export function CallExpression(node, parent, scope) { + var callee = node.callee; + if (!t.isVirtualPropertyExpression(callee)) return; + + var temp = scope.generateTempBasedOnNode(callee.object); + + var call = util.template("abstract-expression-call", { + PROPERTY: callee.property, + OBJECT: temp || callee.object + }); + + call.arguments = call.arguments.concat(node.arguments); + + if (temp) { + return t.sequenceExpression([ + t.assignmentExpression("=", temp, callee.object), + call + ]); + } else { + return call; + } +} + +export function VirtualPropertyExpression(node) { + return util.template("abstract-expression-get", { + PROPERTY: node.property, + OBJECT: node.object + }); +} + +export function PrivateDeclaration(node) { + return t.variableDeclaration("const", node.declarations.map(function (id) { + return t.variableDeclarator(id, t.newExpression(t.identifier("WeakMap"), [])); + })); +} diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 7a24cfd438..9b008d23c6 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -15,7 +15,9 @@ export default { "es6.arrowFunctions": require("./es6/arrow-functions"), "playground.malletOperator": require("./playground/mallet-operator"), + "playground.methodBinding": require("./playground/method-binding"), "playground.memoizationOperator": require("./playground/memoization-operator"), + "playground.objectGetterMemoization": require("./playground/object-getter-memoization"), reactCompat: require("./other/react-compat"), react: require("./other/react"), @@ -47,6 +49,7 @@ export default { "es6.regex.sticky": require("./es6/regex.sticky"), "es6.regex.unicode": require("./es6/regex.unicode"), + "es7.abstractReferences": require("./es7/abstract-references"), "es6.constants": require("./es6/constants"), diff --git a/src/babel/transformation/transformers/playground/method-binding.js b/src/babel/transformation/transformers/playground/method-binding.js new file mode 100644 index 0000000000..5d4ddff3c7 --- /dev/null +++ b/src/babel/transformation/transformers/playground/method-binding.js @@ -0,0 +1,43 @@ +import * as t from "../../../types"; + +export var playground = true; + +export function BindMemberExpression(node, parent, scope) { + var object = node.object; + var prop = node.property; + + var temp = scope.generateTempBasedOnNode(node.object); + if (temp) object = temp; + + var call = t.callExpression( + t.memberExpression(t.memberExpression(object, prop), t.identifier("bind")), + [object, ...node.arguments] + ); + + if (temp) { + return t.sequenceExpression([ + t.assignmentExpression("=", temp, node.object), + call + ]); + } else { + return call; + } +} + +export function BindFunctionExpression(node, parent, scope) { + var buildCall = function (args) { + var param = scope.generateUidIdentifier("val"); + return t.functionExpression(null, [param], t.blockStatement([ + t.returnStatement(t.callExpression(t.memberExpression(param, node.callee), args)) + ])); + }; + + var temp = scope.generateTemp("args"); + + return t.sequenceExpression([ + t.assignmentExpression("=", temp, t.arrayExpression(node.arguments)), + buildCall(node.arguments.map(function (node, i) { + return t.memberExpression(temp, t.literal(i), true); + })) + ]); +} diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js new file mode 100644 index 0000000000..dbbd3a065e --- /dev/null +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -0,0 +1,43 @@ +import * as t from "../../../types"; + +export var playground = true; + +var visitor = { + enter(node, parent, scope, state) { + if (this.isFunction()) return this.skip(); + + if (this.isReturnStatement() && node.argument) { + node.argument = t.memberExpression(t.callExpression(state.file.addHelper("define-property"), [ + t.thisExpression(), + state.key, + node.argument + ]), state.key, true); + } + } +}; + +export function MethodDefinition(node, parent, scope, file) { + if (node.kind !== "memo") return; + node.kind = "get"; + + console.error("Object getter memoization is deprecated and will be removed in 5.0.0"); + + t.ensureBlock(node.value); + + var key = node.key; + + if (t.isIdentifier(key) && !node.computed) { + key = t.literal(key.name); + } + + var state = { + key: key, + file: file + }; + + this.get("value").traverse(visitor, state); + + return node; +} + +export { MethodDefinition as Property }; diff --git a/test/fixtures/transformation/es7-abstract-references/call/actual.js b/test/fixtures/transformation/es7-abstract-references/call/actual.js new file mode 100644 index 0000000000..51355b9a69 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/actual.js @@ -0,0 +1,6 @@ +foo::bar(); +foo::bar("arg"); + +var test = "test"; +test::bar(); +test::bar("arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/call/expected.js b/test/fixtures/transformation/es7-abstract-references/call/expected.js new file mode 100644 index 0000000000..55bdecc949 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +var _foo, _foo2; + +_foo = foo, bar[Symbol.referenceGet](_foo).call(_foo); +_foo2 = foo, bar[Symbol.referenceGet](_foo2).call(_foo2, "arg"); + +var test = "test"; +bar[Symbol.referenceGet](test).call(test); +bar[Symbol.referenceGet](test).call(test, "arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/delete/actual.js b/test/fixtures/transformation/es7-abstract-references/delete/actual.js new file mode 100644 index 0000000000..af9f86c23c --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/actual.js @@ -0,0 +1,3 @@ +delete foo::bar; + +if (delete foo::bar) {} diff --git a/test/fixtures/transformation/es7-abstract-references/delete/expected.js b/test/fixtures/transformation/es7-abstract-references/delete/expected.js new file mode 100644 index 0000000000..489c0b7863 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +bar[Symbol.referenceDelete](foo); + +if ((bar[Symbol.referenceDelete](foo), true)) {} diff --git a/test/fixtures/transformation/es7-abstract-references/get/actual.js b/test/fixtures/transformation/es7-abstract-references/get/actual.js new file mode 100644 index 0000000000..d6a275fb29 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/actual.js @@ -0,0 +1 @@ +foo::bar; diff --git a/test/fixtures/transformation/es7-abstract-references/get/expected.js b/test/fixtures/transformation/es7-abstract-references/get/expected.js new file mode 100644 index 0000000000..70419d5d74 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +bar[Symbol.referenceGet](foo); diff --git a/test/fixtures/transformation/es7-abstract-references/options.json b/test/fixtures/transformation/es7-abstract-references/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es7-abstract-references/private/actual.js b/test/fixtures/transformation/es7-abstract-references/private/actual.js new file mode 100644 index 0000000000..9643ff19c2 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/private/actual.js @@ -0,0 +1,12 @@ +private A; +private B, C; + +class D { + private E; + private F, G; +} + +var H = class { + private I; + private J, K; +}; diff --git a/test/fixtures/transformation/es7-abstract-references/private/expected.js b/test/fixtures/transformation/es7-abstract-references/private/expected.js new file mode 100644 index 0000000000..53ef631e1d --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/private/expected.js @@ -0,0 +1,30 @@ +"use strict"; + +var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + +var A = new WeakMap(); +var B = new WeakMap(), + C = new WeakMap(); + +var D = (function () { + var F = new WeakMap(), + G = new WeakMap(); + var E = new WeakMap(); + + function D() { + _classCallCheck(this, D); + } + + return D; +})(); + +var H = (function () { + var _class = function H() { + _classCallCheck(this, _class); + }; + + var J = new WeakMap(), + K = new WeakMap(); + var I = new WeakMap(); + return _class; +})(); \ No newline at end of file diff --git a/test/fixtures/transformation/es7-abstract-references/set/actual.js b/test/fixtures/transformation/es7-abstract-references/set/actual.js new file mode 100644 index 0000000000..8abf694209 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/actual.js @@ -0,0 +1,3 @@ +var baz = "foo"; +foo::bar = baz; +if (foo::bar = baz) {} diff --git a/test/fixtures/transformation/es7-abstract-references/set/expected.js b/test/fixtures/transformation/es7-abstract-references/set/expected.js new file mode 100644 index 0000000000..be9e6ef9f3 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +var baz = "foo"; +bar[Symbol.referenceSet](foo, baz); +if ((bar[Symbol.referenceSet](foo, baz), baz)) {} diff --git a/test/fixtures/transformation/playground/method-binding/actual.js b/test/fixtures/transformation/playground/method-binding/actual.js new file mode 100644 index 0000000000..12185e1a7c --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/actual.js @@ -0,0 +1,13 @@ +var fn = obj#method; +var fn = obj#method("foob"); +var fn = obj[foo]#method; +var fn = obj.foo#method; +var fn = obj[foo()]#method; + +["foo", "bar"].map(#toUpperCase); +[1.1234, 23.53245, 3].map(#toFixed(2)); + +var get = function () { + return 2; +}; +[1.1234, 23.53245, 3].map(#toFixed(get())); diff --git a/test/fixtures/transformation/playground/method-binding/exec.js b/test/fixtures/transformation/playground/method-binding/exec.js new file mode 100644 index 0000000000..b83e4b49aa --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/exec.js @@ -0,0 +1,30 @@ +var obj = { + foo: "foo", + bar: "bar", + getFoo: function () { + return this.foo; + }, + getBar: function (arg) { + return arg + " " + this.bar; + }, + getZoo: function (a, b) { + return this.foo + " " + this.bar + " " + a + " " + b; + } +}; + +var foo = obj#getFoo; +assert.equal(foo(), "foo"); + +var bar = obj#getBar("foo"); +assert.equal(bar(), "foo bar"); + +var zoo = obj#getZoo("foo"); +assert.equal(zoo("bar"), "foo bar foo bar"); + +assert.deepEqual(["foo", "bar"].map(#toUpperCase), ["FOO", "BAR"]); +assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(2)), ["1.12", "23.53", "3.00"]); + +var get = function () { + return 2; +} +assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(get())), ["1.12", "23.53", "3.00"]); diff --git a/test/fixtures/transformation/playground/method-binding/expected.js b/test/fixtures/transformation/playground/method-binding/expected.js new file mode 100644 index 0000000000..6f76e45d89 --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/expected.js @@ -0,0 +1,23 @@ +"use strict"; + +var _obj, _obj2, _obj$foo, _obj$foo2, _obj$foo3, _args, _args2, _args3; + +var fn = (_obj = obj, _obj.method.bind(_obj)); +var fn = (_obj2 = obj, _obj2.method.bind(_obj2, "foob")); +var fn = (_obj$foo = obj[foo], _obj$foo.method.bind(_obj$foo)); +var fn = (_obj$foo2 = obj.foo, _obj$foo2.method.bind(_obj$foo2)); +var fn = (_obj$foo3 = obj[foo()], _obj$foo3.method.bind(_obj$foo3)); + +["foo", "bar"].map((_args = [], function (_val) { + return _val.toUpperCase(); +})); +[1.1234, 23.53245, 3].map((_args2 = [2], function (_val2) { + return _val2.toFixed(_args2[0]); +})); + +var get = function get() { + return 2; +}; +[1.1234, 23.53245, 3].map((_args3 = [get()], function (_val3) { + return _val3.toFixed(_args3[0]); +})); diff --git a/test/fixtures/transformation/playground/method-binding/options.json b/test/fixtures/transformation/playground/method-binding/options.json new file mode 100644 index 0000000000..b8b90ecb11 --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/options.json @@ -0,0 +1,3 @@ +{ + "blacklist": ["es6.tailCall"] +} diff --git a/test/fixtures/transformation/playground/object-getter-memoization/actual.js b/test/fixtures/transformation/playground/object-getter-memoization/actual.js new file mode 100644 index 0000000000..9b0d317c04 --- /dev/null +++ b/test/fixtures/transformation/playground/object-getter-memoization/actual.js @@ -0,0 +1,19 @@ +class Foo { + memo bar() { + return complex(); + } + + memo [bar]() { + return complex(); + } +} + +var foo = { + memo bar() { + return complex(); + }, + + memo [bar]() { + return complex(); + } +}; diff --git a/test/fixtures/transformation/playground/object-getter-memoization/expected.js b/test/fixtures/transformation/playground/object-getter-memoization/expected.js new file mode 100644 index 0000000000..214f9235f5 --- /dev/null +++ b/test/fixtures/transformation/playground/object-getter-memoization/expected.js @@ -0,0 +1,36 @@ +"use strict"; + +var Foo = (function () { + function Foo() { + babelHelpers.classCallCheck(this, Foo); + } + + babelHelpers.createComputedClass(Foo, [{ + key: "bar", + get: function () { + return babelHelpers.defineProperty(this, "bar", complex()).bar; + } + }, { + key: bar, + get: function () { + return babelHelpers.defineProperty(this, bar, complex())[bar]; + } + }]); + return Foo; +})(); + +var foo = Object.defineProperties({}, babelHelpers.defineProperty({ + bar: { + get: function () { + return babelHelpers.defineProperty(this, "bar", complex()).bar; + }, + configurable: true, + enumerable: true + } +}, bar, { + get: function () { + return babelHelpers.defineProperty(this, bar, complex())[bar]; + }, + configurable: true, + enumerable: true +})); \ No newline at end of file From 5a319fd553ff28761488c29995d90545e7873c29 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:23:57 +1100 Subject: [PATCH 17/24] automate option generation --- bin/babel/index.js | 71 ++++----- src/babel/transformation/file.js | 121 +++++---------- src/babel/transformation/options.json | 152 +++++++++++++++++++ src/babel/transformation/transformer-pass.js | 10 +- 4 files changed, 222 insertions(+), 132 deletions(-) create mode 100644 src/babel/transformation/options.json diff --git a/bin/babel/index.js b/bin/babel/index.js index da34ba6fe2..99ab67d1f7 100755 --- a/bin/babel/index.js +++ b/bin/babel/index.js @@ -2,32 +2,38 @@ var commander = require("commander"); var transform = require("../../lib/babel/transformation"); +var kebabCase = require("lodash/string/kebabCase"); +var File = require("../../lib/babel/transformation/file"); var util = require("../../lib/babel/util"); -var fs = require("fs"); var each = require("lodash/collection/each"); var keys = require("lodash/object/keys"); +var fs = require("fs"); + +each(File.options, function (option, key) { + if (option.hidden) return; + + var arg = kebabCase(key); + + if (option.type !== "boolean") { + arg += ` [${option.type || "string"}]`; + } + + if (option.type === "boolean" && option.default === true) { + arg = `no-${key}`; + } + + arg = `--${arg}`; + + if (option.shorthand) { + arg = `-${option.shorthand}, ${arg}`; + } + + commander.option(arg, option.description); +}) -commander.option("-t, --source-maps-inline", "Append sourceMappingURL comment to bottom of code"); -commander.option("-s, --source-maps", "Save source map alongside the compiled code"); -commander.option("-f, --filename [filename]", "Filename to use when reading from stdin - this will be used in source-maps, errors etc [stdin]", "stdin"); commander.option("-w, --watch", "Recompile files on changes"); -commander.option("-r, --external-helpers", "Replace helpers with references to a `babelHelpers` global"); -commander.option("-e, --experimental", "Enable experimental support for proposed ES7 features"); -commander.option("-p, --playground", "Enable playground support"); - -commander.option("-c, --compact [mode]", "When set to \"auto\" compact is `true` when the input size exceeds 100KB. (auto|true|false)", "auto"); -commander.option("-m, --modules [modules]", "Module formatter type to use [common]", "common"); -commander.option("-l, --whitelist [whitelist]", "Whitelist of transformers to ONLY use", util.list); -commander.option("-b, --blacklist [blacklist]", "Blacklist of transformers to NOT use", util.list); -commander.option("-i, --optional [list]", "List of optional transformers to enable", util.list); -commander.option("-L, --loose [list]", "List of transformers to enable loose mode ON", util.list); commander.option("-o, --out-file [out]", "Compile all input files into a single file"); commander.option("-d, --out-dir [out]", "Compile an input directory of modules into an output directory"); -commander.option("-c, --remove-comments", "Remove comments from the compiled code", false); -commander.option("-M, --module-ids", "Insert module id in modules", false); -commander.option("-R, --react-compat", "Makes the react transformer produce pre-v0.12 code"); -commander.option("--keep-module-id-extensions", "Keep extensions when generating module ids", false); -commander.option("-a, --auxiliary-comment [comment]", "Comment text to prepend to all auxiliary code"); commander.option("-D, --copy-files", "When compiling a directory copy over non-compilable files"); commander.on("--help", function () { @@ -38,9 +44,7 @@ commander.on("--help", function () { each(keys(obj).sort(), function (key) { if (key[0] === "_") return; - if (obj[key].optional) { - key = "[" + key + "]"; - } + if (obj[key].optional) key = `[${key}]`; console.log(" - " + key); }); @@ -100,24 +104,11 @@ if (errors.length) { // -exports.opts = { - keepModuleIdExtensions: commander.keepModuleIdExtensions, - auxiliaryComment: commander.auxiliaryComment, - externalHelpers: commander.externalHelpers, - sourceMapName: commander.outFile, - experimental: commander.experimental, - reactCompat: commander.reactCompat, - playground: commander.playground, - moduleIds: commander.moduleIds, - blacklist: commander.blacklist, - whitelist: commander.whitelist, - sourceMap: commander.sourceMaps || commander.sourceMapsInline, - optional: commander.optional, - comments: !commander.removeComments, - modules: commander.modules, - compact: commander.compact, - loose: commander.loose -}; +exports.opts = {}; + +each(File.options, function (opt, key) { + exports.opts[key] = commander[key]; +}); var fn; diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file.js index 3d1c5b1766..a8a63a02f8 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file.js @@ -82,52 +82,7 @@ export default class File { "self-global" ]; - static validOptions = [ - "filename", - "filenameRelative", - - "blacklist", - "whitelist", - "optional", - - "loose", - "playground", - "experimental", - - "modules", - "moduleIds", - "moduleId", - "resolveModuleSource", - "keepModuleIdExtensions", - - "code", - "ast", - - "comments", - "compact", - - "auxiliaryComment", - "externalHelpers", - "returnUsedHelpers", - - "inputSourceMap", - "sourceMap", - "sourceMapName", - "sourceFileName", - "sourceRoot", - "moduleRoot", - - "ignore", - "only", - - // legacy - "format", - "reactCompat", - - // these are used by plugins - "extensions", - "accept" - ]; + static options = require("./options"); normalizeOptions(opts: Object) { opts = assign({}, opts); @@ -136,36 +91,32 @@ export default class File { opts = resolveRc(opts.filename, opts); } - for (var key in opts) { - if (key[0] !== "_" && File.validOptions.indexOf(key) < 0) { - throw new ReferenceError(`Unknown option: ${key}`); - } + // + + for (let key in opts) { + if (key[0] === "_") continue; + + let option = File.options[key]; + if (!option) throw new ReferenceError(`Unknown option: ${key}`); } + for (let key in File.options) { + let option = File.options[key]; + + var val = opts[key]; + if (val == null) val = option.default || null; + opts[key] = val; + } + + // + defaults(opts, { - keepModuleIdExtensions: false, - resolveModuleSource: null, - returnUsedHelpers: false, - externalHelpers: false, - auxilaryComment: "", - inputSourceMap: null, - experimental: false, - reactCompat: false, - playground: false, - moduleIds: false, - blacklist: [], - whitelist: [], - sourceMap: false, - optional: [], - comments: true, - filename: "unknown", - modules: "common", - compact: "auto", - loose: [], - ignore: [], - only: [], - code: true, - ast: true + blacklist: [], + whitelist: [], + optional: [], + loose: [], + ignore: [], + only: [], }); if (opts.inputSourceMap) { @@ -213,9 +164,7 @@ export default class File { sourceMapName: opts.filenameRelative }); - if (opts.playground) { - opts.experimental = true; - } + // if (opts.externalHelpers) { this.set("helpersNamespace", t.identifier("babelHelpers")); @@ -226,15 +175,6 @@ export default class File { opts.optional = transform._ensureTransformerNames("optional", opts.optional); opts.loose = transform._ensureTransformerNames("loose", opts.loose); - var ensureEnabled = function (key) { - var namespace = transform.transformerNamespaces[key]; - if (namespace === "es7") opts.experimental = true; - if (namespace === "playground") opts.playground = true; - }; - - each(opts.whitelist, ensureEnabled); - each(opts.optional, ensureEnabled); - return opts; }; @@ -242,6 +182,10 @@ export default class File { return includes(this.opts.loose, key); } + buildPlugins(stack) { + + } + buildTransformers() { var file = this; @@ -250,6 +194,8 @@ export default class File { var secondaryStack = []; var stack = []; + this.buildPlugins(stack); + each(transform.transformers, function (transformer, key) { var pass = transformers[key] = transformer.buildPass(file); @@ -551,6 +497,7 @@ export default class File { } generate(): { + usedHelpers?: Array; code: string; map?: Object; ast?: Object; @@ -560,8 +507,8 @@ export default class File { var result = { code: "", - map: null, - ast: null + map: null, + ast: null }; if (this.opts.returnUsedHelpers) { diff --git a/src/babel/transformation/options.json b/src/babel/transformation/options.json new file mode 100644 index 0000000000..97c633f137 --- /dev/null +++ b/src/babel/transformation/options.json @@ -0,0 +1,152 @@ +{ + "filename": { + "type": "string", + "description": "Filename to use when reading from stdin - this will be used in source-maps, errors etc", + "default": "unknown", + "shorthand": "f" + }, + + "filenameRelative": { + "hidden": true, + "type": "string" + }, + + "inputSourceMap": { + "hidden": true + }, + + "moduleId": { + + }, + + "resolveModuleSource": { + "hidden": true + }, + + "experimental": { + "description": "Enable all ES7+ transformers", + "shorthand": "e", + "type": "boolean", + "default": false + }, + + "playground": { + "description": "Enable all playground transformers", + "shorthand": "p", + "type": "boolean", + "default": false + }, + + "blacklist": { + "type": "transformerList", + "description": "Blacklist of transformers to NOT use", + "shorthand": "b" + }, + + "whitelist": { + "type": "transformerList", + "description": "Whitelist of transformers to ONLY use", + "shorthand": "l" + }, + + "optional": { + "type": "transformerList", + "description": "List of optional transformers to enable" + }, + + "modules": { + "type": "string", + "description": "Module formatter type to use [common]", + "default": "common", + "shorthand": "m" + }, + + "moduleIds": { + "type": "boolean", + "default": false, + "shorthand": "M" + }, + + "loose": { + "type": "transformerList", + "description": "List of transformers to enable loose mode ON", + "shorthand": "L" + }, + + "ignore": { + "type": "list" + }, + + "only": { + "type": "list" + }, + + "code": { + "hidden": true, + "default": true, + "type": "boolean" + }, + + "ast": { + "hidden": true, + "default": true, + "type": "boolean" + }, + + "comments": { + "type": "boolean", + "default": true + }, + + "compact": { + "type": "string", + "default": "auto" + }, + + "keepModuleIdExtensions": { + "type": "boolean", + "description": "Keep extensions when generating module ids", + "default": false, + "shorthand": "k" + }, + + "auxiliaryComment": { + "type": "string", + "default": "", + "shorthand": "a" + }, + + "externalHelpers": { + "type": "string", + "default": false, + "shorthand": "r" + }, + + "returnUsedHelpers": { + "type": "boolean", + "default": false, + "hidden": true + }, + + "sourceMap": { + "type": "string", + "default": false, + "shorthand": "s" + }, + + "sourceMapName": { + "type": "string" + }, + + "sourceFileName": { + "type": "string" + }, + + "sourceRoot": { + "type": "string" + }, + + "moduleRoot": { + "type": "string" + } +} diff --git a/src/babel/transformation/transformer-pass.js b/src/babel/transformation/transformer-pass.js index dd8fab0431..0d9897f259 100644 --- a/src/babel/transformation/transformer-pass.js +++ b/src/babel/transformation/transformer-pass.js @@ -32,14 +32,14 @@ export default class TransformerPass { var whitelist = opts.whitelist; if (whitelist.length) return includes(whitelist, key); - // optional - if (transformer.optional && !includes(opts.optional, key)) return false; - // experimental - if (transformer.experimental && !opts.experimental) return false; + if (transformer.experimental && opts.experimental) return true; // playground - if (transformer.playground && !opts.playground) return false; + if (transformer.playground && opts.playground) return true; + + // optional + if (transformer.optional && !includes(opts.optional, key)) return false; return true; } From 1c6ac0a07a8d090bcf5c8d7ac244bce162a111f8 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:25:25 +1100 Subject: [PATCH 18/24] designate all playground and es7 transformers as optional --- .../transformation/transformers/es7/abstract-references.js | 1 + src/babel/transformation/transformers/es7/comprehensions.js | 1 + .../transformers/es7/exponentiation-operator.js | 1 + .../transformation/transformers/es7/object-rest-spread.js | 1 + .../transformers/playground/mallet-operator.js | 1 + .../transformers/playground/memoization-operator.js | 1 + .../transformation/transformers/playground/method-binding.js | 5 +++++ .../transformers/playground/object-getter-memoization.js | 1 + 8 files changed, 12 insertions(+) diff --git a/src/babel/transformation/transformers/es7/abstract-references.js b/src/babel/transformation/transformers/es7/abstract-references.js index 7713b41444..79f6415fad 100644 --- a/src/babel/transformation/transformers/es7/abstract-references.js +++ b/src/babel/transformation/transformers/es7/abstract-references.js @@ -4,6 +4,7 @@ import * as util from "../../../util"; import * as t from "../../../types"; export var experimental = true; +export var optional = true; var container = function (parent, call, ret, file) { if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) { diff --git a/src/babel/transformation/transformers/es7/comprehensions.js b/src/babel/transformation/transformers/es7/comprehensions.js index 98484701b0..901f42e7e2 100644 --- a/src/babel/transformation/transformers/es7/comprehensions.js +++ b/src/babel/transformation/transformers/es7/comprehensions.js @@ -4,6 +4,7 @@ import * as util from "../../../util"; import * as t from "../../../types"; export var experimental = true; +export var optional = true; export function ComprehensionExpression(node, parent, scope, file) { var callback = array; diff --git a/src/babel/transformation/transformers/es7/exponentiation-operator.js b/src/babel/transformation/transformers/es7/exponentiation-operator.js index da988d07dd..e5106b938c 100644 --- a/src/babel/transformation/transformers/es7/exponentiation-operator.js +++ b/src/babel/transformation/transformers/es7/exponentiation-operator.js @@ -4,6 +4,7 @@ import build from "../../helpers/build-binary-assignment-operator-transformer"; import * as t from "../../../types"; export var experimental = true; +export var optional = true; var MATH_POW = t.memberExpression(t.identifier("Math"), t.identifier("pow")); diff --git a/src/babel/transformation/transformers/es7/object-rest-spread.js b/src/babel/transformation/transformers/es7/object-rest-spread.js index c5d7b08c3d..257064dbcd 100644 --- a/src/babel/transformation/transformers/es7/object-rest-spread.js +++ b/src/babel/transformation/transformers/es7/object-rest-spread.js @@ -3,6 +3,7 @@ import * as t from "../../../types"; export var experimental = true; +export var optional = true; export function manipulateOptions(opts) { if (opts.whitelist.length) opts.whitelist.push("es6.destructuring"); diff --git a/src/babel/transformation/transformers/playground/mallet-operator.js b/src/babel/transformation/transformers/playground/mallet-operator.js index 25e6896a45..1d51716fe0 100644 --- a/src/babel/transformation/transformers/playground/mallet-operator.js +++ b/src/babel/transformation/transformers/playground/mallet-operator.js @@ -3,6 +3,7 @@ import build from "../../helpers/build-conditional-assignment-operator-transform import * as t from "../../../types"; export var playground = true; +export var optional = true; build(exports, { is(node, file) { diff --git a/src/babel/transformation/transformers/playground/memoization-operator.js b/src/babel/transformation/transformers/playground/memoization-operator.js index 7c63ad102a..c399166eef 100644 --- a/src/babel/transformation/transformers/playground/memoization-operator.js +++ b/src/babel/transformation/transformers/playground/memoization-operator.js @@ -2,6 +2,7 @@ import build from "../../helpers/build-conditional-assignment-operator-transform import * as t from "../../../types"; export var playground = true; +export var optional = true; build(exports, { is(node) { diff --git a/src/babel/transformation/transformers/playground/method-binding.js b/src/babel/transformation/transformers/playground/method-binding.js index 5d4ddff3c7..ed92f512d6 100644 --- a/src/babel/transformation/transformers/playground/method-binding.js +++ b/src/babel/transformation/transformers/playground/method-binding.js @@ -1,8 +1,11 @@ import * as t from "../../../types"; export var playground = true; +export var optional = true; export function BindMemberExpression(node, parent, scope) { + console.error("Method binding is deprecated and will be removed in 5.0.0"); + var object = node.object; var prop = node.property; @@ -25,6 +28,8 @@ export function BindMemberExpression(node, parent, scope) { } export function BindFunctionExpression(node, parent, scope) { + console.error("Method binding is deprecated and will be removed in 5.0.0"); + var buildCall = function (args) { var param = scope.generateUidIdentifier("val"); return t.functionExpression(null, [param], t.blockStatement([ diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js index dbbd3a065e..6fb0685e89 100644 --- a/src/babel/transformation/transformers/playground/object-getter-memoization.js +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -1,6 +1,7 @@ import * as t from "../../../types"; export var playground = true; +export var optional = true; var visitor = { enter(node, parent, scope, state) { From b13b4bbfbaed66d862797aeb80921015de6a70aa Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:25:59 +1100 Subject: [PATCH 19/24] add buildPass File check in preperation for plugin api --- src/babel/transformation/transformer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/babel/transformation/transformer.js b/src/babel/transformation/transformer.js index fddb3ae2a1..6a05af07c1 100644 --- a/src/babel/transformation/transformer.js +++ b/src/babel/transformation/transformer.js @@ -3,6 +3,7 @@ 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"; /** @@ -66,6 +67,10 @@ export default class Transformer { } buildPass(file: File): TransformerPass { + if (!(file instanceof File)) { + throw new Error("Multiple versions of babel are interacting, this is either due to a version mismatch in a plugin or it was resolved incorrectly"); + } + return new TransformerPass(file, this); } } From cc0f43813cbe2285f59dd11b050fbdecd7c60f92 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:26:26 +1100 Subject: [PATCH 20/24] small style fixes --- src/babel/transformation/transformers/es6/classes.js | 4 ++-- .../transformation/transformers/es6/template-literals.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index 46126e45dd..add480ac59 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -33,7 +33,7 @@ var verifyConstructorVisitor = traverse.explode({ CallExpression: { enter(node, parent, scope, state) { - if (t.isIdentifier(node.callee, { name: "super" })) { + if (this.get("callee").isIdentifier({ name: "super" })) { state.hasBareSuper = true; if (!state.hasSuper) { @@ -208,7 +208,7 @@ class ClassTransformer { } } - // we have no constructor, we have a super, and the super doesn't appear to be falsy + // we have no constructor, but we're a derived class if (!this.hasConstructor && this.hasSuper) { var helperName = "class-super-constructor-call"; if (this.isLoose) helperName += "-loose"; diff --git a/src/babel/transformation/transformers/es6/template-literals.js b/src/babel/transformation/transformers/es6/template-literals.js index 9bee577561..537f57bde1 100644 --- a/src/babel/transformation/transformers/es6/template-literals.js +++ b/src/babel/transformation/transformers/es6/template-literals.js @@ -9,11 +9,11 @@ export function check(node) { } export function TaggedTemplateExpression(node, parent, scope, file) { - var args = []; var quasi = node.quasi; + var args = []; var strings = []; - var raw = []; + var raw = []; for (var i = 0; i < quasi.quasis.length; i++) { var elem = quasi.quasis[i]; From 1bbe52a0221e5bf527ac92c9bd366fc72cc2484a Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:44:10 +1100 Subject: [PATCH 21/24] split up module class declarations - fixes #1021 --- src/babel/transformation/transformers/internal/modules.js | 7 ++++++- .../es6-modules-amd/exports-variable/expected.js | 6 ++++-- .../es6-modules-common/exports-variable/expected.js | 6 ++++-- .../es6-modules-system/exports-variable/expected.js | 8 +++++--- .../es6-modules-umd/exports-variable/expected.js | 6 ++++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/babel/transformation/transformers/internal/modules.js b/src/babel/transformation/transformers/internal/modules.js index 69de42f4d5..50ecd3a7a8 100644 --- a/src/babel/transformation/transformers/internal/modules.js +++ b/src/babel/transformation/transformers/internal/modules.js @@ -50,7 +50,12 @@ export function ExportDeclaration(node, parent, scope) { return [getDeclar(), node]; } } else { - if (t.isFunctionDeclaration(declar)) { + if (t.isClassDeclaration(declar)) { + // export class Foo {} + node.specifiers = [t.importSpecifier(declar.id, declar.id)]; + node.declaration = null; + return [getDeclar(), node]; + } else if (t.isFunctionDeclaration(declar)) { // export function Foo() {} node.specifiers = [t.importSpecifier(declar.id, declar.id)]; node.declaration = null; diff --git a/test/fixtures/transformation/es6-modules-amd/exports-variable/expected.js b/test/fixtures/transformation/es6-modules-amd/exports-variable/expected.js index bd6d12e3f6..b5f5c1538f 100644 --- a/test/fixtures/transformation/es6-modules-amd/exports-variable/expected.js +++ b/test/fixtures/transformation/es6-modules-amd/exports-variable/expected.js @@ -26,7 +26,9 @@ define(["exports"], function (exports) { function foo7() {} - var foo8 = exports.foo8 = function foo8() { + var foo8 = function foo8() { _classCallCheck(this, foo8); }; -}); \ No newline at end of file + + exports.foo8 = foo8; +}); diff --git a/test/fixtures/transformation/es6-modules-common/exports-variable/expected.js b/test/fixtures/transformation/es6-modules-common/exports-variable/expected.js index 0b2cbf18e7..b468b9bc5d 100644 --- a/test/fixtures/transformation/es6-modules-common/exports-variable/expected.js +++ b/test/fixtures/transformation/es6-modules-common/exports-variable/expected.js @@ -25,6 +25,8 @@ exports.foo6 = foo6; function foo7() {} -var foo8 = exports.foo8 = function foo8() { +var foo8 = function foo8() { _classCallCheck(this, foo8); -}; \ No newline at end of file +}; + +exports.foo8 = foo8; diff --git a/test/fixtures/transformation/es6-modules-system/exports-variable/expected.js b/test/fixtures/transformation/es6-modules-system/exports-variable/expected.js index 8626db3270..5006e12bbf 100644 --- a/test/fixtures/transformation/es6-modules-system/exports-variable/expected.js +++ b/test/fixtures/transformation/es6-modules-system/exports-variable/expected.js @@ -34,11 +34,13 @@ System.register([], function (_export) { _export("foo6", foo6); - foo8 = _export("foo8", function foo8() { + foo8 = function foo8() { _classCallCheck(this, foo8); - }); + }; + + _export("foo8", foo8); _export("foo3", foo3 = 5); } }; -}); \ No newline at end of file +}); diff --git a/test/fixtures/transformation/es6-modules-umd/exports-variable/expected.js b/test/fixtures/transformation/es6-modules-umd/exports-variable/expected.js index 49eca85aba..0f1a05c14d 100644 --- a/test/fixtures/transformation/es6-modules-umd/exports-variable/expected.js +++ b/test/fixtures/transformation/es6-modules-umd/exports-variable/expected.js @@ -32,7 +32,9 @@ function foo7() {} - var foo8 = exports.foo8 = function foo8() { + var foo8 = function foo8() { _classCallCheck(this, foo8); }; -}); \ No newline at end of file + + exports.foo8 = foo8; +}); From 76e1f0dd9f92753b3d58556ba488a59c1f9a1460 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:44:17 +1100 Subject: [PATCH 22/24] add babelrc to vendor --- vendor/.babelrc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 vendor/.babelrc diff --git a/vendor/.babelrc b/vendor/.babelrc new file mode 100644 index 0000000000..3a8f8eb911 --- /dev/null +++ b/vendor/.babelrc @@ -0,0 +1,3 @@ +{ + "breakConfig": true +} From 866d3a8eab67410d0270c0bb2502b04bc00f6cfe Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:45:08 +1100 Subject: [PATCH 23/24] move file into a separate folder --- .../transformation/{file.js => file/index.js} | 54 +++++++------------ src/babel/transformation/{ => file}/logger.js | 8 ++- .../transformation/file/option-parsers.js | 20 +++++++ .../transformation/{ => file}/options.json | 6 +++ .../transformers/playground/method-binding.js | 8 +-- .../playground/object-getter-memoization.js | 2 +- test/_transformation-helper.js | 1 + 7 files changed, 57 insertions(+), 42 deletions(-) rename src/babel/transformation/{file.js => file/index.js} (89%) rename src/babel/transformation/{ => file}/logger.js (74%) create mode 100644 src/babel/transformation/file/option-parsers.js rename src/babel/transformation/{ => file}/options.json (96%) diff --git a/src/babel/transformation/file.js b/src/babel/transformation/file/index.js similarity index 89% rename from src/babel/transformation/file.js rename to src/babel/transformation/file/index.js index a8a63a02f8..22559262c1 100644 --- a/src/babel/transformation/file.js +++ b/src/babel/transformation/file/index.js @@ -1,24 +1,25 @@ import convertSourceMap from "convert-source-map"; +import * as optionParsers from "./option-parsers"; import shebangRegex from "shebang-regex"; -import TraversalPath from "../traversal/path"; +import TraversalPath from "../../traversal/path"; import isFunction from "lodash/lang/isFunction"; import isAbsolute from "path-is-absolute"; -import resolveRc from "../tools/resolve-rc"; +import resolveRc from "../../tools/resolve-rc"; import sourceMap from "source-map"; -import transform from "./index"; -import generate from "../generation"; +import transform from "./../index"; +import generate from "../../generation"; import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; -import traverse from "../traversal"; +import traverse from "../../traversal"; import assign from "lodash/object/assign"; import Logger from "./logger"; -import parse from "../helpers/parse"; -import Scope from "../traversal/scope"; +import parse from "../../helpers/parse"; +import Scope from "../../traversal/scope"; import slash from "slash"; -import * as util from "../util"; +import * as util from "../../util"; import path from "path"; import each from "lodash/collection/each"; -import * as t from "../types"; +import * as t from "../../types"; var checkTransformerVisitor = { enter(node, parent, scope, state) { @@ -105,20 +106,15 @@ export default class File { var val = opts[key]; if (val == null) val = option.default || null; + + var optionParser = optionParsers[option.type]; + if (optionParser) { + val = optionParser(key, val); + } + opts[key] = val; } - // - - defaults(opts, { - blacklist: [], - whitelist: [], - optional: [], - loose: [], - ignore: [], - only: [], - }); - if (opts.inputSourceMap) { opts.sourceMap = true; } @@ -135,17 +131,8 @@ export default class File { opts.basename = path.basename(opts.filename, path.extname(opts.filename)); - opts.blacklist = util.arrayify(opts.blacklist); - opts.whitelist = util.arrayify(opts.whitelist); - opts.optional = util.arrayify(opts.optional); - opts.compact = util.booleanify(opts.compact); - opts.loose = util.arrayify(opts.loose); - opts.ignore = util.arrayify(opts.ignore, util.regexify); - opts.only = util.arrayify(opts.only, util.regexify); - - if (includes(opts.loose, "all") || includes(opts.loose, true)) { - opts.loose = Object.keys(transform.transformers); - } + opts.ignore = util.arrayify(opts.ignore, util.regexify); + opts.only = util.arrayify(opts.only, util.regexify); defaults(opts, { moduleRoot: opts.sourceRoot @@ -170,11 +157,6 @@ export default class File { this.set("helpersNamespace", t.identifier("babelHelpers")); } - opts.blacklist = transform._ensureTransformerNames("blacklist", opts.blacklist); - opts.whitelist = transform._ensureTransformerNames("whitelist", opts.whitelist); - opts.optional = transform._ensureTransformerNames("optional", opts.optional); - opts.loose = transform._ensureTransformerNames("loose", opts.loose); - return opts; }; diff --git a/src/babel/transformation/logger.js b/src/babel/transformation/file/logger.js similarity index 74% rename from src/babel/transformation/logger.js rename to src/babel/transformation/file/logger.js index 29e185af9d..109019c810 100644 --- a/src/babel/transformation/logger.js +++ b/src/babel/transformation/file/logger.js @@ -1,4 +1,4 @@ -import * as util from "../util"; +import * as util from "../../util"; export default class Logger { constructor(file: File) { @@ -12,6 +12,12 @@ export default class Logger { return parts; } + deprecate(msg) { + if (!file.opts.suppressDeprecationMessages) { + console.error(msg); + } + } + debug(msg: string) { util.debug(this._buildMessage(msg)); } diff --git a/src/babel/transformation/file/option-parsers.js b/src/babel/transformation/file/option-parsers.js new file mode 100644 index 0000000000..a0a2cc33d2 --- /dev/null +++ b/src/babel/transformation/file/option-parsers.js @@ -0,0 +1,20 @@ +import transform from "./../index"; +import * as util from "../../util"; + +export function transformerList(key, val) { + val = util.arrayify(val); + + if (val.indexOf("all") >= 0 || val.indexOf(true) >= 0) { + val = Object.keys(transform.transformers); + } + + return transform._ensureTransformerNames(key, val); +} + +export function boolean(key, val) { + return util.booleanify(val); +} + +export function list(key, val) { + return util.list(val); +} diff --git a/src/babel/transformation/options.json b/src/babel/transformation/file/options.json similarity index 96% rename from src/babel/transformation/options.json rename to src/babel/transformation/file/options.json index 97c633f137..6824ebb66c 100644 --- a/src/babel/transformation/options.json +++ b/src/babel/transformation/file/options.json @@ -19,6 +19,12 @@ }, + "suppressDeprecationMessages": { + "type": "boolean", + "default": false, + "hidden": true + }, + "resolveModuleSource": { "hidden": true }, diff --git a/src/babel/transformation/transformers/playground/method-binding.js b/src/babel/transformation/transformers/playground/method-binding.js index ed92f512d6..86ec1312ae 100644 --- a/src/babel/transformation/transformers/playground/method-binding.js +++ b/src/babel/transformation/transformers/playground/method-binding.js @@ -3,8 +3,8 @@ import * as t from "../../../types"; export var playground = true; export var optional = true; -export function BindMemberExpression(node, parent, scope) { - console.error("Method binding is deprecated and will be removed in 5.0.0"); +export function BindMemberExpression(node, parent, scope, file) { + file.log.deprecate("Method binding is deprecated and will be removed in 5.0.0"); var object = node.object; var prop = node.property; @@ -27,8 +27,8 @@ export function BindMemberExpression(node, parent, scope) { } } -export function BindFunctionExpression(node, parent, scope) { - console.error("Method binding is deprecated and will be removed in 5.0.0"); +export function BindFunctionExpression(node, parent, scope, file) { + file.log.deprecate("Method binding is deprecated and will be removed in 5.0.0"); var buildCall = function (args) { var param = scope.generateUidIdentifier("val"); diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js index 6fb0685e89..8c117811a7 100644 --- a/src/babel/transformation/transformers/playground/object-getter-memoization.js +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -21,7 +21,7 @@ export function MethodDefinition(node, parent, scope, file) { if (node.kind !== "memo") return; node.kind = "get"; - console.error("Object getter memoization is deprecated and will be removed in 5.0.0"); + file.log.deprecate("Object getter memoization is deprecated and will be removed in 5.0.0"); t.ensureBlock(node.value); diff --git a/test/_transformation-helper.js b/test/_transformation-helper.js index bcad6fdba8..14f0146983 100644 --- a/test/_transformation-helper.js +++ b/test/_transformation-helper.js @@ -55,6 +55,7 @@ var run = function (task, done) { var getOpts = function (self) { return _.merge({ + suppressDeprecationMessages: true, filename: self.loc }, opts); }; From 1fb877e9f5403a45937504d3ad63215c77ccc31b Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 16 Mar 2015 01:50:37 +1100 Subject: [PATCH 24/24] add highlightErrors option - fixes #837 --- src/babel/helpers/code-frame.js | 4 ++-- src/babel/helpers/parse.js | 4 ++-- src/babel/transformation/file/options.json | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/babel/helpers/code-frame.js b/src/babel/helpers/code-frame.js index e1bb415681..8f24c61502 100644 --- a/src/babel/helpers/code-frame.js +++ b/src/babel/helpers/code-frame.js @@ -55,10 +55,10 @@ var highlight = function (text) { }); }; -export default function (lines, lineNumber, colNumber) { +export default function (lines: number, lineNumber: number, colNumber: number, color?): string { colNumber = Math.max(colNumber, 0); - if (chalk.supportsColor) { + if (color && chalk.supportsColor) { lines = highlight(lines); } diff --git a/src/babel/helpers/parse.js b/src/babel/helpers/parse.js index b93361c638..124407c96f 100644 --- a/src/babel/helpers/parse.js +++ b/src/babel/helpers/parse.js @@ -32,12 +32,12 @@ export default function (opts, code, callback) { } catch (err) { if (!err._babel) { err._babel = true; + var message = `${opts.filename}: ${err.message}`; var loc = err.loc; if (loc) { - var frame = codeFrame(code, loc.line, loc.column + 1); - message += frame; + message += codeFrame(code, loc.line, loc.column + 1, opts.highlightErrors); } if (err.stack) { diff --git a/src/babel/transformation/file/options.json b/src/babel/transformation/file/options.json index 6824ebb66c..0482ff9576 100644 --- a/src/babel/transformation/file/options.json +++ b/src/babel/transformation/file/options.json @@ -19,6 +19,12 @@ }, + "highlightErrors": { + "description": "ANSI syntax highlight error messages" + "type": "boolean", + "default": true + }, + "suppressDeprecationMessages": { "type": "boolean", "default": false,