{ } Lips v0.2.0

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.