ContextModule.js 34 KB

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