You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1396 lines
39 KiB
1396 lines
39 KiB
1 month ago
|
import process from 'node:process';
|
||
|
import sirv from 'sirv';
|
||
|
import c from 'picocolors';
|
||
|
import { debounce } from 'perfect-debounce';
|
||
|
import { dirname, resolve, isAbsolute, join } from 'node:path';
|
||
|
import { fileURLToPath } from 'node:url';
|
||
|
import { Buffer as Buffer$1 } from 'node:buffer';
|
||
|
import { createFilter } from '@rollup/pluginutils';
|
||
|
import Debug from 'debug';
|
||
|
import { parse } from 'error-stack-parser-es';
|
||
|
import fs from 'fs-extra';
|
||
|
import { createServer } from 'node:http';
|
||
|
|
||
|
const DEFAULT_TIMEOUT = 6e4;
|
||
|
function defaultSerialize(i) {
|
||
|
return i;
|
||
|
}
|
||
|
const defaultDeserialize = defaultSerialize;
|
||
|
const { clearTimeout, setTimeout: setTimeout$1 } = globalThis;
|
||
|
const random = Math.random.bind(Math);
|
||
|
function createBirpc(functions, options) {
|
||
|
const {
|
||
|
post,
|
||
|
on,
|
||
|
eventNames = [],
|
||
|
serialize = defaultSerialize,
|
||
|
deserialize = defaultDeserialize,
|
||
|
resolver,
|
||
|
timeout = DEFAULT_TIMEOUT
|
||
|
} = options;
|
||
|
const rpcPromiseMap = /* @__PURE__ */ new Map();
|
||
|
let _promise;
|
||
|
const rpc = new Proxy({}, {
|
||
|
get(_, method) {
|
||
|
if (method === "$functions")
|
||
|
return functions;
|
||
|
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();
|
||
|
let timeoutId;
|
||
|
if (timeout >= 0) {
|
||
|
timeoutId = setTimeout$1(() => {
|
||
|
reject(new Error(`[birpc] timeout on calling "${method}"`));
|
||
|
rpcPromiseMap.delete(id);
|
||
|
}, timeout).unref?.();
|
||
|
}
|
||
|
rpcPromiseMap.set(id, { resolve, reject, timeoutId });
|
||
|
post(serialize({ m: method, a: args, i: id, t: "q" }));
|
||
|
});
|
||
|
};
|
||
|
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;
|
||
|
const fn = resolver ? resolver(method, functions[method]) : functions[method];
|
||
|
if (!fn) {
|
||
|
error = new Error(`[birpc] function "${method}" not found`);
|
||
|
} else {
|
||
|
try {
|
||
|
result = await fn.apply(rpc, args);
|
||
|
} catch (e) {
|
||
|
error = e;
|
||
|
}
|
||
|
}
|
||
|
if (msg.i) {
|
||
|
if (error && options.onError)
|
||
|
options.onError(error, method, args);
|
||
|
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) {
|
||
|
clearTimeout(promise.timeoutId);
|
||
|
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 callbacks = client.map((c) => c[method]);
|
||
|
const sendCall = (...args) => {
|
||
|
return Promise.all(callbacks.map((i) => i(...args)));
|
||
|
};
|
||
|
sendCall.asEvent = (...args) => {
|
||
|
callbacks.map((i) => i.asEvent(...args));
|
||
|
};
|
||
|
return sendCall;
|
||
|
}
|
||
|
});
|
||
|
function updateChannels(fn) {
|
||
|
const channels2 = getChannels();
|
||
|
fn?.(channels2);
|
||
|
return getClients(channels2);
|
||
|
}
|
||
|
getClients();
|
||
|
return {
|
||
|
get clients() {
|
||
|
return getClients();
|
||
|
},
|
||
|
functions,
|
||
|
updateChannels,
|
||
|
broadcast: broadcastProxy,
|
||
|
/**
|
||
|
* @deprecated use `broadcast`
|
||
|
*/
|
||
|
// @ts-expect-error deprecated
|
||
|
boardcast: broadcastProxy
|
||
|
};
|
||
|
}
|
||
|
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
||
|
function nanoid(size = 21) {
|
||
|
let id = "";
|
||
|
let i = size;
|
||
|
while (i--)
|
||
|
id += urlAlphabet[random() * 64 | 0];
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
function createRPCServer(name, ws, functions, options = {}) {
|
||
|
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);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
),
|
||
|
options
|
||
|
);
|
||
|
ws.on("connection", () => {
|
||
|
group.updateChannels();
|
||
|
});
|
||
|
return group.broadcast;
|
||
|
}
|
||
|
|
||
|
const DIR_DIST = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
||
|
const DIR_CLIENT = resolve(DIR_DIST, "../dist/client");
|
||
|
|
||
|
const DUMMY_LOAD_PLUGIN_NAME = "__load__";
|
||
|
|
||
|
var __defProp$1 = Object.defineProperty;
|
||
|
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||
|
var __publicField$1 = (obj, key, value) => {
|
||
|
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||
|
return value;
|
||
|
};
|
||
|
class Recorder {
|
||
|
constructor(context) {
|
||
|
this.context = context;
|
||
|
__publicField$1(this, "transform", {});
|
||
|
__publicField$1(this, "resolveId", {});
|
||
|
__publicField$1(this, "transformCounter", {});
|
||
|
this.context = context;
|
||
|
}
|
||
|
recordTransform(id, info, preTransformCode) {
|
||
|
id = this.context.normalizeId(id);
|
||
|
if (!this.transform[id] || !this.transform[id].some((tr) => tr.result)) {
|
||
|
this.transform[id] = [{
|
||
|
name: DUMMY_LOAD_PLUGIN_NAME,
|
||
|
result: preTransformCode,
|
||
|
start: info.start,
|
||
|
end: info.start,
|
||
|
sourcemaps: info.sourcemaps
|
||
|
}];
|
||
|
this.transformCounter[id] = (this.transformCounter[id] || 0) + 1;
|
||
|
}
|
||
|
this.transform[id].push(info);
|
||
|
}
|
||
|
recordLoad(id, info) {
|
||
|
id = this.context.normalizeId(id);
|
||
|
this.transform[id] = [info];
|
||
|
this.transformCounter[id] = (this.transformCounter[id] || 0) + 1;
|
||
|
}
|
||
|
recordResolveId(id, info) {
|
||
|
id = this.context.normalizeId(id);
|
||
|
if (!this.resolveId[id])
|
||
|
this.resolveId[id] = [];
|
||
|
this.resolveId[id].push(info);
|
||
|
}
|
||
|
invalidate(id) {
|
||
|
id = this.context.normalizeId(id);
|
||
|
delete this.transform[id];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function openBrowser(address) {
|
||
|
await import('open').then((r) => r.default(address, { newInstance: true })).catch(() => {
|
||
|
});
|
||
|
}
|
||
|
function removeVersionQuery(url) {
|
||
|
if (url.includes("v=")) {
|
||
|
return url.replace(/&v=\w+/, "").replace(/\?v=\w+/, "?").replace(/\?$/, "");
|
||
|
}
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
var __defProp = Object.defineProperty;
|
||
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||
|
var __publicField = (obj, key, value) => {
|
||
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||
|
return value;
|
||
|
};
|
||
|
class ViteInspectContext {
|
||
|
constructor(options) {
|
||
|
this.options = options;
|
||
|
__publicField(this, "filter");
|
||
|
__publicField(this, "config");
|
||
|
__publicField(this, "recorderClient");
|
||
|
__publicField(this, "recorderServer");
|
||
|
this.filter = createFilter(options.include, options.exclude);
|
||
|
this.recorderClient = new Recorder(this);
|
||
|
this.recorderServer = new Recorder(this);
|
||
|
}
|
||
|
normalizeId(id) {
|
||
|
if (this.options.removeVersionQuery !== false)
|
||
|
return removeVersionQuery(id);
|
||
|
return id;
|
||
|
}
|
||
|
getRecorder(ssr) {
|
||
|
return ssr ? this.recorderServer : this.recorderClient;
|
||
|
}
|
||
|
resolveId(id = "", ssr = false) {
|
||
|
if (id.startsWith("./"))
|
||
|
id = resolve(this.config.root, id).replace(/\\/g, "/");
|
||
|
return this.resolveIdRecursive(id, ssr);
|
||
|
}
|
||
|
resolveIdRecursive(id, ssr = false) {
|
||
|
const rec = this.getRecorder(ssr);
|
||
|
const resolved = rec.resolveId[id]?.[0]?.result;
|
||
|
return resolved ? this.resolveIdRecursive(resolved, ssr) : id;
|
||
|
}
|
||
|
getList(server) {
|
||
|
const isVirtual = (pluginName) => pluginName !== DUMMY_LOAD_PLUGIN_NAME;
|
||
|
const getDeps = (id) => Array.from(server.moduleGraph.getModuleById(id)?.importedModules || []).map((i) => i.id || "").filter(Boolean);
|
||
|
return {
|
||
|
root: this.config.root,
|
||
|
modules: this.getModulesInfo(this.recorderClient, getDeps, isVirtual),
|
||
|
ssrModules: this.getModulesInfo(this.recorderServer, getDeps, isVirtual)
|
||
|
};
|
||
|
}
|
||
|
getModulesInfo(recorder, getDeps, isVirtual) {
|
||
|
function transformIdMap(recorder2) {
|
||
|
return Object.values(recorder2.resolveId).reduce((map, ids2) => {
|
||
|
ids2.forEach((id) => {
|
||
|
var _a;
|
||
|
map[_a = id.result] ?? (map[_a] = []);
|
||
|
map[id.result].push(id);
|
||
|
});
|
||
|
return map;
|
||
|
}, {});
|
||
|
}
|
||
|
const transformedIdMap = transformIdMap(recorder);
|
||
|
const ids = new Set(Object.keys(recorder.transform).concat(Object.keys(transformedIdMap)));
|
||
|
return Array.from(ids).sort().map((id) => {
|
||
|
let totalTime = 0;
|
||
|
const plugins = (recorder.transform[id] || []).filter((tr) => tr.result).map((transItem) => {
|
||
|
const delta = transItem.end - transItem.start;
|
||
|
totalTime += delta;
|
||
|
return { name: transItem.name, transform: delta };
|
||
|
}).concat(
|
||
|
// @ts-expect-error transform is optional
|
||
|
(transformedIdMap[id] || []).map((idItem) => {
|
||
|
return { name: idItem.name, resolveId: idItem.end - idItem.start };
|
||
|
})
|
||
|
);
|
||
|
function getSize(str) {
|
||
|
if (!str)
|
||
|
return 0;
|
||
|
return Buffer$1.byteLength(str, "utf8");
|
||
|
}
|
||
|
return {
|
||
|
id,
|
||
|
deps: getDeps ? getDeps(id) : [],
|
||
|
plugins,
|
||
|
virtual: isVirtual(plugins[0]?.name || "", recorder.transform[id]?.[0].name || ""),
|
||
|
totalTime,
|
||
|
invokeCount: recorder.transformCounter?.[id] || 0,
|
||
|
sourceSize: getSize(recorder.transform[id]?.[0]?.result),
|
||
|
distSize: getSize(recorder.transform[id]?.[recorder.transform[id].length - 1]?.result)
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
getPluginMetrics(ssr = false) {
|
||
|
const map = {};
|
||
|
const defaultMetricInfo = () => ({
|
||
|
transform: { invokeCount: 0, totalTime: 0 },
|
||
|
resolveId: { invokeCount: 0, totalTime: 0 }
|
||
|
});
|
||
|
this.config.plugins.forEach((i) => {
|
||
|
map[i.name] = {
|
||
|
...defaultMetricInfo(),
|
||
|
name: i.name,
|
||
|
enforce: i.enforce
|
||
|
};
|
||
|
});
|
||
|
const recorder = this.getRecorder(ssr);
|
||
|
Object.values(recorder.transform).forEach((transformInfos) => {
|
||
|
transformInfos.forEach(({ name, start, end }) => {
|
||
|
if (name === DUMMY_LOAD_PLUGIN_NAME)
|
||
|
return;
|
||
|
if (!map[name])
|
||
|
map[name] = { ...defaultMetricInfo(), name };
|
||
|
map[name].transform.totalTime += end - start;
|
||
|
map[name].transform.invokeCount += 1;
|
||
|
});
|
||
|
});
|
||
|
Object.values(recorder.resolveId).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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const debug = Debug("vite-plugin-inspect");
|
||
|
function hijackHook(plugin, name, wrapper) {
|
||
|
if (!plugin[name])
|
||
|
return;
|
||
|
debug(`hijack plugin "${name}"`, plugin.name);
|
||
|
let order = plugin.order || plugin.enforce || "normal";
|
||
|
const hook = plugin[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;
|
||
|
plugin[name] = function(...args) {
|
||
|
return wrapper(oldFn, this, args, order);
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
function hijackPlugin(plugin, ctx) {
|
||
|
hijackHook(plugin, "transform", async (fn, context, args, order) => {
|
||
|
const code = args[0];
|
||
|
const id = args[1];
|
||
|
const ssr = args[2]?.ssr;
|
||
|
let _result;
|
||
|
let error;
|
||
|
const start = Date.now();
|
||
|
try {
|
||
|
_result = await fn.apply(context, args);
|
||
|
} catch (_err) {
|
||
|
error = _err;
|
||
|
}
|
||
|
const end = Date.now();
|
||
|
const result = error ? "[Error]" : typeof _result === "string" ? _result : _result?.code;
|
||
|
if (ctx.filter(id)) {
|
||
|
const sourcemaps = typeof _result === "string" ? null : _result?.map;
|
||
|
const rec = ctx.getRecorder(ssr);
|
||
|
rec.recordTransform(id, {
|
||
|
name: plugin.name,
|
||
|
result,
|
||
|
start,
|
||
|
end,
|
||
|
order,
|
||
|
sourcemaps,
|
||
|
error: error ? parseError(error) : void 0
|
||
|
}, code);
|
||
|
}
|
||
|
if (error)
|
||
|
throw error;
|
||
|
return _result;
|
||
|
});
|
||
|
hijackHook(plugin, "load", async (fn, context, args) => {
|
||
|
const id = args[0];
|
||
|
const ssr = args[1]?.ssr;
|
||
|
let _result;
|
||
|
let error;
|
||
|
const start = Date.now();
|
||
|
try {
|
||
|
_result = await fn.apply(context, args);
|
||
|
} catch (err) {
|
||
|
error = err;
|
||
|
}
|
||
|
const end = Date.now();
|
||
|
const result = error ? "[Error]" : typeof _result === "string" ? _result : _result?.code;
|
||
|
const sourcemaps = typeof _result === "string" ? null : _result?.map;
|
||
|
if (result) {
|
||
|
ctx.getRecorder(ssr).recordLoad(id, {
|
||
|
name: plugin.name,
|
||
|
result,
|
||
|
start,
|
||
|
end,
|
||
|
sourcemaps,
|
||
|
error: error ? parseError(error) : void 0
|
||
|
});
|
||
|
}
|
||
|
if (error)
|
||
|
throw error;
|
||
|
return _result;
|
||
|
});
|
||
|
hijackHook(plugin, "resolveId", async (fn, context, args) => {
|
||
|
const id = args[0];
|
||
|
const ssr = args[2]?.ssr;
|
||
|
let _result;
|
||
|
let error;
|
||
|
const start = Date.now();
|
||
|
try {
|
||
|
_result = await fn.apply(context, args);
|
||
|
} catch (err) {
|
||
|
error = err;
|
||
|
}
|
||
|
const end = Date.now();
|
||
|
if (!ctx.filter(id)) {
|
||
|
if (error)
|
||
|
throw error;
|
||
|
return _result;
|
||
|
}
|
||
|
const result = error ? stringifyError(error) : typeof _result === "object" ? _result?.id : _result;
|
||
|
if (result && result !== id) {
|
||
|
ctx.getRecorder(ssr).recordResolveId(id, {
|
||
|
name: plugin.name,
|
||
|
result,
|
||
|
start,
|
||
|
end,
|
||
|
error
|
||
|
});
|
||
|
}
|
||
|
if (error)
|
||
|
throw error;
|
||
|
return _result;
|
||
|
});
|
||
|
}
|
||
|
function parseError(error) {
|
||
|
const stack = parse(error, { allowEmpty: true });
|
||
|
const message = error.message || String(error);
|
||
|
return {
|
||
|
message,
|
||
|
stack,
|
||
|
raw: error
|
||
|
};
|
||
|
}
|
||
|
function stringifyError(err) {
|
||
|
return String(err.stack ? err.stack : err);
|
||
|
}
|
||
|
|
||
|
const defaults = Object.freeze({
|
||
|
ignoreUnknown: false,
|
||
|
respectType: false,
|
||
|
respectFunctionNames: false,
|
||
|
respectFunctionProperties: false,
|
||
|
unorderedObjects: true,
|
||
|
unorderedArrays: false,
|
||
|
unorderedSets: false,
|
||
|
excludeKeys: void 0,
|
||
|
excludeValues: void 0,
|
||
|
replacer: void 0
|
||
|
});
|
||
|
function objectHash(object, options) {
|
||
|
if (options) {
|
||
|
options = { ...defaults, ...options };
|
||
|
} else {
|
||
|
options = defaults;
|
||
|
}
|
||
|
const hasher = createHasher(options);
|
||
|
hasher.dispatch(object);
|
||
|
return hasher.toString();
|
||
|
}
|
||
|
const defaultPrototypesKeys = Object.freeze([
|
||
|
"prototype",
|
||
|
"__proto__",
|
||
|
"constructor"
|
||
|
]);
|
||
|
function createHasher(options) {
|
||
|
let buff = "";
|
||
|
let context = /* @__PURE__ */ new Map();
|
||
|
const write = (str) => {
|
||
|
buff += str;
|
||
|
};
|
||
|
return {
|
||
|
toString() {
|
||
|
return buff;
|
||
|
},
|
||
|
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) {
|
||
|
if (object && typeof object.toJSON === "function") {
|
||
|
return this.object(object.toJSON());
|
||
|
}
|
||
|
const objString = Object.prototype.toString.call(object);
|
||
|
let objType = "";
|
||
|
const objectLength = objString.length;
|
||
|
if (objectLength < 10) {
|
||
|
objType = "unknown:[" + objString + "]";
|
||
|
} else {
|
||
|
objType = objString.slice(8, objectLength - 1);
|
||
|
}
|
||
|
objType = objType.toLowerCase();
|
||
|
let objectNumber = null;
|
||
|
if ((objectNumber = context.get(object)) === void 0) {
|
||
|
context.set(object, context.size);
|
||
|
} else {
|
||
|
return this.dispatch("[CIRCULAR:" + objectNumber + "]");
|
||
|
}
|
||
|
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) {
|
||
|
this.unkown(object, objType);
|
||
|
}
|
||
|
} else {
|
||
|
let keys = Object.keys(object);
|
||
|
if (options.unorderedObjects) {
|
||
|
keys = keys.sort();
|
||
|
}
|
||
|
let extraKeys = [];
|
||
|
if (options.respectType !== false && !isNativeFunction(object)) {
|
||
|
extraKeys = defaultPrototypesKeys;
|
||
|
}
|
||
|
if (options.excludeKeys) {
|
||
|
keys = keys.filter((key) => {
|
||
|
return !options.excludeKeys(key);
|
||
|
});
|
||
|
extraKeys = extraKeys.filter((key) => {
|
||
|
return !options.excludeKeys(key);
|
||
|
});
|
||
|
}
|
||
|
write("object:" + (keys.length + extraKeys.length) + ":");
|
||
|
const dispatchForKey = (key) => {
|
||
|
this.dispatch(key);
|
||
|
write(":");
|
||
|
if (!options.excludeValues) {
|
||
|
this.dispatch(object[key]);
|
||
|
}
|
||
|
write(",");
|
||
|
};
|
||
|
for (const key of keys) {
|
||
|
dispatchForKey(key);
|
||
|
}
|
||
|
for (const key of extraKeys) {
|
||
|
dispatchForKey(key);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
array(arr, unordered) {
|
||
|
unordered = unordered === void 0 ? options.unorderedArrays !== false : unordered;
|
||
|
write("array:" + arr.length + ":");
|
||
|
if (!unordered || arr.length <= 1) {
|
||
|
for (const entry of arr) {
|
||
|
this.dispatch(entry);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
const contextAdditions = /* @__PURE__ */ new Map();
|
||
|
const entries = arr.map((entry) => {
|
||
|
const hasher = createHasher(options);
|
||
|
hasher.dispatch(entry);
|
||
|
for (const [key, value] of hasher.getContext()) {
|
||
|
contextAdditions.set(key, value);
|
||
|
}
|
||
|
return hasher.toString();
|
||
|
});
|
||
|
context = contextAdditions;
|
||
|
entries.sort();
|
||
|
return this.array(entries, false);
|
||
|
},
|
||
|
date(date) {
|
||
|
return write("date:" + date.toJSON());
|
||
|
},
|
||
|
symbol(sym) {
|
||
|
return write("symbol:" + sym.toString());
|
||
|
},
|
||
|
unkown(value, type) {
|
||
|
write(type);
|
||
|
if (!value) {
|
||
|
return;
|
||
|
}
|
||
|
write(":");
|
||
|
if (value && typeof value.entries === "function") {
|
||
|
return this.array(
|
||
|
Array.from(value.entries()),
|
||
|
true
|
||
|
/* ordered */
|
||
|
);
|
||
|
}
|
||
|
},
|
||
|
error(err) {
|
||
|
return write("error:" + err.toString());
|
||
|
},
|
||
|
boolean(bool) {
|
||
|
return write("bool:" + bool);
|
||
|
},
|
||
|
string(string) {
|
||
|
write("string:" + string.length + ":");
|
||
|
write(string);
|
||
|
},
|
||
|
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);
|
||
|
},
|
||
|
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());
|
||
|
},
|
||
|
/* Node.js standard native objects */
|
||
|
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");
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
const nativeFunc = "[native code] }";
|
||
|
const nativeFuncLength = nativeFunc.length;
|
||
|
function isNativeFunction(f) {
|
||
|
if (typeof f !== "function") {
|
||
|
return false;
|
||
|
}
|
||
|
return Function.prototype.toString.call(f).slice(-nativeFuncLength) === nativeFunc;
|
||
|
}
|
||
|
|
||
|
class WordArray {
|
||
|
constructor(words, sigBytes) {
|
||
|
words = this.words = words || [];
|
||
|
this.sigBytes = sigBytes === void 0 ? words.length * 4 : sigBytes;
|
||
|
}
|
||
|
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._data = new WordArray();
|
||
|
this._nDataBytes = 0;
|
||
|
this._minBufferSize = 0;
|
||
|
this.blockSize = 512 / 32;
|
||
|
}
|
||
|
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;
|
||
|
}
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
|
_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(...arguments);
|
||
|
this._hash = new WordArray([...H]);
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
async function generateBuild(ctx, config) {
|
||
|
const {
|
||
|
outputDir = ".vite-inspect"
|
||
|
} = ctx.options;
|
||
|
const targetDir = isAbsolute(outputDir) ? outputDir : resolve(config.root, outputDir);
|
||
|
const reportsDir = join(targetDir, "reports");
|
||
|
await fs.emptyDir(targetDir);
|
||
|
await fs.ensureDir(reportsDir);
|
||
|
await fs.copy(DIR_CLIENT, targetDir);
|
||
|
const isVirtual = (pluginName, transformName) => pluginName !== DUMMY_LOAD_PLUGIN_NAME && transformName !== "vite:load-fallback";
|
||
|
function list() {
|
||
|
return {
|
||
|
root: config.root,
|
||
|
modules: ctx.getModulesInfo(ctx.recorderClient, null, isVirtual),
|
||
|
ssrModules: ctx.getModulesInfo(ctx.recorderServer, null, isVirtual)
|
||
|
};
|
||
|
}
|
||
|
async function dumpModuleInfo(dir, recorder, ssr = false) {
|
||
|
await fs.ensureDir(dir);
|
||
|
return Promise.all(
|
||
|
Object.entries(recorder.transform).map(
|
||
|
([id, info]) => fs.writeJSON(
|
||
|
join(dir, `${hash(id)}.json`),
|
||
|
{
|
||
|
resolvedId: ctx.resolveId(id, ssr),
|
||
|
transforms: info
|
||
|
},
|
||
|
{ spaces: 2 }
|
||
|
)
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
await Promise.all([
|
||
|
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"'
|
||
|
)
|
||
|
),
|
||
|
fs.writeJSON(
|
||
|
join(reportsDir, "list.json"),
|
||
|
list(),
|
||
|
{ spaces: 2 }
|
||
|
),
|
||
|
fs.writeJSON(
|
||
|
join(reportsDir, "metrics.json"),
|
||
|
ctx.getPluginMetrics(false),
|
||
|
{ spaces: 2 }
|
||
|
),
|
||
|
fs.writeJSON(
|
||
|
join(reportsDir, "metrics-ssr.json"),
|
||
|
ctx.getPluginMetrics(true),
|
||
|
{ spaces: 2 }
|
||
|
),
|
||
|
dumpModuleInfo(join(reportsDir, "transform"), ctx.recorderClient),
|
||
|
dumpModuleInfo(join(reportsDir, "transform-ssr"), ctx.recorderServer, true)
|
||
|
]);
|
||
|
return targetDir;
|
||
|
}
|
||
|
|
||
|
function createPreviewServer(staticPath) {
|
||
|
const server = createServer();
|
||
|
const statics = sirv(staticPath);
|
||
|
server.on("request", (req, res) => {
|
||
|
statics(req, res, () => {
|
||
|
res.statusCode = 404;
|
||
|
res.end("File not found");
|
||
|
});
|
||
|
});
|
||
|
server.listen(0, () => {
|
||
|
const { port } = server.address();
|
||
|
const url = `http://localhost:${port}`;
|
||
|
console.log(` ${c.green("\u279C")} ${c.bold("Inspect Preview Started")}: ${url}`);
|
||
|
openBrowser(url);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
const NAME = "vite-plugin-inspect";
|
||
|
const isCI = !!process.env.CI;
|
||
|
function PluginInspect(options = {}) {
|
||
|
const {
|
||
|
dev = true,
|
||
|
build = false,
|
||
|
silent = false,
|
||
|
open: _open = false
|
||
|
} = options;
|
||
|
if (!dev && !build) {
|
||
|
return {
|
||
|
name: NAME
|
||
|
};
|
||
|
}
|
||
|
const ctx = new ViteInspectContext(options);
|
||
|
const timestampRE = /\bt=\d{13}&?\b/;
|
||
|
const trailingSeparatorRE = /[?&]$/;
|
||
|
let config;
|
||
|
const serverPerf = {
|
||
|
middleware: {}
|
||
|
};
|
||
|
function setupMiddlewarePerf(middlewares) {
|
||
|
let firstMiddlewareIndex = -1;
|
||
|
middlewares.forEach((middleware, index) => {
|
||
|
const { handle: originalHandle } = middleware;
|
||
|
if (typeof originalHandle !== "function" || !originalHandle.name)
|
||
|
return middleware;
|
||
|
middleware.handle = (...middlewareArgs) => {
|
||
|
var _a;
|
||
|
let req;
|
||
|
if (middlewareArgs.length === 4)
|
||
|
[, req] = middlewareArgs;
|
||
|
else
|
||
|
[req] = middlewareArgs;
|
||
|
const start = Date.now();
|
||
|
const url = req.url?.replace(timestampRE, "").replace(trailingSeparatorRE, "");
|
||
|
(_a = serverPerf.middleware)[url] ?? (_a[url] = []);
|
||
|
if (firstMiddlewareIndex < 0)
|
||
|
firstMiddlewareIndex = index;
|
||
|
if (index === firstMiddlewareIndex)
|
||
|
serverPerf.middleware[url] = [];
|
||
|
const result = originalHandle(...middlewareArgs);
|
||
|
Promise.resolve(result).then(() => {
|
||
|
const total = Date.now() - start;
|
||
|
const metrics = serverPerf.middleware[url];
|
||
|
serverPerf.middleware[url].push({
|
||
|
self: metrics.length ? Math.max(total - metrics[metrics.length - 1].total, 0) : total,
|
||
|
total,
|
||
|
name: originalHandle.name
|
||
|
});
|
||
|
});
|
||
|
return result;
|
||
|
};
|
||
|
Object.defineProperty(middleware.handle, "name", {
|
||
|
value: originalHandle.name,
|
||
|
configurable: true,
|
||
|
enumerable: true
|
||
|
});
|
||
|
return middleware;
|
||
|
});
|
||
|
}
|
||
|
function configureServer(server) {
|
||
|
const _invalidateModule = server.moduleGraph.invalidateModule;
|
||
|
server.moduleGraph.invalidateModule = function(...args) {
|
||
|
const mod = args[0];
|
||
|
if (mod?.id) {
|
||
|
ctx.recorderClient.invalidate(mod.id);
|
||
|
ctx.recorderServer.invalidate(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: () => ctx.getList(server),
|
||
|
getIdInfo,
|
||
|
getPluginMetrics: (ssr = false) => ctx.getPluginMetrics(ssr),
|
||
|
getServerMetrics,
|
||
|
resolveId: (id, ssr = false) => ctx.resolveId(id, ssr),
|
||
|
clear: clearId,
|
||
|
moduleUpdated: () => {
|
||
|
}
|
||
|
};
|
||
|
const rpcServer = createRPCServer("vite-plugin-inspect", server.ws, rpcFunctions);
|
||
|
const debouncedModuleUpdated = debounce(() => {
|
||
|
rpcServer.moduleUpdated.asEvent();
|
||
|
}, 100);
|
||
|
server.middlewares.use((req, res, next) => {
|
||
|
debouncedModuleUpdated();
|
||
|
next();
|
||
|
});
|
||
|
function getServerMetrics() {
|
||
|
return serverPerf || {};
|
||
|
}
|
||
|
async function getIdInfo(id, ssr = false, clear = false) {
|
||
|
if (clear) {
|
||
|
clearId(id, ssr);
|
||
|
try {
|
||
|
await server.transformRequest(id, { ssr });
|
||
|
} catch {
|
||
|
}
|
||
|
}
|
||
|
const resolvedId = ctx.resolveId(id, ssr);
|
||
|
const recorder = ctx.getRecorder(ssr);
|
||
|
return {
|
||
|
resolvedId,
|
||
|
transforms: recorder.transform[resolvedId] || []
|
||
|
};
|
||
|
}
|
||
|
function clearId(_id, ssr = false) {
|
||
|
const id = ctx.resolveId(_id);
|
||
|
if (id) {
|
||
|
const mod = server.moduleGraph.getModuleById(id);
|
||
|
if (mod)
|
||
|
server.moduleGraph.invalidateModule(mod);
|
||
|
ctx.getRecorder(ssr).invalidate(id);
|
||
|
}
|
||
|
}
|
||
|
const _print = server.printUrls;
|
||
|
server.printUrls = () => {
|
||
|
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) {
|
||
|
config.logger.warn(`Parse resolved url failed: ${error}`);
|
||
|
}
|
||
|
}
|
||
|
_print();
|
||
|
if (!silent) {
|
||
|
const colorUrl = (url2) => c.green(url2.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`));
|
||
|
config.logger.info(` ${c.green("\u279C")} ${c.bold("Inspect")}: ${colorUrl(`${host}${base}__inspect/`)}`);
|
||
|
}
|
||
|
if (_open && !isCI) {
|
||
|
setTimeout(() => {
|
||
|
openBrowser(`${host}${base}__inspect/`);
|
||
|
}, 500);
|
||
|
}
|
||
|
};
|
||
|
return rpcFunctions;
|
||
|
}
|
||
|
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 = ctx.config = _config;
|
||
|
config.plugins.forEach((plugin2) => hijackPlugin(plugin2, ctx));
|
||
|
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();
|
||
|
if (result && result !== id) {
|
||
|
const pluginName = aliasOnly ? "alias" : "vite:resolve (+alias)";
|
||
|
ctx.getRecorder(ssr).recordResolveId(id, { name: pluginName, result, start, end });
|
||
|
}
|
||
|
return result;
|
||
|
};
|
||
|
};
|
||
|
},
|
||
|
configureServer(server) {
|
||
|
const rpc = configureServer(server);
|
||
|
plugin.api = {
|
||
|
rpc
|
||
|
};
|
||
|
return () => {
|
||
|
setupMiddlewarePerf(server.middlewares.stack);
|
||
|
};
|
||
|
},
|
||
|
load: {
|
||
|
order: "pre",
|
||
|
handler(id, { ssr } = {}) {
|
||
|
ctx.getRecorder(ssr).invalidate(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(ctx, config);
|
||
|
config.logger.info(`${c.green("Inspect report generated at")} ${c.dim(`${dir}`)}`);
|
||
|
if (_open && !isCI)
|
||
|
createPreviewServer(dir);
|
||
|
}
|
||
|
};
|
||
|
return plugin;
|
||
|
}
|
||
|
PluginInspect.getViteInspectAPI = function(plugins) {
|
||
|
return plugins.find((p) => p.name === NAME)?.api;
|
||
|
};
|
||
|
|
||
|
export { PluginInspect as default };
|