Move plugin processing to top of plugins (#6381)

* centralize plugin options

* Centralize plugins options

- move more options to the top
- move validations that depend on options to the top

* use isLoose option

* Move more validations to the top

* Move ref parameter for rewriteModuleStatementsAndPrepareHeader() to the top

* fix eslint errors

* remove unused parameter

* set default systemGlobal value

* Revert "Move ref parameter for rewriteModuleStatementsAndPrepareHeader() to the top"

This reverts commit b3855302d17fa19d8acb4c8accab3680c8d2710e.

* Revert "Move more validations to the top"

This reverts commit e5861d8a034ff8f553391f55654f753bcf428a5d.

* fix allowMutablePropsOnTags option usage

* improve naming

* change Contructor definition for sake of consistency

* move allowMutablePropsOnTags validation to the top

* add missing !
This commit is contained in:
Anton Rusinov
2017-10-10 07:51:34 +03:00
committed by Logan Smyth
parent d89063bb32
commit fcdfc61bdb
17 changed files with 135 additions and 130 deletions

View File

@@ -3,7 +3,8 @@ import syntaxAsyncFunctions from "babel-plugin-syntax-async-functions";
import { addNamed } from "babel-helper-module-imports";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { method, module } = options;
return {
inherits: syntaxAsyncFunctions,
@@ -11,8 +12,6 @@ export default function({ types: t }) {
Function(path, state) {
if (!path.node.async || path.node.generator) return;
const { module, method } = state.opts;
let wrapAsync = state.methodWrapper;
if (wrapAsync) {
wrapAsync = t.cloneDeep(wrapAsync);

View File

@@ -2,7 +2,9 @@ import nameFunction from "babel-helper-function-name";
import template from "babel-template";
import syntaxClassProperties from "babel-plugin-syntax-class-properties";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { loose } = options;
const findBareSupers = {
Super(path) {
if (path.parentPath.isCallExpression({ callee: path.node })) {
@@ -48,14 +50,15 @@ export default function({ types: t }) {
),
);
const buildClassProperty = loose
? buildClassPropertyLoose
: buildClassPropertySpec;
return {
inherits: syntaxClassProperties,
visitor: {
Class(path, state) {
const buildClassProperty = state.opts.loose
? buildClassPropertyLoose
: buildClassPropertySpec;
Class(path) {
const isDerived = !!path.node.superClass;
let constructor;
const props = [];

View File

@@ -1,11 +1,11 @@
import type NodePath from "babel-traverse";
export default function() {
export default function(babel, options) {
const { spec } = options;
return {
visitor: {
ArrowFunctionExpression(
path: NodePath<BabelNodeArrowFunctionExpression>,
state: Object,
) {
// In some conversion cases, it may have already been converted to a function while this callback
// was queued up.
@@ -15,7 +15,7 @@ export default function() {
// While other utils may be fine inserting other arrows to make more transforms possible,
// the arrow transform itself absolutely cannot insert new arrow functions.
allowInsertArrow: false,
specCompliant: !!state.opts.spec,
specCompliant: !!spec,
});
},
},

View File

@@ -3,7 +3,10 @@ import VanillaTransformer from "./vanilla";
import annotateAsPure from "babel-helper-annotate-as-pure";
import nameFunction from "babel-helper-function-name";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { loose } = options;
const Constructor = loose ? LooseTransformer : VanillaTransformer;
// todo: investigate traversal requeueing
const VISITED = Symbol();
@@ -50,9 +53,6 @@ export default function({ types: t }) {
node[VISITED] = true;
let Constructor = VanillaTransformer;
if (state.opts.loose) Constructor = LooseTransformer;
path.replaceWith(new Constructor(path, state.file).run());
if (path.isCallExpression()) {

View File

@@ -1,4 +1,9 @@
export default function({ types: t, template }) {
export default function({ types: t, template }, options) {
const { loose } = options;
const pushComputedProps = loose
? pushComputedPropsLoose
: pushComputedPropsSpec;
const buildMutatorMapAssign = template(`
MUTATOR_MAP_REF[KEY] = MUTATOR_MAP_REF[KEY] || {};
MUTATOR_MAP_REF[KEY].KIND = VALUE;
@@ -62,7 +67,7 @@ export default function({ types: t, template }) {
);
}
function loose(info) {
function pushComputedPropsLoose(info) {
for (const prop of info.computedProps) {
if (prop.kind === "get" || prop.kind === "set") {
pushMutatorDefine(info, prop);
@@ -72,7 +77,7 @@ export default function({ types: t, template }) {
}
}
function spec(info) {
function pushComputedPropsSpec(info) {
const { objId, body, computedProps, state } = info;
for (const prop of computedProps) {
@@ -145,9 +150,6 @@ export default function({ types: t, template }) {
]),
);
let callback = spec;
if (state.opts.loose) callback = loose;
let mutatorRef;
const getMutatorId = function() {
@@ -164,7 +166,7 @@ export default function({ types: t, template }) {
return mutatorRef;
};
const single = callback({
const single = pushComputedProps({
scope,
objId,
body,

View File

@@ -1,4 +1,9 @@
export default function({ template, types: t }) {
export default function({ template, types: t }, options) {
const { loose } = options;
const pushComputedProps = loose
? pushComputedPropsLoose
: pushComputedPropsSpec;
const buildForOfArray = template(`
for (var KEY = 0; KEY < ARR.length; KEY++) BODY;
`);
@@ -113,11 +118,8 @@ export default function({ template, types: t }) {
return;
}
let callback = spec;
if (state.opts.loose) callback = loose;
const { node } = path;
const build = callback(path, state);
const build = pushComputedProps(path, state);
const declar = build.declar;
const loop = build.loop;
const block = loop.body;
@@ -146,7 +148,7 @@ export default function({ template, types: t }) {
},
};
function loose(path, file) {
function pushComputedPropsLoose(path, file) {
const { node, scope, parent } = path;
const { left } = node;
let declar, id, intermediate;
@@ -201,7 +203,7 @@ export default function({ template, types: t }) {
};
}
function spec(path, file) {
function pushComputedPropsSpec(path, file) {
const { node, scope, parent } = path;
const left = node.left;
let declar;

View File

@@ -14,21 +14,14 @@ const buildWrapper = template(`
})
`);
export default function({ types: t }) {
export default function({ types: t }, options) {
const { loose, allowTopLevelThis, strict, strictMode, noInterop } = options;
return {
visitor: {
Program: {
exit(path, state) {
exit(path) {
if (!isModule(path)) return;
const {
loose,
allowTopLevelThis,
strict,
strictMode,
noInterop,
} = state.opts;
let moduleName = this.getModuleName();
if (moduleName) moduleName = t.stringLiteral(moduleName);

View File

@@ -8,7 +8,17 @@ import {
} from "babel-helper-module-transforms";
import simplifyAccess from "babel-helper-simple-access";
export default function({ types: t, template }) {
export default function({ types: t, template }, options) {
const {
loose,
allowTopLevelThis,
strict,
strictMode,
noInterop,
// Defaulting to 'true' for now. May change before 7.x major.
allowCommonJSExports = true,
} = options;
const moduleAssertion = template(`
(function(){
throw new Error("The CommonJS 'module' variable is not available in ES6 modules.");
@@ -85,22 +95,11 @@ export default function({ types: t, template }) {
return {
visitor: {
Program: {
exit(path, state) {
exit(path) {
// For now this requires unambiguous rather that just sourceType
// because Babel currently parses all files as sourceType:module.
if (!isModule(path, true /* requireUnambiguous */)) return;
const {
loose,
allowTopLevelThis,
strict,
strictMode,
noInterop,
// Defaulting to 'true' for now. May change before 7.x major.
allowCommonJSExports = true,
} = state.opts;
// Rename the bindings auto-injected into the scope so there is no
// risk of conflict between the bindings.
path.scope.rename("exports");

View File

@@ -22,7 +22,8 @@ const buildExportAll = template(`
const TYPE_IMPORT = "Import";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { systemGlobal = "System" } = options;
const IGNORE_REASSIGNMENT_SYMBOL = Symbol();
const reassignmentVisitor = {
@@ -359,7 +360,7 @@ export default function({ types: t }) {
path.node.body = [
buildTemplate({
SYSTEM_REGISTER: t.memberExpression(
t.identifier(state.opts.systemGlobal || "System"),
t.identifier(systemGlobal),
t.identifier("register"),
),
BEFORE_BODY: beforeBody,

View File

@@ -30,7 +30,17 @@ const buildWrapper = template(`
})
`);
export default function({ types: t }) {
export default function({ types: t }, options) {
const {
globals,
exactGlobals,
loose,
allowTopLevelThis,
strict,
strictMode,
noInterop,
} = options;
/**
* Build the assignment statements that initialize the UMD global.
*/
@@ -107,18 +117,9 @@ export default function({ types: t }) {
return {
visitor: {
Program: {
exit(path, state) {
exit(path) {
if (!isModule(path)) return;
const {
globals,
exactGlobals,
loose,
allowTopLevelThis,
strict,
strictMode,
noInterop,
} = state.opts;
const browserGlobals = globals || {};
let moduleName = this.getModuleName();

View File

@@ -1,7 +1,8 @@
import convertFunctionParams from "./params";
import convertFunctionRest from "./rest";
export default function() {
export default function(babel, options) {
const { loose } = options;
return {
visitor: {
Function(path) {
@@ -16,7 +17,7 @@ export default function() {
}
const convertedRest = convertFunctionRest(path);
const convertedParams = convertFunctionParams(path, this.opts.loose);
const convertedParams = convertFunctionParams(path, loose);
if (convertedRest || convertedParams) {
// Manually reprocess this scope to ensure that the moved params are updated.

View File

@@ -1,9 +1,8 @@
export default function({ types: t }) {
function getSpreadLiteral(spread, scope, state) {
if (
state.opts.loose &&
!t.isIdentifier(spread.argument, { name: "arguments" })
) {
export default function({ types: t }, options) {
const { loose } = options;
function getSpreadLiteral(spread, scope) {
if (loose && !t.isIdentifier(spread.argument, { name: "arguments" })) {
return spread.argument;
} else {
return scope.toArray(spread.argument, true);
@@ -25,14 +24,14 @@ export default function({ types: t }) {
return [];
}
function build(props: Array, scope, state) {
function build(props: Array, scope) {
const nodes = [];
let _props = [];
for (const prop of props) {
if (t.isSpreadElement(prop)) {
_props = push(_props, nodes);
nodes.push(getSpreadLiteral(prop, scope, state));
nodes.push(getSpreadLiteral(prop, scope));
} else {
_props.push(prop);
}

View File

@@ -1,6 +1,11 @@
import annotateAsPure from "babel-helper-annotate-as-pure";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { loose } = options;
let helperName = "taggedTemplateLiteral";
if (loose) helperName += "Loose";
/**
* This function groups the objects into multiple calls to `.concat()` in
* order to preserve execution order of the primitive conversion, e.g.
@@ -41,7 +46,7 @@ export default function({ types: t }) {
this.templates = new Map();
},
visitor: {
TaggedTemplateExpression(path, state) {
TaggedTemplateExpression(path) {
const { node } = path;
const { quasi } = node;
@@ -59,9 +64,6 @@ export default function({ types: t }) {
raws.push(t.stringLiteral(raw));
}
let helperName = "taggedTemplateLiteral";
if (state.opts.loose) helperName += "Loose";
// Generate a unique name based on the string literals so we dedupe
// identical strings used in the program.
const rawParts = raws.map(s => s.value).join(",");
@@ -97,7 +99,7 @@ export default function({ types: t }) {
);
},
TemplateLiteral(path, state) {
TemplateLiteral(path) {
const nodes = [];
const expressions = path.get("expressions");
@@ -118,14 +120,13 @@ export default function({ types: t }) {
// since `+` is left-to-right associative
// ensure the first node is a string if first/second isn't
const considerSecondNode =
!state.opts.loose || !t.isStringLiteral(nodes[1]);
const considerSecondNode = !loose || !t.isStringLiteral(nodes[1]);
if (!t.isStringLiteral(nodes[0]) && considerSecondNode) {
nodes.unshift(t.stringLiteral(""));
}
let root = nodes[0];
if (state.opts.loose) {
if (loose) {
for (let i = 1; i < nodes.length; i++) {
root = t.binaryExpression("+", root, nodes[i]);
}

View File

@@ -1,7 +1,9 @@
import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining";
export default function({ types: t }) {
function optional(path, replacementPath, loose = false) {
export default function({ types: t }, options) {
const { loose = false } = options;
function optional(path, replacementPath) {
const { scope } = path;
const optionals = [];
const nil = scope.buildUndefinedNode();
@@ -123,7 +125,7 @@ export default function({ types: t }) {
return;
}
optional(path, findReplacementPath(path), this.opts.loose);
optional(path, findReplacementPath(path));
},
},
};

View File

@@ -1,4 +1,15 @@
export default function transformReactConstantElement({ types: t }) {
export default function transformReactConstantElement({ types: t }, options) {
const { allowMutablePropsOnTags } = options;
if (
allowMutablePropsOnTags != null &&
!Array.isArray(allowMutablePropsOnTags)
) {
throw new Error(
".allowMutablePropsOnTags must be an array, null, or undefined.",
);
}
const HOISTED = new WeakSet();
const immutabilityVisitor = {
@@ -71,12 +82,7 @@ export default function transformReactConstantElement({ types: t }) {
// This transform takes the option `allowMutablePropsOnTags`, which is an array
// of JSX tags to allow mutable props (such as objects, functions) on. Use sparingly
// and only on tags you know will never modify their own props.
if (this.opts.allowMutablePropsOnTags != null) {
if (!Array.isArray(this.opts.allowMutablePropsOnTags)) {
throw new Error(
".allowMutablePropsOnTags must be an array, null, or undefined.",
);
}
if (allowMutablePropsOnTags != null) {
// Get the element's name. If it's a member expression, we use the last part of the path.
// So the option ["FormattedMessage"] would match "Intl.FormattedMessage".
let namePath = path.get("openingElement.name");
@@ -86,7 +92,7 @@ export default function transformReactConstantElement({ types: t }) {
const elementName = namePath.node.name;
state.mutablePropsAllowed =
this.opts.allowMutablePropsOnTags.indexOf(elementName) > -1;
allowMutablePropsOnTags.indexOf(elementName) > -1;
}
// Traverse all props passed to this element for immutability.

View File

@@ -1,7 +1,10 @@
import jsx from "babel-plugin-syntax-jsx";
import helper from "babel-helper-builder-react-jsx";
export default function({ types: t }) {
export default function({ types: t }, options) {
const { pragma } = options;
let id = pragma || "React.createElement";
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
const visitor = helper({
@@ -22,7 +25,6 @@ export default function({ types: t }) {
visitor.Program = function(path, state) {
const { file } = state;
let id = state.opts.pragma || "React.createElement";
for (const comment of (file.ast.comments: Array<Object>)) {
const matches = JSX_ANNOTATION_REGEX.exec(comment.value);

View File

@@ -2,10 +2,20 @@ import { addDefault, isModule } from "babel-helper-module-imports";
import definitions from "./definitions";
export default function({ types: t }) {
function getRuntimeModuleName(opts) {
return opts.moduleName || "babel-runtime";
}
export default function({ types: t }, options) {
const {
helpers,
moduleName = "babel-runtime",
polyfill,
regenerator,
useBuiltIns,
useESModules,
} = options;
const notRegenerator = regenerator !== false;
const notPolyfillOrDoesUseBuiltIns = polyfill === false || useBuiltIns;
const isPolyfillAndUseBuiltIns = polyfill && useBuiltIns;
const baseHelpersDir = useBuiltIns ? "helpers/builtin" : "helpers";
const helpersDir = useESModules ? `${baseHelpersDir}/es6` : baseHelpersDir;
function has(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
@@ -15,15 +25,7 @@ export default function({ types: t }) {
return {
pre(file) {
const moduleName = getRuntimeModuleName(this.opts);
if (this.opts.helpers !== false) {
const baseHelpersDir = this.opts.useBuiltIns
? "helpers/builtin"
: "helpers";
const helpersDir = this.opts.useESModules
? `${baseHelpersDir}/es6`
: baseHelpersDir;
if (helpers !== false) {
file.set("helperGenerator", name => {
const isInteropHelper = HEADER_HELPERS.indexOf(name) !== -1;
@@ -41,7 +43,7 @@ export default function({ types: t }) {
});
}
if (this.opts.polyfill && this.opts.useBuiltIns) {
if (isPolyfillAndUseBuiltIns) {
throw new Error(
"The polyfill option conflicts with useBuiltIns; use one or the other",
);
@@ -75,12 +77,9 @@ export default function({ types: t }) {
},
visitor: {
ReferencedIdentifier(path, state) {
ReferencedIdentifier(path) {
const { node, parent, scope } = path;
if (
node.name === "regeneratorRuntime" &&
state.opts.regenerator !== false
) {
if (node.name === "regeneratorRuntime" && notRegenerator) {
path.replaceWith(
this.addDefaultImport(
`${this.moduleName}/regenerator`,
@@ -90,14 +89,13 @@ export default function({ types: t }) {
return;
}
if (state.opts.polyfill === false || state.opts.useBuiltIns) return;
if (notPolyfillOrDoesUseBuiltIns) return;
if (t.isMemberExpression(parent)) return;
if (!has(definitions.builtins, node.name)) return;
if (scope.getBindingIdentifier(node.name)) return;
// Symbol() -> _core.Symbol(); new Promise -> new _core.Promise
const moduleName = getRuntimeModuleName(state.opts);
path.replaceWith(
this.addDefaultImport(
`${moduleName}/core-js/${definitions.builtins[node.name]}`,
@@ -107,8 +105,8 @@ export default function({ types: t }) {
},
// arr[Symbol.iterator]() -> _core.$for.getIterator(arr)
CallExpression(path, state) {
if (state.opts.polyfill === false || state.opts.useBuiltIns) return;
CallExpression(path) {
if (notPolyfillOrDoesUseBuiltIns) return;
// we can't compile this
if (path.node.arguments.length) return;
@@ -120,7 +118,6 @@ export default function({ types: t }) {
return;
}
const moduleName = getRuntimeModuleName(state.opts);
path.replaceWith(
t.callExpression(
this.addDefaultImport(
@@ -133,13 +130,12 @@ export default function({ types: t }) {
},
// Symbol.iterator in arr -> core.$for.isIterable(arr)
BinaryExpression(path, state) {
if (state.opts.polyfill === false || state.opts.useBuiltIns) return;
BinaryExpression(path) {
if (notPolyfillOrDoesUseBuiltIns) return;
if (path.node.operator !== "in") return;
if (!path.get("left").matchesPattern("Symbol.iterator")) return;
const moduleName = getRuntimeModuleName(state.opts);
path.replaceWith(
t.callExpression(
this.addDefaultImport(
@@ -153,8 +149,8 @@ export default function({ types: t }) {
// Array.from -> _core.Array.from
MemberExpression: {
enter(path, state) {
if (state.opts.polyfill === false || state.opts.useBuiltIns) return;
enter(path) {
if (notPolyfillOrDoesUseBuiltIns) return;
if (!path.isReferenced()) return;
const { node } = path;
@@ -183,7 +179,6 @@ export default function({ types: t }) {
}
}
const moduleName = getRuntimeModuleName(state.opts);
path.replaceWith(
this.addDefaultImport(
`${moduleName}/core-js/${methods[prop.name]}`,
@@ -192,8 +187,8 @@ export default function({ types: t }) {
);
},
exit(path, state) {
if (state.opts.polyfill === false || state.opts.useBuiltIns) return;
exit(path) {
if (notPolyfillOrDoesUseBuiltIns) return;
if (!path.isReferenced()) return;
const { node } = path;
@@ -202,7 +197,6 @@ export default function({ types: t }) {
if (!has(definitions.builtins, obj.name)) return;
if (path.scope.getBindingIdentifier(obj.name)) return;
const moduleName = getRuntimeModuleName(state.opts);
path.replaceWith(
t.memberExpression(
this.addDefaultImport(