Added babel-helper-split-export-declaration (#7313)

This commit is contained in:
Mateusz Burzyński 2018-02-13 16:44:05 +01:00 committed by GitHub
parent ea3f2d9299
commit 4d164bd8e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 158 additions and 104 deletions

View File

@ -10,6 +10,7 @@
"dependencies": {
"@babel/helper-module-imports": "7.0.0-beta.40",
"@babel/helper-simple-access": "7.0.0-beta.40",
"@babel/helper-split-export-declaration": "7.0.0-beta.40",
"@babel/template": "7.0.0-beta.40",
"@babel/types": "7.0.0-beta.40",
"lodash": "^4.2.0"

View File

@ -1,6 +1,6 @@
import { basename, extname } from "path";
import * as t from "@babel/types";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
export type ModuleMetadata = {
exportName: string,
@ -399,35 +399,7 @@ function nameAnonymousExports(programPath: NodePath) {
// Name anonymous exported locals.
programPath.get("body").forEach(child => {
if (!child.isExportDefaultDeclaration()) return;
// export default foo;
const declaration = child.get("declaration");
if (declaration.isFunctionDeclaration()) {
if (!declaration.node.id) {
declaration.node.id = declaration.scope.generateUidIdentifier(
"default",
);
}
} else if (declaration.isClassDeclaration()) {
if (!declaration.node.id) {
declaration.node.id = declaration.scope.generateUidIdentifier(
"default",
);
}
} else {
const id = declaration.scope.generateUidIdentifier("default");
const namedDecl = t.exportNamedDeclaration(null, [
t.exportSpecifier(t.identifier(id.name), t.identifier("default")),
]);
namedDecl._blockHoist = child.node._blockHoist;
const varDecl = t.variableDeclaration("var", [
t.variableDeclarator(id, declaration.node),
]);
varDecl._blockHoist = child.node._blockHoist;
child.replaceWithMultiple([namedDecl, varDecl]);
}
splitExportDeclaration(child);
});
}

View File

@ -112,6 +112,7 @@ const rewriteBindingInitVisitor = {
Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => {
const exportNames = exported.get(localName) || [];
if (exportNames.length > 0) {
const statement = t.expressionStatement(
buildBindingExportAssignmentExpression(

View File

@ -0,0 +1,3 @@
src
test
*.log

View File

@ -0,0 +1,23 @@
# @babel/helper-split-export-declaration
## API
```js
declare export default splitExportDeclaration(path: NodePath);
```
## Usage
```js
import traverse from "@babel/traverse";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
// ...
traverse(file, {
ExportDefaultDeclaration(path) {
if (!path.get("declaration").isClassDeclaration()) return;
splitExportDeclaration(path);
},
});
```

View File

@ -0,0 +1,11 @@
{
"name": "@babel/helper-split-export-declaration",
"version": "7.0.0-beta.40",
"description": "",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-split-export-declaration",
"license": "MIT",
"main": "lib/index.js",
"dependencies": {
"@babel/types": "7.0.0-beta.40"
}
}

View File

@ -0,0 +1,76 @@
import * as t from "@babel/types";
export default function splitExportDeclaration(exportDeclaration) {
if (!exportDeclaration.isExportDeclaration()) {
throw new Error("Only export declarations can be splitted.");
}
// build specifiers that point back to this export declaration
const isDefault = exportDeclaration.isExportDefaultDeclaration();
const declaration = exportDeclaration.get("declaration");
const isClassDeclaration = declaration.isClassDeclaration();
if (isDefault) {
const standaloneDeclaration =
declaration.isFunctionDeclaration() || isClassDeclaration;
const scope = declaration.isScope()
? declaration.scope.parent
: declaration.scope;
let id = declaration.node.id;
let needBindingRegistration = false;
if (!id) {
needBindingRegistration = true;
id = scope.generateUidIdentifier("default");
if (
standaloneDeclaration ||
declaration.isFunctionExpression() ||
declaration.isClassExpression()
) {
declaration.node.id = t.cloneNode(id);
}
}
const updatedDeclaration = standaloneDeclaration
? declaration
: t.variableDeclaration("var", [
t.variableDeclarator(t.cloneNode(id), declaration.node),
]);
const updatedExportDeclaration = t.exportNamedDeclaration(null, [
t.exportSpecifier(t.cloneNode(id), t.identifier("default")),
]);
exportDeclaration.insertAfter(updatedExportDeclaration);
exportDeclaration.replaceWith(updatedDeclaration);
if (needBindingRegistration) {
scope.registerBinding(
isClassDeclaration ? "let" : "var",
exportDeclaration,
);
}
return exportDeclaration;
}
if (exportDeclaration.get("specifiers").length > 0) {
throw new Error("It doesn't make sense to split exported specifiers.");
}
const bindingIdentifiers = declaration.getOuterBindingIdentifiers();
const specifiers = Object.keys(bindingIdentifiers).map(name => {
return t.exportSpecifier(t.identifier(name), t.identifier(name));
});
const aliasDeclar = t.exportNamedDeclaration(null, specifiers);
exportDeclaration.insertAfter(aliasDeclar);
exportDeclaration.replaceWith(declaration.node);
return exportDeclaration;
}

View File

@ -9,15 +9,15 @@ call((_temp = _class = function _class() {
value: true
}), _temp));
var _class2 = function _class2() {
babelHelpers.classCallCheck(this, _class2);
var _default = function _default() {
babelHelpers.classCallCheck(this, _default);
};
Object.defineProperty(_class2, "test", {
Object.defineProperty(_default, "test", {
configurable: true,
enumerable: true,
writable: true,
value: true
});
export { _class2 as default };
export { _default as default };
;

View File

@ -4,10 +4,10 @@ call((_temp = _class = function _class() {
babelHelpers.classCallCheck(this, _class);
}, _class.test = true, _temp));
var _class2 = function _class2() {
babelHelpers.classCallCheck(this, _class2);
var _default = function _default() {
babelHelpers.classCallCheck(this, _default);
};
_class2.test = true;
export { _class2 as default };
_default.test = true;
export { _default as default };
;

View File

@ -11,6 +11,7 @@
"@babel/helper-function-name": "7.0.0-beta.40",
"@babel/helper-optimise-call-expression": "7.0.0-beta.40",
"@babel/helper-replace-supers": "7.0.0-beta.40",
"@babel/helper-split-export-declaration": "7.0.0-beta.40",
"globals": "^11.1.0"
},
"keywords": [

View File

@ -2,6 +2,7 @@ import LooseTransformer from "./loose";
import VanillaTransformer from "./vanilla";
import annotateAsPure from "@babel/helper-annotate-as-pure";
import nameFunction from "@babel/helper-function-name";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
import { types as t } from "@babel/core";
import globals from "globals";
@ -24,19 +25,7 @@ export default function(api, options) {
visitor: {
ExportDefaultDeclaration(path) {
if (!path.get("declaration").isClassDeclaration()) return;
const { node } = path;
const ref =
node.declaration.id || path.scope.generateUidIdentifier("class");
node.declaration.id = ref;
// Split the class declaration and the export into two separate statements.
path.replaceWith(node.declaration);
path.insertAfter(
t.exportNamedDeclaration(null, [
t.exportSpecifier(t.cloneNode(ref), t.identifier("default")),
]),
);
splitExportDeclaration(path);
},
ClassDeclaration(path) {

View File

@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
});
exports.default = void 0;
var _class = function _class() {
babelHelpers.classCallCheck(this, _class);
var _default = function _default() {
babelHelpers.classCallCheck(this, _default);
};
exports.default = _class;
exports.default = _default;

View File

@ -1,14 +1,14 @@
var _class =
var _default =
/*#__PURE__*/
function (_A) {
babelHelpers.inherits(_class, _A);
babelHelpers.inherits(_default, _A);
function _class() {
babelHelpers.classCallCheck(this, _class);
return babelHelpers.possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).apply(this, arguments));
function _default() {
babelHelpers.classCallCheck(this, _default);
return babelHelpers.possibleConstructorReturn(this, (_default.__proto__ || Object.getPrototypeOf(_default)).apply(this, arguments));
}
return _class;
return _default;
}(A);
export { _class as default };
export { _default as default };

View File

@ -1,13 +1,14 @@
export { _whatever as whatever };
export { _wowzers as default };
var _foo = "yes",
foob = "no";
export { _foo as foo, foob };
function _whatever() {}
export { _whatever as whatever };
function _wowzers() {}
export { _wowzers as default };
var bar = {
foo: function foo() {
_foo;

View File

@ -1,7 +1,6 @@
export { _foo as foo };
function _foo(bar) {}
export { _foo as foo };
var bar = {
foo: function foo() {
_foo;

View File

@ -11,6 +11,7 @@
"@babel/code-frame": "7.0.0-beta.40",
"@babel/generator": "7.0.0-beta.40",
"@babel/helper-function-name": "7.0.0-beta.40",
"@babel/helper-split-export-declaration": "7.0.0-beta.40",
"@babel/types": "7.0.0-beta.40",
"babylon": "7.0.0-beta.40",
"debug": "^3.0.1",

View File

@ -210,7 +210,7 @@ export default class Scope {
* Generate a unique identifier and add it to the current scope.
*/
generateDeclaredUidIdentifier(name: string = "temp") {
generateDeclaredUidIdentifier(name?: string) {
const id = this.generateUidIdentifier(name);
this.push({ id });
return t.cloneNode(id);
@ -220,7 +220,7 @@ export default class Scope {
* Generate a unique identifier.
*/
generateUidIdentifier(name: string = "temp") {
generateUidIdentifier(name?: string) {
return t.identifier(this.generateUid(name));
}

View File

@ -1,4 +1,5 @@
import Binding from "../binding";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
import * as t from "@babel/types";
const renameVisitor = {
@ -40,48 +41,20 @@ export default class Renamer {
binding: Binding;
maybeConvertFromExportDeclaration(parentDeclar) {
const exportDeclar =
parentDeclar.parentPath.isExportDeclaration() && parentDeclar.parentPath;
if (!exportDeclar) return;
const maybeExportDeclar = parentDeclar.parentPath;
// build specifiers that point back to this export declaration
const isDefault = exportDeclar.isExportDefaultDeclaration();
if (!maybeExportDeclar.isExportDeclaration()) {
return;
}
if (
isDefault &&
(parentDeclar.isFunctionDeclaration() ||
parentDeclar.isClassDeclaration()) &&
!parentDeclar.node.id
maybeExportDeclar.isExportDefaultDeclaration() &&
!maybeExportDeclar.get("declaration").node.id
) {
// Ensure that default class and function exports have a name so they have a identifier to
// reference from the export specifier list.
parentDeclar.node.id = parentDeclar.scope.generateUidIdentifier(
"default",
);
return;
}
const bindingIdentifiers = parentDeclar.getOuterBindingIdentifiers();
const specifiers = [];
for (const name in bindingIdentifiers) {
const localName = name === this.oldName ? this.newName : name;
const exportedName = isDefault ? "default" : name;
specifiers.push(
t.exportSpecifier(t.identifier(localName), t.identifier(exportedName)),
);
}
if (specifiers.length) {
const aliasDeclar = t.exportNamedDeclaration(null, specifiers);
// hoist to the top if it's a function
if (parentDeclar.isFunctionDeclaration()) {
aliasDeclar._blockHoist = 3;
}
exportDeclar.insertAfter(aliasDeclar);
exportDeclar.replaceWith(parentDeclar.node);
}
splitExportDeclaration(maybeExportDeclar);
}
maybeConvertFromClassFunctionDeclaration(path) {
@ -129,7 +102,10 @@ export default class Renamer {
const { scope, path } = binding;
const parentDeclar = path.find(
path => path.isDeclaration() || path.isFunctionExpression(),
path =>
path.isDeclaration() ||
path.isFunctionExpression() ||
path.isClassExpression(),
);
if (parentDeclar) {
this.maybeConvertFromExportDeclaration(parentDeclar);