UNPKG

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
//#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 };