add support for replacing nodes with expression source strings

This commit is contained in:
Sebastian McKenzie 2015-05-03 23:02:19 +01:00
parent f24b5164d4
commit 0fc02f2cf0
3 changed files with 104 additions and 67 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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]);
}