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": { "dependencies": {
"@babel/helper-module-imports": "7.0.0-beta.40", "@babel/helper-module-imports": "7.0.0-beta.40",
"@babel/helper-simple-access": "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/template": "7.0.0-beta.40",
"@babel/types": "7.0.0-beta.40", "@babel/types": "7.0.0-beta.40",
"lodash": "^4.2.0" "lodash": "^4.2.0"

View File

@ -1,6 +1,6 @@
import { basename, extname } from "path"; import { basename, extname } from "path";
import * as t from "@babel/types"; import splitExportDeclaration from "@babel/helper-split-export-declaration";
export type ModuleMetadata = { export type ModuleMetadata = {
exportName: string, exportName: string,
@ -399,35 +399,7 @@ function nameAnonymousExports(programPath: NodePath) {
// Name anonymous exported locals. // Name anonymous exported locals.
programPath.get("body").forEach(child => { programPath.get("body").forEach(child => {
if (!child.isExportDefaultDeclaration()) return; if (!child.isExportDefaultDeclaration()) return;
splitExportDeclaration(child);
// 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]);
}
}); });
} }

View File

@ -112,6 +112,7 @@ const rewriteBindingInitVisitor = {
Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => { Object.keys(path.getOuterBindingIdentifiers()).forEach(localName => {
const exportNames = exported.get(localName) || []; const exportNames = exported.get(localName) || [];
if (exportNames.length > 0) { if (exportNames.length > 0) {
const statement = t.expressionStatement( const statement = t.expressionStatement(
buildBindingExportAssignmentExpression( 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 value: true
}), _temp)); }), _temp));
var _class2 = function _class2() { var _default = function _default() {
babelHelpers.classCallCheck(this, _class2); babelHelpers.classCallCheck(this, _default);
}; };
Object.defineProperty(_class2, "test", { Object.defineProperty(_default, "test", {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
writable: true, writable: true,
value: 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); babelHelpers.classCallCheck(this, _class);
}, _class.test = true, _temp)); }, _class.test = true, _temp));
var _class2 = function _class2() { var _default = function _default() {
babelHelpers.classCallCheck(this, _class2); babelHelpers.classCallCheck(this, _default);
}; };
_class2.test = true; _default.test = true;
export { _class2 as default }; export { _default as default };
; ;

View File

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

View File

@ -2,6 +2,7 @@ import LooseTransformer from "./loose";
import VanillaTransformer from "./vanilla"; import VanillaTransformer from "./vanilla";
import annotateAsPure from "@babel/helper-annotate-as-pure"; import annotateAsPure from "@babel/helper-annotate-as-pure";
import nameFunction from "@babel/helper-function-name"; import nameFunction from "@babel/helper-function-name";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
import { types as t } from "@babel/core"; import { types as t } from "@babel/core";
import globals from "globals"; import globals from "globals";
@ -24,19 +25,7 @@ export default function(api, options) {
visitor: { visitor: {
ExportDefaultDeclaration(path) { ExportDefaultDeclaration(path) {
if (!path.get("declaration").isClassDeclaration()) return; if (!path.get("declaration").isClassDeclaration()) return;
splitExportDeclaration(path);
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")),
]),
);
}, },
ClassDeclaration(path) { ClassDeclaration(path) {

View File

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

View File

@ -1,14 +1,14 @@
var _class = var _default =
/*#__PURE__*/ /*#__PURE__*/
function (_A) { function (_A) {
babelHelpers.inherits(_class, _A); babelHelpers.inherits(_default, _A);
function _class() { function _default() {
babelHelpers.classCallCheck(this, _class); babelHelpers.classCallCheck(this, _default);
return babelHelpers.possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).apply(this, arguments)); return babelHelpers.possibleConstructorReturn(this, (_default.__proto__ || Object.getPrototypeOf(_default)).apply(this, arguments));
} }
return _class; return _default;
}(A); }(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", var _foo = "yes",
foob = "no"; foob = "no";
export { _foo as foo, foob }; export { _foo as foo, foob };
function _whatever() {} function _whatever() {}
export { _whatever as whatever };
function _wowzers() {} function _wowzers() {}
export { _wowzers as default };
var bar = { var bar = {
foo: function foo() { foo: function foo() {
_foo; _foo;

View File

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

View File

@ -11,6 +11,7 @@
"@babel/code-frame": "7.0.0-beta.40", "@babel/code-frame": "7.0.0-beta.40",
"@babel/generator": "7.0.0-beta.40", "@babel/generator": "7.0.0-beta.40",
"@babel/helper-function-name": "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", "@babel/types": "7.0.0-beta.40",
"babylon": "7.0.0-beta.40", "babylon": "7.0.0-beta.40",
"debug": "^3.0.1", "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. * Generate a unique identifier and add it to the current scope.
*/ */
generateDeclaredUidIdentifier(name: string = "temp") { generateDeclaredUidIdentifier(name?: string) {
const id = this.generateUidIdentifier(name); const id = this.generateUidIdentifier(name);
this.push({ id }); this.push({ id });
return t.cloneNode(id); return t.cloneNode(id);
@ -220,7 +220,7 @@ export default class Scope {
* Generate a unique identifier. * Generate a unique identifier.
*/ */
generateUidIdentifier(name: string = "temp") { generateUidIdentifier(name?: string) {
return t.identifier(this.generateUid(name)); return t.identifier(this.generateUid(name));
} }

View File

@ -1,4 +1,5 @@
import Binding from "../binding"; import Binding from "../binding";
import splitExportDeclaration from "@babel/helper-split-export-declaration";
import * as t from "@babel/types"; import * as t from "@babel/types";
const renameVisitor = { const renameVisitor = {
@ -40,48 +41,20 @@ export default class Renamer {
binding: Binding; binding: Binding;
maybeConvertFromExportDeclaration(parentDeclar) { maybeConvertFromExportDeclaration(parentDeclar) {
const exportDeclar = const maybeExportDeclar = parentDeclar.parentPath;
parentDeclar.parentPath.isExportDeclaration() && parentDeclar.parentPath;
if (!exportDeclar) return;
// build specifiers that point back to this export declaration if (!maybeExportDeclar.isExportDeclaration()) {
const isDefault = exportDeclar.isExportDefaultDeclaration(); return;
}
if ( if (
isDefault && maybeExportDeclar.isExportDefaultDeclaration() &&
(parentDeclar.isFunctionDeclaration() || !maybeExportDeclar.get("declaration").node.id
parentDeclar.isClassDeclaration()) &&
!parentDeclar.node.id
) { ) {
// Ensure that default class and function exports have a name so they have a identifier to return;
// reference from the export specifier list.
parentDeclar.node.id = parentDeclar.scope.generateUidIdentifier(
"default",
);
} }
const bindingIdentifiers = parentDeclar.getOuterBindingIdentifiers(); splitExportDeclaration(maybeExportDeclar);
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);
}
} }
maybeConvertFromClassFunctionDeclaration(path) { maybeConvertFromClassFunctionDeclaration(path) {
@ -129,7 +102,10 @@ export default class Renamer {
const { scope, path } = binding; const { scope, path } = binding;
const parentDeclar = path.find( const parentDeclar = path.find(
path => path.isDeclaration() || path.isFunctionExpression(), path =>
path.isDeclaration() ||
path.isFunctionExpression() ||
path.isClassExpression(),
); );
if (parentDeclar) { if (parentDeclar) {
this.maybeConvertFromExportDeclaration(parentDeclar); this.maybeConvertFromExportDeclaration(parentDeclar);