@esportsplus/template 0.16.13 → 0.16.15

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 (40) hide show
  1. package/bench/runtime.bench.ts +207 -0
  2. package/build/attributes.js +4 -1
  3. package/build/slot/array.js +50 -4
  4. package/build/slot/render.js +1 -2
  5. package/build/utilities.d.ts +2 -1
  6. package/build/utilities.js +2 -1
  7. package/package.json +5 -5
  8. package/src/attributes.ts +4 -1
  9. package/src/slot/array.ts +76 -4
  10. package/src/slot/render.ts +1 -4
  11. package/src/utilities.ts +3 -1
  12. package/storage/feature-research-2026-03-24.md +475 -0
  13. package/test-output.txt +0 -0
  14. package/{test/attributes.test.ts → tests/attributes.ts} +3 -2
  15. package/tests/compiler/codegen.ts +292 -0
  16. package/tests/compiler/integration.ts +252 -0
  17. package/tests/compiler/ts-parser.ts +160 -0
  18. package/{test/constants.test.ts → tests/constants.ts} +5 -1
  19. package/tests/event/onconnect.ts +147 -0
  20. package/tests/event/onresize.ts +187 -0
  21. package/tests/event/ontick.ts +273 -0
  22. package/{test/slot/array.test.ts → tests/slot/array.ts} +274 -0
  23. package/vitest.bench.config.ts +18 -0
  24. package/vitest.config.ts +1 -1
  25. package/storage/compiler-architecture-2026-01-13.md +0 -420
  26. /package/{test → examples}/index.ts +0 -0
  27. /package/{test → examples}/vite.config.ts +0 -0
  28. /package/{test/compiler/parser.test.ts → tests/compiler/parser.ts} +0 -0
  29. /package/{test/compiler/ts-analyzer.test.ts → tests/compiler/ts-analyzer.ts} +0 -0
  30. /package/{test → tests}/dist/test.js +0 -0
  31. /package/{test → tests}/dist/test.js.map +0 -0
  32. /package/{test/event/index.test.ts → tests/event/index.ts} +0 -0
  33. /package/{test/html.test.ts → tests/html.ts} +0 -0
  34. /package/{test/render.test.ts → tests/render.ts} +0 -0
  35. /package/{test/slot/cleanup.test.ts → tests/slot/cleanup.ts} +0 -0
  36. /package/{test/slot/effect.test.ts → tests/slot/effect.ts} +0 -0
  37. /package/{test/slot/index.test.ts → tests/slot/index.ts} +0 -0
  38. /package/{test/slot/render.test.ts → tests/slot/render.ts} +0 -0
  39. /package/{test/svg.test.ts → tests/svg.ts} +0 -0
  40. /package/{test/utilities.test.ts → tests/utilities.ts} +0 -0
@@ -0,0 +1,475 @@
1
+ # Feature Research — @esportsplus/template
2
+
3
+ **Date:** 2026-03-24
4
+ **Method:** Parallel web research agents covering modern framework features, DOM APIs, compiler/DX improvements
5
+ **Scope:** Runtime features, compiler enhancements, developer experience, new browser APIs
6
+
7
+ ---
8
+
9
+ ## Current Library Capabilities
10
+
11
+ - Compile-time `html` tagged template literal → optimized DOM construction
12
+ - `svg` tagged template support with SVG sprite helper
13
+ - Reactive slots: `EffectSlot` (single values), `ArraySlot` (lists with fine-grained ops)
14
+ - Event delegation (document-level) + direct attachment for non-bubbling events
15
+ - Lifecycle events: `onconnect`, `ondisconnect`, `onrender`, `onresize`, `ontick`
16
+ - Attribute system with batched RAF updates, class/style list merging
17
+ - Cleanup system via symbol-keyed arrays on nodes
18
+ - Vite plugin + TypeScript transformer
19
+
20
+ ---
21
+
22
+ ## Removed Features
23
+
24
+ - **Directive System** — Already supported. Compile-time transforms handle `classMap`, `styleMap`, conditionals, and keyed iteration natively.
25
+ - **Portals** — Already supported. `render(anyElement, content)` renders into any DOM target.
26
+
27
+ ---
28
+
29
+ ## Feature Findings
30
+
31
+ ### F2: Enter/Exit Animation Primitives
32
+
33
+ **What:** Built-in support for animating elements entering and leaving the DOM. Separate from View Transitions — this covers individual element lifecycle animations.
34
+
35
+ **How frameworks implement:**
36
+ - Svelte: `transition:fade`, `in:fly`, `out:slide` directives with built-in easing
37
+ - Vue: `<Transition>` component with CSS class hooks (`v-enter-from`, `v-enter-active`, `v-leave-to`)
38
+ - SolidJS: `<Transition>` + `onEnter`/`onExit` callbacks
39
+ - FLIP technique: First-Last-Invert-Play for layout animations
40
+
41
+ **Why useful:**
42
+ - Enter animations: fade in, slide in, scale up when element added
43
+ - Exit animations: fade out, slide out when element removed (requires delaying removal)
44
+ - List reorder animations: FLIP calculates position delta, animates transform
45
+ - Without framework support, exit animations require manual `setTimeout` + class toggling
46
+
47
+ **Key challenge:** Exit animations require the library to **delay node removal** until the animation completes. This needs integration with the cleanup system.
48
+
49
+ **Proposed API:**
50
+ ```typescript
51
+ // Attribute-based (compile-time detected)
52
+ html`<div ontransition=${(el, phase) => { /* 'enter' | 'exit' | 'move' */ }}>...</div>`;
53
+
54
+ // Or: explicit animation hooks
55
+ onenter?: (element: Element, done: VoidFunction) => void;
56
+ onexit?: (element: Element, done: VoidFunction) => void;
57
+ ```
58
+
59
+ **Applicability:** High — the library's `ondisconnect` hook is the right place to intercept removal. Compile-time can detect animation attributes and emit deferred removal code.
60
+
61
+ **Complexity:** Medium (exit animations need deferred removal; FLIP needs position tracking)
62
+ **Priority:** Medium-High
63
+
64
+ ---
65
+
66
+ ### F3: Suspense / Async Content Boundaries
67
+
68
+ **What:** Declarative async content handling — show fallback UI while async data loads, then swap in real content. Nested suspense boundaries enable progressive rendering.
69
+
70
+ **How frameworks implement:**
71
+ - React: `<Suspense fallback={<Loading/>}>`
72
+ - SolidJS 2.0: `<Loading>` boundary with `isPending()` — distinguishes initial load from revalidation
73
+ - Svelte: `{#await promise}...{:then data}...{:catch error}...{/await}`
74
+ - Qwik: Automatic suspense via resumability
75
+
76
+ **Why useful:**
77
+ - Async data fetching is universal; every app needs loading states
78
+ - Nested boundaries prevent full-page loading spinners
79
+ - Streaming SSR: flush boundary fallbacks, then stream resolved content
80
+ - SolidJS 2.0's `isPending()` avoids tearing — query if async work is in flight without demolishing UI
81
+
82
+ **Proposed API:**
83
+ ```typescript
84
+ // Compile-time: detect async effects, wrap in boundary
85
+ html`<div>${async () => {
86
+ let data = await fetch('/api');
87
+ return html`<span>${data.name}</span>`;
88
+ }}</div>`;
89
+
90
+ // Runtime: AsyncSlot that renders fallback, then swaps to resolved content
91
+ ```
92
+
93
+ **Applicability:** Medium — requires new `AsyncSlot` type. Compile-time could detect async arrow functions and emit boundary code. Runtime needs promise tracking + fallback rendering.
94
+
95
+ **Complexity:** Medium-High
96
+ **Priority:** Medium
97
+
98
+ ---
99
+
100
+ ### F4: Error Boundaries
101
+
102
+ **What:** Catch rendering errors in a subtree and display fallback UI instead of crashing the entire app.
103
+
104
+ **How frameworks implement:**
105
+ - React: `class ErrorBoundary extends Component` with `componentDidCatch`
106
+ - SolidJS: `<ErrorBoundary fallback={err => <p>{err.message}</p>}>`
107
+ - Vue: `onErrorCaptured` lifecycle hook
108
+ - Svelte: No built-in (try/catch in load functions)
109
+
110
+ **Why useful:**
111
+ - Production apps need graceful degradation
112
+ - Isolate errors to the component that failed
113
+ - Show meaningful error messages instead of blank screens
114
+ - Enable retry mechanisms
115
+
116
+ **Proposed API:**
117
+ ```typescript
118
+ // Wrap slot rendering in try/catch
119
+ import { boundary } from '@esportsplus/template';
120
+
121
+ boundary(
122
+ () => html`<div>${riskyContent}</div>`,
123
+ (error) => html`<div class="error">${error.message}</div>`
124
+ );
125
+ ```
126
+
127
+ **Applicability:** Medium — `EffectSlot` already wraps effect execution. Adding try/catch + fallback rendering is natural. Compile-time could detect `boundary()` calls and emit optimized error handling.
128
+
129
+ **Complexity:** Low-Medium
130
+ **Priority:** Medium
131
+
132
+ ---
133
+
134
+ ### F5: Popover API Integration
135
+
136
+ **What:** Native browser API for overlay/popup content. `popover` attribute + `popovertarget` for declarative show/hide. Automatic top-layer rendering, light-dismiss, focus management.
137
+
138
+ **Browser support:** Chrome 114+, Firefox 125+, Safari 17+
139
+
140
+ **Why useful:**
141
+ - Replaces custom tooltip/dropdown/menu libraries
142
+ - Native accessibility (inert background, keyboard handling)
143
+ - Top-layer eliminates z-index conflicts
144
+ - `beforetoggle`/`toggle` events for state tracking
145
+ - `interestfor` (experimental) for hover/focus activation
146
+
147
+ **Current library gap:** No special handling for `popover` attribute — it would be set via generic `setAttribute`. But `popovertarget` needs special handling since it references another element by ID.
148
+
149
+ **Proposed integration:**
150
+ - Compile-time: detect `popover` and `popovertarget` attributes
151
+ - Runtime: auto-generate unique IDs for `popovertarget` references
152
+ - Bind `toggle` event via event delegation
153
+ - Add `onpopovershow`/`onpopoverhide` lifecycle events
154
+
155
+ **Applicability:** Medium — mostly attribute detection + event binding. The compile-time system can validate `popovertarget` references.
156
+
157
+ **Complexity:** Low
158
+ **Priority:** Medium
159
+
160
+ ---
161
+
162
+ ### F6: `moveBefore()` DOM API
163
+
164
+ **What:** New DOM method (`parentNode.moveBefore(node, referenceNode)`) for moving nodes within the DOM without triggering disconnect/reconnect lifecycle callbacks. Preserves iframe state, CSS animations, focus, form data, `<video>` playback.
165
+
166
+ **Browser support:** Chrome 133+, Firefox 144+, Edge 133+. Safari: not yet.
167
+
168
+ **Why useful:**
169
+ - Array sort/reverse currently detaches all nodes into fragment, then reattaches
170
+ - This triggers `disconnectedCallback` + `connectedCallback` for every node
171
+ - Destroys iframe content, CSS animations, focus state, video playback
172
+ - `moveBefore` preserves all of these — just repositions in the tree
173
+
174
+ **Proposed integration:**
175
+ ```typescript
176
+ // In ArraySlot.sort() / ArraySlot.sync():
177
+ if ('moveBefore' in this.marker.parentNode!) {
178
+ // Use moveBefore for each node
179
+ ref.parentNode!.moveBefore(node, ref.nextSibling);
180
+ }
181
+ else {
182
+ // Fallback: existing fragment-based approach
183
+ }
184
+ ```
185
+
186
+ **Applicability:** High — direct drop-in for `ArraySlot.sort()` and `ArraySlot.sync()`. The LIS algorithm (already implemented) determines which nodes to move; `moveBefore` makes those moves non-destructive.
187
+
188
+ **Complexity:** Low
189
+ **Priority:** High (when Safari ships support)
190
+
191
+ ---
192
+
193
+ ### F7: Two-Way Binding / Form Handling
194
+
195
+ **What:** Syntactic sugar for binding form input values to reactive signals with automatic synchronization.
196
+
197
+ **How frameworks implement:**
198
+ - Vue: `v-model` directive (compiles to value binding + input event)
199
+ - Svelte: `bind:value`, `bind:checked`, `bind:group`
200
+ - SolidJS: No built-in two-way binding (explicit event handlers)
201
+ - Angular: `[(ngModel)]` with FormsModule
202
+
203
+ **Why useful:**
204
+ - Forms are the most common interactive pattern
205
+ - Manual `oninput` + signal write is boilerplate
206
+ - Checkbox groups, radio groups, select multiples need special handling
207
+ - Validation interception (transform before write)
208
+
209
+ **Proposed API:**
210
+ ```typescript
211
+ // Compile-time: detect reactive value in value/checked position
212
+ html`<input value=${signal} />` // auto-bind: set value + listen for input event
213
+
214
+ // Or explicit:
215
+ html`<input bind:value=${signal} />`
216
+ ```
217
+
218
+ **Compile-time detection:** If `value` attribute is an `Effect` (reactive function) on an `<input>`, emit both the attribute binding AND an `input` event listener that writes back to the signal.
219
+
220
+ **Applicability:** High — compile-time system can detect input elements + reactive attributes and emit two-way binding code.
221
+
222
+ **Complexity:** Medium (many edge cases: select, textarea, checkbox, radio, contenteditable)
223
+ **Priority:** Medium-High
224
+
225
+ ---
226
+
227
+ ### F8: Ref / Element Access Pattern
228
+
229
+ **What:** A way to get a reference to the actual DOM element after rendering, for use in imperative DOM manipulation, measurements, third-party library integration.
230
+
231
+ **How frameworks implement:**
232
+ - React: `useRef()` + `ref={myRef}`
233
+ - SolidJS: `let el; <div ref={el}>`
234
+ - Vue: `ref="myRef"` template attribute
235
+ - Lit: `@query('#selector')` decorator
236
+
237
+ **Why useful:**
238
+ - Canvas/WebGL integration needs element reference
239
+ - Third-party library init (charts, maps, editors)
240
+ - Measurements (getBoundingClientRect)
241
+ - Focus management
242
+ - Animation libraries (GSAP, Motion One)
243
+
244
+ **Current library state:** The `onconnect` lifecycle event already provides element reference:
245
+ ```typescript
246
+ html`<div onconnect=${(el) => { /* have reference */ }}>...</div>`
247
+ ```
248
+
249
+ This is already a ref mechanism. A dedicated `ref` API would be sugar for this pattern.
250
+
251
+ **Proposed API:**
252
+ ```typescript
253
+ // Callback ref (already supported via onconnect)
254
+ html`<div onconnect=${(el) => { canvasRef = el; }}>...</div>`
255
+
256
+ // Signal ref (new — populate signal with element)
257
+ let el = signal<HTMLDivElement | null>(null);
258
+ html`<div ref=${el}>...</div>`
259
+ // Compile-time: emit `onconnect` that writes to signal + `ondisconnect` that nulls it
260
+ ```
261
+
262
+ **Applicability:** Low-Medium — `onconnect` already covers the core use case. Signal ref is just syntactic sugar.
263
+
264
+ **Complexity:** Low
265
+ **Priority:** Low
266
+
267
+ ---
268
+
269
+ ### F9: Keyed List Rendering
270
+
271
+ **What:** Explicit key association for list items to ensure correct reconciliation when items are reordered, inserted, or removed. Without keys, positional identity is used (item at index 0 is always the "first" item).
272
+
273
+ **Why useful:**
274
+ - Reordering without keys: animations break, form state lost, components remount
275
+ - With keys: framework knows "item A moved from position 2 to 0" — can reuse existing DOM
276
+ - Critical for: sortable lists, drag-and-drop, filtered lists, paginated data
277
+
278
+ **Current library state:** `ArraySlot` uses `html.reactive(array, template)` — the template function receives the value. There's no explicit key mechanism; the array index IS the identity (positional).
279
+
280
+ **How frameworks handle it:**
281
+ - React: `key` prop on JSX elements
282
+ - SolidJS: `<For each={items}>{(item) => ...}</For>` — items tracked by reference
283
+ - Svelte: `{#each items as item (item.id)}` — parenthetical key expression
284
+ - Lit: `repeat(items, item => item.id, (item) => html\`...\`)` directive
285
+
286
+ **Proposed API:**
287
+ ```typescript
288
+ // Key function as third argument to html.reactive:
289
+ html.reactive(array, (item) => html`<div>${item.name}</div>`, (item) => item.id);
290
+
291
+ // Compile-time: emit ArraySlot with key-based reconciliation
292
+ ```
293
+
294
+ **Applicability:** High — the ArraySlot already has `splice`, `set`, `sort` operations. Adding key-based identity would improve reconciliation when the array is replaced entirely (vs mutated). Compile-time can detect the key function and emit optimal diff code.
295
+
296
+ **Complexity:** Medium-High (key-based diff algorithm, handling key collisions, mapping old→new)
297
+ **Priority:** High
298
+
299
+ ---
300
+
301
+ ### F10: Streaming SSR + Hydration
302
+
303
+ **What:** Server-side rendering that streams HTML chunks as they become ready, with client-side hydration that reuses the server-rendered DOM instead of re-creating it.
304
+
305
+ **How frameworks implement:**
306
+ - React 18: `renderToReadableStream()` + Suspense boundaries for streaming
307
+ - SolidJS: `renderToStream()` with island deduplication
308
+ - Lit: `@lit-labs/ssr` with Declarative Shadow DOM
309
+ - Qwik: Resumability — no hydration needed, just resume from serialized state
310
+
311
+ **Why useful:**
312
+ - TTFB improvement: stream first content immediately
313
+ - Progressive rendering: show above-the-fold content while below-fold loads
314
+ - SEO: search engines crawl the streamed HTML
315
+ - Performance: reuse server DOM instead of re-creating
316
+
317
+ **Key components needed:**
318
+ 1. **Template serializer:** Convert compiled template to HTML string (server-side)
319
+ 2. **Hydration runtime:** Walk existing DOM, attach event handlers, connect reactivity
320
+ 3. **Marker reconciliation:** Match server-rendered markers to slot positions
321
+ 4. **Streaming boundaries:** `Suspense`-like boundaries for progressive flush
322
+
323
+ **Applicability:** Medium — the compile-time system knows template structure. A server-side codegen target could emit `renderToString()` instead of DOM construction. Hydration would walk the existing DOM using the same paths the runtime uses.
324
+
325
+ **Complexity:** High
326
+ **Priority:** Medium (for full-stack applications)
327
+
328
+ ---
329
+
330
+ ### F11: HMR (Hot Module Replacement) for Templates
331
+
332
+ **What:** Fine-grained hot reloading during development — when a template changes, only that template's DOM updates without full page reload.
333
+
334
+ **How frameworks implement:**
335
+ - Vite: Module-level HMR via `import.meta.hot.accept()`
336
+ - Svelte: Component-level HMR preserving state
337
+ - Vue: Template-only HMR (script state preserved)
338
+ - SolidJS + Vite: Component-boundary HMR
339
+
340
+ **Why useful:**
341
+ - Faster development iteration
342
+ - Preserves app state during UI changes
343
+ - Instant visual feedback
344
+
345
+ **Current library state:** The Vite plugin (`compiler/plugins/vite.ts`) transforms templates at build time. No HMR handling exists.
346
+
347
+ **Proposed integration:**
348
+ - Vite plugin emits `import.meta.hot.accept()` for modules with `html` templates
349
+ - On update: re-run template factory, diff against existing DOM, patch
350
+ - Preserve reactive bindings across hot updates
351
+
352
+ **Applicability:** High — Vite plugin is already the entry point. Adding HMR is a natural extension.
353
+
354
+ **Complexity:** Medium
355
+ **Priority:** High (developer experience)
356
+
357
+ ---
358
+
359
+ ### F12: Declarative Shadow DOM
360
+
361
+ **What:** HTML attribute `shadowrootmode="open|closed"` on `<template>` elements creates shadow roots during HTML parsing — no JavaScript needed for initial render.
362
+
363
+ **Browser support:** Chrome 111+, Firefox 125+, Safari 16.4+
364
+
365
+ **Why useful:**
366
+ - SSR'd Web Components render with encapsulation immediately (no FOUC)
367
+ - No JavaScript needed for initial shadow DOM creation
368
+ - Works with streaming SSR
369
+ - `adoptedStyleSheets` share styles across shadow roots efficiently
370
+
371
+ **Proposed integration:**
372
+ ```typescript
373
+ // Compile-time: detect shadow root directive
374
+ html`<my-component>
375
+ <template shadowrootmode="open">
376
+ <style>${styles}</style>
377
+ <slot></slot>
378
+ </template>
379
+ </my-component>`
380
+ ```
381
+
382
+ **Applicability:** Medium — useful for Web Component authoring. Compile-time could emit Declarative Shadow DOM for SSR, then hydrate on client.
383
+
384
+ **Complexity:** Medium
385
+ **Priority:** Low-Medium (niche — Web Component authors)
386
+
387
+ ---
388
+
389
+ ### F13: Incremental Compilation
390
+
391
+ **What:** Only re-compile templates that changed since last build, using cached AST data.
392
+
393
+ **How frameworks implement:**
394
+ - TypeScript: `.tsbuildinfo` for incremental builds
395
+ - Vite: Module dependency graph + timestamp checking
396
+ - esbuild: Content hash comparison
397
+
398
+ **Why useful:**
399
+ - Large projects with 100+ templates: full recompilation is slow
400
+ - Dev builds benefit most — save seconds per change
401
+ - CI builds: cache compilation artifacts between runs
402
+
403
+ **Proposed implementation:**
404
+ - Store template hashes + compiled output in `.tsbuildinfo`-style cache
405
+ - On build: compare template string hash, skip unchanged
406
+ - Vite plugin: leverage Vite's module graph for dependency tracking
407
+
408
+ **Applicability:** Medium — more valuable as the library is adopted in larger projects.
409
+
410
+ **Complexity:** Medium
411
+ **Priority:** Medium (scales with project size)
412
+
413
+ ---
414
+
415
+ ## Priority Matrix
416
+
417
+ ### Tier 1 — High Value, Practical to Implement
418
+
419
+ | # | Feature | Complexity | Notes |
420
+ |---|---------|------------|-------|
421
+ | F6 | `moveBefore()` API | Low | Drop-in for ArraySlot sort/sync |
422
+ | F9 | Keyed list rendering | Medium-High | Correctness requirement for real apps |
423
+ | F11 | HMR for templates | Medium | Critical DX improvement |
424
+
425
+ ### Tier 2 — High Value, Higher Effort
426
+
427
+ | # | Feature | Complexity | Notes |
428
+ |---|---------|------------|-------|
429
+ | F2 | Enter/exit animations | Medium | Needs deferred removal |
430
+ | F3 | Suspense / Async boundaries | Medium-High | New slot type |
431
+ | F4 | Error boundaries | Low-Medium | Try/catch wrapper |
432
+ | F7 | Two-way binding | Medium | Many input type edge cases |
433
+
434
+ ### Tier 3 — Nice to Have
435
+
436
+ | # | Feature | Complexity | Notes |
437
+ |---|---------|------------|-------|
438
+ | F1 | View Transitions API | Low-Medium | Wrapper around browser API |
439
+ | F5 | Popover API integration | Low | Attribute detection + events |
440
+ | F8 | Ref / element access | Low | `onconnect` already covers this |
441
+ | F12 | Declarative Shadow DOM | Medium | Niche use case |
442
+ | F13 | Incremental compilation | Medium | Scales with adoption |
443
+
444
+ ### Tier 4 — Strategic / Long-term
445
+
446
+ | # | Feature | Complexity | Notes |
447
+ |---|---------|------------|-------|
448
+ | F10 | Streaming SSR + Hydration | High | Full-stack requirement |
449
+
450
+ ---
451
+
452
+ ## Recommended Implementation Order
453
+
454
+ 1. **F6: `moveBefore()`** — Low complexity, improves ArraySlot sort/reverse
455
+ 2. **F4: Error boundaries** — Low-medium complexity, production necessity
456
+ 3. **F9: Keyed lists** — Medium-high complexity, correctness requirement
457
+ 4. **F7: Two-way binding** — Medium complexity, form-heavy apps
458
+ 5. **F2: Enter/exit animations** — Medium complexity, visual polish
459
+ 6. **F11: HMR** — Medium complexity, developer experience
460
+ 7. **F3: Suspense** — Medium-high complexity, async patterns
461
+ 8. **F1: View Transitions** — Low complexity but niche, can add anytime
462
+
463
+ ---
464
+
465
+ ## Research Sources
466
+
467
+ - Lit 3.x documentation (directives, SSR, task management)
468
+ - Svelte 5 runes specification (fine-grained reactivity, snippets)
469
+ - SolidJS 2.0 RFC (async primitives, streaming dedup, `isPending`)
470
+ - Qwik documentation (resumability, symbol extraction)
471
+ - Million.js architecture (block virtual DOM)
472
+ - MDN: View Transitions API, Popover API, `moveBefore()`, Declarative Shadow DOM
473
+ - Chrome DevRel blog: View Transitions (Chrome 111/126), Popover API (Chrome 114)
474
+ - TC39 Signals proposal (Stage 1)
475
+ - Vite HMR documentation
Binary file
@@ -61,7 +61,7 @@ describe('attributes', () => {
61
61
  it('sets style attribute', () => {
62
62
  setProperty(element as unknown as Element, 'style', 'color: red');
63
63
 
64
- expect(element.getAttribute('style')).toBe('color: red');
64
+ expect(element.style.cssText).toContain('color: red');
65
65
  });
66
66
 
67
67
  it('sets data-* attributes via setAttribute', () => {
@@ -107,7 +107,8 @@ describe('attributes', () => {
107
107
  it('merges static and dynamic styles', () => {
108
108
  setList(element as unknown as Element, 'style', 'font-size: 14px', { style: 'color: red' });
109
109
 
110
- expect(element.getAttribute('style')).toBe('color: red;font-size: 14px');
110
+ expect(element.style.cssText).toContain('color: red');
111
+ expect(element.style.cssText).toContain('font-size: 14px');
111
112
  });
112
113
 
113
114
  it('handles null value', () => {