diff --git a/src/babel/helpers/parse.js b/src/babel/helpers/parse.js index ebb78b3633..e30c77aa94 100644 --- a/src/babel/helpers/parse.js +++ b/src/babel/helpers/parse.js @@ -1,56 +1,23 @@ import normalizeAst from "./normalize-ast"; import estraverse from "estraverse"; -import codeFrame from "./code-frame"; import * as acorn from "../../acorn"; -function parseCatch(code, opts) { - var comments = opts.onComment = []; - var tokens = opts.onToken = []; - - try { - return acorn.parse(code, opts); - } catch (err) { - if (err._babel) { - throw err; - } else { - err._babel = true; - } - - if (opts.errorMessage) { - err.message += ` - ${opts.errorMessage}`; - } - - var message = err.message = `${opts.filename || "unknown"}: ${err.message}`; - - var loc = err.loc; - if (loc) { - err.codeFrame = codeFrame(code, loc.line, loc.column + 1, opts); - message += "\n" + err.codeFrame; - } - - if (err.stack) { - var newStack = err.stack.replace(err.message, message); - try { - err.stack = newStack; - } catch (e) { - // `err.stack` may be a readonly property in some environments - } - } - - throw err; - } -} - export default function (opts, code, callback) { + var comments = []; + var tokens = []; + var parseOpts = { allowImportExportEverywhere: opts.looseModules, allowReturnOutsideFunction: opts.looseModules, + allowHashBang: true, ecmaVersion: 6, strictMode: opts.strictMode, sourceType: opts.sourceType, locations: true, + onComment: comments, features: opts.features || {}, plugins: opts.plugins || {}, + onToken: tokens, ranges: true }; @@ -59,19 +26,15 @@ export default function (opts, code, callback) { parseOpts.plugins.flow = true; } - var ast = parseCatch(code, parseOpts); + var ast = acorn.parse(code, parseOpts); - estraverse.attachComments(ast, parseOpts.onComment, parseOpts.onToken); - ast = normalizeAst(ast, parseOpts.onComment, parseOpts.onToken); - - if (callback) { - return callback(ast); - } else { - return ast; - } + estraverse.attachComments(ast, comments, tokens); + ast = normalizeAst(ast, comments, tokens); + return ast; } export function all(code, opts = {}) { + opts.allowHashBang = true; opts.sourceType = "module"; opts.ecmaVersion = Infinity; opts.plugins = { @@ -84,5 +47,5 @@ export function all(code, opts = {}) { opts.features[key] = true; } - return parseCatch(code, opts); + return acorn.parse(code, opts); } diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 7fb069666d..53b1280e53 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -10,6 +10,7 @@ import resolveRc from "../../tools/resolve-rc"; import sourceMap from "source-map"; import transform from "./../index"; import generate from "../../generation"; +import codeFrame from "../../helpers/code-frame"; import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; import traverse from "../../traversal"; @@ -236,48 +237,6 @@ export default class File { this.transformerStack = stack.concat(secondaryStack); } - getModuleFormatter(type: string) { - var ModuleFormatter = isFunction(type) ? type : moduleFormatters[type]; - - if (!ModuleFormatter) { - var loc = util.resolveRelative(type); - if (loc) ModuleFormatter = require(loc); - } - - if (!ModuleFormatter) { - throw new ReferenceError(`Unknown module formatter type ${JSON.stringify(type)}`); - } - - return new ModuleFormatter(this); - } - - parseInputSourceMap(code: string) { - var opts = this.opts; - - if (opts.inputSourceMap !== false) { - var inputMap = convertSourceMap.fromSource(code); - if (inputMap) { - opts.inputSourceMap = inputMap.toObject(); - code = convertSourceMap.removeComments(code); - } - } - - return code; - } - - parseShebang(code: string) { - var shebangMatch = shebangRegex.exec(code); - - if (shebangMatch) { - this.shebang = shebangMatch[0]; - - // remove shebang - code = code.replace(shebangRegex, ""); - } - - return code; - } - set(key: string, val): any { return this.data[key] = val; }; @@ -398,110 +357,6 @@ export default class File { return err; } - addCode(code: string) { - code = (code || "") + ""; - code = this.parseInputSourceMap(code); - this.code = code; - return this.parseShebang(code); - } - - shouldIgnore() { - var opts = this.opts; - return util.shouldIgnore(opts.filename, opts.ignore, opts.only); - } - - parse(code: string) { - if (this.shouldIgnore()) { - return { - metadata: {}, - code: code, - map: null, - ast: null - }; - } - - code = this.addCode(code); - - var opts = this.opts; - - // - - var parseOpts = { - highlightCode: opts.highlightCode, - nonStandard: opts.nonStandard, - filename: opts.filename, - plugins: {} - }; - - var features = parseOpts.features = {}; - for (var key in this.transformers) { - var transformer = this.transformers[key]; - features[key] = transformer.canTransform(); - } - - parseOpts.looseModules = this.isLoose("es6.modules"); - parseOpts.strictMode = features.strict; - parseOpts.sourceType = "module"; - - this.log.debug("Parse start"); - - // - - return parse(parseOpts, code, (tree) => { - this.log.debug("Parse stop"); - this.transform(tree); - return this.generate(); - }); - } - - setAst(ast) { - this.path = TraversalPath.get(null, null, ast, ast, "program", this); - this.scope = this.path.scope; - this.ast = ast; - - this.path.traverse({ - enter(node, parent, scope) { - if (this.isScope()) { - for (var key in scope.bindings) { - scope.bindings[key].setTypeAnnotation(); - } - } - } - }); - } - - transform(ast) { - this.log.debug("Start set AST"); - this.setAst(ast); - this.log.debug("End set AST"); - - this.log.debug("Start prepass"); - this.checkPath(this.path); - this.log.debug("End prepass"); - - this.log.debug("Start module formatter init"); - var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules); - if (modFormatter.init && this.transformers["es6.modules"].canTransform()) { - modFormatter.init(); - } - this.log.debug("End module formatter init"); - - this.call("pre"); - each(this.transformerStack, function (pass) { - pass.transform(); - }); - this.call("post"); - } - - call(key: string) { - var stack = this.transformerStack; - for (var i = 0; i < stack.length; i++) { - var transformer = stack[i].transformer; - var fn = transformer[key]; - if (fn) fn(this); - } - } - checkPath(path) { if (Array.isArray(path)) { for (var i = 0; i < path.length; i++) { @@ -541,6 +396,178 @@ export default class File { return map; } + + getModuleFormatter(type: string) { + var ModuleFormatter = isFunction(type) ? type : moduleFormatters[type]; + + if (!ModuleFormatter) { + var loc = util.resolveRelative(type); + if (loc) ModuleFormatter = require(loc); + } + + if (!ModuleFormatter) { + throw new ReferenceError(`Unknown module formatter type ${JSON.stringify(type)}`); + } + + return new ModuleFormatter(this); + } + + parse(code: string) { + var opts = this.opts; + + // + + var parseOpts = { + highlightCode: opts.highlightCode, + nonStandard: opts.nonStandard, + filename: opts.filename, + plugins: {} + }; + + var features = parseOpts.features = {}; + for (var key in this.transformers) { + var transformer = this.transformers[key]; + features[key] = transformer.canTransform(); + } + + parseOpts.looseModules = this.isLoose("es6.modules"); + parseOpts.strictMode = features.strict; + parseOpts.sourceType = "module"; + + this.log.debug("Parse start"); + var tree = parse(parseOpts, code); + this.log.debug("Parse stop"); + return tree; + } + + _addAst(ast) { + this.path = TraversalPath.get(null, null, ast, ast, "program", this); + this.scope = this.path.scope; + this.ast = ast; + + this.path.traverse({ + enter(node, parent, scope) { + if (this.isScope()) { + for (var key in scope.bindings) { + scope.bindings[key].setTypeAnnotation(); + } + } + } + }); + } + + addAst(ast) { + this.log.debug("Start set AST"); + this._addAst(ast); + this.log.debug("End set AST"); + + this.log.debug("Start prepass"); + this.checkPath(this.path); + this.log.debug("End prepass"); + + this.log.debug("Start module formatter init"); + var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules); + if (modFormatter.init && this.transformers["es6.modules"].canTransform()) { + modFormatter.init(); + } + this.log.debug("End module formatter init"); + + this.call("pre"); + each(this.transformerStack, function (pass) { + pass.transform(); + }); + this.call("post"); + } + + wrap(code, callback) { + try { + if (this.shouldIgnore()) { + return { + metadata: {}, + code: code, + map: null, + ast: null + }; + } + + callback(); + + return this.generate(); + } catch (err) { + if (err._babel) { + throw err; + } else { + err._babel = true; + } + + var message = err.message = `${this.opts.filename}: ${err.message}`; + + var loc = err.loc; + if (loc) { + err.codeFrame = codeFrame(code, loc.line, loc.column + 1, this.opts); + message += "\n" + err.codeFrame; + } + + if (err.stack) { + var newStack = err.stack.replace(err.message, message); + try { + err.stack = newStack; + } catch (e) { + // `err.stack` may be a readonly property in some environments + } + } + + throw err; + } + } + + addCode(code: string, parseCode?) { + code = (code || "") + ""; + code = this.parseInputSourceMap(code); + this.code = code; + + if (parseCode) { + this.parseShebang(); + this.addAst(this.parse(this.code)); + } + } + + shouldIgnore() { + var opts = this.opts; + return util.shouldIgnore(opts.filename, opts.ignore, opts.only); + } + + call(key: string) { + var stack = this.transformerStack; + for (var i = 0; i < stack.length; i++) { + var transformer = stack[i].transformer; + var fn = transformer[key]; + if (fn) fn(this); + } + } + + parseInputSourceMap(code: string) { + var opts = this.opts; + + if (opts.inputSourceMap !== false) { + var inputMap = convertSourceMap.fromSource(code); + if (inputMap) { + opts.inputSourceMap = inputMap.toObject(); + code = convertSourceMap.removeComments(code); + } + } + + return code; + } + + parseShebang() { + var shebangMatch = shebangRegex.exec(this.code); + if (shebangMatch) { + this.shebang = shebangMatch[0]; + this.code = this.code.replace(shebangRegex, ""); + } + } + generate(): { usedHelpers?: Array; code: string; diff --git a/src/babel/transformation/transformer-pipeline.js b/src/babel/transformation/transformer-pipeline.js index 20fb5eeced..7d6afc0a4a 100644 --- a/src/babel/transformation/transformer-pipeline.js +++ b/src/babel/transformation/transformer-pipeline.js @@ -57,16 +57,19 @@ export default class TransformerPipeline { transform(code: string, opts?: Object) { var file = new File(opts, this); - return file.parse(code); + return file.wrap(code, function () { + file.addCode(code, true); + }); } transformFromAst(ast, code, opts) { ast = normalizeAst(ast); var file = new File(opts, this); - file.addCode(code); - file.transform(ast); - return file.generate(); + return file.wrap(code, function () { + file.addCode(code); + file.addAst(ast); + }); } _ensureTransformerNames(type: string, rawKeys: Array) { diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index 0071b4ef35..0be1359729 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -3,7 +3,8 @@ 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 * as parse from "../../helpers/parse"; +import codeFrame from "../../helpers/code-frame"; +import { all as parse } from "../../helpers/parse"; import traverse from "../index"; import includes from "lodash/collection/includes"; import assign from "lodash/object/assign"; @@ -478,11 +479,19 @@ export default class TraversalPath { */ replaceWithSourceString(replacement) { - replacement = parse.all(`(${replacement})`, { - filename: "custom string", - errorMessage: "make sure this is an expression" - }).body[0].expression; + try { + replacement = `(${replacement})`; + replacement = parse(code); + } catch (err) { + var loc = err.loc; + if (loc) { + err.message += " - make sure this is an expression."; + err.message += "\n" + codeFrame(replacement, loc.line, loc.column + 1); + } + throw err; + } + replacement = replacement.body[0].expression; traverse.removeProperties(replacement); return this.replaceWith(replacement); }