218 lines
12 KiB
Markdown
218 lines
12 KiB
Markdown
# @wrnexus/compiler
|
|
|
|
## Partial-static rendering
|
|
|
|
Pages can select `render = "partial-static"` and divide their view with `<Static>` and
|
|
`<Dynamic>` boundaries. The compiler emits a build-only shell renderer that never evaluates
|
|
dynamic-boundary children. `wrnexus build` expands static component mounts into
|
|
`dist/partial-shells.json`, records byte/region evidence in `build-report.json`, and embeds
|
|
the shell in the production route manifest. At request time the production runtime retains
|
|
request-aware layouts, locale/theme metadata and security nonces while streaming dynamic
|
|
regions into stable placeholders.
|
|
|
|
> 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.
|
|
|
|
Production adapters use `analyzeRuntimeImports` before bundling. Edge, worker,
|
|
service-worker, and browser targets reject Node filesystem, TCP, and process
|
|
modules with `WRN-RUNTIME-CAPABILITY`. Package manifests can declare supported
|
|
`wrnexus.runtimes` and required `wrnexus.requires` capabilities; discovery fails
|
|
when the selected deployment cannot satisfy them.
|
|
|
|
## Server actions
|
|
|
|
```wrn
|
|
action createUser using CreateUserSchema {
|
|
const user = await users.create(input)
|
|
invalidate("users")
|
|
return user
|
|
}
|
|
|
|
view {
|
|
<form @submit="createUser">...</form>
|
|
}
|
|
```
|
|
|
|
The compiler produces a schema-aware server registry, a fully inferred action
|
|
client, and progressively enhanced form metadata. The shared runtime performs
|
|
validation, authentication/permission checks, CSRF verification, serialization,
|
|
invalidation reporting, and browser lifecycle events.
|
|
|
|
## 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:
|
|
|
|
```wrn
|
|
import { appUrl } from "@wrnexus/helpers";
|
|
|
|
layout PublicLayout {
|
|
view {
|
|
<PublicHeader signInHref="{appUrl('sso', '/sign-in')}" />
|
|
}
|
|
}
|
|
```
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
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`).
|
|
|
|
### `compileWrnFile(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.
|
|
|
|
```ts
|
|
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 `LexError`s 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.
|
|
|
|
```ts
|
|
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`, `compileWrnFile`, `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:
|
|
|
|
```ts
|
|
import { compileWrnFile } from "@wrnexus/compiler";
|
|
|
|
const ts = compileWrnFile(`
|
|
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:
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
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.
|
|
- `@event name = function` inside `props` — declares a public component event. Emit it from component behavior with `name(detail)` or `$emit("name", detail)`, and consume it with `<Component @name="handler(event)" />`.
|
|
- `state <ident>: Type = <expr>` — typed reactive state seeded from a raw JS expression, including native array and object literals. The annotation is optional for backward compatibility.
|
|
- `view { <html> }` — plain HTML with `{expr}` interpolation in text and attributes, JSX-style component props such as `items={items}`, `items={[...]}`, and `options={{...}}`, hyphenated attributes, boolean attributes, `@event="..."` client bindings, and `<!-- comments -->`. Structured component props are serialized safely for SSR; expressions that reference `state` retain their initial value and update reactively in the browser.
|
|
- Client functions automatically commit state changed by `setTimeout` callbacks. For other deferred callbacks (observers, third-party APIs, or detached promise callbacks), call the injected `commit()` function after changing local state; returning/awaiting a promise also commits through the normal function boundary.
|
|
- `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*`/`__wrn*` helpers) — consume the output within a WrNexus app, e.g. via `@wrnexus/core`'s dev loader.
|