lithent 1.21.0 → 1.21.2
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/README.md +1 -1
- package/devHelper/dist/lithentDevHelper.mjs +48 -31
- package/devHelper/dist/lithentDevHelper.mjs.map +1 -1
- package/devHelper/dist/lithentDevHelper.umd.js +1 -1
- package/devHelper/dist/lithentDevHelper.umd.js.map +1 -1
- package/devHelper/src/createBoundary.ts +44 -4
- package/devHelper/src/tests/createBoundaryRetry.test.ts +52 -0
- package/devHelper/src/tests/hmrBoundary.tsx +1 -1
- package/dist/lithent.mjs +186 -170
- package/dist/lithent.mjs.map +1 -1
- package/dist/lithent.umd.js +1 -1
- package/dist/lithent.umd.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/examples/dist/assets/index-DYJGSNhf.js +2 -0
- package/examples/dist/assets/index-DYJGSNhf.js.map +1 -0
- package/examples/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/render.ts +56 -19
- package/src/tests/core-nullUpdater.test.tsx +33 -0
- package/src/types/index.ts +11 -4
- package/src/wDom.ts +6 -4
- package/examples/dist/assets/index-C8ubEFcX.js +0 -2
- package/examples/dist/assets/index-C8ubEFcX.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ It trims away unnecessary magic and complex APIs so your UI code stays simple, e
|
|
|
10
10
|
|
|
11
11
|
<br />
|
|
12
12
|
|
|
13
|
-
<div align="
|
|
13
|
+
<div align="left">
|
|
14
14
|
|
|
15
15
|
### 🚀 [Get Started](https://superlucky84.github.io/lithent/#/guide/quick-start) · 📖 [Documentation](https://superlucky84.github.io/lithent/#/) · 💡 [Examples](https://superlucky84.github.io/lithent/#/examples/1)
|
|
16
16
|
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { setComponentMapManualMode as
|
|
1
|
+
import { setComponentMapManualMode as R, disposeComponentEntry as b, componentMap as p, replaceWDom as B } from "lithent";
|
|
2
2
|
let c = 0;
|
|
3
|
-
const
|
|
4
|
-
c += 1,
|
|
5
|
-
},
|
|
6
|
-
c = Math.max(0, c - 1), c === 0 &&
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
}, s = /* @__PURE__ */ new Map(),
|
|
3
|
+
const w = () => {
|
|
4
|
+
c += 1, R(!0);
|
|
5
|
+
}, E = () => {
|
|
6
|
+
c = Math.max(0, c - 1), c === 0 && R(!1);
|
|
7
|
+
}, x = (t) => {
|
|
8
|
+
b(t);
|
|
9
|
+
}, D = 3, s = /* @__PURE__ */ new Map(), U = (t) => {
|
|
10
10
|
let e = s.get(t);
|
|
11
11
|
return e || (e = {
|
|
12
12
|
instances: /* @__PURE__ */ new Set(),
|
|
13
13
|
domMap: /* @__PURE__ */ new Map(),
|
|
14
14
|
scheduled: !1,
|
|
15
|
-
pendingCtor: void 0
|
|
15
|
+
pendingCtor: void 0,
|
|
16
|
+
retryCount: 0
|
|
16
17
|
}, s.set(t, e)), e;
|
|
17
|
-
},
|
|
18
|
-
const n =
|
|
18
|
+
}, W = (t, e) => {
|
|
19
|
+
const n = U(t);
|
|
19
20
|
return n.instances.add(e), queueMicrotask(() => {
|
|
20
21
|
var o;
|
|
21
22
|
const r = p.get(e), a = ((o = r == null ? void 0 : r.vd) == null ? void 0 : o.value) ?? null;
|
|
@@ -24,15 +25,15 @@ const R = () => {
|
|
|
24
25
|
const r = s.get(t);
|
|
25
26
|
r && (r.instances.delete(e), r.domMap.delete(e), r.instances.size || s.delete(t));
|
|
26
27
|
};
|
|
27
|
-
},
|
|
28
|
+
}, $ = (t) => {
|
|
28
29
|
const e = s.get(t);
|
|
29
30
|
e && Array.from(e.instances).forEach(
|
|
30
|
-
(n) =>
|
|
31
|
-
), s.delete(t),
|
|
31
|
+
(n) => x(n)
|
|
32
|
+
), s.delete(t), E();
|
|
32
33
|
}, k = (t, e) => {
|
|
33
34
|
const n = s.get(t);
|
|
34
|
-
return n ? (n.pendingCtor = e, n.scheduled || (n.scheduled = !0, queueMicrotask(() =>
|
|
35
|
-
},
|
|
35
|
+
return n ? (n.pendingCtor = e, n.retryCount = 0, n.scheduled || (n.scheduled = !0, queueMicrotask(() => v(t))), !0) : !1;
|
|
36
|
+
}, v = (t) => {
|
|
36
37
|
const e = s.get(t);
|
|
37
38
|
if (!e) return;
|
|
38
39
|
e.scheduled = !1;
|
|
@@ -41,32 +42,48 @@ const R = () => {
|
|
|
41
42
|
return;
|
|
42
43
|
e.pendingCtor = void 0;
|
|
43
44
|
let r = !1, a = !1;
|
|
44
|
-
e.instances.forEach((
|
|
45
|
-
var
|
|
46
|
-
const
|
|
45
|
+
e.instances.forEach((u) => {
|
|
46
|
+
var h, y, C;
|
|
47
|
+
const d = p.get(u), m = e.domMap.get(u), i = ((h = d == null ? void 0 : d.vd) == null ? void 0 : h.value) || m;
|
|
47
48
|
if (!i || !i.el) {
|
|
48
49
|
a = !0;
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
|
-
const { compProps:
|
|
52
|
-
if (!
|
|
52
|
+
const { compProps: g, compChild: M } = i;
|
|
53
|
+
if (!g || !M) {
|
|
53
54
|
a = !0;
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
57
|
try {
|
|
57
|
-
B(n,
|
|
58
|
-
const
|
|
59
|
-
e.domMap.set(
|
|
60
|
-
} catch (
|
|
61
|
-
console.warn(`[Lithent HMR] boundary update 실패: ${t}`,
|
|
58
|
+
B(n, g, M, i);
|
|
59
|
+
const l = ((C = (y = p.get(u)) == null ? void 0 : y.vd) == null ? void 0 : C.value) || i;
|
|
60
|
+
e.domMap.set(u, l), r = !0;
|
|
61
|
+
} catch (l) {
|
|
62
|
+
console.warn(`[Lithent HMR] boundary update 실패: ${t}`, l);
|
|
62
63
|
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
});
|
|
65
|
+
let o = e.pendingCtor;
|
|
66
|
+
const f = a && !r;
|
|
67
|
+
if (!(f || !!o)) {
|
|
68
|
+
e.retryCount = 0;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (f) {
|
|
72
|
+
if (e.retryCount += 1, e.retryCount > D) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`[Lithent HMR] boundary update failed after ${e.retryCount} attempts; aborting: ${t}`
|
|
75
|
+
), e.pendingCtor = void 0, e.scheduled = !1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
o || (o = n);
|
|
79
|
+
}
|
|
80
|
+
e.pendingCtor = o, e.scheduled || (e.scheduled = !0, setTimeout(() => v(t), 0));
|
|
81
|
+
}, L = (t) => (w(), {
|
|
82
|
+
register: (e) => W(t, e),
|
|
66
83
|
update: (e) => k(t, e),
|
|
67
|
-
dispose: () =>
|
|
84
|
+
dispose: () => $(t)
|
|
68
85
|
});
|
|
69
86
|
export {
|
|
70
|
-
|
|
87
|
+
L as createBoundary
|
|
71
88
|
};
|
|
72
89
|
//# sourceMappingURL=lithentDevHelper.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lithentDevHelper.mjs","sources":["../src/componentMapControl.ts","../src/createBoundary.ts"],"sourcesContent":["import type { CompKey } from 'lithent';\nimport { setComponentMapManualMode, disposeComponentEntry } from 'lithent';\n\nlet manualModeRefCount = 0;\n\nexport const enableComponentMapManualMode = (): void => {\n manualModeRefCount += 1;\n setComponentMapManualMode(true);\n};\n\nexport const disableComponentMapManualMode = (): void => {\n manualModeRefCount = Math.max(0, manualModeRefCount - 1);\n if (manualModeRefCount === 0) {\n setComponentMapManualMode(false);\n }\n};\n\nexport const removeComponentEntry = (compKey: CompKey): void => {\n disposeComponentEntry(compKey);\n};\n","import type { CompKey, TagFunction, WDom } from 'lithent';\nimport { componentMap, replaceWDom } from 'lithent';\nimport {\n disableComponentMapManualMode,\n enableComponentMapManualMode,\n removeComponentEntry,\n} from './componentMapControl';\n\ntype InstanceRegistry = {\n instances: Set<CompKey>;\n domMap: Map<CompKey, WDom>;\n scheduled: boolean;\n pendingCtor?: TagFunction;\n};\n\nconst boundaryRegistry = new Map<string, InstanceRegistry>();\n\nconst getRegistry = (moduleId: string): InstanceRegistry => {\n let registry = boundaryRegistry.get(moduleId);\n\n if (!registry) {\n registry = {\n instances: new Set(),\n domMap: new Map(),\n scheduled: false,\n pendingCtor: undefined,\n };\n boundaryRegistry.set(moduleId, registry);\n }\n\n return registry;\n};\n\nexport type BoundaryController = {\n register: (compKey: CompKey) => () => void;\n update: (nextCtor: TagFunction) => boolean;\n dispose: () => void;\n};\n\nexport const registerBoundaryInstance = (\n moduleId: string,\n compKey: CompKey\n) => {\n const registry = getRegistry(moduleId);\n\n registry.instances.add(compKey);\n\n queueMicrotask(() => {\n const entry = componentMap.get(compKey);\n const currentWDom = entry?.vd?.value ?? null;\n\n if (currentWDom) {\n registry.domMap.set(compKey, currentWDom);\n }\n });\n\n return () => {\n const currentRegistry = boundaryRegistry.get(moduleId);\n if (!currentRegistry) return;\n\n currentRegistry.instances.delete(compKey);\n currentRegistry.domMap.delete(compKey);\n\n if (!currentRegistry.instances.size) {\n boundaryRegistry.delete(moduleId);\n }\n };\n};\n\nexport const disposeBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (registry) {\n Array.from(registry.instances).forEach(compKey =>\n removeComponentEntry(compKey)\n );\n }\n\n boundaryRegistry.delete(moduleId);\n disableComponentMapManualMode();\n};\n\nexport const applyBoundaryUpdate = (\n moduleId: string,\n nextCtor: TagFunction\n) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (!registry) return false;\n\n registry.pendingCtor = nextCtor;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n queueMicrotask(() => flushBoundary(moduleId));\n }\n\n return true;\n};\n\nconst flushBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n if (!registry) return;\n\n registry.scheduled = false;\n\n const ctor = registry.pendingCtor;\n if (!ctor) {\n return;\n }\n\n registry.pendingCtor = undefined;\n\n let updated = false;\n let hasMissing = false;\n\n registry.instances.forEach(compKey => {\n const entry = componentMap.get(compKey);\n const cached = registry.domMap.get(compKey);\n const currentWDom = entry?.vd?.value || cached;\n\n if (!currentWDom || !currentWDom.el) {\n hasMissing = true;\n return;\n }\n\n const { compProps, compChild } = currentWDom;\n\n if (!compProps || !compChild) {\n hasMissing = true;\n return;\n }\n\n try {\n replaceWDom(ctor, compProps, compChild, currentWDom as any);\n const refreshed = componentMap.get(compKey)?.vd?.value || currentWDom;\n registry.domMap.set(compKey, refreshed as WDom);\n updated = true;\n } catch (error) {\n console.warn(`[Lithent HMR] boundary update 실패: ${moduleId}`, error);\n }\n });\n\n
|
|
1
|
+
{"version":3,"file":"lithentDevHelper.mjs","sources":["../src/componentMapControl.ts","../src/createBoundary.ts"],"sourcesContent":["import type { CompKey } from 'lithent';\nimport { setComponentMapManualMode, disposeComponentEntry } from 'lithent';\n\nlet manualModeRefCount = 0;\n\nexport const enableComponentMapManualMode = (): void => {\n manualModeRefCount += 1;\n setComponentMapManualMode(true);\n};\n\nexport const disableComponentMapManualMode = (): void => {\n manualModeRefCount = Math.max(0, manualModeRefCount - 1);\n if (manualModeRefCount === 0) {\n setComponentMapManualMode(false);\n }\n};\n\nexport const removeComponentEntry = (compKey: CompKey): void => {\n disposeComponentEntry(compKey);\n};\n","import type { CompKey, TagFunction, WDom } from 'lithent';\nimport { componentMap, replaceWDom } from 'lithent';\nimport {\n disableComponentMapManualMode,\n enableComponentMapManualMode,\n removeComponentEntry,\n} from './componentMapControl';\n\nconst MAX_RETRY = 3;\n\ntype InstanceRegistry = {\n instances: Set<CompKey>;\n domMap: Map<CompKey, WDom>;\n scheduled: boolean;\n pendingCtor?: TagFunction;\n retryCount: number;\n};\n\nconst boundaryRegistry = new Map<string, InstanceRegistry>();\n\nconst getRegistry = (moduleId: string): InstanceRegistry => {\n let registry = boundaryRegistry.get(moduleId);\n\n if (!registry) {\n registry = {\n instances: new Set(),\n domMap: new Map(),\n scheduled: false,\n pendingCtor: undefined,\n retryCount: 0,\n };\n boundaryRegistry.set(moduleId, registry);\n }\n\n return registry;\n};\n\nexport type BoundaryController = {\n register: (compKey: CompKey) => () => void;\n update: (nextCtor: TagFunction) => boolean;\n dispose: () => void;\n};\n\nexport const registerBoundaryInstance = (\n moduleId: string,\n compKey: CompKey\n) => {\n const registry = getRegistry(moduleId);\n\n registry.instances.add(compKey);\n\n queueMicrotask(() => {\n const entry = componentMap.get(compKey);\n const currentWDom = entry?.vd?.value ?? null;\n\n if (currentWDom) {\n registry.domMap.set(compKey, currentWDom);\n }\n });\n\n return () => {\n const currentRegistry = boundaryRegistry.get(moduleId);\n if (!currentRegistry) return;\n\n currentRegistry.instances.delete(compKey);\n currentRegistry.domMap.delete(compKey);\n\n if (!currentRegistry.instances.size) {\n boundaryRegistry.delete(moduleId);\n }\n };\n};\n\nexport const disposeBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (registry) {\n Array.from(registry.instances).forEach(compKey =>\n removeComponentEntry(compKey)\n );\n }\n\n boundaryRegistry.delete(moduleId);\n disableComponentMapManualMode();\n};\n\nexport const applyBoundaryUpdate = (\n moduleId: string,\n nextCtor: TagFunction\n) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (!registry) return false;\n\n registry.pendingCtor = nextCtor;\n registry.retryCount = 0;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n queueMicrotask(() => flushBoundary(moduleId));\n }\n\n return true;\n};\n\nconst flushBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n if (!registry) return;\n\n registry.scheduled = false;\n\n const ctor = registry.pendingCtor;\n if (!ctor) {\n return;\n }\n\n registry.pendingCtor = undefined;\n\n let updated = false;\n let hasMissing = false;\n\n registry.instances.forEach(compKey => {\n const entry = componentMap.get(compKey);\n const cached = registry.domMap.get(compKey);\n const currentWDom = entry?.vd?.value || cached;\n\n if (!currentWDom || !currentWDom.el) {\n hasMissing = true;\n return;\n }\n\n const { compProps, compChild } = currentWDom;\n\n if (!compProps || !compChild) {\n hasMissing = true;\n return;\n }\n\n try {\n replaceWDom(ctor, compProps, compChild, currentWDom as any);\n const refreshed = componentMap.get(compKey)?.vd?.value || currentWDom;\n registry.domMap.set(compKey, refreshed as WDom);\n updated = true;\n } catch (error) {\n console.warn(`[Lithent HMR] boundary update 실패: ${moduleId}`, error);\n }\n });\n\n let nextPendingCtor: TagFunction | undefined = registry.pendingCtor;\n const shouldRetryMissing = hasMissing && !updated;\n const hasQueuedUpdate = Boolean(nextPendingCtor);\n const shouldRetry = shouldRetryMissing || hasQueuedUpdate;\n\n if (!shouldRetry) {\n registry.retryCount = 0;\n return;\n }\n\n if (shouldRetryMissing) {\n registry.retryCount += 1;\n\n if (registry.retryCount > MAX_RETRY) {\n console.warn(\n `[Lithent HMR] boundary update failed after ${registry.retryCount} attempts; aborting: ${moduleId}`\n );\n\n const hot = (\n import.meta as unknown as {\n hot?: { invalidate?: () => void };\n }\n ).hot;\n hot?.invalidate?.();\n\n registry.pendingCtor = undefined;\n registry.scheduled = false;\n return;\n }\n\n if (!nextPendingCtor) {\n nextPendingCtor = ctor;\n }\n }\n\n registry.pendingCtor = nextPendingCtor;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n setTimeout(() => flushBoundary(moduleId), 0);\n }\n};\n\nexport const createBoundary = (moduleId: string): BoundaryController => {\n enableComponentMapManualMode();\n\n return {\n register: (compKey: CompKey) => registerBoundaryInstance(moduleId, compKey),\n update: (nextCtor: TagFunction) => applyBoundaryUpdate(moduleId, nextCtor),\n dispose: () => disposeBoundary(moduleId),\n };\n};\n"],"names":["manualModeRefCount","enableComponentMapManualMode","setComponentMapManualMode","disableComponentMapManualMode","removeComponentEntry","compKey","disposeComponentEntry","MAX_RETRY","boundaryRegistry","getRegistry","moduleId","registry","registerBoundaryInstance","entry","componentMap","currentWDom","_a","currentRegistry","disposeBoundary","applyBoundaryUpdate","nextCtor","flushBoundary","ctor","updated","hasMissing","cached","compProps","compChild","replaceWDom","refreshed","_c","_b","error","nextPendingCtor","shouldRetryMissing","createBoundary"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(a,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("lithent")):typeof define=="function"&&define.amd?define(["exports","lithent"],r):(a=typeof globalThis<"u"?globalThis:a||self,r(a.lithentDevHelper={},a.lithent))})(this,function(a,r){"use strict";let
|
|
1
|
+
(function(a,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("lithent")):typeof define=="function"&&define.amd?define(["exports","lithent"],r):(a=typeof globalThis<"u"?globalThis:a||self,r(a.lithentDevHelper={},a.lithent))})(this,function(a,r){"use strict";let c=0;const R=()=>{c+=1,r.setComponentMapManualMode(!0)},b=()=>{c=Math.max(0,c-1),c===0&&r.setComponentMapManualMode(!1)},B=t=>{r.disposeComponentEntry(t)},w=3,s=new Map,E=t=>{let e=s.get(t);return e||(e={instances:new Set,domMap:new Map,scheduled:!1,pendingCtor:void 0,retryCount:0},s.set(t,e)),e},T=(t,e)=>{const n=E(t);return n.instances.add(e),queueMicrotask(()=>{var i;const o=r.componentMap.get(e),u=((i=o==null?void 0:o.vd)==null?void 0:i.value)??null;u&&n.domMap.set(e,u)}),()=>{const o=s.get(t);o&&(o.instances.delete(e),o.domMap.delete(e),o.instances.size||s.delete(t))}},x=t=>{const e=s.get(t);e&&Array.from(e.instances).forEach(n=>B(n)),s.delete(t),b()},D=(t,e)=>{const n=s.get(t);return n?(n.pendingCtor=e,n.retryCount=0,n.scheduled||(n.scheduled=!0,queueMicrotask(()=>g(t))),!0):!1},g=t=>{const e=s.get(t);if(!e)return;e.scheduled=!1;const n=e.pendingCtor;if(!n)return;e.pendingCtor=void 0;let o=!1,u=!1;e.instances.forEach(p=>{var C,v,m;const f=r.componentMap.get(p),H=e.domMap.get(p),d=((C=f==null?void 0:f.vd)==null?void 0:C.value)||H;if(!d||!d.el){u=!0;return}const{compProps:y,compChild:h}=d;if(!y||!h){u=!0;return}try{r.replaceWDom(n,y,h,d);const l=((m=(v=r.componentMap.get(p))==null?void 0:v.vd)==null?void 0:m.value)||d;e.domMap.set(p,l),o=!0}catch(l){console.warn(`[Lithent HMR] boundary update 실패: ${t}`,l)}});let i=e.pendingCtor;const M=u&&!o;if(!(M||!!i)){e.retryCount=0;return}if(M){if(e.retryCount+=1,e.retryCount>w){console.warn(`[Lithent HMR] boundary update failed after ${e.retryCount} attempts; aborting: ${t}`),e.pendingCtor=void 0,e.scheduled=!1;return}i||(i=n)}e.pendingCtor=i,e.scheduled||(e.scheduled=!0,setTimeout(()=>g(t),0))},q=t=>(R(),{register:e=>T(t,e),update:e=>D(t,e),dispose:()=>x(t)});a.createBoundary=q,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=lithentDevHelper.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lithentDevHelper.umd.js","sources":["../src/componentMapControl.ts","../src/createBoundary.ts"],"sourcesContent":["import type { CompKey } from 'lithent';\nimport { setComponentMapManualMode, disposeComponentEntry } from 'lithent';\n\nlet manualModeRefCount = 0;\n\nexport const enableComponentMapManualMode = (): void => {\n manualModeRefCount += 1;\n setComponentMapManualMode(true);\n};\n\nexport const disableComponentMapManualMode = (): void => {\n manualModeRefCount = Math.max(0, manualModeRefCount - 1);\n if (manualModeRefCount === 0) {\n setComponentMapManualMode(false);\n }\n};\n\nexport const removeComponentEntry = (compKey: CompKey): void => {\n disposeComponentEntry(compKey);\n};\n","import type { CompKey, TagFunction, WDom } from 'lithent';\nimport { componentMap, replaceWDom } from 'lithent';\nimport {\n disableComponentMapManualMode,\n enableComponentMapManualMode,\n removeComponentEntry,\n} from './componentMapControl';\n\ntype InstanceRegistry = {\n instances: Set<CompKey>;\n domMap: Map<CompKey, WDom>;\n scheduled: boolean;\n pendingCtor?: TagFunction;\n};\n\nconst boundaryRegistry = new Map<string, InstanceRegistry>();\n\nconst getRegistry = (moduleId: string): InstanceRegistry => {\n let registry = boundaryRegistry.get(moduleId);\n\n if (!registry) {\n registry = {\n instances: new Set(),\n domMap: new Map(),\n scheduled: false,\n pendingCtor: undefined,\n };\n boundaryRegistry.set(moduleId, registry);\n }\n\n return registry;\n};\n\nexport type BoundaryController = {\n register: (compKey: CompKey) => () => void;\n update: (nextCtor: TagFunction) => boolean;\n dispose: () => void;\n};\n\nexport const registerBoundaryInstance = (\n moduleId: string,\n compKey: CompKey\n) => {\n const registry = getRegistry(moduleId);\n\n registry.instances.add(compKey);\n\n queueMicrotask(() => {\n const entry = componentMap.get(compKey);\n const currentWDom = entry?.vd?.value ?? null;\n\n if (currentWDom) {\n registry.domMap.set(compKey, currentWDom);\n }\n });\n\n return () => {\n const currentRegistry = boundaryRegistry.get(moduleId);\n if (!currentRegistry) return;\n\n currentRegistry.instances.delete(compKey);\n currentRegistry.domMap.delete(compKey);\n\n if (!currentRegistry.instances.size) {\n boundaryRegistry.delete(moduleId);\n }\n };\n};\n\nexport const disposeBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (registry) {\n Array.from(registry.instances).forEach(compKey =>\n removeComponentEntry(compKey)\n );\n }\n\n boundaryRegistry.delete(moduleId);\n disableComponentMapManualMode();\n};\n\nexport const applyBoundaryUpdate = (\n moduleId: string,\n nextCtor: TagFunction\n) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (!registry) return false;\n\n registry.pendingCtor = nextCtor;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n queueMicrotask(() => flushBoundary(moduleId));\n }\n\n return true;\n};\n\nconst flushBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n if (!registry) return;\n\n registry.scheduled = false;\n\n const ctor = registry.pendingCtor;\n if (!ctor) {\n return;\n }\n\n registry.pendingCtor = undefined;\n\n let updated = false;\n let hasMissing = false;\n\n registry.instances.forEach(compKey => {\n const entry = componentMap.get(compKey);\n const cached = registry.domMap.get(compKey);\n const currentWDom = entry?.vd?.value || cached;\n\n if (!currentWDom || !currentWDom.el) {\n hasMissing = true;\n return;\n }\n\n const { compProps, compChild } = currentWDom;\n\n if (!compProps || !compChild) {\n hasMissing = true;\n return;\n }\n\n try {\n replaceWDom(ctor, compProps, compChild, currentWDom as any);\n const refreshed = componentMap.get(compKey)?.vd?.value || currentWDom;\n registry.domMap.set(compKey, refreshed as WDom);\n updated = true;\n } catch (error) {\n console.warn(`[Lithent HMR] boundary update 실패: ${moduleId}`, error);\n }\n });\n\n
|
|
1
|
+
{"version":3,"file":"lithentDevHelper.umd.js","sources":["../src/componentMapControl.ts","../src/createBoundary.ts"],"sourcesContent":["import type { CompKey } from 'lithent';\nimport { setComponentMapManualMode, disposeComponentEntry } from 'lithent';\n\nlet manualModeRefCount = 0;\n\nexport const enableComponentMapManualMode = (): void => {\n manualModeRefCount += 1;\n setComponentMapManualMode(true);\n};\n\nexport const disableComponentMapManualMode = (): void => {\n manualModeRefCount = Math.max(0, manualModeRefCount - 1);\n if (manualModeRefCount === 0) {\n setComponentMapManualMode(false);\n }\n};\n\nexport const removeComponentEntry = (compKey: CompKey): void => {\n disposeComponentEntry(compKey);\n};\n","import type { CompKey, TagFunction, WDom } from 'lithent';\nimport { componentMap, replaceWDom } from 'lithent';\nimport {\n disableComponentMapManualMode,\n enableComponentMapManualMode,\n removeComponentEntry,\n} from './componentMapControl';\n\nconst MAX_RETRY = 3;\n\ntype InstanceRegistry = {\n instances: Set<CompKey>;\n domMap: Map<CompKey, WDom>;\n scheduled: boolean;\n pendingCtor?: TagFunction;\n retryCount: number;\n};\n\nconst boundaryRegistry = new Map<string, InstanceRegistry>();\n\nconst getRegistry = (moduleId: string): InstanceRegistry => {\n let registry = boundaryRegistry.get(moduleId);\n\n if (!registry) {\n registry = {\n instances: new Set(),\n domMap: new Map(),\n scheduled: false,\n pendingCtor: undefined,\n retryCount: 0,\n };\n boundaryRegistry.set(moduleId, registry);\n }\n\n return registry;\n};\n\nexport type BoundaryController = {\n register: (compKey: CompKey) => () => void;\n update: (nextCtor: TagFunction) => boolean;\n dispose: () => void;\n};\n\nexport const registerBoundaryInstance = (\n moduleId: string,\n compKey: CompKey\n) => {\n const registry = getRegistry(moduleId);\n\n registry.instances.add(compKey);\n\n queueMicrotask(() => {\n const entry = componentMap.get(compKey);\n const currentWDom = entry?.vd?.value ?? null;\n\n if (currentWDom) {\n registry.domMap.set(compKey, currentWDom);\n }\n });\n\n return () => {\n const currentRegistry = boundaryRegistry.get(moduleId);\n if (!currentRegistry) return;\n\n currentRegistry.instances.delete(compKey);\n currentRegistry.domMap.delete(compKey);\n\n if (!currentRegistry.instances.size) {\n boundaryRegistry.delete(moduleId);\n }\n };\n};\n\nexport const disposeBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (registry) {\n Array.from(registry.instances).forEach(compKey =>\n removeComponentEntry(compKey)\n );\n }\n\n boundaryRegistry.delete(moduleId);\n disableComponentMapManualMode();\n};\n\nexport const applyBoundaryUpdate = (\n moduleId: string,\n nextCtor: TagFunction\n) => {\n const registry = boundaryRegistry.get(moduleId);\n\n if (!registry) return false;\n\n registry.pendingCtor = nextCtor;\n registry.retryCount = 0;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n queueMicrotask(() => flushBoundary(moduleId));\n }\n\n return true;\n};\n\nconst flushBoundary = (moduleId: string) => {\n const registry = boundaryRegistry.get(moduleId);\n if (!registry) return;\n\n registry.scheduled = false;\n\n const ctor = registry.pendingCtor;\n if (!ctor) {\n return;\n }\n\n registry.pendingCtor = undefined;\n\n let updated = false;\n let hasMissing = false;\n\n registry.instances.forEach(compKey => {\n const entry = componentMap.get(compKey);\n const cached = registry.domMap.get(compKey);\n const currentWDom = entry?.vd?.value || cached;\n\n if (!currentWDom || !currentWDom.el) {\n hasMissing = true;\n return;\n }\n\n const { compProps, compChild } = currentWDom;\n\n if (!compProps || !compChild) {\n hasMissing = true;\n return;\n }\n\n try {\n replaceWDom(ctor, compProps, compChild, currentWDom as any);\n const refreshed = componentMap.get(compKey)?.vd?.value || currentWDom;\n registry.domMap.set(compKey, refreshed as WDom);\n updated = true;\n } catch (error) {\n console.warn(`[Lithent HMR] boundary update 실패: ${moduleId}`, error);\n }\n });\n\n let nextPendingCtor: TagFunction | undefined = registry.pendingCtor;\n const shouldRetryMissing = hasMissing && !updated;\n const hasQueuedUpdate = Boolean(nextPendingCtor);\n const shouldRetry = shouldRetryMissing || hasQueuedUpdate;\n\n if (!shouldRetry) {\n registry.retryCount = 0;\n return;\n }\n\n if (shouldRetryMissing) {\n registry.retryCount += 1;\n\n if (registry.retryCount > MAX_RETRY) {\n console.warn(\n `[Lithent HMR] boundary update failed after ${registry.retryCount} attempts; aborting: ${moduleId}`\n );\n\n const hot = (\n import.meta as unknown as {\n hot?: { invalidate?: () => void };\n }\n ).hot;\n hot?.invalidate?.();\n\n registry.pendingCtor = undefined;\n registry.scheduled = false;\n return;\n }\n\n if (!nextPendingCtor) {\n nextPendingCtor = ctor;\n }\n }\n\n registry.pendingCtor = nextPendingCtor;\n\n if (!registry.scheduled) {\n registry.scheduled = true;\n setTimeout(() => flushBoundary(moduleId), 0);\n }\n};\n\nexport const createBoundary = (moduleId: string): BoundaryController => {\n enableComponentMapManualMode();\n\n return {\n register: (compKey: CompKey) => registerBoundaryInstance(moduleId, compKey),\n update: (nextCtor: TagFunction) => applyBoundaryUpdate(moduleId, nextCtor),\n dispose: () => disposeBoundary(moduleId),\n };\n};\n"],"names":["manualModeRefCount","enableComponentMapManualMode","setComponentMapManualMode","disableComponentMapManualMode","removeComponentEntry","compKey","disposeComponentEntry","MAX_RETRY","boundaryRegistry","getRegistry","moduleId","registry","registerBoundaryInstance","entry","componentMap","currentWDom","_a","currentRegistry","disposeBoundary","applyBoundaryUpdate","nextCtor","flushBoundary","ctor","updated","hasMissing","cached","compProps","compChild","replaceWDom","refreshed","error","nextPendingCtor","shouldRetryMissing","createBoundary"],"mappings":"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"}
|
|
@@ -6,11 +6,14 @@ import {
|
|
|
6
6
|
removeComponentEntry,
|
|
7
7
|
} from './componentMapControl';
|
|
8
8
|
|
|
9
|
+
const MAX_RETRY = 3;
|
|
10
|
+
|
|
9
11
|
type InstanceRegistry = {
|
|
10
12
|
instances: Set<CompKey>;
|
|
11
13
|
domMap: Map<CompKey, WDom>;
|
|
12
14
|
scheduled: boolean;
|
|
13
15
|
pendingCtor?: TagFunction;
|
|
16
|
+
retryCount: number;
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
const boundaryRegistry = new Map<string, InstanceRegistry>();
|
|
@@ -24,6 +27,7 @@ const getRegistry = (moduleId: string): InstanceRegistry => {
|
|
|
24
27
|
domMap: new Map(),
|
|
25
28
|
scheduled: false,
|
|
26
29
|
pendingCtor: undefined,
|
|
30
|
+
retryCount: 0,
|
|
27
31
|
};
|
|
28
32
|
boundaryRegistry.set(moduleId, registry);
|
|
29
33
|
}
|
|
@@ -89,6 +93,7 @@ export const applyBoundaryUpdate = (
|
|
|
89
93
|
if (!registry) return false;
|
|
90
94
|
|
|
91
95
|
registry.pendingCtor = nextCtor;
|
|
96
|
+
registry.retryCount = 0;
|
|
92
97
|
|
|
93
98
|
if (!registry.scheduled) {
|
|
94
99
|
registry.scheduled = true;
|
|
@@ -141,12 +146,47 @@ const flushBoundary = (moduleId: string) => {
|
|
|
141
146
|
}
|
|
142
147
|
});
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
let nextPendingCtor: TagFunction | undefined = registry.pendingCtor;
|
|
150
|
+
const shouldRetryMissing = hasMissing && !updated;
|
|
151
|
+
const hasQueuedUpdate = Boolean(nextPendingCtor);
|
|
152
|
+
const shouldRetry = shouldRetryMissing || hasQueuedUpdate;
|
|
153
|
+
|
|
154
|
+
if (!shouldRetry) {
|
|
155
|
+
registry.retryCount = 0;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (shouldRetryMissing) {
|
|
160
|
+
registry.retryCount += 1;
|
|
161
|
+
|
|
162
|
+
if (registry.retryCount > MAX_RETRY) {
|
|
163
|
+
console.warn(
|
|
164
|
+
`[Lithent HMR] boundary update failed after ${registry.retryCount} attempts; aborting: ${moduleId}`
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const hot = (
|
|
168
|
+
import.meta as unknown as {
|
|
169
|
+
hot?: { invalidate?: () => void };
|
|
170
|
+
}
|
|
171
|
+
).hot;
|
|
172
|
+
hot?.invalidate?.();
|
|
173
|
+
|
|
174
|
+
registry.pendingCtor = undefined;
|
|
175
|
+
registry.scheduled = false;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!nextPendingCtor) {
|
|
180
|
+
nextPendingCtor = ctor;
|
|
148
181
|
}
|
|
149
182
|
}
|
|
183
|
+
|
|
184
|
+
registry.pendingCtor = nextPendingCtor;
|
|
185
|
+
|
|
186
|
+
if (!registry.scheduled) {
|
|
187
|
+
registry.scheduled = true;
|
|
188
|
+
setTimeout(() => flushBoundary(moduleId), 0);
|
|
189
|
+
}
|
|
150
190
|
};
|
|
151
191
|
|
|
152
192
|
export const createBoundary = (moduleId: string): BoundaryController => {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createBoundary } from '../index';
|
|
3
|
+
|
|
4
|
+
describe('createBoundary retry limit', () => {
|
|
5
|
+
it('prevents infinite retry loops on failed boundary updates', async () => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
|
|
8
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
9
|
+
const originalHot = (import.meta as unknown as { hot?: unknown }).hot;
|
|
10
|
+
const hot = { invalidate: vi.fn() };
|
|
11
|
+
Object.defineProperty(import.meta, 'hot', {
|
|
12
|
+
configurable: true,
|
|
13
|
+
value: hot,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const boundary = createBoundary('module-retry');
|
|
17
|
+
const compKey = {} as Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
boundary.register(compKey);
|
|
20
|
+
boundary.update((() => null) as any);
|
|
21
|
+
|
|
22
|
+
// queueMicrotask로 스케줄된 첫 flush가 실행되도록 처리
|
|
23
|
+
await Promise.resolve();
|
|
24
|
+
|
|
25
|
+
// MAX_RETRY(3)를 초과하면 더 이상 setTimeout을 예약하지 않는다
|
|
26
|
+
let safety = 0;
|
|
27
|
+
while (vi.getTimerCount() > 0 && safety < 5) {
|
|
28
|
+
vi.runOnlyPendingTimers();
|
|
29
|
+
safety += 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
33
|
+
expect.stringContaining(
|
|
34
|
+
'boundary update failed after 4 attempts; aborting'
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
38
|
+
|
|
39
|
+
boundary.dispose();
|
|
40
|
+
if (originalHot === undefined) {
|
|
41
|
+
Reflect.deleteProperty(import.meta, 'hot');
|
|
42
|
+
} else {
|
|
43
|
+
Object.defineProperty(import.meta, 'hot', {
|
|
44
|
+
configurable: true,
|
|
45
|
+
value: originalHot,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
vi.useRealTimers();
|
|
50
|
+
warnSpy.mockRestore();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getComponentKey,
|
|
8
8
|
} from 'lithent';
|
|
9
9
|
import type { TagFunction } from 'lithent';
|
|
10
|
-
import { createBoundary } from '
|
|
10
|
+
import { createBoundary } from '../index';
|
|
11
11
|
|
|
12
12
|
const __lithentModuleId = new URL(import.meta.url).pathname;
|
|
13
13
|
const __lithentBoundaryStoreKey = `__lithent_hmr_boundary__${__lithentModuleId}`;
|