1 | "use strict";
|
2 |
|
3 | const types = require('@babel/types');
|
4 |
|
5 | const template = require('@babel/template').default;
|
6 |
|
7 | const traverse = require('@babel/traverse').default;
|
8 |
|
9 | const urlJoin = require('../utils/urlJoin');
|
10 |
|
11 | const isURL = require('../utils/is-url');
|
12 |
|
13 | const nodeBuiltins = require('node-libs-browser');
|
14 |
|
15 | const requireTemplate = template('require("_bundle_loader")');
|
16 | const argTemplate = template('require.resolve(MODULE)');
|
17 | const serviceWorkerPattern = ['navigator', 'serviceWorker', 'register'];
|
18 | module.exports = {
|
19 | ImportDeclaration(node, asset) {
|
20 | asset.isES6Module = true;
|
21 | addDependency(asset, node.source);
|
22 | },
|
23 |
|
24 | ExportNamedDeclaration(node, asset) {
|
25 | asset.isES6Module = true;
|
26 |
|
27 | if (node.source) {
|
28 | addDependency(asset, node.source);
|
29 | }
|
30 | },
|
31 |
|
32 | ExportAllDeclaration(node, asset) {
|
33 | asset.isES6Module = true;
|
34 | addDependency(asset, node.source);
|
35 | },
|
36 |
|
37 | ExportDefaultDeclaration(node, asset) {
|
38 | asset.isES6Module = true;
|
39 | },
|
40 |
|
41 | CallExpression(node, asset, ancestors) {
|
42 | let callee = node.callee,
|
43 | args = node.arguments;
|
44 | let isRequire = types.isIdentifier(callee) && callee.name === 'require' && args.length === 1 && types.isStringLiteral(args[0]) && !hasBinding(ancestors, 'require') && !isInFalsyBranch(ancestors);
|
45 |
|
46 | if (isRequire) {
|
47 | let optional = ancestors.some(a => types.isTryStatement(a)) || undefined;
|
48 | addDependency(asset, args[0], {
|
49 | optional
|
50 | });
|
51 | return;
|
52 | }
|
53 |
|
54 | let isDynamicImport = callee.type === 'Import' && args.length === 1 && types.isStringLiteral(args[0]);
|
55 |
|
56 | if (isDynamicImport) {
|
57 | if (isURL(args[0].value)) return;
|
58 | asset.addDependency('_bundle_loader');
|
59 | addDependency(asset, args[0], {
|
60 | dynamic: true
|
61 | });
|
62 | node.callee = requireTemplate().expression;
|
63 | node.arguments[0] = argTemplate({
|
64 | MODULE: args[0]
|
65 | }).expression;
|
66 | asset.isAstDirty = true;
|
67 | return;
|
68 | }
|
69 |
|
70 | const isRegisterServiceWorker = types.isStringLiteral(args[0]) && types.matchesPattern(callee, serviceWorkerPattern);
|
71 |
|
72 | if (isRegisterServiceWorker) {
|
73 |
|
74 |
|
75 | addURLDependency(asset, args[0], {
|
76 | entry: true,
|
77 | isolated: true
|
78 | });
|
79 | return;
|
80 | }
|
81 | },
|
82 |
|
83 | NewExpression(node, asset) {
|
84 | const callee = node.callee,
|
85 | args = node.arguments;
|
86 | const isWebWorker = callee.type === 'Identifier' && (callee.name === 'Worker' || callee.name === 'SharedWorker') && args.length === 1 && types.isStringLiteral(args[0]);
|
87 |
|
88 | if (isWebWorker) {
|
89 | addURLDependency(asset, args[0], {
|
90 | isolated: true
|
91 | });
|
92 | return;
|
93 | }
|
94 | }
|
95 |
|
96 | };
|
97 |
|
98 | function hasBinding(node, name) {
|
99 | if (Array.isArray(node)) {
|
100 | return node.some(ancestor => hasBinding(ancestor, name));
|
101 | } else if (types.isProgram(node) || types.isBlockStatement(node) || types.isBlock(node)) {
|
102 | return node.body.some(statement => hasBinding(statement, name));
|
103 | } else if (types.isFunctionDeclaration(node) || types.isFunctionExpression(node) || types.isArrowFunctionExpression(node)) {
|
104 | return node.id && node.id.name === name || node.params.some(param => types.isIdentifier(param) && param.name === name);
|
105 | } else if (types.isVariableDeclaration(node)) {
|
106 | return node.declarations.some(declaration => declaration.id.name === name);
|
107 | }
|
108 |
|
109 | return false;
|
110 | }
|
111 |
|
112 | function isInFalsyBranch(ancestors) {
|
113 |
|
114 | return ancestors.some((node, index) => {
|
115 | if (types.isIfStatement(node)) {
|
116 | let res = evaluateExpression(node.test);
|
117 |
|
118 | if (res && res.confident) {
|
119 |
|
120 |
|
121 | let child = ancestors[index + 1];
|
122 | return res.value ? child === node.alternate : child === node.consequent;
|
123 | }
|
124 | }
|
125 | });
|
126 | }
|
127 |
|
128 | function evaluateExpression(node) {
|
129 |
|
130 | node = types.file(types.program([types.expressionStatement(node)]));
|
131 |
|
132 | let res = null;
|
133 | traverse(node, {
|
134 | Expression(path) {
|
135 | res = path.evaluate();
|
136 | path.stop();
|
137 | }
|
138 |
|
139 | });
|
140 | return res;
|
141 | }
|
142 |
|
143 | function addDependency(asset, node, opts = {}) {
|
144 |
|
145 | if (asset.options.target === 'node' && node.value in nodeBuiltins) {
|
146 | return;
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 | let inlineHTML = asset.options.rendition && asset.options.rendition.inlineHTML;
|
152 |
|
153 | if (inlineHTML) {
|
154 | let err = new Error('Imports and requires are not supported inside inline <script> tags yet.');
|
155 | err.loc = node.loc && node.loc.start;
|
156 | throw err;
|
157 | }
|
158 |
|
159 | if (!asset.options.bundleNodeModules) {
|
160 | const isRelativeImport = /^[/~.]/.test(node.value);
|
161 | if (!isRelativeImport) return;
|
162 | }
|
163 |
|
164 | opts.loc = node.loc && node.loc.start;
|
165 | asset.addDependency(node.value, opts);
|
166 | }
|
167 |
|
168 | function addURLDependency(asset, node, opts = {}) {
|
169 | opts.loc = node.loc && node.loc.start;
|
170 | let assetPath = asset.addURLDependency(node.value, opts);
|
171 |
|
172 | if (!isURL(assetPath)) {
|
173 | assetPath = urlJoin(asset.options.publicURL, assetPath);
|
174 | }
|
175 |
|
176 | node.value = assetPath;
|
177 | asset.isAstDirty = true;
|
178 | } |
\ | No newline at end of file |