@nisli/core 0.56.0 → 0.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +70 -9
  2. package/dist/bundler/component.js +409 -0
  3. package/dist/bundler/component.js.map +7 -0
  4. package/dist/bundler/context.js +27 -0
  5. package/dist/bundler/context.js.map +7 -0
  6. package/dist/bundler/diagnostics.js +55 -0
  7. package/dist/bundler/diagnostics.js.map +7 -0
  8. package/dist/bundler/disposable.js +9 -0
  9. package/dist/bundler/disposable.js.map +7 -0
  10. package/dist/bundler/element-context.js +47 -0
  11. package/dist/bundler/element-context.js.map +7 -0
  12. package/dist/bundler/emitter.js +70 -0
  13. package/dist/bundler/emitter.js.map +7 -0
  14. package/dist/bundler/hmr/registry.js +72 -0
  15. package/dist/bundler/hmr/registry.js.map +7 -0
  16. package/dist/bundler/hmr/transform.js +30 -0
  17. package/dist/bundler/hmr/transform.js.map +7 -0
  18. package/dist/bundler/index.js +103 -0
  19. package/dist/bundler/index.js.map +7 -0
  20. package/dist/bundler/injector.js +75 -0
  21. package/dist/bundler/injector.js.map +7 -0
  22. package/dist/bundler/lifecycle.js +56 -0
  23. package/dist/bundler/lifecycle.js.map +7 -0
  24. package/dist/bundler/projection.js +92 -0
  25. package/dist/bundler/projection.js.map +7 -0
  26. package/dist/bundler/query.js +302 -0
  27. package/dist/bundler/query.js.map +7 -0
  28. package/dist/bundler/ref.js +16 -0
  29. package/dist/bundler/ref.js.map +7 -0
  30. package/dist/bundler/resource.js +99 -0
  31. package/dist/bundler/resource.js.map +7 -0
  32. package/dist/bundler/settle.js +43 -0
  33. package/dist/bundler/settle.js.map +7 -0
  34. package/dist/bundler/signal.js +403 -0
  35. package/dist/bundler/signal.js.map +7 -0
  36. package/dist/bundler/template-audit.js +162 -0
  37. package/dist/bundler/template-audit.js.map +7 -0
  38. package/dist/bundler/template.js +922 -0
  39. package/dist/bundler/template.js.map +7 -0
  40. package/dist/bundler/view-transition.js +20 -0
  41. package/dist/bundler/view-transition.js.map +7 -0
  42. package/dist/bundler/vite-hmr/plugin.js +32 -0
  43. package/dist/bundler/vite-hmr/plugin.js.map +7 -0
  44. package/dist/bundler/vite-hmr/runtime.js +8 -0
  45. package/dist/bundler/vite-hmr/runtime.js.map +7 -0
  46. package/dist/index.js +2682 -47
  47. package/dist/index.js.map +7 -1
  48. package/dist/index.production.js +2 -0
  49. package/dist/index.production.js.map +6 -0
  50. package/dist/{diagnostics.d.ts → types/diagnostics.d.ts} +8 -15
  51. package/dist/{template-audit.d.ts → types/template-audit.d.ts} +4 -5
  52. package/dist/{template.d.ts → types/template.d.ts} +2 -1
  53. package/package.json +15 -10
  54. package/dist/component.js +0 -633
  55. package/dist/component.js.map +0 -1
  56. package/dist/context.js +0 -55
  57. package/dist/context.js.map +0 -1
  58. package/dist/diagnostics.js +0 -118
  59. package/dist/diagnostics.js.map +0 -1
  60. package/dist/disposable.js +0 -8
  61. package/dist/disposable.js.map +0 -1
  62. package/dist/element-context.js +0 -108
  63. package/dist/element-context.js.map +0 -1
  64. package/dist/emitter.js +0 -91
  65. package/dist/emitter.js.map +0 -1
  66. package/dist/hmr/registry.js +0 -136
  67. package/dist/hmr/registry.js.map +0 -1
  68. package/dist/hmr/transform.js +0 -52
  69. package/dist/hmr/transform.js.map +0 -1
  70. package/dist/injector.js +0 -118
  71. package/dist/injector.js.map +0 -1
  72. package/dist/lifecycle.js +0 -117
  73. package/dist/lifecycle.js.map +0 -1
  74. package/dist/projection.js +0 -179
  75. package/dist/projection.js.map +0 -1
  76. package/dist/query.js +0 -429
  77. package/dist/query.js.map +0 -1
  78. package/dist/ref.js +0 -43
  79. package/dist/ref.js.map +0 -1
  80. package/dist/resource.js +0 -129
  81. package/dist/resource.js.map +0 -1
  82. package/dist/settle.js +0 -106
  83. package/dist/settle.js.map +0 -1
  84. package/dist/signal.js +0 -645
  85. package/dist/signal.js.map +0 -1
  86. package/dist/template-audit.js +0 -246
  87. package/dist/template-audit.js.map +0 -1
  88. package/dist/template.js +0 -1244
  89. package/dist/template.js.map +0 -1
  90. package/dist/view-transition.js +0 -98
  91. package/dist/view-transition.js.map +0 -1
  92. package/dist/vite-hmr/plugin.js +0 -62
  93. package/dist/vite-hmr/plugin.js.map +0 -1
  94. package/dist/vite-hmr/runtime.js +0 -17
  95. package/dist/vite-hmr/runtime.js.map +0 -1
  96. /package/dist/{component.d.ts → types/component.d.ts} +0 -0
  97. /package/dist/{context.d.ts → types/context.d.ts} +0 -0
  98. /package/dist/{disposable.d.ts → types/disposable.d.ts} +0 -0
  99. /package/dist/{element-context.d.ts → types/element-context.d.ts} +0 -0
  100. /package/dist/{emitter.d.ts → types/emitter.d.ts} +0 -0
  101. /package/dist/{hmr → types/hmr}/registry.d.ts +0 -0
  102. /package/dist/{hmr → types/hmr}/transform.d.ts +0 -0
  103. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  104. /package/dist/{injector.d.ts → types/injector.d.ts} +0 -0
  105. /package/dist/{lifecycle.d.ts → types/lifecycle.d.ts} +0 -0
  106. /package/dist/{projection.d.ts → types/projection.d.ts} +0 -0
  107. /package/dist/{query.d.ts → types/query.d.ts} +0 -0
  108. /package/dist/{ref.d.ts → types/ref.d.ts} +0 -0
  109. /package/dist/{resource.d.ts → types/resource.d.ts} +0 -0
  110. /package/dist/{settle.d.ts → types/settle.d.ts} +0 -0
  111. /package/dist/{signal.d.ts → types/signal.d.ts} +0 -0
  112. /package/dist/{view-transition.d.ts → types/view-transition.d.ts} +0 -0
  113. /package/dist/{vite-hmr → types/vite-hmr}/plugin.d.ts +0 -0
  114. /package/dist/{vite-hmr → types/vite-hmr}/runtime.d.ts +0 -0
package/dist/injector.js DELETED
@@ -1,118 +0,0 @@
1
- /**
2
- * injector.ts — Dependency injection: inject(), provide(), class-as-token
3
- *
4
- * Two key insights from Angular's evolution:
5
- * 1. The class IS the token — no createToken() needed for 99% of cases
6
- * 2. provide() is optional — inject() auto-creates singletons on first call
7
- *
8
- * ```ts
9
- * class BacklogAPI {
10
- * async getTasks(): Promise<Task[]> { ... }
11
- * }
12
- *
13
- * // In component setup:
14
- * const api = inject(BacklogAPI); // auto-created singleton
15
- *
16
- * // In tests:
17
- * provide(BacklogAPI, () => new MockAPI());
18
- * ```
19
- */
20
- import { hasContext, getCurrentComponent } from './context.js';
21
- import { formatDiag, isDev } from './diagnostics.js';
22
- // ── Global singleton registry ───────────────────────────────────────
23
- /** Stores singleton instances keyed by constructor or token. */
24
- const singletonCache = new Map();
25
- /** Stores factory overrides from provide(). */
26
- const factoryOverrides = new Map();
27
- /** Tracks which constructors are currently being instantiated (circular detection). */
28
- const instantiating = new Set();
29
- export function inject(token) {
30
- // Check cache first
31
- if (singletonCache.has(token)) {
32
- return singletonCache.get(token);
33
- }
34
- // Circular dependency detection
35
- if (instantiating.has(token)) {
36
- const name = typeof token === 'function' ? token.name : token.name;
37
- throw new Error(`Circular dependency detected when instantiating '${name}'. ` +
38
- 'Check your service constructors for circular inject() calls.');
39
- }
40
- instantiating.add(token);
41
- try {
42
- let instance;
43
- // Check for factory override
44
- const factory = factoryOverrides.get(token);
45
- if (factory) {
46
- instance = factory();
47
- }
48
- else if (typeof token === 'function') {
49
- // Class token — auto-create via constructor
50
- instance = new token();
51
- }
52
- else {
53
- // InjectionToken with default factory
54
- const injToken = token;
55
- if (injToken._factory) {
56
- instance = injToken._factory();
57
- }
58
- else {
59
- throw new Error(`No provider found for token '${injToken.name}'. ` +
60
- 'Use provide() to register a factory before calling inject().');
61
- }
62
- }
63
- singletonCache.set(token, instance);
64
- return instance;
65
- }
66
- finally {
67
- instantiating.delete(token);
68
- }
69
- }
70
- export function provide(token, factory) {
71
- if (isDev() && singletonCache.has(token)) {
72
- const name = typeof token === 'function' ? token.name : token.name;
73
- throw new Error(formatDiag('N501', `provide(${name}) after '${name}' was already instantiated — existing `
74
- + `consumers keep the old instance while new inject() calls would get the `
75
- + `new one (mixed-instance split). Call resetInjector() first (tests), or `
76
- + `provide() before the first inject().`));
77
- }
78
- factoryOverrides.set(token, factory);
79
- // Remove cached instance so next inject() uses the new factory
80
- singletonCache.delete(token);
81
- }
82
- /**
83
- * Create an injection token for non-class dependencies.
84
- * Use this for config objects, primitives, or interfaces.
85
- *
86
- * ```ts
87
- * const AppConfig = createToken<{ apiUrl: string }>('AppConfig');
88
- * provide(AppConfig, () => ({ apiUrl: '/api' }));
89
- * const config = inject(AppConfig);
90
- * ```
91
- */
92
- export function createToken(name, defaultFactory) {
93
- return {
94
- __brand: 'InjectionToken',
95
- name,
96
- _factory: defaultFactory,
97
- };
98
- }
99
- // Hooks run by resetInjector() — module-level caches elsewhere (settle's
100
- // pending registry) stay test-isolated without upside-down imports: those
101
- // modules import the injector and self-register, never the reverse.
102
- const resetHooks = new Set();
103
- /** @internal Register a hook run by every resetInjector() call. */
104
- export function registerResetHook(hook) {
105
- resetHooks.add(hook);
106
- }
107
- /**
108
- * Reset the injector — clears all singletons and factory overrides.
109
- * Used in tests to ensure isolation between test cases.
110
- */
111
- export function resetInjector() {
112
- singletonCache.clear();
113
- factoryOverrides.clear();
114
- instantiating.clear();
115
- for (const hook of resetHooks)
116
- hook();
117
- }
118
- //# sourceMappingURL=injector.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"injector.js","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"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"}
package/dist/lifecycle.js DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * lifecycle.ts — Post-mount and cleanup lifecycle hooks
3
- *
4
- * `onMount()` registers a callback that runs after the component's
5
- * template is mounted to the DOM. If the callback returns a function,
6
- * that function runs on disconnect (cleanup).
7
- *
8
- * `onCleanup()` registers a standalone cleanup callback that runs
9
- * on disconnect — for cases where you need cleanup without mount logic.
10
- *
11
- * Both must be called synchronously during component setup.
12
- *
13
- * ```ts
14
- * component('my-el', (props, host) => {
15
- * onMount(() => {
16
- * host.querySelector('input')?.focus();
17
- * return () => console.log('unmounted');
18
- * });
19
- *
20
- * onCleanup(() => clearInterval(timerId));
21
- *
22
- * return html`<input />`;
23
- * });
24
- * ```
25
- */
26
- import { hasContext, getCurrentComponent } from './context.js';
27
- // ── Mount callback storage ──────────────────────────────────────────
28
- /** Mount callbacks are stored on the component host and called after template mount. */
29
- const mountCallbacks = new WeakMap();
30
- /**
31
- * Register a callback to run after the component's template is mounted.
32
- * If the callback returns a function, it will be called on disconnect.
33
- *
34
- * Must be called synchronously during component setup.
35
- */
36
- export function onMount(callback) {
37
- if (!hasContext()) {
38
- throw new Error('onMount() called outside setup(). ' +
39
- 'It can only be called during component initialization.');
40
- }
41
- const comp = getCurrentComponent();
42
- let callbacks = mountCallbacks.get(comp);
43
- if (!callbacks) {
44
- callbacks = [];
45
- mountCallbacks.set(comp, callbacks);
46
- }
47
- callbacks.push(callback);
48
- }
49
- /**
50
- * Register a cleanup callback to run on disconnect.
51
- * This is a convenience for cases where you need cleanup without mount logic.
52
- *
53
- * Must be called synchronously during component setup.
54
- */
55
- export function onCleanup(callback) {
56
- if (!hasContext()) {
57
- throw new Error('onCleanup() called outside setup(). ' +
58
- 'It can only be called during component initialization.');
59
- }
60
- getCurrentComponent().addDisposer(callback);
61
- }
62
- /**
63
- * Listen for a DOM event on a target and auto-dispose the listener when the
64
- * component disconnects. Works with any EventTarget (host element, document,
65
- * window, etc.). Must be called during component setup.
66
- *
67
- * ```ts
68
- * useHostEvent(host, 'click', (e) => {
69
- * // Runs on each click
70
- * });
71
- * ```
72
- */
73
- export function useHostEvent(target, eventName, handler) {
74
- if (!hasContext()) {
75
- throw new Error('useHostEvent() called outside setup(). ' +
76
- 'It can only be called during component initialization.');
77
- }
78
- target.addEventListener(eventName, handler);
79
- getCurrentComponent().addDisposer(() => {
80
- target.removeEventListener(eventName, handler);
81
- });
82
- }
83
- /**
84
- * Run all registered mount callbacks for a component host.
85
- * Called by component.ts after the template is mounted to the DOM.
86
- *
87
- * A throw here PROPAGATES to component.ts's containment boundary — the SAME
88
- * boundary as a setup throw (ADR 0030.2 T6, code N402): remaining callbacks
89
- * are skipped, the partial scope (including cleanups already registered by
90
- * earlier callbacks) is disposed, the error fallback renders, and the host is
91
- * stamped `data-nisli-error`. The pre-T6 behavior (console-and-limp per
92
- * callback) left a half-mounted component alive with no machine-readable
93
- * trace and no teardown — the one containment semantics setup already had
94
- * (issue 0010) now covers mount too.
95
- * @internal
96
- */
97
- export function runMountCallbacks(comp) {
98
- const callbacks = mountCallbacks.get(comp);
99
- if (!callbacks)
100
- return;
101
- try {
102
- for (const cb of callbacks) {
103
- const cleanup = cb();
104
- if (typeof cleanup === 'function') {
105
- // Register cleanup as a disposer on the component
106
- comp.addDisposer(cleanup);
107
- }
108
- }
109
- }
110
- finally {
111
- // Clear — mount callbacks run once (also when a throw contains the scope:
112
- // the boundary re-runs setup from scratch on the next mount, which
113
- // re-registers a fresh callback list).
114
- mountCallbacks.delete(comp);
115
- }
116
- }
117
- //# sourceMappingURL=lifecycle.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE/D,uEAAuE;AAEvE,wFAAwF;AACxF,MAAM,cAAc,GAAG,IAAI,OAAO,EAAyC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAmC;IACzD,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,oCAAoC;YACpC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,IAAI,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,CAAC;QACf,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAoB;IAC5C,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,sCAAsC;YACtC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,mBAAmB,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,SAAiB,EACjB,OAA2B;IAE3B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,yCAAyC;YACzC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAC7D,mBAAmB,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QACrC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS;QAAE,OAAO;IAEvB,IAAI,CAAC;QACH,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,EAAE,EAAE,CAAC;YACrB,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;gBAClC,kDAAkD;gBACjD,IAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,0EAA0E;QAC1E,mEAAmE;QACnE,uCAAuC;QACvC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC"}
@@ -1,179 +0,0 @@
1
- /**
2
- * projection.ts — First-class content projection (ADR 0025 item 1)
3
- *
4
- * `children(fallback?)` is a setup-context primitive that owns the light-DOM
5
- * projection machinery the registry currently hand-rolls
6
- * (captureChildren/projectChildren + per-component conditional-default swaps).
7
- *
8
- * ```ts
9
- * component('ui-button', (props, host) => {
10
- * transparentHost(host);
11
- * return html`<button class="${classes}">${children()}</button>`;
12
- * });
13
- * ```
14
- *
15
- * Semantics:
16
- * - Returns a reactive slot value (a signal) that a template interpolates. The
17
- * slot resolves, in priority order: the factory `children` prop → the host's
18
- * captured light-DOM children → the fallback.
19
- * - Factory `children` route through the SAME slot: `children()` READS the
20
- * host's `children` prop signal (sugar over one path). Crucially it renders
21
- * them AT the slot position (not by relocating them to light DOM), so a
22
- * factory-composed child element is a DOM descendant of its parent when its
23
- * connectedCallback runs — `closest()`-based parent lookup keeps working.
24
- * - Plain-HTML light-DOM children are captured pre-setup by component.ts
25
- * (SEED → CAPTURE → CONTEXT) and projected here; late parser children are
26
- * swept in a microtask (the ADR 0023 streaming machinery, now in core).
27
- * - `children(fallback)` renders `fallback` when no MEANINGFUL children exist
28
- * (whitespace-only light DOM does not count) and REPLACES it reactively when
29
- * children arrive — including late parser children. This natively solves the
30
- * conditional-default class (message-scroller-button's default glyph).
31
- * - Single default slot only; named/multiple slots are a later design (v1 OUT).
32
- *
33
- * Zero-cost when unused: component.ts only snapshots childNodes when the host
34
- * has any, and only children() consumes them.
35
- */
36
- import { getCurrentComponent } from './context.js';
37
- import { onCleanup } from './lifecycle.js';
38
- import { signal, computed, flush } from './signal.js';
39
- /** True if the nodes carry meaningful content (element or non-blank text). */
40
- function isMeaningful(nodes) {
41
- return nodes.some((n) => n.nodeType === 1 || (n.nodeType === 3 && (n.textContent ?? '').trim() !== ''));
42
- }
43
- /** A minimal TemplateResult that mounts pre-existing DOM nodes into the slot. */
44
- function nodesTemplate(nodes) {
45
- return {
46
- __templateResult: true,
47
- mount(w) {
48
- for (const node of nodes)
49
- w.appendChild(node);
50
- },
51
- dispose() { },
52
- };
53
- }
54
- /** A re-mountable TemplateResult that renders a primitive as a fresh text node. */
55
- function textTemplate(text) {
56
- return {
57
- __templateResult: true,
58
- mount(w) {
59
- w.appendChild(document.createTextNode(text));
60
- },
61
- dispose() { },
62
- };
63
- }
64
- /**
65
- * Normalize a slot value so the template ALWAYS sees a TemplateResult / factory
66
- * / array / null — never a bare primitive. Projection values may be detached
67
- * and reinserted as fallback/light-DOM ownership changes, so wrapping primitive
68
- * text keeps that projection value explicitly re-mountable.
69
- */
70
- function asSlot(value) {
71
- if (value == null || value === '')
72
- return null;
73
- if (typeof value === 'object'
74
- && ('__templateResult' in value || '__type' in value)) {
75
- return value;
76
- }
77
- if (Array.isArray(value))
78
- return value;
79
- return textTemplate(String(value));
80
- }
81
- /**
82
- * Project the host's children (factory `children` prop or light DOM) into this
83
- * slot, with an optional `fallback` rendered when empty. Must be called in
84
- * setup context. Interpolate the result in a template:
85
- * `html`<button>${children()}</button>``.
86
- */
87
- export function children(fallback) {
88
- const host = getCurrentComponent().element;
89
- // Consume the pre-setup light-DOM capture; remove those nodes from the host
90
- // so the rendered template doesn't duplicate them.
91
- const captured = host.__capturedChildren ?? [];
92
- host.__capturedChildren = undefined;
93
- for (const node of captured) {
94
- if (node.parentNode === host)
95
- host.removeChild(node);
96
- }
97
- // The factory `children` prop, folded into the same slot (sugar over one path).
98
- const childrenProp = host._propSignal ? host._propSignal('children') : undefined;
99
- // Projected light-DOM nodes; grows when the late-parser sweep finds more.
100
- const projectedNodes = signal(captured);
101
- // True once this setup scope has been disposed (true disconnect, HMR
102
- // teardown, OR the containment boundary tearing down a failed mount). The
103
- // queued sweep below must then no-op: it would otherwise misread the
104
- // post-teardown host children (restored light DOM, or the error fallback)
105
- // as "late" and relocate them into a dead slot.
106
- let disposed = false;
107
- // A true disconnect/HMR teardown clears the component-owned rendered tree
108
- // before setup runs again. Hand the plain light-DOM nodes back to component.ts
109
- // so it can restore them as direct host children for the next capture.
110
- onCleanup(() => {
111
- disposed = true;
112
- const nodes = projectedNodes.value;
113
- host.__capturedChildren = nodes.length ? nodes : undefined;
114
- });
115
- // LATE-CHILDREN SWEEP — scheduled by component.ts's dedicated projection
116
- // phase (ADR 0030.2 §3), which runs after the template is committed and
117
- // BEFORE any onMount callback. Pre-0030.2 this was registered via
118
- // children()'s own onMount, so the rendered-roots snapshot depended on
119
- // where children() sat relative to other onMount() registrations (the 0025
120
- // batch-3B ordering trap). Hoisting the SCHEDULING makes the rule
121
- // universal: the snapshot is exactly the rendered template roots, and
122
- // anything appearing after it — late parser/innerHTML children AND onMount
123
- // host-appends — classifies as projected content. Only scheduling moved;
124
- // the prop READ path (the computed below) is unchanged.
125
- host.__nisliProjectionSweep = () => {
126
- // The host's direct children at phase time are the rendered template
127
- // roots. Anything that appears later is late projected content.
128
- const renderedRoots = new Set(Array.from(host.childNodes));
129
- queueMicrotask(() => {
130
- if (disposed)
131
- return; // scope torn down before the sweep ran
132
- const late = Array.from(host.childNodes).filter((n) => !renderedRoots.has(n));
133
- if (late.length && isMeaningful(late)) {
134
- for (const node of late)
135
- host.removeChild(node);
136
- projectedNodes.value = [...projectedNodes.value, ...late];
137
- // Flush the slot effect NOW so the late children land in this same
138
- // microtask — the projectChildren() this replaces mutated the DOM
139
- // imperatively, so callers only await one microtask for the sweep.
140
- flush();
141
- }
142
- });
143
- };
144
- // Stable, RE-MOUNTABLE fallback slot: the same slot object is returned on
145
- // every empty render, and it can be mounted → unmounted → mounted again
146
- // (empty → filled → empty) without crashing. A string wraps to a fresh text
147
- // node each mount. A TemplateResult is mounted ONCE here into a detached
148
- // holder (its reactive bindings run in this component's context and dispose
149
- // with it); its rendered nodes are then exposed as a re-mountable node slot,
150
- // so the engine can detach and re-append them freely.
151
- let fallbackSlot = null;
152
- if (fallback != null) {
153
- if (typeof fallback === 'string') {
154
- fallbackSlot = textTemplate(fallback);
155
- }
156
- else {
157
- const holder = document.createDocumentFragment();
158
- fallback.mount(holder);
159
- const fallbackNodes = Array.from(holder.childNodes);
160
- onCleanup(() => { try {
161
- fallback.dispose();
162
- }
163
- catch (_) { /* ignore */ } });
164
- fallbackSlot = nodesTemplate(fallbackNodes);
165
- }
166
- }
167
- // Priority: factory children → captured/late light DOM → fallback. Reactive,
168
- // so late children (or a changing `children` prop) replace the fallback.
169
- return computed(() => {
170
- const kids = childrenProp?.value;
171
- if (kids != null && kids !== '')
172
- return asSlot(kids);
173
- const nodes = projectedNodes.value;
174
- if (isMeaningful(nodes))
175
- return nodesTemplate(nodes);
176
- return fallbackSlot;
177
- });
178
- }
179
- //# sourceMappingURL=projection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}