RecordIdsPlugin.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { compareNumbers } = require("./util/comparators");
  7. const identifierUtils = require("./util/identifier");
  8. /** @typedef {import("./Chunk")} Chunk */
  9. /** @typedef {import("./Compiler")} Compiler */
  10. /** @typedef {import("./Module")} Module */
  11. /**
  12. * @typedef {object} RecordsChunks
  13. * @property {Record<string, number>=} byName
  14. * @property {Record<string, number>=} bySource
  15. * @property {number[]=} usedIds
  16. */
  17. /**
  18. * @typedef {object} RecordsModules
  19. * @property {Record<string, number>=} byIdentifier
  20. * @property {number[]=} usedIds
  21. */
  22. /**
  23. * @typedef {object} Records
  24. * @property {RecordsChunks=} chunks
  25. * @property {RecordsModules=} modules
  26. */
  27. /**
  28. * @typedef {object} RecordIdsPluginOptions
  29. * @property {boolean=} portableIds true, when ids need to be portable
  30. */
  31. const PLUGIN_NAME = "RecordIdsPlugin";
  32. class RecordIdsPlugin {
  33. /**
  34. * @param {RecordIdsPluginOptions=} options object
  35. */
  36. constructor(options) {
  37. this.options = options || {};
  38. }
  39. /**
  40. * @param {Compiler} compiler the Compiler
  41. * @returns {void}
  42. */
  43. apply(compiler) {
  44. const portableIds = this.options.portableIds;
  45. const makePathsRelative =
  46. identifierUtils.makePathsRelative.bindContextCache(
  47. compiler.context,
  48. compiler.root
  49. );
  50. /**
  51. * @param {Module} module the module
  52. * @returns {string} the (portable) identifier
  53. */
  54. const getModuleIdentifier = module => {
  55. if (portableIds) {
  56. return makePathsRelative(module.identifier());
  57. }
  58. return module.identifier();
  59. };
  60. compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
  61. compilation.hooks.recordModules.tap(PLUGIN_NAME, (modules, records) => {
  62. const chunkGraph = compilation.chunkGraph;
  63. if (!records.modules) records.modules = {};
  64. if (!records.modules.byIdentifier) records.modules.byIdentifier = {};
  65. /** @type {Set<number>} */
  66. const usedIds = new Set();
  67. for (const module of modules) {
  68. const moduleId = chunkGraph.getModuleId(module);
  69. if (typeof moduleId !== "number") continue;
  70. const identifier = getModuleIdentifier(module);
  71. records.modules.byIdentifier[identifier] = moduleId;
  72. usedIds.add(moduleId);
  73. }
  74. records.modules.usedIds = [...usedIds].sort(compareNumbers);
  75. });
  76. compilation.hooks.reviveModules.tap(PLUGIN_NAME, (modules, records) => {
  77. if (!records.modules) return;
  78. if (records.modules.byIdentifier) {
  79. const chunkGraph = compilation.chunkGraph;
  80. /** @type {Set<number>} */
  81. const usedIds = new Set();
  82. for (const module of modules) {
  83. const moduleId = chunkGraph.getModuleId(module);
  84. if (moduleId !== null) continue;
  85. const identifier = getModuleIdentifier(module);
  86. const id = records.modules.byIdentifier[identifier];
  87. if (id === undefined) continue;
  88. if (usedIds.has(id)) continue;
  89. usedIds.add(id);
  90. chunkGraph.setModuleId(module, id);
  91. }
  92. }
  93. if (Array.isArray(records.modules.usedIds)) {
  94. compilation.usedModuleIds = new Set(records.modules.usedIds);
  95. }
  96. });
  97. /**
  98. * @param {Chunk} chunk the chunk
  99. * @returns {string[]} sources of the chunk
  100. */
  101. const getChunkSources = chunk => {
  102. /** @type {string[]} */
  103. const sources = [];
  104. for (const chunkGroup of chunk.groupsIterable) {
  105. const index = chunkGroup.chunks.indexOf(chunk);
  106. if (chunkGroup.name) {
  107. sources.push(`${index} ${chunkGroup.name}`);
  108. } else {
  109. for (const origin of chunkGroup.origins) {
  110. if (origin.module) {
  111. if (origin.request) {
  112. sources.push(
  113. `${index} ${getModuleIdentifier(origin.module)} ${
  114. origin.request
  115. }`
  116. );
  117. } else if (typeof origin.loc === "string") {
  118. sources.push(
  119. `${index} ${getModuleIdentifier(origin.module)} ${
  120. origin.loc
  121. }`
  122. );
  123. } else if (
  124. origin.loc &&
  125. typeof origin.loc === "object" &&
  126. "start" in origin.loc
  127. ) {
  128. sources.push(
  129. `${index} ${getModuleIdentifier(
  130. origin.module
  131. )} ${JSON.stringify(origin.loc.start)}`
  132. );
  133. }
  134. }
  135. }
  136. }
  137. }
  138. return sources;
  139. };
  140. compilation.hooks.recordChunks.tap(PLUGIN_NAME, (chunks, records) => {
  141. if (!records.chunks) records.chunks = {};
  142. if (!records.chunks.byName) records.chunks.byName = {};
  143. if (!records.chunks.bySource) records.chunks.bySource = {};
  144. /** @type {Set<number>} */
  145. const usedIds = new Set();
  146. for (const chunk of chunks) {
  147. if (typeof chunk.id !== "number") continue;
  148. const name = chunk.name;
  149. if (name) records.chunks.byName[name] = chunk.id;
  150. const sources = getChunkSources(chunk);
  151. for (const source of sources) {
  152. records.chunks.bySource[source] = chunk.id;
  153. }
  154. usedIds.add(chunk.id);
  155. }
  156. records.chunks.usedIds = [...usedIds].sort(compareNumbers);
  157. });
  158. compilation.hooks.reviveChunks.tap(PLUGIN_NAME, (chunks, records) => {
  159. if (!records.chunks) return;
  160. /** @type {Set<number>} */
  161. const usedIds = new Set();
  162. if (records.chunks.byName) {
  163. for (const chunk of chunks) {
  164. if (chunk.id !== null) continue;
  165. if (!chunk.name) continue;
  166. const id = records.chunks.byName[chunk.name];
  167. if (id === undefined) continue;
  168. if (usedIds.has(id)) continue;
  169. usedIds.add(id);
  170. chunk.id = id;
  171. chunk.ids = [id];
  172. }
  173. }
  174. if (records.chunks.bySource) {
  175. for (const chunk of chunks) {
  176. if (chunk.id !== null) continue;
  177. const sources = getChunkSources(chunk);
  178. for (const source of sources) {
  179. const id = records.chunks.bySource[source];
  180. if (id === undefined) continue;
  181. if (usedIds.has(id)) continue;
  182. usedIds.add(id);
  183. chunk.id = id;
  184. chunk.ids = [id];
  185. break;
  186. }
  187. }
  188. }
  189. if (Array.isArray(records.chunks.usedIds)) {
  190. compilation.usedChunkIds = new Set(records.chunks.usedIds);
  191. }
  192. });
  193. });
  194. }
  195. }
  196. module.exports = RecordIdsPlugin;