Files
WRNexusJS/packages/compiler
2026-07-22 12:56:07 +05:30
..
2026-07-22 01:53:18 +05:30
2026-07-22 01:53:18 +05:30
2026-07-22 12:56:07 +05:30
2026-07-12 15:55:18 +05:30

@wrnexus/compiler

Compiler for the .wrn language — tokenizes, parses, and lowers .wrn page and component files to TypeScript.

Part of the WrNexus framework — an SSR-first, Bun-native full-stack web framework.

Overview

@wrnexus/compiler turns .wrn source into TypeScript that targets the framework's runtime primitives. A .wrn file declares either a page (a route) or a component (a reusable, prop-driven fragment) with blocks for state, view (plain HTML), seo, style, functions, api, ssr/client data bindings, and realtime websocket handlers. The pipeline is source → Lexer → parse() → PageAst → generate() → TypeScript. It is a build/server-side library — the WrNexus dev loader calls it to compile .wrn files on the fly, surfacing ParseError as a readable error page.

Static ES module imports may appear before the root declaration. Imported values are available to server-rendered expressions, including component props:

import { appUrl } from "@wrnexus/helpers";

layout PublicLayout {
  view {
    <PublicHeader signInHref="{appUrl('sso', '/sign-in')}" />
  }
}

Installation

bun add @wrnexus/compiler

Private package — the machine must be authenticated to the wrnexus npm org (a read token in ~/.npmrc). Requires Bun (Node is not supported).

API

All exports come from the package root (@wrnexus/compiler).

compileWireFile(source: string): string

Compile .wrn source to a TypeScript module string. Throws ParseError on invalid input. The output is prefixed with a // compiled from .wrn comment.

compile(source: string): CompileResult

Richer entry point that returns the generated code, the AST, and any diagnostics.

interface CompileResult {
  code: string;
  ast: PageAst;
  diagnostics: string[];
}

On a ParseError it pushes the message into diagnostics and re-throws.

parse(source: string): PageAst

Run the lexer + recursive-descent parser and return the AST. Throws ParseError (lexer LexErrors are caught and rethrown as ParseError).

generate(ast: PageAst): string

Lower a PageAst to TypeScript. page ASTs become a default-export page component (plus meta, optional layout, __wrnexusApi/method handlers, websocket, and SSR/CSR data bindings); component ASTs become a module exporting render(props) and __wrnexusComponent.

Lexer

On-demand lexer for .wrn. Yields structural tokens and exposes raw-span readers for the parser.

class Lexer {
  pos: number;
  constructor(src: string);
  next(): Token; // consume next structural token
  peek(): Token; // look ahead without consuming
  readPath(): string; // route path, e.g. /users/[id]
  readToLineEnd(): string; // rest of line (state/prop initializers)
  readBalancedBraces(): string; // inner text of a { ... } block, string-aware
}

Token is { type: TokenType; value: string; pos: number }, where TokenType is one of ident, string, lbrace, rbrace, lparen, rparen, at, eq, comma, eof.

Errors

Class Thrown by Meaning
ParseError parse, compile, compileWireFile, generate Invalid .wrn grammar or (rewrapped) lex failure.
LexError Lexer Unexpected character / unterminated string / unbalanced braces.

AST types

Exported type-only symbols describing the parsed tree:

Type Description
PageAst Root node including top-level imports, kind, name, types, typed props, typed states, view, styles, functions, data APIs, lifecycle, and routes.
ViewNode { type: "text"; value } or { type: "element"; tag; attrs; children }.
Attr { name; value; event; boolean? }event marks @event bindings.
StateDecl { name; valueType?; expr } — a typed state x: Type = <expr> declaration.
PropDecl { name; valueType?; required; default } — a typed prop declaration.
SeoBlock Record<string, string> from the seo { ... } block.
ApiBlock { method; path; body } — a top-level api METHOD /path { ... }.
DataApiBlock { mode; name; method; path; body } — an api inside an ssr/client block.
DataMode "ssr" | "client".
ModeFunctionsBlock { mode; body } — a functions { ... } inside an ssr/client block.
RealtimeBlock { name; handlers } — a realtime <name> { on evt(args) { ... } } block.

Usage

Compile a page:

import { compileWireFile } from "@wrnexus/compiler";

const ts = compileWireFile(`
page Home {
  state count = 0
  seo { title = "Home" description = "Welcome" }
  view {
    <button @click="count++">Clicked {count} times</button>
  }
}
`);
// ts is a TypeScript module: exports `meta`, and a default page component
// returning an HTML string, wrapped in a data-scope for the reactive runtime.

Inspect the AST and diagnostics:

import { compile, ParseError } from "@wrnexus/compiler";

try {
  const { code, ast, diagnostics } = compile(source);
  console.log(ast.kind, ast.name, ast.states.length);
} catch (err) {
  if (err instanceof ParseError) console.error(err.message);
}

Drive the parse/codegen stages directly:

import { parse, generate } from "@wrnexus/compiler";

const ast = parse(componentSource); // ast.kind === "component"
const module = generate(ast); // exports render(props) + __wrnexusComponent

Use the lexer standalone:

import { Lexer } from "@wrnexus/compiler";

const lx = new Lexer("page Home {");
lx.next(); // { type: "ident", value: "page", pos: 0 }
lx.next(); // { type: "ident", value: "Home", pos: 5 }
lx.next(); // { type: "lbrace", value: "{", pos: 10 }

The .wrn language (as parsed)

A file opens with page <Name> or component <Name> followed by a { ... } body containing zero or more members:

  • layout = "<name>" — selects app/layouts/<name>.wrn (pages only).
  • types { <TypeScript declarations> } — reusable interfaces and aliases for the current file.
  • props { name: Type = <default> ... } — typed component props. Omit = <default> to make a prop required. Legacy inferred props remain supported.
  • state <ident>: Type = <expr> — typed reactive state seeded from a raw JS expression. The annotation is optional for backward compatibility.
  • view { <html> } — plain HTML with {expr} interpolation in text and attributes, hyphenated attributes, boolean attributes, @event="..." client bindings, and <!-- comments -->. Attribute expressions that reference state keep an SSR value and update reactively in the browser.
  • seo { key = "value" ... } — metadata merged into the generated meta.
  • style { <raw css> } — inlined page/component stylesheet (repeatable).
  • functions { <TypeScript> } — helpers with typed parameters and return values. Types remain in server output and are safely erased from browser behavior code.
  • api <METHOD> <path> { <raw js> } — route handler, lowered to a METHOD export (repeatable).
  • ssr { ... } / client { ... } — data blocks holding api <name> <METHOD> <path> { ... } bindings and their own functions { ... }.
  • realtime <name> { on <evt>(<args>) { <raw js> } ... } — websocket handlers, lowered to a websocket export.

view markup is parsed by a lenient dedicated HTML parser (parseHtmlView); HTML void elements (<br>, <img>, …) take no closing tag. Line comments (//) are skipped by the lexer.

Requirements / Notes

  • Pure TypeScript with no runtime dependencies; runs under Bun as part of the WrNexus toolchain (Node is not supported).
  • Generated modules target WrNexus runtime primitives (data-scope, data-text, data-on-*, data-for, data-component, __wrnexus*/__wire* helpers) — consume the output within a WrNexus app, e.g. via @wrnexus/core's dev loader.