simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

RecordIdsPlugin.js (6821B)


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