Create @babel/plugin-proposal-dynamic-import (#9552)

* Create @babel/plugin-proposal-dynamic-import

* Use airbnb/babel-plugin-dynamic-import-node

Do not duplicate code, which will unavoidably lead
to bugs being fixed in one plugin and not in the other.

* Update error message

* Add error callback to amd interop

* Update babel-plugin-dynamic-import-node
This commit is contained in:
Nicolò Ribaudo 2019-06-30 13:21:46 +02:00 committed by GitHub
parent a4170b5e32
commit 38f8bbac1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 406 additions and 45 deletions

View File

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

View File

@ -0,0 +1,19 @@
# @babel/plugin-proposal-dynamic-import
> Transform import() expressions
See our website [@babel/plugin-proposal-dynamic-import](https://babeljs.io/docs/en/next/babel-plugin-proposal-dynamic-import.html) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/plugin-proposal-dynamic-import
```
or using yarn:
```sh
yarn add @babel/plugin-proposal-dynamic-import --dev
```

View File

@ -0,0 +1,25 @@
{
"name": "@babel/plugin-proposal-dynamic-import",
"version": "7.4.0",
"description": "Transform import() expressions",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-dynamic-import",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
"@babel/core": "^7.2.0",
"@babel/helper-plugin-test-runner": "^7.0.0"
}
}

View File

@ -0,0 +1,41 @@
import { declare } from "@babel/helper-plugin-utils";
import syntaxDynamicImport from "@babel/plugin-syntax-dynamic-import";
import { version } from "../package.json";
const SUPPORTED_MODULES = ["commonjs", "amd", "systemjs"];
const MODULES_NOT_FOUND = `\
@babel/plugin-proposal-dynamic-import depends on a modules
transform plugin. Supported plugins are:
- @babel/plugin-transform-modules-commonjs ^7.4.0
- @babel/plugin-transform-modules-amd ^7.4.0
- @babel/plugin-transform-modules-systemjs ^7.4.0
If you are using Webpack or Rollup and thus don't want
Babel to transpile your imports and exports, you can use
the @babel/plugin-syntax-dynamic-import plugin and let your
bundler handle dynamic imports.
`;
export default declare(api => {
api.assertVersion(7);
return {
name: "proposal-dynamic-import",
inherits: syntaxDynamicImport,
pre() {
this.file.set("@babel/plugin-proposal-dynamic-import", version);
},
visitor: {
Program() {
const modules = this.file.get("@babel/plugin-transform-modules-*");
if (!SUPPORTED_MODULES.includes(modules)) {
throw new Error(MODULES_NOT_FOUND);
}
},
},
};
});

View File

@ -0,0 +1,3 @@
import "a";
import("b");

View File

@ -0,0 +1,7 @@
{
"plugins": [
"syntax-dynamic-import",
"transform-modules-amd",
"external-helpers"
]
}

View File

@ -0,0 +1,5 @@
define(["a"], function (_a) {
"use strict";
import("b");
});

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,5 @@
define(["require"], function (_require) {
"use strict";
var modP = new Promise((_resolve, _reject) => _require(["mod"], imported => _resolve(babelHelpers.interopRequireWildcard(imported)), _reject));
});

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,7 @@
{
"plugins": [
"proposal-dynamic-import",
["transform-modules-amd", { "noInterop": true }],
"external-helpers"
]
}

View File

@ -0,0 +1,3 @@
define(["require"], function (_require) {
var modP = new Promise((_resolve, _reject) => _require(["mod"], imported => _resolve(imported), _reject));
});

View File

@ -0,0 +1,7 @@
{
"plugins": [
"proposal-dynamic-import",
"transform-modules-amd",
"external-helpers"
]
}

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,3 @@
define(["require"], function (_require) {
var modP = new Promise((_resolve, _reject) => _require(["mod"], imported => _resolve(babelHelpers.interopRequireWildcard(imported)), _reject));
});

View File

@ -0,0 +1,8 @@
var mod;
import foo from "foo";
import("mod").then(m => mod = m);
export { mod };

View File

@ -0,0 +1,12 @@
define(["require", "exports", "foo"], function (_require, _exports, _foo) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.mod = void 0;
_foo = babelHelpers.interopRequireDefault(_foo);
var mod;
_exports.mod = mod;
new Promise((_resolve, _reject) => _require(["mod"], imported => _resolve(babelHelpers.interopRequireWildcard(imported)), _reject)).then(m => _exports.mod = mod = m);
});

View File

@ -0,0 +1,4 @@
return import("./mod.js").then(({ default: def, named }) => {
expect(def()).toBe("foo");
expect(named()).toBe("bar");
});

View File

@ -0,0 +1,2 @@
module.exports = () => "foo";
module.exports.named = () => "bar";

View File

@ -0,0 +1,5 @@
{
"parserOpts": {
"allowReturnOutsideFunction": true
}
}

View File

@ -0,0 +1,4 @@
return import("./mod.js").then(({ default: def, named }) => {
expect(def()).toBe("foo");
expect(named()).toBe("bar");
});

View File

@ -0,0 +1,5 @@
module.exports = {
__esModule: true,
default: () => "foo",
named: () => "bar",
};

View File

@ -0,0 +1,5 @@
{
"parserOpts": {
"allowReturnOutsideFunction": true
}
}

View File

@ -0,0 +1,3 @@
import "a";
import("b");

View File

@ -0,0 +1,7 @@
{
"plugins": [
"syntax-dynamic-import",
"transform-modules-commonjs",
"external-helpers"
]
}

View File

@ -0,0 +1,5 @@
"use strict";
require("a");
import("b");

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,3 @@
"use strict";
var modP = Promise.resolve().then(() => babelHelpers.interopRequireWildcard(require("mod")));

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,7 @@
{
"plugins": [
"proposal-dynamic-import",
["transform-modules-commonjs", { "noInterop": true }],
"external-helpers"
]
}

View File

@ -0,0 +1 @@
var modP = Promise.resolve().then(() => require("mod"));

View File

@ -0,0 +1,7 @@
{
"plugins": [
"proposal-dynamic-import",
"transform-modules-commonjs",
"external-helpers"
]
}

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1 @@
var modP = Promise.resolve().then(() => babelHelpers.interopRequireWildcard(require("mod")));

View File

@ -0,0 +1,6 @@
var require = "foo";
(async function () {
var require = "bar";
await import("./mod");
})();

View File

@ -0,0 +1,6 @@
var _require2 = "foo";
(async function () {
var _require = "bar";
await Promise.resolve().then(() => babelHelpers.interopRequireWildcard(require("./mod")));
})();

View File

@ -0,0 +1 @@
import(2);

View File

@ -0,0 +1 @@
Promise.resolve().then(() => babelHelpers.interopRequireWildcard(require(`${2}`)));

View File

@ -0,0 +1,4 @@
{
"plugins": ["proposal-dynamic-import"],
"throws": "@babel/plugin-proposal-dynamic-import depends on a modules\ntransform plugin. Supported plugins are:"
}

View File

@ -0,0 +1,5 @@
// TODO: This should throw in Babel 8
import "a";
import("b");

View File

@ -0,0 +1,7 @@
{
"plugins": [
"syntax-dynamic-import",
"transform-modules-systemjs",
"external-helpers"
]
}

View File

@ -0,0 +1,11 @@
System.register(["a"], function (_export, _context) {
"use strict";
return {
setters: [function (_a) {}],
execute: function () {
// TODO: This should throw in Babel 8
_context.import("b");
}
};
});

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,11 @@
System.register([], function (_export, _context) {
"use strict";
var modP;
return {
setters: [],
execute: function () {
modP = _context.import("mod");
}
};
});

View File

@ -0,0 +1,7 @@
{
"plugins": [
"proposal-dynamic-import",
"transform-modules-systemjs",
"external-helpers"
]
}

View File

@ -0,0 +1 @@
var modP = import("mod");

View File

@ -0,0 +1,11 @@
System.register([], function (_export, _context) {
"use strict";
var modP;
return {
setters: [],
execute: function () {
modP = _context.import("mod");
}
};
});

View File

@ -0,0 +1,3 @@
import runner from "@babel/helper-plugin-test-runner";
runner(__dirname);

View File

@ -10,7 +10,8 @@
"main": "lib/index.js",
"dependencies": {
"@babel/helper-module-transforms": "^7.1.0",
"@babel/helper-plugin-utils": "^7.0.0"
"@babel/helper-plugin-utils": "^7.0.0",
"babel-plugin-dynamic-import-node": "^2.3.0"
},
"keywords": [
"babel-plugin"

View File

@ -9,12 +9,31 @@ import {
wrapInterop,
} from "@babel/helper-module-transforms";
import { template, types as t } from "@babel/core";
import { getImportSource } from "babel-plugin-dynamic-import-node/utils";
const buildWrapper = template(`
define(MODULE_NAME, AMD_ARGUMENTS, function(IMPORT_NAMES) {
})
`);
const buildAnonymousWrapper = template(`
define(["require"], function(REQUIRE) {
})
`);
function injectWrapper(path, wrapper) {
const { body, directives } = path.node;
path.node.directives = [];
path.node.body = [];
const amdWrapper = path.pushContainer("body", wrapper)[0];
const amdFactory = amdWrapper
.get("expression.arguments")
.filter(arg => arg.isFunctionExpression())[0]
.get("body");
amdFactory.pushContainer("directives", directives);
amdFactory.pushContainer("body", body);
}
export default declare((api, options) => {
api.assertVersion(7);
@ -22,10 +41,60 @@ export default declare((api, options) => {
return {
name: "transform-modules-amd",
pre() {
this.file.set("@babel/plugin-transform-modules-*", "amd");
},
visitor: {
CallExpression(path, state) {
if (!this.file.has("@babel/plugin-proposal-dynamic-import")) return;
if (!path.get("callee").isImport()) return;
let { requireId, resolveId, rejectId } = state;
if (!requireId) {
requireId = path.scope.generateUidIdentifier("require");
state.requireId = requireId;
}
if (!resolveId || !rejectId) {
resolveId = path.scope.generateUidIdentifier("resolve");
rejectId = path.scope.generateUidIdentifier("reject");
state.resolveId = resolveId;
state.rejectId = rejectId;
}
let result = t.identifier("imported");
if (!noInterop) result = wrapInterop(path, result, "namespace");
path.replaceWith(
template.expression.ast`
new Promise((${resolveId}, ${rejectId}) =>
${requireId}(
[${getImportSource(t, path.node)}],
imported => ${resolveId}(${result}),
${rejectId}
)
)`,
);
},
Program: {
exit(path) {
if (!isModule(path)) return;
exit(path, { requireId }) {
if (!isModule(path)) {
if (requireId) {
injectWrapper(
path,
buildAnonymousWrapper({ REQUIRE: requireId }),
);
}
return;
}
const amdArgs = [];
const importNames = [];
if (requireId) {
amdArgs.push(t.stringLiteral("require"));
importNames.push(requireId);
}
let moduleName = this.getModuleName();
if (moduleName) moduleName = t.stringLiteral(moduleName);
@ -41,9 +110,6 @@ export default declare((api, options) => {
},
);
const amdArgs = [];
const importNames = [];
if (hasExports(meta)) {
amdArgs.push(t.stringLiteral("exports"));
@ -81,23 +147,15 @@ export default declare((api, options) => {
ensureStatementsHoisted(headers);
path.unshiftContainer("body", headers);
const { body, directives } = path.node;
path.node.directives = [];
path.node.body = [];
const amdWrapper = path.pushContainer("body", [
injectWrapper(
path,
buildWrapper({
MODULE_NAME: moduleName,
AMD_ARGUMENTS: t.arrayExpression(amdArgs),
IMPORT_NAMES: importNames,
}),
])[0];
const amdFactory = amdWrapper
.get("expression.arguments")
.filter(arg => arg.isFunctionExpression())[0]
.get("body");
amdFactory.pushContainer("directives", directives);
amdFactory.pushContainer("body", body);
);
},
},
},

View File

@ -11,7 +11,8 @@
"dependencies": {
"@babel/helper-module-transforms": "^7.4.4",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-simple-access": "^7.1.0"
"@babel/helper-simple-access": "^7.1.0",
"babel-plugin-dynamic-import-node": "^2.3.0"
},
"keywords": [
"babel-plugin"

View File

@ -10,9 +10,13 @@ import {
import simplifyAccess from "@babel/helper-simple-access";
import { template, types as t } from "@babel/core";
import { createDynamicImportTransform } from "babel-plugin-dynamic-import-node/utils";
export default declare((api, options) => {
api.assertVersion(7);
const transformImportCall = createDynamicImportTransform(api);
const {
loose,
@ -118,7 +122,23 @@ export default declare((api, options) => {
return {
name: "transform-modules-commonjs",
pre() {
this.file.set("@babel/plugin-transform-modules-*", "commonjs");
},
visitor: {
CallExpression(path) {
if (!this.file.has("@babel/plugin-proposal-dynamic-import")) return;
if (!path.get("callee").isImport()) return;
let { scope } = path;
do {
scope.rename("require");
} while ((scope = scope.parent));
transformImportCall(this, path.get("callee"));
},
Program: {
exit(path, state) {
if (!isModule(path)) return;

View File

@ -23,6 +23,7 @@ var tests = [
tests.forEach(function (code) {
var res = transform(code, {
configFile: false,
sourceMap: true,
plugins: opts.plugins
});

View File

@ -10,7 +10,8 @@
"main": "lib/index.js",
"dependencies": {
"@babel/helper-hoist-variables": "^7.4.4",
"@babel/helper-plugin-utils": "^7.0.0"
"@babel/helper-plugin-utils": "^7.0.0",
"babel-plugin-dynamic-import-node": "^2.3.0"
},
"keywords": [
"babel-plugin"

View File

@ -1,6 +1,7 @@
import { declare } from "@babel/helper-plugin-utils";
import hoistVariables from "@babel/helper-hoist-variables";
import { template, types as t } from "@babel/core";
import { getImportSource } from "babel-plugin-dynamic-import-node/utils";
const buildTemplate = template(`
SYSTEM_REGISTER(MODULE_NAME, SOURCES, function (EXPORT_IDENTIFIER, CONTEXT_IDENTIFIER) {
@ -21,6 +22,12 @@ const buildExportAll = template(`
}
`);
const MISSING_PLUGIN_WARNING = `\
WARNING: Dynamic import() transformation must be enabled using the
@babel/plugin-proposal-dynamic-import plugin. Babel 8 will
no longer transform import() without using that plugin.
`;
function constructExportCall(
path,
exportIdent,
@ -96,8 +103,6 @@ function constructExportCall(
return statements;
}
const TYPE_IMPORT = "Import";
export default declare((api, options) => {
api.assertVersion(7);
@ -168,16 +173,24 @@ export default declare((api, options) => {
return {
name: "transform-modules-systemjs",
pre() {
this.file.set("@babel/plugin-transform-modules-*", "systemjs");
},
visitor: {
CallExpression(path, state) {
if (path.node.callee.type === TYPE_IMPORT) {
if (t.isImport(path.node.callee)) {
if (!this.file.has("@babel/plugin-proposal-dynamic-import")) {
console.warn(MISSING_PLUGIN_WARNING);
}
path.replaceWith(
t.callExpression(
t.memberExpression(
t.identifier(state.contextIdent),
t.identifier("import"),
),
path.node.arguments,
[getImportSource(t, path.node)],
),
);
}

View File

@ -1,6 +0,0 @@
export function lazyLoadOperation () {
return import('./x')
.then(function (x) {
x.y();
});
}

View File

@ -1,16 +0,0 @@
System.register([], function (_export, _context) {
"use strict";
function lazyLoadOperation() {
return _context.import('./x').then(function (x) {
x.y();
});
}
_export("lazyLoadOperation", lazyLoadOperation);
return {
setters: [],
execute: function () {}
};
});