From 0fc02f2cf09e0b1fe8738646c815b869921863b9 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 3 May 2015 23:02:19 +0100 Subject: [PATCH] add support for replacing nodes with expression source strings --- src/babel/api/node.js | 17 +---- src/babel/helpers/parse.js | 121 ++++++++++++++++++------------ src/babel/traversal/path/index.js | 33 +++++++- 3 files changed, 104 insertions(+), 67 deletions(-) diff --git a/src/babel/api/node.js b/src/babel/api/node.js index 41153924ea..4c3e512180 100644 --- a/src/babel/api/node.js +++ b/src/babel/api/node.js @@ -14,6 +14,7 @@ export { default as TransformerPipeline } from "../transformation/transformer-pi export { default as traverse } from "../traversal"; export { default as buildExternalHelpers } from "../tools/build-external-helpers"; export { version } from "../../../package"; +export { all as parse } from "../helpers/parse"; import * as t from "../types"; export { t as types }; @@ -55,19 +56,3 @@ export function transformFileSync(filename: string, opts?: Object = {}) { opts.filename = filename; return transform(fs.readFileSync(filename), opts); } - -export function parse(code, opts = {}) { - opts.sourceType = "module"; - opts.ecmaVersion = Infinity; - opts.plugins = { - flow: true, - jsx: true - }; - opts.features = {}; - - for (var key in transform.pipeline.transformers) { - opts.features[key] = true; - } - - return acorn.parse(code, opts); -} diff --git a/src/babel/helpers/parse.js b/src/babel/helpers/parse.js index f7bcef7ba9..ebb78b3633 100644 --- a/src/babel/helpers/parse.js +++ b/src/babel/helpers/parse.js @@ -3,63 +3,86 @@ import estraverse from "estraverse"; import codeFrame from "./code-frame"; import * as acorn from "../../acorn"; -export default function (opts, code, callback) { +function parseCatch(code, opts) { + var comments = opts.onComment = []; + var tokens = opts.onToken = []; + try { - var comments = []; - var tokens = []; - - var parseOpts = { - allowImportExportEverywhere: opts.looseModules, - allowReturnOutsideFunction: opts.looseModules, - ecmaVersion: 6, - strictMode: opts.strictMode, - sourceType: opts.sourceType, - onComment: comments, - locations: true, - features: opts.features || {}, - plugins: opts.plugins || {}, - onToken: tokens, - ranges: true - }; - - if (opts.nonStandard) { - parseOpts.plugins.jsx = true; - parseOpts.plugins.flow = true; - } - - var ast = acorn.parse(code, parseOpts); - - estraverse.attachComments(ast, comments, tokens); - - ast = normalizeAst(ast, comments, tokens); - - if (callback) { - return callback(ast); - } else { - return ast; - } + return acorn.parse(code, opts); } catch (err) { - if (!err._babel) { + if (err._babel) { + throw err; + } else { err._babel = true; + } - var message = err.message = `${opts.filename}: ${err.message}`; + if (opts.errorMessage) { + err.message += ` - ${opts.errorMessage}`; + } - var loc = err.loc; - if (loc) { - err.codeFrame = codeFrame(code, loc.line, loc.column + 1, opts); - message += "\n" + err.codeFrame; - } + var message = err.message = `${opts.filename || "unknown"}: ${err.message}`; - 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 - } + 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 parseOpts = { + allowImportExportEverywhere: opts.looseModules, + allowReturnOutsideFunction: opts.looseModules, + ecmaVersion: 6, + strictMode: opts.strictMode, + sourceType: opts.sourceType, + locations: true, + features: opts.features || {}, + plugins: opts.plugins || {}, + ranges: true + }; + + if (opts.nonStandard) { + parseOpts.plugins.jsx = true; + parseOpts.plugins.flow = true; + } + + var ast = parseCatch(code, parseOpts); + + estraverse.attachComments(ast, parseOpts.onComment, parseOpts.onToken); + ast = normalizeAst(ast, parseOpts.onComment, parseOpts.onToken); + + if (callback) { + return callback(ast); + } else { + return ast; + } +} + +export function all(code, opts = {}) { + opts.sourceType = "module"; + opts.ecmaVersion = Infinity; + opts.plugins = { + flow: true, + jsx: true + }; + opts.features = {}; + + for (var key in transform.pipeline.transformers) { + opts.features[key] = true; + } + + return parseCatch(code, opts); +} diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index 197e1fbdf3..0071b4ef35 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -3,6 +3,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 * as parse from "../../helpers/parse"; import traverse from "../index"; import includes from "lodash/collection/includes"; import assign from "lodash/object/assign"; @@ -476,7 +477,21 @@ export default class TraversalPath { * Description */ - replaceWith(replacement, arraysAllowed) { + replaceWithSourceString(replacement) { + replacement = parse.all(`(${replacement})`, { + filename: "custom string", + errorMessage: "make sure this is an expression" + }).body[0].expression; + + traverse.removeProperties(replacement); + return this.replaceWith(replacement); + } + + /** + * Description + */ + + replaceWith(replacement, whateverAllowed) { if (this.removed) { throw new Error("You can't replace this node, we've already removed it"); } @@ -486,13 +501,27 @@ export default class TraversalPath { } if (Array.isArray(replacement)) { - if (arraysAllowed) { + if (whateverAllowed) { return this.replaceWithMultiple(replacement); } else { throw new Error("Don't use `path.replaceWith()` with an array of nodes, use `path.replaceWithMultiple()`"); } } + if (typeof replacement === "string") { + if (whateverAllowed) { + return this.replaceWithSourceString(replacement); + } else { + throw new Error("Don't use `path.replaceWith()` with a string, use `path.replaceWithSourceString()`"); + } + } + + // replacing a statement with an expression so wrap it in an expression statement + if (this.isPreviousType("Statement") && t.isExpression(replacement)) { + replacement = t.expressionStatement(replacement); + } + + // replacing an expression with a statement so let's explode it if (this.isPreviousType("Expression") && t.isStatement(replacement)) { return this.replaceExpressionWithStatements([replacement]); }