UNPKG

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