Remove object-rest-spread syntax.

This commit is contained in:
Artem Yavorsky 2017-03-23 00:33:03 +02:00
parent 70354013f1
commit ddfb6f2c44
4 changed files with 339 additions and 351 deletions

View File

@ -9,14 +9,12 @@
"babel-types": "^6.23.0", "babel-types": "^6.23.0",
"babel-runtime": "^6.22.0", "babel-runtime": "^6.22.0",
"babel-template": "^6.23.0", "babel-template": "^6.23.0",
"babel-plugin-transform-strict-mode": "^6.22.0", "babel-plugin-transform-strict-mode": "^6.22.0"
"babel-plugin-syntax-object-rest-spread": "^6.13.0"
}, },
"keywords": [ "keywords": [
"babel-plugin" "babel-plugin"
], ],
"devDependencies": { "devDependencies": {
"babel-plugin-transform-object-assign": "6.22.0",
"babel-helper-plugin-test-runner": "^6.24.0" "babel-helper-plugin-test-runner": "^6.24.0"
} }
} }

View File

@ -1,6 +1,5 @@
import { basename, extname } from "path"; import { basename, extname } from "path";
import template from "babel-template"; import template from "babel-template";
import babelPluginSyntaxObjectRestSpread from "babel-plugin-syntax-object-rest-spread";
import babelPluginTransformStrictMode from "babel-plugin-transform-strict-mode"; import babelPluginTransformStrictMode from "babel-plugin-transform-strict-mode";
import * as t from "babel-types"; import * as t from "babel-types";
@ -129,287 +128,316 @@ export default function () {
} }
}; };
return Object.assign( return {
{ inherits: babelPluginTransformStrictMode,
inherits: babelPluginTransformStrictMode,
visitor: { visitor: {
ThisExpression(path, state) { ThisExpression(path, state) {
// If other plugins run after this plugin's Program#exit handler, we allow them to // If other plugins run after this plugin's Program#exit handler, we allow them to
// insert top-level `this` values. This allows the AMD and UMD plugins to // insert top-level `this` values. This allows the AMD and UMD plugins to
// function properly. // function properly.
if (this.ranCommonJS) return; if (this.ranCommonJS) return;
if ( if (
state.opts.allowTopLevelThis !== true && state.opts.allowTopLevelThis !== true &&
!path.findParent((path) => !path.is("shadow") && !path.findParent((path) => !path.is("shadow") &&
THIS_BREAK_KEYS.indexOf(path.type) >= 0) THIS_BREAK_KEYS.indexOf(path.type) >= 0)
) { ) {
path.replaceWith(t.identifier("undefined")); path.replaceWith(t.identifier("undefined"));
}
},
Program: {
exit(path) {
this.ranCommonJS = true;
const strict = !!this.opts.strict;
const noInterop = !!this.opts.noInterop;
const { scope } = path;
// rename these commonjs variables if they're declared in the file
scope.rename("module");
scope.rename("exports");
scope.rename("require");
let hasExports = false;
let hasImports = false;
const body: Array<Object> = path.get("body");
const imports = Object.create(null);
const exports = Object.create(null);
const nonHoistedExportNames = Object.create(null);
const topNodes = [];
const remaps = Object.create(null);
const requires = Object.create(null);
function addRequire(source, blockHoist) {
const cached = requires[source];
if (cached) return cached;
const ref = path.scope.generateUidIdentifier(basename(source, extname(source)));
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(ref, buildRequire(
t.stringLiteral(source)
).expression)
]);
// Copy location from the original import statement for sourcemap
// generation.
if (imports[source]) {
varDecl.loc = imports[source].loc;
}
if (typeof blockHoist === "number" && blockHoist > 0) {
varDecl._blockHoist = blockHoist;
}
topNodes.push(varDecl);
return requires[source] = ref;
} }
},
Program: { function addTo(obj, key, arr) {
exit(path) { const existing = obj[key] || [];
this.ranCommonJS = true; obj[key] = existing.concat(arr);
}
const strict = !!this.opts.strict; for (const path of body) {
const noInterop = !!this.opts.noInterop; if (path.isExportDeclaration()) {
hasExports = true;
const { scope } = path; const specifiers = [].concat(path.get("declaration"), path.get("specifiers"));
for (const specifier of specifiers) {
// rename these commonjs variables if they're declared in the file const ids = specifier.getBindingIdentifiers();
scope.rename("module"); if (ids.__esModule) {
scope.rename("exports"); throw specifier.buildCodeFrameError("Illegal export \"__esModule\"");
scope.rename("require");
let hasExports = false;
let hasImports = false;
const body: Array<Object> = path.get("body");
const imports = Object.create(null);
const exports = Object.create(null);
const nonHoistedExportNames = Object.create(null);
const topNodes = [];
const remaps = Object.create(null);
const requires = Object.create(null);
function addRequire(source, blockHoist) {
const cached = requires[source];
if (cached) return cached;
const ref = path.scope.generateUidIdentifier(basename(source, extname(source)));
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(ref, buildRequire(
t.stringLiteral(source)
).expression)
]);
// Copy location from the original import statement for sourcemap
// generation.
if (imports[source]) {
varDecl.loc = imports[source].loc;
}
if (typeof blockHoist === "number" && blockHoist > 0) {
varDecl._blockHoist = blockHoist;
}
topNodes.push(varDecl);
return requires[source] = ref;
}
function addTo(obj, key, arr) {
const existing = obj[key] || [];
obj[key] = existing.concat(arr);
}
for (const path of body) {
if (path.isExportDeclaration()) {
hasExports = true;
const specifiers = [].concat(path.get("declaration"), path.get("specifiers"));
for (const specifier of specifiers) {
const ids = specifier.getBindingIdentifiers();
if (ids.__esModule) {
throw specifier.buildCodeFrameError("Illegal export \"__esModule\"");
}
} }
} }
}
if (path.isImportDeclaration()) { if (path.isImportDeclaration()) {
hasImports = true; hasImports = true;
const key = path.node.source.value; const key = path.node.source.value;
const importsEntry = imports[key] || { const importsEntry = imports[key] || {
specifiers: [], specifiers: [],
maxBlockHoist: 0, maxBlockHoist: 0,
loc: path.node.loc, loc: path.node.loc,
}; };
importsEntry.specifiers.push(...path.node.specifiers); importsEntry.specifiers.push(...path.node.specifiers);
if (typeof path.node._blockHoist === "number") { if (typeof path.node._blockHoist === "number") {
importsEntry.maxBlockHoist = Math.max( importsEntry.maxBlockHoist = Math.max(
path.node._blockHoist, path.node._blockHoist,
importsEntry.maxBlockHoist importsEntry.maxBlockHoist
); );
} }
imports[key] = importsEntry; imports[key] = importsEntry;
path.remove(); path.remove();
} else if (path.isExportDefaultDeclaration()) { } else if (path.isExportDefaultDeclaration()) {
const declaration = path.get("declaration"); const declaration = path.get("declaration");
if (declaration.isFunctionDeclaration()) { if (declaration.isFunctionDeclaration()) {
const id = declaration.node.id; const id = declaration.node.id;
const defNode = t.identifier("default"); const defNode = t.identifier("default");
if (id) { if (id) {
addTo(exports, id.name, defNode); addTo(exports, id.name, defNode);
topNodes.push(buildExportsAssignment(defNode, id)); topNodes.push(buildExportsAssignment(defNode, id));
path.replaceWith(declaration.node); path.replaceWith(declaration.node);
} else {
topNodes.push(buildExportsAssignment(defNode, t.toExpression(declaration.node)));
path.remove();
}
} else if (declaration.isClassDeclaration()) {
const id = declaration.node.id;
const defNode = t.identifier("default");
if (id) {
addTo(exports, id.name, defNode);
path.replaceWithMultiple([
declaration.node,
buildExportsAssignment(defNode, id)
]);
} else {
path.replaceWith(buildExportsAssignment(defNode, t.toExpression(declaration.node)));
// Manualy re-queue `export default class {}` expressions so that the ES3 transform
// has an opportunity to convert them. Ideally this would happen automatically from the
// replaceWith above. See #4140 for more info.
path.parentPath.requeue(path.get("expression.left"));
}
} else { } else {
path.replaceWith(buildExportsAssignment(t.identifier("default"), declaration.node)); topNodes.push(buildExportsAssignment(defNode, t.toExpression(declaration.node)));
path.remove();
}
} else if (declaration.isClassDeclaration()) {
const id = declaration.node.id;
const defNode = t.identifier("default");
if (id) {
addTo(exports, id.name, defNode);
path.replaceWithMultiple([
declaration.node,
buildExportsAssignment(defNode, id)
]);
} else {
path.replaceWith(buildExportsAssignment(defNode, t.toExpression(declaration.node)));
// Manualy re-queue `export default foo;` expressions so that the ES3 transform // Manualy re-queue `export default class {}` expressions so that the ES3 transform
// has an opportunity to convert them. Ideally this would happen automatically from the // has an opportunity to convert them. Ideally this would happen automatically from the
// replaceWith above. See #4140 for more info. // replaceWith above. See #4140 for more info.
path.parentPath.requeue(path.get("expression.left")); path.parentPath.requeue(path.get("expression.left"));
} }
} else if (path.isExportNamedDeclaration()) { } else {
const declaration = path.get("declaration"); path.replaceWith(buildExportsAssignment(t.identifier("default"), declaration.node));
if (declaration.node) {
if (declaration.isFunctionDeclaration()) {
const id = declaration.node.id;
addTo(exports, id.name, id);
topNodes.push(buildExportsAssignment(id, id));
path.replaceWith(declaration.node);
} else if (declaration.isClassDeclaration()) {
const id = declaration.node.id;
addTo(exports, id.name, id);
path.replaceWithMultiple([
declaration.node,
buildExportsAssignment(id, id)
]);
nonHoistedExportNames[id.name] = true;
} else if (declaration.isVariableDeclaration()) {
const declarators = declaration.get("declarations");
for (const decl of declarators) {
const id = decl.get("id");
const init = decl.get("init"); // Manualy re-queue `export default foo;` expressions so that the ES3 transform
const exportsToInsert = []; // has an opportunity to convert them. Ideally this would happen automatically from the
if (!init.node) init.replaceWith(t.identifier("undefined")); // replaceWith above. See #4140 for more info.
path.parentPath.requeue(path.get("expression.left"));
if (id.isIdentifier()) {
addTo(exports, id.node.name, id.node);
init.replaceWith(buildExportsAssignment(id.node, init.node).expression);
nonHoistedExportNames[id.node.name] = true;
} else if (id.isObjectPattern()) {
for (let i = 0; i < id.node.properties.length; i++) {
const prop = id.node.properties[i];
let propValue = prop.value;
if (t.isAssignmentPattern(propValue)) {
propValue = propValue.left;
} else if (t.isRestProperty(prop)) {
propValue = prop.argument;
}
addTo(exports, propValue.name, propValue);
exportsToInsert.push(buildExportsAssignment(propValue, propValue));
nonHoistedExportNames[propValue.name] = true;
}
} else if (id.isArrayPattern() && id.node.elements) {
for (let i = 0; i < id.node.elements.length; i++) {
let elem = id.node.elements[i];
if (!elem) continue;
if (t.isAssignmentPattern(elem)) {
elem = elem.left;
} else if (t.isRestElement(elem)) {
elem = elem.argument;
}
const name = elem.name;
addTo(exports, name, elem);
exportsToInsert.push(buildExportsAssignment(elem, elem));
nonHoistedExportNames[name] = true;
}
}
path.insertAfter(exportsToInsert);
}
path.replaceWith(declaration.node);
}
continue;
}
const specifiers = path.get("specifiers");
const nodes = [];
const source = path.node.source;
if (source) {
const ref = addRequire(source.value, path.node._blockHoist);
for (const specifier of specifiers) {
if (specifier.isExportNamespaceSpecifier()) {
// todo
} else if (specifier.isExportDefaultSpecifier()) {
// todo
} else if (specifier.isExportSpecifier()) {
if (!noInterop && specifier.node.local.name === "default") {
topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name),
t.memberExpression(
t.callExpression(this.addHelper("interopRequireDefault"), [ref]),
specifier.node.local
)
));
} else {
topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name),
t.memberExpression(ref, specifier.node.local)));
}
nonHoistedExportNames[specifier.node.exported.name] = true;
}
}
} else {
for (const specifier of specifiers) {
if (specifier.isExportSpecifier()) {
addTo(exports, specifier.node.local.name, specifier.node.exported);
nonHoistedExportNames[specifier.node.exported.name] = true;
nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local));
}
}
}
path.replaceWithMultiple(nodes);
} else if (path.isExportAllDeclaration()) {
const exportNode = buildExportAll({
OBJECT: addRequire(path.node.source.value, path.node._blockHoist)
});
exportNode.loc = path.node.loc;
topNodes.push(exportNode);
path.remove();
} }
} } else if (path.isExportNamedDeclaration()) {
const declaration = path.get("declaration");
if (declaration.node) {
if (declaration.isFunctionDeclaration()) {
const id = declaration.node.id;
addTo(exports, id.name, id);
topNodes.push(buildExportsAssignment(id, id));
path.replaceWith(declaration.node);
} else if (declaration.isClassDeclaration()) {
const id = declaration.node.id;
addTo(exports, id.name, id);
path.replaceWithMultiple([
declaration.node,
buildExportsAssignment(id, id)
]);
nonHoistedExportNames[id.name] = true;
} else if (declaration.isVariableDeclaration()) {
const declarators = declaration.get("declarations");
for (const decl of declarators) {
const id = decl.get("id");
for (const source in imports) { const init = decl.get("init");
const { specifiers, maxBlockHoist } = imports[source]; const exportsToInsert = [];
if (specifiers.length) { if (!init.node) init.replaceWith(t.identifier("undefined"));
const uid = addRequire(source, maxBlockHoist);
let wildcard; if (id.isIdentifier()) {
addTo(exports, id.node.name, id.node);
init.replaceWith(buildExportsAssignment(id.node, init.node).expression);
nonHoistedExportNames[id.node.name] = true;
} else if (id.isObjectPattern()) {
for (let i = 0; i < id.node.properties.length; i++) {
const prop = id.node.properties[i];
let propValue = prop.value;
if (t.isAssignmentPattern(propValue)) {
propValue = propValue.left;
} else if (t.isRestProperty(prop)) {
propValue = prop.argument;
}
addTo(exports, propValue.name, propValue);
exportsToInsert.push(buildExportsAssignment(propValue, propValue));
nonHoistedExportNames[propValue.name] = true;
}
} else if (id.isArrayPattern() && id.node.elements) {
for (let i = 0; i < id.node.elements.length; i++) {
let elem = id.node.elements[i];
if (!elem) continue;
if (t.isAssignmentPattern(elem)) {
elem = elem.left;
} else if (t.isRestElement(elem)) {
elem = elem.argument;
}
const name = elem.name;
addTo(exports, name, elem);
exportsToInsert.push(buildExportsAssignment(elem, elem));
nonHoistedExportNames[name] = true;
}
}
path.insertAfter(exportsToInsert);
}
path.replaceWith(declaration.node);
}
continue;
}
for (let i = 0; i < specifiers.length; i++) { const specifiers = path.get("specifiers");
const specifier = specifiers[i]; const nodes = [];
if (t.isImportNamespaceSpecifier(specifier)) { const source = path.node.source;
if (strict || noInterop) { if (source) {
remaps[specifier.local.name] = uid; const ref = addRequire(source.value, path.node._blockHoist);
for (const specifier of specifiers) {
if (specifier.isExportNamespaceSpecifier()) {
// todo
} else if (specifier.isExportDefaultSpecifier()) {
// todo
} else if (specifier.isExportSpecifier()) {
if (!noInterop && specifier.node.local.name === "default") {
topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name),
t.memberExpression(
t.callExpression(this.addHelper("interopRequireDefault"), [ref]),
specifier.node.local
)
));
} else { } else {
topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name),
t.memberExpression(ref, specifier.node.local)));
}
nonHoistedExportNames[specifier.node.exported.name] = true;
}
}
} else {
for (const specifier of specifiers) {
if (specifier.isExportSpecifier()) {
addTo(exports, specifier.node.local.name, specifier.node.exported);
nonHoistedExportNames[specifier.node.exported.name] = true;
nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local));
}
}
}
path.replaceWithMultiple(nodes);
} else if (path.isExportAllDeclaration()) {
const exportNode = buildExportAll({
OBJECT: addRequire(path.node.source.value, path.node._blockHoist)
});
exportNode.loc = path.node.loc;
topNodes.push(exportNode);
path.remove();
}
}
for (const source in imports) {
const { specifiers, maxBlockHoist } = imports[source];
if (specifiers.length) {
const uid = addRequire(source, maxBlockHoist);
let wildcard;
for (let i = 0; i < specifiers.length; i++) {
const specifier = specifiers[i];
if (t.isImportNamespaceSpecifier(specifier)) {
if (strict || noInterop) {
remaps[specifier.local.name] = uid;
} else {
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(
specifier.local,
t.callExpression(
this.addHelper("interopRequireWildcard"),
[uid]
)
)
]);
if (maxBlockHoist > 0) {
varDecl._blockHoist = maxBlockHoist;
}
topNodes.push(varDecl);
}
wildcard = specifier.local;
} else if (t.isImportDefaultSpecifier(specifier)) {
specifiers[i] = t.importSpecifier(specifier.local, t.identifier("default"));
}
}
for (const specifier of specifiers) {
if (t.isImportSpecifier(specifier)) {
let target = uid;
if (specifier.imported.name === "default") {
if (wildcard) {
target = wildcard;
} else if (!noInterop) {
target = wildcard = path.scope.generateUidIdentifier(uid.name);
const varDecl = t.variableDeclaration("var", [ const varDecl = t.variableDeclaration("var", [
t.variableDeclarator( t.variableDeclarator(
specifier.local, target,
t.callExpression( t.callExpression(
this.addHelper("interopRequireWildcard"), this.addHelper("interopRequireDefault"),
[uid] [uid]
) )
) )
@ -421,100 +449,68 @@ export default function () {
topNodes.push(varDecl); topNodes.push(varDecl);
} }
wildcard = specifier.local;
} else if (t.isImportDefaultSpecifier(specifier)) {
specifiers[i] = t.importSpecifier(specifier.local, t.identifier("default"));
} }
remaps[specifier.local.name] = t.memberExpression(target,
t.cloneWithoutLoc(specifier.imported));
} }
for (const specifier of specifiers) {
if (t.isImportSpecifier(specifier)) {
let target = uid;
if (specifier.imported.name === "default") {
if (wildcard) {
target = wildcard;
} else if (!noInterop) {
target = wildcard = path.scope.generateUidIdentifier(uid.name);
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(
target,
t.callExpression(
this.addHelper("interopRequireDefault"),
[uid]
)
)
]);
if (maxBlockHoist > 0) {
varDecl._blockHoist = maxBlockHoist;
}
topNodes.push(varDecl);
}
}
remaps[specifier.local.name] = t.memberExpression(target,
t.cloneWithoutLoc(specifier.imported));
}
}
} else {
// bare import
const requireNode = buildRequire(t.stringLiteral(source));
requireNode.loc = imports[source].loc;
topNodes.push(requireNode);
} }
} else {
// bare import
const requireNode = buildRequire(t.stringLiteral(source));
requireNode.loc = imports[source].loc;
topNodes.push(requireNode);
} }
if (hasImports && Object.keys(nonHoistedExportNames).length) {
// avoid creating too long of export assignment to prevent stack overflow
const maxHoistedExportsNodeAssignmentLength = 100;
const nonHoistedExportNamesArr = Object.keys(nonHoistedExportNames);
for (
let currentExportsNodeAssignmentLength = 0;
currentExportsNodeAssignmentLength < nonHoistedExportNamesArr.length;
currentExportsNodeAssignmentLength += maxHoistedExportsNodeAssignmentLength
) {
const nonHoistedExportNamesChunk = nonHoistedExportNamesArr.slice(
currentExportsNodeAssignmentLength,
currentExportsNodeAssignmentLength + maxHoistedExportsNodeAssignmentLength);
let hoistedExportsNode = t.identifier("undefined");
nonHoistedExportNamesChunk.forEach(function (name) {
hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode)
.expression;
});
const node = t.expressionStatement(hoistedExportsNode);
node._blockHoist = 3;
topNodes.unshift(node);
}
}
// add __esModule declaration if this file has any exports
if (hasExports && !strict) {
let buildTemplate = buildExportsModuleDeclaration;
if (this.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration;
const declar = buildTemplate();
declar._blockHoist = 3;
topNodes.unshift(declar);
}
path.unshiftContainer("body", topNodes);
path.traverse(reassignmentVisitor, {
remaps,
scope,
exports,
requeueInParent: (newPath) => path.requeue(newPath),
});
} }
if (hasImports && Object.keys(nonHoistedExportNames).length) {
// avoid creating too long of export assignment to prevent stack overflow
const maxHoistedExportsNodeAssignmentLength = 100;
const nonHoistedExportNamesArr = Object.keys(nonHoistedExportNames);
for (
let currentExportsNodeAssignmentLength = 0;
currentExportsNodeAssignmentLength < nonHoistedExportNamesArr.length;
currentExportsNodeAssignmentLength += maxHoistedExportsNodeAssignmentLength
) {
const nonHoistedExportNamesChunk = nonHoistedExportNamesArr.slice(
currentExportsNodeAssignmentLength,
currentExportsNodeAssignmentLength + maxHoistedExportsNodeAssignmentLength);
let hoistedExportsNode = t.identifier("undefined");
nonHoistedExportNamesChunk.forEach(function (name) {
hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode)
.expression;
});
const node = t.expressionStatement(hoistedExportsNode);
node._blockHoist = 3;
topNodes.unshift(node);
}
}
// add __esModule declaration if this file has any exports
if (hasExports && !strict) {
let buildTemplate = buildExportsModuleDeclaration;
if (this.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration;
const declar = buildTemplate();
declar._blockHoist = 3;
topNodes.unshift(declar);
}
path.unshiftContainer("body", topNodes);
path.traverse(reassignmentVisitor, {
remaps,
scope,
exports,
requeueInParent: (newPath) => path.requeue(newPath),
});
} }
} }
}, }
babelPluginSyntaxObjectRestSpread() };
);
} }

View File

@ -1,5 +0,0 @@
"use strict";
const { foo, ...bar } = {};
exports.foo = foo;
exports.bar = bar;