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 templeffectimport * 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: stringCreates a number parameter that parses from string.
T.number('age') // Type: numberCreates a boolean parameter that parses from string.
T.boolean('isActive') // Type: booleanCreates 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: DateCreates a BigInt parameter.
T.bigInt('largeNumber') // Type: bigintCreates a BigDecimal parameter.
T.bigDecimal('precise') // Type: bigdecimalCreates a Duration parameter that handles time spans.
T.duration('timeout') // Type: DurationCreates 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