ContextModule.js 37 KB

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