A powerful, type-safe templating library for TypeScript powered by Effect. It provides a seamless way to create and compose templates with built-in parameter validation, template nesting, and automatic type inference.
- π Type-safe template parameters
- π― Built-in parameter validation
- π Template composition and nesting
- π Automatic indentation handling
- β‘ Effect-based async template resolution
- π Streaming template rendering support
- π¨ Rich parameter type support (strings, numbers, dates, UUIDs, etc.)
- π§ Custom Schema support for anything that can be encoded as a string
npm install templeffect
# or
yarn add templeffect
# or
pnpm add templeffect
import * as T from 'templeffect'
import { Effect } from 'effect'
// Create a template with a typed parameter
const makeHtml = T.template('page')`<html>
<body>
<h1>Hello, ${T.param('name')}!</h1>
</body>
</html>`
// Use the template with type checking
Effect.runPromise(makeHtml({ name: 'world' }))
.then(result => console.log(result.output))
Creates a new template with the given name. The template supports parameter interpolation and type checking.
const greeting = T.template('greeting')`Hello, ${T.param('name')}!`
Creates a new template that automatically removes common leading whitespace from every line, making template definitions more readable.
const html = T.dedent('page')`
<html>
<body>
<h1>${T.param('title')}</h1>
</body>
</html>`
Creates a string parameter.
T.param('name') // Type: string
Creates a number parameter that parses from string.
T.number('age') // Type: number
Creates a boolean parameter that parses from string.
T.boolean('isActive') // Type: boolean
Creates an integer parameter that parses from string.
T.integer('count') // Type: number (integer)
Creates a UUID parameter with validation.
T.uuid('id') // Type: string (UUID format)
Creates a ULID parameter with validation.
T.ulid('id') // Type: string (ULID format)
Creates a Date parameter.
T.date('timestamp') // Type: Date
Creates a BigInt parameter.
T.bigInt('largeNumber') // Type: bigint
Creates a BigDecimal parameter.
T.bigDecimal('precise') // Type: bigdecimal
Creates a Duration parameter that handles time spans.
T.duration('timeout') // Type: Duration
Creates a parameter that handles JSON data with optional pretty printing.
T.json('data', 2) // Type: unknown (parsed JSON)
Templates can be nested within other templates:
const header = T.template('header')`<h1>${T.param('title')}</h1>`
const page = T.template('page')`
<html>
<body>
${header}
<p>${T.param('content')}</p>
</body>
</html>`
// Usage
page({
header: { title: 'Welcome' },
content: 'Hello world!'
})
Templates can be streamed for efficient processing of large templates or real-time output:
import { Stream } from 'effect'
const largeTemplate = T.template('large')`
<div>
<h1>${T.param('title')}</h1>
${T.param('content')}
</div>`
// Access the streaming version of the template
const stream = largeTemplate.stream({
title: 'Large Content',
content: '...'
})
// Process the stream in chunks
Stream.runForEach(
stream,
(chunk) => console.log(chunk), // Each chunk is a string
)
The streaming implementation:
- Processes template parts incrementally
- Maintains proper indentation across chunks
- Handles both static and dynamic content
- Preserves template composition
- Supports dedentation with
T.dedent
Example with dedentation and streaming:
const indentedTemplate = T.dedent('page')`
<html>
<body>
<h1>${T.param('title')}</h1>
<div>
${T.param('content')}
</div>
</body>
</html>`
// Stream with proper indentation
const stream = indentedTemplate.stream({
title: 'Streaming Demo',
content: 'Multi\nLine\nContent'
})
// Each chunk will maintain proper indentation
Stream.runForEach(stream, console.log)
Templates can directly use Effect values:
const dynamic = T.template('dynamic')`
<div>
${Effect.succeed('Dynamic content')}
</div>`
MIT