UNPKG

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