@pfeiferio/string-interpolate
Lightweight string interpolation with named placeholders and filter pipelines.
- No dependencies
- No template engine
- No expressions, no loops
- Built-in and custom filters
- Safe, test-driven parser
Installation
npm install @pfeiferio/string-interpolate
Basic Usage
import { interpolate } from '@pfeiferio/string-interpolate'
interpolate(
'Hello {{ user.name }}',
{ user: { name: 'Pascal' } }
)
// → "Hello Pascal"
Nested Placeholders (Dot Notation)
interpolate(
'ID: {{ order.customer.id }}',
{
order: {
customer: { id: 42 }
}
}
)
// → "ID: 42"
Selective Removal of Missing Placeholders
By default, unresolved placeholders are kept when keep=true.
With the remove option, you can explicitly remove selected placeholders when their value is missing, while keeping all others unchanged.
interpolate(
'Hello {{ user.name }} {{ user.email }}',
{
user: { name: 'Pascal' }
},
{
keep: true,
remove: ['user.email']
}
)
// → "Hello Pascal "
Behavior
removeentries act as prefix paths- All placeholders under the given pathare removed if their value is
undefined - Existing values are never removed
removeoverrideskeepfor the specified paths
interpolate(
'Hello {{ user.email }}',
{},
{
keep: true,
remove: ['user.email']
}
)
// → "Hello "
Partial matches do apply:
interpolate(
'Hello {{ user.email }}',
{},
{
keep: true,
remove: ['user']
}
)
// → "Hello "
Options Reference
interpolate(
template: string,
replacements: Record<string, unknown>,
options?: {
keep?: boolean
remove?: string[]
customFilters?: Record<string, FilterFunction>
}
): string
Filters
Filters are applied using a pipe syntax.
interpolate(
'{{ name | uppercase }}',
{ name: 'pascal' }
)
// → "PASCAL"
Multiple filters are applied left to right:
interpolate(
'{{ name | uppercase | truncate length=3 }}',
{ name: 'pascal' }
)
// → "PAS..."
Filter Arguments
Filters receive named arguments using key=value syntax.
interpolate(
'{{ text | truncate length=4 ending="..." }}',
{ text: 'abcdef' }
)
// → "abcd..."
Argument Rules
Arguments are key-value based
Values may be:
- numbers (
length=10) - booleans (
pretty=true) - strings (
suffix=!!!) - quoted strings with spaces (
ending="...")
- numbers (
No escaping inside quoted strings
Whitespace is used as argument separator
Filter Composition
Interpolation is single-pass. If a placeholder resolves to another placeholder expression, filters are not executed immediately. Instead, they are appended to the resulting placeholder expression.
Built-in Filters
| Filter | Description |
|---|---|
uppercase |
Convert string to upper case |
lowercase |
Convert string to lower case |
capitalize |
Capitalize first character |
append |
Append a suffix |
concat |
Concatenate text |
replace |
Replace all occurrences |
ltrim |
Trim characters from start |
rtrim |
Trim characters from end |
trim |
Trim characters from both sides |
truncate |
Shorten string |
default |
Fallback for undefined |
padStart |
Left-pad string |
padEnd |
Right-pad string |
json |
JSON stringify |
Example:
interpolate(
'{{ value | replace search=" " replace="_" }}',
{ value: 'hello world' }
)
// → "hello_world"
Custom Filters
You can provide custom filters via the options object.
interpolate(
'{{ name | reverse }}',
{ name: 'abc' },
{
customFilters: {
reverse: (value) =>
typeof value === 'string'
? value.split('').reverse().join('')
: value
}
}
)
// → "cba"
Custom filters override built-in filters with the same name.
Function Values
If a replacement value is a function, it will be executed:
interpolate(
'Now: {{ now }}',
{
now: () => new Date().toISOString()
}
)
API
interpolate(template, replacements, options?)
interpolate(
template: string,
replacements: Record<string, unknown>,
options?: {
keep?: boolean,
customFilters?: Record<string, FilterFunction>
}
): string
Design Goals
- Explicit syntax
- Predictable behavior
- No hidden magic
- Easy to extend
- Safe defaults
This package is not intended to be a full template engine.
License
MIT