UNPKG

32.5 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { OriginalSource, RawSource } = require("webpack-sources");
9const AsyncDependenciesBlock = require("./AsyncDependenciesBlock");
10const { makeWebpackError } = require("./HookWebpackError");
11const Module = require("./Module");
12const RuntimeGlobals = require("./RuntimeGlobals");
13const Template = require("./Template");
14const WebpackError = require("./WebpackError");
15const {
16 compareLocations,
17 concatComparators,
18 compareSelect,
19 keepOriginalOrder,
20 compareModulesById
21} = require("./util/comparators");
22const { contextify, parseResource } = require("./util/identifier");
23const makeSerializable = require("./util/makeSerializable");
24
25/** @typedef {import("webpack-sources").Source} Source */
26/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
27/** @typedef {import("./ChunkGraph")} ChunkGraph */
28/** @typedef {import("./ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
29/** @typedef {import("./Compilation")} Compilation */
30/** @typedef {import("./DependencyTemplates")} DependencyTemplates */
31/** @typedef {import("./Module").BuildMeta} BuildMeta */
32/** @typedef {import("./Module").CodeGenerationContext} CodeGenerationContext */
33/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
34/** @typedef {import("./Module").LibIdentOptions} LibIdentOptions */
35/** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
36/** @typedef {import("./ModuleGraph")} ModuleGraph */
37/** @typedef {import("./RequestShortener")} RequestShortener */
38/** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
39/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
40/** @typedef {import("./dependencies/ContextElementDependency")} ContextElementDependency */
41/** @template T @typedef {import("./util/LazySet")<T>} LazySet<T> */
42/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
43
44/** @typedef {"sync" | "eager" | "weak" | "async-weak" | "lazy" | "lazy-once"} ContextMode Context mode */
45
46/**
47 * @typedef {Object} ContextOptions
48 * @property {ContextMode} mode
49 * @property {boolean} recursive
50 * @property {RegExp} regExp
51 * @property {"strict"|boolean=} namespaceObject
52 * @property {string=} addon
53 * @property {string=} chunkName
54 * @property {RegExp=} include
55 * @property {RegExp=} exclude
56 * @property {RawChunkGroupOptions=} groupOptions
57 * @property {string=} typePrefix
58 * @property {string=} category
59 * @property {string[][]=} referencedExports exports referenced from modules (won't be mangled)
60 */
61
62/**
63 * @typedef {Object} ContextModuleOptionsExtras
64 * @property {string} resource
65 * @property {string=} resourceQuery
66 * @property {string=} resourceFragment
67 * @property {TODO} resolveOptions
68 */
69
70/** @typedef {ContextOptions & ContextModuleOptionsExtras} ContextModuleOptions */
71
72/**
73 * @callback ResolveDependenciesCallback
74 * @param {(Error | null)=} err
75 * @param {ContextElementDependency[]=} dependencies
76 */
77
78/**
79 * @callback ResolveDependencies
80 * @param {InputFileSystem} fs
81 * @param {ContextModuleOptions} options
82 * @param {ResolveDependenciesCallback} callback
83 */
84
85const SNAPSHOT_OPTIONS = { timestamp: true };
86
87const TYPES = new Set(["javascript"]);
88
89class ContextModule extends Module {
90 /**
91 * @param {ResolveDependencies} resolveDependencies function to get dependencies in this context
92 * @param {ContextModuleOptions} options options object
93 */
94 constructor(resolveDependencies, options) {
95 const parsed = parseResource(options ? options.resource : "");
96 const resource = parsed.path;
97 const resourceQuery = (options && options.resourceQuery) || parsed.query;
98 const resourceFragment =
99 (options && options.resourceFragment) || parsed.fragment;
100
101 super("javascript/dynamic", resource);
102
103 // Info from Factory
104 this.resolveDependencies = resolveDependencies;
105 /** @type {ContextModuleOptions} */
106 this.options = {
107 ...options,
108 resource,
109 resourceQuery,
110 resourceFragment
111 };
112 if (options && options.resolveOptions !== undefined) {
113 this.resolveOptions = options.resolveOptions;
114 }
115
116 if (options && typeof options.mode !== "string") {
117 throw new Error("options.mode is a required option");
118 }
119
120 this._identifier = this._createIdentifier();
121 this._forceBuild = true;
122 }
123
124 /**
125 * @returns {Set<string>} types available (do not mutate)
126 */
127 getSourceTypes() {
128 return TYPES;
129 }
130
131 /**
132 * Assuming this module is in the cache. Update the (cached) module with
133 * the fresh module from the factory. Usually updates internal references
134 * and properties.
135 * @param {Module} module fresh module
136 * @returns {void}
137 */
138 updateCacheModule(module) {
139 const m = /** @type {ContextModule} */ (module);
140 this.resolveDependencies = m.resolveDependencies;
141 this.options = m.options;
142 }
143
144 /**
145 * Assuming this module is in the cache. Remove internal references to allow freeing some memory.
146 */
147 cleanupForCache() {
148 super.cleanupForCache();
149 this.resolveDependencies = undefined;
150 }
151
152 _prettyRegExp(regexString, stripSlash = true) {
153 const str = (regexString + "").replace(/!/g, "%21").replace(/\|/g, "%7C");
154 return stripSlash ? str.substring(1, str.length - 1) : str;
155 }
156
157 _createIdentifier() {
158 let identifier = this.context;
159 if (this.options.resourceQuery) {
160 identifier += `|${this.options.resourceQuery}`;
161 }
162 if (this.options.resourceFragment) {
163 identifier += `|${this.options.resourceFragment}`;
164 }
165 if (this.options.mode) {
166 identifier += `|${this.options.mode}`;
167 }
168 if (!this.options.recursive) {
169 identifier += "|nonrecursive";
170 }
171 if (this.options.addon) {
172 identifier += `|${this.options.addon}`;
173 }
174 if (this.options.regExp) {
175 identifier += `|${this._prettyRegExp(this.options.regExp, false)}`;
176 }
177 if (this.options.include) {
178 identifier += `|include: ${this._prettyRegExp(
179 this.options.include,
180 false
181 )}`;
182 }
183 if (this.options.exclude) {
184 identifier += `|exclude: ${this._prettyRegExp(
185 this.options.exclude,
186 false
187 )}`;
188 }
189 if (this.options.referencedExports) {
190 identifier += `|referencedExports: ${JSON.stringify(
191 this.options.referencedExports
192 )}`;
193 }
194 if (this.options.chunkName) {
195 identifier += `|chunkName: ${this.options.chunkName}`;
196 }
197 if (this.options.groupOptions) {
198 identifier += `|groupOptions: ${JSON.stringify(
199 this.options.groupOptions
200 )}`;
201 }
202 if (this.options.namespaceObject === "strict") {
203 identifier += "|strict namespace object";
204 } else if (this.options.namespaceObject) {
205 identifier += "|namespace object";
206 }
207
208 return identifier;
209 }
210
211 /**
212 * @returns {string} a unique identifier of the module
213 */
214 identifier() {
215 return this._identifier;
216 }
217
218 /**
219 * @param {RequestShortener} requestShortener the request shortener
220 * @returns {string} a user readable identifier of the module
221 */
222 readableIdentifier(requestShortener) {
223 let identifier = requestShortener.shorten(this.context) + "/";
224 if (this.options.resourceQuery) {
225 identifier += ` ${this.options.resourceQuery}`;
226 }
227 if (this.options.mode) {
228 identifier += ` ${this.options.mode}`;
229 }
230 if (!this.options.recursive) {
231 identifier += " nonrecursive";
232 }
233 if (this.options.addon) {
234 identifier += ` ${requestShortener.shorten(this.options.addon)}`;
235 }
236 if (this.options.regExp) {
237 identifier += ` ${this._prettyRegExp(this.options.regExp)}`;
238 }
239 if (this.options.include) {
240 identifier += ` include: ${this._prettyRegExp(this.options.include)}`;
241 }
242 if (this.options.exclude) {
243 identifier += ` exclude: ${this._prettyRegExp(this.options.exclude)}`;
244 }
245 if (this.options.referencedExports) {
246 identifier += ` referencedExports: ${this.options.referencedExports
247 .map(e => e.join("."))
248 .join(", ")}`;
249 }
250 if (this.options.chunkName) {
251 identifier += ` chunkName: ${this.options.chunkName}`;
252 }
253 if (this.options.groupOptions) {
254 const groupOptions = this.options.groupOptions;
255 for (const key of Object.keys(groupOptions)) {
256 identifier += ` ${key}: ${groupOptions[key]}`;
257 }
258 }
259 if (this.options.namespaceObject === "strict") {
260 identifier += " strict namespace object";
261 } else if (this.options.namespaceObject) {
262 identifier += " namespace object";
263 }
264
265 return identifier;
266 }
267
268 /**
269 * @param {LibIdentOptions} options options
270 * @returns {string | null} an identifier for library inclusion
271 */
272 libIdent(options) {
273 let identifier = contextify(
274 options.context,
275 this.context,
276 options.associatedObjectForCache
277 );
278 if (this.layer) identifier = `(${this.layer})/${identifier}`;
279 if (this.options.mode) {
280 identifier += ` ${this.options.mode}`;
281 }
282 if (this.options.recursive) {
283 identifier += " recursive";
284 }
285 if (this.options.addon) {
286 identifier += ` ${contextify(
287 options.context,
288 this.options.addon,
289 options.associatedObjectForCache
290 )}`;
291 }
292 if (this.options.regExp) {
293 identifier += ` ${this._prettyRegExp(this.options.regExp)}`;
294 }
295 if (this.options.include) {
296 identifier += ` include: ${this._prettyRegExp(this.options.include)}`;
297 }
298 if (this.options.exclude) {
299 identifier += ` exclude: ${this._prettyRegExp(this.options.exclude)}`;
300 }
301 if (this.options.referencedExports) {
302 identifier += ` referencedExports: ${this.options.referencedExports
303 .map(e => e.join("."))
304 .join(", ")}`;
305 }
306
307 return identifier;
308 }
309
310 /**
311 * @returns {void}
312 */
313 invalidateBuild() {
314 this._forceBuild = true;
315 }
316
317 /**
318 * @param {NeedBuildContext} context context info
319 * @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
320 * @returns {void}
321 */
322 needBuild({ fileSystemInfo }, callback) {
323 // build if enforced
324 if (this._forceBuild) return callback(null, true);
325
326 // always build when we have no snapshot
327 if (!this.buildInfo.snapshot) return callback(null, true);
328
329 fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
330 callback(err, !valid);
331 });
332 }
333
334 /**
335 * @param {WebpackOptions} options webpack options
336 * @param {Compilation} compilation the compilation
337 * @param {ResolverWithOptions} resolver the resolver
338 * @param {InputFileSystem} fs the file system
339 * @param {function(WebpackError=): void} callback callback function
340 * @returns {void}
341 */
342 build(options, compilation, resolver, fs, callback) {
343 this._forceBuild = false;
344 /** @type {BuildMeta} */
345 this.buildMeta = {
346 exportsType: "default",
347 defaultObject: "redirect-warn"
348 };
349 this.buildInfo = {
350 snapshot: undefined
351 };
352 this.dependencies.length = 0;
353 this.blocks.length = 0;
354 const startTime = Date.now();
355 this.resolveDependencies(fs, this.options, (err, dependencies) => {
356 if (err) {
357 return callback(
358 makeWebpackError(err, "ContextModule.resolveDependencies")
359 );
360 }
361
362 // abort if something failed
363 // this will create an empty context
364 if (!dependencies) {
365 callback();
366 return;
367 }
368
369 // enhance dependencies with meta info
370 for (const dep of dependencies) {
371 dep.loc = {
372 name: dep.userRequest
373 };
374 dep.request = this.options.addon + dep.request;
375 }
376 dependencies.sort(
377 concatComparators(
378 compareSelect(a => a.loc, compareLocations),
379 keepOriginalOrder(this.dependencies)
380 )
381 );
382
383 if (this.options.mode === "sync" || this.options.mode === "eager") {
384 // if we have an sync or eager context
385 // just add all dependencies and continue
386 this.dependencies = dependencies;
387 } else if (this.options.mode === "lazy-once") {
388 // for the lazy-once mode create a new async dependency block
389 // and add that block to this context
390 if (dependencies.length > 0) {
391 const block = new AsyncDependenciesBlock({
392 ...this.options.groupOptions,
393 name: this.options.chunkName
394 });
395 for (const dep of dependencies) {
396 block.addDependency(dep);
397 }
398 this.addBlock(block);
399 }
400 } else if (
401 this.options.mode === "weak" ||
402 this.options.mode === "async-weak"
403 ) {
404 // we mark all dependencies as weak
405 for (const dep of dependencies) {
406 dep.weak = true;
407 }
408 this.dependencies = dependencies;
409 } else if (this.options.mode === "lazy") {
410 // if we are lazy create a new async dependency block per dependency
411 // and add all blocks to this context
412 let index = 0;
413 for (const dep of dependencies) {
414 let chunkName = this.options.chunkName;
415 if (chunkName) {
416 if (!/\[(index|request)\]/.test(chunkName)) {
417 chunkName += "[index]";
418 }
419 chunkName = chunkName.replace(/\[index\]/g, `${index++}`);
420 chunkName = chunkName.replace(
421 /\[request\]/g,
422 Template.toPath(dep.userRequest)
423 );
424 }
425 const block = new AsyncDependenciesBlock(
426 {
427 ...this.options.groupOptions,
428 name: chunkName
429 },
430 dep.loc,
431 dep.userRequest
432 );
433 block.addDependency(dep);
434 this.addBlock(block);
435 }
436 } else {
437 callback(
438 new WebpackError(`Unsupported mode "${this.options.mode}" in context`)
439 );
440 return;
441 }
442 compilation.fileSystemInfo.createSnapshot(
443 startTime,
444 null,
445 [this.context],
446 null,
447 SNAPSHOT_OPTIONS,
448 (err, snapshot) => {
449 if (err) return callback(err);
450 this.buildInfo.snapshot = snapshot;
451 callback();
452 }
453 );
454 });
455 }
456
457 /**
458 * @param {LazySet<string>} fileDependencies set where file dependencies are added to
459 * @param {LazySet<string>} contextDependencies set where context dependencies are added to
460 * @param {LazySet<string>} missingDependencies set where missing dependencies are added to
461 * @param {LazySet<string>} buildDependencies set where build dependencies are added to
462 */
463 addCacheDependencies(
464 fileDependencies,
465 contextDependencies,
466 missingDependencies,
467 buildDependencies
468 ) {
469 contextDependencies.add(this.context);
470 }
471
472 /**
473 * @param {ContextElementDependency[]} dependencies all dependencies
474 * @param {ChunkGraph} chunkGraph chunk graph
475 * @returns {TODO} TODO
476 */
477 getUserRequestMap(dependencies, chunkGraph) {
478 const moduleGraph = chunkGraph.moduleGraph;
479 // if we filter first we get a new array
480 // therefore we don't need to create a clone of dependencies explicitly
481 // therefore the order of this is !important!
482 const sortedDependencies = dependencies
483 .filter(dependency => moduleGraph.getModule(dependency))
484 .sort((a, b) => {
485 if (a.userRequest === b.userRequest) {
486 return 0;
487 }
488 return a.userRequest < b.userRequest ? -1 : 1;
489 });
490 const map = Object.create(null);
491 for (const dep of sortedDependencies) {
492 const module = moduleGraph.getModule(dep);
493 map[dep.userRequest] = chunkGraph.getModuleId(module);
494 }
495 return map;
496 }
497
498 /**
499 * @param {ContextElementDependency[]} dependencies all dependencies
500 * @param {ChunkGraph} chunkGraph chunk graph
501 * @returns {TODO} TODO
502 */
503 getFakeMap(dependencies, chunkGraph) {
504 if (!this.options.namespaceObject) {
505 return 9;
506 }
507 const moduleGraph = chunkGraph.moduleGraph;
508 // bitfield
509 let hasType = 0;
510 const comparator = compareModulesById(chunkGraph);
511 // if we filter first we get a new array
512 // therefore we don't need to create a clone of dependencies explicitly
513 // therefore the order of this is !important!
514 const sortedModules = dependencies
515 .map(dependency => moduleGraph.getModule(dependency))
516 .filter(Boolean)
517 .sort(comparator);
518 const fakeMap = Object.create(null);
519 for (const module of sortedModules) {
520 const exportsType = module.getExportsType(
521 moduleGraph,
522 this.options.namespaceObject === "strict"
523 );
524 const id = chunkGraph.getModuleId(module);
525 switch (exportsType) {
526 case "namespace":
527 fakeMap[id] = 9;
528 hasType |= 1;
529 break;
530 case "dynamic":
531 fakeMap[id] = 7;
532 hasType |= 2;
533 break;
534 case "default-only":
535 fakeMap[id] = 1;
536 hasType |= 4;
537 break;
538 case "default-with-named":
539 fakeMap[id] = 3;
540 hasType |= 8;
541 break;
542 default:
543 throw new Error(`Unexpected exports type ${exportsType}`);
544 }
545 }
546 if (hasType === 1) {
547 return 9;
548 }
549 if (hasType === 2) {
550 return 7;
551 }
552 if (hasType === 4) {
553 return 1;
554 }
555 if (hasType === 8) {
556 return 3;
557 }
558 if (hasType === 0) {
559 return 9;
560 }
561 return fakeMap;
562 }
563
564 getFakeMapInitStatement(fakeMap) {
565 return typeof fakeMap === "object"
566 ? `var fakeMap = ${JSON.stringify(fakeMap, null, "\t")};`
567 : "";
568 }
569
570 getReturn(type, asyncModule) {
571 if (type === 9) {
572 return "__webpack_require__(id)";
573 }
574 return `${RuntimeGlobals.createFakeNamespaceObject}(id, ${type}${
575 asyncModule ? " | 16" : ""
576 })`;
577 }
578
579 getReturnModuleObjectSource(
580 fakeMap,
581 asyncModule,
582 fakeMapDataExpression = "fakeMap[id]"
583 ) {
584 if (typeof fakeMap === "number") {
585 return `return ${this.getReturn(fakeMap, asyncModule)};`;
586 }
587 return `return ${
588 RuntimeGlobals.createFakeNamespaceObject
589 }(id, ${fakeMapDataExpression}${asyncModule ? " | 16" : ""})`;
590 }
591
592 /**
593 * @param {TODO} dependencies TODO
594 * @param {TODO} id TODO
595 * @param {ChunkGraph} chunkGraph the chunk graph
596 * @returns {string} source code
597 */
598 getSyncSource(dependencies, id, chunkGraph) {
599 const map = this.getUserRequestMap(dependencies, chunkGraph);
600 const fakeMap = this.getFakeMap(dependencies, chunkGraph);
601 const returnModuleObject = this.getReturnModuleObjectSource(fakeMap);
602
603 return `var map = ${JSON.stringify(map, null, "\t")};
604${this.getFakeMapInitStatement(fakeMap)}
605
606function webpackContext(req) {
607 var id = webpackContextResolve(req);
608 ${returnModuleObject}
609}
610function webpackContextResolve(req) {
611 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
612 var e = new Error("Cannot find module '" + req + "'");
613 e.code = 'MODULE_NOT_FOUND';
614 throw e;
615 }
616 return map[req];
617}
618webpackContext.keys = function webpackContextKeys() {
619 return Object.keys(map);
620};
621webpackContext.resolve = webpackContextResolve;
622module.exports = webpackContext;
623webpackContext.id = ${JSON.stringify(id)};`;
624 }
625
626 /**
627 * @param {TODO} dependencies TODO
628 * @param {TODO} id TODO
629 * @param {ChunkGraph} chunkGraph the chunk graph
630 * @returns {string} source code
631 */
632 getWeakSyncSource(dependencies, id, chunkGraph) {
633 const map = this.getUserRequestMap(dependencies, chunkGraph);
634 const fakeMap = this.getFakeMap(dependencies, chunkGraph);
635 const returnModuleObject = this.getReturnModuleObjectSource(fakeMap);
636
637 return `var map = ${JSON.stringify(map, null, "\t")};
638${this.getFakeMapInitStatement(fakeMap)}
639
640function webpackContext(req) {
641 var id = webpackContextResolve(req);
642 if(!${RuntimeGlobals.moduleFactories}[id]) {
643 var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
644 e.code = 'MODULE_NOT_FOUND';
645 throw e;
646 }
647 ${returnModuleObject}
648}
649function webpackContextResolve(req) {
650 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
651 var e = new Error("Cannot find module '" + req + "'");
652 e.code = 'MODULE_NOT_FOUND';
653 throw e;
654 }
655 return map[req];
656}
657webpackContext.keys = function webpackContextKeys() {
658 return Object.keys(map);
659};
660webpackContext.resolve = webpackContextResolve;
661webpackContext.id = ${JSON.stringify(id)};
662module.exports = webpackContext;`;
663 }
664
665 /**
666 * @param {TODO} dependencies TODO
667 * @param {TODO} id TODO
668 * @param {Object} context context
669 * @param {ChunkGraph} context.chunkGraph the chunk graph
670 * @param {RuntimeTemplate} context.runtimeTemplate the chunk graph
671 * @returns {string} source code
672 */
673 getAsyncWeakSource(dependencies, id, { chunkGraph, runtimeTemplate }) {
674 const arrow = runtimeTemplate.supportsArrowFunction();
675 const map = this.getUserRequestMap(dependencies, chunkGraph);
676 const fakeMap = this.getFakeMap(dependencies, chunkGraph);
677 const returnModuleObject = this.getReturnModuleObjectSource(fakeMap, true);
678
679 return `var map = ${JSON.stringify(map, null, "\t")};
680${this.getFakeMapInitStatement(fakeMap)}
681
682function webpackAsyncContext(req) {
683 return webpackAsyncContextResolve(req).then(${
684 arrow ? "id =>" : "function(id)"
685 } {
686 if(!${RuntimeGlobals.moduleFactories}[id]) {
687 var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
688 e.code = 'MODULE_NOT_FOUND';
689 throw e;
690 }
691 ${returnModuleObject}
692 });
693}
694function webpackAsyncContextResolve(req) {
695 // Here Promise.resolve().then() is used instead of new Promise() to prevent
696 // uncaught exception popping up in devtools
697 return Promise.resolve().then(${arrow ? "() =>" : "function()"} {
698 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
699 var e = new Error("Cannot find module '" + req + "'");
700 e.code = 'MODULE_NOT_FOUND';
701 throw e;
702 }
703 return map[req];
704 });
705}
706webpackAsyncContext.keys = ${runtimeTemplate.returningFunction(
707 "Object.keys(map)"
708 )};
709webpackAsyncContext.resolve = webpackAsyncContextResolve;
710webpackAsyncContext.id = ${JSON.stringify(id)};
711module.exports = webpackAsyncContext;`;
712 }
713
714 /**
715 * @param {TODO} dependencies TODO
716 * @param {TODO} id TODO
717 * @param {Object} context context
718 * @param {ChunkGraph} context.chunkGraph the chunk graph
719 * @param {RuntimeTemplate} context.runtimeTemplate the chunk graph
720 * @returns {string} source code
721 */
722 getEagerSource(dependencies, id, { chunkGraph, runtimeTemplate }) {
723 const arrow = runtimeTemplate.supportsArrowFunction();
724 const map = this.getUserRequestMap(dependencies, chunkGraph);
725 const fakeMap = this.getFakeMap(dependencies, chunkGraph);
726 const thenFunction =
727 fakeMap !== 9
728 ? `${arrow ? "id =>" : "function(id)"} {
729 ${this.getReturnModuleObjectSource(fakeMap)}
730 }`
731 : "__webpack_require__";
732 return `var map = ${JSON.stringify(map, null, "\t")};
733${this.getFakeMapInitStatement(fakeMap)}
734
735function webpackAsyncContext(req) {
736 return webpackAsyncContextResolve(req).then(${thenFunction});
737}
738function webpackAsyncContextResolve(req) {
739 // Here Promise.resolve().then() is used instead of new Promise() to prevent
740 // uncaught exception popping up in devtools
741 return Promise.resolve().then(${arrow ? "() =>" : "function()"} {
742 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
743 var e = new Error("Cannot find module '" + req + "'");
744 e.code = 'MODULE_NOT_FOUND';
745 throw e;
746 }
747 return map[req];
748 });
749}
750webpackAsyncContext.keys = ${runtimeTemplate.returningFunction(
751 "Object.keys(map)"
752 )};
753webpackAsyncContext.resolve = webpackAsyncContextResolve;
754webpackAsyncContext.id = ${JSON.stringify(id)};
755module.exports = webpackAsyncContext;`;
756 }
757
758 /**
759 * @param {TODO} block TODO
760 * @param {TODO} dependencies TODO
761 * @param {TODO} id TODO
762 * @param {Object} options options object
763 * @param {RuntimeTemplate} options.runtimeTemplate the runtime template
764 * @param {ChunkGraph} options.chunkGraph the chunk graph
765 * @returns {string} source code
766 */
767 getLazyOnceSource(block, dependencies, id, { runtimeTemplate, chunkGraph }) {
768 const promise = runtimeTemplate.blockPromise({
769 chunkGraph,
770 block,
771 message: "lazy-once context",
772 runtimeRequirements: new Set()
773 });
774 const arrow = runtimeTemplate.supportsArrowFunction();
775 const map = this.getUserRequestMap(dependencies, chunkGraph);
776 const fakeMap = this.getFakeMap(dependencies, chunkGraph);
777 const thenFunction =
778 fakeMap !== 9
779 ? `${arrow ? "id =>" : "function(id)"} {
780 ${this.getReturnModuleObjectSource(fakeMap, true)};
781 }`
782 : "__webpack_require__";
783
784 return `var map = ${JSON.stringify(map, null, "\t")};
785${this.getFakeMapInitStatement(fakeMap)}
786
787function webpackAsyncContext(req) {
788 return webpackAsyncContextResolve(req).then(${thenFunction});
789}
790function webpackAsyncContextResolve(req) {
791 return ${promise}.then(${arrow ? "() =>" : "function()"} {
792 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
793 var e = new Error("Cannot find module '" + req + "'");
794 e.code = 'MODULE_NOT_FOUND';
795 throw e;
796 }
797 return map[req];
798 });
799}
800webpackAsyncContext.keys = ${runtimeTemplate.returningFunction(
801 "Object.keys(map)"
802 )};
803webpackAsyncContext.resolve = webpackAsyncContextResolve;
804webpackAsyncContext.id = ${JSON.stringify(id)};
805module.exports = webpackAsyncContext;`;
806 }
807
808 /**
809 * @param {TODO} blocks TODO
810 * @param {TODO} id TODO
811 * @param {Object} context context
812 * @param {ChunkGraph} context.chunkGraph the chunk graph
813 * @param {RuntimeTemplate} context.runtimeTemplate the chunk graph
814 * @returns {string} source code
815 */
816 getLazySource(blocks, id, { chunkGraph, runtimeTemplate }) {
817 const moduleGraph = chunkGraph.moduleGraph;
818 const arrow = runtimeTemplate.supportsArrowFunction();
819 let hasMultipleOrNoChunks = false;
820 let hasNoChunk = true;
821 const fakeMap = this.getFakeMap(
822 blocks.map(b => b.dependencies[0]),
823 chunkGraph
824 );
825 const hasFakeMap = typeof fakeMap === "object";
826 const items = blocks
827 .map(block => {
828 const dependency = block.dependencies[0];
829 return {
830 dependency: dependency,
831 module: moduleGraph.getModule(dependency),
832 block: block,
833 userRequest: dependency.userRequest,
834 chunks: undefined
835 };
836 })
837 .filter(item => item.module);
838 for (const item of items) {
839 const chunkGroup = chunkGraph.getBlockChunkGroup(item.block);
840 const chunks = (chunkGroup && chunkGroup.chunks) || [];
841 item.chunks = chunks;
842 if (chunks.length > 0) {
843 hasNoChunk = false;
844 }
845 if (chunks.length !== 1) {
846 hasMultipleOrNoChunks = true;
847 }
848 }
849 const shortMode = hasNoChunk && !hasFakeMap;
850 const sortedItems = items.sort((a, b) => {
851 if (a.userRequest === b.userRequest) return 0;
852 return a.userRequest < b.userRequest ? -1 : 1;
853 });
854 const map = Object.create(null);
855 for (const item of sortedItems) {
856 const moduleId = chunkGraph.getModuleId(item.module);
857 if (shortMode) {
858 map[item.userRequest] = moduleId;
859 } else {
860 const arrayStart = [moduleId];
861 if (hasFakeMap) {
862 arrayStart.push(fakeMap[moduleId]);
863 }
864 map[item.userRequest] = arrayStart.concat(
865 item.chunks.map(chunk => chunk.id)
866 );
867 }
868 }
869
870 const chunksStartPosition = hasFakeMap ? 2 : 1;
871 const requestPrefix = hasNoChunk
872 ? "Promise.resolve()"
873 : hasMultipleOrNoChunks
874 ? `Promise.all(ids.slice(${chunksStartPosition}).map(${RuntimeGlobals.ensureChunk}))`
875 : `${RuntimeGlobals.ensureChunk}(ids[${chunksStartPosition}])`;
876 const returnModuleObject = this.getReturnModuleObjectSource(
877 fakeMap,
878 true,
879 shortMode ? "invalid" : "ids[1]"
880 );
881
882 const webpackAsyncContext =
883 requestPrefix === "Promise.resolve()"
884 ? `
885function webpackAsyncContext(req) {
886 return Promise.resolve().then(${arrow ? "() =>" : "function()"} {
887 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
888 var e = new Error("Cannot find module '" + req + "'");
889 e.code = 'MODULE_NOT_FOUND';
890 throw e;
891 }
892
893 ${shortMode ? "var id = map[req];" : "var ids = map[req], id = ids[0];"}
894 ${returnModuleObject}
895 });
896}`
897 : `function webpackAsyncContext(req) {
898 if(!${RuntimeGlobals.hasOwnProperty}(map, req)) {
899 return Promise.resolve().then(${arrow ? "() =>" : "function()"} {
900 var e = new Error("Cannot find module '" + req + "'");
901 e.code = 'MODULE_NOT_FOUND';
902 throw e;
903 });
904 }
905
906 var ids = map[req], id = ids[0];
907 return ${requestPrefix}.then(${arrow ? "() =>" : "function()"} {
908 ${returnModuleObject}
909 });
910}`;
911
912 return `var map = ${JSON.stringify(map, null, "\t")};
913${webpackAsyncContext}
914webpackAsyncContext.keys = ${runtimeTemplate.returningFunction(
915 "Object.keys(map)"
916 )};
917webpackAsyncContext.id = ${JSON.stringify(id)};
918module.exports = webpackAsyncContext;`;
919 }
920
921 getSourceForEmptyContext(id, runtimeTemplate) {
922 return `function webpackEmptyContext(req) {
923 var e = new Error("Cannot find module '" + req + "'");
924 e.code = 'MODULE_NOT_FOUND';
925 throw e;
926}
927webpackEmptyContext.keys = ${runtimeTemplate.returningFunction("[]")};
928webpackEmptyContext.resolve = webpackEmptyContext;
929webpackEmptyContext.id = ${JSON.stringify(id)};
930module.exports = webpackEmptyContext;`;
931 }
932
933 getSourceForEmptyAsyncContext(id, runtimeTemplate) {
934 const arrow = runtimeTemplate.supportsArrowFunction();
935 return `function webpackEmptyAsyncContext(req) {
936 // Here Promise.resolve().then() is used instead of new Promise() to prevent
937 // uncaught exception popping up in devtools
938 return Promise.resolve().then(${arrow ? "() =>" : "function()"} {
939 var e = new Error("Cannot find module '" + req + "'");
940 e.code = 'MODULE_NOT_FOUND';
941 throw e;
942 });
943}
944webpackEmptyAsyncContext.keys = ${runtimeTemplate.returningFunction("[]")};
945webpackEmptyAsyncContext.resolve = webpackEmptyAsyncContext;
946webpackEmptyAsyncContext.id = ${JSON.stringify(id)};
947module.exports = webpackEmptyAsyncContext;`;
948 }
949
950 /**
951 * @param {string} asyncMode module mode
952 * @param {CodeGenerationContext} context context info
953 * @returns {string} the source code
954 */
955 getSourceString(asyncMode, { runtimeTemplate, chunkGraph }) {
956 const id = chunkGraph.getModuleId(this);
957 if (asyncMode === "lazy") {
958 if (this.blocks && this.blocks.length > 0) {
959 return this.getLazySource(this.blocks, id, {
960 runtimeTemplate,
961 chunkGraph
962 });
963 }
964 return this.getSourceForEmptyAsyncContext(id, runtimeTemplate);
965 }
966 if (asyncMode === "eager") {
967 if (this.dependencies && this.dependencies.length > 0) {
968 return this.getEagerSource(this.dependencies, id, {
969 chunkGraph,
970 runtimeTemplate
971 });
972 }
973 return this.getSourceForEmptyAsyncContext(id, runtimeTemplate);
974 }
975 if (asyncMode === "lazy-once") {
976 const block = this.blocks[0];
977 if (block) {
978 return this.getLazyOnceSource(block, block.dependencies, id, {
979 runtimeTemplate,
980 chunkGraph
981 });
982 }
983 return this.getSourceForEmptyAsyncContext(id, runtimeTemplate);
984 }
985 if (asyncMode === "async-weak") {
986 if (this.dependencies && this.dependencies.length > 0) {
987 return this.getAsyncWeakSource(this.dependencies, id, {
988 chunkGraph,
989 runtimeTemplate
990 });
991 }
992 return this.getSourceForEmptyAsyncContext(id, runtimeTemplate);
993 }
994 if (asyncMode === "weak") {
995 if (this.dependencies && this.dependencies.length > 0) {
996 return this.getWeakSyncSource(this.dependencies, id, chunkGraph);
997 }
998 }
999 if (this.dependencies && this.dependencies.length > 0) {
1000 return this.getSyncSource(this.dependencies, id, chunkGraph);
1001 }
1002 return this.getSourceForEmptyContext(id, runtimeTemplate);
1003 }
1004
1005 getSource(sourceString) {
1006 if (this.useSourceMap || this.useSimpleSourceMap) {
1007 return new OriginalSource(sourceString, this.identifier());
1008 }
1009 return new RawSource(sourceString);
1010 }
1011
1012 /**
1013 * @param {CodeGenerationContext} context context for code generation
1014 * @returns {CodeGenerationResult} result
1015 */
1016 codeGeneration(context) {
1017 const { chunkGraph } = context;
1018 const sources = new Map();
1019 sources.set(
1020 "javascript",
1021 this.getSource(this.getSourceString(this.options.mode, context))
1022 );
1023 const set = new Set();
1024 const allDeps = /** @type {ContextElementDependency[]} */ (
1025 this.dependencies.concat(this.blocks.map(b => b.dependencies[0]))
1026 );
1027 set.add(RuntimeGlobals.module);
1028 set.add(RuntimeGlobals.hasOwnProperty);
1029 if (allDeps.length > 0) {
1030 const asyncMode = this.options.mode;
1031 set.add(RuntimeGlobals.require);
1032 if (asyncMode === "weak") {
1033 set.add(RuntimeGlobals.moduleFactories);
1034 } else if (asyncMode === "async-weak") {
1035 set.add(RuntimeGlobals.moduleFactories);
1036 set.add(RuntimeGlobals.ensureChunk);
1037 } else if (asyncMode === "lazy" || asyncMode === "lazy-once") {
1038 set.add(RuntimeGlobals.ensureChunk);
1039 }
1040 if (this.getFakeMap(allDeps, chunkGraph) !== 9) {
1041 set.add(RuntimeGlobals.createFakeNamespaceObject);
1042 }
1043 }
1044 return {
1045 sources,
1046 runtimeRequirements: set
1047 };
1048 }
1049
1050 /**
1051 * @param {string=} type the source type for which the size should be estimated
1052 * @returns {number} the estimated size of the module (must be non-zero)
1053 */
1054 size(type) {
1055 // base penalty
1056 let size = 160;
1057
1058 // if we don't have dependencies we stop here.
1059 for (const dependency of this.dependencies) {
1060 const element = /** @type {ContextElementDependency} */ (dependency);
1061 size += 5 + element.userRequest.length;
1062 }
1063 return size;
1064 }
1065
1066 serialize(context) {
1067 const { write } = context;
1068 write(this._identifier);
1069 write(this._forceBuild);
1070 super.serialize(context);
1071 }
1072
1073 deserialize(context) {
1074 const { read } = context;
1075 this._identifier = read();
1076 this._forceBuild = read();
1077 super.deserialize(context);
1078 }
1079}
1080
1081makeSerializable(ContextModule, "webpack/lib/ContextModule");
1082
1083module.exports = ContextModule;