1 | /**
|
2 | * @license
|
3 | * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
4 | * This code may only be used under the BSD style license found at
|
5 | * http://polymer.github.io/LICENSE.txt
|
6 | * The complete set of authors may be found at
|
7 | * http://polymer.github.io/AUTHORS.txt
|
8 | * The complete set of contributors may be found at
|
9 | * http://polymer.github.io/CONTRIBUTORS.txt
|
10 | * Code distributed by Google as part of the polymer project is also
|
11 | * subject to an additional IP rights grant found at
|
12 | * http://polymer.github.io/PATENTS.txt
|
13 | */
|
14 |
|
15 | import dyanamicImportSyntax from '@babel/plugin-syntax-dynamic-import';
|
16 | import {NodePath} from '@babel/traverse';
|
17 | import {CallExpression, Identifier, Program, Statement} from 'babel-types';
|
18 | import template from '@babel/template';
|
19 |
|
20 | const ast = template.ast;
|
21 |
|
22 | /**
|
23 | * Rewrites dynamic import() calls to AMD async require() calls.
|
24 | */
|
25 | export const dynamicImportAmd = {
|
26 | inherits: dyanamicImportSyntax,
|
27 |
|
28 | visitor: {
|
29 | Program(path: NodePath<Program>) {
|
30 | // We transform dynamic import() into a Promise whose initializer calls
|
31 | // require(). We must use the "local" require - the one provided by the
|
32 | // AMD loaded when a module explicitly depends on the "require" module ID.
|
33 | // To get the emitted define() call to depend on "require", we inject an
|
34 | // import of a module called "require".
|
35 |
|
36 | // Collect all the NodePaths to dynamic import() expressions
|
37 | // and all the identifiers in scope for them.
|
38 | const identifiers = new Set<string>();
|
39 | const dynamicImports: NodePath<CallExpression>[] = [];
|
40 | path.traverse({
|
41 | CallExpression(path: NodePath<CallExpression>) {
|
42 | if (path.node.callee.type as string === 'Import') {
|
43 | dynamicImports.push(path);
|
44 | const bindings = path.scope.getAllBindings();
|
45 | for (const name of Object.keys(bindings)) {
|
46 | identifiers.add(name);
|
47 | }
|
48 | }
|
49 | }
|
50 | });
|
51 |
|
52 | if (dynamicImports.length === 0) {
|
53 | return;
|
54 | }
|
55 |
|
56 | // Choose a unique name to import "require" as.
|
57 | let requireId: Identifier|undefined = undefined;
|
58 | do {
|
59 | requireId = path.scope.generateUidIdentifier('require');
|
60 | } while (identifiers.has(requireId.name));
|
61 |
|
62 | // Inject the import of "require"
|
63 | const statements = path.node.body as Statement[];
|
64 | statements.unshift(ast`import * as ${requireId} from 'require';`);
|
65 |
|
66 | // Transform the dynamic import callsites
|
67 | for (const importPath of dynamicImports) {
|
68 | const specifier = importPath.node.arguments[0];
|
69 | // Call as `require.default` because the AMD transformer that we assume
|
70 | // is running next will rewrite `require` from a function to a module
|
71 | // object with the function at `default`.
|
72 | importPath.replaceWith(ast`(
|
73 | new Promise((res, rej) => ${requireId}.default([${
|
74 | specifier}], res, rej))
|
75 | )`);
|
76 | }
|
77 | },
|
78 | },
|
79 | };
|