API reference
Lips
import Lips from '@lipsjs/lips'
const lips = new Lips( config? )
Config
interface LipsConfig {
debug?: boolean
context?: Record<string, any>
/** 'compiled' (default) uses Function; 'interpreted' is CSP-safe */
mode?: 'compiled' | 'interpreted'
}
Registry
| Method | Returns | Description |
|---|---|---|
register( name, template ) |
Lips |
make a component available by tag name |
unregister( name ) |
Lips |
remove it |
has( name ) |
boolean |
is a name registered |
Lookup is lazy, so registration order does not matter.
Rendering
| Method | Returns | Description |
|---|---|---|
render( name, template, input? ) |
Component |
create an instance; name is also the stylesheet scope |
root( template, selector ) |
Component |
render and append in one call, tracked for dispose() |
dispose() |
void |
destroy the root created by root() |
Context
| Method | Returns | Description |
|---|---|---|
getContext() |
object |
the reactive context store |
setContext( key, value ) |
void |
set one field |
setContext( object ) |
void |
merge several fields |
watchContext( fields, fn ) |
() => void |
run fn when any listed field changes |
useContext( fields, fn ) |
() => void |
same, but fn receives the current values |
Internationalization
| Method | Returns | Description |
|---|---|---|
setLanguage( lang ) |
void |
switch language; re-renders translated bindings |
getLanguage() |
string |
current language |
useTranslator( support, fn ) |
() => void |
run fn on language change; support is a list or '*' |
i18n |
I18N |
the dictionary API |
Component
The handle returned by render() and root().
| Member | Type | Description |
|---|---|---|
state |
object |
the reactive state store |
node |
Element[] |
live root elements |
appendTo( target ) |
Component |
append into a selector or element |
swap( ir ) |
SwapReport |
replace the template, preserving state |
destroy() |
void |
tear down effects, listeners, styles and DOM |
on( event, fn ) |
Component |
subscribe |
once( event, fn ) |
Component |
subscribe for one emission |
off( event, fn? ) |
Component |
remove one listener, or all for the event |
emit( event, ...args ) |
void |
emit |
Component self
this inside a handler.
| Member | Type | Description |
|---|---|---|
state |
object |
reactive state |
input |
object |
inputs from the parent |
static |
object |
the template’s _static |
context |
object |
shared context |
node |
Element[] |
live root elements |
emit( event, ...args ) |
void |
emit to the parent’s on-* and to subscribers |
on / once / off |
self |
the component’s own event bus |
setContext( key, value ) |
void |
write shared context |
Plus every method declared in handler.
Lifecycle hooks
| Hook | When |
|---|---|
onCreate() |
instance created, before first render |
onInput( input ) |
initial input received |
onMount() |
first render complete |
onRender() |
after every render |
onUpdate() |
after a state/input driven update |
onAttach() |
nodes became live in the document |
onDetach() |
nodes were removed |
onContext() |
a declared context field changed |
onError( error ) |
a binding or handler threw |
onDestroy() |
instance torn down |
Lifecycle events
Emitted on the component’s own bus: component:mount, component:attached,
component:detached, component:destroy.
Reserved handler names — using one throws at registration: state, input, static,
context, emit, on, once, off, node, destroy, appendTo, prependTo,
replaceWith, render, swap.
I18N
lips.i18n.setDictionary( id, dictionary )
lips.i18n.translate( text, lang? ) // → { text, lang }
lips.i18n.format( reference, params, locale? ) // → string
lips.i18n.lang // get / set
Dictionary ids are the language part of a locale — fr serves fr, fr-FR and fr-CA.
Compiler exports
import {
compileTemplate, parseTemplate, parseSFC,
signal, effect, untrack, reactive
} from '@lipsjs/lips'
| Export | Signature | Description |
|---|---|---|
compileTemplate |
( src, options? ) => { ir, diagnostics } |
template source → IR |
parseTemplate |
( src ) => { root, diagnostics } |
template source → AST |
parseSFC |
( src ) => { script, root, diagnostics } |
split and parse a .lips file |
signal |
( value ) => [ read, write, touch ] |
a single reactive value |
effect |
( fn ) => { dispose } |
run and re-run on dependency change |
untrack |
( fn ) => T |
read without subscribing |
reactive |
( obj, deep? ) => Proxy |
per-key reactive store |
CompileOptions
interface CompileOptions {
/** <macro> definitions, inlined at call sites */
macros?: string
}
Precompile exports
import { precompile, serializeIR, lipsPlugin } from '@lipsjs/lips/precompile'
| Export | Signature | Description |
|---|---|---|
precompile |
( template ) => { template, diagnostics } |
authoring object → { ir, … } |
serializeIR |
( ir ) => string |
JSON for embedding in generated source |
lipsPlugin |
( options? ) => Plugin |
Vite/Rollup transform for .lips files |
interface PluginOptions {
include?: RegExp // default /\.lips$/
reportDiagnostics?: boolean // default true
}
Types
Template
type Template = {
default?: string
ir?: TemplateIR
state?: object
_static?: object
context?: string[]
macros?: string
handler?: Handler
stylesheet?: string
}
TemplateDiagnostic
interface TemplateDiagnostic {
code: string // e.g. 'LIPS-C013'
severity: 'error' | 'warning'
message: string
hint?: string
loc: { line: number, col: number, offset: number, length: number }
}
Diagnostics are returned, never thrown — you always get an IR plus a list of problems.
SwapReport
interface SwapReport {
changes: { kind: 'skeleton' | 'binds' | 'block', path: string }[]
/** names of component instances carried across instead of remounted */
salvaged: string[]
}
Metavars
For typing a component in TypeScript:
import type { Metavars, Template } from '@lipsjs/lips'
type Input = { userId: number }
type State = { name: string, loading: boolean }
type Static = { api: string }
type Context = { theme: string }
type Profile = Metavars<Input, State, Static, Context>
const profile: Template<Profile> = {
state: { name: '', loading: false },
_static: { api: '/api' },
context: [ 'theme' ],
handler: {
async onMount(){
this.state.loading = true
const res = await fetch(`${this.static.api}/users/${this.input.userId}`)
this.state.name = ( await res.json() ).name
this.state.loading = false
}
},
default: `
<if( state.loading )><p>Loading…</p></if>
<else><h2 class="theme-{context.theme}">{state.name}</h2></else>`
}
Diagnostic codes
Parser codes are LIPS-P###, compiler codes LIPS-C###. Frequently seen:
| Code | Meaning |
|---|---|
LIPS-C009 |
recursive macro — use a component instead |
LIPS-C011 |
<macro> is missing its name attribute |
LIPS-C013 |
spread on <let>/<const>; bind the object to one name instead |
Every diagnostic carries line, col and often a hint.