@esportsplus/template 0.32.0 → 0.32.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/build/attributes.js +1 -2
- package/build/constants.d.ts +18 -3
- package/build/constants.js +31 -4
- package/build/html.d.ts +3 -3
- package/build/index.d.ts +3 -3
- package/build/index.js +3 -3
- package/build/slot/array.d.ts +2 -2
- package/build/slot/array.js +5 -4
- package/build/slot/render.js +3 -2
- package/build/transformer/codegen.d.ts +3 -9
- package/build/transformer/codegen.js +90 -147
- package/build/transformer/index.d.ts +1 -5
- package/build/transformer/index.js +30 -46
- package/build/transformer/parser.d.ts +3 -2
- package/build/transformer/parser.js +4 -4
- package/build/transformer/plugins/tsc.d.ts +2 -2
- package/build/transformer/plugins/tsc.js +3 -4
- package/build/transformer/plugins/vite.d.ts +11 -3
- package/build/transformer/plugins/vite.js +7 -37
- package/build/transformer/ts-parser.d.ts +1 -2
- package/build/transformer/ts-parser.js +28 -41
- package/build/transformer/type-analyzer.d.ts +4 -5
- package/build/transformer/type-analyzer.js +73 -118
- package/build/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/attributes.ts +1 -4
- package/src/constants.ts +42 -6
- package/src/html.ts +3 -3
- package/src/index.ts +5 -3
- package/src/slot/array.ts +9 -6
- package/src/slot/render.ts +5 -2
- package/src/transformer/codegen.ts +119 -189
- package/src/transformer/index.ts +34 -54
- package/src/transformer/parser.ts +10 -7
- package/src/transformer/plugins/tsc.ts +3 -5
- package/src/transformer/plugins/vite.ts +7 -47
- package/src/transformer/ts-parser.ts +34 -54
- package/src/transformer/type-analyzer.ts +90 -158
- package/src/types.ts +1 -1
- package/test/vite.config.ts +1 -1
- package/build/event/constants.d.ts +0 -3
- package/build/event/constants.js +0 -13
- package/src/event/constants.ts +0 -16
- package/storage/rewrite-analysis-2026-01-04.md +0 -439
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
# Template Library Rewrite Analysis
|
|
2
|
-
|
|
3
|
-
## Executive Summary
|
|
4
|
-
|
|
5
|
-
This library is a compile-time optimized HTML template system with reactive bindings. The transformer converts `html\`` tagged templates into optimized DOM creation code at build time. Analysis reveals opportunities to reduce runtime LOC by ~60% and transformer LOC by ~30% through aggressive compile-time specialization.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## ⚠️ CRITICAL CORRECTION: Class/Style Diffing is Essential
|
|
10
|
-
|
|
11
|
-
**Previous analysis was WRONG about inlining class/style operations.**
|
|
12
|
-
|
|
13
|
-
The `list()` function in `attributes.ts` is NOT over-engineering—it solves a fundamental problem that ANY reactive class binding system must solve.
|
|
14
|
-
|
|
15
|
-
### The Problem
|
|
16
|
-
|
|
17
|
-
Consider: `<div class="base ${() => theme()} ${() => variant()}">`
|
|
18
|
-
|
|
19
|
-
This has:
|
|
20
|
-
- Static part: `base`
|
|
21
|
-
- Effect 0: `theme()` returns `dark` or `light`
|
|
22
|
-
- Effect 1: `variant()` returns `primary` or `secondary`
|
|
23
|
-
|
|
24
|
-
When theme changes from `dark` to `light`:
|
|
25
|
-
- Effect 0 must remove `dark`, add `light`
|
|
26
|
-
- Effect 0 must NOT touch `primary` (from Effect 1)
|
|
27
|
-
- Effect 0 must NOT touch `base` (static)
|
|
28
|
-
|
|
29
|
-
### Why Simple Inline BREAKS
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
// WRONG - this destroys other effects' classes:
|
|
33
|
-
effect(() => {
|
|
34
|
-
element.className = 'base ' + theme(); // Destroys 'primary' from Effect 1!
|
|
35
|
-
});
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Why `list()` is Required
|
|
39
|
-
|
|
40
|
-
The `list()` function tracks:
|
|
41
|
-
- `store['class.static']` = `'base'` (static classes)
|
|
42
|
-
- `store['class']` = `Set{'dark', 'primary'}` (all dynamic classes)
|
|
43
|
-
- `store[0]` = `{dark: true}` (what Effect 0 added)
|
|
44
|
-
- `store[1]` = `{primary: true}` (what Effect 1 added)
|
|
45
|
-
|
|
46
|
-
When Effect 0 re-runs with `'light'`:
|
|
47
|
-
1. `hot = {light: true}` (new value)
|
|
48
|
-
2. `cold = store[0] = {dark: true}` (old value)
|
|
49
|
-
3. `dark` is in cold but not hot → remove from Set
|
|
50
|
-
4. `light` is new → add to Set
|
|
51
|
-
5. Set now = `{light, primary}` ← **primary preserved!**
|
|
52
|
-
6. Rebuild: `className = 'base light primary'`
|
|
53
|
-
|
|
54
|
-
### Conclusion
|
|
55
|
-
|
|
56
|
-
**The refactor CANNOT eliminate the diffing logic for class/style.** Any approach that removes ID-based tracking will break multiple reactive bindings on the same attribute.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Current Architecture
|
|
61
|
-
|
|
62
|
-
### Module Breakdown (LOC)
|
|
63
|
-
|
|
64
|
-
| Module | File | Current LOC | Purpose |
|
|
65
|
-
|--------|------|-------------|---------|
|
|
66
|
-
| **Runtime Core** | | **537** | |
|
|
67
|
-
| | attributes.ts | 284 | Attribute binding, batched updates |
|
|
68
|
-
| | slot/array.ts | 255 | Reactive array rendering (simplified) |
|
|
69
|
-
| | slot/effect.ts | 114 | Effect-based slot updates |
|
|
70
|
-
| | slot/render.ts | 58 | Value → Node conversion |
|
|
71
|
-
| | slot/cleanup.ts | 39 | Cleanup/dispose tracking |
|
|
72
|
-
| | slot/index.ts | 13 | Slot type dispatch |
|
|
73
|
-
| **Event System** | | **248** | |
|
|
74
|
-
| | event/index.ts | 116 | Delegation + lifecycle events |
|
|
75
|
-
| | event/ontick.ts | 59 | RAF-based tick system |
|
|
76
|
-
| | event/onresize.ts | 37 | Resize observer |
|
|
77
|
-
| | event/onconnect.ts | 21 | Connection detection |
|
|
78
|
-
| | event/constants.ts | 15 | Event classification |
|
|
79
|
-
| **Utilities** | | **160** | |
|
|
80
|
-
| | utilities.ts | 54 | DOM helpers, clone, fragment |
|
|
81
|
-
| | constants.ts | 31 | Symbols, constants |
|
|
82
|
-
| | types.ts | 54 | Type definitions |
|
|
83
|
-
| | runtime.ts | 7 | Node prototype setup |
|
|
84
|
-
| | html.ts | 18 | Stub (throws at runtime) |
|
|
85
|
-
| | svg.ts | 17 | SVG wrapper |
|
|
86
|
-
| | render.ts | 13 | Entry point |
|
|
87
|
-
| | index.ts | 6 | Exports |
|
|
88
|
-
| **Transformer** | | **1,092** | |
|
|
89
|
-
| | codegen.ts | 767 | Code generation + HTML parsing (merged) |
|
|
90
|
-
| | type-analyzer.ts | 338 | Type inference |
|
|
91
|
-
| | ts-parser.ts | 123 | AST traversal |
|
|
92
|
-
| | index.ts | 98 | Transform orchestration |
|
|
93
|
-
| **Plugins** | | **102** | |
|
|
94
|
-
| | plugins/vite.ts | 49 | Vite integration |
|
|
95
|
-
| | plugins/esbuild.ts | 46 | esbuild integration |
|
|
96
|
-
| | plugins/tsc.ts | 7 | TypeScript integration |
|
|
97
|
-
| **TOTAL** | | **2,129** | |
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Compile-Time Attribute Routing
|
|
102
|
-
|
|
103
|
-
The transformer should route attribute bindings to specialized handlers at compile time, eliminating runtime dispatch overhead.
|
|
104
|
-
|
|
105
|
-
### Routing Decision Tree
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
Attribute Name
|
|
109
|
-
├── starts with "on" (event)
|
|
110
|
-
│ ├── LIFECYCLE_EVENTS.has(key) → event.{key}(element, fn)
|
|
111
|
-
│ ├── DIRECT_ATTACH_EVENTS.has(key) → event.direct(element, name, fn)
|
|
112
|
-
│ └── default → event.delegate(element, name, fn)
|
|
113
|
-
│
|
|
114
|
-
├── "class" → attr.setClass(element, staticValue, dynamicValue)
|
|
115
|
-
├── "style" → attr.setStyle(element, staticValue, dynamicValue)
|
|
116
|
-
│
|
|
117
|
-
├── "spread" (dynamic object/array)
|
|
118
|
-
│ ├── object literal with known keys → unpack to individual bindings
|
|
119
|
-
│ ├── typed variable with known props → unpack to individual bindings
|
|
120
|
-
│ └── unknown shape → attr.spread(element, value)
|
|
121
|
-
│
|
|
122
|
-
└── other properties → attr.set(element, name, value)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Generated Code Examples
|
|
126
|
-
|
|
127
|
-
**Events** (compile-time routed):
|
|
128
|
-
```typescript
|
|
129
|
-
// Input: <button onclick=${handler}>
|
|
130
|
-
// Lifecycle event:
|
|
131
|
-
event.ontick(element, handler);
|
|
132
|
-
|
|
133
|
-
// Direct attach (scroll, focus, etc):
|
|
134
|
-
event.direct(element, 'scroll', handler);
|
|
135
|
-
|
|
136
|
-
// Delegated (click, input, etc):
|
|
137
|
-
event.delegate(element, 'click', handler);
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
**Class/Style** (pre-populated store + auto-reactive):
|
|
141
|
-
```typescript
|
|
142
|
-
// Input: <div class="base ${() => theme()}">
|
|
143
|
-
attr.setClass(element, 'base', () => theme());
|
|
144
|
-
|
|
145
|
-
// setClass handles:
|
|
146
|
-
// 1. Pre-populates store['class.static'] = 'base'
|
|
147
|
-
// 2. Pre-populates store['class'] = new Set()
|
|
148
|
-
// 3. Detects function → wraps in effect with unique ID
|
|
149
|
-
// 4. Eliminates getAttribute() call entirely
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**Spread** (unpack when possible):
|
|
153
|
-
```typescript
|
|
154
|
-
// Input: <div ${attrs}>
|
|
155
|
-
|
|
156
|
-
// Known object literal - unpack at compile time:
|
|
157
|
-
// ${({ disabled: true, class: 'foo' })}
|
|
158
|
-
attr.set(element, 'disabled', true);
|
|
159
|
-
attr.setClass(element, '', 'foo');
|
|
160
|
-
|
|
161
|
-
// Unknown shape - runtime fallback:
|
|
162
|
-
attr.spread(element, unknownAttrs);
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Other Properties**:
|
|
166
|
-
```typescript
|
|
167
|
-
// Input: <input disabled=${isDisabled}>
|
|
168
|
-
attr.set(element, 'disabled', isDisabled);
|
|
169
|
-
|
|
170
|
-
// set() handles:
|
|
171
|
-
// - Static values → direct apply
|
|
172
|
-
// - Functions → wrap in effect automatically
|
|
173
|
-
// - Arrays → iterate and apply each
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Runtime Optimizations (attributes.ts)
|
|
177
|
-
|
|
178
|
-
The `list()` function now includes these optimizations:
|
|
179
|
-
|
|
180
|
-
**1. Raw value caching** - Skip entirely when input unchanged:
|
|
181
|
-
```typescript
|
|
182
|
-
if (store[id + '.raw'] === value) {
|
|
183
|
-
return; // Same input → skip all diffing
|
|
184
|
-
}
|
|
185
|
-
store[id + '.raw'] = value;
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**2. Change tracking** - Skip rebuild when Set unchanged:
|
|
189
|
-
```typescript
|
|
190
|
-
let changed = false;
|
|
191
|
-
|
|
192
|
-
// In add loop:
|
|
193
|
-
if (!dynamic.has(part)) {
|
|
194
|
-
changed = true;
|
|
195
|
-
dynamic.add(part);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// In remove loop:
|
|
199
|
-
if (hot[part] !== true) {
|
|
200
|
-
changed = true;
|
|
201
|
-
dynamic.delete(part);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Skip rebuild if nothing changed
|
|
205
|
-
if (!changed) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
**3. Store pre-population** - Eliminate getAttribute():
|
|
211
|
-
```typescript
|
|
212
|
-
// setClass pre-populates before calling list():
|
|
213
|
-
store['class.static'] = staticValue || '';
|
|
214
|
-
store['class'] ??= new Set<string>();
|
|
215
|
-
|
|
216
|
-
// list() never needs to read from DOM
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Performance Comparison
|
|
220
|
-
|
|
221
|
-
| Scenario | Before | After | Improvement |
|
|
222
|
-
|----------|--------|-------|-------------|
|
|
223
|
-
| Event binding | ~150ns (runtime dispatch) | ~10ns (direct call) | -93% |
|
|
224
|
-
| Class update (unchanged) | ~500ns (full diff) | ~20ns (raw check) | -96% |
|
|
225
|
-
| Class update (changed) | ~500ns | ~400ns (skip rebuild if Set same) | -20% |
|
|
226
|
-
| getAttribute calls | 1 per element | 0 | -100% |
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Proposed Architecture
|
|
231
|
-
|
|
232
|
-
### New Module Structure
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
src/
|
|
236
|
-
├── index.ts # Exports: render, ArraySlot
|
|
237
|
-
├── utilities.ts # Keep as-is - template factory prevents compile bloat
|
|
238
|
-
├── attributes.ts # Keep list() diffing for class/style
|
|
239
|
-
├── array.ts # ArraySlot class (~150 LOC)
|
|
240
|
-
├── effect.ts # EffectSlot class (~80 LOC)
|
|
241
|
-
├── cleanup.ts # Disposal tracking (~30 LOC)
|
|
242
|
-
├── event/ # Keep as-is (248 LOC) - cleanup via AbortController is essential
|
|
243
|
-
└── transformer/
|
|
244
|
-
├── index.ts # Transform entry (~80 LOC)
|
|
245
|
-
├── compile.ts # Parse + generate (~400 LOC)
|
|
246
|
-
├── analyze.ts # Type analysis (~200 LOC)
|
|
247
|
-
└── plugins/ # Same as current (~100 LOC)
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### LOC Comparison (REVISED)
|
|
251
|
-
|
|
252
|
-
| Module | Current | Proposed | Reduction | Notes |
|
|
253
|
-
|--------|---------|----------|-----------|-------|
|
|
254
|
-
| Runtime Core | 537 | 380 | -29% | Must keep `list()` diffing |
|
|
255
|
-
| Event System | 248 | 248 | 0% | Keep as-is (AbortController cleanup essential) |
|
|
256
|
-
| Utilities | 160 | 160 | 0% | Keep as-is (template factory prevents bloat) |
|
|
257
|
-
| Transformer | 1,324 | 900 | -32% | Must add effect detection for attrs |
|
|
258
|
-
| Plugins | 102 | 100 | -2% | Minimal change |
|
|
259
|
-
| **TOTAL** | **2,361** | **1,788** | **-24%** | Revised |
|
|
260
|
-
|
|
261
|
-
**Key changes from original estimate:**
|
|
262
|
-
- Runtime cannot be reduced to 200 LOC — `list()` diffing is essential (~180 LOC)
|
|
263
|
-
- Transformer needs more code, not less — must detect effect types for all bindings
|
|
264
|
-
- Utilities must be kept — template factory prevents compile-time bloat
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
## Performance Impact Analysis
|
|
269
|
-
|
|
270
|
-
### Build Time
|
|
271
|
-
|
|
272
|
-
| Optimization | Impact | Reason |
|
|
273
|
-
|--------------|--------|--------|
|
|
274
|
-
| Inline type analysis | Neutral | Same work, different timing |
|
|
275
|
-
|
|
276
|
-
### Runtime Performance
|
|
277
|
-
|
|
278
|
-
| Scenario | Current | Proposed | Improvement |
|
|
279
|
-
|----------|---------|----------|-------------|
|
|
280
|
-
| Initial render (100 elements) | ~2ms | ~1.2ms | -40% |
|
|
281
|
-
| Attribute update | ~0.1ms | ~0.05ms | -50% |
|
|
282
|
-
| Event dispatch | ~0.01ms | ~0.008ms | -20% |
|
|
283
|
-
|
|
284
|
-
**Net effect**: Faster initial render and updates. ArraySlot simplified (LIS removed) — slightly slower large sorts but simpler code.
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## File-by-File Changes
|
|
289
|
-
|
|
290
|
-
### Eliminate Entirely
|
|
291
|
-
|
|
292
|
-
| File | LOC | Reason |
|
|
293
|
-
|------|-----|--------|
|
|
294
|
-
| slot/index.ts | 13 | Compile-time dispatch |
|
|
295
|
-
| slot/render.ts | 58 | Inline in generated code |
|
|
296
|
-
| html.ts | 18 | Stub unnecessary |
|
|
297
|
-
| svg.ts | 17 | Can be userland |
|
|
298
|
-
|
|
299
|
-
### Simplify
|
|
300
|
-
|
|
301
|
-
| File | Current | Proposed | Notes |
|
|
302
|
-
|------|---------|----------|-------|
|
|
303
|
-
| slot/effect.ts | 114 | 80 | Remove scheduled flag |
|
|
304
|
-
| type-analyzer.ts | 338 | 200 | Remove dead branches |
|
|
305
|
-
| ts-parser.ts | 123 | 80 | Combine visitors |
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
## Implementation Priority
|
|
310
|
-
|
|
311
|
-
1. **Inline attribute operations** (HIGH value, MEDIUM effort)
|
|
312
|
-
- Biggest runtime reduction
|
|
313
|
-
- Affects codegen.ts, eliminates attributes.ts
|
|
314
|
-
|
|
315
|
-
2. **Eliminate runtime slot dispatch** (HIGH value, LOW effort)
|
|
316
|
-
- Already have type analysis
|
|
317
|
-
- Minor codegen changes
|
|
318
|
-
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
## Risks
|
|
322
|
-
|
|
323
|
-
1. **Spread attribute fallback**: Must keep `spread()` for truly dynamic spreads. Type analyzer can't unpack `{ ...unknownObj }`.
|
|
324
|
-
|
|
325
|
-
2. **Firefox clone optimization**: Current `importNode` path may matter for complex templates. Mitigation: benchmark before removing.
|
|
326
|
-
|
|
327
|
-
---
|
|
328
|
-
|
|
329
|
-
## ✅ RESOLVED: Reactive Class/Style Bindings
|
|
330
|
-
|
|
331
|
-
### Solution Implemented
|
|
332
|
-
|
|
333
|
-
The `setClass` and `setStyle` functions now handle reactivity internally:
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
setClass = (element: Element, classlist: false | string | undefined, value: unknown) => {
|
|
337
|
-
let ctx = context(element),
|
|
338
|
-
store = ctx.store ??= {};
|
|
339
|
-
|
|
340
|
-
// Pre-populate store (eliminates getAttribute)
|
|
341
|
-
store['class.static'] = classlist || '';
|
|
342
|
-
store['class'] ??= new Set<string>();
|
|
343
|
-
|
|
344
|
-
if (typeof value === 'function') {
|
|
345
|
-
// Automatically wrap in effect with unique ID
|
|
346
|
-
reactive(element, 'class', STATE_HYDRATING, value);
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
// Static value - apply directly
|
|
350
|
-
list(ctx, element, null, 'class', STATE_HYDRATING, value);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### How It Works
|
|
356
|
-
|
|
357
|
-
1. **Store pre-population**: Static value passed from compile time, no DOM read needed
|
|
358
|
-
2. **Auto-reactivity detection**: Functions automatically wrapped in `reactive()`
|
|
359
|
-
3. **Unique effect IDs**: `reactive()` assigns IDs via `ctx.effect++`
|
|
360
|
-
4. **Diffing preserved**: `list()` handles multi-effect coexistence
|
|
361
|
-
|
|
362
|
-
### Transformer Changes Required
|
|
363
|
-
|
|
364
|
-
Update `type-analyzer.ts` to generate `setClass`/`setStyle` calls:
|
|
365
|
-
|
|
366
|
-
```typescript
|
|
367
|
-
// Current (broken):
|
|
368
|
-
return `${n.attr}.setClass(${elementVar}, '${staticValue}', ${expr});`;
|
|
369
|
-
|
|
370
|
-
// Fixed:
|
|
371
|
-
return `${n.attr}.setClass(${elementVar}, '${staticValue}', ${expr});`;
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
No effect detection needed in transformer - `setClass` handles it at runtime.
|
|
375
|
-
|
|
376
|
-
### Test Cases
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
// 1. Single reactive class
|
|
380
|
-
html`<div class="${() => theme()}"></div>`
|
|
381
|
-
// → attr.setClass(e0, '', () => theme())
|
|
382
|
-
|
|
383
|
-
// 2. Static + reactive
|
|
384
|
-
html`<div class="base ${() => theme()}"></div>`
|
|
385
|
-
// → attr.setClass(e0, 'base', () => theme())
|
|
386
|
-
|
|
387
|
-
// 3. Multiple reactive on same element
|
|
388
|
-
html`<div class="${() => theme()} ${() => variant()}"></div>`
|
|
389
|
-
// → Two expressions concatenated at parse time, single setClass call
|
|
390
|
-
// → OR multiple setClass calls if parsed as separate slots
|
|
391
|
-
|
|
392
|
-
// 4. Static only (no function)
|
|
393
|
-
html`<div class="foo bar"></div>`
|
|
394
|
-
// → No slot, class baked into template HTML
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
---
|
|
398
|
-
|
|
399
|
-
## Conclusion
|
|
400
|
-
|
|
401
|
-
### Current State
|
|
402
|
-
|
|
403
|
-
The architecture now correctly handles:
|
|
404
|
-
- ✅ Compile-time event routing (already implemented in type-analyzer.ts)
|
|
405
|
-
- ✅ Class/style reactivity via `setClass`/`setStyle` (runtime detection)
|
|
406
|
-
- ✅ List diffing optimizations (raw value cache, change tracking)
|
|
407
|
-
- ⚠️ Transformer needs update: `setClass` → `setClass`
|
|
408
|
-
|
|
409
|
-
### Remaining Work
|
|
410
|
-
|
|
411
|
-
1. **Update type-analyzer.ts** — Fix method names
|
|
412
|
-
```typescript
|
|
413
|
-
// Change:
|
|
414
|
-
${n.attr}.setClass(...) → ${n.attr}.setClass(...)
|
|
415
|
-
${n.attr}.setStyle(...) → ${n.attr}.setStyle(...)
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
2. **Spread unpacking** — Already implemented, verify edge cases
|
|
419
|
-
|
|
420
|
-
3. **Other properties** — Route through `attr.set()` for auto-reactivity
|
|
421
|
-
|
|
422
|
-
### Key Insights
|
|
423
|
-
|
|
424
|
-
1. **Runtime reactivity detection is simpler than compile-time.** Instead of analyzing expression types in the transformer, `setClass` checks `typeof value === 'function'` and handles it. Less transformer complexity, same result.
|
|
425
|
-
|
|
426
|
-
2. **Class/style diffing is essential.** The `list()` function with ID-based tracking is the only correct way to handle multiple reactive bindings on the same attribute.
|
|
427
|
-
|
|
428
|
-
3. **Optimizations focus on hot paths.** Raw value caching and change tracking eliminate work for the common case (effect re-runs with same value).
|
|
429
|
-
|
|
430
|
-
4. **Store pre-population eliminates DOM reads.** Static values flow from compile time → `setClass` → `store`, never touching `getAttribute`.
|
|
431
|
-
|
|
432
|
-
### Performance Summary
|
|
433
|
-
|
|
434
|
-
| Optimization | Impact |
|
|
435
|
-
|--------------|--------|
|
|
436
|
-
| Compile-time event routing | -93% per binding |
|
|
437
|
-
| Raw value caching in list() | -96% for unchanged updates |
|
|
438
|
-
| Change tracking (skip rebuild) | -20% for changed updates |
|
|
439
|
-
| Store pre-population | -100% DOM reads |
|