@esportsplus/template 0.16.0

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 (90) hide show
  1. package/.editorconfig +9 -0
  2. package/.gitattributes +2 -0
  3. package/.github/dependabot.yml +25 -0
  4. package/.github/workflows/bump.yml +9 -0
  5. package/.github/workflows/dependabot.yml +12 -0
  6. package/.github/workflows/publish.yml +16 -0
  7. package/README.md +385 -0
  8. package/build/attributes.d.ts +5 -0
  9. package/build/attributes.js +212 -0
  10. package/build/compiler/codegen.d.ts +21 -0
  11. package/build/compiler/codegen.js +303 -0
  12. package/build/compiler/constants.d.ts +16 -0
  13. package/build/compiler/constants.js +19 -0
  14. package/build/compiler/index.d.ts +14 -0
  15. package/build/compiler/index.js +61 -0
  16. package/build/compiler/parser.d.ts +19 -0
  17. package/build/compiler/parser.js +164 -0
  18. package/build/compiler/plugins/tsc.d.ts +3 -0
  19. package/build/compiler/plugins/tsc.js +4 -0
  20. package/build/compiler/plugins/vite.d.ts +13 -0
  21. package/build/compiler/plugins/vite.js +8 -0
  22. package/build/compiler/ts-analyzer.d.ts +4 -0
  23. package/build/compiler/ts-analyzer.js +63 -0
  24. package/build/compiler/ts-parser.d.ts +24 -0
  25. package/build/compiler/ts-parser.js +67 -0
  26. package/build/constants.d.ts +12 -0
  27. package/build/constants.js +25 -0
  28. package/build/event/index.d.ts +10 -0
  29. package/build/event/index.js +90 -0
  30. package/build/event/onconnect.d.ts +3 -0
  31. package/build/event/onconnect.js +15 -0
  32. package/build/event/onresize.d.ts +3 -0
  33. package/build/event/onresize.js +26 -0
  34. package/build/event/ontick.d.ts +6 -0
  35. package/build/event/ontick.js +41 -0
  36. package/build/html.d.ts +9 -0
  37. package/build/html.js +7 -0
  38. package/build/index.d.ts +8 -0
  39. package/build/index.js +12 -0
  40. package/build/render.d.ts +3 -0
  41. package/build/render.js +8 -0
  42. package/build/slot/array.d.ts +25 -0
  43. package/build/slot/array.js +189 -0
  44. package/build/slot/cleanup.d.ts +4 -0
  45. package/build/slot/cleanup.js +23 -0
  46. package/build/slot/effect.d.ts +12 -0
  47. package/build/slot/effect.js +85 -0
  48. package/build/slot/index.d.ts +7 -0
  49. package/build/slot/index.js +14 -0
  50. package/build/slot/render.d.ts +2 -0
  51. package/build/slot/render.js +44 -0
  52. package/build/svg.d.ts +5 -0
  53. package/build/svg.js +14 -0
  54. package/build/types.d.ts +23 -0
  55. package/build/types.js +1 -0
  56. package/build/utilities.d.ts +7 -0
  57. package/build/utilities.js +31 -0
  58. package/package.json +43 -0
  59. package/src/attributes.ts +313 -0
  60. package/src/compiler/codegen.ts +492 -0
  61. package/src/compiler/constants.ts +25 -0
  62. package/src/compiler/index.ts +87 -0
  63. package/src/compiler/parser.ts +242 -0
  64. package/src/compiler/plugins/tsc.ts +6 -0
  65. package/src/compiler/plugins/vite.ts +10 -0
  66. package/src/compiler/ts-analyzer.ts +89 -0
  67. package/src/compiler/ts-parser.ts +112 -0
  68. package/src/constants.ts +44 -0
  69. package/src/event/index.ts +130 -0
  70. package/src/event/onconnect.ts +22 -0
  71. package/src/event/onresize.ts +37 -0
  72. package/src/event/ontick.ts +59 -0
  73. package/src/html.ts +18 -0
  74. package/src/index.ts +19 -0
  75. package/src/llm.txt +403 -0
  76. package/src/render.ts +13 -0
  77. package/src/slot/array.ts +257 -0
  78. package/src/slot/cleanup.ts +37 -0
  79. package/src/slot/effect.ts +114 -0
  80. package/src/slot/index.ts +17 -0
  81. package/src/slot/render.ts +61 -0
  82. package/src/svg.ts +27 -0
  83. package/src/types.ts +40 -0
  84. package/src/utilities.ts +53 -0
  85. package/storage/compiler-architecture-2026-01-13.md +420 -0
  86. package/test/dist/test.js +1912 -0
  87. package/test/dist/test.js.map +1 -0
  88. package/test/index.ts +648 -0
  89. package/test/vite.config.ts +23 -0
  90. package/tsconfig.json +8 -0
package/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 4
6
+ charset = utf-8
7
+ trim_trailing_whitespace = true
8
+ insert_final_newline = true
9
+ end_of_line = lf
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,25 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+
8
+ registries:
9
+ npm-npmjs:
10
+ token: ${{secrets.NPM_TOKEN}}
11
+ type: npm-registry
12
+ url: https://registry.npmjs.org
13
+
14
+ updates:
15
+ - package-ecosystem: "npm"
16
+ directory: "/"
17
+ groups:
18
+ production-dependencies:
19
+ dependency-type: "production"
20
+ development-dependencies:
21
+ dependency-type: "development"
22
+ registries:
23
+ - npm-npmjs
24
+ schedule:
25
+ interval: "daily"
@@ -0,0 +1,9 @@
1
+ name: bump version
2
+
3
+ on:
4
+ push:
5
+ branches: '**' # only trigger on branches, not on tags
6
+
7
+ jobs:
8
+ bump:
9
+ uses: esportsplus/typescript/.github/workflows/bump.yml@main
@@ -0,0 +1,12 @@
1
+ name: dependabot automerge
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, labeled]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ automerge:
10
+ secrets:
11
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
12
+ uses: esportsplus/typescript/.github/workflows/dependabot.yml@main
@@ -0,0 +1,16 @@
1
+ name: publish to npm
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ workflow_run:
8
+ workflows: [bump version]
9
+ types:
10
+ - completed
11
+
12
+ jobs:
13
+ publish:
14
+ secrets:
15
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
16
+ uses: esportsplus/typescript/.github/workflows/publish.yml@main
package/README.md ADDED
@@ -0,0 +1,385 @@
1
+ # @esportsplus/template
2
+
3
+ High-performance, compiler-optimized HTML templating library for JavaScript/TypeScript. Templates are transformed at build time into optimized DOM manipulation code with zero runtime parsing overhead.
4
+
5
+ ## Features
6
+
7
+ - **Compile-time transformation** - Templates converted to optimized code during build
8
+ - **Zero runtime parsing** - No template parsing at runtime
9
+ - **Reactive integration** - Works with `@esportsplus/reactivity` for dynamic updates
10
+ - **Event delegation** - Efficient event handling with automatic delegation
11
+ - **Lifecycle events** - `onconnect`, `ondisconnect`, `onrender`, `onresize`, `ontick`
12
+ - **Type-safe** - Full TypeScript support
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @esportsplus/template
18
+ ```
19
+
20
+ ## Transformer Plugins
21
+
22
+ The library requires a build-time transformer to convert template literals into optimized code. Choose the plugin for your build tool:
23
+
24
+ ### Vite
25
+
26
+ ```typescript
27
+ // vite.config.ts
28
+ import { defineConfig } from 'vite';
29
+ import template from '@esportsplus/template/compiler/vite';
30
+
31
+ export default defineConfig({
32
+ plugins: [template]
33
+ });
34
+ ```
35
+
36
+ ### TypeScript Compiler (tsc)
37
+
38
+ For direct `tsc` compilation, use the transformer:
39
+
40
+ ```typescript
41
+ // tsconfig.json (with ts-patch or ttypescript)
42
+ {
43
+ "compilerOptions": {
44
+ "plugins": [
45
+ { "transform": "@esportsplus/template/compiler/tsc" }
46
+ ]
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Basic Usage
52
+
53
+ ```typescript
54
+ import { html, render } from '@esportsplus/template';
55
+
56
+ // Static template
57
+ const greeting = html`<div class="greeting">Hello World</div>`;
58
+
59
+ // Dynamic text
60
+ const message = (text: string) => html`<div>${text}</div>`;
61
+
62
+ // Dynamic attributes
63
+ const button = (cls: string) => html`<button class="${cls}">Click</button>`;
64
+
65
+ // Render to DOM
66
+ render(document.body, greeting);
67
+ ```
68
+
69
+ ## Template Syntax
70
+
71
+ ### Text Slots
72
+
73
+ ```typescript
74
+ // Basic text interpolation
75
+ const text = (value: string) => html`<span>${value}</span>`;
76
+
77
+ // Multiple slots
78
+ const multi = (a: string, b: string) => html`<p>${a} and ${b}</p>`;
79
+
80
+ // Adjacent slots
81
+ const adjacent = (a: string, b: string, c: string) => html`<div>${a}${b}${c}</div>`;
82
+ ```
83
+
84
+ ### Attribute Slots
85
+
86
+ ```typescript
87
+ // Class
88
+ const dynamic = (cls: string) => html`<div class="${cls}"></div>`;
89
+
90
+ // Mixed static and dynamic
91
+ const mixed = (dynamic: string) => html`<div class="base ${dynamic}"></div>`;
92
+
93
+ // Multiple attributes
94
+ const attrs = (id: string, cls: string, style: string) =>
95
+ html`<div id="${id}" class="${cls}" style="${style}"></div>`;
96
+
97
+ // Data attributes
98
+ const data = (value: string) => html`<div data-value="${value}"></div>`;
99
+ ```
100
+
101
+ ### Spread Attributes
102
+
103
+ ```typescript
104
+ // Spread object as attributes
105
+ const spread = (attrs: Record<string, unknown>) => html`<div ${attrs}></div>`;
106
+
107
+ // With static attributes
108
+ const spreadMixed = (attrs: Record<string, unknown>) =>
109
+ html`<div class="base" ${attrs}></div>`;
110
+ ```
111
+
112
+ ### Nested Templates
113
+
114
+ ```typescript
115
+ // Map to nested templates
116
+ const list = (items: string[]) =>
117
+ html`<ul>${items.map(item => html`<li>${item}</li>`)}</ul>`;
118
+
119
+ // Deeply nested
120
+ const nested = (sections: { title: string; items: string[] }[]) =>
121
+ html`<div>
122
+ ${sections.map(section => html`
123
+ <section>
124
+ <h2>${section.title}</h2>
125
+ <ul>${section.items.map(item => html`<li>${item}</li>`)}</ul>
126
+ </section>
127
+ `)}
128
+ </div>`;
129
+ ```
130
+
131
+ ### Conditional Rendering
132
+
133
+ ```typescript
134
+ // Conditional class
135
+ const toggle = (active: boolean) =>
136
+ html`<div class="${active ? 'active' : 'inactive'}"></div>`;
137
+
138
+ // Conditional content
139
+ const conditional = (show: boolean, content: string) =>
140
+ html`<div>${show ? content : ''}</div>`;
141
+
142
+ // Conditional template
143
+ const conditionalTemplate = (items: string[] | null) =>
144
+ html`<div>
145
+ ${items ? html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>` : html`<p>No items</p>`}
146
+ </div>`;
147
+ ```
148
+
149
+ ## Reactive Bindings
150
+
151
+ ### Effect Slots
152
+
153
+ Function expressions automatically become reactive effect slots:
154
+
155
+ ```typescript
156
+ import { html } from '@esportsplus/template';
157
+
158
+ // Arrow function = reactive
159
+ const counter = (state: { count: number }) =>
160
+ html`<div>${() => state.count}</div>`;
161
+
162
+ // Reactive class
163
+ const toggle = (state: { active: boolean }) =>
164
+ html`<div class="${() => state.active ? 'on' : 'off'}"></div>`;
165
+
166
+ // Reactive style
167
+ const progress = (state: { width: number }) =>
168
+ html`<div style="${() => `width: ${state.width}px`}"></div>`;
169
+ ```
170
+
171
+ ### Array Slots
172
+
173
+ For reactive arrays, use `html.reactive`:
174
+
175
+ ```typescript
176
+ import { html } from '@esportsplus/template';
177
+
178
+ const todoList = (todos: string[]) =>
179
+ html`<ul>${html.reactive(todos, todo => html`<li>${todo}</li>`)}</ul>`;
180
+ ```
181
+
182
+ ## Events
183
+
184
+ ### Standard DOM Events
185
+
186
+ Events use delegation by default for efficiency:
187
+
188
+ ```typescript
189
+ // Click
190
+ const button = (handler: () => void) =>
191
+ html`<button onclick="${handler}">Click</button>`;
192
+
193
+ // Keyboard
194
+ const input = (handler: (e: KeyboardEvent) => void) =>
195
+ html`<input onkeydown="${handler}">`;
196
+
197
+ // Input
198
+ const textbox = (handler: (e: Event) => void) =>
199
+ html`<input oninput="${handler}">`;
200
+
201
+ // Multiple events
202
+ const interactive = (click: () => void, hover: () => void) =>
203
+ html`<div onclick="${click}" onmouseenter="${hover}">Interact</div>`;
204
+ ```
205
+
206
+ ### Lifecycle Events
207
+
208
+ Custom lifecycle events for DOM attachment:
209
+
210
+ ```typescript
211
+ // Called when element is added to DOM
212
+ const connect = (handler: (el: HTMLElement) => void) =>
213
+ html`<div onconnect="${handler}">Connected</div>`;
214
+
215
+ // Called when element is removed from DOM
216
+ const disconnect = (handler: (el: HTMLElement) => void) =>
217
+ html`<div ondisconnect="${handler}">Will disconnect</div>`;
218
+
219
+ // Called after template renders
220
+ const render = (handler: (el: HTMLElement) => void) =>
221
+ html`<div onrender="${handler}">Rendered</div>`;
222
+
223
+ // Called on element resize
224
+ const resize = (handler: (el: HTMLElement) => void) =>
225
+ html`<div onresize="${handler}">Resizable</div>`;
226
+
227
+ // Called on animation frame (with dispose function)
228
+ const tick = (handler: (dispose: () => void, el: HTMLElement) => void) =>
229
+ html`<div ontick="${handler}">Animating</div>`;
230
+ ```
231
+
232
+ ### Direct Attach Events
233
+
234
+ Some events don't bubble and are attached directly:
235
+
236
+ - `onfocus`, `onblur`, `onfocusin`, `onfocusout`
237
+ - `onload`, `onerror`
238
+ - `onplay`, `onpause`, `onended`, `ontimeupdate`
239
+ - `onscroll`
240
+
241
+ ```typescript
242
+ const input = (focus: () => void, blur: () => void) =>
243
+ html`<input onfocus="${focus}" onblur="${blur}">`;
244
+ ```
245
+
246
+ ## SVG Support
247
+
248
+ Use `svg` for SVG templates:
249
+
250
+ ```typescript
251
+ import { svg } from '@esportsplus/template';
252
+
253
+ const circle = (fill: string) =>
254
+ svg`<svg width="100" height="100">
255
+ <circle cx="50" cy="50" r="40" fill="${fill}"/>
256
+ </svg>`;
257
+ ```
258
+
259
+ ## API Reference
260
+
261
+ ### Exports
262
+
263
+ | Export | Description |
264
+ |--------|-------------|
265
+ | `html` | Template literal tag for HTML |
266
+ | `svg` | Template literal tag for SVG |
267
+ | `render` | Mount renderable to DOM element |
268
+ | `attributes` | Attribute manipulation utilities |
269
+ | `event` | Event system |
270
+ | `slot` | Slot rendering |
271
+ | `ArraySlot` | Reactive array rendering |
272
+ | `EffectSlot` | Reactive effect rendering |
273
+
274
+ ### Types
275
+
276
+ ```typescript
277
+ type Renderable = DocumentFragment | Node | string | number | null | undefined;
278
+ type Element = HTMLElement & { [STORE]?: Record<string, unknown> };
279
+ type Attributes = Record<string, unknown>;
280
+ ```
281
+
282
+ ### render(parent, renderable)
283
+
284
+ Mounts a renderable to a parent element.
285
+
286
+ ```typescript
287
+ import { html, render } from '@esportsplus/template';
288
+
289
+ const app = html`<div>App</div>`;
290
+ render(document.getElementById('root'), app);
291
+ ```
292
+
293
+ ## Complete Example
294
+
295
+ ```typescript
296
+ import { html, render } from '@esportsplus/template';
297
+
298
+ type Todo = { id: number; text: string; done: boolean };
299
+
300
+ const TodoItem = (todo: Todo, onToggle: () => void, onDelete: () => void) => html`
301
+ <li class="${() => todo.done ? 'completed' : ''}">
302
+ <input type="checkbox"
303
+ checked="${() => todo.done}"
304
+ onchange="${onToggle}">
305
+ <span>${todo.text}</span>
306
+ <button onclick="${onDelete}">Delete</button>
307
+ </li>
308
+ `;
309
+
310
+ const TodoApp = (state: {
311
+ todos: Todo[];
312
+ input: string;
313
+ addTodo: () => void;
314
+ toggleTodo: (id: number) => void;
315
+ deleteTodo: (id: number) => void;
316
+ setInput: (value: string) => void;
317
+ }) => html`
318
+ <div class="todo-app">
319
+ <h1>Todos</h1>
320
+ <form onsubmit="${(e: Event) => { e.preventDefault(); state.addTodo(); }}">
321
+ <input type="text"
322
+ value="${() => state.input}"
323
+ oninput="${(e: Event) => state.setInput((e.target as HTMLInputElement).value)}"
324
+ placeholder="Add todo...">
325
+ <button type="submit">Add</button>
326
+ </form>
327
+ <ul>
328
+ ${html.reactive(state.todos, todo =>
329
+ TodoItem(
330
+ todo,
331
+ () => state.toggleTodo(todo.id),
332
+ () => state.deleteTodo(todo.id)
333
+ )
334
+ )}
335
+ </ul>
336
+ </div>
337
+ `;
338
+
339
+ // Mount
340
+ render(document.body, TodoApp(/* state */));
341
+ ```
342
+
343
+ ## How Transformation Works
344
+
345
+ The transformer converts template literals into optimized code at build time:
346
+
347
+ **Input:**
348
+ ```typescript
349
+ const greeting = (name: string) => html`<div class="hello">${name}</div>`;
350
+ ```
351
+
352
+ **Output (simplified):**
353
+ ```typescript
354
+ const $template = (() => {
355
+ let cached;
356
+ return () => {
357
+ if (!cached) cached = template('<div class="hello"><!--$--></div>');
358
+ return clone(cached);
359
+ };
360
+ })();
361
+
362
+ const greeting = (name: string) => {
363
+ const fragment = $template();
364
+ const $0 = firstChild(fragment);
365
+ slot($0.firstChild, name);
366
+ return fragment;
367
+ };
368
+ ```
369
+
370
+ Key optimizations:
371
+ - Template HTML is parsed once and cached
372
+ - Slot positions are computed at compile time
373
+ - Type analysis determines optimal slot binding
374
+ - No runtime template parsing
375
+
376
+ ## Dependencies
377
+
378
+ - `@esportsplus/reactivity` - Reactive primitives
379
+ - `@esportsplus/queue` - Object pooling
380
+ - `@esportsplus/typescript` - TypeScript compiler utilities
381
+ - `@esportsplus/utilities` - Utility functions
382
+
383
+ ## License
384
+
385
+ MIT
@@ -0,0 +1,5 @@
1
+ import { Attributes, Element } from './types.js';
2
+ declare const setList: (element: Element, name: "class" | "style", value: unknown, attributes?: Record<string, string>) => void;
3
+ declare const setProperty: (element: Element, name: string, value: unknown) => void;
4
+ declare const setProperties: (element: Element, properties: Attributes | Attributes[] | false | null | undefined, attributes?: Record<string, string>) => void;
5
+ export { setList, setProperty, setProperties };
@@ -0,0 +1,212 @@
1
+ import { effect } from '@esportsplus/reactivity';
2
+ import { isArray, isObject } from '@esportsplus/utilities';
3
+ import { ATTRIBUTE_DELIMITERS, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE } from './constants.js';
4
+ import { raf } from './utilities.js';
5
+ import { runtime } from './event/index.js';
6
+ import q from '@esportsplus/queue';
7
+ let queue = q(64), scheduled = false;
8
+ function apply(element, name, value) {
9
+ if (value == null || value === false || value === '') {
10
+ element.removeAttribute(name);
11
+ }
12
+ else if (name === 'class') {
13
+ element.className = value;
14
+ }
15
+ else if (name === 'style' || (name[0] === 'd' && name.startsWith('data-')) || element['ownerSVGElement']) {
16
+ element.setAttribute(name, value);
17
+ }
18
+ else {
19
+ element[name] = value;
20
+ }
21
+ }
22
+ function context(element) {
23
+ return (element[STORE] ??= { element });
24
+ }
25
+ function list(ctx, element, id, name, state, value) {
26
+ if (value == null || value === false || value === '') {
27
+ value = '';
28
+ }
29
+ let changed = false, delimiter = ATTRIBUTE_DELIMITERS[name], store = (ctx ??= context(element)).store ??= {}, dynamic = store[name];
30
+ if (!dynamic) {
31
+ store[name + '.static'] = (element.getAttribute(name) || '').trim();
32
+ store[name] = dynamic = new Set();
33
+ }
34
+ if (id === null) {
35
+ if (value && typeof value === 'string') {
36
+ changed = true;
37
+ store[name + '.static'] += (store[name + '.static'] ? delimiter : '') + value;
38
+ }
39
+ }
40
+ else if (store[id + '.raw'] !== value) {
41
+ let hot = {};
42
+ if (value && typeof value === 'string') {
43
+ let part, parts = value.split(delimiter);
44
+ while (part = parts.pop()) {
45
+ part = part.trim();
46
+ if (part === '') {
47
+ continue;
48
+ }
49
+ if (!dynamic.has(part)) {
50
+ changed = true;
51
+ dynamic.add(part);
52
+ }
53
+ hot[part] = true;
54
+ }
55
+ }
56
+ let cold = store[id];
57
+ if (cold !== undefined) {
58
+ for (let part in cold) {
59
+ if (hot[part] === true) {
60
+ continue;
61
+ }
62
+ changed = true;
63
+ dynamic.delete(part);
64
+ }
65
+ }
66
+ store[id + '.raw'] = value;
67
+ store[id] = hot;
68
+ }
69
+ if (!changed) {
70
+ return;
71
+ }
72
+ value = store[name + '.static'];
73
+ for (let key of dynamic) {
74
+ value += (value ? delimiter : '') + key;
75
+ }
76
+ if (state === STATE_HYDRATING) {
77
+ apply(element, name, value);
78
+ }
79
+ else {
80
+ schedule(ctx, element, name, state, value);
81
+ }
82
+ }
83
+ function property(ctx, element, id, name, state, value) {
84
+ if (value == null || value === false || value === '') {
85
+ value = '';
86
+ }
87
+ if (id !== null) {
88
+ ctx ??= context(element);
89
+ if (ctx[name] === value) {
90
+ return;
91
+ }
92
+ ctx[name] = value;
93
+ }
94
+ if (state === STATE_HYDRATING) {
95
+ apply(element, name, value);
96
+ }
97
+ else {
98
+ schedule(ctx, element, name, state, value);
99
+ }
100
+ }
101
+ function reactive(element, name, state, value) {
102
+ let ctx = context(element), fn = (name === 'class' || name === 'style') ? list : property;
103
+ ctx.effect ??= 0;
104
+ let id = ctx.effect++;
105
+ effect(() => {
106
+ let v = value(element);
107
+ if (v == null || typeof v !== 'object') {
108
+ fn(ctx, element, id, name, state, v);
109
+ }
110
+ else if (isArray(v)) {
111
+ let last = v.length - 1;
112
+ for (let i = 0, n = v.length; i < n; i++) {
113
+ fn(ctx, element, id, name, state === STATE_HYDRATING
114
+ ? state
115
+ : i !== last ? STATE_WAITING : state, v[i]);
116
+ }
117
+ }
118
+ });
119
+ state = STATE_NONE;
120
+ }
121
+ function schedule(ctx, element, name, state, value) {
122
+ ctx ??= context(element);
123
+ (ctx.updates ??= {})[name] = value;
124
+ if (state === STATE_NONE && !ctx.updating) {
125
+ ctx.updating = true;
126
+ queue.add(ctx);
127
+ }
128
+ if (scheduled) {
129
+ return;
130
+ }
131
+ scheduled = true;
132
+ raf(task);
133
+ }
134
+ function task() {
135
+ let context, n = queue.length;
136
+ while ((context = queue.next()) && n--) {
137
+ let { element, updates } = context;
138
+ for (let name in updates) {
139
+ apply(element, name, updates[name]);
140
+ }
141
+ context.updates = {};
142
+ context.updating = false;
143
+ }
144
+ if (queue.length) {
145
+ raf(task);
146
+ }
147
+ else {
148
+ scheduled = false;
149
+ }
150
+ }
151
+ const setList = (element, name, value, attributes = {}) => {
152
+ let ctx = context(element), store = ctx.store ??= {};
153
+ store[name] ??= new Set();
154
+ store[name + '.static'] ??= '';
155
+ store[name + '.static'] += `${attributes[name] && store[name + '.static'] ? ATTRIBUTE_DELIMITERS[name] : ''}${attributes[name]}`;
156
+ if (typeof value === 'function') {
157
+ reactive(element, name, STATE_HYDRATING, value);
158
+ }
159
+ else if (typeof value !== 'object') {
160
+ list(ctx, element, null, name, STATE_HYDRATING, value);
161
+ }
162
+ else if (isArray(value)) {
163
+ for (let i = 0, n = value.length; i < n; i++) {
164
+ let v = value[i];
165
+ if (v == null || v === false || v === '') {
166
+ continue;
167
+ }
168
+ list(ctx, element, null, name, STATE_HYDRATING, v);
169
+ }
170
+ }
171
+ };
172
+ const setProperty = (element, name, value) => {
173
+ if (typeof value === 'function') {
174
+ reactive(element, name, STATE_HYDRATING, value);
175
+ }
176
+ else {
177
+ property(null, element, null, name, STATE_HYDRATING, value);
178
+ }
179
+ };
180
+ const setProperties = function (element, properties, attributes = {}) {
181
+ if (!properties) {
182
+ return;
183
+ }
184
+ else if (isObject(properties)) {
185
+ for (let name in properties) {
186
+ let value = properties[name];
187
+ if (value == null || value === false || value === '') {
188
+ continue;
189
+ }
190
+ if (name === 'class' || name === 'style') {
191
+ setList(element, name, value, attributes);
192
+ }
193
+ else if (typeof value === 'function') {
194
+ if (name[0] === 'o' && name[1] === 'n') {
195
+ runtime(element, name, value);
196
+ }
197
+ else {
198
+ reactive(element, name, STATE_HYDRATING, value);
199
+ }
200
+ }
201
+ else {
202
+ property(null, element, null, name, STATE_HYDRATING, value);
203
+ }
204
+ }
205
+ }
206
+ else if (isArray(properties)) {
207
+ for (let i = 0, n = properties.length; i < n; i++) {
208
+ setProperties(element, properties[i], attributes);
209
+ }
210
+ }
211
+ };
212
+ export { setList, setProperty, setProperties };
@@ -0,0 +1,21 @@
1
+ import { type ReplacementIntent } from '@esportsplus/typescript/compiler';
2
+ import type { TemplateInfo } from './ts-parser.js';
3
+ import { ts } from '@esportsplus/typescript';
4
+ type CodegenContext = {
5
+ checker?: ts.TypeChecker;
6
+ sourceFile: ts.SourceFile;
7
+ templates: Map<string, string>;
8
+ };
9
+ type CodegenResult = {
10
+ prepend: string[];
11
+ replacements: ReplacementIntent[];
12
+ templates: Map<string, string>;
13
+ };
14
+ declare let printer: ts.Printer;
15
+ declare const generateCode: (templates: TemplateInfo[], sourceFile: ts.SourceFile, checker?: ts.TypeChecker, callRanges?: {
16
+ end: number;
17
+ start: number;
18
+ }[]) => CodegenResult;
19
+ declare const rewriteExpression: (ctx: CodegenContext, expr: ts.Expression) => string;
20
+ export { generateCode, printer, rewriteExpression };
21
+ export type { CodegenResult };