From 0b7a97f016f48db7316363b0657d8207592dbe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 22 Sep 2020 16:55:26 -0400 Subject: [PATCH] feat: support systemjs --- .../package.json | 1 + .../src/index.js | 88 +++++++++++++++---- .../systemjs/export-from-string/input.mjs | 1 + .../systemjs/export-from-string/output.mjs | 10 +++ .../input.mjs | 2 + .../output.mjs | 15 ++++ .../systemjs/export-named-string/input.mjs | 2 + .../systemjs/export-named-string/output.mjs | 15 ++++ .../input.mjs | 3 + .../output.mjs | 13 +++ .../systemjs/imports-named-string/input.mjs | 3 + .../systemjs/imports-named-string/output.mjs | 13 +++ yarn.lock | 1 + 13 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/input.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/output.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/input.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/output.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/input.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/output.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/input.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/output.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/input.mjs create mode 100644 packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/output.mjs diff --git a/packages/babel-plugin-transform-modules-systemjs/package.json b/packages/babel-plugin-transform-modules-systemjs/package.json index 0149d44733e2..a26f6b10078c 100644 --- a/packages/babel-plugin-transform-modules-systemjs/package.json +++ b/packages/babel-plugin-transform-modules-systemjs/package.json @@ -16,6 +16,7 @@ "@babel/helper-hoist-variables": "workspace:^7.10.4", "@babel/helper-module-transforms": "workspace:^7.10.5", "@babel/helper-plugin-utils": "workspace:^7.10.4", + "@babel/helper-validator-identifier": "workspace:^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" }, "keywords": [ diff --git a/packages/babel-plugin-transform-modules-systemjs/src/index.js b/packages/babel-plugin-transform-modules-systemjs/src/index.js index 56e9001428b1..607fd01005c9 100644 --- a/packages/babel-plugin-transform-modules-systemjs/src/index.js +++ b/packages/babel-plugin-transform-modules-systemjs/src/index.js @@ -3,6 +3,7 @@ import hoistVariables from "@babel/helper-hoist-variables"; import { template, types as t } from "@babel/core"; import { getImportSource } from "babel-plugin-dynamic-import-node/utils"; import { rewriteThis, getModuleName } from "@babel/helper-module-transforms"; +import { isIdentifierName } from "@babel/helper-validator-identifier"; const buildTemplate = template(` SYSTEM_REGISTER(MODULE_NAME, SOURCES, function (EXPORT_IDENTIFIER, CONTEXT_IDENTIFIER) { @@ -29,12 +30,50 @@ WARNING: Dynamic import() transformation must be enabled using the no longer transform import() without using that plugin. `; +//todo: use getExportSpecifierName in `helper-module-transforms` when this library is refactored to NodePath usage. + +export function getExportSpecifierName( + node: Node, + stringSpecifiers: Set, +): string { + if (node.type === "Identifier") { + return node.name; + } else if (node.type === "StringLiteral") { + const stringValue = node.value; + // add specifier value to `stringSpecifiers` only when it can not be converted to an identifier name + // i.e In `import { "foo" as bar }` + // we do not consider `"foo"` to be a `stringSpecifier` because we can treat it as + // `import { foo as bar }` + // This helps minimize the size of `stringSpecifiers` and reduce overhead of checking valid identifier names + // when building transpiled code from metadata + if (!isIdentifierName(stringValue)) { + stringSpecifiers.add(stringValue); + } + return stringValue; + } else { + throw new Error( + `Expected export specifier to be either Identifier or StringLiteral, got ${node.type}`, + ); + } +} + +type PluginState = {| + contextIdent: string, + + // List of names that should only be printed as string literals. + // i.e. `import { "any unicode" as foo } from "some-module"` + // `stringSpecifiers` is Set(1) ["any unicode"] + // In most cases `stringSpecifiers` is an empty Set + stringSpecifiers: Set, +|}; + function constructExportCall( path, exportIdent, exportNames, exportValues, exportStarTarget, + stringSpecifiers: Set, ) { const statements = []; if (exportNames.length === 1) { @@ -52,7 +91,12 @@ function constructExportCall( const exportName = exportNames[i]; const exportValue = exportValues[i]; objectProperties.push( - t.objectProperty(t.identifier(exportName), exportValue), + t.objectProperty( + stringSpecifiers.has(exportName) + ? t.stringLiteral(exportName) + : t.identifier(exportName), + exportValue, + ), ); } statements.push( @@ -179,7 +223,7 @@ export default declare((api, options) => { }, visitor: { - CallExpression(path, state) { + CallExpression(path, state: PluginState) { if (t.isImport(path.node.callee)) { if (!this.file.has("@babel/plugin-proposal-dynamic-import")) { console.warn(MISSING_PLUGIN_WARNING); @@ -197,7 +241,7 @@ export default declare((api, options) => { } }, - MetaProperty(path, state) { + MetaProperty(path, state: PluginState) { if ( path.node.meta.name === "import" && path.node.property.name === "meta" @@ -228,14 +272,15 @@ export default declare((api, options) => { Program: { enter(path, state) { state.contextIdent = path.scope.generateUid("context"); + state.stringSpecifiers = new Set(); if (!allowTopLevelThis) { rewriteThis(path); } }, - exit(path, state) { + exit(path, state: PluginState) { const scope = path.scope; const exportIdent = scope.generateUid("export"); - const contextIdent = state.contextIdent; + const { contextIdent, stringSpecifiers } = state; const exportMap = Object.create(null); const modules = []; @@ -389,28 +434,25 @@ export default declare((api, options) => { const nodes = []; for (const specifier of specifiers) { - const binding = scope.getBinding(specifier.local.name); + const { local, exported } = specifier; + const binding = scope.getBinding(local.name); + const exportedName = getExportSpecifierName( + exported, + stringSpecifiers, + ); // hoisted function export if ( binding && t.isFunctionDeclaration(binding.path.node) ) { - exportNames.push(specifier.exported.name); - exportValues.push(t.cloneNode(specifier.local)); + exportNames.push(exportedName); + exportValues.push(t.cloneNode(local)); } // only globals also exported this way else if (!binding) { - nodes.push( - buildExportCall( - specifier.exported.name, - specifier.local, - ), - ); + nodes.push(buildExportCall(exportedName, local)); } - addExportName( - specifier.local.name, - specifier.exported.name, - ); + addExportName(local.name, exportedName); } path.replaceWithMultiple(nodes); @@ -445,6 +487,7 @@ export default declare((api, options) => { } if (t.isImportSpecifier(specifier)) { + const { imported } = specifier; setterBody.push( t.expressionStatement( t.assignmentExpression( @@ -453,6 +496,7 @@ export default declare((api, options) => { t.memberExpression( t.identifier(target), specifier.imported, + /* computed */ imported.type === "StringLiteral", ), ), ), @@ -469,7 +513,11 @@ export default declare((api, options) => { if (t.isExportAllDeclaration(node)) { hasExportStar = true; } else if (t.isExportSpecifier(node)) { - exportNames.push(node.exported.name); + const exportedName = getExportSpecifierName( + node.exported, + stringSpecifiers, + ); + exportNames.push(exportedName); exportValues.push( t.memberExpression(t.identifier(target), node.local), ); @@ -485,6 +533,7 @@ export default declare((api, options) => { exportNames, exportValues, hasExportStar ? t.identifier(target) : null, + stringSpecifiers, ), ); } @@ -533,6 +582,7 @@ export default declare((api, options) => { exportNames, exportValues, null, + stringSpecifiers, ), ); } diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/input.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/input.mjs new file mode 100644 index 000000000000..2dc9ce983e0f --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/input.mjs @@ -0,0 +1 @@ +export { foo as "some exports" } from "foo"; diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/output.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/output.mjs new file mode 100644 index 000000000000..3f7e3c9f4bad --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-from-string/output.mjs @@ -0,0 +1,10 @@ +System.register(["foo"], function (_export, _context) { + "use strict"; + + return { + setters: [function (_foo) { + _export("some exports", _foo.foo); + }], + execute: function () {} + }; +}); diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/input.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/input.mjs new file mode 100644 index 000000000000..ee4aa01e3988 --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/input.mjs @@ -0,0 +1,2 @@ +var foo, bar; +export {foo as "defaultExports", bar}; diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/output.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/output.mjs new file mode 100644 index 000000000000..bed82c28410d --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string-can-be-identifier/output.mjs @@ -0,0 +1,15 @@ +System.register([], function (_export, _context) { + "use strict"; + + var foo, bar; + + _export({ + foo: void 0, + bar: void 0 + }); + + return { + setters: [], + execute: function () {} + }; +}); diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/input.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/input.mjs new file mode 100644 index 000000000000..f36164d076f9 --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/input.mjs @@ -0,0 +1,2 @@ +var foo, bar; +export {foo as "default exports", bar}; diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/output.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/output.mjs new file mode 100644 index 000000000000..bed82c28410d --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/export-named-string/output.mjs @@ -0,0 +1,15 @@ +System.register([], function (_export, _context) { + "use strict"; + + var foo, bar; + + _export({ + foo: void 0, + bar: void 0 + }); + + return { + setters: [], + execute: function () {} + }; +}); diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/input.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/input.mjs new file mode 100644 index 000000000000..c6975c24a9a7 --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/input.mjs @@ -0,0 +1,3 @@ +import { "defaultImports" as bar} from "foo"; + +bar; diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/output.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/output.mjs new file mode 100644 index 000000000000..9e58fa1fca5b --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string-can-be-identifier/output.mjs @@ -0,0 +1,13 @@ +System.register(["foo"], function (_export, _context) { + "use strict"; + + var bar; + return { + setters: [function (_foo) { + bar = _foo["defaultImports"]; + }], + execute: function () { + bar; + } + }; +}); diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/input.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/input.mjs new file mode 100644 index 000000000000..97932c921132 --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/input.mjs @@ -0,0 +1,3 @@ +import {"default imports" as bar} from "foo"; + +bar; diff --git a/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/output.mjs b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/output.mjs new file mode 100644 index 000000000000..19d3adb757d5 --- /dev/null +++ b/packages/babel-plugin-transform-modules-systemjs/test/fixtures/systemjs/imports-named-string/output.mjs @@ -0,0 +1,13 @@ +System.register(["foo"], function (_export, _context) { + "use strict"; + + var bar; + return { + setters: [function (_foo) { + bar = _foo["default imports"]; + }], + execute: function () { + bar; + } + }; +}); diff --git a/yarn.lock b/yarn.lock index 79baffa2206b..fe9e8fe9ff1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2218,6 +2218,7 @@ __metadata: "@babel/helper-module-transforms": "workspace:^7.10.5" "@babel/helper-plugin-test-runner": "workspace:^7.10.4" "@babel/helper-plugin-utils": "workspace:^7.10.4" + "@babel/helper-validator-identifier": "workspace:^7.10.4" "@babel/plugin-syntax-dynamic-import": ^7.8.0 babel-plugin-dynamic-import-node: ^2.3.3 peerDependencies: