source: frontend/node_modules/webpack/lib/dependencies/ImportParserPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 19.9 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
9const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
10const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
11const {
12 VariableInfo,
13 getImportAttributes
14} = require("../javascript/JavascriptParser");
15const traverseDestructuringAssignmentProperties = require("../util/traverseDestructuringAssignmentProperties");
16const ContextDependencyHelpers = require("./ContextDependencyHelpers");
17const { getNonOptionalPart } = require("./HarmonyImportDependency");
18const ImportContextDependency = require("./ImportContextDependency");
19const ImportDependency = require("./ImportDependency");
20const ImportEagerDependency = require("./ImportEagerDependency");
21const { createGetImportPhase } = require("./ImportPhase");
22const ImportWeakDependency = require("./ImportWeakDependency");
23
24/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
25/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
26/** @typedef {import("../ContextModule").ContextMode} ContextMode */
27/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
28/** @typedef {import("../Dependency").RawReferencedExports} RawReferencedExports */
29/** @typedef {import("../Module").BuildMeta} BuildMeta */
30/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
31/** @typedef {import("../javascript/JavascriptParser").ImportExpression} ImportExpression */
32/** @typedef {import("../javascript/JavascriptParser").Range} Range */
33/** @typedef {import("../javascript/JavascriptParser").JavascriptParserState} JavascriptParserState */
34/** @typedef {import("../javascript/JavascriptParser").Members} Members */
35/** @typedef {import("../javascript/JavascriptParser").MembersOptionals} MembersOptionals */
36/** @typedef {import("../javascript/JavascriptParser").ArrowFunctionExpression} ArrowFunctionExpression */
37/** @typedef {import("../javascript/JavascriptParser").FunctionExpression} FunctionExpression */
38/** @typedef {import("../javascript/JavascriptParser").Identifier} Identifier */
39/** @typedef {import("../javascript/JavascriptParser").ObjectPattern} ObjectPattern */
40/** @typedef {import("../javascript/JavascriptParser").CallExpression} CallExpression */
41
42/** @typedef {{ references: RawReferencedExports, expression: ImportExpression }} ImportSettings */
43/** @typedef {WeakMap<ImportExpression, RawReferencedExports>} State */
44
45/** @type {WeakMap<JavascriptParserState, State>} */
46const parserStateMap = new WeakMap();
47const dynamicImportTag = Symbol("import()");
48
49/**
50 * Returns import parser plugin state.
51 * @param {JavascriptParser} parser javascript parser
52 * @returns {State} import parser plugin state
53 */
54function getState(parser) {
55 if (!parserStateMap.has(parser.state)) {
56 parserStateMap.set(parser.state, new WeakMap());
57 }
58 return /** @type {State} */ (parserStateMap.get(parser.state));
59}
60
61/**
62 * Tag dynamic import referenced.
63 * @param {JavascriptParser} parser javascript parser
64 * @param {ImportExpression} importCall import expression
65 * @param {string} variableName variable name
66 */
67function tagDynamicImportReferenced(parser, importCall, variableName) {
68 const state = getState(parser);
69 /** @type {RawReferencedExports} */
70 const references = state.get(importCall) || [];
71 state.set(importCall, references);
72 parser.tagVariable(
73 variableName,
74 dynamicImportTag,
75 /** @type {ImportSettings} */ ({
76 references,
77 expression: importCall
78 })
79 );
80}
81
82/**
83 * Gets fulfilled callback namespace obj.
84 * @param {CallExpression} importThen import().then() call
85 * @returns {Identifier | ObjectPattern | undefined} the dynamic imported namespace obj
86 */
87function getFulfilledCallbackNamespaceObj(importThen) {
88 const fulfilledCallback = importThen.arguments[0];
89 if (
90 fulfilledCallback &&
91 (fulfilledCallback.type === "ArrowFunctionExpression" ||
92 fulfilledCallback.type === "FunctionExpression") &&
93 fulfilledCallback.params[0] &&
94 (fulfilledCallback.params[0].type === "Identifier" ||
95 fulfilledCallback.params[0].type === "ObjectPattern")
96 ) {
97 return fulfilledCallback.params[0];
98 }
99}
100
101/**
102 * Walk import then fulfilled callback.
103 * @param {JavascriptParser} parser javascript parser
104 * @param {ImportExpression} importCall import expression
105 * @param {ArrowFunctionExpression | FunctionExpression} fulfilledCallback the fulfilled callback
106 * @param {Identifier | ObjectPattern} namespaceObjArg the argument of namespace object=
107 */
108function walkImportThenFulfilledCallback(
109 parser,
110 importCall,
111 fulfilledCallback,
112 namespaceObjArg
113) {
114 const arrow = fulfilledCallback.type === "ArrowFunctionExpression";
115 const wasTopLevel = parser.scope.topLevelScope;
116 parser.scope.topLevelScope = arrow ? (wasTopLevel ? "arrow" : false) : false;
117 const scopeParams = [...fulfilledCallback.params];
118
119 // Add function name in scope for recursive calls
120 if (!arrow && fulfilledCallback.id) {
121 scopeParams.push(fulfilledCallback.id);
122 }
123
124 parser.inFunctionScope(!arrow, scopeParams, () => {
125 if (namespaceObjArg.type === "Identifier") {
126 tagDynamicImportReferenced(parser, importCall, namespaceObjArg.name);
127 } else {
128 parser.enterDestructuringAssignment(namespaceObjArg, importCall);
129 const referencedPropertiesInDestructuring =
130 parser.destructuringAssignmentPropertiesFor(importCall);
131 if (referencedPropertiesInDestructuring) {
132 const state = getState(parser);
133 const references = /** @type {RawReferencedExports} */ (
134 state.get(importCall)
135 );
136 /** @type {RawReferencedExports} */
137 const refsInDestructuring = [];
138 traverseDestructuringAssignmentProperties(
139 referencedPropertiesInDestructuring,
140 (stack) => refsInDestructuring.push(stack.map((p) => p.id))
141 );
142 for (const ids of refsInDestructuring) {
143 references.push(ids);
144 }
145 }
146 }
147 for (const param of fulfilledCallback.params) {
148 parser.walkPattern(param);
149 }
150 if (fulfilledCallback.body.type === "BlockStatement") {
151 parser.detectMode(fulfilledCallback.body.body);
152 const prev = parser.prevStatement;
153 parser.preWalkStatement(fulfilledCallback.body);
154 parser.prevStatement = prev;
155 parser.walkStatement(fulfilledCallback.body);
156 } else {
157 parser.walkExpression(fulfilledCallback.body);
158 }
159 });
160 parser.scope.topLevelScope = wasTopLevel;
161}
162
163/**
164 * Exports from enumerable.
165 * @template T
166 * @param {Iterable<T>} enumerable enumerable
167 * @returns {T[][]} array of array
168 */
169const exportsFromEnumerable = (enumerable) =>
170 Array.from(enumerable, (e) => [e]);
171
172const PLUGIN_NAME = "ImportParserPlugin";
173
174class ImportParserPlugin {
175 /**
176 * Creates an instance of ImportParserPlugin.
177 * @param {JavascriptParserOptions} options options
178 */
179 constructor(options) {
180 this.options = options;
181 }
182
183 /**
184 * Applies the plugin by registering its hooks on the compiler.
185 * @param {JavascriptParser} parser the parser
186 * @returns {void}
187 */
188 apply(parser) {
189 parser.hooks.collectDestructuringAssignmentProperties.tap(
190 PLUGIN_NAME,
191 (expr) => {
192 if (expr.type === "ImportExpression") return true;
193 const nameInfo = parser.getNameForExpression(expr);
194 if (
195 nameInfo &&
196 nameInfo.rootInfo instanceof VariableInfo &&
197 nameInfo.rootInfo.name &&
198 parser.getTagData(nameInfo.rootInfo.name, dynamicImportTag)
199 ) {
200 return true;
201 }
202 }
203 );
204 parser.hooks.preDeclarator.tap(PLUGIN_NAME, (decl) => {
205 if (
206 decl.init &&
207 decl.init.type === "AwaitExpression" &&
208 decl.init.argument.type === "ImportExpression" &&
209 decl.id.type === "Identifier"
210 ) {
211 parser.defineVariable(decl.id.name);
212 tagDynamicImportReferenced(parser, decl.init.argument, decl.id.name);
213 }
214 });
215 parser.hooks.expression.for(dynamicImportTag).tap(PLUGIN_NAME, (expr) => {
216 const settings = /** @type {ImportSettings} */ (parser.currentTagData);
217 const referencedPropertiesInDestructuring =
218 parser.destructuringAssignmentPropertiesFor(expr);
219 if (referencedPropertiesInDestructuring) {
220 /** @type {RawReferencedExports} */
221 const refsInDestructuring = [];
222 traverseDestructuringAssignmentProperties(
223 referencedPropertiesInDestructuring,
224 (stack) => refsInDestructuring.push(stack.map((p) => p.id))
225 );
226 for (const ids of refsInDestructuring) {
227 settings.references.push(ids);
228 }
229 } else {
230 settings.references.push([]);
231 }
232 return true;
233 });
234 parser.hooks.expressionMemberChain
235 .for(dynamicImportTag)
236 .tap(PLUGIN_NAME, (_expression, members, membersOptionals) => {
237 const settings = /** @type {ImportSettings} */ (parser.currentTagData);
238 const ids = getNonOptionalPart(members, membersOptionals);
239 settings.references.push(ids);
240 return true;
241 });
242 parser.hooks.callMemberChain
243 .for(dynamicImportTag)
244 .tap(PLUGIN_NAME, (expression, members, membersOptionals) => {
245 const { arguments: args } = expression;
246 const settings = /** @type {ImportSettings} */ (parser.currentTagData);
247 let ids = getNonOptionalPart(members, membersOptionals);
248 const directImport = members.length === 0;
249 if (
250 !directImport &&
251 (this.options.strictThisContextOnImports || ids.length > 1)
252 ) {
253 ids = ids.slice(0, -1);
254 }
255 settings.references.push(ids);
256 if (args) parser.walkExpressions(args);
257 return true;
258 });
259 parser.hooks.importCall.tap(PLUGIN_NAME, (expr, importThen) => {
260 const param = parser.evaluateExpression(expr.source);
261
262 /** @type {null | string} */
263 let chunkName = null;
264 let mode = /** @type {ContextMode} */ (this.options.dynamicImportMode);
265 /** @type {null | RegExp} */
266 let include = null;
267 /** @type {null | RegExp} */
268 let exclude = null;
269 /** @type {null | RawReferencedExports} */
270 let exports = null;
271 /** @type {RawChunkGroupOptions} */
272 const groupOptions = {};
273
274 const {
275 dynamicImportPreload,
276 dynamicImportPrefetch,
277 dynamicImportFetchPriority
278 } = this.options;
279 if (
280 dynamicImportPreload !== undefined &&
281 dynamicImportPreload !== false
282 ) {
283 groupOptions.preloadOrder =
284 dynamicImportPreload === true ? 0 : dynamicImportPreload;
285 }
286 if (
287 dynamicImportPrefetch !== undefined &&
288 dynamicImportPrefetch !== false
289 ) {
290 groupOptions.prefetchOrder =
291 dynamicImportPrefetch === true ? 0 : dynamicImportPrefetch;
292 }
293 if (
294 dynamicImportFetchPriority !== undefined &&
295 dynamicImportFetchPriority !== false
296 ) {
297 groupOptions.fetchPriority = dynamicImportFetchPriority;
298 }
299
300 const { options: importOptions, errors: commentErrors } =
301 parser.parseCommentOptions(/** @type {Range} */ (expr.range));
302
303 if (commentErrors) {
304 for (const e of commentErrors) {
305 const { comment } = e;
306 parser.state.module.addWarning(
307 new CommentCompilationWarning(
308 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
309 /** @type {DependencyLocation} */ (comment.loc)
310 )
311 );
312 }
313 }
314
315 const phase = createGetImportPhase(
316 this.options.deferImport,
317 this.options.sourceImport
318 )(parser, expr, () => importOptions);
319
320 if (importOptions) {
321 if (importOptions.webpackIgnore !== undefined) {
322 if (typeof importOptions.webpackIgnore !== "boolean") {
323 parser.state.module.addWarning(
324 new UnsupportedFeatureWarning(
325 `\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
326 /** @type {DependencyLocation} */ (expr.loc)
327 )
328 );
329 } else if (importOptions.webpackIgnore) {
330 // Do not instrument `import()` if `webpackIgnore` is `true`
331 return false;
332 }
333 }
334 if (importOptions.webpackChunkName !== undefined) {
335 if (typeof importOptions.webpackChunkName !== "string") {
336 parser.state.module.addWarning(
337 new UnsupportedFeatureWarning(
338 `\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
339 /** @type {DependencyLocation} */ (expr.loc)
340 )
341 );
342 } else {
343 chunkName = importOptions.webpackChunkName;
344 }
345 }
346 if (importOptions.webpackMode !== undefined) {
347 if (typeof importOptions.webpackMode !== "string") {
348 parser.state.module.addWarning(
349 new UnsupportedFeatureWarning(
350 `\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`,
351 /** @type {DependencyLocation} */ (expr.loc)
352 )
353 );
354 } else {
355 mode = /** @type {ContextMode} */ (importOptions.webpackMode);
356 }
357 }
358 if (importOptions.webpackPrefetch !== undefined) {
359 if (importOptions.webpackPrefetch === true) {
360 groupOptions.prefetchOrder = 0;
361 } else if (typeof importOptions.webpackPrefetch === "number") {
362 groupOptions.prefetchOrder = importOptions.webpackPrefetch;
363 } else {
364 parser.state.module.addWarning(
365 new UnsupportedFeatureWarning(
366 `\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
367 /** @type {DependencyLocation} */ (expr.loc)
368 )
369 );
370 }
371 }
372 if (importOptions.webpackPreload !== undefined) {
373 if (importOptions.webpackPreload === true) {
374 groupOptions.preloadOrder = 0;
375 } else if (typeof importOptions.webpackPreload === "number") {
376 groupOptions.preloadOrder = importOptions.webpackPreload;
377 } else {
378 parser.state.module.addWarning(
379 new UnsupportedFeatureWarning(
380 `\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
381 /** @type {DependencyLocation} */ (expr.loc)
382 )
383 );
384 }
385 }
386 if (importOptions.webpackFetchPriority !== undefined) {
387 if (
388 typeof importOptions.webpackFetchPriority === "string" &&
389 ["high", "low", "auto"].includes(importOptions.webpackFetchPriority)
390 ) {
391 groupOptions.fetchPriority =
392 /** @type {"low" | "high" | "auto"} */
393 (importOptions.webpackFetchPriority);
394 } else {
395 parser.state.module.addWarning(
396 new UnsupportedFeatureWarning(
397 `\`webpackFetchPriority\` expected true or "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
398 /** @type {DependencyLocation} */ (expr.loc)
399 )
400 );
401 }
402 }
403 if (importOptions.webpackInclude !== undefined) {
404 if (
405 !importOptions.webpackInclude ||
406 !(importOptions.webpackInclude instanceof RegExp)
407 ) {
408 parser.state.module.addWarning(
409 new UnsupportedFeatureWarning(
410 `\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
411 /** @type {DependencyLocation} */ (expr.loc)
412 )
413 );
414 } else {
415 include = importOptions.webpackInclude;
416 }
417 }
418 if (importOptions.webpackExclude !== undefined) {
419 if (
420 !importOptions.webpackExclude ||
421 !(importOptions.webpackExclude instanceof RegExp)
422 ) {
423 parser.state.module.addWarning(
424 new UnsupportedFeatureWarning(
425 `\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
426 /** @type {DependencyLocation} */ (expr.loc)
427 )
428 );
429 } else {
430 exclude = importOptions.webpackExclude;
431 }
432 }
433 if (importOptions.webpackExports !== undefined) {
434 if (
435 !(
436 typeof importOptions.webpackExports === "string" ||
437 (Array.isArray(importOptions.webpackExports) &&
438 importOptions.webpackExports.every(
439 (item) => typeof item === "string"
440 ))
441 )
442 ) {
443 parser.state.module.addWarning(
444 new UnsupportedFeatureWarning(
445 `\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
446 /** @type {DependencyLocation} */ (expr.loc)
447 )
448 );
449 } else if (typeof importOptions.webpackExports === "string") {
450 exports = [[importOptions.webpackExports]];
451 } else {
452 exports = exportsFromEnumerable(importOptions.webpackExports);
453 }
454 }
455 }
456
457 if (
458 mode !== "lazy" &&
459 mode !== "lazy-once" &&
460 mode !== "eager" &&
461 mode !== "weak"
462 ) {
463 parser.state.module.addWarning(
464 new UnsupportedFeatureWarning(
465 `\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`,
466 /** @type {DependencyLocation} */ (expr.loc)
467 )
468 );
469 mode = "lazy";
470 }
471
472 const referencedPropertiesInDestructuring =
473 parser.destructuringAssignmentPropertiesFor(expr);
474 const state = getState(parser);
475 const referencedPropertiesInMember = state.get(expr);
476 const fulfilledNamespaceObj =
477 importThen && getFulfilledCallbackNamespaceObj(importThen);
478 if (
479 referencedPropertiesInDestructuring ||
480 referencedPropertiesInMember ||
481 fulfilledNamespaceObj
482 ) {
483 if (exports) {
484 parser.state.module.addWarning(
485 new UnsupportedFeatureWarning(
486 "You don't need `webpackExports` if the usage of dynamic import is statically analyse-able. You can safely remove the `webpackExports` magic comment.",
487 /** @type {DependencyLocation} */ (expr.loc)
488 )
489 );
490 }
491
492 if (referencedPropertiesInDestructuring) {
493 /** @type {RawReferencedExports} */
494 const refsInDestructuring = [];
495 traverseDestructuringAssignmentProperties(
496 referencedPropertiesInDestructuring,
497 (stack) => refsInDestructuring.push(stack.map((p) => p.id))
498 );
499
500 exports = refsInDestructuring;
501 } else if (referencedPropertiesInMember) {
502 exports = referencedPropertiesInMember;
503 } else {
504 /** @type {RawReferencedExports} */
505 const references = [];
506 state.set(expr, references);
507
508 exports = references;
509 }
510 }
511
512 if (param.isString()) {
513 const attributes = getImportAttributes(expr);
514
515 if (mode === "eager") {
516 const dep = new ImportEagerDependency(
517 /** @type {string} */ (param.string),
518 /** @type {Range} */ (expr.range),
519 exports,
520 phase,
521 attributes
522 );
523 parser.state.current.addDependency(dep);
524 } else if (mode === "weak") {
525 const dep = new ImportWeakDependency(
526 /** @type {string} */ (param.string),
527 /** @type {Range} */ (expr.range),
528 exports,
529 phase,
530 attributes
531 );
532 parser.state.current.addDependency(dep);
533 } else {
534 const depBlock = new AsyncDependenciesBlock(
535 {
536 ...groupOptions,
537 name: chunkName
538 },
539 /** @type {DependencyLocation} */ (expr.loc),
540 param.string
541 );
542 const dep = new ImportDependency(
543 /** @type {string} */ (param.string),
544 /** @type {Range} */ (expr.range),
545 exports,
546 phase,
547 attributes
548 );
549 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
550 dep.optional = Boolean(parser.scope.inTry);
551 depBlock.addDependency(dep);
552 parser.state.current.addBlock(depBlock);
553 }
554 } else {
555 if (mode === "weak") {
556 mode = "async-weak";
557 }
558
559 const dep = ContextDependencyHelpers.create(
560 ImportContextDependency,
561 /** @type {Range} */ (expr.range),
562 param,
563 expr,
564 this.options,
565 {
566 chunkName,
567 groupOptions,
568 include,
569 exclude,
570 mode,
571 namespaceObject:
572 /** @type {BuildMeta} */
573 (parser.state.module.buildMeta).strictHarmonyModule
574 ? "strict"
575 : true,
576 typePrefix: "import()",
577 category: "esm",
578 referencedExports: exports,
579 attributes: getImportAttributes(expr),
580 phase
581 },
582 parser
583 );
584 if (!dep) return;
585 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
586 dep.optional = Boolean(parser.scope.inTry);
587 parser.state.current.addDependency(dep);
588 }
589
590 if (fulfilledNamespaceObj) {
591 walkImportThenFulfilledCallback(
592 parser,
593 expr,
594 /** @type {ArrowFunctionExpression | FunctionExpression} */
595 (importThen.arguments[0]),
596 fulfilledNamespaceObj
597 );
598 parser.walkExpressions(importThen.arguments.slice(1));
599 } else if (importThen) {
600 parser.walkExpressions(importThen.arguments);
601 }
602
603 return true;
604 });
605 }
606}
607
608module.exports = ImportParserPlugin;
Note: See TracBrowser for help on using the repository browser.