UNPKG

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