feat(wrn): support native structured prop expressions
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -28,18 +28,18 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0",
|
||||
"@wrnexus/router": "^0.5.0",
|
||||
"@wrnexus/csr": "^0.5.0",
|
||||
"@wrnexus/compiler": "^0.5.0",
|
||||
"@wrnexus/styles": "^0.5.0",
|
||||
"@wrnexus/dev-server": "^0.5.0",
|
||||
"@wrnexus/ui": "^0.5.0",
|
||||
"@wrnexus/validation": "^0.5.0",
|
||||
"@wrnexus/i18n": "^0.5.0",
|
||||
"@wrnexus/db": "^0.5.0",
|
||||
"@wrnexus/plugin": "^0.5.0",
|
||||
"@wrnexus/syntax": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1",
|
||||
"@wrnexus/router": "^0.5.1",
|
||||
"@wrnexus/csr": "^0.5.1",
|
||||
"@wrnexus/compiler": "^0.5.1",
|
||||
"@wrnexus/styles": "^0.5.1",
|
||||
"@wrnexus/dev-server": "^0.5.1",
|
||||
"@wrnexus/ui": "^0.5.1",
|
||||
"@wrnexus/validation": "^0.5.1",
|
||||
"@wrnexus/i18n": "^0.5.1",
|
||||
"@wrnexus/db": "^0.5.1",
|
||||
"@wrnexus/plugin": "^0.5.1",
|
||||
"@wrnexus/syntax": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -164,8 +164,8 @@ A file opens with `page <Name>` or `component <Name>` followed by a `{ ... }` bo
|
||||
- `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. 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.
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "^0.5.0"
|
||||
"@wrnexus/syntax": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -25,20 +25,20 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0",
|
||||
"@wrnexus/dev-toolbar": "^0.5.0",
|
||||
"@wrnexus/router": "^0.5.0",
|
||||
"@wrnexus/ssr": "^0.5.0",
|
||||
"@wrnexus/csr": "^0.5.0",
|
||||
"@wrnexus/compiler": "^0.5.0",
|
||||
"@wrnexus/styles": "^0.5.0",
|
||||
"@wrnexus/ui": "^0.5.0",
|
||||
"@wrnexus/validation": "^0.5.0",
|
||||
"@wrnexus/i18n": "^0.5.0",
|
||||
"@wrnexus/db": "^0.5.0",
|
||||
"@wrnexus/pubsub": "^0.5.0",
|
||||
"@wrnexus/uploader": "^0.5.0",
|
||||
"@wrnexus/plugin": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1",
|
||||
"@wrnexus/dev-toolbar": "^0.5.1",
|
||||
"@wrnexus/router": "^0.5.1",
|
||||
"@wrnexus/ssr": "^0.5.1",
|
||||
"@wrnexus/csr": "^0.5.1",
|
||||
"@wrnexus/compiler": "^0.5.1",
|
||||
"@wrnexus/styles": "^0.5.1",
|
||||
"@wrnexus/ui": "^0.5.1",
|
||||
"@wrnexus/validation": "^0.5.1",
|
||||
"@wrnexus/i18n": "^0.5.1",
|
||||
"@wrnexus/db": "^0.5.1",
|
||||
"@wrnexus/pubsub": "^0.5.1",
|
||||
"@wrnexus/uploader": "^0.5.1",
|
||||
"@wrnexus/plugin": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.5.0"
|
||||
"@wrnexus/native": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/plugin — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "^0.5.0"
|
||||
"@wrnexus/syntax": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,8 +21,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.5.0",
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/compiler": "^0.5.1",
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,9 +21,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.5.0",
|
||||
"@wrnexus/core": "^0.5.0",
|
||||
"@wrnexus/plugin": "^0.5.0"
|
||||
"@wrnexus/uploader": "^0.5.1",
|
||||
"@wrnexus/core": "^0.5.1",
|
||||
"@wrnexus/plugin": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/syntax — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -26,7 +26,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.5.0"
|
||||
"@wrnexus/core": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -15,14 +15,20 @@ WRNexusJS 0.3 keeps all previously supported declarations, including:
|
||||
- `data-for`, `{#each}`, conditions, interpolations, events, and `class:*`
|
||||
|
||||
New syntax is additive. Existing projects do not need to adopt it immediately.
|
||||
Dynamic attribute expressions should be single-quoted so braces remain unambiguous:
|
||||
Since WRNexusJS 0.5.1, dynamic component props may use JSX-style unquoted
|
||||
expressions. Quoted expression attributes remain supported for compatibility:
|
||||
|
||||
```wrn
|
||||
<FeatureList items='{items}' primaryAction='{{ label: "Open" }}' />
|
||||
state items = [{"label":"Accessibility","href":"/accessibility"}]
|
||||
|
||||
<FeatureList items={items} />
|
||||
<FeatureList items={[{"label":"Accessibility","href":"/accessibility"}]} />
|
||||
<FeatureList primaryAction={{"label":"Open","href":"/open"}} />
|
||||
<FeatureList items="{items}" />
|
||||
```
|
||||
|
||||
The 0.3 updater only rewrites simple, unambiguous `prop={expression}` values. Nested
|
||||
brace expressions are left unchanged and reported for manual review.
|
||||
Expression-valued component props serialize arrays and objects as JSON while preserving
|
||||
booleans, numbers, and strings. Literal HTML attributes continue to require quotes.
|
||||
|
||||
## File structure
|
||||
|
||||
@@ -76,9 +82,12 @@ props {
|
||||
}
|
||||
|
||||
state open: boolean = false
|
||||
state links = [{"label":"Home","href":"/"}]
|
||||
state options = {"dense":true}
|
||||
```
|
||||
|
||||
A prop without an initializer is required. State always requires an initializer.
|
||||
State initializers are JavaScript expressions, including native arrays and objects.
|
||||
Typed initializers are validated when their literal type can be determined.
|
||||
|
||||
## Computed values and effects
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wrnexus",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "An SSR-first full-stack web framework with server-rendered reactive components. Bun-first, Node-friendly.",
|
||||
@@ -25,7 +25,7 @@
|
||||
"lint:fix": "eslint . --fix",
|
||||
"format": "prettier . --write",
|
||||
"format:check": "prettier . --check",
|
||||
"check": "bun run typecheck && bun run lint && bun run test && bun run format:check",
|
||||
"check": "bun run typecheck \u0026\u0026 bun run lint \u0026\u0026 bun run test \u0026\u0026 bun run format:check",
|
||||
"verify:0.3": "node scripts/verify-0.3.mjs",
|
||||
"verify:0.4": "node scripts/verify-0.4.mjs",
|
||||
"validate:0.4": "node scripts/validate-0.4.mjs",
|
||||
@@ -44,7 +44,7 @@
|
||||
"typescript-eslint": "latest"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.3.0"
|
||||
"bun": "\u003e=1.3.0"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.28.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/auth",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"description": "Complete authentication, account security, MFA, passkeys, recovery, devices, risk, and audit system for WRNexusJS.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@@ -36,7 +36,7 @@
|
||||
"scripts": {
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"check": "bun run typecheck && bun run test"
|
||||
"check": "bun run typecheck \u0026\u0026 bun run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/captcha",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"description": "First-class CAPTCHA challenges, providers, verification guards, page gates, and WRNexusJS UI.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@@ -38,7 +38,7 @@
|
||||
"scripts": {
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"check": "bun run typecheck && bun run test"
|
||||
"check": "bun run typecheck \u0026\u0026 bun run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1094,6 +1094,15 @@ const MIGRATIONS: Migration[] = [
|
||||
// Package, component, and development-runtime improvements apply automatically.
|
||||
},
|
||||
},
|
||||
{
|
||||
version: "0.5.1",
|
||||
id: "native-wrn-json-expressions",
|
||||
description:
|
||||
"Adds native array and object state initializers plus JSX-style expression props in WRN component markup.",
|
||||
apply() {
|
||||
// Syntax and compiler support are package-managed; existing applications remain compatible.
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/** Release tooling uses this to require an explicit migration entry per version. */
|
||||
|
||||
@@ -164,8 +164,8 @@ A file opens with `page <Name>` or `component <Name>` followed by a `{ ... }` bo
|
||||
- `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. 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.
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -464,6 +464,28 @@ component ParentCard {
|
||||
expect(output).toContain("function __wireProp");
|
||||
});
|
||||
|
||||
test("native array and object props serialize through component mounts", async () => {
|
||||
const mod = await compileAndImport(`
|
||||
component NativeProps {
|
||||
state items = [{"label":"Home","href":"/"}]
|
||||
view {
|
||||
<Navbar items={items} options={{"dense":true}} />
|
||||
<Sidebar items={[{"label":"Cases","href":"/cases"}]} />
|
||||
}
|
||||
}
|
||||
`);
|
||||
const render = mod.render as (props?: Record<string, unknown>) => string;
|
||||
const html = render();
|
||||
|
||||
expect(html).toContain(
|
||||
'items="[{"label":"Home","href":"/"}]"',
|
||||
);
|
||||
expect(html).toContain('options="{"dense":true}"');
|
||||
expect(html).toContain(
|
||||
'items="[{"label":"Cases","href":"/cases"}]"',
|
||||
);
|
||||
});
|
||||
|
||||
test("parses a layout block", () => {
|
||||
const ast = parse(`
|
||||
layout AppLayout {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@@ -16,7 +16,7 @@
|
||||
"scripts": {
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"check": "bun run typecheck && bun run test"
|
||||
"check": "bun run typecheck \u0026\u0026 bun run test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -712,13 +712,30 @@ export function parseHtmlView(src: string, pos: number): { nodes: ViewNode[]; en
|
||||
while (i < src.length && isWs(src[i]!)) i++;
|
||||
};
|
||||
|
||||
/** Read a `{...}` interpolation (brace-balanced), braces included. */
|
||||
/** Read a `{...}` interpolation (brace-balanced and quote-aware), braces included. */
|
||||
const readInterpolation = (): string => {
|
||||
const start = i;
|
||||
let depth = 0;
|
||||
let quote: string | null = null;
|
||||
for (; i < src.length; i++) {
|
||||
if (src[i] === "{") depth++;
|
||||
else if (src[i] === "}" && --depth === 0) {
|
||||
const char = src[i]!;
|
||||
|
||||
if (quote) {
|
||||
if (char === "\\" && i + 1 < src.length) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (char === quote) quote = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"' || char === "'" || char === "`") {
|
||||
quote = char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === "{") depth++;
|
||||
else if (char === "}" && --depth === 0) {
|
||||
i++;
|
||||
return src.slice(start, i);
|
||||
}
|
||||
@@ -819,7 +836,13 @@ export function parseHtmlView(src: string, pos: number): { nodes: ViewNode[]; en
|
||||
if (src[i] === "=") {
|
||||
i++;
|
||||
skipWs();
|
||||
attrs.push({ name, value: readQuoted(), event: false });
|
||||
const value =
|
||||
src[i] === '"' || src[i] === "'"
|
||||
? readQuoted()
|
||||
: src[i] === "{"
|
||||
? readInterpolation()
|
||||
: fail(`Expected a quoted value or {...} expression after '${name}='`);
|
||||
attrs.push({ name, value, event: false });
|
||||
} else {
|
||||
attrs.push({ name, value: "", event: false, boolean: true });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,35 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { diagnose, formatDiagnostic, parse } from "../src/index.ts";
|
||||
|
||||
test("parses native array and object literals in state and component props", () => {
|
||||
const ast = parse(`
|
||||
component Navigation {
|
||||
state items = [{"label":"Home","href":"/"}]
|
||||
state options = {"dense":true,"nested":{"label":"A {brace}"}}
|
||||
view {
|
||||
<Navbar items={items} />
|
||||
<Sidebar items={[{"label":"Cases","href":"/cases"}]} options={{"dense":true}} />
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
expect(ast.states[0]?.expr).toBe('[{"label":"Home","href":"/"}]');
|
||||
expect(ast.states[1]?.expr).toBe('{"dense":true,"nested":{"label":"A {brace}"}}');
|
||||
|
||||
const elements = ast.view.filter((node) => node.type === "element");
|
||||
const navbar = elements[0];
|
||||
const sidebar = elements[1];
|
||||
expect(navbar?.type).toBe("element");
|
||||
expect(sidebar?.type).toBe("element");
|
||||
if (navbar?.type !== "element" || sidebar?.type !== "element") return;
|
||||
|
||||
expect(navbar.attrs.find((attr) => attr.name === "items")?.value).toBe("{items}");
|
||||
expect(sidebar.attrs.find((attr) => attr.name === "items")?.value).toBe(
|
||||
'{[{"label":"Cases","href":"/cases"}]}',
|
||||
);
|
||||
expect(sidebar.attrs.find((attr) => attr.name === "options")?.value).toBe('{{"dense":true}}');
|
||||
});
|
||||
|
||||
test("parses WRN 0.3 execution, data, security, and reactivity blocks", () => {
|
||||
const ast = parse(`page Dashboard {
|
||||
runtime = "universal"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Reference in New Issue
Block a user