| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const identifierUtils = require("./util/identifier"); |
| |
| /** @typedef {import("./Compiler")} Compiler */ |
| /** @typedef {import("./Chunk")} Chunk */ |
| /** @typedef {import("./Module")} Module */ |
| |
| /** |
| * @typedef {Object} RecordsChunks |
| * @property {Record<string, number>=} byName |
| * @property {Record<string, number>=} bySource |
| * @property {number[]=} usedIds |
| */ |
| |
| /** |
| * @typedef {Object} RecordsModules |
| * @property {Record<string, number>=} byIdentifier |
| * @property {Record<string, number>=} bySource |
| * @property {Record<number, number>=} usedIds |
| */ |
| |
| /** |
| * @typedef {Object} Records |
| * @property {RecordsChunks=} chunks |
| * @property {RecordsModules=} modules |
| */ |
| |
| class RecordIdsPlugin { |
| /** |
| * @param {Object} options Options object |
| * @param {boolean=} options.portableIds true, when ids need to be portable |
| */ |
| constructor(options) { |
| this.options = options || {}; |
| } |
| |
| /** |
| * @param {Compiler} compiler the Compiler |
| * @returns {void} |
| */ |
| apply(compiler) { |
| const portableIds = this.options.portableIds; |
| compiler.hooks.compilation.tap("RecordIdsPlugin", compilation => { |
| compilation.hooks.recordModules.tap( |
| "RecordIdsPlugin", |
| /** |
| * @param {Module[]} modules the modules array |
| * @param {Records} records the records object |
| * @returns {void} |
| */ |
| (modules, records) => { |
| if (!records.modules) records.modules = {}; |
| if (!records.modules.byIdentifier) records.modules.byIdentifier = {}; |
| if (!records.modules.usedIds) records.modules.usedIds = {}; |
| for (const module of modules) { |
| if (typeof module.id !== "number") continue; |
| const identifier = portableIds |
| ? identifierUtils.makePathsRelative( |
| compiler.context, |
| module.identifier(), |
| compilation.cache |
| ) |
| : module.identifier(); |
| records.modules.byIdentifier[identifier] = module.id; |
| records.modules.usedIds[module.id] = module.id; |
| } |
| } |
| ); |
| compilation.hooks.reviveModules.tap( |
| "RecordIdsPlugin", |
| /** |
| * @param {Module[]} modules the modules array |
| * @param {Records} records the records object |
| * @returns {void} |
| */ |
| (modules, records) => { |
| if (!records.modules) return; |
| if (records.modules.byIdentifier) { |
| /** @type {Set<number>} */ |
| const usedIds = new Set(); |
| for (const module of modules) { |
| if (module.id !== null) continue; |
| const identifier = portableIds |
| ? identifierUtils.makePathsRelative( |
| compiler.context, |
| module.identifier(), |
| compilation.cache |
| ) |
| : module.identifier(); |
| const id = records.modules.byIdentifier[identifier]; |
| if (id === undefined) continue; |
| if (usedIds.has(id)) continue; |
| usedIds.add(id); |
| module.id = id; |
| } |
| } |
| if (Array.isArray(records.modules.usedIds)) { |
| compilation.usedModuleIds = new Set(records.modules.usedIds); |
| } |
| } |
| ); |
| |
| /** |
| * @param {Module} module the module |
| * @returns {string} the (portable) identifier |
| */ |
| const getModuleIdentifier = module => { |
| if (portableIds) { |
| return identifierUtils.makePathsRelative( |
| compiler.context, |
| module.identifier(), |
| compilation.cache |
| ); |
| } |
| return module.identifier(); |
| }; |
| |
| /** |
| * @param {Chunk} chunk the chunk |
| * @returns {string[]} sources of the chunk |
| */ |
| const getChunkSources = chunk => { |
| /** @type {string[]} */ |
| const sources = []; |
| for (const chunkGroup of chunk.groupsIterable) { |
| const index = chunkGroup.chunks.indexOf(chunk); |
| if (chunkGroup.name) { |
| sources.push(`${index} ${chunkGroup.name}`); |
| } else { |
| for (const origin of chunkGroup.origins) { |
| if (origin.module) { |
| if (origin.request) { |
| sources.push( |
| `${index} ${getModuleIdentifier(origin.module)} ${ |
| origin.request |
| }` |
| ); |
| } else if (typeof origin.loc === "string") { |
| sources.push( |
| `${index} ${getModuleIdentifier(origin.module)} ${ |
| origin.loc |
| }` |
| ); |
| } else if ( |
| origin.loc && |
| typeof origin.loc === "object" && |
| origin.loc.start |
| ) { |
| sources.push( |
| `${index} ${getModuleIdentifier( |
| origin.module |
| )} ${JSON.stringify(origin.loc.start)}` |
| ); |
| } |
| } |
| } |
| } |
| } |
| return sources; |
| }; |
| |
| compilation.hooks.recordChunks.tap( |
| "RecordIdsPlugin", |
| /** |
| * @param {Chunk[]} chunks the chunks array |
| * @param {Records} records the records object |
| * @returns {void} |
| */ |
| (chunks, records) => { |
| if (!records.chunks) records.chunks = {}; |
| if (!records.chunks.byName) records.chunks.byName = {}; |
| if (!records.chunks.bySource) records.chunks.bySource = {}; |
| /** @type {Set<number>} */ |
| const usedIds = new Set(); |
| for (const chunk of chunks) { |
| if (typeof chunk.id !== "number") continue; |
| const name = chunk.name; |
| if (name) records.chunks.byName[name] = chunk.id; |
| const sources = getChunkSources(chunk); |
| for (const source of sources) { |
| records.chunks.bySource[source] = chunk.id; |
| } |
| usedIds.add(chunk.id); |
| } |
| records.chunks.usedIds = Array.from(usedIds).sort(); |
| } |
| ); |
| compilation.hooks.reviveChunks.tap( |
| "RecordIdsPlugin", |
| /** |
| * @param {Chunk[]} chunks the chunks array |
| * @param {Records} records the records object |
| * @returns {void} |
| */ |
| (chunks, records) => { |
| if (!records.chunks) return; |
| /** @type {Set<number>} */ |
| const usedIds = new Set(); |
| if (records.chunks.byName) { |
| for (const chunk of chunks) { |
| if (chunk.id !== null) continue; |
| if (!chunk.name) continue; |
| const id = records.chunks.byName[chunk.name]; |
| if (id === undefined) continue; |
| if (usedIds.has(id)) continue; |
| usedIds.add(id); |
| chunk.id = id; |
| } |
| } |
| if (records.chunks.bySource) { |
| for (const chunk of chunks) { |
| const sources = getChunkSources(chunk); |
| for (const source of sources) { |
| const id = records.chunks.bySource[source]; |
| if (id === undefined) continue; |
| if (usedIds.has(id)) continue; |
| usedIds.add(id); |
| chunk.id = id; |
| break; |
| } |
| } |
| } |
| if (Array.isArray(records.chunks.usedIds)) { |
| compilation.usedChunkIds = new Set(records.chunks.usedIds); |
| } |
| } |
| ); |
| }); |
| } |
| } |
| module.exports = RecordIdsPlugin; |