vite-plugin-vue-inspector
Version:
Jump to local IDE source code when clicking Vue elements in the browser.
143 lines (142 loc) • 4.76 kB
JavaScript
//#region packages/core/src/client/record.ts
const KEY_DATA = "data-v-inspector";
const KEY_IGNORE = "data-v-inspector-ignore";
const KEY_PROPS_DATA = "__v_inspector";
const KEY_GLOBAL = "__vue_inspector_store__";
const globalStore = globalThis;
const store = globalStore[KEY_GLOBAL] ?? {
hasData: false,
vnodeToPos: /* @__PURE__ */ new WeakMap(),
fileToVNode: /* @__PURE__ */ new Map(),
posToVNode: /* @__PURE__ */ new Map()
};
if (!globalStore[KEY_GLOBAL]) Object.defineProperty(globalStore, KEY_GLOBAL, {
value: store,
configurable: true,
enumerable: false
});
function getInternalStore() {
return store;
}
function parsePositionInfo(value) {
if (typeof value !== "string") return;
const match = value.match(/^(.+):(\d+):(\d+)$/);
if (!match) return;
return [
match[1],
Number(match[2]),
Number(match[3])
];
}
function moveInspectorDataToHiddenProps(props) {
const data = props[KEY_DATA];
if (typeof data !== "string") return;
delete props[KEY_DATA];
if (KEY_PROPS_DATA in props) return;
Object.defineProperty(props, KEY_PROPS_DATA, {
value: data,
configurable: true,
enumerable: false
});
}
function recordPosition(source, line, column, node) {
if (!node || typeof node === "string" || typeof node === "number") return node;
store.hasData = true;
const props = node.props ||= {};
moveInspectorDataToHiddenProps(props);
store.vnodeToPos.set(props, [
source,
line,
column
]);
if (!store.fileToVNode.has(source)) store.fileToVNode.set(source, /* @__PURE__ */ new WeakSet());
store.fileToVNode.get(source).add(props);
if (!store.posToVNode.has(source)) store.posToVNode.set(source, /* @__PURE__ */ new Map());
const lineMap = store.posToVNode.get(source);
if (!lineMap.has(line)) lineMap.set(line, /* @__PURE__ */ new Map());
const columnMap = lineMap.get(line);
if (!columnMap.has(column)) columnMap.set(column, /* @__PURE__ */ new WeakSet());
columnMap.get(column).add(props);
return node;
}
function getPositionFromVNode(node) {
const props = node?.props;
if (!props) return;
return store.vnodeToPos.get(props) ?? parsePositionInfo(props[KEY_PROPS_DATA]);
}
function getComponentPositionFromElement(el) {
const vnode = el.__vnode?.ctx?.vnode;
if (!vnode || vnode.el !== el) return;
const pos = getPositionFromVNode(vnode);
if (!pos) return;
return [pos, vnode];
}
function isSamePosition(a, b) {
return Boolean(a && a[0] === b[0] && a[1] === b[1] && a[2] === b[2]);
}
var ElementTraceInfo = class {
pos;
vnode;
el;
constructor(pos, el, vnode) {
this.vnode = vnode;
this.pos = pos;
this.el = el;
}
get filepath() {
return this.pos[0];
}
get fullpath() {
let file = this.pos[0];
if (this.pos[1]) {
file += `:${this.pos[1]}`;
if (this.pos[2]) file += `:${this.pos[2]}`;
}
return file;
}
get rect() {
return this.el?.getBoundingClientRect();
}
getElementsSameFile() {
return Array.from(document.querySelectorAll("*")).filter((el) => el !== this.el && findTraceFromElement(el)?.filepath === this.filepath);
}
getParent() {
const parentVNode = this.vnode?.parent;
const parentEl = this.el?.parentElement;
return findTraceFromVNode(parentVNode) ?? findTraceFromElement(parentEl);
}
getElementsSamePosition() {
const selector = typeof this.vnode?.type === "string" ? this.vnode.type : this.el?.tagName.toLowerCase() || "*";
return Array.from(document.querySelectorAll(selector)).filter((el) => el !== this.el && isSamePosition(findTraceFromElement(el)?.pos, this.pos));
}
};
function findTraceFromElement(el) {
if (!el) return;
const vnode = el.__vnode;
const vnodeTrace = findTraceFromVNode(vnode, el);
if (vnodeTrace) return vnodeTrace;
const componentPosition = getComponentPositionFromElement(el);
if (componentPosition) return new ElementTraceInfo(componentPosition[0], el, componentPosition[1]);
const attrPos = parsePositionInfo(el.getAttribute(KEY_DATA));
if (attrPos) return new ElementTraceInfo(attrPos, el, vnode);
}
function findTraceFromVNode(vnode, el) {
if (!vnode) return;
const pos = getPositionFromVNode(vnode);
if (!pos) return;
return new ElementTraceInfo(pos, el ?? vnode?.el ?? void 0, vnode);
}
function findTraceAtPointer(e) {
let elements = document.elementsFromPoint(e.x, e.y);
const ignoreIndex = elements.findIndex((node) => node?.hasAttribute?.(KEY_IGNORE));
if (ignoreIndex !== -1) elements = elements.slice(ignoreIndex + 1);
for (const el of elements) {
const match = findTraceFromElement(el);
if (match) return match;
}
}
function hasData() {
return store.hasData || typeof document !== "undefined" && document.querySelector(`[${KEY_DATA}]`) != null;
}
//#endregion
export { ElementTraceInfo, findTraceAtPointer, findTraceFromElement, findTraceFromVNode, getInternalStore, hasData, recordPosition };