@esportsplus/template 0.28.3 → 0.29.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.
Files changed (110) hide show
  1. package/README.md +431 -0
  2. package/build/attributes.d.ts +7 -1
  3. package/build/attributes.js +86 -33
  4. package/build/constants.d.ts +3 -11
  5. package/build/constants.js +4 -32
  6. package/build/event/constants.d.ts +3 -0
  7. package/build/event/constants.js +13 -0
  8. package/build/event/index.d.ts +9 -1
  9. package/build/event/index.js +29 -35
  10. package/build/event/ontick.js +6 -9
  11. package/build/html.d.ts +9 -0
  12. package/build/html.js +7 -0
  13. package/build/index.d.ts +8 -2
  14. package/build/index.js +8 -1
  15. package/build/render.d.ts +2 -2
  16. package/build/render.js +2 -3
  17. package/build/runtime.d.ts +1 -0
  18. package/build/runtime.js +5 -0
  19. package/build/slot/array.d.ts +3 -3
  20. package/build/slot/array.js +11 -14
  21. package/build/slot/cleanup.d.ts +1 -1
  22. package/build/slot/cleanup.js +1 -2
  23. package/build/slot/effect.js +5 -7
  24. package/build/slot/index.js +1 -7
  25. package/build/slot/render.js +6 -8
  26. package/build/svg.d.ts +1 -1
  27. package/build/svg.js +1 -1
  28. package/build/transformer/codegen.d.ts +18 -0
  29. package/build/transformer/codegen.js +316 -0
  30. package/build/transformer/index.d.ts +12 -0
  31. package/build/transformer/index.js +62 -0
  32. package/build/transformer/parser.d.ts +18 -0
  33. package/build/transformer/parser.js +166 -0
  34. package/build/transformer/plugins/esbuild.d.ts +5 -0
  35. package/build/transformer/plugins/esbuild.js +35 -0
  36. package/build/transformer/plugins/tsc.d.ts +3 -0
  37. package/build/transformer/plugins/tsc.js +4 -0
  38. package/build/transformer/plugins/vite.d.ts +5 -0
  39. package/build/transformer/plugins/vite.js +37 -0
  40. package/build/transformer/ts-parser.d.ts +21 -0
  41. package/build/transformer/ts-parser.js +72 -0
  42. package/build/transformer/type-analyzer.d.ts +7 -0
  43. package/build/transformer/type-analyzer.js +230 -0
  44. package/build/types.d.ts +2 -3
  45. package/build/utilities.d.ts +7 -0
  46. package/build/utilities.js +31 -0
  47. package/package.json +33 -4
  48. package/src/attributes.ts +115 -51
  49. package/src/constants.ts +6 -53
  50. package/src/event/constants.ts +16 -0
  51. package/src/event/index.ts +36 -42
  52. package/src/event/onconnect.ts +1 -1
  53. package/src/event/onresize.ts +1 -1
  54. package/src/event/ontick.ts +7 -11
  55. package/src/html.ts +18 -0
  56. package/src/index.ts +8 -2
  57. package/src/render.ts +6 -7
  58. package/src/runtime.ts +8 -0
  59. package/src/slot/array.ts +18 -24
  60. package/src/slot/cleanup.ts +3 -4
  61. package/src/slot/effect.ts +6 -8
  62. package/src/slot/index.ts +2 -8
  63. package/src/slot/render.ts +7 -9
  64. package/src/svg.ts +1 -1
  65. package/src/transformer/codegen.ts +518 -0
  66. package/src/transformer/index.ts +98 -0
  67. package/src/transformer/parser.ts +239 -0
  68. package/src/transformer/plugins/esbuild.ts +46 -0
  69. package/src/transformer/plugins/tsc.ts +7 -0
  70. package/src/transformer/plugins/vite.ts +49 -0
  71. package/src/transformer/ts-parser.ts +123 -0
  72. package/src/transformer/type-analyzer.ts +334 -0
  73. package/src/types.ts +3 -4
  74. package/src/utilities.ts +52 -0
  75. package/storage/rewrite-analysis-2026-01-04.md +439 -0
  76. package/test/constants.ts +69 -0
  77. package/test/effects.ts +237 -0
  78. package/test/events.ts +318 -0
  79. package/test/imported-values.ts +253 -0
  80. package/test/nested.ts +298 -0
  81. package/test/slots.ts +259 -0
  82. package/test/spread.ts +290 -0
  83. package/test/static.ts +118 -0
  84. package/test/templates.ts +473 -0
  85. package/test/tsconfig.json +17 -0
  86. package/test/vite.config.ts +50 -0
  87. package/build/html/index.d.ts +0 -9
  88. package/build/html/index.js +0 -29
  89. package/build/html/parser.d.ts +0 -5
  90. package/build/html/parser.js +0 -165
  91. package/build/utilities/element.d.ts +0 -11
  92. package/build/utilities/element.js +0 -9
  93. package/build/utilities/fragment.d.ts +0 -3
  94. package/build/utilities/fragment.js +0 -10
  95. package/build/utilities/marker.d.ts +0 -2
  96. package/build/utilities/marker.js +0 -4
  97. package/build/utilities/node.d.ts +0 -9
  98. package/build/utilities/node.js +0 -10
  99. package/build/utilities/raf.d.ts +0 -2
  100. package/build/utilities/raf.js +0 -1
  101. package/build/utilities/text.d.ts +0 -2
  102. package/build/utilities/text.js +0 -9
  103. package/src/html/index.ts +0 -48
  104. package/src/html/parser.ts +0 -235
  105. package/src/utilities/element.ts +0 -28
  106. package/src/utilities/fragment.ts +0 -19
  107. package/src/utilities/marker.ts +0 -6
  108. package/src/utilities/node.ts +0 -29
  109. package/src/utilities/raf.ts +0 -1
  110. package/src/utilities/text.ts +0 -15
package/test/slots.ts ADDED
@@ -0,0 +1,259 @@
1
+ // Slot Templates - Single and multiple slot bindings
2
+ // Tests text slots, attribute slots, and mixed combinations
3
+
4
+
5
+ import { html } from '../src';
6
+
7
+
8
+ // =============================================================================
9
+ // SINGLE TEXT SLOTS
10
+ // =============================================================================
11
+
12
+ // Basic text slot
13
+ export const textSimple = (text: string) => html`<div>${text}</div>`;
14
+
15
+ // Text in span
16
+ export const textInSpan = (text: string) => html`<span>${text}</span>`;
17
+
18
+ // Text in paragraph
19
+ export const textInParagraph = (text: string) => html`<p>${text}</p>`;
20
+
21
+ // Text in heading
22
+ export const textInHeading = (text: string) => html`<h1>${text}</h1>`;
23
+
24
+ // Text in button
25
+ export const textInButton = (text: string) => html`<button>${text}</button>`;
26
+
27
+ // Text in nested element
28
+ export const textNested = (text: string) =>
29
+ html`<div><span><strong>${text}</strong></span></div>`;
30
+
31
+
32
+ // =============================================================================
33
+ // SINGLE ATTRIBUTE SLOTS
34
+ // =============================================================================
35
+
36
+ // Class attribute
37
+ export const attrClass = (cls: string) => html`<div class="${cls}"></div>`;
38
+
39
+ // ID attribute
40
+ export const attrId = (id: string) => html`<div id="${id}"></div>`;
41
+
42
+ // Style attribute
43
+ export const attrStyle = (style: string) => html`<div style="${style}"></div>`;
44
+
45
+ // Data attribute
46
+ export const attrData = (value: string) => html`<div data-value="${value}"></div>`;
47
+
48
+ // Custom data attribute
49
+ export const attrDataCustom = (id: string) => html`<div data-item-id="${id}"></div>`;
50
+
51
+ // Href attribute
52
+ export const attrHref = (url: string) => html`<a href="${url}">Link</a>`;
53
+
54
+ // Src attribute
55
+ export const attrSrc = (src: string) => html`<img src="${src}" alt="image">`;
56
+
57
+ // Value attribute
58
+ export const attrValue = (val: string) => html`<input type="text" value="${val}">`;
59
+
60
+ // Placeholder attribute
61
+ export const attrPlaceholder = (text: string) =>
62
+ html`<input type="text" placeholder="${text}">`;
63
+
64
+
65
+ // =============================================================================
66
+ // MIXED STATIC AND DYNAMIC ATTRIBUTES
67
+ // =============================================================================
68
+
69
+ // Class with static prefix
70
+ export const attrClassWithStatic = (dynamic: string) =>
71
+ html`<div class="base-class ${dynamic}"></div>`;
72
+
73
+ // Style with static prefix
74
+ export const attrStyleWithStatic = (dynamic: string) =>
75
+ html`<div style="display: flex; ${dynamic}"></div>`;
76
+
77
+ // Multiple statics, one dynamic
78
+ export const attrMixedStatics = (cls: string) =>
79
+ html`<div id="fixed-id" class="${cls}" data-static="value"></div>`;
80
+
81
+
82
+ // =============================================================================
83
+ // MULTIPLE TEXT SLOTS
84
+ // =============================================================================
85
+
86
+ // Two text slots
87
+ export const textTwo = (a: string, b: string) =>
88
+ html`<div><span>${a}</span><span>${b}</span></div>`;
89
+
90
+ // Three text slots
91
+ export const textThree = (a: string, b: string, c: string) =>
92
+ html`<div><span>${a}</span><span>${b}</span><span>${c}</span></div>`;
93
+
94
+ // Five text slots
95
+ export const textFive = (a: string, b: string, c: string, d: string, e: string) =>
96
+ html`<div>
97
+ <span>${a}</span>
98
+ <span>${b}</span>
99
+ <span>${c}</span>
100
+ <span>${d}</span>
101
+ <span>${e}</span>
102
+ </div>`;
103
+
104
+ // Adjacent text slots (no element separators)
105
+ export const textAdjacent = (a: string, b: string, c: string) =>
106
+ html`<div>${a}${b}${c}</div>`;
107
+
108
+
109
+ // =============================================================================
110
+ // MULTIPLE ATTRIBUTE SLOTS - SAME ELEMENT
111
+ // =============================================================================
112
+
113
+ // Two attributes
114
+ export const attrTwo = (id: string, cls: string) =>
115
+ html`<div id="${id}" class="${cls}"></div>`;
116
+
117
+ // Three attributes
118
+ export const attrThree = (id: string, cls: string, style: string) =>
119
+ html`<div id="${id}" class="${cls}" style="${style}"></div>`;
120
+
121
+ // All common attributes
122
+ export const attrFull = (
123
+ id: string,
124
+ cls: string,
125
+ style: string,
126
+ dataId: string,
127
+ title: string
128
+ ) => html`<div
129
+ id="${id}"
130
+ class="${cls}"
131
+ style="${style}"
132
+ data-id="${dataId}"
133
+ title="${title}"
134
+ ></div>`;
135
+
136
+
137
+ // =============================================================================
138
+ // MULTIPLE ATTRIBUTE SLOTS - DIFFERENT ELEMENTS
139
+ // =============================================================================
140
+
141
+ // Attributes on sibling elements
142
+ export const attrSiblings = (cls1: string, cls2: string) =>
143
+ html`<div><span class="${cls1}">First</span><span class="${cls2}">Second</span></div>`;
144
+
145
+ // Attributes on nested elements
146
+ export const attrNested = (outerCls: string, innerCls: string) =>
147
+ html`<div class="${outerCls}"><span class="${innerCls}">Content</span></div>`;
148
+
149
+
150
+ // =============================================================================
151
+ // MIXED TEXT AND ATTRIBUTE SLOTS
152
+ // =============================================================================
153
+
154
+ // Attribute and text on same element
155
+ export const mixedSame = (cls: string, text: string) =>
156
+ html`<div class="${cls}">${text}</div>`;
157
+
158
+ // Multiple mixed on same element
159
+ export const mixedSameFull = (id: string, cls: string, style: string, text: string) =>
160
+ html`<div id="${id}" class="${cls}" style="${style}">${text}</div>`;
161
+
162
+ // Mixed across elements
163
+ export const mixedAcross = (cls: string, text: string) =>
164
+ html`<article class="${cls}"><p>${text}</p></article>`;
165
+
166
+ // Complex mixed pattern
167
+ export const mixedComplex = (
168
+ wrapperCls: string,
169
+ headerText: string,
170
+ bodyCls: string,
171
+ bodyText: string,
172
+ footerText: string
173
+ ) => html`
174
+ <article class="${wrapperCls}">
175
+ <header>${headerText}</header>
176
+ <main class="${bodyCls}">${bodyText}</main>
177
+ <footer>${footerText}</footer>
178
+ </article>
179
+ `;
180
+
181
+
182
+ // =============================================================================
183
+ // FORM INPUT SLOTS
184
+ // =============================================================================
185
+
186
+ // Text input
187
+ export const inputText = (name: string, value: string, placeholder: string) =>
188
+ html`<input type="text" name="${name}" value="${value}" placeholder="${placeholder}">`;
189
+
190
+ // Email input
191
+ export const inputEmail = (value: string) =>
192
+ html`<input type="email" value="${value}" placeholder="email@example.com">`;
193
+
194
+ // Checkbox input
195
+ export const inputCheckbox = (name: string, value: string, label: string) =>
196
+ html`<label><input type="checkbox" name="${name}" value="${value}"> ${label}</label>`;
197
+
198
+ // Full form
199
+ export const formComplete = (
200
+ name: string,
201
+ email: string,
202
+ message: string,
203
+ submitText: string
204
+ ) => html`
205
+ <form>
206
+ <input type="text" name="name" value="${name}" placeholder="Name">
207
+ <input type="email" name="email" value="${email}" placeholder="Email">
208
+ <textarea name="message">${message}</textarea>
209
+ <button type="submit">${submitText}</button>
210
+ </form>
211
+ `;
212
+
213
+
214
+ // =============================================================================
215
+ // DEEP PATH SLOTS (path ancestor caching test)
216
+ // =============================================================================
217
+
218
+ // Slot at depth 4
219
+ export const deepPath4 = (text: string) =>
220
+ html`<div><div><div><div>${text}</div></div></div></div>`;
221
+
222
+ // Slot at depth 6
223
+ export const deepPath6 = (text: string) =>
224
+ html`<div><div><div><div><div><div>${text}</div></div></div></div></div></div>`;
225
+
226
+ // Multiple slots at same depth (should share ancestors)
227
+ export const deepPathShared = (a: string, b: string) =>
228
+ html`<div><div><div><span>${a}</span><span>${b}</span></div></div></div>`;
229
+
230
+ // Slots at different depths
231
+ export const deepPathMixed = (shallow: string, deep: string) =>
232
+ html`<div>
233
+ <span>${shallow}</span>
234
+ <div><div><div><span>${deep}</span></div></div></div>
235
+ </div>`;
236
+
237
+
238
+ // =============================================================================
239
+ // WIDE SIBLING SLOTS (nextSibling traversal test)
240
+ // =============================================================================
241
+
242
+ // 10 sibling text slots
243
+ export const wideSiblings10 = (v: string[]) =>
244
+ html`<div>
245
+ <span>${v[0]}</span><span>${v[1]}</span><span>${v[2]}</span>
246
+ <span>${v[3]}</span><span>${v[4]}</span><span>${v[5]}</span>
247
+ <span>${v[6]}</span><span>${v[7]}</span><span>${v[8]}</span>
248
+ <span>${v[9]}</span>
249
+ </div>`;
250
+
251
+ // Alternating slots and static content
252
+ export const wideAlternating = (a: string, b: string, c: string) =>
253
+ html`<div>
254
+ <span>${a}</span>
255
+ <span>static</span>
256
+ <span>${b}</span>
257
+ <span>static</span>
258
+ <span>${c}</span>
259
+ </div>`;
package/test/spread.ts ADDED
@@ -0,0 +1,290 @@
1
+ // Spread Templates - Object spread attribute unpacking
2
+ // Tests compile-time unpacking of spread attributes
3
+
4
+
5
+ import { html } from '../src';
6
+
7
+
8
+ // =============================================================================
9
+ // SIMPLE SPREAD - OBJECT LITERAL (compile-time unpacking)
10
+ // =============================================================================
11
+
12
+ // Single property spread
13
+ export const spreadSingle = () =>
14
+ html`<div ${{ class: 'spread-class' }}></div>`;
15
+
16
+ // Two property spread
17
+ export const spreadTwo = () =>
18
+ html`<div ${{ id: 'my-id', class: 'my-class' }}></div>`;
19
+
20
+ // Multiple property spread
21
+ export const spreadMultiple = () =>
22
+ html`<div ${{
23
+ id: 'element-id',
24
+ class: 'element-class',
25
+ 'data-id': '123',
26
+ title: 'Element title'
27
+ }}></div>`;
28
+
29
+
30
+ // =============================================================================
31
+ // SPREAD WITH CLASS AND STYLE
32
+ // =============================================================================
33
+
34
+ // Spread with class
35
+ export const spreadClass = () =>
36
+ html`<div ${{ class: 'btn btn-primary' }}></div>`;
37
+
38
+ // Spread with style
39
+ export const spreadStyle = () =>
40
+ html`<div ${{ style: 'color: red; font-size: 16px;' }}></div>`;
41
+
42
+ // Spread with both class and style
43
+ export const spreadClassStyle = () =>
44
+ html`<div ${{
45
+ class: 'container mx-auto',
46
+ style: 'padding: 20px; margin: 10px;'
47
+ }}></div>`;
48
+
49
+
50
+ // =============================================================================
51
+ // SPREAD WITH EVENTS (compile-time key detection)
52
+ // =============================================================================
53
+
54
+ // Spread with click event
55
+ export const spreadClickEvent = (onClick: () => void) =>
56
+ html`<button ${{ onclick: onClick }}>Click</button>`;
57
+
58
+ // Spread with multiple events
59
+ export const spreadMultipleEvents = (
60
+ onClick: () => void,
61
+ onMouseEnter: () => void
62
+ ) => html`<div ${{
63
+ onclick: onClick,
64
+ onmouseenter: onMouseEnter
65
+ }}>Interactive</div>`;
66
+
67
+ // Spread with lifecycle event
68
+ export const spreadLifecycleEvent = (onConnect: () => void) =>
69
+ html`<div ${{ onconnect: onConnect }}>Lifecycle</div>`;
70
+
71
+
72
+ // =============================================================================
73
+ // SPREAD WITH DATA ATTRIBUTES
74
+ // =============================================================================
75
+
76
+ // Single data attribute
77
+ export const spreadDataSingle = () =>
78
+ html`<div ${{ 'data-id': '123' }}></div>`;
79
+
80
+ // Multiple data attributes
81
+ export const spreadDataMultiple = () =>
82
+ html`<div ${{
83
+ 'data-id': '123',
84
+ 'data-type': 'user',
85
+ 'data-action': 'view'
86
+ }}></div>`;
87
+
88
+
89
+ // =============================================================================
90
+ // SPREAD - VARIABLE REFERENCE (TypeChecker unpacking)
91
+ // =============================================================================
92
+
93
+ // Typed object variable
94
+ type ButtonAttrs = {
95
+ class: string;
96
+ type: string;
97
+ disabled?: boolean;
98
+ };
99
+
100
+ export const spreadTypedVar = (attrs: ButtonAttrs) =>
101
+ html`<button ${attrs}>Button</button>`;
102
+
103
+ // Record type variable
104
+ export const spreadRecordVar = (attrs: Record<string, string>) =>
105
+ html`<div ${attrs}></div>`;
106
+
107
+ // Partial type variable
108
+ type CardAttrs = {
109
+ id?: string;
110
+ class?: string;
111
+ 'data-card-id'?: string;
112
+ };
113
+
114
+ export const spreadPartialVar = (attrs: CardAttrs) =>
115
+ html`<div ${attrs}></div>`;
116
+
117
+
118
+ // =============================================================================
119
+ // SPREAD WITH STATIC ATTRIBUTES
120
+ // =============================================================================
121
+
122
+ // Static before spread
123
+ export const spreadAfterStatic = (attrs: Record<string, unknown>) =>
124
+ html`<div class="base" ${attrs}></div>`;
125
+
126
+ // Static after spread
127
+ export const spreadBeforeStatic = (attrs: Record<string, unknown>) =>
128
+ html`<div ${attrs} class="override"></div>`;
129
+
130
+ // Spread between statics
131
+ export const spreadBetweenStatics = (attrs: Record<string, unknown>) =>
132
+ html`<div id="fixed" ${attrs} class="also-fixed"></div>`;
133
+
134
+
135
+ // =============================================================================
136
+ // SPREAD IN NESTED TEMPLATES
137
+ // =============================================================================
138
+
139
+ // Spread in list items
140
+ export const spreadInList = (items: Record<string, unknown>[]) =>
141
+ html`<ul>${items.map(attrs => html`<li ${attrs}>Item</li>`)}</ul>`;
142
+
143
+ // Spread with content
144
+ export const spreadInListWithContent = (items: { attrs: Record<string, unknown>; text: string }[]) =>
145
+ html`<ul>${items.map(item => html`<li ${item.attrs}>${item.text}</li>`)}</ul>`;
146
+
147
+ // Typed spread in list
148
+ type ListItemAttrs = {
149
+ class: string;
150
+ 'data-id': number;
151
+ };
152
+
153
+ export const spreadTypedInList = (items: { attrs: ListItemAttrs; text: string }[]) =>
154
+ html`<ul>${items.map(item => html`<li ${item.attrs}>${item.text}</li>`)}</ul>`;
155
+
156
+
157
+ // =============================================================================
158
+ // COMPLEX SPREAD PATTERNS
159
+ // =============================================================================
160
+
161
+ // Spread with computed values in object literal
162
+ export const spreadComputed = (isActive: boolean, id: number) =>
163
+ html`<div ${{
164
+ class: isActive ? 'active' : 'inactive',
165
+ 'data-id': id.toString()
166
+ }}></div>`;
167
+
168
+ // Multiple spreads on same element (edge case)
169
+ export const spreadDouble = (
170
+ baseAttrs: Record<string, unknown>,
171
+ extraAttrs: Record<string, unknown>
172
+ ) => html`<div ${baseAttrs} ${extraAttrs}></div>`;
173
+
174
+
175
+ // =============================================================================
176
+ // SPREAD WITH INLINE HANDLERS
177
+ // =============================================================================
178
+
179
+ // Spread with inline arrow function
180
+ export const spreadInlineHandler = (state: { count: number }) =>
181
+ html`<button ${{
182
+ class: 'btn',
183
+ onclick: () => state.count++
184
+ }}>Increment</button>`;
185
+
186
+ // Spread with method reference
187
+ export const spreadMethodRef = (controller: {
188
+ handleClick: () => void;
189
+ handleHover: () => void;
190
+ }) => html`<div ${{
191
+ onclick: controller.handleClick,
192
+ onmouseenter: controller.handleHover
193
+ }}>Controller element</div>`;
194
+
195
+
196
+ // =============================================================================
197
+ // SPREAD - FALLBACK (runtime spread when can't unpack)
198
+ // =============================================================================
199
+
200
+ // Dynamic spread (can't unpack at compile time)
201
+ export const spreadDynamic = (getDynamicAttrs: () => Record<string, unknown>) =>
202
+ html`<div ${getDynamicAttrs()}></div>`;
203
+
204
+ // Conditional spread
205
+ export const spreadConditional = (useAttrs: boolean, attrs: Record<string, unknown>) =>
206
+ html`<div ${useAttrs ? attrs : {}}></div>`;
207
+
208
+
209
+ // =============================================================================
210
+ // REAL-WORLD SPREAD PATTERNS
211
+ // =============================================================================
212
+
213
+ // Input component with spread
214
+ type InputProps = {
215
+ type: string;
216
+ name: string;
217
+ value: string;
218
+ placeholder?: string;
219
+ class?: string;
220
+ };
221
+
222
+ export const inputComponent = (props: InputProps) =>
223
+ html`<input ${props}>`;
224
+
225
+ // Button component with spread
226
+ type ButtonProps = {
227
+ class: string;
228
+ type: 'button' | 'submit' | 'reset';
229
+ disabled?: boolean;
230
+ onclick?: () => void;
231
+ };
232
+
233
+ export const buttonComponent = (props: ButtonProps, label: string) =>
234
+ html`<button ${props}>${label}</button>`;
235
+
236
+ // Link component with spread
237
+ type LinkProps = {
238
+ href: string;
239
+ class?: string;
240
+ target?: string;
241
+ rel?: string;
242
+ };
243
+
244
+ export const linkComponent = (props: LinkProps, children: string) =>
245
+ html`<a ${props}>${children}</a>`;
246
+
247
+ // Card component with spread
248
+ type CardProps = {
249
+ class?: string;
250
+ 'data-card-id'?: string;
251
+ onclick?: () => void;
252
+ };
253
+
254
+ export const cardComponent = (
255
+ props: CardProps,
256
+ content: { title: string; body: string }
257
+ ) => html`
258
+ <div ${props}>
259
+ <h3>${content.title}</h3>
260
+ <p>${content.body}</p>
261
+ </div>
262
+ `;
263
+
264
+
265
+ // =============================================================================
266
+ // SPREAD STRESS TESTS
267
+ // =============================================================================
268
+
269
+ // Many properties
270
+ export const spreadManyProps = () =>
271
+ html`<div ${{
272
+ id: 'stress-id',
273
+ class: 'stress-class-1 stress-class-2 stress-class-3',
274
+ style: 'color: red; background: blue; padding: 10px;',
275
+ 'data-a': '1',
276
+ 'data-b': '2',
277
+ 'data-c': '3',
278
+ 'data-d': '4',
279
+ 'data-e': '5',
280
+ title: 'Stress test element',
281
+ tabindex: '0'
282
+ }}></div>`;
283
+
284
+ // Spread in deep nesting
285
+ export const spreadDeepNesting = (attrs: Record<string, unknown>) =>
286
+ html`<div><div><div><div ${attrs}>Deep</div></div></div></div>`;
287
+
288
+ // Many spreads in list
289
+ export const spreadManyInList = (items: Record<string, unknown>[]) =>
290
+ html`<div>${items.slice(0, 20).map(attrs => html`<span ${attrs}>Item</span>`)}</div>`;
package/test/static.ts ADDED
@@ -0,0 +1,118 @@
1
+ // Static Templates - No dynamic slots
2
+ // Tests template parsing and factory hoisting for pure HTML
3
+
4
+
5
+ import { html } from '../src';
6
+
7
+
8
+ // Simple static element
9
+ export const staticSimple = () => html`<div>Hello World</div>`;
10
+
11
+ // Static with classes
12
+ export const staticClasses = () => html`<div class="container mx-auto p-4">Static content</div>`;
13
+
14
+ // Static with multiple attributes
15
+ export const staticMultiAttr = () =>
16
+ html`<div id="main" class="wrapper" data-testid="static-test">Content</div>`;
17
+
18
+ // Static nested structure
19
+ export const staticNested = () => html`
20
+ <div class="wrapper">
21
+ <header class="header">Title</header>
22
+ <main class="content">Body</main>
23
+ <footer class="footer">Footer</footer>
24
+ </div>
25
+ `;
26
+
27
+ // Static list
28
+ export const staticList = () => html`
29
+ <ul class="list">
30
+ <li>Item 1</li>
31
+ <li>Item 2</li>
32
+ <li>Item 3</li>
33
+ </ul>
34
+ `;
35
+
36
+ // Static table
37
+ export const staticTable = () => html`
38
+ <table class="table">
39
+ <thead><tr><th>ID</th><th>Name</th></tr></thead>
40
+ <tbody>
41
+ <tr><td>1</td><td>Alice</td></tr>
42
+ <tr><td>2</td><td>Bob</td></tr>
43
+ </tbody>
44
+ </table>
45
+ `;
46
+
47
+ // Static form
48
+ export const staticForm = () => html`
49
+ <form action="/submit" method="POST">
50
+ <label for="name">Name:</label>
51
+ <input type="text" id="name" name="name" required>
52
+ <button type="submit">Submit</button>
53
+ </form>
54
+ `;
55
+
56
+ // Static with void elements
57
+ export const staticVoid = () => html`
58
+ <div>
59
+ <img src="/image.png" alt="Test">
60
+ <br>
61
+ <hr>
62
+ <input type="hidden" name="token" value="abc123">
63
+ </div>
64
+ `;
65
+
66
+ // Static deeply nested (8 levels)
67
+ export const staticDeepNest = () => html`
68
+ <div class="l1">
69
+ <div class="l2">
70
+ <div class="l3">
71
+ <div class="l4">
72
+ <div class="l5">
73
+ <div class="l6">
74
+ <div class="l7">
75
+ <div class="l8">Deep content</div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ `;
84
+
85
+ // Static wide structure (many siblings)
86
+ export const staticWide = () => html`
87
+ <div class="container">
88
+ <span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
89
+ <span>6</span><span>7</span><span>8</span><span>9</span><span>10</span>
90
+ </div>
91
+ `;
92
+
93
+ // Static SVG
94
+ export const staticSvg = () => html`
95
+ <svg width="100" height="100" viewBox="0 0 100 100">
96
+ <circle cx="50" cy="50" r="40" fill="blue"/>
97
+ <rect x="10" y="10" width="20" height="20" fill="red"/>
98
+ </svg>
99
+ `;
100
+
101
+ // Static with comments preserved in output
102
+ export const staticComments = () => {
103
+ let value = 'sadasd';
104
+
105
+ return html`
106
+ <div>
107
+ <!-- Header section -->
108
+ <header>${value}</header>
109
+ <!-- Main content -->
110
+ <main>Content</main>
111
+ </div>
112
+ `;
113
+ };
114
+
115
+ // Identical templates - tests deduplication
116
+ export const staticDupe1 = () => html`<div class="dupe">Duplicate content</div>`;
117
+ export const staticDupe2 = () => html`<div class="dupe">Duplicate content</div>`;
118
+ export const staticDupe3 = () => html`<div class="dupe">Duplicate content</div>`;