import { dirname, resolve, join } from 'node:path'; import fs from 'fs-extra'; import _debug from 'debug'; import { green, dim, bold } from 'kolorist'; import sirv from 'sirv'; import { createFilter } from '@rollup/pluginutils'; import { fileURLToPath } from 'node:url'; const DEFAULT_TIMEOUT = 6e4; const defaultSerialize = (i) => i; const defaultDeserialize = defaultSerialize; function createBirpc(functions, options) { const { post, on, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, timeout = DEFAULT_TIMEOUT } = options; const rpcPromiseMap = /* @__PURE__ */ new Map(); let _promise; const rpc = new Proxy({}, { get(_, method) { const sendEvent = (...args) => { post(serialize({ m: method, a: args, t: "q" })); }; if (eventNames.includes(method)) { sendEvent.asEvent = sendEvent; return sendEvent; } const sendCall = async (...args) => { await _promise; return new Promise((resolve, reject) => { const id = nanoid(); rpcPromiseMap.set(id, { resolve, reject }); post(serialize({ m: method, a: args, i: id, t: "q" })); if (timeout >= 0) { setTimeout(() => { reject(new Error(`[birpc] timeout on calling "${method}"`)); rpcPromiseMap.delete(id); }, timeout); } }); }; sendCall.asEvent = sendEvent; return sendCall; } }); _promise = on(async (data, ...extra) => { const msg = deserialize(data); if (msg.t === "q") { const { m: method, a: args } = msg; let result, error; try { result = await functions[method].apply(rpc, args); } catch (e) { error = e; } if (msg.i) post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra); } else { const { i: ack, r: result, e: error } = msg; const promise = rpcPromiseMap.get(ack); if (promise) { if (error) promise.reject(error); else promise.resolve(result); } rpcPromiseMap.delete(ack); } }); return rpc; } const cacheMap = /* @__PURE__ */ new WeakMap(); function cachedMap(items, fn) { return items.map((i) => { let r = cacheMap.get(i); if (!r) { r = fn(i); cacheMap.set(i, r); } return r; }); } function createBirpcGroup(functions, channels, options = {}) { const getChannels = () => typeof channels === "function" ? channels() : channels; const getClients = (channels2 = getChannels()) => cachedMap(channels2, (s) => createBirpc(functions, { ...options, ...s })); const broadcastProxy = new Proxy({}, { get(_, method) { const client = getClients(); const functions2 = client.map((c) => c[method]); const sendCall = (...args) => { return Promise.all(functions2.map((i) => i(...args))); }; sendCall.asEvent = (...args) => { functions2.map((i) => i.asEvent(...args)); }; return sendCall; } }); function updateChannels(fn) { const channels2 = getChannels(); fn?.(channels2); return getClients(channels2); } getClients(); return { get clients() { return getClients(); }, updateChannels, broadcast: broadcastProxy, /** * @deprecated use `broadcast` */ boardcast: broadcastProxy }; } const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; function nanoid(size = 21) { let id = ""; let i = size; while (i--) id += urlAlphabet[Math.random() * 64 | 0]; return id; } function createRPCServer(name, ws, functions) { const event = `${name}:rpc`; const group = createBirpcGroup( functions, () => cachedMap( Array.from(ws?.clients || []), (socket) => { return { on: (fn) => { ws.on(event, (data, source) => { if (socket === source) fn(data); }); }, post: (data) => { socket.send(event, data); } }; } ) ); ws.on("connection", () => { group.updateChannels(); }); return group.broadcast; } const defaults = { ignoreUnknown: false, respectType: false, respectFunctionNames: false, respectFunctionProperties: false, unorderedObjects: true, unorderedArrays: false, unorderedSets: false }; function objectHash(object, options = {}) { options = { ...defaults, ...options }; const hasher = createHasher(options); hasher.dispatch(object); return hasher.toString(); } function createHasher(options) { const buff = []; let context = []; const write = (str) => { buff.push(str); }; return { toString() { return buff.join(""); }, getContext() { return context; }, dispatch(value) { if (options.replacer) { value = options.replacer(value); } const type = value === null ? "null" : typeof value; return this["_" + type](value); }, _object(object) { const pattern = /\[object (.*)]/i; const objString = Object.prototype.toString.call(object); const _objType = pattern.exec(objString); const objType = _objType ? _objType[1].toLowerCase() : "unknown:[" + objString.toLowerCase() + "]"; let objectNumber = null; if ((objectNumber = context.indexOf(object)) >= 0) { return this.dispatch("[CIRCULAR:" + objectNumber + "]"); } else { context.push(object); } if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(object)) { write("buffer:"); return write(object.toString("utf8")); } if (objType !== "object" && objType !== "function" && objType !== "asyncfunction") { if (this["_" + objType]) { this["_" + objType](object); } else if (options.ignoreUnknown) { return write("[" + objType + "]"); } else { throw new Error('Unknown object type "' + objType + '"'); } } else { let keys = Object.keys(object); if (options.unorderedObjects) { keys = keys.sort(); } if (options.respectType !== false && !isNativeFunction(object)) { keys.splice(0, 0, "prototype", "__proto__", "letructor"); } if (options.excludeKeys) { keys = keys.filter(function(key) { return !options.excludeKeys(key); }); } write("object:" + keys.length + ":"); for (const key of keys) { this.dispatch(key); write(":"); if (!options.excludeValues) { this.dispatch(object[key]); } write(","); } } }, _array(arr, unordered) { unordered = typeof unordered !== "undefined" ? unordered : options.unorderedArrays !== false; write("array:" + arr.length + ":"); if (!unordered || arr.length <= 1) { for (const entry of arr) { this.dispatch(entry); } return; } const contextAdditions = []; const entries = arr.map((entry) => { const hasher = createHasher(options); hasher.dispatch(entry); contextAdditions.push(hasher.getContext()); return hasher.toString(); }); context = [...context, ...contextAdditions]; entries.sort(); return this._array(entries, false); }, _date(date) { return write("date:" + date.toJSON()); }, _symbol(sym) { return write("symbol:" + sym.toString()); }, _error(err) { return write("error:" + err.toString()); }, _boolean(bool) { return write("bool:" + bool.toString()); }, _string(string) { write("string:" + string.length + ":"); write(string.toString()); }, _function(fn) { write("fn:"); if (isNativeFunction(fn)) { this.dispatch("[native]"); } else { this.dispatch(fn.toString()); } if (options.respectFunctionNames !== false) { this.dispatch("function-name:" + String(fn.name)); } if (options.respectFunctionProperties) { this._object(fn); } }, _number(number) { return write("number:" + number.toString()); }, _xml(xml) { return write("xml:" + xml.toString()); }, _null() { return write("Null"); }, _undefined() { return write("Undefined"); }, _regexp(regex) { return write("regex:" + regex.toString()); }, _uint8array(arr) { write("uint8array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _uint8clampedarray(arr) { write("uint8clampedarray:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _int8array(arr) { write("int8array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _uint16array(arr) { write("uint16array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _int16array(arr) { write("int16array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _uint32array(arr) { write("uint32array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _int32array(arr) { write("int32array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _float32array(arr) { write("float32array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _float64array(arr) { write("float64array:"); return this.dispatch(Array.prototype.slice.call(arr)); }, _arraybuffer(arr) { write("arraybuffer:"); return this.dispatch(new Uint8Array(arr)); }, _url(url) { return write("url:" + url.toString()); }, _map(map) { write("map:"); const arr = [...map]; return this._array(arr, options.unorderedSets !== false); }, _set(set) { write("set:"); const arr = [...set]; return this._array(arr, options.unorderedSets !== false); }, _file(file) { write("file:"); return this.dispatch([file.name, file.size, file.type, file.lastModfied]); }, _blob() { if (options.ignoreUnknown) { return write("[blob]"); } throw new Error('Hashing Blob objects is currently not supported\nUse "options.replacer" or "options.ignoreUnknown"\n'); }, _domwindow() { return write("domwindow"); }, _bigint(number) { return write("bigint:" + number.toString()); }, _process() { return write("process"); }, _timer() { return write("timer"); }, _pipe() { return write("pipe"); }, _tcp() { return write("tcp"); }, _udp() { return write("udp"); }, _tty() { return write("tty"); }, _statwatcher() { return write("statwatcher"); }, _securecontext() { return write("securecontext"); }, _connection() { return write("connection"); }, _zlib() { return write("zlib"); }, _context() { return write("context"); }, _nodescript() { return write("nodescript"); }, _httpparser() { return write("httpparser"); }, _dataview() { return write("dataview"); }, _signal() { return write("signal"); }, _fsevent() { return write("fsevent"); }, _tlswrap() { return write("tlswrap"); } }; } function isNativeFunction(f) { if (typeof f !== "function") { return false; } const exp = /^function\s+\w*\s*\(\s*\)\s*{\s+\[native code]\s+}$/i; return exp.exec(Function.prototype.toString.call(f)) != null; } class WordArray { constructor(words, sigBytes) { words = this.words = words || []; this.sigBytes = sigBytes !== void 0 ? sigBytes : words.length * 4; } toString(encoder) { return (encoder || Hex).stringify(this); } concat(wordArray) { this.clamp(); if (this.sigBytes % 4) { for (let i = 0; i < wordArray.sigBytes; i++) { const thatByte = wordArray.words[i >>> 2] >>> 24 - i % 4 * 8 & 255; this.words[this.sigBytes + i >>> 2] |= thatByte << 24 - (this.sigBytes + i) % 4 * 8; } } else { for (let j = 0; j < wordArray.sigBytes; j += 4) { this.words[this.sigBytes + j >>> 2] = wordArray.words[j >>> 2]; } } this.sigBytes += wordArray.sigBytes; return this; } clamp() { this.words[this.sigBytes >>> 2] &= 4294967295 << 32 - this.sigBytes % 4 * 8; this.words.length = Math.ceil(this.sigBytes / 4); } clone() { return new WordArray([...this.words]); } } const Hex = { stringify(wordArray) { const hexChars = []; for (let i = 0; i < wordArray.sigBytes; i++) { const bite = wordArray.words[i >>> 2] >>> 24 - i % 4 * 8 & 255; hexChars.push( (bite >>> 4).toString(16), (bite & 15).toString(16) ); } return hexChars.join(""); } }; const Base64 = { stringify(wordArray) { const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const base64Chars = []; for (let i = 0; i < wordArray.sigBytes; i += 3) { const byte1 = wordArray.words[i >>> 2] >>> 24 - i % 4 * 8 & 255; const byte2 = wordArray.words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 255; const byte3 = wordArray.words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 255; const triplet = byte1 << 16 | byte2 << 8 | byte3; for (let j = 0; j < 4 && i * 8 + j * 6 < wordArray.sigBytes * 8; j++) { base64Chars.push(keyStr.charAt(triplet >>> 6 * (3 - j) & 63)); } } return base64Chars.join(""); } }; const Latin1 = { parse(latin1Str) { const latin1StrLength = latin1Str.length; const words = []; for (let i = 0; i < latin1StrLength; i++) { words[i >>> 2] |= (latin1Str.charCodeAt(i) & 255) << 24 - i % 4 * 8; } return new WordArray(words, latin1StrLength); } }; const Utf8 = { parse(utf8Str) { return Latin1.parse(unescape(encodeURIComponent(utf8Str))); } }; class BufferedBlockAlgorithm { constructor() { this._minBufferSize = 0; this.blockSize = 512 / 32; this.reset(); } reset() { this._data = new WordArray(); this._nDataBytes = 0; } _append(data) { if (typeof data === "string") { data = Utf8.parse(data); } this._data.concat(data); this._nDataBytes += data.sigBytes; } _doProcessBlock(_dataWords, _offset) { } _process(doFlush) { let processedWords; let nBlocksReady = this._data.sigBytes / (this.blockSize * 4); if (doFlush) { nBlocksReady = Math.ceil(nBlocksReady); } else { nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); } const nWordsReady = nBlocksReady * this.blockSize; const nBytesReady = Math.min(nWordsReady * 4, this._data.sigBytes); if (nWordsReady) { for (let offset = 0; offset < nWordsReady; offset += this.blockSize) { this._doProcessBlock(this._data.words, offset); } processedWords = this._data.words.splice(0, nWordsReady); this._data.sigBytes -= nBytesReady; } return new WordArray(processedWords, nBytesReady); } } class Hasher extends BufferedBlockAlgorithm { update(messageUpdate) { this._append(messageUpdate); this._process(); return this; } finalize(messageUpdate) { if (messageUpdate) { this._append(messageUpdate); } } } const H = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225]; const K = [1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998]; const W = []; class SHA256 extends Hasher { constructor() { super(); this.reset(); } reset() { super.reset(); this._hash = new WordArray([...H]); } _doProcessBlock(M, offset) { const H2 = this._hash.words; let a = H2[0]; let b = H2[1]; let c = H2[2]; let d = H2[3]; let e = H2[4]; let f = H2[5]; let g = H2[6]; let h = H2[7]; for (let i = 0; i < 64; i++) { if (i < 16) { W[i] = M[offset + i] | 0; } else { const gamma0x = W[i - 15]; const gamma0 = (gamma0x << 25 | gamma0x >>> 7) ^ (gamma0x << 14 | gamma0x >>> 18) ^ gamma0x >>> 3; const gamma1x = W[i - 2]; const gamma1 = (gamma1x << 15 | gamma1x >>> 17) ^ (gamma1x << 13 | gamma1x >>> 19) ^ gamma1x >>> 10; W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; } const ch = e & f ^ ~e & g; const maj = a & b ^ a & c ^ b & c; const sigma0 = (a << 30 | a >>> 2) ^ (a << 19 | a >>> 13) ^ (a << 10 | a >>> 22); const sigma1 = (e << 26 | e >>> 6) ^ (e << 21 | e >>> 11) ^ (e << 7 | e >>> 25); const t1 = h + sigma1 + ch + K[i] + W[i]; const t2 = sigma0 + maj; h = g; g = f; f = e; e = d + t1 | 0; d = c; c = b; b = a; a = t1 + t2 | 0; } H2[0] = H2[0] + a | 0; H2[1] = H2[1] + b | 0; H2[2] = H2[2] + c | 0; H2[3] = H2[3] + d | 0; H2[4] = H2[4] + e | 0; H2[5] = H2[5] + f | 0; H2[6] = H2[6] + g | 0; H2[7] = H2[7] + h | 0; } finalize(messageUpdate) { super.finalize(messageUpdate); const nBitsTotal = this._nDataBytes * 8; const nBitsLeft = this._data.sigBytes * 8; this._data.words[nBitsLeft >>> 5] |= 128 << 24 - nBitsLeft % 32; this._data.words[(nBitsLeft + 64 >>> 9 << 4) + 14] = Math.floor(nBitsTotal / 4294967296); this._data.words[(nBitsLeft + 64 >>> 9 << 4) + 15] = nBitsTotal; this._data.sigBytes = this._data.words.length * 4; this._process(); return this._hash; } } function sha256base64(message) { return new SHA256().finalize(message).toString(Base64); } function hash(object, options = {}) { const hashed = typeof object === "string" ? object : objectHash(object, options); return sha256base64(hashed).slice(0, 10); } const DIR_DIST = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url)); const DIR_CLIENT = resolve(DIR_DIST, "../dist/client"); const debug = _debug("vite-plugin-inspect"); const NAME = "vite-plugin-inspect"; const dummyLoadPluginName = "__load__"; function PluginInspect(options = {}) { const { dev = true, build = false, outputDir = ".vite-inspect" } = options; if (!dev && !build) { return { name: NAME }; } const filter = createFilter(options.include, options.exclude); let config; const transformMap = {}; const transformMapSSR = {}; const idMap = {}; const idMapSSR = {}; function hijackHook(plugin2, name, wrapper) { if (!plugin2[name]) return; debug(`hijack plugin "${name}"`, plugin2.name); let order = plugin2.order || plugin2.enforce || "normal"; const hook = plugin2[name]; if ("handler" in hook) { const oldFn = hook.handler; order += `-${hook.order || hook.enforce || "normal"}`; hook.handler = function(...args) { return wrapper(oldFn, this, args, order); }; } else if ("transform" in hook) { const oldFn = hook.transform; order += `-${hook.order || hook.enforce || "normal"}`; hook.transform = function(...args) { return wrapper(oldFn, this, args, order); }; } else { const oldFn = hook; plugin2[name] = function(...args) { return wrapper(oldFn, this, args, order); }; } } function hijackPlugin(plugin2) { hijackHook(plugin2, "transform", async (fn, context, args, order) => { const code = args[0]; const id = args[1]; const ssr = args[2]?.ssr; const start = Date.now(); const _result = await fn.apply(context, args); const end = Date.now(); const result = typeof _result === "string" ? _result : _result?.code; const map = ssr ? transformMapSSR : transformMap; if (filter(id) && result != null) { if (!map[id]) map[id] = [{ name: dummyLoadPluginName, result: code, start, end: start }]; map[id].push({ name: plugin2.name, result, start, end, order }); } return _result; }); hijackHook(plugin2, "load", async (fn, context, args) => { const id = args[0]; const ssr = args[1]?.ssr; const start = Date.now(); const _result = await fn.apply(context, args); const end = Date.now(); const result = typeof _result === "string" ? _result : _result?.code; const map = ssr ? transformMapSSR : transformMap; if (filter(id) && result != null) map[id] = [{ name: plugin2.name, result, start, end }]; return _result; }); hijackHook(plugin2, "resolveId", async (fn, context, args) => { const id = args[0]; const ssr = args[2]?.ssr; const start = Date.now(); const _result = await fn.apply(context, args); const end = Date.now(); const result = typeof _result === "object" ? _result?.id : _result; const map = ssr ? idMapSSR : idMap; if (result && result !== id) { if (!map[id]) map[id] = []; map[id].push({ name: plugin2.name, result, start, end }); } return _result; }); } function resolveId(id = "", ssr = false) { if (id.startsWith("./")) id = resolve(config.root, id).replace(/\\/g, "/"); return resolveIdRec(id, ssr); } function resolveIdRec(id, ssr = false) { const map = ssr ? idMapSSR : idMap; return map[id]?.[0] ? resolveIdRec(map[id][0].result, ssr) : id; } function getPluginMetrics(ssr = false) { const map = {}; const defaultMetricInfo = () => ({ transform: { invokeCount: 0, totalTime: 0 }, resolveId: { invokeCount: 0, totalTime: 0 } }); config.plugins.forEach((i) => { map[i.name] = { ...defaultMetricInfo(), name: i.name, enforce: i.enforce }; }); Object.values(ssr ? transformMapSSR : transformMap).forEach((transformInfos) => { transformInfos.forEach(({ name, start, end }) => { if (name === dummyLoadPluginName) return; if (!map[name]) map[name] = { ...defaultMetricInfo(), name }; map[name].transform.totalTime += end - start; map[name].transform.invokeCount += 1; }); }); Object.values(ssr ? idMapSSR : idMap).forEach((resolveIdInfos) => { resolveIdInfos.forEach(({ name, start, end }) => { if (!map[name]) map[name] = { ...defaultMetricInfo(), name }; map[name].resolveId.totalTime += end - start; map[name].resolveId.invokeCount += 1; }); }); const metrics = Object.values(map).filter(Boolean).sort((a, b) => a.name.localeCompare(b.name)); return metrics; } function configureServer(server) { const _invalidateModule = server.moduleGraph.invalidateModule; server.moduleGraph.invalidateModule = function(...args) { const mod = args[0]; if (mod?.id) { delete transformMap[mod.id]; delete transformMapSSR[mod.id]; } return _invalidateModule.apply(this, args); }; const base = (options.base ?? server.config.base) || "/"; server.middlewares.use(`${base}__inspect`, sirv(DIR_CLIENT, { single: true, dev: true })); const rpcFunctions = { list, getIdInfo, getPluginMetrics, resolveId, clear: clearId }; createRPCServer("vite-plugin-inspect", server.ws, rpcFunctions); async function getIdInfo(id, ssr = false, clear = false) { if (clear) { clearId(id, ssr); try { await server.transformRequest(id, { ssr }); } catch { } } const resolvedId = resolveId(id, ssr); const map = ssr ? transformMapSSR : transformMap; return { resolvedId, transforms: map[resolvedId] || [] }; } function getModulesInfo(map) { return Object.keys(map).sort().map((id) => { const plugins = map[id]?.map((i) => i.name); const deps = Array.from(server.moduleGraph.getModuleById(id)?.importedModules || []).map((i) => i.id || "").filter(Boolean); return { id, plugins, deps, virtual: plugins[0] !== dummyLoadPluginName }; }); } function list() { return { root: config.root, modules: getModulesInfo(transformMap), ssrModules: getModulesInfo(transformMapSSR) }; } function clearId(_id, ssr = false) { const id = resolveId(_id); if (id) { const mod = server.moduleGraph.getModuleById(id); if (mod) server.moduleGraph.invalidateModule(mod); const map = ssr ? transformMapSSR : transformMap; delete map[id]; } } const _print = server.printUrls; server.printUrls = () => { const colorUrl = (url2) => green(url2.replace(/:(\d+)\//, (_, port) => `:${bold(port)}/`)); let host = `${config.server.https ? "https" : "http"}://localhost:${config.server.port || "80"}`; const url = server.resolvedUrls?.local[0]; if (url) { try { const u = new URL(url); host = `${u.protocol}//${u.host}`; } catch (error) { console.warn("Parse resolved url failed:", error); } } _print(); console.log(` ${green("\u279C")} ${bold("Inspect")}: ${colorUrl(`${host}${base}__inspect/`)}`); }; return rpcFunctions; } async function generateBuild() { const targetDir = join(config.root, outputDir); const reportsDir = join(targetDir, "reports"); await fs.mkdir(targetDir, { recursive: true }); await fs.copy(DIR_CLIENT, targetDir, { overwrite: true }); await fs.writeFile( join(targetDir, "index.html"), (await fs.readFile(join(targetDir, "index.html"), "utf-8")).replace( 'data-vite-inspect-mode="DEV"', 'data-vite-inspect-mode="BUILD"' ) ); await fs.rm(reportsDir, { recursive: true, force: true }); await fs.mkdir(reportsDir, { recursive: true }); function getModulesInfo(map) { return Object.keys(map).sort().map((id) => { const plugins = map[id]?.map((i) => i.name); return { id, deps: [], plugins, virtual: plugins[0] !== dummyLoadPluginName && map[id][0].name !== "vite:load-fallback" }; }); } function list() { return { root: config.root, modules: getModulesInfo(transformMap), ssrModules: getModulesInfo(transformMapSSR) }; } await fs.writeFile( join(reportsDir, "list.json"), JSON.stringify(list(), null, 2), "utf-8" ); await fs.writeFile( join(reportsDir, "metrics.json"), JSON.stringify(getPluginMetrics(false), null, 2), "utf-8" ); await fs.writeFile( join(reportsDir, "metrics-ssr.json"), JSON.stringify(getPluginMetrics(true), null, 2), "utf-8" ); async function dumpModuleInfo(dir, map, ssr = false) { await fs.ensureDir(dir); return Promise.all( Object.entries(map).map( ([id, info]) => fs.writeJSON( join(dir, `${hash(id)}.json`), { resolvedId: resolveId(id, ssr), transforms: info } ) ) ); } await dumpModuleInfo(join(reportsDir, "transform"), transformMap); await dumpModuleInfo(join(reportsDir, "transform-ssr"), transformMapSSR, true); return targetDir; } const plugin = { name: NAME, enforce: "pre", apply(_, { command }) { if (command === "serve" && dev) return true; if (command === "build" && build) return true; return false; }, configResolved(_config) { config = _config; config.plugins.forEach(hijackPlugin); const _createResolver = config.createResolver; config.createResolver = function(...args) { const _resolver = _createResolver.apply(this, args); return async function(...args2) { const id = args2[0]; const aliasOnly = args2[2]; const ssr = args2[3]; const start = Date.now(); const result = await _resolver.apply(this, args2); const end = Date.now(); const map = ssr ? idMapSSR : idMap; if (result && result !== id) { const pluginName = aliasOnly ? "alias" : "vite:resolve (+alias)"; if (!map[id]) map[id] = []; map[id].push({ name: pluginName, result, start, end }); } return result; }; }; }, configureServer(server) { const rpc = configureServer(server); plugin.api = { rpc }; }, load: { order: "pre", handler(id, { ssr } = {}) { const map = ssr ? transformMapSSR : transformMap; delete map[id]; return null; } }, handleHotUpdate({ modules, server }) { const ids = modules.map((module) => module.id); server.ws.send({ type: "custom", event: "vite-plugin-inspect:update", data: { ids } }); }, async buildEnd() { if (!build) return; const dir = await generateBuild(); console.log(green("Inspect report generated at"), dim(`${dir}`)); } }; return plugin; } PluginInspect.getViteInspectAPI = function(plugins) { return plugins.find((p) => p.name === NAME)?.api; }; export { PluginInspect as default };