@arcmantle/lit-jsx 1.0.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 (119) hide show
  1. package/README.md +768 -0
  2. package/dist/compiler/attribute-processor.d.ts +128 -0
  3. package/dist/compiler/attribute-processor.d.ts.map +1 -0
  4. package/dist/compiler/attribute-processor.js +380 -0
  5. package/dist/compiler/attribute-processor.js.map +1 -0
  6. package/dist/compiler/babel-preset.d.ts +6 -0
  7. package/dist/compiler/babel-preset.d.ts.map +1 -0
  8. package/dist/compiler/babel-preset.js +27 -0
  9. package/dist/compiler/babel-preset.js.map +1 -0
  10. package/dist/compiler/builder.d.ts +22 -0
  11. package/dist/compiler/builder.d.ts.map +1 -0
  12. package/dist/compiler/builder.js +62 -0
  13. package/dist/compiler/builder.js.map +1 -0
  14. package/dist/compiler/compiler-utils.d.ts +81 -0
  15. package/dist/compiler/compiler-utils.d.ts.map +1 -0
  16. package/dist/compiler/compiler-utils.js +410 -0
  17. package/dist/compiler/compiler-utils.js.map +1 -0
  18. package/dist/compiler/config.d.ts +77 -0
  19. package/dist/compiler/config.d.ts.map +1 -0
  20. package/dist/compiler/config.js +78 -0
  21. package/dist/compiler/config.js.map +1 -0
  22. package/dist/compiler/postprocess.d.ts +5 -0
  23. package/dist/compiler/postprocess.d.ts.map +1 -0
  24. package/dist/compiler/postprocess.js +3 -0
  25. package/dist/compiler/postprocess.js.map +1 -0
  26. package/dist/compiler/preprocess.d.ts +5 -0
  27. package/dist/compiler/preprocess.d.ts.map +1 -0
  28. package/dist/compiler/preprocess.js +28 -0
  29. package/dist/compiler/preprocess.js.map +1 -0
  30. package/dist/compiler/transform-jsx.d.ts +5 -0
  31. package/dist/compiler/transform-jsx.d.ts.map +1 -0
  32. package/dist/compiler/transform-jsx.js +25 -0
  33. package/dist/compiler/transform-jsx.js.map +1 -0
  34. package/dist/compiler/transpiler.d.ts +48 -0
  35. package/dist/compiler/transpiler.d.ts.map +1 -0
  36. package/dist/compiler/transpiler.js +463 -0
  37. package/dist/compiler/transpiler.js.map +1 -0
  38. package/dist/compiler/vite-plugin.d.ts +38 -0
  39. package/dist/compiler/vite-plugin.d.ts.map +1 -0
  40. package/dist/compiler/vite-plugin.js +96 -0
  41. package/dist/compiler/vite-plugin.js.map +1 -0
  42. package/dist/runtime/choose-component.d.ts +39 -0
  43. package/dist/runtime/choose-component.d.ts.map +1 -0
  44. package/dist/runtime/choose-component.js +40 -0
  45. package/dist/runtime/choose-component.js.map +1 -0
  46. package/dist/runtime/compiler-ctors.d.ts +21 -0
  47. package/dist/runtime/compiler-ctors.d.ts.map +1 -0
  48. package/dist/runtime/compiler-ctors.js +21 -0
  49. package/dist/runtime/compiler-ctors.js.map +1 -0
  50. package/dist/runtime/for-component.d.ts +25 -0
  51. package/dist/runtime/for-component.d.ts.map +1 -0
  52. package/dist/runtime/for-component.js +35 -0
  53. package/dist/runtime/for-component.js.map +1 -0
  54. package/dist/runtime/literal-map.d.ts +22 -0
  55. package/dist/runtime/literal-map.d.ts.map +1 -0
  56. package/dist/runtime/literal-map.js +29 -0
  57. package/dist/runtime/literal-map.js.map +1 -0
  58. package/dist/runtime/rest-directive.d.ts +28 -0
  59. package/dist/runtime/rest-directive.d.ts.map +1 -0
  60. package/dist/runtime/rest-directive.js +49 -0
  61. package/dist/runtime/rest-directive.js.map +1 -0
  62. package/dist/runtime/show-component.d.ts +33 -0
  63. package/dist/runtime/show-component.d.ts.map +1 -0
  64. package/dist/runtime/show-component.js +30 -0
  65. package/dist/runtime/show-component.js.map +1 -0
  66. package/dist/runtime/tagged-template.d.ts +12 -0
  67. package/dist/runtime/tagged-template.d.ts.map +1 -0
  68. package/dist/runtime/tagged-template.js +12 -0
  69. package/dist/runtime/tagged-template.js.map +1 -0
  70. package/dist/runtime/type-helpers.d.ts +80 -0
  71. package/dist/runtime/type-helpers.d.ts.map +1 -0
  72. package/dist/runtime/type-helpers.js +85 -0
  73. package/dist/runtime/type-helpers.js.map +1 -0
  74. package/dist/shared/jsx-types.d.ts +2139 -0
  75. package/dist/shared/jsx-types.d.ts.map +1 -0
  76. package/dist/shared/jsx-types.js +2 -0
  77. package/dist/shared/jsx-types.js.map +1 -0
  78. package/dist/shared/jsx-utils.d.ts +30 -0
  79. package/dist/shared/jsx-utils.d.ts.map +1 -0
  80. package/dist/shared/jsx-utils.js +58 -0
  81. package/dist/shared/jsx-utils.js.map +1 -0
  82. package/dist/shared/mathml-tags.d.ts +12 -0
  83. package/dist/shared/mathml-tags.d.ts.map +1 -0
  84. package/dist/shared/mathml-tags.js +215 -0
  85. package/dist/shared/mathml-tags.js.map +1 -0
  86. package/dist/shared/svg-tags.d.ts +13 -0
  87. package/dist/shared/svg-tags.d.ts.map +1 -0
  88. package/dist/shared/svg-tags.js +95 -0
  89. package/dist/shared/svg-tags.js.map +1 -0
  90. package/dist/utils.d.ts +30 -0
  91. package/dist/utils.d.ts.map +1 -0
  92. package/dist/utils.js +30 -0
  93. package/dist/utils.js.map +1 -0
  94. package/package.json +52 -0
  95. package/src/compiler/attribute-processor.ts +579 -0
  96. package/src/compiler/babel-preset.ts +34 -0
  97. package/src/compiler/builder.ts +86 -0
  98. package/src/compiler/compiler-utils.ts +789 -0
  99. package/src/compiler/config.ts +77 -0
  100. package/src/compiler/postprocess.ts +7 -0
  101. package/src/compiler/preprocess.ts +40 -0
  102. package/src/compiler/transform-jsx.ts +36 -0
  103. package/src/compiler/transpiler.ts +644 -0
  104. package/src/compiler/vite-plugin.ts +114 -0
  105. package/src/external.d.ts +9 -0
  106. package/src/runtime/choose-component.ts +53 -0
  107. package/src/runtime/compiler-ctors.ts +28 -0
  108. package/src/runtime/for-component.ts +54 -0
  109. package/src/runtime/literal-map.ts +37 -0
  110. package/src/runtime/rest-directive.ts +66 -0
  111. package/src/runtime/show-component.ts +48 -0
  112. package/src/runtime/tagged-template.ts +11 -0
  113. package/src/runtime/type-helpers.ts +91 -0
  114. package/src/shared/jsx-types.ts +2556 -0
  115. package/src/shared/jsx-utils.ts +85 -0
  116. package/src/shared/mathml-tags.ts +235 -0
  117. package/src/shared/svg-tags.ts +103 -0
  118. package/src/tsconfig.json +4 -0
  119. package/src/utils.ts +30 -0
package/README.md ADDED
@@ -0,0 +1,768 @@
1
+ # lit-jsx
2
+
3
+ A powerful JSX compiler and Vite plugin that transforms JSX into native Lit templates at compile time with zero runtime overhead.
4
+
5
+ ## 🚀 Features
6
+
7
+ jsx-lit brings the familiar JSX syntax to the Lit ecosystem while maintaining the performance and capabilities that make Lit exceptional.
8
+
9
+ ```tsx
10
+ // Write familiar JSX
11
+ function TodoItem({ todo, onToggle, onDelete }) {
12
+ return (
13
+ <div classList={{ completed: todo.completed }}>
14
+ <input
15
+ type="checkbox"
16
+ checked={as.prop(todo.completed)}
17
+ disabled={as.bool(todo.readonly)}
18
+ on-change={() => onToggle(todo.id)}
19
+ />
20
+ <span>{todo.text}</span>
21
+ <button on-click={() => onDelete(todo.id)}>Delete</button>
22
+ </div>
23
+ );
24
+ }
25
+
26
+ // Compiles to efficient Lit templates
27
+ html`
28
+ <div class=${classMap({ completed: todo.completed })}>
29
+ <input
30
+ type="checkbox"
31
+ .checked=${todo.completed}
32
+ ?disabled=${todo.readonly}
33
+ @change=${() => onToggle(todo.id)}
34
+ />
35
+ <span>${todo.text}</span>
36
+ <button @click=${() => onDelete(todo.id)}>Delete</button>
37
+ </div>
38
+ `
39
+ ```
40
+
41
+ ### ✨ Key Benefits
42
+
43
+ - **⚡ Zero Runtime Overhead**: Pure compile-time transformation to native Lit templates
44
+ - **🎯 Type-Safe**: Full TypeScript support with comprehensive JSX type definitions
45
+ - **🔧 Vite Integration**: Seamless setup with the included Vite plugin
46
+ - **🎨 Lit Ecosystem**: Works with all Lit directives, custom elements, and patterns
47
+ - **🎛️ Flexible Binding**: Fine-grained control over attribute, property, and boolean bindings
48
+ - **🏷️ Dynamic Tags**: Support for conditional element types with static template optimization
49
+ - **📦 Function Components**: Full support for composable function components
50
+ - **🔗 Custom Elements**: Type-safe integration with Lit-based custom elements
51
+ - **🧩 Library Components**: Built-in `For`, `Show`, and `Choose` components for common rendering patterns
52
+
53
+ ## 📦 Installation
54
+
55
+ ```bash
56
+ npm install jsx-lit
57
+ # or
58
+ pnpm add jsx-lit
59
+ # or
60
+ yarn add jsx-lit
61
+ ```
62
+
63
+ ## ⚡ Quick Start
64
+
65
+ ### 1. Configure Vite
66
+
67
+ ```typescript
68
+ // vite.config.ts
69
+ import { litJsx } from 'jsx-lit/vite-jsx-preserve';
70
+ import { defineConfig } from 'vite';
71
+
72
+ export default defineConfig({
73
+ plugins: [litJsx()],
74
+ });
75
+ ```
76
+
77
+ ### 2. Configure TypeScript
78
+
79
+ ```json
80
+ {
81
+ "compilerOptions": {
82
+ "jsx": "preserve",
83
+ "jsxImportSource": "jsx-lit"
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### 3. Start Writing JSX
89
+
90
+ ```tsx
91
+ import { LitElement } from 'lit';
92
+ import { For, Show, Choose } from 'jsx-lit';
93
+
94
+ export class MyComponent extends LitElement {
95
+ render() {
96
+ return (
97
+ <div>
98
+ <h1>Hello jsx-lit!</h1>
99
+ <p>JSX compiled to Lit templates with utility components</p>
100
+
101
+ <Show when={this.items.length > 0}>
102
+ {(length) => (
103
+ <For each={this.items}>
104
+ {(item, index) => <div>{item}</div>}
105
+ </For>
106
+ )}
107
+ </Show>
108
+ </div>
109
+ );
110
+ }
111
+ }
112
+ ```
113
+
114
+ ## 🎯 Core Concepts
115
+
116
+ ### Attribute Binding Control
117
+
118
+ jsx-lit provides precise control over how values are bound to elements:
119
+
120
+ #### Default Behavior (Attribute Binding)
121
+
122
+ ```tsx
123
+ <input value={userInput} />
124
+ // Compiles to: <input value=${userInput} />
125
+ ```
126
+
127
+ #### Property Binding
128
+
129
+ ```tsx
130
+ <input value={as.prop(userInput)} />
131
+ // or
132
+ <input value={prop => userInput} />
133
+ // Compiles to: <input .value=${userInput} />
134
+ ```
135
+
136
+ #### Boolean Attribute Binding
137
+
138
+ ```tsx
139
+ <input disabled={as.bool(isDisabled)} />
140
+ // or
141
+ <input disabled={bool => isDisabled} />
142
+ // Compiles to: <input ?disabled=${isDisabled} />
143
+ ```
144
+
145
+ ### Special Attributes
146
+
147
+ #### classList - Object to Class Mapping
148
+
149
+ ```tsx
150
+ <div classList={{ active: isActive, disabled: !isEnabled }}>
151
+ // Compiles to: <div class=${classMap({ active: isActive, disabled: !isEnabled })}>
152
+ ```
153
+
154
+ #### styleList - Object to Style Mapping
155
+
156
+ ```tsx
157
+ <div styleList={{ color: textColor, fontSize: '16px' }}>
158
+ // Compiles to: <div style=${styleMap({ color: textColor, fontSize: '16px' })}>
159
+ ```
160
+
161
+ #### Event Handlers
162
+
163
+ ```tsx
164
+ <button on-click={handleClick} on-dblclick={handleDoubleClick}>
165
+ // Compiles to: <button @click=${handleClick} @dblclick=${handleDoubleClick}>
166
+ ```
167
+
168
+ #### References
169
+
170
+ ```tsx
171
+ <input ref={inputRef} />
172
+ // Compiles to: <input ${ref(inputRef)} />
173
+ ```
174
+
175
+ #### Element Directives
176
+
177
+ ```tsx
178
+ <div directive={myDirective()} />
179
+ // Compiles to: <div ${myDirective()} />
180
+
181
+ // Multiple directives as an array
182
+ <div directive={[directive1(), directive2()]} />
183
+ // Compiles to: <div ${directive1()} ${directive2()} />
184
+ ```
185
+
186
+ #### Spread Attributes
187
+
188
+ ```tsx
189
+ <div {...dynamicProps} />
190
+ // Compiles to: <div ${__$rest(dynamicProps)} />
191
+ ```
192
+
193
+ ## 🏗️ Component Patterns
194
+
195
+ ### Function Components
196
+
197
+ jsx-lit fully supports function components that return JSX:
198
+
199
+ ```tsx
200
+ const Button = ({ label, variant = 'primary', disabled, onClick, children }) => (
201
+ <button
202
+ classList={{ [`btn-${variant}`]: true, 'disabled': disabled }}
203
+ disabled={as.bool(disabled)}
204
+ on-click={onClick}
205
+ >
206
+ {label || children}
207
+ </button>
208
+ );
209
+
210
+ // Usage
211
+ <Button
212
+ label="Submit"
213
+ variant="success"
214
+ onClick={handleSubmit}
215
+ disabled={isLoading}
216
+ />
217
+ ```
218
+
219
+ Function components:
220
+
221
+ - Receive props as a single object parameter
222
+ - Support `children` via the `children` property
223
+ - Compile to efficient function calls
224
+ - Support all JSX features including conditional rendering and loops
225
+
226
+ ### Custom Element Integration
227
+
228
+ Use `toJSX()` for type-safe custom element components:
229
+
230
+ ```tsx
231
+ import { toJSX } from 'jsx-lit';
232
+ import { LitElement } from 'lit';
233
+
234
+ export class MyButton extends LitElement {
235
+ static tagName = 'my-button';
236
+ static tag = toJSX(MyButton);
237
+
238
+ render() {
239
+ return html`<button><slot></slot></button>`;
240
+ }
241
+ }
242
+
243
+ // Usage with type safety
244
+ <MyButton.tag
245
+ class="custom-btn"
246
+ onClick={() => console.log('Clicked!')}
247
+ />
248
+ ```
249
+
250
+ #### Generic Custom Elements
251
+
252
+ For custom elements with generic types, you can create type-safe JSX components using explicit type casting:
253
+
254
+ ```tsx
255
+ import { toJSX } from 'jsx-lit';
256
+ import { LitElement } from 'lit';
257
+
258
+ class DataList<T> extends LitElement {
259
+ static tagName = 'data-list';
260
+ // Type casting is required due to TypeScript's inability to forward generics
261
+ static tag = toJSX(this) as <T>(props: JSX.JSXProps<DataList<T>>) => JSX.JSXElement;
262
+
263
+ @property({ type: Array }) items: T[] = [];
264
+ @property() renderItem?: (item: T) => TemplateResult;
265
+
266
+ render() {
267
+ return html`
268
+ <ul>
269
+ ${this.items.map(item => html`
270
+ <li>${this.renderItem ? this.renderItem(item) : item}</li>
271
+ `)}
272
+ </ul>
273
+ `;
274
+ }
275
+ }
276
+
277
+ // Usage with explicit type parameter
278
+ <DataList.tag<User>
279
+ items={users}
280
+ renderItem={(user) => `${user.name} (${user.email})`}
281
+ />
282
+
283
+ // Type inference works for the renderItem callback
284
+ <DataList.tag<Product>
285
+ items={products}
286
+ renderItem={(product) => `${product.name} - $${product.price}`}
287
+ />
288
+ ```
289
+
290
+ **Note**: The current generic syntax requires explicit type casting due to TypeScript's limitations in forwarding generic parameters through static properties. If TypeScript gains the ability to forward generics in this context in the future, jsx-lit will implement a more seamless syntax.
291
+
292
+ ### Dynamic Tag Names
293
+
294
+ jsx-lit supports dynamic element types with the `.tag` property pattern:
295
+
296
+ ```tsx
297
+ import { toTag } from 'jsx-lit';
298
+
299
+ function ActionElement({ href, children }) {
300
+ const Tag = toTag(href ? 'a' : 'button');
301
+
302
+ return (
303
+ <Tag.tag href={href} class="action-element">
304
+ {children}
305
+ </Tag.tag>
306
+ );
307
+ }
308
+ ```
309
+
310
+ **Important**: Dynamic tag names must use the `.tag` property pattern for proper static template compilation.
311
+
312
+ ### Library Components
313
+
314
+ jsx-lit provides utility components that enhance common patterns and integrate seamlessly with Lit directives:
315
+
316
+ #### For Component - Declarative List Rendering
317
+
318
+ The `For` component provides a declarative way to render lists with optional keys and separators:
319
+
320
+ ```tsx
321
+ import { For } from 'jsx-lit';
322
+
323
+ // Basic list rendering
324
+ <For each={users}>
325
+ {(user, index) => (
326
+ <div class="user-item">
327
+ {index + 1}. {user.name}
328
+ </div>
329
+ )}
330
+ </For>
331
+
332
+ // With key function for efficient updates
333
+ <For each={todos} key={(todo) => todo.id}>
334
+ {(todo, index) => (
335
+ <li classList={{ completed: todo.completed }}>
336
+ {todo.text}
337
+ </li>
338
+ )}
339
+ </For>
340
+
341
+ // With separators between items
342
+ <For each={breadcrumbs} separator={<span> / </span>}>
343
+ {(crumb, index) => (
344
+ <a href={crumb.url}>{crumb.label}</a>
345
+ )}
346
+ </For>
347
+ ```
348
+
349
+ The `For` component automatically uses lit-html's optimized directives:
350
+
351
+ - **Without key**: Uses `map` directive for simple iteration
352
+ - **With key**: Uses `repeat` directive for efficient updates when items change
353
+ - **With separator**: Uses `join` directive to insert elements between items
354
+
355
+ #### Show Component - Conditional Rendering
356
+
357
+ The `Show` component provides type-safe conditional rendering with optional fallback:
358
+
359
+ ```tsx
360
+ import { Show } from 'jsx-lit';
361
+
362
+ // Simple conditional rendering
363
+ <Show when={user}>
364
+ {(user) => (
365
+ <div class="welcome">
366
+ Welcome back, {user.name}!
367
+ </div>
368
+ )}
369
+ </Show>
370
+
371
+ // With fallback content
372
+ <Show when={currentUser}>
373
+ {[
374
+ (user) => (
375
+ <div class="user-panel">
376
+ <img src={user.avatar} alt={user.name} />
377
+ <span>{user.name}</span>
378
+ </div>
379
+ ),
380
+ () => (
381
+ <div class="login-prompt">
382
+ <button>Sign In</button>
383
+ </div>
384
+ )
385
+ ]}
386
+ </Show>
387
+
388
+ // Conditional rendering with complex conditions
389
+ <Show when={items.length > 0}>
390
+ {(length) => (
391
+ <div class="item-count">
392
+ Found {length} items
393
+ </div>
394
+ )}
395
+ </Show>
396
+ ```
397
+
398
+ The `Show` component uses lit-html's `when` directive internally and provides strong TypeScript inference for the truthy value.
399
+
400
+ #### Choose Component - Multi-Condition Rendering
401
+
402
+ The `Choose` component enables clean switch-like conditional rendering with multiple condition-output pairs:
403
+
404
+ ```tsx
405
+ import { Choose } from 'jsx-lit';
406
+
407
+ // Multiple conditions based on a value
408
+ <Choose value={status}>
409
+ {[
410
+ (status) => status === 'loading',
411
+ () => (
412
+ <div class="loading">
413
+ <spinner-icon></spinner-icon>
414
+ Loading...
415
+ </div>
416
+ )
417
+ ]}
418
+ {[
419
+ (status) => status === 'error',
420
+ (status) => (
421
+ <div class="error">
422
+ Error: {status}
423
+ </div>
424
+ )
425
+ ]}
426
+ {[
427
+ (status) => status === 'success',
428
+ (status) => (
429
+ <div class="success">
430
+ Operation completed successfully!
431
+ </div>
432
+ )
433
+ ]}
434
+ {[
435
+ () => true, // Default case
436
+ (status) => (
437
+ <div class="unknown">
438
+ Unknown status: {status}
439
+ </div>
440
+ )
441
+ ]}
442
+ </Choose>
443
+
444
+ // Without a value (boolean conditions)
445
+ <Choose>
446
+ {[
447
+ () => user.isAdmin,
448
+ () => <admin-panel></admin-panel>
449
+ ]}
450
+ {[
451
+ () => user.isModerator,
452
+ () => <moderator-panel></moderator-panel>
453
+ ]}
454
+ {[
455
+ () => true, // Default case
456
+ () => <user-panel></user-panel>
457
+ ]}
458
+ </Choose>
459
+ ```
460
+
461
+ The `Choose` component evaluates conditions in order and renders the first matching case, similar to a switch statement but as an expression.
462
+
463
+ #### Combining Library Components
464
+
465
+ These components work seamlessly together for complex rendering scenarios:
466
+
467
+ ```tsx
468
+ import { For, Show, Choose } from 'jsx-lit';
469
+
470
+ @customElement('user-dashboard')
471
+ export class UserDashboard extends LitElement {
472
+ @property({ type: Array }) users = [];
473
+ @property() currentUser = null;
474
+ @property() viewMode = 'list';
475
+
476
+ render() {
477
+ return (
478
+ <div class="dashboard">
479
+ {/* Conditional user greeting */}
480
+ <Show when={this.currentUser}>
481
+ {(user) => (
482
+ <header class="welcome">
483
+ Welcome back, {user.name}!
484
+ </header>
485
+ )}
486
+ </Show>
487
+
488
+ {/* Dynamic view rendering based on mode */}
489
+ <Choose value={this.viewMode}>
490
+ {[
491
+ (mode) => mode === 'grid',
492
+ () => (
493
+ <div class="user-grid">
494
+ <For each={this.users} key={(user) => user.id}>
495
+ {(user) => (
496
+ <div class="user-card">
497
+ <img src={user.avatar} alt={user.name} />
498
+ <h3>{user.name}</h3>
499
+ <p>{user.role}</p>
500
+ </div>
501
+ )}
502
+ </For>
503
+ </div>
504
+ )
505
+ ]}
506
+ {[
507
+ (mode) => mode === 'list',
508
+ () => (
509
+ <div class="user-list">
510
+ <For each={this.users} separator={<hr />}>
511
+ {(user, index) => (
512
+ <div class="user-row">
513
+ <span class="user-index">{index + 1}.</span>
514
+ <span class="user-name">{user.name}</span>
515
+ <span class="user-role">{user.role}</span>
516
+ </div>
517
+ )}
518
+ </For>
519
+ </div>
520
+ )
521
+ ]}
522
+ {[
523
+ () => true, // Default case
524
+ (mode) => (
525
+ <div class="error">
526
+ Unknown view mode: {mode}
527
+ </div>
528
+ )
529
+ ]}
530
+ </Choose>
531
+
532
+ {/* Conditional empty state */}
533
+ <Show when={this.users.length === 0}>
534
+ {() => (
535
+ <div class="empty-state">
536
+ <p>No users found</p>
537
+ <button on-click={this.loadUsers}>Load Users</button>
538
+ </div>
539
+ )}
540
+ </Show>
541
+ </div>
542
+ );
543
+ }
544
+ }
545
+ ```
546
+
547
+ ## 🔧 Advanced Usage
548
+
549
+ ### Lit Directives Integration
550
+
551
+ jsx-lit works seamlessly with all Lit directives:
552
+
553
+ ```tsx
554
+ import { when } from 'lit-html/directives/when.js';
555
+ import { repeat } from 'lit-html/directives/repeat.js';
556
+ import { guard } from 'lit-html/directives/guard.js';
557
+
558
+ return (
559
+ <div>
560
+ {when(condition, () => <p>Conditional content</p>)}
561
+ {repeat(items, item => item.id, item => (
562
+ <li key={item.id}>{item.name}</li>
563
+ ))}
564
+ {guard([expensiveData], () => (
565
+ <ExpensiveComponent data={expensiveData} />
566
+ ))}
567
+ </div>
568
+ );
569
+ ```
570
+
571
+ ### Complex Example: Todo List
572
+
573
+ ```tsx
574
+ @customElement('todo-list')
575
+ export class TodoList extends LitElement {
576
+ @property({ type: Array }) items = [];
577
+ @state() private newItemText = '';
578
+ @state() private filter = 'all';
579
+
580
+ private inputRef = createRef();
581
+
582
+ get filteredItems() {
583
+ switch (this.filter) {
584
+ case 'active': return this.items.filter(item => !item.completed);
585
+ case 'completed': return this.items.filter(item => item.completed);
586
+ default: return this.items;
587
+ }
588
+ }
589
+
590
+ addItem() {
591
+ if (this.newItemText.trim()) {
592
+ this.items = [...this.items, {
593
+ id: Date.now(),
594
+ text: this.newItemText,
595
+ completed: false
596
+ }];
597
+ this.newItemText = '';
598
+ this.inputRef.value?.focus();
599
+ }
600
+ }
601
+
602
+ render() {
603
+ return (
604
+ <div class="todo-container">
605
+ <h1>Todo List</h1>
606
+
607
+ <div class="add-form">
608
+ <input
609
+ ref={this.inputRef}
610
+ value={as.prop(this.newItemText)}
611
+ placeholder="Add new todo..."
612
+ on-input={(e) => this.newItemText = e.target.value}
613
+ on-keydown={(e) => e.key === 'Enter' && this.addItem()}
614
+ />
615
+ <button on-click={this.addItem}>Add</button>
616
+ </div>
617
+
618
+ <div class="filters">
619
+ {['all', 'active', 'completed'].map(filterType => (
620
+ <button
621
+ classList={{ active: this.filter === filterType }}
622
+ on-click={() => this.filter = filterType}
623
+ >
624
+ {filterType}
625
+ </button>
626
+ ))}
627
+ </div>
628
+
629
+ {when(this.filteredItems.length > 0, () => (
630
+ <ul class="todo-list">
631
+ {repeat(this.filteredItems, item => item.id, item => (
632
+ <TodoItem
633
+ todo={item}
634
+ onToggle={(id) => this.toggleItem(id)}
635
+ onDelete={(id) => this.deleteItem(id)}
636
+ />
637
+ ))}
638
+ </ul>
639
+ ), () => (
640
+ <p class="empty-state">No items to show</p>
641
+ ))}
642
+ </div>
643
+ );
644
+ }
645
+ }
646
+ ```
647
+
648
+ ## 🎛️ Configuration
649
+
650
+ ### Vite Plugin Options
651
+
652
+ ```typescript
653
+ import { litJsx } from 'jsx-lit/vite-jsx-preserve';
654
+
655
+ export default defineConfig({
656
+ plugins: [
657
+ litJsx({
658
+ babel: {
659
+ // Babel transform options
660
+ plugins: ['@babel/plugin-proposal-decorators'],
661
+ },
662
+ // Or use a function for dynamic configuration
663
+ babel: (code, id) => ({
664
+ plugins: id.includes('legacy') ? [] : ['modern-plugin'],
665
+ }),
666
+ }),
667
+ ],
668
+ });
669
+ ```
670
+
671
+ ## 🚀 Template Types
672
+
673
+ jsx-lit automatically detects and uses the appropriate template type:
674
+
675
+ - **HTML templates**: `html\`...\`` for regular HTML elements
676
+ - **SVG templates**: `svg\`...\`` for SVG elements
677
+ - **MathML templates**: `mathml\`...\`` for MathML elements
678
+ - **Static templates**: `htmlStatic\`...\`` for dynamic tag names
679
+
680
+ ## 🎯 Best Practices
681
+
682
+ ### When to Use Each Binding Type
683
+
684
+ #### **Attribute Binding (Default)**
685
+
686
+ - Custom attributes and data attributes
687
+ - Values that should appear in HTML as attributes
688
+ - Working with libraries that expect attributes
689
+
690
+ ```tsx
691
+ <div data-id={item.id} aria-label={item.description} />
692
+ ```
693
+
694
+ #### **Property Binding (`as.prop()` or `prop =>`)**
695
+
696
+ - Standard DOM properties like `value`, `checked`, `selected`
697
+ - Interactive elements that need live property updates
698
+ - Complex object values
699
+
700
+ ```tsx
701
+ <input value={as.prop(formData.email)} checked={as.prop(isSelected)} />
702
+ ```
703
+
704
+ #### **Boolean Attribute Binding (`as.bool()` or `bool =>`)**
705
+
706
+ - Boolean HTML attributes like `disabled`, `hidden`, `readonly`
707
+ - Accessibility attributes that follow boolean patterns
708
+ - Presence/absence semantics
709
+
710
+ ```tsx
711
+ <button disabled={as.bool(isLoading)} hidden={as.bool(!isVisible)} />
712
+ ```
713
+
714
+ ### Function Component Guidelines
715
+
716
+ - Use descriptive prop names and provide defaults where appropriate
717
+ - Keep components focused and composable
718
+ - Leverage TypeScript for better developer experience
719
+ - Handle `children` appropriately for flexible composition
720
+
721
+ ### Dynamic Tag Best Practices
722
+
723
+ - Always use `toTag()` with the `.tag` property pattern
724
+ - Use descriptive variable names for clarity
725
+ - Consider TypeScript for better type safety with HTML elements
726
+ - Document complex dynamic tag logic
727
+
728
+ ## 🔗 Ecosystem Integration
729
+
730
+ jsx-lit is designed to work seamlessly with the entire Lit ecosystem:
731
+
732
+ - **Lit Elements**: Full compatibility with LitElement and reactive properties
733
+ - **Lit Directives**: All official and community directives work out of the box
734
+ - **Custom Elements**: Easy integration with any custom elements
735
+ - **Web Components**: Standard web component patterns and lifecycle
736
+ - **TypeScript**: Comprehensive type definitions for the best developer experience
737
+
738
+ ## 📚 Migration Guide
739
+
740
+ ### From React JSX
741
+
742
+ jsx-lit syntax is very similar to React, with a few key differences:
743
+
744
+ ```tsx
745
+ // React
746
+ <button onClick={handler} className="btn" />
747
+
748
+ // jsx-lit
749
+ <button on-click={handler} class="btn" />
750
+ ```
751
+
752
+ ### From Lit html Templates
753
+
754
+ ```tsx
755
+ // Lit html
756
+ html`<div class=${classMap(classes)}>${content}</div>`
757
+
758
+ // jsx-lit
759
+ <div classList={classes}>{content}</div>
760
+ ```
761
+
762
+ ## 🤝 Contributing
763
+
764
+ jsx-lit is part of the larger Weave project. Contributions are welcome!
765
+
766
+ ## 📄 License
767
+
768
+ Apache-2.0