@stacksjs/stx
Version:
A Bun plugin that allows for using Laravel Blade-like syntax.
801 lines (603 loc) โข 17.7 kB
Markdown

[![npm version][npm-version-src]][npm-version-href]
[![GitHub Actions][github-actions-src]][github-actions-href]
[](http://commitizen.github.io/cz-cli/)
[![npm downloads][npm-downloads-src]][npm-downloads-href]
<!-- [![Codecov][codecov-src]][codecov-href] -->
# stx
A Blade-like template engine plugin for Bun, enabling simple and powerful templating with .stx files.
## Features
- ๐ฆ Laravel Blade-like syntax
- ๐ Fast and lightweight
- ๐ฆ Zero config
## Installation
```bash
bun add bun-plugin-stx
```
## Setup
Add the plugin to your `bunfig.toml`:
```toml
preload = [ "bun-plugin-stx" ]
# or as a serve plugin
[serve.static]
plugins = [ "bun-plugin-stx" ]
```
Or register the plugin in your build script:
```ts
import { build } from 'bun'
import stxPlugin from 'bun-plugin-stx'
await build({
entrypoints: ['./src/index.ts', './templates/home.stx'],
outdir: './dist',
plugins: [stxPlugin],
})
```
## Usage with ESM
### 1. Configure Bun to use the plugin
In your build script or Bun configuration:
```js
// build.js
import { build } from 'bun'
import stxPlugin from 'bun-plugin-stx'
await build({
entrypoints: ['./src/index.ts', './templates/home.stx'],
outdir: './dist',
plugins: [stxPlugin],
})
```
### 2. Import and use .stx files directly
You can import .stx files directly in your ESM code:
```js
// app.js
import homeTemplate from './templates/home.stx'
// Use the processed HTML content
document.body.innerHTML = homeTemplate
```
### 3. Use with Bun's server
You can serve .stx files directly with Bun's server:
```js
// server.js
import { serve } from 'bun'
import homeTemplate from './home.stx'
serve({
port: 3000,
fetch(req) {
return new Response(homeTemplate, {
headers: { 'Content-Type': 'text/html' }
})
}
})
```
Or use as route handlers:
```js
import about from './about.stx'
// server.js
import home from './home.stx'
export default {
port: 3000,
routes: {
'/': home,
'/about': about
}
}
```
## STX Template Syntax
STX templates use a syntax inspired by Laravel Blade. Templates can contain HTML with special directives for rendering dynamic content.
### Basic Example
```html
<html>
<head>
<title>STX Example</title>
<script>
// Define your data as an ESM export
export const title = "Hello World";
export const items = ["Apple", "Banana", "Cherry"];
export const showFooter = true;
</script>
</head>
<body>
<h1>{{ title }}</h1>
<ul>
@foreach (items as item)
<li>{{ item }}</li>
@endforeach
</ul>
@if (showFooter)
<footer>Copyright 2023</footer>
@endif
</body>
</html>
```
### Data Export Options
There are two ways to expose data in your STX templates:
#### 1. ESM exports (recommended)
```html
<script>
// Modern ESM named exports
export const title = "Hello World";
export const count = 42;
// Export functions
export function getFullName(first, last) {
return `${first} ${last}`;
}
// Export default object
export default {
items: ["Apple", "Banana", "Cherry"],
showDetails: true
};
</script>
```
#### 2. Legacy CommonJS (module.exports)
```html
<script>
// Legacy CommonJS exports
module.exports = {
title: "Hello World",
items: ["Apple", "Banana", "Cherry"],
showFooter: true
};
</script>
```
### Template Directives
#### Custom Directives
STX supports defining your own custom directives for template processing:
```ts
import type { CustomDirective } from 'bun-plugin-stx'
// Configure custom directives
import stxPlugin from 'bun-plugin-stx'
// Create custom directives
const uppercaseDirective: CustomDirective = {
name: 'uppercase',
handler: (content, params) => {
return params[0] ? params[0].toUpperCase() : content.toUpperCase()
},
// No hasEndTag needed for single-parameter directives
}
const wrapDirective: CustomDirective = {
name: 'wrap',
handler: (content, params) => {
const className = params[0] || 'default-wrapper'
return `<div class="${className}">${content}</div>`
},
hasEndTag: true, // This directive requires an end tag (@wrap...@endwrap)
}
// Register custom directives
await build({
entrypoints: ['./src/index.ts', './templates/home.stx'],
outdir: './dist',
plugins: [stxPlugin],
stx: {
customDirectives: [uppercaseDirective, wrapDirective],
},
})
```
Then use them in your templates:
```html
<!-- Single-parameter directive -->
<p>@uppercase('hello world')</p>
<!-- Block directive with content and optional parameter -->
@wrap(highlight)
<p>This content will be wrapped in a div with class "highlight"</p>
@endwrap
```
Custom directives have access to:
- `content`: The content between start and end tags (for block directives)
- `params`: Array of parameters passed to the directive
- `context`: The template data context (all variables)
- `filePath`: The current template file path
#### Variables
Display content with double curly braces:
```html
<h1>{{ title }}</h1>
<p>{{ user.name }}</p>
```
#### Conditionals
Use `@if`, `@elseif`, and `@else` for conditional rendering:
```html
@if (user.isAdmin)
<div class="admin-panel">Admin content</div>
@elseif (user.isEditor)
<div class="editor-tools">Editor tools</div>
@else
<div class="user-view">Regular user view</div>
@endif
```
#### Loops
Iterate over arrays with `@foreach`:
```html
<ul>
@foreach (items as item)
<li>{{ item }}</li>
@endforeach
</ul>
```
Use `@for` for numeric loops:
```html
<ol>
@for (let i = 1; i <= 5; i++)
<li>Item {{ i }}</li>
@endfor
</ol>
```
#### Raw HTML
Output unescaped HTML content:
```html
{!! rawHtmlContent !!}
```
#### Markdown Support
STX supports rendering Markdown content directly in your templates using the `@markdown` directive:
```html
<div class="content">
@markdown
# Heading 1
This is a paragraph with **bold text** and *italic text*.
- List item 1
- List item 2
- List item 3
```js
// Code block
function hello() {
console.log('Hello world');
}
```
@endmarkdown
</div>
```
You can also pass options to the markdown renderer:
```html
<!-- Enable line breaks (converts single line breaks to <br>) -->
@markdown(breaks)
Line 1
Line 2
@endmarkdown
<!-- Disable GitHub Flavored Markdown -->
@markdown(no-gfm)
Content here
@endmarkdown
```
### Internationalization (i18n)
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.
#### Configuration
Configure i18n in your build script:
```js
import stxPlugin from 'bun-plugin-stx'
await build({
entrypoints: ['./templates/home.stx'],
outdir: './dist',
plugins: [stxPlugin],
stx: {
i18n: {
locale: 'en', // Current locale
defaultLocale: 'en', // Fallback locale
translationsDir: 'translations', // Directory containing translations
format: 'yaml', // Format of translation files (yaml, yml, json, or js)
fallbackToKey: true, // Use key as fallback when translation not found
cache: true // Cache translations in memory
}
}
})
```
#### Translation Files
Create translation files in your translationsDir:
```yaml
# translations/en.yaml
welcome: Welcome to STX
greeting: Hello, :name!
nav:
home: Home
about: About
contact: Contact
```
```yaml
# translations/de.yaml
welcome: Willkommen bei STX
greeting: Hallo, :name!
nav:
home: Startseite
about: รber uns
contact: Kontakt
```
#### Using Translations
STX provides multiple ways to use translations in your templates:
1. **@translate Directive**
```html
<!-- Basic translation -->
<p>@translate('welcome')</p>
<!-- With parameters -->
<p>@translate('greeting', { "name": "John" })</p>
<!-- Nested keys -->
<p>@translate('nav.home')</p>
<!-- With fallback content -->
<p>@translate('missing.key')Fallback Content@endtranslate</p>
```
2. **Filter Syntax**
```html
<!-- Basic translation as filter -->
<p>{{ 'welcome' | translate }}</p>
<!-- With parameters -->
<p>{{ 'greeting' | translate({ "name": "Alice" }) }}</p>
<!-- Short alias -->
<p>{{ 'nav.home' | t }}</p>
```
Parameters in translations use the `:param` syntax, similar to Laravel:
```yaml
greeting: Hello, :name!
items: You have :count items in your cart.
```
Then in your template:
```html
<p>@translate('greeting', { "name": "John" })</p>
<p>@translate('items', { "count": 5 })</p>
```
### Web Components Integration
STX now provides seamless integration with Web Components, allowing you to automatically build and use custom elements from your STX components.
#### Configuration
Enable web component integration in your build configuration:
```ts
import { build } from 'bun'
import stxPlugin from 'bun-plugin-stx'
await build({
entrypoints: ['./templates/home.stx'],
outdir: './dist',
plugins: [stxPlugin],
config: {
stx: {
webComponents: {
enabled: true,
outputDir: 'dist/web-components',
components: [
{
name: 'MyButton', // Class name for the component
tag: 'my-button', // HTML tag name (must contain a hyphen)
file: 'components/button.stx', // Path to the STX component
attributes: ['type', 'text', 'disabled'] // Observed attributes
},
{
name: 'MyCard',
tag: 'my-card',
file: 'components/card.stx',
shadowDOM: true, // Use Shadow DOM (default: true)
template: true, // Use template element (default: true)
styleSource: 'styles/card.css', // Optional external stylesheet
attributes: ['title', 'footer']
}
]
}
}
}
})
```
#### Using Web Components in Templates
Include web components in your templates with the `@webcomponent` directive:
```html
<html>
<head>
<title>Web Component Demo</title>
<!-- Include the web components -->
@webcomponent('my-button')
@webcomponent('my-card')
</head>
<body>
<h1>Web Components Demo</h1>
<!-- Use the custom elements -->
<my-button type="primary" text="Click Me"></my-button>
<my-card title="Card Title" footer="Card Footer">
This is the card content
</my-card>
</body>
</html>
```
#### Source STX Components
The original STX components can be simple:
```html
<!-- components/button.stx -->
<button class="btn {{ type ? 'btn-' + type : '' }}" {{ disabled ? 'disabled' : '' }}>
{{ text || slot }}
</button>
<!-- components/card.stx -->
<div class="card">
<div class="card-header">{{ title }}</div>
<div class="card-body">
{{ slot }}
</div>
<div class="card-footer">{{ footer }}</div>
</div>
```
#### Advanced Options
Web components support several configuration options:
- `shadowDOM`: Enable/disable Shadow DOM (default: true)
- `template`: Use template element for better performance (default: true)
- `extends`: Extend a specific HTML element class
- `styleSource`: Path to external stylesheet
- `attributes`: List of attributes to observe for changes
## TypeScript Support
STX includes TypeScript declarations for importing .stx files. Make sure your `tsconfig.json` includes the necessary configuration:
```json
{
"compilerOptions": {
// ... your other options
"types": ["bun"]
},
"files": ["src/stx.d.ts"],
"include": ["**/*.ts", "**/*.d.ts", "*.stx", "./**/*.stx"]
}
```
Create a declaration file (`src/stx.d.ts`):
```ts
// Allow importing .stx files
declare module '*.stx';
```
## Example Server
Run a development server with your STX templates:
```ts
// serve.ts
import home from './home.stx'
const server = Bun.serve({
routes: {
'/': home,
},
development: true,
fetch(req) {
return new Response('Not Found', { status: 404 })
},
})
console.log(`Listening on ${server.url}`)
```
## Testing This Plugin
To test the plugin with the included examples:
1. Build the test file:
```bash
bun run test-build.ts
```
2. Run the test server:
```bash
bun run serve-test.ts
```
3. Open your browser to the displayed URL (typically `http://localhost:3000`).
## How It Works
The plugin works by:
1. Extracting script tags from .stx files
2. Creating an execution context with variables from the script
3. Processing Blade-like directives (@if, @foreach, etc.) into HTML
4. Processing variable tags ({{ var }}) with their values
5. Returning the processed HTML content
## Testing
```bash
bun test
```
## Changelog
Please see our [releases](https://github.com/stacksjs/stx/releases) page for more information on what has changed recently.
## Contributing
Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
## Community
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
[Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
For casual chit-chat with others using this package:
[Join the Stacks Discord Server](https://discord.gg/stacksjs)
## Postcardware
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._
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States ๐
## Sponsors
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.
- [JetBrains](https://www.jetbrains.com/)
- [The Solana Foundation](https://solana.com/)
## Credits
Many thanks to the following core technologies & people who have contributed to this package:
- [Laravel](https://laravel.com)
- [Anthony Fu](https://github.com/antfu)
- [Chris Breuer](https://github.com/chrisbbreuer)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stx/tree/main/LICENSE.md) for more information.
Made with ๐
<!-- Badges -->
[npm-version-src]: <https://img.shields.io/npm/v/bun-plugin-stx?style=flat-square>
[npm-version-href]: <https://npmjs.com/package/bun-plugin-stx>
[npm-downloads-src]: <https://img.shields.io/npm/dm/bun-plugin-stx?style=flat-square>
[npm-downloads-href]: <https://npmjs.com/package/bun-plugin-stx>
[github-actions-src]: <https://img.shields.io/github/actions/workflow/status/stacksjs/stx/ci.yml?style=flat-square&branch=main>
[github-actions-href]: <https://github.com/stacksjs/stx/actions?query=workflow%3Aci>
<!-- [codecov-src]: https://img.shields.io/codecov/c/gh/stacksjs/stx/main?style=flat-square
[codecov-href]: https://codecov.io/gh/stacksjs/stx -->
## Documentation Generation
STX can automatically generate documentation for your components, templates, and directives. This helps developers understand your UI components and how to use them.
### Command Line
Generate documentation using the CLI:
```bash
# Generate markdown documentation (default)
stx docs
# Generate HTML documentation
stx docs --format html
# Generate JSON documentation
stx docs --format json
# Specify output directory
stx docs --output my-docs
# Only generate specific sections
stx docs --no-components
stx docs --no-templates
stx docs --no-directives
# Specify custom directories
stx docs --components-dir src/components --templates-dir src/views
```
### Configuration
You can configure documentation generation in your `stx.config.ts` file:
```ts
export default {
// ...other config options
docs: {
enabled: true,
outputDir: 'docs',
format: 'markdown', // 'markdown', 'html', or 'json'
components: true,
templates: true,
directives: true,
extraContent: '## Getting Started\n\nThis is additional content to include in the documentation.',
},
};
```
### Component Documentation
STX can extract component metadata from JSDoc comments in your component files:
```html
<!--
Alert component for displaying messages to the user.
This component supports different types (success, warning, error).
-->
<div class="alert alert-{{ type }}">
<div class="alert-title">{{ title }}</div>
<div class="alert-body">{{ message }}</div>
</div>
<script>
/**
* The type of alert to display
* @type {string}
* @default "info"
*/
const type = module.exports.type || "info";
/**
* The alert title
* @type {string}
* @required
*/
const title = module.exports.title;
/**
* The alert message
* @type {string}
*/
const message = module.exports.message || "";
// Prepare the component's context
module.exports = {
type,
title,
message
};
</script>
```
This component will be documented with all its properties, types, default values, and requirements.
### Web Component Documentation
STX will automatically document web components defined in your configuration:
```ts
export default {
// ... other config
webComponents: {
enabled: true,
outputDir: 'dist/web-components',
components: [
{
name: 'MyButton',
tag: 'my-button',
file: 'components/button.stx',
attributes: ['type', 'text', 'disabled'],
description: 'A customizable button component'
}
]
}
}
```
The documentation will include:
- Component name and description
- Custom element tag
- Observed attributes
- Usage examples
This makes it easy for developers to understand how to use your web components in their HTML.