@oddsquad/vite-plugin-lit 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -157,14 +157,39 @@ if (hot !== undefined && typeof window !== 'undefined') {
157
157
  }
158
158
  };
159
159
  // -------------------------------------------------------------------------
160
+ // SPA navigation: a route change swaps the component tree without any HMR
161
+ // or panel event, so the snapshot went stale until a manual refresh. Push a
162
+ // deduped tree after each navigation — twice, since routes typically
163
+ // lazy-load and render async. Redundant while the live observer watches.
164
+ // -------------------------------------------------------------------------
165
+ const onNavigation = () => {
166
+ if (observer !== null)
167
+ return;
168
+ for (const delay of [150, 1000]) {
169
+ setTimeout(pushTreeIfChanged, delay);
170
+ }
171
+ };
172
+ window.addEventListener('popstate', onNavigation);
173
+ window.addEventListener('hashchange', onNavigation);
174
+ for (const method of ['pushState', 'replaceState']) {
175
+ const original = history[method].bind(history);
176
+ history[method] = (...args) => {
177
+ original(...args);
178
+ onNavigation();
179
+ };
180
+ }
181
+ // -------------------------------------------------------------------------
160
182
  // Command dispatch
161
183
  // -------------------------------------------------------------------------
162
184
  hot.on(INSPECT_CMD_CHANNEL, (data) => {
163
185
  const cmd = data;
164
186
  switch (cmd.type) {
165
- case 'tree':
166
- send({ type: 'tree', roots: buildTree() });
187
+ case 'tree': {
188
+ const roots = buildTree();
189
+ lastTreeJson = JSON.stringify(roots);
190
+ send({ type: 'tree', roots });
167
191
  break;
192
+ }
168
193
  case 'details': {
169
194
  const el = elementById(cmd.id);
170
195
  if (el !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/inspector/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Components inspector runtime.\n *\n * Injected into the host page (alongside the timeline runtime) when the\n * DevTools panel is enabled. Answers the panel's tree/details requests over the\n * Vite HMR channel, keeps a watched element's details fresh as it updates, and\n * outlines elements the panel hovers in its tree.\n */\n\nimport {elementById} from '../timeline/identity.js';\nimport {buildTree, collectDetails} from './collect.js';\nimport {\n INSPECT_CMD_CHANNEL,\n INSPECT_DATA_CHANNEL,\n type InspectorCommand,\n type InspectorMessage,\n} from '../../../types/inspector.js';\n\ntype ViteHot = {\n send: (event: string, data: unknown) => void;\n on: (event: string, handler: (data: unknown) => void) => void;\n};\n\nconst hot = (import.meta as {hot?: ViteHot}).hot;\n\nif (hot !== undefined && typeof window !== 'undefined') {\n const send = (msg: InspectorMessage): void => {\n try {\n hot.send(INSPECT_DATA_CHANNEL, msg);\n } catch {\n // HMR channel temporarily unavailable; the panel can re-request.\n }\n };\n\n // -------------------------------------------------------------------------\n // Live-watch: re-push details whenever the watched element finishes updating.\n // We wrap the instance's `updated` (not the prototype) so the hook is scoped\n // to one element and trivially removable, and it works without the timeline's\n // recording gate.\n // -------------------------------------------------------------------------\n\n type Updatable = Element & {\n updated?: (changed: unknown) => void;\n };\n // Hold the watched element via a WeakRef so an active watch doesn't keep a\n // removed element alive: the override and `restore` only reach `el` through\n // the ref, leaving the DOM as its sole strong root.\n let watched: {ref: WeakRef<Updatable>; restore: () => void} | null = null;\n\n const unwatch = (): void => {\n watched?.restore();\n watched = null;\n };\n\n const watch = (id: number): void => {\n unwatch();\n const el = elementById(id) as Updatable | undefined;\n if (el === undefined) {\n send({type: 'gone', id});\n return;\n }\n const hadOwn = Object.prototype.hasOwnProperty.call(el, 'updated');\n const prev = el.updated;\n const ref = new WeakRef(el);\n el.updated = function (changed: unknown) {\n prev?.call(this, changed);\n const cur = ref.deref();\n if (cur !== undefined) {\n send({type: 'details', details: collectDetails(cur)});\n }\n };\n watched = {\n ref,\n restore: () => {\n const cur = ref.deref();\n if (cur === undefined) return;\n if (hadOwn) cur.updated = prev;\n else delete cur.updated;\n },\n };\n // Push an immediate snapshot so the panel doesn't wait for the next update.\n send({type: 'details', details: collectDetails(el)});\n };\n\n // -------------------------------------------------------------------------\n // Highlight box: a lightweight fixed-position outline drawn over an element\n // the panel hovers in its tree. Independent of the source overlay so it works\n // whether or not that feature is enabled.\n // -------------------------------------------------------------------------\n\n let highlightBox: HTMLElement | null = null;\n\n const showHighlight = (el: Element): void => {\n if (highlightBox === null) {\n highlightBox = document.createElement('div');\n highlightBox.setAttribute('data-lit-devtools-highlight', '');\n Object.assign(highlightBox.style, {\n position: 'fixed',\n zIndex: '2147483646',\n pointerEvents: 'none',\n background: 'rgba(77, 99, 255, 0.25)',\n outline: '1px solid #4d63ff',\n borderRadius: '2px',\n transition: 'all 80ms ease-out',\n } satisfies Partial<CSSStyleDeclaration>);\n document.body.append(highlightBox);\n }\n const r = el.getBoundingClientRect();\n Object.assign(highlightBox.style, {\n display: 'block',\n left: `${r.left}px`,\n top: `${r.top}px`,\n width: `${r.width}px`,\n height: `${r.height}px`,\n });\n };\n\n const clearHighlight = (): void => {\n if (highlightBox !== null) highlightBox.style.display = 'none';\n };\n\n // -------------------------------------------------------------------------\n // Opt-in live tree: watch the DOM (light + every shadow root) and push a\n // fresh tree when the component hierarchy actually changes. Off by default.\n // childList-only (no attributes), so cosmetic changes — including our own\n // highlight box toggling — don't churn; identical rebuilds are also deduped.\n // -------------------------------------------------------------------------\n\n let observer: MutationObserver | null = null;\n let observeTimer: ReturnType<typeof setTimeout> | undefined;\n let lastTreeJson = '';\n\n /** Re-attach the observer to the body and every shadow root in the page. */\n const syncObserverTargets = (obs: MutationObserver): void => {\n obs.disconnect();\n obs.observe(document.body, {childList: true, subtree: true});\n const walk = (root: ParentNode): void => {\n for (const el of root.querySelectorAll('*')) {\n if (el.shadowRoot !== null) {\n obs.observe(el.shadowRoot, {childList: true, subtree: true});\n walk(el.shadowRoot);\n }\n }\n };\n walk(document);\n };\n\n const pushTreeIfChanged = (): void => {\n const roots = buildTree();\n const json = JSON.stringify(roots);\n if (json === lastTreeJson) return;\n lastTreeJson = json;\n send({type: 'tree', roots});\n };\n\n const onMutation = (): void => {\n if (observeTimer !== undefined) clearTimeout(observeTimer);\n observeTimer = setTimeout(() => {\n observeTimer = undefined;\n if (observer !== null) syncObserverTargets(observer);\n pushTreeIfChanged();\n }, 100);\n };\n\n const setObserving = (enabled: boolean): void => {\n if (enabled) {\n if (observer === null) observer = new MutationObserver(onMutation);\n lastTreeJson = '';\n syncObserverTargets(observer);\n pushTreeIfChanged();\n } else if (observer !== null) {\n observer.disconnect();\n observer = null;\n if (observeTimer !== undefined) {\n clearTimeout(observeTimer);\n observeTimer = undefined;\n }\n }\n };\n\n // -------------------------------------------------------------------------\n // Command dispatch\n // -------------------------------------------------------------------------\n\n hot.on(INSPECT_CMD_CHANNEL, (data) => {\n const cmd = data as InspectorCommand;\n switch (cmd.type) {\n case 'tree':\n send({type: 'tree', roots: buildTree()});\n break;\n case 'details': {\n const el = elementById(cmd.id);\n if (el !== undefined) {\n send({type: 'details', details: collectDetails(el)});\n } else {\n send({type: 'gone', id: cmd.id});\n }\n break;\n }\n case 'watch':\n if (cmd.id === null) unwatch();\n else watch(cmd.id);\n break;\n case 'highlight': {\n if (cmd.id === null) {\n clearHighlight();\n break;\n }\n const el = elementById(cmd.id);\n if (el !== undefined) showHighlight(el);\n else clearHighlight();\n break;\n }\n case 'observe':\n setObserving(cmd.enabled);\n break;\n }\n });\n\n // Announce readiness so a panel that loaded first re-requests the tree.\n send({type: 'ready'});\n}\n"]}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/inspector/install.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAC,SAAS,EAAE,cAAc,EAAC,MAAM,cAAc,CAAC;AACvD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,6BAA6B,CAAC;AAOrC,MAAM,GAAG,GAAI,MAAM,CAAC,IAAwB,CAAC,GAAG,CAAC;AAEjD,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,GAAqB,EAAQ,EAAE;QAC3C,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,iEAAiE;QACnE,CAAC;IACH,CAAC,CAAC;IAYF,2EAA2E;IAC3E,4EAA4E;IAC5E,oDAAoD;IACpD,IAAI,OAAO,GAA0D,IAAI,CAAC;IAE1E,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,OAAO,EAAE,OAAO,EAAE,CAAC;QACnB,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,EAAU,EAAQ,EAAE;QACjC,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAA0B,CAAC;QACpD,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAC,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5B,EAAE,CAAC,OAAO,GAAG,UAAU,OAAgB;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC,CAAC;QACF,OAAO,GAAG;YACR,GAAG;YACH,OAAO,EAAE,GAAG,EAAE;gBACZ,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC;gBACxB,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO;gBAC9B,IAAI,MAAM;oBAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;;oBAC1B,OAAO,GAAG,CAAC,OAAO,CAAC;YAC1B,CAAC;SACF,CAAC;QACF,4EAA4E;QAC5E,IAAI,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,EAAC,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,4EAA4E;IAC5E,4EAA4E;IAC5E,8EAA8E;IAC9E,0CAA0C;IAC1C,4EAA4E;IAE5E,IAAI,YAAY,GAAuB,IAAI,CAAC;IAE5C,MAAM,aAAa,GAAG,CAAC,EAAW,EAAQ,EAAE;QAC1C,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC7C,YAAY,CAAC,YAAY,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE;gBAChC,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,YAAY;gBACpB,aAAa,EAAE,MAAM;gBACrB,UAAU,EAAE,yBAAyB;gBACrC,OAAO,EAAE,mBAAmB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,UAAU,EAAE,mBAAmB;aACO,CAAC,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE;YAChC,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI;YACnB,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI;YACjB,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI;YACrB,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,IAAI;SACxB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,GAAS,EAAE;QAChC,IAAI,YAAY,KAAK,IAAI;YAAE,YAAY,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;IACjE,CAAC,CAAC;IAEF,4EAA4E;IAC5E,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAE5E,IAAI,QAAQ,GAA4B,IAAI,CAAC;IAC7C,IAAI,YAAuD,CAAC;IAC5D,IAAI,YAAY,GAAG,EAAE,CAAC;IAEtB,4EAA4E;IAC5E,MAAM,mBAAmB,GAAG,CAAC,GAAqB,EAAQ,EAAE;QAC1D,GAAG,CAAC,UAAU,EAAE,CAAC;QACjB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,CAAC,IAAgB,EAAQ,EAAE;YACtC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5C,IAAI,EAAE,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;oBAC3B,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;oBAC7D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,GAAS,EAAE;QACnC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO;QAClC,YAAY,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAC,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,GAAS,EAAE;QAC5B,IAAI,YAAY,KAAK,SAAS;YAAE,YAAY,CAAC,YAAY,CAAC,CAAC;QAC3D,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,YAAY,GAAG,SAAS,CAAC;YACzB,IAAI,QAAQ,KAAK,IAAI;gBAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YACrD,iBAAiB,EAAE,CAAC;QACtB,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAQ,EAAE;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,QAAQ,KAAK,IAAI;gBAAE,QAAQ,GAAG,IAAI,gBAAgB,CAAC,UAAU,CAAC,CAAC;YACnE,YAAY,GAAG,EAAE,CAAC;YAClB,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YAC9B,iBAAiB,EAAE,CAAC;QACtB,CAAC;aAAM,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC7B,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC/B,YAAY,CAAC,YAAY,CAAC,CAAC;gBAC3B,YAAY,GAAG,SAAS,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,qEAAqE;IACrE,yEAAyE;IACzE,4EAA4E;IAE5E,MAAM,YAAY,GAAG,GAAS,EAAE;QAC9B,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO;QAC9B,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;YAChC,UAAU,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IACpD,KAAK,MAAM,MAAM,IAAI,CAAC,WAAW,EAAE,cAAc,CAAU,EAAE,CAAC;QAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAsC,EAAE,EAAE;YAC9D,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;YAClB,YAAY,EAAE,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,mBAAmB;IACnB,4EAA4E;IAE5E,GAAG,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,IAAI,EAAE,EAAE;QACnC,MAAM,GAAG,GAAG,IAAwB,CAAC;QACrC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;gBAC1B,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC/B,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;oBACrB,IAAI,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,EAAC,CAAC,CAAC;gBACvD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAC;gBACnC,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,OAAO;gBACV,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI;oBAAE,OAAO,EAAE,CAAC;;oBAC1B,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnB,MAAM;YACR,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;oBACpB,cAAc,EAAE,CAAC;oBACjB,MAAM;gBACR,CAAC;gBACD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC/B,IAAI,EAAE,KAAK,SAAS;oBAAE,aAAa,CAAC,EAAE,CAAC,CAAC;;oBACnC,cAAc,EAAE,CAAC;gBACtB,MAAM;YACR,CAAC;YACD,KAAK,SAAS;gBACZ,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC1B,MAAM;QACV,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,wEAAwE;IACxE,IAAI,CAAC,EAAC,IAAI,EAAE,OAAO,EAAC,CAAC,CAAC;AACxB,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Components inspector runtime.\n *\n * Injected into the host page (alongside the timeline runtime) when the\n * DevTools panel is enabled. Answers the panel's tree/details requests over the\n * Vite HMR channel, keeps a watched element's details fresh as it updates, and\n * outlines elements the panel hovers in its tree.\n */\n\nimport {elementById} from '../timeline/identity.js';\nimport {buildTree, collectDetails} from './collect.js';\nimport {\n INSPECT_CMD_CHANNEL,\n INSPECT_DATA_CHANNEL,\n type InspectorCommand,\n type InspectorMessage,\n} from '../../../types/inspector.js';\n\ntype ViteHot = {\n send: (event: string, data: unknown) => void;\n on: (event: string, handler: (data: unknown) => void) => void;\n};\n\nconst hot = (import.meta as {hot?: ViteHot}).hot;\n\nif (hot !== undefined && typeof window !== 'undefined') {\n const send = (msg: InspectorMessage): void => {\n try {\n hot.send(INSPECT_DATA_CHANNEL, msg);\n } catch {\n // HMR channel temporarily unavailable; the panel can re-request.\n }\n };\n\n // -------------------------------------------------------------------------\n // Live-watch: re-push details whenever the watched element finishes updating.\n // We wrap the instance's `updated` (not the prototype) so the hook is scoped\n // to one element and trivially removable, and it works without the timeline's\n // recording gate.\n // -------------------------------------------------------------------------\n\n type Updatable = Element & {\n updated?: (changed: unknown) => void;\n };\n // Hold the watched element via a WeakRef so an active watch doesn't keep a\n // removed element alive: the override and `restore` only reach `el` through\n // the ref, leaving the DOM as its sole strong root.\n let watched: {ref: WeakRef<Updatable>; restore: () => void} | null = null;\n\n const unwatch = (): void => {\n watched?.restore();\n watched = null;\n };\n\n const watch = (id: number): void => {\n unwatch();\n const el = elementById(id) as Updatable | undefined;\n if (el === undefined) {\n send({type: 'gone', id});\n return;\n }\n const hadOwn = Object.prototype.hasOwnProperty.call(el, 'updated');\n const prev = el.updated;\n const ref = new WeakRef(el);\n el.updated = function (changed: unknown) {\n prev?.call(this, changed);\n const cur = ref.deref();\n if (cur !== undefined) {\n send({type: 'details', details: collectDetails(cur)});\n }\n };\n watched = {\n ref,\n restore: () => {\n const cur = ref.deref();\n if (cur === undefined) return;\n if (hadOwn) cur.updated = prev;\n else delete cur.updated;\n },\n };\n // Push an immediate snapshot so the panel doesn't wait for the next update.\n send({type: 'details', details: collectDetails(el)});\n };\n\n // -------------------------------------------------------------------------\n // Highlight box: a lightweight fixed-position outline drawn over an element\n // the panel hovers in its tree. Independent of the source overlay so it works\n // whether or not that feature is enabled.\n // -------------------------------------------------------------------------\n\n let highlightBox: HTMLElement | null = null;\n\n const showHighlight = (el: Element): void => {\n if (highlightBox === null) {\n highlightBox = document.createElement('div');\n highlightBox.setAttribute('data-lit-devtools-highlight', '');\n Object.assign(highlightBox.style, {\n position: 'fixed',\n zIndex: '2147483646',\n pointerEvents: 'none',\n background: 'rgba(77, 99, 255, 0.25)',\n outline: '1px solid #4d63ff',\n borderRadius: '2px',\n transition: 'all 80ms ease-out',\n } satisfies Partial<CSSStyleDeclaration>);\n document.body.append(highlightBox);\n }\n const r = el.getBoundingClientRect();\n Object.assign(highlightBox.style, {\n display: 'block',\n left: `${r.left}px`,\n top: `${r.top}px`,\n width: `${r.width}px`,\n height: `${r.height}px`,\n });\n };\n\n const clearHighlight = (): void => {\n if (highlightBox !== null) highlightBox.style.display = 'none';\n };\n\n // -------------------------------------------------------------------------\n // Opt-in live tree: watch the DOM (light + every shadow root) and push a\n // fresh tree when the component hierarchy actually changes. Off by default.\n // childList-only (no attributes), so cosmetic changes — including our own\n // highlight box toggling — don't churn; identical rebuilds are also deduped.\n // -------------------------------------------------------------------------\n\n let observer: MutationObserver | null = null;\n let observeTimer: ReturnType<typeof setTimeout> | undefined;\n let lastTreeJson = '';\n\n /** Re-attach the observer to the body and every shadow root in the page. */\n const syncObserverTargets = (obs: MutationObserver): void => {\n obs.disconnect();\n obs.observe(document.body, {childList: true, subtree: true});\n const walk = (root: ParentNode): void => {\n for (const el of root.querySelectorAll('*')) {\n if (el.shadowRoot !== null) {\n obs.observe(el.shadowRoot, {childList: true, subtree: true});\n walk(el.shadowRoot);\n }\n }\n };\n walk(document);\n };\n\n const pushTreeIfChanged = (): void => {\n const roots = buildTree();\n const json = JSON.stringify(roots);\n if (json === lastTreeJson) return;\n lastTreeJson = json;\n send({type: 'tree', roots});\n };\n\n const onMutation = (): void => {\n if (observeTimer !== undefined) clearTimeout(observeTimer);\n observeTimer = setTimeout(() => {\n observeTimer = undefined;\n if (observer !== null) syncObserverTargets(observer);\n pushTreeIfChanged();\n }, 100);\n };\n\n const setObserving = (enabled: boolean): void => {\n if (enabled) {\n if (observer === null) observer = new MutationObserver(onMutation);\n lastTreeJson = '';\n syncObserverTargets(observer);\n pushTreeIfChanged();\n } else if (observer !== null) {\n observer.disconnect();\n observer = null;\n if (observeTimer !== undefined) {\n clearTimeout(observeTimer);\n observeTimer = undefined;\n }\n }\n };\n\n // -------------------------------------------------------------------------\n // SPA navigation: a route change swaps the component tree without any HMR\n // or panel event, so the snapshot went stale until a manual refresh. Push a\n // deduped tree after each navigation — twice, since routes typically\n // lazy-load and render async. Redundant while the live observer watches.\n // -------------------------------------------------------------------------\n\n const onNavigation = (): void => {\n if (observer !== null) return;\n for (const delay of [150, 1000]) {\n setTimeout(pushTreeIfChanged, delay);\n }\n };\n window.addEventListener('popstate', onNavigation);\n window.addEventListener('hashchange', onNavigation);\n for (const method of ['pushState', 'replaceState'] as const) {\n const original = history[method].bind(history);\n history[method] = (...args: Parameters<History['pushState']>) => {\n original(...args);\n onNavigation();\n };\n }\n\n // -------------------------------------------------------------------------\n // Command dispatch\n // -------------------------------------------------------------------------\n\n hot.on(INSPECT_CMD_CHANNEL, (data) => {\n const cmd = data as InspectorCommand;\n switch (cmd.type) {\n case 'tree': {\n const roots = buildTree();\n lastTreeJson = JSON.stringify(roots);\n send({type: 'tree', roots});\n break;\n }\n case 'details': {\n const el = elementById(cmd.id);\n if (el !== undefined) {\n send({type: 'details', details: collectDetails(el)});\n } else {\n send({type: 'gone', id: cmd.id});\n }\n break;\n }\n case 'watch':\n if (cmd.id === null) unwatch();\n else watch(cmd.id);\n break;\n case 'highlight': {\n if (cmd.id === null) {\n clearHighlight();\n break;\n }\n const el = elementById(cmd.id);\n if (el !== undefined) showHighlight(el);\n else clearHighlight();\n break;\n }\n case 'observe':\n setObserving(cmd.enabled);\n break;\n }\n });\n\n // Announce readiness so a panel that loaded first re-requests the tree.\n send({type: 'ready'});\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oddsquad/vite-plugin-lit",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Vite plugin providing true HMR for Lit components, plus CSS helpers and a DevTools panel",
5
5
  "license": "BSD-3-Clause",
6
6
  "author": "Oddsquad (based on @lit-labs/vite-hmr by Google LLC)",