UNPKG

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