bun-plugin-stx 0.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.
package/README.md ADDED
@@ -0,0 +1,1076 @@
1
+ ![Social Card of Bun Plugin blade](https://github.com/stacksjs/stx/blob/main/.github/art/cover.jpg)
2
+
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![GitHub Actions][github-actions-src]][github-actions-href]
5
+ [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
6
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
7
+ <!-- [![Codecov][codecov-src]][codecov-href] -->
8
+
9
+ # bun-plugin-stx
10
+
11
+ A Blade-like template engine plugin for Bun, enabling simple and powerful templating with .stx files.
12
+
13
+ ## Features
14
+
15
+ - 🦋 Laravel Blade-like syntax
16
+ - 🚀 Fast and lightweight
17
+ - 📦 Zero config
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ bun add bun-plugin-stx
23
+ ```
24
+
25
+ ## Setup
26
+
27
+ Add the plugin to your `bunfig.toml`:
28
+
29
+ ```toml
30
+ preload = [ "bun-plugin-stx" ]
31
+
32
+ # or as a serve plugin
33
+ [serve.static]
34
+ plugins = [ "bun-plugin-stx" ]
35
+ ```
36
+
37
+ Or register the plugin in your build script:
38
+
39
+ ```ts
40
+ import { build } from 'bun'
41
+ import stxPlugin from 'bun-plugin-stx'
42
+
43
+ await build({
44
+ entrypoints: ['./src/index.ts', './templates/home.stx'],
45
+ outdir: './dist',
46
+ plugins: [stxPlugin],
47
+ })
48
+ ```
49
+
50
+ ## Usage with ESM
51
+
52
+ ### 1. Configure Bun to use the plugin
53
+
54
+ In your build script or Bun configuration:
55
+
56
+ ```js
57
+ // build.js
58
+ import { build } from 'bun'
59
+ import stxPlugin from 'bun-plugin-stx'
60
+
61
+ await build({
62
+ entrypoints: ['./src/index.ts', './templates/home.stx'],
63
+ outdir: './dist',
64
+ plugins: [stxPlugin],
65
+ })
66
+ ```
67
+
68
+ ### 2. Import and use .stx files directly
69
+
70
+ You can import .stx files directly in your ESM code:
71
+
72
+ ```js
73
+ // app.js
74
+ import homeTemplate from './templates/home.stx'
75
+
76
+ // Use the processed HTML content
77
+ document.body.innerHTML = homeTemplate
78
+ ```
79
+
80
+ ### 3. Use with Bun's server
81
+
82
+ You can serve .stx files directly with Bun's server:
83
+
84
+ ```js
85
+ // server.js
86
+ import { serve } from 'bun'
87
+ import homeTemplate from './home.stx'
88
+
89
+ serve({
90
+ port: 3000,
91
+ fetch(req) {
92
+ return new Response(homeTemplate, {
93
+ headers: { 'Content-Type': 'text/html' }
94
+ })
95
+ }
96
+ })
97
+ ```
98
+
99
+ Or use as route handlers:
100
+
101
+ ```js
102
+ import about from './about.stx'
103
+ // server.js
104
+ import home from './home.stx'
105
+
106
+ export default {
107
+ port: 3000,
108
+ routes: {
109
+ '/': home,
110
+ '/about': about
111
+ }
112
+ }
113
+ ```
114
+
115
+ ## STX Template Syntax
116
+
117
+ STX templates use a syntax inspired by Laravel Blade. Templates can contain HTML with special directives for rendering dynamic content.
118
+
119
+ ### Basic Example
120
+
121
+ ```html
122
+ <!DOCTYPE html>
123
+ <html>
124
+ <head>
125
+ <title>STX Example</title>
126
+ <script>
127
+ // Define your data as an ESM export
128
+ export const title = "Hello World";
129
+ export const items = ["Apple", "Banana", "Cherry"];
130
+ export const showFooter = true;
131
+ </script>
132
+ </head>
133
+ <body>
134
+ <h1>{{ title }}</h1>
135
+
136
+ <ul>
137
+ @foreach (items as item)
138
+ <li>{{ item }}</li>
139
+ @endforeach
140
+ </ul>
141
+
142
+ @if (showFooter)
143
+ <footer>Copyright 2023</footer>
144
+ @endif
145
+ </body>
146
+ </html>
147
+ ```
148
+
149
+ ### Data Export Options
150
+
151
+ There are two ways to expose data in your STX templates:
152
+
153
+ #### 1. ESM exports (recommended)
154
+
155
+ ```html
156
+ <script>
157
+ // Modern ESM named exports
158
+ export const title = "Hello World";
159
+ export const count = 42;
160
+
161
+ // Export functions
162
+ export function getFullName(first, last) {
163
+ return `${first} ${last}`;
164
+ }
165
+
166
+ // Export default object
167
+ export default {
168
+ items: ["Apple", "Banana", "Cherry"],
169
+ showDetails: true
170
+ };
171
+ </script>
172
+ ```
173
+
174
+ #### 2. Legacy CommonJS (module.exports)
175
+
176
+ ```html
177
+ <script>
178
+ // Legacy CommonJS exports
179
+ module.exports = {
180
+ title: "Hello World",
181
+ items: ["Apple", "Banana", "Cherry"],
182
+ showFooter: true
183
+ };
184
+ </script>
185
+ ```
186
+
187
+ ### Template Directives
188
+
189
+ #### Custom Directives
190
+
191
+ STX supports defining your own custom directives for template processing:
192
+
193
+ ```ts
194
+ import type { CustomDirective } from 'bun-plugin-stx'
195
+ // Configure custom directives
196
+ import stxPlugin from 'bun-plugin-stx'
197
+
198
+ // Create custom directives
199
+ const uppercaseDirective: CustomDirective = {
200
+ name: 'uppercase',
201
+ handler: (content, params) => {
202
+ return params[0] ? params[0].toUpperCase() : content.toUpperCase()
203
+ },
204
+ // No hasEndTag needed for single-parameter directives
205
+ }
206
+
207
+ const wrapDirective: CustomDirective = {
208
+ name: 'wrap',
209
+ handler: (content, params) => {
210
+ const className = params[0] || 'default-wrapper'
211
+ return `<div class="${className}">${content}</div>`
212
+ },
213
+ hasEndTag: true, // This directive requires an end tag (@wrap...@endwrap)
214
+ }
215
+
216
+ // Register custom directives
217
+ await build({
218
+ entrypoints: ['./src/index.ts', './templates/home.stx'],
219
+ outdir: './dist',
220
+ plugins: [stxPlugin],
221
+ stx: {
222
+ customDirectives: [uppercaseDirective, wrapDirective],
223
+ },
224
+ })
225
+ ```
226
+
227
+ Then use them in your templates:
228
+
229
+ ```html
230
+ <!-- Single-parameter directive -->
231
+ <p>@uppercase('hello world')</p>
232
+
233
+ <!-- Block directive with content and optional parameter -->
234
+ @wrap(highlight)
235
+ <p>This content will be wrapped in a div with class "highlight"</p>
236
+ @endwrap
237
+ ```
238
+
239
+ Custom directives have access to:
240
+
241
+ - `content`: The content between start and end tags (for block directives)
242
+ - `params`: Array of parameters passed to the directive
243
+ - `context`: The template data context (all variables)
244
+ - `filePath`: The current template file path
245
+
246
+ #### Variables
247
+
248
+ Display content with double curly braces:
249
+
250
+ ```html
251
+ <h1>{{ title }}</h1>
252
+ <p>{{ user.name }}</p>
253
+ ```
254
+
255
+ #### Conditionals
256
+
257
+ Use `@if`, `@elseif`, and `@else` for conditional rendering:
258
+
259
+ ```html
260
+ @if (user.isAdmin)
261
+ <div class="admin-panel">Admin content</div>
262
+ @elseif (user.isEditor)
263
+ <div class="editor-tools">Editor tools</div>
264
+ @else
265
+ <div class="user-view">Regular user view</div>
266
+ @endif
267
+ ```
268
+
269
+ #### Loops
270
+
271
+ Iterate over arrays with `@foreach`:
272
+
273
+ ```html
274
+ <ul>
275
+ @foreach (items as item)
276
+ <li>{{ item }}</li>
277
+ @endforeach
278
+ </ul>
279
+ ```
280
+
281
+ Use `@for` for numeric loops:
282
+
283
+ ```html
284
+ <ol>
285
+ @for (let i = 1; i <= 5; i++)
286
+ <li>Item {{ i }}</li>
287
+ @endfor
288
+ </ol>
289
+ ```
290
+
291
+ #### Raw HTML
292
+
293
+ Output unescaped HTML content:
294
+
295
+ ```html
296
+ {!! rawHtmlContent !!}
297
+ ```
298
+
299
+ #### Markdown Support
300
+
301
+ STX supports rendering Markdown content directly in your templates using the `@markdown` directive:
302
+
303
+ ```html
304
+ <div class="content">
305
+ @markdown
306
+ # Heading 1
307
+
308
+ This is a paragraph with **bold text** and *italic text*.
309
+
310
+ - List item 1
311
+ - List item 2
312
+ - List item 3
313
+
314
+ ```js
315
+ // Code block
316
+ function hello() {
317
+ console.log('Hello world');
318
+ }
319
+ ```
320
+
321
+ @endmarkdown
322
+ </div>
323
+ ```
324
+
325
+ You can also pass options to the markdown renderer:
326
+
327
+ ```html
328
+ <!-- Enable line breaks (converts single line breaks to <br>) -->
329
+ @markdown(breaks)
330
+ Line 1
331
+ Line 2
332
+ @endmarkdown
333
+
334
+ <!-- Disable GitHub Flavored Markdown -->
335
+ @markdown(no-gfm)
336
+ Content here
337
+ @endmarkdown
338
+ ```
339
+
340
+ ### Internationalization (i18n)
341
+
342
+ STX supports internationalization to help you build multilingual applications. Translation files are stored in YAML format (JSON also supported) and support nested keys and parameter replacements.
343
+
344
+ #### Configuration
345
+
346
+ Configure i18n in your build script:
347
+
348
+ ```js
349
+ import stxPlugin from 'bun-plugin-stx'
350
+
351
+ await build({
352
+ entrypoints: ['./templates/home.stx'],
353
+ outdir: './dist',
354
+ plugins: [stxPlugin],
355
+ stx: {
356
+ i18n: {
357
+ locale: 'en', // Current locale
358
+ defaultLocale: 'en', // Fallback locale
359
+ translationsDir: 'translations', // Directory containing translations
360
+ format: 'yaml', // Format of translation files (yaml, yml, json, or js)
361
+ fallbackToKey: true, // Use key as fallback when translation not found
362
+ cache: true // Cache translations in memory
363
+ }
364
+ }
365
+ })
366
+ ```
367
+
368
+ #### Translation Files
369
+
370
+ Create translation files in your translationsDir:
371
+
372
+ ```yaml
373
+ # translations/en.yaml
374
+ welcome: Welcome to STX
375
+ greeting: Hello, :name!
376
+ nav:
377
+ home: Home
378
+ about: About
379
+ contact: Contact
380
+ ```
381
+
382
+ ```yaml
383
+ # translations/de.yaml
384
+ welcome: Willkommen bei STX
385
+ greeting: Hallo, :name!
386
+ nav:
387
+ home: Startseite
388
+ about: Über uns
389
+ contact: Kontakt
390
+ ```
391
+
392
+ #### Using Translations
393
+
394
+ STX provides multiple ways to use translations in your templates:
395
+
396
+ 1. **@translate Directive**
397
+
398
+ ```html
399
+ <!-- Basic translation -->
400
+ <p>@translate('welcome')</p>
401
+
402
+ <!-- With parameters -->
403
+ <p>@translate('greeting', { "name": "John" })</p>
404
+
405
+ <!-- Nested keys -->
406
+ <p>@translate('nav.home')</p>
407
+
408
+ <!-- With fallback content -->
409
+ <p>@translate('missing.key')Fallback Content@endtranslate</p>
410
+ ```
411
+
412
+ 2. **Filter Syntax**
413
+
414
+ ```html
415
+ <!-- Basic translation as filter -->
416
+ <p>{{ 'welcome' | translate }}</p>
417
+
418
+ <!-- With parameters -->
419
+ <p>{{ 'greeting' | translate({ "name": "Alice" }) }}</p>
420
+
421
+ <!-- Short alias -->
422
+ <p>{{ 'nav.home' | t }}</p>
423
+ ```
424
+
425
+ Parameters in translations use the `:param` syntax, similar to Laravel:
426
+
427
+ ```yaml
428
+ greeting: Hello, :name!
429
+ items: You have :count items in your cart.
430
+ ```
431
+
432
+ Then in your template:
433
+
434
+ ```html
435
+ <p>@translate('greeting', { "name": "John" })</p>
436
+ <p>@translate('items', { "count": 5 })</p>
437
+ ```
438
+
439
+ ### Web Components Integration
440
+
441
+ STX now provides seamless integration with Web Components, allowing you to automatically build and use custom elements from your STX components.
442
+
443
+ #### Configuration
444
+
445
+ Enable web component integration in your build configuration:
446
+
447
+ ```ts
448
+ import { build } from 'bun'
449
+ import stxPlugin from 'bun-plugin-stx'
450
+
451
+ await build({
452
+ entrypoints: ['./templates/home.stx'],
453
+ outdir: './dist',
454
+ plugins: [stxPlugin],
455
+ config: {
456
+ stx: {
457
+ webComponents: {
458
+ enabled: true,
459
+ outputDir: 'dist/web-components',
460
+ components: [
461
+ {
462
+ name: 'MyButton', // Class name for the component
463
+ tag: 'my-button', // HTML tag name (must contain a hyphen)
464
+ file: 'components/button.stx', // Path to the STX component
465
+ attributes: ['type', 'text', 'disabled'] // Observed attributes
466
+ },
467
+ {
468
+ name: 'MyCard',
469
+ tag: 'my-card',
470
+ file: 'components/card.stx',
471
+ shadowDOM: true, // Use Shadow DOM (default: true)
472
+ template: true, // Use template element (default: true)
473
+ styleSource: 'styles/card.css', // Optional external stylesheet
474
+ attributes: ['title', 'footer']
475
+ }
476
+ ]
477
+ }
478
+ }
479
+ }
480
+ })
481
+ ```
482
+
483
+ #### Using Web Components in Templates
484
+
485
+ Include web components in your templates with the `@webcomponent` directive:
486
+
487
+ ```html
488
+ <!DOCTYPE html>
489
+ <html>
490
+ <head>
491
+ <title>Web Component Demo</title>
492
+
493
+ <!-- Include the web components -->
494
+ @webcomponent('my-button')
495
+ @webcomponent('my-card')
496
+ </head>
497
+ <body>
498
+ <h1>Web Components Demo</h1>
499
+
500
+ <!-- Use the custom elements -->
501
+ <my-button type="primary" text="Click Me"></my-button>
502
+
503
+ <my-card title="Card Title" footer="Card Footer">
504
+ This is the card content
505
+ </my-card>
506
+ </body>
507
+ </html>
508
+ ```
509
+
510
+ #### Source STX Components
511
+
512
+ The original STX components can be simple:
513
+
514
+ ```html
515
+ <!-- components/button.stx -->
516
+ <button class="btn {{ type ? 'btn-' + type : '' }}" {{ disabled ? 'disabled' : '' }}>
517
+ {{ text || slot }}
518
+ </button>
519
+
520
+ <!-- components/card.stx -->
521
+ <div class="card">
522
+ <div class="card-header">{{ title }}</div>
523
+ <div class="card-body">
524
+ {{ slot }}
525
+ </div>
526
+ <div class="card-footer">{{ footer }}</div>
527
+ </div>
528
+ ```
529
+
530
+ #### Advanced Options
531
+
532
+ Web components support several configuration options:
533
+
534
+ - `shadowDOM`: Enable/disable Shadow DOM (default: true)
535
+ - `template`: Use template element for better performance (default: true)
536
+ - `extends`: Extend a specific HTML element class
537
+ - `styleSource`: Path to external stylesheet
538
+ - `attributes`: List of attributes to observe for changes
539
+
540
+ ## TypeScript Support
541
+
542
+ STX includes TypeScript declarations for importing .stx files. Make sure your `tsconfig.json` includes the necessary configuration:
543
+
544
+ ```json
545
+ {
546
+ "compilerOptions": {
547
+ // ... your other options
548
+ "types": ["bun"]
549
+ },
550
+ "files": ["src/stx.d.ts"],
551
+ "include": ["**/*.ts", "**/*.d.ts", "*.stx", "./**/*.stx"]
552
+ }
553
+ ```
554
+
555
+ Create a declaration file (`src/stx.d.ts`):
556
+
557
+ ```ts
558
+ // Allow importing .stx files
559
+ declare module '*.stx';
560
+ ```
561
+
562
+ ## Server-Side Rendering Optimizations
563
+
564
+ STX supports advanced server-side rendering features including streaming and partial hydration.
565
+
566
+ ### Streaming Rendering
567
+
568
+ Streaming allows you to send parts of your HTML to the client as they become available, improving perceived performance and time-to-first-byte. This is especially useful for large pages with content that can be progressively rendered.
569
+
570
+ #### Basic Usage
571
+
572
+ STX templates can be rendered as streams using the `stream()` method:
573
+
574
+ ```ts
575
+ import { streamTemplate } from 'bun-plugin-stx'
576
+ import template from './templates/home.stx'
577
+
578
+ // Create a streaming response
579
+ const stream = await streamTemplate(template, {
580
+ title: "Streaming Example",
581
+ items: ["Apple", "Banana", "Cherry"]
582
+ })
583
+
584
+ // Use with Bun's server
585
+ Bun.serve({
586
+ port: 3000,
587
+ fetch(req) {
588
+ return new Response(stream, {
589
+ headers: { 'Content-Type': 'text/html' }
590
+ })
591
+ }
592
+ })
593
+ ```
594
+
595
+ #### Progressive Loading with Chunks
596
+
597
+ For more control, you can use the progressive loading API to manually control when chunks are sent:
598
+
599
+ ```ts
600
+ import { createStreamRenderer } from 'bun-plugin-stx'
601
+
602
+ // Create a stream renderer
603
+ const renderer = await createStreamRenderer('./templates/dashboard.stx')
604
+
605
+ // Use with Bun's server
606
+ Bun.serve({
607
+ port: 3000,
608
+ async fetch(req) {
609
+ // Create a new ReadableStream
610
+ const stream = new ReadableStream({
611
+ type: "direct",
612
+ async start(controller) {
613
+ // Render the page shell immediately
614
+ controller.write(await renderer.renderShell({
615
+ title: "Dashboard",
616
+ user: { name: "John" }
617
+ }))
618
+
619
+ // Simulate fetching data and stream sections as they're ready
620
+ controller.write(await renderer.renderSection('header'))
621
+
622
+ // Fetch data from database (simulated with delay)
623
+ const stats = await fetchStats() // async function that takes time
624
+ controller.write(await renderer.renderSection('stats', { stats }))
625
+
626
+ // Fetch more data in parallel
627
+ const [recentActivity, notifications] = await Promise.all([
628
+ fetchActivity(),
629
+ fetchNotifications()
630
+ ])
631
+
632
+ // Render final sections
633
+ controller.write(await renderer.renderSection('activity', { recentActivity }))
634
+ controller.write(await renderer.renderSection('notifications', { notifications }))
635
+
636
+ // Complete the stream
637
+ controller.close()
638
+ }
639
+ })
640
+
641
+ return new Response(stream, {
642
+ headers: { 'Content-Type': 'text/html' }
643
+ })
644
+ }
645
+ })
646
+ ```
647
+
648
+ #### Template Structure for Streaming
649
+
650
+ Organize your templates for streaming by using section comments:
651
+
652
+ ```html
653
+ <!DOCTYPE html>
654
+ <html>
655
+ <head>
656
+ <title>{{ title }}</title>
657
+ <meta charset="utf-8">
658
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
659
+ <link rel="stylesheet" href="/css/main.css">
660
+ <!-- Shell styles that should load immediately -->
661
+ <style>
662
+ .shell { display: flex; min-height: 100vh; }
663
+ .loading { opacity: 0.7; }
664
+ </style>
665
+ </head>
666
+ <body>
667
+ <div class="shell">
668
+ <!-- @section:header -->
669
+ <header>
670
+ <h1>{{ user.name }}'s Dashboard</h1>
671
+ </header>
672
+ <!-- @endsection:header -->
673
+
674
+ <main>
675
+ <!-- @section:stats -->
676
+ <div class="stats-panel">
677
+ <h2>Statistics</h2>
678
+ <div class="stats-grid">
679
+ @foreach (stats.items as stat)
680
+ <div class="stat-card">
681
+ <div class="stat-value">{{ stat.value }}</div>
682
+ <div class="stat-label">{{ stat.label }}</div>
683
+ </div>
684
+ @endforeach
685
+ </div>
686
+ </div>
687
+ <!-- @endsection:stats -->
688
+
689
+ <!-- @section:activity -->
690
+ <div class="activity-feed">
691
+ <h2>Recent Activity</h2>
692
+ <ul>
693
+ @foreach (recentActivity as activity)
694
+ <li class="activity-item">
695
+ <span class="activity-time">{{ activity.time }}</span>
696
+ <span class="activity-desc">{{ activity.description }}</span>
697
+ </li>
698
+ @endforeach
699
+ </ul>
700
+ </div>
701
+ <!-- @endsection:activity -->
702
+
703
+ <!-- @section:notifications -->
704
+ <div class="notifications-panel">
705
+ <h2>Notifications</h2>
706
+ <ul class="notification-list">
707
+ @foreach (notifications as notification)
708
+ <li class="notification {{ notification.read ? 'read' : 'unread' }}">
709
+ {{ notification.message }}
710
+ </li>
711
+ @endforeach
712
+ </ul>
713
+ </div>
714
+ <!-- @endsection:notifications -->
715
+ </main>
716
+ </div>
717
+
718
+ <script src="/js/dashboard.js"></script>
719
+ </body>
720
+ </html>
721
+ ```
722
+
723
+ ### Partial Hydration
724
+
725
+ STX supports partial hydration to minimize the amount of JavaScript sent to the client and improve performance. This technique allows you to selectively hydrate only the interactive parts of your page.
726
+
727
+ #### Configuration
728
+
729
+ Enable partial hydration in your STX configuration:
730
+
731
+ ```ts
732
+ // stx.config.ts
733
+ export default {
734
+ // ... other config
735
+ hydration: {
736
+ enabled: true,
737
+ mode: 'progressive', // 'progressive' or 'islands'
738
+ clientEntry: 'src/client.ts' // Path to client-side hydration entry
739
+ }
740
+ }
741
+ ```
742
+
743
+ #### Island Architecture
744
+
745
+ Define interactive islands in your templates with the `@island` directive:
746
+
747
+ ```html
748
+ <div class="product-page">
749
+ <h1>{{ product.name }}</h1>
750
+ <p>{{ product.description }}</p>
751
+
752
+ @island('product-gallery')
753
+ <div class="product-gallery">
754
+ @foreach (product.images as image)
755
+ <img src="{{ image.url }}" alt="{{ image.alt }}">
756
+ @endforeach
757
+ </div>
758
+ @endisland
759
+
760
+ <div class="product-details">
761
+ <p class="price">${{ product.price }}</p>
762
+
763
+ @island('add-to-cart')
764
+ <div class="add-to-cart-container">
765
+ <input type="number" min="1" value="1" id="quantity">
766
+ <button class="add-to-cart-btn">Add to Cart</button>
767
+ </div>
768
+ @endisland
769
+ </div>
770
+ </div>
771
+ ```
772
+
773
+ #### Client-Side Hydration
774
+
775
+ Create hydration handlers for your islands:
776
+
777
+ ```ts
778
+ // src/client.ts
779
+ import { hydrateIslands } from 'bun-plugin-stx/client'
780
+
781
+ // Register island handlers
782
+ hydrateIslands({
783
+ 'product-gallery': () => import('./islands/product-gallery'),
784
+ 'add-to-cart': () => import('./islands/add-to-cart')
785
+ })
786
+ ```
787
+
788
+ For each island, implement the hydration logic:
789
+
790
+ ```ts
791
+ // islands/add-to-cart.ts
792
+ export default function hydrate(el: HTMLElement, props: any) {
793
+ const quantityInput = el.querySelector('#quantity') as HTMLInputElement
794
+ const addButton = el.querySelector('.add-to-cart-btn') as HTMLButtonElement
795
+
796
+ addButton.addEventListener('click', () => {
797
+ const quantity = parseInt(quantityInput.value)
798
+
799
+ // Add to cart logic
800
+ console.log(`Adding ${quantity} items to cart`)
801
+ addToCart(props.productId, quantity)
802
+ })
803
+ }
804
+
805
+ function addToCart(productId: string, quantity: number) {
806
+ // API call to add item to cart
807
+ }
808
+ ```
809
+
810
+ This approach ensures that only the necessary JavaScript is loaded for interactive components, keeping the rest of your page lightweight and fast.
811
+
812
+ ### Async Data Fetching
813
+
814
+ STX supports async data fetching within templates for efficient server-side rendering:
815
+
816
+ ```html
817
+ <script>
818
+ // Async data fetching in template
819
+ export async function getProductData() {
820
+ const response = await fetch('https://api.example.com/products')
821
+ return await response.json()
822
+ }
823
+
824
+ // Use the data in the template
825
+ export const products = await getProductData()
826
+ </script>
827
+
828
+ <div class="product-list">
829
+ @foreach (products as product)
830
+ <div class="product-card">
831
+ <h3>{{ product.name }}</h3>
832
+ <p>{{ product.description }}</p>
833
+ </div>
834
+ @endforeach
835
+ </div>
836
+ ```
837
+
838
+ ## Example Server
839
+
840
+ Run a development server with your STX templates:
841
+
842
+ ```ts
843
+ // serve.ts
844
+ import home from './home.stx'
845
+
846
+ const server = Bun.serve({
847
+ routes: {
848
+ '/': home,
849
+ },
850
+ development: true,
851
+
852
+ fetch(req) {
853
+ return new Response('Not Found', { status: 404 })
854
+ },
855
+ })
856
+
857
+ console.log(`Listening on ${server.url}`)
858
+ ```
859
+
860
+ ## Testing This Plugin
861
+
862
+ To test the plugin with the included examples:
863
+
864
+ 1. Build the test file:
865
+
866
+ ```bash
867
+ bun run test-build.ts
868
+ ```
869
+
870
+ 2. Run the test server:
871
+
872
+ ```bash
873
+ bun run serve-test.ts
874
+ ```
875
+
876
+ 3. Open your browser to the displayed URL (typically `http://localhost:3000`).
877
+
878
+ ## How It Works
879
+
880
+ The plugin works by:
881
+
882
+ 1. Extracting script tags from .stx files
883
+ 2. Creating an execution context with variables from the script
884
+ 3. Processing Blade-like directives (@if, @foreach, etc.) into HTML
885
+ 4. Processing variable tags ({{ var }}) with their values
886
+ 5. Returning the processed HTML content
887
+
888
+ ## Testing
889
+
890
+ ```bash
891
+ bun test
892
+ ```
893
+
894
+ ## Changelog
895
+
896
+ Please see our [releases](https://github.com/stacksjs/stx/releases) page for more information on what has changed recently.
897
+
898
+ ## Contributing
899
+
900
+ Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
901
+
902
+ ## Community
903
+
904
+ For help, discussion about best practices, or any other conversation that would benefit from being searchable:
905
+
906
+ [Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
907
+
908
+ For casual chit-chat with others using this package:
909
+
910
+ [Join the Stacks Discord Server](https://discord.gg/stacksjs)
911
+
912
+ ## Postcardware
913
+
914
+ You will always be free to use any of the Stacks OSS software. We would also love to see which parts of the world Stacks ends up in. _Receiving postcards makes us happy—and we will publish them on our website._
915
+
916
+ Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
917
+
918
+ ## Sponsors
919
+
920
+ We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
921
+
922
+ - [JetBrains](https://www.jetbrains.com/)
923
+ - [The Solana Foundation](https://solana.com/)
924
+
925
+ ## Credits
926
+
927
+ Many thanks to the following core technologies & people who have contributed to this package:
928
+
929
+ - [Laravel](https://laravel.com)
930
+ - [Anthony Fu](https://github.com/antfu)
931
+ - [Chris Breuer](https://github.com/chrisbbreuer)
932
+ - [All Contributors](../../contributors)
933
+
934
+ ## License
935
+
936
+ The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stx/tree/main/LICENSE.md) for more information.
937
+
938
+ Made with 💙
939
+
940
+ <!-- Badges -->
941
+ [npm-version-src]: <https://img.shields.io/npm/v/bun-plugin-stx?style=flat-square>
942
+ [npm-version-href]: <https://npmjs.com/package/bun-plugin-stx>
943
+ [npm-downloads-src]: <https://img.shields.io/npm/dm/bun-plugin-stx?style=flat-square>
944
+ [npm-downloads-href]: <https://npmjs.com/package/bun-plugin-stx>
945
+ [github-actions-src]: <https://img.shields.io/github/actions/workflow/status/stacksjs/stx/ci.yml?style=flat-square&branch=main>
946
+ [github-actions-href]: <https://github.com/stacksjs/stx/actions?query=workflow%3Aci>
947
+
948
+ <!-- [codecov-src]: https://img.shields.io/codecov/c/gh/stacksjs/stx/main?style=flat-square
949
+ [codecov-href]: https://codecov.io/gh/stacksjs/stx -->
950
+
951
+ ## Documentation Generation
952
+
953
+ STX can automatically generate documentation for your components, templates, and directives. This helps developers understand your UI components and how to use them.
954
+
955
+ ### Command Line
956
+
957
+ Generate documentation using the CLI:
958
+
959
+ ```bash
960
+ # Generate markdown documentation (default)
961
+ stx docs
962
+
963
+ # Generate HTML documentation
964
+ stx docs --format html
965
+
966
+ # Generate JSON documentation
967
+ stx docs --format json
968
+
969
+ # Specify output directory
970
+ stx docs --output my-docs
971
+
972
+ # Only generate specific sections
973
+ stx docs --no-components
974
+ stx docs --no-templates
975
+ stx docs --no-directives
976
+
977
+ # Specify custom directories
978
+ stx docs --components-dir src/components --templates-dir src/views
979
+ ```
980
+
981
+ ### Configuration
982
+
983
+ You can configure documentation generation in your `stx.config.ts` file:
984
+
985
+ ```ts
986
+ export default {
987
+ // ...other config options
988
+ docs: {
989
+ enabled: true,
990
+ outputDir: 'docs',
991
+ format: 'markdown', // 'markdown', 'html', or 'json'
992
+ components: true,
993
+ templates: true,
994
+ directives: true,
995
+ extraContent: '## Getting Started\n\nThis is additional content to include in the documentation.',
996
+ },
997
+ };
998
+ ```
999
+
1000
+ ### Component Documentation
1001
+
1002
+ STX can extract component metadata from JSDoc comments in your component files:
1003
+
1004
+ ```html
1005
+ <!--
1006
+ Alert component for displaying messages to the user.
1007
+ This component supports different types (success, warning, error).
1008
+ -->
1009
+ <div class="alert alert-{{ type }}">
1010
+ <div class="alert-title">{{ title }}</div>
1011
+ <div class="alert-body">{{ message }}</div>
1012
+ </div>
1013
+
1014
+ <script>
1015
+ /**
1016
+ * The type of alert to display
1017
+ * @type {string}
1018
+ * @default "info"
1019
+ */
1020
+ const type = module.exports.type || "info";
1021
+
1022
+ /**
1023
+ * The alert title
1024
+ * @type {string}
1025
+ * @required
1026
+ */
1027
+ const title = module.exports.title;
1028
+
1029
+ /**
1030
+ * The alert message
1031
+ * @type {string}
1032
+ */
1033
+ const message = module.exports.message || "";
1034
+
1035
+ // Prepare the component's context
1036
+ module.exports = {
1037
+ type,
1038
+ title,
1039
+ message
1040
+ };
1041
+ </script>
1042
+ ```
1043
+
1044
+ This component will be documented with all its properties, types, default values, and requirements.
1045
+
1046
+ ### Web Component Documentation
1047
+
1048
+ STX will automatically document web components defined in your configuration:
1049
+
1050
+ ```ts
1051
+ export default {
1052
+ // ... other config
1053
+ webComponents: {
1054
+ enabled: true,
1055
+ outputDir: 'dist/web-components',
1056
+ components: [
1057
+ {
1058
+ name: 'MyButton',
1059
+ tag: 'my-button',
1060
+ file: 'components/button.stx',
1061
+ attributes: ['type', 'text', 'disabled'],
1062
+ description: 'A customizable button component'
1063
+ }
1064
+ ]
1065
+ }
1066
+ }
1067
+ ```
1068
+
1069
+ The documentation will include:
1070
+
1071
+ - Component name and description
1072
+ - Custom element tag
1073
+ - Observed attributes
1074
+ - Usage examples
1075
+
1076
+ This makes it easy for developers to understand how to use your web components in their HTML.