UNPKG

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