| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const { compareNumbers } = require("./util/comparators");const identifierUtils = require("./util/identifier");/** @typedef {import("./Chunk")} Chunk *//** @typedef {import("./Compiler")} Compiler *//** @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 {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;		const makePathsRelative =			identifierUtils.makePathsRelative.bindContextCache(				compiler.context,				compiler.root			);		/**		 * @param {Module} module the module		 * @returns {string} the (portable) identifier		 */		const getModuleIdentifier = module => {			if (portableIds) {				return makePathsRelative(module.identifier());			}			return module.identifier();		};		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) => {					const chunkGraph = compilation.chunkGraph;					if (!records.modules) records.modules = {};					if (!records.modules.byIdentifier) records.modules.byIdentifier = {};					/** @type {Set<number>} */					const usedIds = new Set();					for (const module of modules) {						const moduleId = chunkGraph.getModuleId(module);						if (typeof moduleId !== "number") continue;						const identifier = getModuleIdentifier(module);						records.modules.byIdentifier[identifier] = moduleId;						usedIds.add(moduleId);					}					records.modules.usedIds = Array.from(usedIds).sort(compareNumbers);				}			);			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) {						const chunkGraph = compilation.chunkGraph;						/** @type {Set<number>} */						const usedIds = new Set();						for (const module of modules) {							const moduleId = chunkGraph.getModuleId(module);							if (moduleId !== null) continue;							const identifier = getModuleIdentifier(module);							const id = records.modules.byIdentifier[identifier];							if (id === undefined) continue;							if (usedIds.has(id)) continue;							usedIds.add(id);							chunkGraph.setModuleId(module, id);						}					}					if (Array.isArray(records.modules.usedIds)) {						compilation.usedModuleIds = new Set(records.modules.usedIds);					}				}			);			/**			 * @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" &&									"start" in origin.loc								) {									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(compareNumbers);				}			);			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;							chunk.ids = [id];						}					}					if (records.chunks.bySource) {						for (const chunk of chunks) {							if (chunk.id !== null) continue;							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;								chunk.ids = [id];								break;							}						}					}					if (Array.isArray(records.chunks.usedIds)) {						compilation.usedChunkIds = new Set(records.chunks.usedIds);					}				}			);		});	}}module.exports = RecordIdsPlugin;
 |