UNPKG

2.97 kBPlain TextView Raw
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
15import dyanamicImportSyntax from '@babel/plugin-syntax-dynamic-import';
16import {NodePath} from '@babel/traverse';
17import {CallExpression, Identifier, Program, Statement} from 'babel-types';
18import template from '@babel/template';
19
20const ast = template.ast;
21
22/**
23 * Rewrites dynamic import() calls to AMD async require() calls.
24 */
25export 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};