提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1265 lines
38 KiB

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