ContextModule.js 34 KB

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