ContextModule.js 32 KB

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