@esportsplus/template 0.28.1 → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bump.yml +2 -2
- package/.github/workflows/dependabot.yml +1 -1
- package/.github/workflows/publish.yml +2 -2
- package/build/attributes.d.ts +7 -1
- package/build/attributes.js +86 -33
- package/build/constants.d.ts +3 -11
- package/build/constants.js +4 -32
- package/build/event/constants.d.ts +3 -0
- package/build/event/constants.js +13 -0
- package/build/event/index.d.ts +9 -1
- package/build/event/index.js +29 -35
- package/build/event/ontick.js +6 -9
- package/build/html.d.ts +9 -0
- package/build/html.js +7 -0
- package/build/index.d.ts +8 -2
- package/build/index.js +8 -1
- package/build/render.d.ts +2 -2
- package/build/render.js +2 -3
- package/build/runtime.d.ts +1 -0
- package/build/runtime.js +5 -0
- package/build/slot/array.d.ts +3 -3
- package/build/slot/array.js +11 -14
- package/build/slot/cleanup.d.ts +1 -1
- package/build/slot/cleanup.js +1 -2
- package/build/slot/effect.js +5 -7
- package/build/slot/index.js +1 -7
- package/build/slot/render.js +6 -8
- package/build/svg.d.ts +1 -1
- package/build/svg.js +1 -1
- package/build/transformer/codegen.d.ts +18 -0
- package/build/transformer/codegen.js +316 -0
- package/build/transformer/index.d.ts +12 -0
- package/build/transformer/index.js +62 -0
- package/build/transformer/parser.d.ts +18 -0
- package/build/transformer/parser.js +166 -0
- package/build/transformer/plugins/esbuild.d.ts +5 -0
- package/build/transformer/plugins/esbuild.js +35 -0
- package/build/transformer/plugins/tsc.d.ts +3 -0
- package/build/transformer/plugins/tsc.js +4 -0
- package/build/transformer/plugins/vite.d.ts +5 -0
- package/build/transformer/plugins/vite.js +37 -0
- package/build/transformer/ts-parser.d.ts +21 -0
- package/build/transformer/ts-parser.js +72 -0
- package/build/transformer/type-analyzer.d.ts +7 -0
- package/build/transformer/type-analyzer.js +230 -0
- package/build/types.d.ts +2 -3
- package/build/utilities.d.ts +7 -0
- package/build/utilities.js +31 -0
- package/package.json +11 -4
- package/src/attributes.ts +115 -51
- package/src/constants.ts +6 -53
- package/src/event/constants.ts +16 -0
- package/src/event/index.ts +36 -42
- package/src/event/onconnect.ts +1 -1
- package/src/event/onresize.ts +1 -1
- package/src/event/ontick.ts +7 -11
- package/src/html.ts +18 -0
- package/src/index.ts +8 -2
- package/src/render.ts +6 -7
- package/src/runtime.ts +8 -0
- package/src/slot/array.ts +18 -24
- package/src/slot/cleanup.ts +3 -4
- package/src/slot/effect.ts +6 -8
- package/src/slot/index.ts +2 -8
- package/src/slot/render.ts +7 -9
- package/src/svg.ts +1 -1
- package/src/transformer/codegen.ts +518 -0
- package/src/transformer/index.ts +98 -0
- package/src/transformer/parser.ts +239 -0
- package/src/transformer/plugins/esbuild.ts +46 -0
- package/src/transformer/plugins/tsc.ts +7 -0
- package/src/transformer/plugins/vite.ts +49 -0
- package/src/transformer/ts-parser.ts +123 -0
- package/src/transformer/type-analyzer.ts +334 -0
- package/src/types.ts +3 -4
- package/src/utilities.ts +52 -0
- package/storage/rewrite-analysis-2026-01-04.md +439 -0
- package/test/constants.ts +69 -0
- package/test/effects.ts +237 -0
- package/test/events.ts +318 -0
- package/test/imported-values.ts +253 -0
- package/test/nested.ts +298 -0
- package/test/slots.ts +259 -0
- package/test/spread.ts +290 -0
- package/test/static.ts +118 -0
- package/test/templates.ts +473 -0
- package/test/tsconfig.json +17 -0
- package/test/vite.config.ts +50 -0
- package/build/html/index.d.ts +0 -9
- package/build/html/index.js +0 -29
- package/build/html/parser.d.ts +0 -5
- package/build/html/parser.js +0 -165
- package/build/utilities/element.d.ts +0 -11
- package/build/utilities/element.js +0 -9
- package/build/utilities/fragment.d.ts +0 -3
- package/build/utilities/fragment.js +0 -10
- package/build/utilities/marker.d.ts +0 -2
- package/build/utilities/marker.js +0 -4
- package/build/utilities/node.d.ts +0 -9
- package/build/utilities/node.js +0 -10
- package/build/utilities/raf.d.ts +0 -2
- package/build/utilities/raf.js +0 -1
- package/build/utilities/text.d.ts +0 -2
- package/build/utilities/text.js +0 -9
- package/src/html/index.ts +0 -48
- package/src/html/parser.ts +0 -235
- package/src/utilities/element.ts +0 -28
- package/src/utilities/fragment.ts +0 -19
- package/src/utilities/marker.ts +0 -6
- package/src/utilities/node.ts +0 -29
- package/src/utilities/raf.ts +0 -1
- 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>`;
|