@oddsquad/vite-plugin-lit 0.0.3
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.
- package/index.d.ts +8 -0
- package/index.d.ts.map +1 -0
- package/index.js +7 -0
- package/index.js.map +1 -0
- package/lib/plugin.d.ts +28 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +94 -0
- package/lib/plugin.js.map +1 -0
- package/lib/runtime/intern.d.ts +19 -0
- package/lib/runtime/intern.d.ts.map +1 -0
- package/lib/runtime/intern.js +62 -0
- package/lib/runtime/intern.js.map +1 -0
- package/lib/runtime/patch.d.ts +38 -0
- package/lib/runtime/patch.d.ts.map +1 -0
- package/lib/runtime/patch.js +322 -0
- package/lib/runtime/patch.js.map +1 -0
- package/lib/transform.d.ts +41 -0
- package/lib/transform.d.ts.map +1 -0
- package/lib/transform.js +99 -0
- package/lib/transform.js.map +1 -0
- package/lib/wrap-table.d.ts +25 -0
- package/lib/wrap-table.d.ts.map +1 -0
- package/lib/wrap-table.js +24 -0
- package/lib/wrap-table.js.map +1 -0
- package/package.json +40 -0
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/labs/vite-hmr/src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,MAAM,EAAE,MAAM,IAAI,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC"}
|
package/index.js
ADDED
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../packages/labs/vite-hmr/src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,MAAM,EAAE,MAAM,IAAI,OAAO,EAAC,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nexport {litHmr, litHmr as default} from './lib/plugin.js';\nexport type {LitHmrOptions} from './lib/plugin.js';\n"]}
|
package/lib/plugin.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
import type { Plugin } from 'vite';
|
|
7
|
+
/**
|
|
8
|
+
* Options for the Lit HMR Vite plugin.
|
|
9
|
+
*/
|
|
10
|
+
export interface LitHmrOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Cycle `disconnectedCallback()`/`connectedCallback()` on live instances
|
|
13
|
+
* after a hot patch. Interning makes this mostly unnecessary, so it's
|
|
14
|
+
* opt-in. Defaults to `false`.
|
|
15
|
+
*/
|
|
16
|
+
reconnect?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* What to do when a component can't be hot-patched in place (e.g.
|
|
19
|
+
* standard `accessor` decorators). Defaults to `'reload'`.
|
|
20
|
+
*/
|
|
21
|
+
onIncompatible?: 'reload' | 'warn';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Dev-only Vite plugin providing true HMR for Lit: template-strings
|
|
25
|
+
* interning plus in-place custom element class patching.
|
|
26
|
+
*/
|
|
27
|
+
export declare const litHmr: (options?: LitHmrOptions) => Plugin;
|
|
28
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,MAAM,CAAC;AAIjC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAmBD;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,UAAS,aAAkB,KAAG,MAsEpD,CAAC"}
|
package/lib/plugin.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { INSTALL_ID, VIRTUAL_PREFIX, transformLitModule } from './transform.js';
|
|
9
|
+
import { WRAP_TABLE } from './wrap-table.js';
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a runtime module to an absolute fs path (served via `/@fs/`), so
|
|
12
|
+
* the plugin works from any served root. Falls back from the built `.js` to
|
|
13
|
+
* the `.ts` source when running un-built (e.g. under vitest).
|
|
14
|
+
*/
|
|
15
|
+
const resolveRuntimeModule = (name) => {
|
|
16
|
+
for (const ext of ['js', 'ts']) {
|
|
17
|
+
const url = new URL(`./runtime/${name}.${ext}`, import.meta.url);
|
|
18
|
+
if (existsSync(url)) {
|
|
19
|
+
return fileURLToPath(url).replace(/\\/g, '/');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`[lit-hmr] runtime module not found: ${name}`);
|
|
23
|
+
};
|
|
24
|
+
const JS_FILE_RE = /\.[cm]?[jt]sx?$/;
|
|
25
|
+
/**
|
|
26
|
+
* Dev-only Vite plugin providing true HMR for Lit: template-strings
|
|
27
|
+
* interning plus in-place custom element class patching.
|
|
28
|
+
*/
|
|
29
|
+
export const litHmr = (options = {}) => {
|
|
30
|
+
const runtimeOptions = {
|
|
31
|
+
reconnect: options.reconnect ?? false,
|
|
32
|
+
onIncompatible: options.onIncompatible ?? 'reload',
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
name: 'lit-hmr',
|
|
36
|
+
apply: 'serve',
|
|
37
|
+
// The injected runtime imports are invisible to the dep scanner. The
|
|
38
|
+
// lit family stays prebundle-eligible on purpose: the wrapper modules'
|
|
39
|
+
// bare imports then resolve to the same URL every other importer gets —
|
|
40
|
+
// single lit instance, single template cache.
|
|
41
|
+
config: () => ({
|
|
42
|
+
optimizeDeps: { exclude: ['@lit-labs/vite-hmr'] },
|
|
43
|
+
}),
|
|
44
|
+
resolveId(id) {
|
|
45
|
+
if (id.startsWith(VIRTUAL_PREFIX)) {
|
|
46
|
+
return id;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
},
|
|
50
|
+
load(id) {
|
|
51
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (id === INSTALL_ID) {
|
|
55
|
+
const patchPath = resolveRuntimeModule('patch');
|
|
56
|
+
return (`import {install} from ${JSON.stringify(patchPath)};\n` +
|
|
57
|
+
`install(${JSON.stringify(runtimeOptions)});\n`);
|
|
58
|
+
}
|
|
59
|
+
const spec = id.slice(VIRTUAL_PREFIX.length);
|
|
60
|
+
const wrappedTags = WRAP_TABLE.get(spec);
|
|
61
|
+
if (wrappedTags === undefined) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const internPath = resolveRuntimeModule('intern');
|
|
65
|
+
// Browser-native ESM: explicit local exports shadow `export *` names
|
|
66
|
+
// (spec-guaranteed), so everything except the wrapped tags passes
|
|
67
|
+
// through unchanged.
|
|
68
|
+
const lines = [
|
|
69
|
+
`import * as __lit from ${JSON.stringify(spec)};`,
|
|
70
|
+
`import {wrapTag} from ${JSON.stringify(internPath)};`,
|
|
71
|
+
`export * from ${JSON.stringify(spec)};`,
|
|
72
|
+
];
|
|
73
|
+
for (const { exportName, ns } of wrappedTags) {
|
|
74
|
+
lines.push(`export const ${exportName} = wrapTag(__lit.${exportName}, ${JSON.stringify(ns)});`);
|
|
75
|
+
}
|
|
76
|
+
return lines.join('\n') + '\n';
|
|
77
|
+
},
|
|
78
|
+
async transform(code, id, transformOptions) {
|
|
79
|
+
if (transformOptions?.ssr) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (id.startsWith('\0') || id.includes('/node_modules/')) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const [file] = id.split('?', 2);
|
|
86
|
+
// Allow inline scripts extracted from HTML (`?html-proxy`).
|
|
87
|
+
if (!JS_FILE_RE.test(file) && !id.includes('?html-proxy')) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return transformLitModule(code);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nimport {existsSync} from 'node:fs';\nimport {fileURLToPath} from 'node:url';\nimport type {Plugin} from 'vite';\nimport {INSTALL_ID, VIRTUAL_PREFIX, transformLitModule} from './transform.js';\nimport {WRAP_TABLE} from './wrap-table.js';\n\n/**\n * Options for the Lit HMR Vite plugin.\n */\nexport interface LitHmrOptions {\n /**\n * Cycle `disconnectedCallback()`/`connectedCallback()` on live instances\n * after a hot patch. Interning makes this mostly unnecessary, so it's\n * opt-in. Defaults to `false`.\n */\n reconnect?: boolean;\n\n /**\n * What to do when a component can't be hot-patched in place (e.g.\n * standard `accessor` decorators). Defaults to `'reload'`.\n */\n onIncompatible?: 'reload' | 'warn';\n}\n\n/**\n * Resolves a runtime module to an absolute fs path (served via `/@fs/`), so\n * the plugin works from any served root. Falls back from the built `.js` to\n * the `.ts` source when running un-built (e.g. under vitest).\n */\nconst resolveRuntimeModule = (name: string): string => {\n for (const ext of ['js', 'ts'] as const) {\n const url = new URL(`./runtime/${name}.${ext}`, import.meta.url);\n if (existsSync(url)) {\n return fileURLToPath(url).replace(/\\\\/g, '/');\n }\n }\n throw new Error(`[lit-hmr] runtime module not found: ${name}`);\n};\n\nconst JS_FILE_RE = /\\.[cm]?[jt]sx?$/;\n\n/**\n * Dev-only Vite plugin providing true HMR for Lit: template-strings\n * interning plus in-place custom element class patching.\n */\nexport const litHmr = (options: LitHmrOptions = {}): Plugin => {\n const runtimeOptions = {\n reconnect: options.reconnect ?? false,\n onIncompatible: options.onIncompatible ?? 'reload',\n };\n return {\n name: 'lit-hmr',\n apply: 'serve',\n // The injected runtime imports are invisible to the dep scanner. The\n // lit family stays prebundle-eligible on purpose: the wrapper modules'\n // bare imports then resolve to the same URL every other importer gets —\n // single lit instance, single template cache.\n config: () => ({\n optimizeDeps: {exclude: ['@lit-labs/vite-hmr']},\n }),\n resolveId(id) {\n if (id.startsWith(VIRTUAL_PREFIX)) {\n return id;\n }\n return null;\n },\n load(id) {\n if (!id.startsWith(VIRTUAL_PREFIX)) {\n return null;\n }\n if (id === INSTALL_ID) {\n const patchPath = resolveRuntimeModule('patch');\n return (\n `import {install} from ${JSON.stringify(patchPath)};\\n` +\n `install(${JSON.stringify(runtimeOptions)});\\n`\n );\n }\n const spec = id.slice(VIRTUAL_PREFIX.length);\n const wrappedTags = WRAP_TABLE.get(spec);\n if (wrappedTags === undefined) {\n return null;\n }\n const internPath = resolveRuntimeModule('intern');\n // Browser-native ESM: explicit local exports shadow `export *` names\n // (spec-guaranteed), so everything except the wrapped tags passes\n // through unchanged.\n const lines = [\n `import * as __lit from ${JSON.stringify(spec)};`,\n `import {wrapTag} from ${JSON.stringify(internPath)};`,\n `export * from ${JSON.stringify(spec)};`,\n ];\n for (const {exportName, ns} of wrappedTags) {\n lines.push(\n `export const ${exportName} = wrapTag(__lit.${exportName}, ${JSON.stringify(\n ns\n )});`\n );\n }\n return lines.join('\\n') + '\\n';\n },\n async transform(code, id, transformOptions) {\n if (transformOptions?.ssr) {\n return null;\n }\n if (id.startsWith('\\0') || id.includes('/node_modules/')) {\n return null;\n }\n const [file] = id.split('?', 2);\n // Allow inline scripts extracted from HTML (`?html-proxy`).\n if (!JS_FILE_RE.test(file) && !id.includes('?html-proxy')) {\n return null;\n }\n return transformLitModule(code);\n },\n };\n};\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Returns the canonical strings array for `strings` within namespace `ns`.
|
|
8
|
+
*
|
|
9
|
+
* The first genuine `TemplateStringsArray` seen for a given content becomes
|
|
10
|
+
* the canonical array; later content-equal arrays map onto it. Non-template
|
|
11
|
+
* input (anything without a `raw` array) passes through untouched so lit
|
|
12
|
+
* can raise its own error.
|
|
13
|
+
*/
|
|
14
|
+
export declare const intern: (ns: string, strings: TemplateStringsArray) => TemplateStringsArray;
|
|
15
|
+
/**
|
|
16
|
+
* Wraps a template tag so its strings argument is interned in `ns`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const wrapTag: <T extends (strings: TemplateStringsArray, ...values: never[]) => unknown>(tag: T, ns: string) => T;
|
|
19
|
+
//# sourceMappingURL=intern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intern.d.ts","sourceRoot":"","sources":["../../../../packages/labs/vite-hmr/src/lib/runtime/intern.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAuDH;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GACjB,IAAI,MAAM,EACV,SAAS,oBAAoB,KAC5B,oBAoBF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,GAClB,CAAC,SAAS,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,OAAO,EAExE,KAAK,CAAC,EACN,IAAI,MAAM,KACT,CAE0C,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
// Exactly one intern state per page, even if this module is loaded twice
|
|
7
|
+
// (e.g. via both an optimized and a raw URL).
|
|
8
|
+
const STATE_KEY = Symbol.for('@lit-labs/vite-hmr#intern');
|
|
9
|
+
const getState = () => {
|
|
10
|
+
const g = globalThis;
|
|
11
|
+
return (g[STATE_KEY] ??= {
|
|
12
|
+
byContent: new Map(),
|
|
13
|
+
canonicalOf: new WeakMap(),
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Builds a collision-free content key for a strings array.
|
|
18
|
+
*
|
|
19
|
+
* Uses the `raw` chunks (cooked strings derive from raw) with length
|
|
20
|
+
* prefixes so that chunk boundaries can never collide, namespaced per tag
|
|
21
|
+
* type: `html` and `svg` templates with equal content must not share a
|
|
22
|
+
* canonical array, or one shared `Template` would parse SVG as HTML.
|
|
23
|
+
*/
|
|
24
|
+
const contentKey = (ns, raw) => {
|
|
25
|
+
let key = ns + '\x00' + raw.length;
|
|
26
|
+
for (const chunk of raw) {
|
|
27
|
+
key += '\x01' + chunk.length + '\x01' + chunk;
|
|
28
|
+
}
|
|
29
|
+
return key;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Returns the canonical strings array for `strings` within namespace `ns`.
|
|
33
|
+
*
|
|
34
|
+
* The first genuine `TemplateStringsArray` seen for a given content becomes
|
|
35
|
+
* the canonical array; later content-equal arrays map onto it. Non-template
|
|
36
|
+
* input (anything without a `raw` array) passes through untouched so lit
|
|
37
|
+
* can raise its own error.
|
|
38
|
+
*/
|
|
39
|
+
export const intern = (ns, strings) => {
|
|
40
|
+
if (!Array.isArray(strings) ||
|
|
41
|
+
!Array.isArray(strings.raw)) {
|
|
42
|
+
return strings;
|
|
43
|
+
}
|
|
44
|
+
const state = getState();
|
|
45
|
+
const fast = state.canonicalOf.get(strings);
|
|
46
|
+
if (fast !== undefined) {
|
|
47
|
+
return fast;
|
|
48
|
+
}
|
|
49
|
+
const key = contentKey(ns, strings.raw);
|
|
50
|
+
let canonical = state.byContent.get(key);
|
|
51
|
+
if (canonical === undefined) {
|
|
52
|
+
canonical = strings;
|
|
53
|
+
state.byContent.set(key, canonical);
|
|
54
|
+
}
|
|
55
|
+
state.canonicalOf.set(strings, canonical);
|
|
56
|
+
return canonical;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Wraps a template tag so its strings argument is interned in `ns`.
|
|
60
|
+
*/
|
|
61
|
+
export const wrapTag = (tag, ns) => ((strings, ...values) => tag(intern(ns, strings), ...values));
|
|
62
|
+
//# sourceMappingURL=intern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intern.js","sourceRoot":"","sources":["../../../../packages/labs/vite-hmr/src/lib/runtime/intern.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA2BH,yEAAyE;AACzE,8CAA8C;AAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;AAE1D,MAAM,QAAQ,GAAG,GAAgB,EAAE;IACjC,MAAM,CAAC,GAAG,UAAgE,CAAC;IAC3E,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK;QACvB,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,WAAW,EAAE,IAAI,OAAO,EAAE;KAC3B,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,GAAsB,EAAU,EAAE;IAChE,IAAI,GAAG,GAAG,EAAE,GAAG,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,GAAG,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IAChD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,EAAU,EACV,OAA6B,EACP,EAAE;IACxB,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QACvB,CAAC,KAAK,CAAC,OAAO,CAAE,OAA2B,CAAC,GAAG,CAAC,EAChD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,SAAS,GAAG,OAAO,CAAC;QACpB,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1C,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAGrB,GAAM,EACN,EAAU,EACP,EAAE,CACL,CAAC,CAAC,OAA6B,EAAE,GAAG,MAAe,EAAE,EAAE,CACrD,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,CAAM,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Template-strings interning.\n *\n * lit-html keys its template cache by the object identity of the\n * `TemplateStringsArray`, so when Vite HMR re-executes a module, every\n * template literal produces a fresh array and lit rebuilds the entire DOM\n * subtree. Interning canonicalizes strings arrays by content: unchanged\n * templates keep their identity across re-execution, so only the edited\n * template rebuilds its part.\n *\n * This module is dependency-free and must stay safe to load in any\n * environment (it is injected into user modules in dev).\n */\n\ninterface InternState {\n /** Content key → the canonical (first-seen) strings array. */\n byContent: Map<string, TemplateStringsArray>;\n /**\n * Fast path: per-call-site arrays recur on every render, so after the\n * first lookup we can map them to their canonical array with a single\n * WeakMap hit instead of rebuilding the content key.\n */\n canonicalOf: WeakMap<TemplateStringsArray, TemplateStringsArray>;\n}\n\n// Exactly one intern state per page, even if this module is loaded twice\n// (e.g. via both an optimized and a raw URL).\nconst STATE_KEY = Symbol.for('@lit-labs/vite-hmr#intern');\n\nconst getState = (): InternState => {\n const g = globalThis as unknown as Record<symbol, InternState | undefined>;\n return (g[STATE_KEY] ??= {\n byContent: new Map(),\n canonicalOf: new WeakMap(),\n });\n};\n\n/**\n * Builds a collision-free content key for a strings array.\n *\n * Uses the `raw` chunks (cooked strings derive from raw) with length\n * prefixes so that chunk boundaries can never collide, namespaced per tag\n * type: `html` and `svg` templates with equal content must not share a\n * canonical array, or one shared `Template` would parse SVG as HTML.\n */\nconst contentKey = (ns: string, raw: readonly string[]): string => {\n let key = ns + '\\x00' + raw.length;\n for (const chunk of raw) {\n key += '\\x01' + chunk.length + '\\x01' + chunk;\n }\n return key;\n};\n\n/**\n * Returns the canonical strings array for `strings` within namespace `ns`.\n *\n * The first genuine `TemplateStringsArray` seen for a given content becomes\n * the canonical array; later content-equal arrays map onto it. Non-template\n * input (anything without a `raw` array) passes through untouched so lit\n * can raise its own error.\n */\nexport const intern = (\n ns: string,\n strings: TemplateStringsArray\n): TemplateStringsArray => {\n if (\n !Array.isArray(strings) ||\n !Array.isArray((strings as {raw?: unknown}).raw)\n ) {\n return strings;\n }\n const state = getState();\n const fast = state.canonicalOf.get(strings);\n if (fast !== undefined) {\n return fast;\n }\n const key = contentKey(ns, strings.raw);\n let canonical = state.byContent.get(key);\n if (canonical === undefined) {\n canonical = strings;\n state.byContent.set(key, canonical);\n }\n state.canonicalOf.set(strings, canonical);\n return canonical;\n};\n\n/**\n * Wraps a template tag so its strings argument is interned in `ns`.\n */\nexport const wrapTag = <\n T extends (strings: TemplateStringsArray, ...values: never[]) => unknown,\n>(\n tag: T,\n ns: string\n): T =>\n ((strings: TemplateStringsArray, ...values: never[]) =>\n tag(intern(ns, strings), ...values)) as T;\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* In-place custom element class patching.
|
|
8
|
+
*
|
|
9
|
+
* Vite HMR re-executes an edited component module, which calls
|
|
10
|
+
* `customElements.define()` again with a freshly evaluated class. The
|
|
11
|
+
* platform registry can't re-define a tag, and live instances keep their
|
|
12
|
+
* original prototype chain, so instead of swapping classes we intercept the
|
|
13
|
+
* duplicate define and patch the originally-registered class in place:
|
|
14
|
+
* copy prototype + static descriptors from the new class onto the old one
|
|
15
|
+
* and nudge live instances to re-render.
|
|
16
|
+
*
|
|
17
|
+
* This module is dependency-free and duck-typed against ReactiveElement so
|
|
18
|
+
* it never imports lit (keeps it version-agnostic and safe for non-Lit
|
|
19
|
+
* custom elements, which get prototype/static patching only).
|
|
20
|
+
*/
|
|
21
|
+
export interface PatchOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Cycle `disconnectedCallback()`/`connectedCallback()` on live instances
|
|
24
|
+
* after a hot patch. Defaults to `false`.
|
|
25
|
+
*/
|
|
26
|
+
reconnect?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* What to do when a component can't be hot-patched in place (e.g.
|
|
29
|
+
* standard `accessor` decorators). Defaults to `'reload'`.
|
|
30
|
+
*/
|
|
31
|
+
onIncompatible?: 'reload' | 'warn';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Installs the `customElements.define` interceptor. Idempotent (per-page
|
|
35
|
+
* global guard); no-ops in environments without `customElements` (SSR).
|
|
36
|
+
*/
|
|
37
|
+
export declare const install: (options?: PatchOptions) => void;
|
|
38
|
+
//# sourceMappingURL=patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../../../packages/labs/vite-hmr/src/lib/runtime/patch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAuVD;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,UAAS,YAAiB,KAAG,IA8DpD,CAAC"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
// Exactly one patch state per page, even if this module loads twice.
|
|
7
|
+
const STATE_KEY = Symbol.for('@lit-labs/vite-hmr#patch');
|
|
8
|
+
// Brands our lifecycle wrappers so re-instrumentation is idempotent.
|
|
9
|
+
const RECORD_BRAND = Symbol.for('@lit-labs/vite-hmr#wrapperRecord');
|
|
10
|
+
const PROTO_SKIP = ['constructor'];
|
|
11
|
+
const STATIC_SKIP = ['prototype', 'name', 'length'];
|
|
12
|
+
const ownKeys = (o) => [
|
|
13
|
+
...Object.getOwnPropertyNames(o),
|
|
14
|
+
...Object.getOwnPropertySymbols(o),
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Syncs own members `source` → `target`: deletes own keys of `target` that
|
|
18
|
+
* vanished from `source`, then copies every own descriptor of `source`.
|
|
19
|
+
* Individual copies are best-effort (non-configurable slots are skipped).
|
|
20
|
+
*/
|
|
21
|
+
const syncOwnMembers = (target, source, skip) => {
|
|
22
|
+
for (const key of ownKeys(target)) {
|
|
23
|
+
if (skip.includes(key)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (Object.getOwnPropertyDescriptor(source, key) === undefined) {
|
|
27
|
+
try {
|
|
28
|
+
delete target[key];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Non-configurable; leave it.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const key of ownKeys(source)) {
|
|
36
|
+
if (skip.includes(key)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Non-configurable; leave it.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Defines branded `connectedCallback`/`disconnectedCallback` wrappers on the
|
|
49
|
+
* prototype to maintain the live-instance set and generation stamps. Called
|
|
50
|
+
* at first define and again after every patch (descriptor sync replaces or
|
|
51
|
+
* deletes the wrappers).
|
|
52
|
+
*/
|
|
53
|
+
const instrument = (state, proto, record) => {
|
|
54
|
+
for (const name of [
|
|
55
|
+
'connectedCallback',
|
|
56
|
+
'disconnectedCallback',
|
|
57
|
+
]) {
|
|
58
|
+
const own = Object.getOwnPropertyDescriptor(proto, name);
|
|
59
|
+
if (own?.value?.[RECORD_BRAND] === record) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// May be an own raw function (just copied from the new class), an
|
|
63
|
+
// inherited base implementation, or absent entirely.
|
|
64
|
+
const original = proto[name];
|
|
65
|
+
const wrapper = name === 'connectedCallback'
|
|
66
|
+
? function () {
|
|
67
|
+
record.instances.add(this);
|
|
68
|
+
const stamp = state.generationOf.get(this);
|
|
69
|
+
if (stamp !== undefined && stamp !== record.generation) {
|
|
70
|
+
// Was detached during one or more patches; catch up.
|
|
71
|
+
this.requestUpdate?.();
|
|
72
|
+
}
|
|
73
|
+
state.generationOf.set(this, record.generation);
|
|
74
|
+
original?.call(this);
|
|
75
|
+
}
|
|
76
|
+
: function () {
|
|
77
|
+
record.instances.delete(this);
|
|
78
|
+
original?.call(this);
|
|
79
|
+
};
|
|
80
|
+
wrapper[RECORD_BRAND] = record;
|
|
81
|
+
try {
|
|
82
|
+
Object.defineProperty(proto, name, {
|
|
83
|
+
value: wrapper,
|
|
84
|
+
writable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Non-configurable lifecycle slot; tracking degrades gracefully.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Detects standard-decorator (`accessor`) reactive properties on the new
|
|
95
|
+
* class. Those close over per-class-evaluation private slots, so copied
|
|
96
|
+
* accessors would brand-check-throw — in-place patching is impossible.
|
|
97
|
+
*/
|
|
98
|
+
const usesStandardDecorators = (ctor) => {
|
|
99
|
+
const metadataSymbol = Symbol.metadata;
|
|
100
|
+
if (metadataSymbol === undefined) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const metadata = Object.getOwnPropertyDescriptor(ctor, metadataSymbol)
|
|
104
|
+
?.value;
|
|
105
|
+
if (metadata === undefined || metadata === null) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const litPropertyMetadata = globalThis.litPropertyMetadata;
|
|
109
|
+
const properties = litPropertyMetadata?.get(metadata);
|
|
110
|
+
return properties !== undefined && properties.size > 0;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Re-adopts constructed stylesheets on an instance's shadow root from the
|
|
114
|
+
* (just-patched) static `elementStyles`. Local reimplementation — no lit
|
|
115
|
+
* import. Skips cleanly for light-DOM components and shimmed sheets.
|
|
116
|
+
*/
|
|
117
|
+
const readoptStyles = (el) => {
|
|
118
|
+
const root = el.renderRoot;
|
|
119
|
+
if (typeof ShadowRoot === 'undefined' || !(root instanceof ShadowRoot)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const styles = el.constructor.elementStyles;
|
|
123
|
+
if (!Array.isArray(styles)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const sheets = [];
|
|
127
|
+
for (const style of styles) {
|
|
128
|
+
const sheet = style instanceof CSSStyleSheet
|
|
129
|
+
? style
|
|
130
|
+
: (style?.styleSheet ??
|
|
131
|
+
undefined);
|
|
132
|
+
if (!(sheet instanceof CSSStyleSheet)) {
|
|
133
|
+
// Shimmed sheet (no constructible stylesheet support); bail out
|
|
134
|
+
// rather than adopt a partial list.
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
sheets.push(sheet);
|
|
138
|
+
}
|
|
139
|
+
// Assign (never accumulate): replaces any previously adopted set.
|
|
140
|
+
root.adoptedStyleSheets = sheets;
|
|
141
|
+
};
|
|
142
|
+
const incompatible = (state, tagName, reason) => {
|
|
143
|
+
if (state.options.onIncompatible === 'warn') {
|
|
144
|
+
console.warn(`[lit-hmr] <${tagName}> can't be hot-patched (${reason}). ` +
|
|
145
|
+
`Reload the page to pick up the change.`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.info(`[lit-hmr] <${tagName}>: ${reason} — performing full reload.`);
|
|
149
|
+
location.reload();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Patches the originally-registered class for `record.tagName` in place
|
|
154
|
+
* from the freshly evaluated `NewClass`, then updates live instances.
|
|
155
|
+
*/
|
|
156
|
+
const hotPatch = (state, record, NewClass) => {
|
|
157
|
+
const OldClass = record.canonical;
|
|
158
|
+
try {
|
|
159
|
+
// 1. Materialize finalized/elementProperties/elementStyles/
|
|
160
|
+
// __attributeToPropertyMap on the new class before we copy statics.
|
|
161
|
+
if (typeof NewClass.finalize === 'function') {
|
|
162
|
+
NewClass.finalize();
|
|
163
|
+
}
|
|
164
|
+
// 2. Standard-decorator `accessor` properties can't be patched in place.
|
|
165
|
+
if (usesStandardDecorators(NewClass)) {
|
|
166
|
+
incompatible(state, record.tagName, 'standard accessor decorators');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// The platform snapshots observedAttributes at define time; new
|
|
170
|
+
// attribute observations can't take effect without a reload.
|
|
171
|
+
const oldObserved = [...(OldClass.observedAttributes ?? [])];
|
|
172
|
+
const newObserved = [...(NewClass.observedAttributes ?? [])];
|
|
173
|
+
// 3. Snapshot reactive property values per live instance through the
|
|
174
|
+
// OLD accessors. Vite serves prod lit in dev (no `development`
|
|
175
|
+
// export condition), where accessor storage keys are unique
|
|
176
|
+
// Symbol()s per class evaluation — without this, state resets.
|
|
177
|
+
const propertyKeys = new Set([
|
|
178
|
+
...(OldClass.elementProperties?.keys() ?? []),
|
|
179
|
+
...(NewClass.elementProperties?.keys() ?? []),
|
|
180
|
+
]);
|
|
181
|
+
const snapshots = new Map();
|
|
182
|
+
for (const el of record.instances) {
|
|
183
|
+
const values = new Map();
|
|
184
|
+
for (const key of propertyKeys) {
|
|
185
|
+
values.set(key, el[key]);
|
|
186
|
+
}
|
|
187
|
+
snapshots.set(el, values);
|
|
188
|
+
}
|
|
189
|
+
// 4. Re-parent prototype chains. Mixins regenerate intermediate classes
|
|
190
|
+
// on every module evaluation, so the parents usually differ.
|
|
191
|
+
const newProtoParent = Object.getPrototypeOf(NewClass.prototype);
|
|
192
|
+
if (Object.getPrototypeOf(OldClass.prototype) !== newProtoParent) {
|
|
193
|
+
Object.setPrototypeOf(OldClass.prototype, newProtoParent);
|
|
194
|
+
}
|
|
195
|
+
const newStaticParent = Object.getPrototypeOf(NewClass);
|
|
196
|
+
if (Object.getPrototypeOf(OldClass) !== newStaticParent) {
|
|
197
|
+
Object.setPrototypeOf(OldClass, newStaticParent);
|
|
198
|
+
}
|
|
199
|
+
// 5. Sync own members new → old on the prototype and on statics.
|
|
200
|
+
syncOwnMembers(OldClass.prototype, NewClass.prototype, PROTO_SKIP);
|
|
201
|
+
syncOwnMembers(OldClass, NewClass, STATIC_SKIP);
|
|
202
|
+
// 6. The sync replaced/removed our lifecycle wrappers; re-instrument.
|
|
203
|
+
instrument(state, OldClass.prototype, record);
|
|
204
|
+
record.generation++;
|
|
205
|
+
// Module-local `instanceof NewClass` checks keep working against
|
|
206
|
+
// instances of the canonical class.
|
|
207
|
+
Object.defineProperty(NewClass, Symbol.hasInstance, {
|
|
208
|
+
value: (instance) => typeof instance === 'object' &&
|
|
209
|
+
instance !== null &&
|
|
210
|
+
OldClass.prototype.isPrototypeOf(instance),
|
|
211
|
+
configurable: true,
|
|
212
|
+
});
|
|
213
|
+
if (oldObserved.length !== newObserved.length ||
|
|
214
|
+
oldObserved.some((attr, i) => attr !== newObserved[i])) {
|
|
215
|
+
console.info(`[lit-hmr] <${record.tagName}> changed observedAttributes; the ` +
|
|
216
|
+
`platform registry can't pick this up — reload recommended.`);
|
|
217
|
+
}
|
|
218
|
+
// 7. Update live instances.
|
|
219
|
+
const newPropertyKeys = NewClass.elementProperties?.keys() ?? [];
|
|
220
|
+
// After the static sync this is the NEW class's initializer list.
|
|
221
|
+
const initializers = OldClass._initializers;
|
|
222
|
+
for (const el of [...record.instances]) {
|
|
223
|
+
if (state.options.reconnect) {
|
|
224
|
+
el.disconnectedCallback?.();
|
|
225
|
+
el.connectedCallback?.();
|
|
226
|
+
}
|
|
227
|
+
// Decorator-created reactive controllers (e.g. @lit/context's
|
|
228
|
+
// @provide/@consume) live in per-class-evaluation closures keyed by
|
|
229
|
+
// instance via addInitializer. Re-running the initializers enrolls
|
|
230
|
+
// live instances in the new closures — without this, the first
|
|
231
|
+
// assignment through a copied accessor throws and we'd full-reload.
|
|
232
|
+
// Controllers from previous evaluations stay attached but inert
|
|
233
|
+
// (everything flows through the newest closures); that's bounded by
|
|
234
|
+
// edit count and dev-only. Runs before the value restore so e.g. a
|
|
235
|
+
// re-created ContextProvider exists when the restore pushes into it.
|
|
236
|
+
if (Array.isArray(initializers)) {
|
|
237
|
+
for (const initialize of initializers) {
|
|
238
|
+
initialize(el);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const values = snapshots.get(el);
|
|
242
|
+
if (values !== undefined) {
|
|
243
|
+
// Restore through the new accessors — only keys the new class
|
|
244
|
+
// still declares.
|
|
245
|
+
for (const key of newPropertyKeys) {
|
|
246
|
+
if (values.has(key)) {
|
|
247
|
+
el[key] =
|
|
248
|
+
values.get(key);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
readoptStyles(el);
|
|
253
|
+
state.generationOf.set(el, record.generation);
|
|
254
|
+
el.requestUpdate?.();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
incompatible(state, record.tagName, `patching failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Installs the `customElements.define` interceptor. Idempotent (per-page
|
|
263
|
+
* global guard); no-ops in environments without `customElements` (SSR).
|
|
264
|
+
*/
|
|
265
|
+
export const install = (options = {}) => {
|
|
266
|
+
if (typeof customElements === 'undefined') {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const g = globalThis;
|
|
270
|
+
const existing = g[STATE_KEY];
|
|
271
|
+
if (existing !== undefined) {
|
|
272
|
+
// Already installed; refresh explicitly-provided options only.
|
|
273
|
+
if (options.reconnect !== undefined) {
|
|
274
|
+
existing.options.reconnect = options.reconnect;
|
|
275
|
+
}
|
|
276
|
+
if (options.onIncompatible !== undefined) {
|
|
277
|
+
existing.options.onIncompatible = options.onIncompatible;
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const state = {
|
|
282
|
+
records: new Map(),
|
|
283
|
+
generationOf: new WeakMap(),
|
|
284
|
+
options: {
|
|
285
|
+
reconnect: options.reconnect ?? false,
|
|
286
|
+
onIncompatible: options.onIncompatible ?? 'reload',
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
g[STATE_KEY] = state;
|
|
290
|
+
const nativeDefine = customElements.define;
|
|
291
|
+
customElements.define = function (name, ctor, defineOptions) {
|
|
292
|
+
let record = state.records.get(name);
|
|
293
|
+
if (record === undefined && this.get(name) !== undefined) {
|
|
294
|
+
// Defined before install() ran (shouldn't happen — the installer is
|
|
295
|
+
// import-hoisted — but recover gracefully without instance tracking).
|
|
296
|
+
record = {
|
|
297
|
+
tagName: name,
|
|
298
|
+
canonical: this.get(name),
|
|
299
|
+
instances: new Set(),
|
|
300
|
+
generation: 0,
|
|
301
|
+
};
|
|
302
|
+
state.records.set(name, record);
|
|
303
|
+
}
|
|
304
|
+
if (record === undefined) {
|
|
305
|
+
record = {
|
|
306
|
+
tagName: name,
|
|
307
|
+
canonical: ctor,
|
|
308
|
+
instances: new Set(),
|
|
309
|
+
generation: 0,
|
|
310
|
+
};
|
|
311
|
+
state.records.set(name, record);
|
|
312
|
+
instrument(state, ctor.prototype, record);
|
|
313
|
+
return nativeDefine.call(this, name, ctor, defineOptions);
|
|
314
|
+
}
|
|
315
|
+
if (ctor === record.canonical) {
|
|
316
|
+
// Same class object re-defined; nothing to patch.
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
hotPatch(state, record, ctor);
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
//# sourceMappingURL=patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.js","sourceRoot":"","sources":["../../../../packages/labs/vite-hmr/src/lib/runtime/patch.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 * In-place custom element class patching.\n *\n * Vite HMR re-executes an edited component module, which calls\n * `customElements.define()` again with a freshly evaluated class. The\n * platform registry can't re-define a tag, and live instances keep their\n * original prototype chain, so instead of swapping classes we intercept the\n * duplicate define and patch the originally-registered class in place:\n * copy prototype + static descriptors from the new class onto the old one\n * and nudge live instances to re-render.\n *\n * This module is dependency-free and duck-typed against ReactiveElement so\n * it never imports lit (keeps it version-agnostic and safe for non-Lit\n * custom elements, which get prototype/static patching only).\n */\n\nexport interface PatchOptions {\n /**\n * Cycle `disconnectedCallback()`/`connectedCallback()` on live instances\n * after a hot patch. Defaults to `false`.\n */\n reconnect?: boolean;\n\n /**\n * What to do when a component can't be hot-patched in place (e.g.\n * standard `accessor` decorators). Defaults to `'reload'`.\n */\n onIncompatible?: 'reload' | 'warn';\n}\n\ninterface ReactiveElementLike extends HTMLElement {\n requestUpdate?: () => void;\n renderRoot?: unknown;\n connectedCallback?: () => void;\n disconnectedCallback?: () => void;\n}\n\n/** Duck-typed view of ReactiveElement constructor statics. */\ninterface ReactiveCtorLike extends CustomElementConstructor {\n finalize?: () => void;\n elementProperties?: Map<PropertyKey, unknown>;\n elementStyles?: ReadonlyArray<unknown>;\n observedAttributes?: readonly string[];\n _initializers?: Array<(element: unknown) => void>;\n}\n\ninterface TagRecord {\n tagName: string;\n canonical: CustomElementConstructor;\n instances: Set<ReactiveElementLike>;\n generation: number;\n}\n\ninterface PatchState {\n records: Map<string, TagRecord>;\n /** Generation a (possibly detached) instance last caught up to. */\n generationOf: WeakMap<ReactiveElementLike, number>;\n options: Required<PatchOptions>;\n}\n\ntype LifecycleName = 'connectedCallback' | 'disconnectedCallback';\n\ninterface BrandedWrapper {\n (this: ReactiveElementLike): void;\n [RECORD_BRAND]?: TagRecord;\n}\n\n// Exactly one patch state per page, even if this module loads twice.\nconst STATE_KEY = Symbol.for('@lit-labs/vite-hmr#patch');\n// Brands our lifecycle wrappers so re-instrumentation is idempotent.\nconst RECORD_BRAND = Symbol.for('@lit-labs/vite-hmr#wrapperRecord');\n\nconst PROTO_SKIP: ReadonlyArray<PropertyKey> = ['constructor'];\nconst STATIC_SKIP: ReadonlyArray<PropertyKey> = ['prototype', 'name', 'length'];\n\nconst ownKeys = (o: object): PropertyKey[] => [\n ...Object.getOwnPropertyNames(o),\n ...Object.getOwnPropertySymbols(o),\n];\n\n/**\n * Syncs own members `source` → `target`: deletes own keys of `target` that\n * vanished from `source`, then copies every own descriptor of `source`.\n * Individual copies are best-effort (non-configurable slots are skipped).\n */\nconst syncOwnMembers = (\n target: object,\n source: object,\n skip: ReadonlyArray<PropertyKey>\n) => {\n for (const key of ownKeys(target)) {\n if (skip.includes(key)) {\n continue;\n }\n if (Object.getOwnPropertyDescriptor(source, key) === undefined) {\n try {\n delete (target as Record<PropertyKey, unknown>)[key];\n } catch {\n // Non-configurable; leave it.\n }\n }\n }\n for (const key of ownKeys(source)) {\n if (skip.includes(key)) {\n continue;\n }\n try {\n Object.defineProperty(\n target,\n key,\n Object.getOwnPropertyDescriptor(source, key)!\n );\n } catch {\n // Non-configurable; leave it.\n }\n }\n};\n\n/**\n * Defines branded `connectedCallback`/`disconnectedCallback` wrappers on the\n * prototype to maintain the live-instance set and generation stamps. Called\n * at first define and again after every patch (descriptor sync replaces or\n * deletes the wrappers).\n */\nconst instrument = (\n state: PatchState,\n proto: object,\n record: TagRecord\n): void => {\n for (const name of [\n 'connectedCallback',\n 'disconnectedCallback',\n ] as const satisfies readonly LifecycleName[]) {\n const own = Object.getOwnPropertyDescriptor(proto, name);\n if ((own?.value as BrandedWrapper | undefined)?.[RECORD_BRAND] === record) {\n continue;\n }\n // May be an own raw function (just copied from the new class), an\n // inherited base implementation, or absent entirely.\n const original = (proto as Record<LifecycleName, (() => void) | undefined>)[\n name\n ];\n const wrapper: BrandedWrapper =\n name === 'connectedCallback'\n ? function (this: ReactiveElementLike) {\n record.instances.add(this);\n const stamp = state.generationOf.get(this);\n if (stamp !== undefined && stamp !== record.generation) {\n // Was detached during one or more patches; catch up.\n this.requestUpdate?.();\n }\n state.generationOf.set(this, record.generation);\n original?.call(this);\n }\n : function (this: ReactiveElementLike) {\n record.instances.delete(this);\n original?.call(this);\n };\n wrapper[RECORD_BRAND] = record;\n try {\n Object.defineProperty(proto, name, {\n value: wrapper,\n writable: true,\n configurable: true,\n });\n } catch {\n // Non-configurable lifecycle slot; tracking degrades gracefully.\n }\n }\n};\n\n/**\n * Detects standard-decorator (`accessor`) reactive properties on the new\n * class. Those close over per-class-evaluation private slots, so copied\n * accessors would brand-check-throw — in-place patching is impossible.\n */\nconst usesStandardDecorators = (ctor: ReactiveCtorLike): boolean => {\n const metadataSymbol = (Symbol as {metadata?: symbol}).metadata;\n if (metadataSymbol === undefined) {\n return false;\n }\n const metadata = Object.getOwnPropertyDescriptor(ctor, metadataSymbol)\n ?.value as object | undefined;\n if (metadata === undefined || metadata === null) {\n return false;\n }\n const litPropertyMetadata = (\n globalThis as {litPropertyMetadata?: WeakMap<object, Map<unknown, unknown>>}\n ).litPropertyMetadata;\n const properties = litPropertyMetadata?.get(metadata);\n return properties !== undefined && properties.size > 0;\n};\n\n/**\n * Re-adopts constructed stylesheets on an instance's shadow root from the\n * (just-patched) static `elementStyles`. Local reimplementation — no lit\n * import. Skips cleanly for light-DOM components and shimmed sheets.\n */\nconst readoptStyles = (el: ReactiveElementLike): void => {\n const root = el.renderRoot;\n if (typeof ShadowRoot === 'undefined' || !(root instanceof ShadowRoot)) {\n return;\n }\n const styles = (el.constructor as ReactiveCtorLike).elementStyles;\n if (!Array.isArray(styles)) {\n return;\n }\n const sheets: CSSStyleSheet[] = [];\n for (const style of styles) {\n const sheet =\n style instanceof CSSStyleSheet\n ? style\n : ((style as {styleSheet?: CSSStyleSheet} | null)?.styleSheet ??\n undefined);\n if (!(sheet instanceof CSSStyleSheet)) {\n // Shimmed sheet (no constructible stylesheet support); bail out\n // rather than adopt a partial list.\n return;\n }\n sheets.push(sheet);\n }\n // Assign (never accumulate): replaces any previously adopted set.\n root.adoptedStyleSheets = sheets;\n};\n\nconst incompatible = (\n state: PatchState,\n tagName: string,\n reason: string\n): void => {\n if (state.options.onIncompatible === 'warn') {\n console.warn(\n `[lit-hmr] <${tagName}> can't be hot-patched (${reason}). ` +\n `Reload the page to pick up the change.`\n );\n } else {\n console.info(`[lit-hmr] <${tagName}>: ${reason} — performing full reload.`);\n location.reload();\n }\n};\n\n/**\n * Patches the originally-registered class for `record.tagName` in place\n * from the freshly evaluated `NewClass`, then updates live instances.\n */\nconst hotPatch = (\n state: PatchState,\n record: TagRecord,\n NewClass: ReactiveCtorLike\n): void => {\n const OldClass = record.canonical as ReactiveCtorLike;\n try {\n // 1. Materialize finalized/elementProperties/elementStyles/\n // __attributeToPropertyMap on the new class before we copy statics.\n if (typeof NewClass.finalize === 'function') {\n NewClass.finalize();\n }\n\n // 2. Standard-decorator `accessor` properties can't be patched in place.\n if (usesStandardDecorators(NewClass)) {\n incompatible(state, record.tagName, 'standard accessor decorators');\n return;\n }\n\n // The platform snapshots observedAttributes at define time; new\n // attribute observations can't take effect without a reload.\n const oldObserved = [...(OldClass.observedAttributes ?? [])];\n const newObserved = [...(NewClass.observedAttributes ?? [])];\n\n // 3. Snapshot reactive property values per live instance through the\n // OLD accessors. Vite serves prod lit in dev (no `development`\n // export condition), where accessor storage keys are unique\n // Symbol()s per class evaluation — without this, state resets.\n const propertyKeys = new Set<PropertyKey>([\n ...(OldClass.elementProperties?.keys() ?? []),\n ...(NewClass.elementProperties?.keys() ?? []),\n ]);\n const snapshots = new Map<ReactiveElementLike, Map<PropertyKey, unknown>>();\n for (const el of record.instances) {\n const values = new Map<PropertyKey, unknown>();\n for (const key of propertyKeys) {\n values.set(key, (el as unknown as Record<PropertyKey, unknown>)[key]);\n }\n snapshots.set(el, values);\n }\n\n // 4. Re-parent prototype chains. Mixins regenerate intermediate classes\n // on every module evaluation, so the parents usually differ.\n const newProtoParent = Object.getPrototypeOf(NewClass.prototype) as object;\n if (Object.getPrototypeOf(OldClass.prototype) !== newProtoParent) {\n Object.setPrototypeOf(OldClass.prototype, newProtoParent);\n }\n const newStaticParent = Object.getPrototypeOf(NewClass) as object;\n if (Object.getPrototypeOf(OldClass) !== newStaticParent) {\n Object.setPrototypeOf(OldClass, newStaticParent);\n }\n\n // 5. Sync own members new → old on the prototype and on statics.\n syncOwnMembers(OldClass.prototype, NewClass.prototype, PROTO_SKIP);\n syncOwnMembers(OldClass, NewClass, STATIC_SKIP);\n\n // 6. The sync replaced/removed our lifecycle wrappers; re-instrument.\n instrument(state, OldClass.prototype, record);\n record.generation++;\n // Module-local `instanceof NewClass` checks keep working against\n // instances of the canonical class.\n Object.defineProperty(NewClass, Symbol.hasInstance, {\n value: (instance: unknown) =>\n typeof instance === 'object' &&\n instance !== null &&\n OldClass.prototype.isPrototypeOf(instance),\n configurable: true,\n });\n\n if (\n oldObserved.length !== newObserved.length ||\n oldObserved.some((attr, i) => attr !== newObserved[i])\n ) {\n console.info(\n `[lit-hmr] <${record.tagName}> changed observedAttributes; the ` +\n `platform registry can't pick this up — reload recommended.`\n );\n }\n\n // 7. Update live instances.\n const newPropertyKeys = NewClass.elementProperties?.keys() ?? [];\n // After the static sync this is the NEW class's initializer list.\n const initializers = OldClass._initializers;\n for (const el of [...record.instances]) {\n if (state.options.reconnect) {\n el.disconnectedCallback?.();\n el.connectedCallback?.();\n }\n // Decorator-created reactive controllers (e.g. @lit/context's\n // @provide/@consume) live in per-class-evaluation closures keyed by\n // instance via addInitializer. Re-running the initializers enrolls\n // live instances in the new closures — without this, the first\n // assignment through a copied accessor throws and we'd full-reload.\n // Controllers from previous evaluations stay attached but inert\n // (everything flows through the newest closures); that's bounded by\n // edit count and dev-only. Runs before the value restore so e.g. a\n // re-created ContextProvider exists when the restore pushes into it.\n if (Array.isArray(initializers)) {\n for (const initialize of initializers) {\n initialize(el);\n }\n }\n const values = snapshots.get(el);\n if (values !== undefined) {\n // Restore through the new accessors — only keys the new class\n // still declares.\n for (const key of newPropertyKeys) {\n if (values.has(key)) {\n (el as unknown as Record<PropertyKey, unknown>)[key] =\n values.get(key);\n }\n }\n }\n readoptStyles(el);\n state.generationOf.set(el, record.generation);\n el.requestUpdate?.();\n }\n } catch (e) {\n incompatible(\n state,\n record.tagName,\n `patching failed: ${e instanceof Error ? e.message : String(e)}`\n );\n }\n};\n\n/**\n * Installs the `customElements.define` interceptor. Idempotent (per-page\n * global guard); no-ops in environments without `customElements` (SSR).\n */\nexport const install = (options: PatchOptions = {}): void => {\n if (typeof customElements === 'undefined') {\n return;\n }\n const g = globalThis as unknown as Record<symbol, PatchState | undefined>;\n const existing = g[STATE_KEY];\n if (existing !== undefined) {\n // Already installed; refresh explicitly-provided options only.\n if (options.reconnect !== undefined) {\n existing.options.reconnect = options.reconnect;\n }\n if (options.onIncompatible !== undefined) {\n existing.options.onIncompatible = options.onIncompatible;\n }\n return;\n }\n const state: PatchState = {\n records: new Map(),\n generationOf: new WeakMap(),\n options: {\n reconnect: options.reconnect ?? false,\n onIncompatible: options.onIncompatible ?? 'reload',\n },\n };\n g[STATE_KEY] = state;\n\n const nativeDefine = customElements.define;\n customElements.define = function (\n this: CustomElementRegistry,\n name: string,\n ctor: CustomElementConstructor,\n defineOptions?: ElementDefinitionOptions\n ) {\n let record = state.records.get(name);\n if (record === undefined && this.get(name) !== undefined) {\n // Defined before install() ran (shouldn't happen — the installer is\n // import-hoisted — but recover gracefully without instance tracking).\n record = {\n tagName: name,\n canonical: this.get(name)!,\n instances: new Set(),\n generation: 0,\n };\n state.records.set(name, record);\n }\n if (record === undefined) {\n record = {\n tagName: name,\n canonical: ctor,\n instances: new Set(),\n generation: 0,\n };\n state.records.set(name, record);\n instrument(state, ctor.prototype, record);\n return nativeDefine.call(this, name, ctor, defineOptions);\n }\n if (ctor === record.canonical) {\n // Same class object re-defined; nothing to patch.\n return;\n }\n hotPatch(state, record, ctor as ReactiveCtorLike);\n };\n};\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
import MagicString from 'magic-string';
|
|
7
|
+
/**
|
|
8
|
+
* Prefix for this plugin's virtual module ids. Rewritten specifiers carry
|
|
9
|
+
* the `\0` convention directly in the transformed source; Vite's
|
|
10
|
+
* import-analysis resolves them through our `resolveId` and encodes the
|
|
11
|
+
* `\0` for the browser, so it never reaches the network.
|
|
12
|
+
*/
|
|
13
|
+
export declare const VIRTUAL_PREFIX = "\0lit-hmr:";
|
|
14
|
+
/** Virtual module that installs the define interceptor. */
|
|
15
|
+
export declare const INSTALL_ID = "\0lit-hmr:install";
|
|
16
|
+
/**
|
|
17
|
+
* A module needs the define interceptor + self-accept if it registers
|
|
18
|
+
* custom elements: a textual `customElements.define(` call, or — only when
|
|
19
|
+
* a lit-family import is present — a `customElement(` decorator call (the
|
|
20
|
+
* decorator runtime call stays textually present after TS transpilation).
|
|
21
|
+
*/
|
|
22
|
+
export declare const isComponentModule: (code: string, hasLitFamilyImport: boolean) => boolean;
|
|
23
|
+
export interface LitHmrTransformResult {
|
|
24
|
+
code: string;
|
|
25
|
+
map: ReturnType<MagicString['generateMap']>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The whole dev transform, as a pure function of the module source:
|
|
29
|
+
*
|
|
30
|
+
* - Pass 1 (all user modules — shared template-partial modules must intern
|
|
31
|
+
* too): rewrite import/export-from/dynamic-import specifiers found in the
|
|
32
|
+
* wrap table to their `\0lit-hmr:` wrapper module.
|
|
33
|
+
* - Pass 2 (component modules): prepend the interceptor install import
|
|
34
|
+
* (static imports hoist, so the interceptor is installed before this
|
|
35
|
+
* module's defines) and append a self-accept (re-execution alone triggers
|
|
36
|
+
* the define interceptor — no hotUpdate hook needed).
|
|
37
|
+
*
|
|
38
|
+
* Returns `null` when the module needs no changes.
|
|
39
|
+
*/
|
|
40
|
+
export declare const transformLitModule: (code: string) => Promise<LitHmrTransformResult | null>;
|
|
41
|
+
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/transform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,WAAW,MAAM,cAAc,CAAC;AAGvC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,2DAA2D;AAC3D,eAAO,MAAM,UAAU,sBAA6B,CAAC;AASrD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,oBAAoB,OAAO,KAC1B,OAEwD,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,KACX,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAqDtC,CAAC"}
|
package/lib/transform.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
import { init, parse } from 'es-module-lexer';
|
|
7
|
+
import MagicString from 'magic-string';
|
|
8
|
+
import { WRAP_TABLE } from './wrap-table.js';
|
|
9
|
+
/**
|
|
10
|
+
* Prefix for this plugin's virtual module ids. Rewritten specifiers carry
|
|
11
|
+
* the `\0` convention directly in the transformed source; Vite's
|
|
12
|
+
* import-analysis resolves them through our `resolveId` and encodes the
|
|
13
|
+
* `\0` for the browser, so it never reaches the network.
|
|
14
|
+
*/
|
|
15
|
+
export const VIRTUAL_PREFIX = '\0lit-hmr:';
|
|
16
|
+
/** Virtual module that installs the define interceptor. */
|
|
17
|
+
export const INSTALL_ID = `${VIRTUAL_PREFIX}install`;
|
|
18
|
+
const LIT_FAMILY_PACKAGES = ['lit', 'lit-html', 'lit-element'];
|
|
19
|
+
const LIT_FAMILY_SCOPES = ['@lit/', '@lit-labs/'];
|
|
20
|
+
const isLitFamilySpecifier = (spec) => LIT_FAMILY_PACKAGES.some((p) => spec === p || spec.startsWith(`${p}/`)) ||
|
|
21
|
+
LIT_FAMILY_SCOPES.some((s) => spec.startsWith(s));
|
|
22
|
+
/**
|
|
23
|
+
* A module needs the define interceptor + self-accept if it registers
|
|
24
|
+
* custom elements: a textual `customElements.define(` call, or — only when
|
|
25
|
+
* a lit-family import is present — a `customElement(` decorator call (the
|
|
26
|
+
* decorator runtime call stays textually present after TS transpilation).
|
|
27
|
+
*/
|
|
28
|
+
export const isComponentModule = (code, hasLitFamilyImport) => code.includes('customElements.define(') ||
|
|
29
|
+
(hasLitFamilyImport && /\bcustomElement\s*\(/.test(code));
|
|
30
|
+
/**
|
|
31
|
+
* The whole dev transform, as a pure function of the module source:
|
|
32
|
+
*
|
|
33
|
+
* - Pass 1 (all user modules — shared template-partial modules must intern
|
|
34
|
+
* too): rewrite import/export-from/dynamic-import specifiers found in the
|
|
35
|
+
* wrap table to their `\0lit-hmr:` wrapper module.
|
|
36
|
+
* - Pass 2 (component modules): prepend the interceptor install import
|
|
37
|
+
* (static imports hoist, so the interceptor is installed before this
|
|
38
|
+
* module's defines) and append a self-accept (re-execution alone triggers
|
|
39
|
+
* the define interceptor — no hotUpdate hook needed).
|
|
40
|
+
*
|
|
41
|
+
* Returns `null` when the module needs no changes.
|
|
42
|
+
*/
|
|
43
|
+
export const transformLitModule = async (code) => {
|
|
44
|
+
// Cheap pre-filter before parsing.
|
|
45
|
+
if (!code.includes('lit') && !code.includes('customElements')) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
// Idempotency: never re-instrument our own output.
|
|
49
|
+
if (code.includes(VIRTUAL_PREFIX)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
await init;
|
|
53
|
+
let imports;
|
|
54
|
+
try {
|
|
55
|
+
[imports] = parse(code);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Not parseable as a module; leave it to Vite to error.
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const ms = new MagicString(code);
|
|
62
|
+
let changed = false;
|
|
63
|
+
let hasLitFamilyImport = false;
|
|
64
|
+
for (const imp of imports) {
|
|
65
|
+
const spec = imp.n;
|
|
66
|
+
if (spec === undefined) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (isLitFamilySpecifier(spec)) {
|
|
70
|
+
hasLitFamilyImport = true;
|
|
71
|
+
}
|
|
72
|
+
if (!WRAP_TABLE.has(spec)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (imp.d > -1) {
|
|
76
|
+
// Dynamic import: [s, e) includes the quotes (or backticks) — narrow
|
|
77
|
+
// them to plain quotes around the rewritten specifier.
|
|
78
|
+
ms.overwrite(imp.s, imp.e, `'${VIRTUAL_PREFIX}${spec}'`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Static import / export-from: [s, e) is the bare specifier text.
|
|
82
|
+
ms.overwrite(imp.s, imp.e, `${VIRTUAL_PREFIX}${spec}`);
|
|
83
|
+
}
|
|
84
|
+
changed = true;
|
|
85
|
+
}
|
|
86
|
+
if (isComponentModule(code, hasLitFamilyImport)) {
|
|
87
|
+
ms.prepend(`import '${INSTALL_ID}';\n`);
|
|
88
|
+
ms.append(`\nimport.meta.hot?.accept();\n`);
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
if (!changed) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
code: ms.toString(),
|
|
96
|
+
map: ms.generateMap({ hires: 'boundary' }),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/transform.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nimport {init, parse} from 'es-module-lexer';\nimport MagicString from 'magic-string';\nimport {WRAP_TABLE} from './wrap-table.js';\n\n/**\n * Prefix for this plugin's virtual module ids. Rewritten specifiers carry\n * the `\\0` convention directly in the transformed source; Vite's\n * import-analysis resolves them through our `resolveId` and encodes the\n * `\\0` for the browser, so it never reaches the network.\n */\nexport const VIRTUAL_PREFIX = '\\0lit-hmr:';\n\n/** Virtual module that installs the define interceptor. */\nexport const INSTALL_ID = `${VIRTUAL_PREFIX}install`;\n\nconst LIT_FAMILY_PACKAGES = ['lit', 'lit-html', 'lit-element'];\nconst LIT_FAMILY_SCOPES = ['@lit/', '@lit-labs/'];\n\nconst isLitFamilySpecifier = (spec: string): boolean =>\n LIT_FAMILY_PACKAGES.some((p) => spec === p || spec.startsWith(`${p}/`)) ||\n LIT_FAMILY_SCOPES.some((s) => spec.startsWith(s));\n\n/**\n * A module needs the define interceptor + self-accept if it registers\n * custom elements: a textual `customElements.define(` call, or — only when\n * a lit-family import is present — a `customElement(` decorator call (the\n * decorator runtime call stays textually present after TS transpilation).\n */\nexport const isComponentModule = (\n code: string,\n hasLitFamilyImport: boolean\n): boolean =>\n code.includes('customElements.define(') ||\n (hasLitFamilyImport && /\\bcustomElement\\s*\\(/.test(code));\n\nexport interface LitHmrTransformResult {\n code: string;\n map: ReturnType<MagicString['generateMap']>;\n}\n\n/**\n * The whole dev transform, as a pure function of the module source:\n *\n * - Pass 1 (all user modules — shared template-partial modules must intern\n * too): rewrite import/export-from/dynamic-import specifiers found in the\n * wrap table to their `\\0lit-hmr:` wrapper module.\n * - Pass 2 (component modules): prepend the interceptor install import\n * (static imports hoist, so the interceptor is installed before this\n * module's defines) and append a self-accept (re-execution alone triggers\n * the define interceptor — no hotUpdate hook needed).\n *\n * Returns `null` when the module needs no changes.\n */\nexport const transformLitModule = async (\n code: string\n): Promise<LitHmrTransformResult | null> => {\n // Cheap pre-filter before parsing.\n if (!code.includes('lit') && !code.includes('customElements')) {\n return null;\n }\n // Idempotency: never re-instrument our own output.\n if (code.includes(VIRTUAL_PREFIX)) {\n return null;\n }\n await init;\n let imports;\n try {\n [imports] = parse(code);\n } catch {\n // Not parseable as a module; leave it to Vite to error.\n return null;\n }\n const ms = new MagicString(code);\n let changed = false;\n let hasLitFamilyImport = false;\n for (const imp of imports) {\n const spec = imp.n;\n if (spec === undefined) {\n continue;\n }\n if (isLitFamilySpecifier(spec)) {\n hasLitFamilyImport = true;\n }\n if (!WRAP_TABLE.has(spec)) {\n continue;\n }\n if (imp.d > -1) {\n // Dynamic import: [s, e) includes the quotes (or backticks) — narrow\n // them to plain quotes around the rewritten specifier.\n ms.overwrite(imp.s, imp.e, `'${VIRTUAL_PREFIX}${spec}'`);\n } else {\n // Static import / export-from: [s, e) is the bare specifier text.\n ms.overwrite(imp.s, imp.e, `${VIRTUAL_PREFIX}${spec}`);\n }\n changed = true;\n }\n if (isComponentModule(code, hasLitFamilyImport)) {\n ms.prepend(`import '${INSTALL_ID}';\\n`);\n ms.append(`\\nimport.meta.hot?.accept();\\n`);\n changed = true;\n }\n if (!changed) {\n return null;\n }\n return {\n code: ms.toString(),\n map: ms.generateMap({hires: 'boundary'}),\n };\n};\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The exact wrap surface: which template-tag exports of which lit-family
|
|
8
|
+
* module specifiers get interned, and in which namespace.
|
|
9
|
+
*
|
|
10
|
+
* Namespaces are per tag *type*, not per module — `html` from `'lit'` and
|
|
11
|
+
* from `'lit-html'` feed the same lit-html template cache, so they must
|
|
12
|
+
* share canonical arrays. But content-equal `html` and `svg` templates must
|
|
13
|
+
* never share one (a shared Template would parse SVG as HTML), and the
|
|
14
|
+
* static-html tags pass zero-statics calls straight through to lit-html, so
|
|
15
|
+
* they get their own namespaces too.
|
|
16
|
+
*
|
|
17
|
+
* Specifiers must be valid export-map subpaths (e.g. `lit/index.js` and
|
|
18
|
+
* `lit-html/lit-html.js` are not, and are deliberately absent).
|
|
19
|
+
*/
|
|
20
|
+
export interface WrappedTag {
|
|
21
|
+
readonly exportName: 'html' | 'svg' | 'mathml' | 'css';
|
|
22
|
+
readonly ns: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const WRAP_TABLE: ReadonlyMap<string, readonly WrappedTag[]>;
|
|
25
|
+
//# sourceMappingURL=wrap-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-table.d.ts","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/wrap-table.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAC;IACvD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAQD,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAehE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
const tags = (...names) => names.map((exportName) => ({ exportName, ns: exportName }));
|
|
7
|
+
const staticTags = (...names) => names.map((exportName) => ({ exportName, ns: `static-${exportName}` }));
|
|
8
|
+
export const WRAP_TABLE = new Map([
|
|
9
|
+
['lit', tags('html', 'svg', 'mathml', 'css')],
|
|
10
|
+
['lit/html.js', tags('html', 'svg', 'mathml')],
|
|
11
|
+
['lit/static-html.js', staticTags('html', 'svg', 'mathml')],
|
|
12
|
+
['lit-html', tags('html', 'svg', 'mathml')],
|
|
13
|
+
['lit-html/static.js', staticTags('html', 'svg', 'mathml')],
|
|
14
|
+
['lit-element', tags('html', 'svg', 'mathml', 'css')],
|
|
15
|
+
['lit-element/lit-element.js', tags('html', 'svg', 'mathml', 'css')],
|
|
16
|
+
['@lit/reactive-element', tags('css')],
|
|
17
|
+
['@lit/reactive-element/css-tag.js', tags('css')],
|
|
18
|
+
// The signals tags wrap the user's values but pass the strings array
|
|
19
|
+
// straight through to lit-html's core tags, so they intern in the same
|
|
20
|
+
// namespaces as plain html/svg (content-equal templates share a cache
|
|
21
|
+
// entry either way).
|
|
22
|
+
['@lit-labs/signals', tags('html', 'svg')],
|
|
23
|
+
]);
|
|
24
|
+
//# sourceMappingURL=wrap-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-table.js","sourceRoot":"","sources":["../../../packages/labs/vite-hmr/src/lib/wrap-table.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsBH,MAAM,IAAI,GAAG,CAAC,GAAG,KAAsC,EAAgB,EAAE,CACvE,KAAK,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAC,UAAU,EAAE,EAAE,EAAE,UAAU,EAAC,CAAC,CAAC,CAAC;AAE5D,MAAM,UAAU,GAAG,CAAC,GAAG,KAAsC,EAAgB,EAAE,CAC7E,KAAK,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAC,UAAU,EAAE,EAAE,EAAE,UAAU,UAAU,EAAE,EAAC,CAAC,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,UAAU,GAA+C,IAAI,GAAG,CAAC;IAC5E,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC,oBAAoB,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC,oBAAoB,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC,4BAA4B,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC,uBAAuB,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,kCAAkC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,qEAAqE;IACrE,uEAAuE;IACvE,sEAAsE;IACtE,qBAAqB;IACrB,CAAC,mBAAmB,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC3C,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * The exact wrap surface: which template-tag exports of which lit-family\n * module specifiers get interned, and in which namespace.\n *\n * Namespaces are per tag *type*, not per module — `html` from `'lit'` and\n * from `'lit-html'` feed the same lit-html template cache, so they must\n * share canonical arrays. But content-equal `html` and `svg` templates must\n * never share one (a shared Template would parse SVG as HTML), and the\n * static-html tags pass zero-statics calls straight through to lit-html, so\n * they get their own namespaces too.\n *\n * Specifiers must be valid export-map subpaths (e.g. `lit/index.js` and\n * `lit-html/lit-html.js` are not, and are deliberately absent).\n */\n\nexport interface WrappedTag {\n readonly exportName: 'html' | 'svg' | 'mathml' | 'css';\n readonly ns: string;\n}\n\nconst tags = (...names: Array<WrappedTag['exportName']>): WrappedTag[] =>\n names.map((exportName) => ({exportName, ns: exportName}));\n\nconst staticTags = (...names: Array<WrappedTag['exportName']>): WrappedTag[] =>\n names.map((exportName) => ({exportName, ns: `static-${exportName}`}));\n\nexport const WRAP_TABLE: ReadonlyMap<string, readonly WrappedTag[]> = new Map([\n ['lit', tags('html', 'svg', 'mathml', 'css')],\n ['lit/html.js', tags('html', 'svg', 'mathml')],\n ['lit/static-html.js', staticTags('html', 'svg', 'mathml')],\n ['lit-html', tags('html', 'svg', 'mathml')],\n ['lit-html/static.js', staticTags('html', 'svg', 'mathml')],\n ['lit-element', tags('html', 'svg', 'mathml', 'css')],\n ['lit-element/lit-element.js', tags('html', 'svg', 'mathml', 'css')],\n ['@lit/reactive-element', tags('css')],\n ['@lit/reactive-element/css-tag.js', tags('css')],\n // The signals tags wrap the user's values but pass the strings array\n // straight through to lit-html's core tags, so they intern in the same\n // namespaces as plain html/svg (content-equal templates share a cache\n // entry either way).\n ['@lit-labs/signals', tags('html', 'svg')],\n]);\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oddsquad/vite-plugin-lit",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Vite plugin providing true HMR for Lit components",
|
|
5
|
+
"license": "BSD-3-Clause",
|
|
6
|
+
"author": "Oddsquad (based on @lit-labs/vite-hmr by Google LLC)",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"module": "index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"/lib/",
|
|
12
|
+
"/index.{d.ts,d.ts.map,js,js.map}"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./index.d.ts",
|
|
17
|
+
"default": "./index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"es-module-lexer": "^1.5.4",
|
|
26
|
+
"magic-string": "^0.30.11"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vite": "^8.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.7.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
39
|
+
}
|
|
40
|
+
}
|