JSX-Based Webpage Generation Design Document
JSX-Based Webpage Generation Design Document
Section titled “JSX-Based Webpage Generation Design Document”Overview
Section titled “Overview”This document describes how Jac’s cl (client) keyword produces browser-ready web experiences. Client-marked declarations compile to JavaScript and ship through jac start as static bundles that execute entirely in the browser. The current implementation is CSR-only (Client-Side Rendering): the server returns an empty HTML shell with bootstrapping metadata and a JavaScript bundle that handles all rendering in the browser.
Key Features:
- JSX Support: Full JSX syntax for declarative UI components
- Reactive State Management: Signal-based reactivity with automatic dependency tracking (no virtual DOM)
- Client-Side Routing: Hash-based routing with declarative route configuration
- Server Integration: Seamless walker spawning and function calls from client code
- Authentication: Built-in auth helpers with token-based authentication
Architecture Overview
Section titled “Architecture Overview”graph TD subgraph "Development - Compilation" A[Jac source with cl] --> B[Jac Compiler] B --> C[PyAST Gen Pass<br/>pyast_gen_pass.py] B --> D[ESTree Gen Pass<br/>esast_gen_pass.py] C --> E[Python module<br/>.py output] D --> F[JavaScript code<br/>module.gen.js] end
subgraph "Runtime - Serving" E --> H[JacAPIServer] F --> I[ClientBundleBuilder] H --> J[GET /cl/fn] I --> K["/static/client.js"] J --> L[HTML shell + init payload] K --> M[Runtime + Module JS] L --> N[Browser] M --> N N --> O[Hydrate & Execute] O --> P[Render JSX to DOM] endCSR Execution Flow
Section titled “CSR Execution Flow”-
Compilation: When a
.jacfile is compiled:- The
clkeyword marks declarations for client-side execution pyast_gen_pass.pyskips Python codegen for client-only nodes (via_should_skip_client)esast_gen_pass.pygenerates ECMAScript AST and JavaScript code- Client metadata is collected in
ClientManifest(exports, globals, params)
- The
-
Bundle Generation:
ClientBundleBuildercreates the browser bundle:- Compiles client_runtime.cl.jac to provide JSX and walker runtime
- Compiles the application module’s client-marked code
- Generates registration code that exposes functions globally
- Includes polyfills (e.g.,
Object.prototype.get()for dict-like access)
-
Page Request: When
GET /cl/<function_name>is requested:- Server returns minimal HTML with empty
<div id="__jac_root"></div> - Embeds
<script id="__jac_init__">with JSON payload containing:- Module name and function name to execute
- Arguments and their ordering
- Global variable values
- Links to
<script src="/static/client.js?hash=...">bundle
- Server returns minimal HTML with empty
-
Client Execution: On DOM load, the browser:
- Parses the
__jac_init__payload - Looks up the requested function from the global registry
- Restores client global variables
- Executes the function with provided arguments
- Calls
renderJsxTree()to render the returned JSX into__jac_root
- Parses the
Note: SSR + hydration is not currently implemented. All rendering happens on the client.
Language Features
Section titled “Language Features”1. The cl (Client) Keyword
Section titled “1. The cl (Client) Keyword”The cl keyword marks Jac declarations for client-side compilation. This enables a single .jac file to contain both:
- Server-side code (compiled to Python via
pyast_gen_pass) - Client-side code (compiled to JavaScript via
esast_gen_pass)
When cl is present:
- Node is marked with
is_client_decl = True - Python codegen is skipped for the declaration (via
_should_skip_client()in pyast_gen_pass.py:310-312) - JavaScript codegen generates ECMAScript AST (in esast_gen_pass.py)
- The declaration is tracked in the module’s
ClientManifest(exports, globals, params, globals_values, imports)
Supported Constructs
Section titled “Supported Constructs”# Client function - executes in browser, can return JSXcl def homepage() -> dict { return <div> <h1>Welcome</h1> <button onclick={load_feed()}>Load Feed</button> </div>;}
# Client object - available on both client and servercl obj ButtonProps { has label: str = "Hello"; has count: int = 0;}
# Client global - literal value sent to browsercl API_BASE_URL: str = "https://api.example.com";Grammar Definition
Section titled “Grammar Definition”From jac.lark:9-10, 591:
toplevel_stmt: KW_CLIENT? onelang_stmt | KW_CLIENT LBRACE onelang_stmt* RBRACE | py_code_block
KW_CLIENT: "cl"The cl keyword can prefix individual statements or wrap multiple statements in braces.
2. Client Imports
Section titled “2. Client Imports”Client code can import functions and utilities from the built-in jac:client_runtime module using the cl import syntax. This allows client-side code to access the runtime’s JSX rendering, authentication helpers, and server interaction functions.
Syntax
Section titled “Syntax”cl import from jac:client_runtime { renderJsxTree, jacLogin, jacLogout, jacSignup, jacIsLoggedIn,}Available Exports from jac:client_runtime
Section titled “Available Exports from jac:client_runtime”The client runtime (client_runtime.cl.jac) exports these public functions for use in client code:
| Function | Signature | Description |
|---|---|---|
| JSX Rendering | ||
renderJsxTree | (node: any, container: any) -> None | Renders a JSX tree into a DOM container |
| Reactive State Management | ||
createSignal | (initialValue: any) -> list | Creates a reactive signal (primitive value). Returns [getter, setter] |
createState | (initialState: dict) -> list | Creates a reactive state (object). Returns [getter, setter] with shallow merge |
createEffect | (effectFn: any) -> None | Runs a side effect function that re-executes when its dependencies change |
| Client-Side Routing | ||
createRouter | (routes: list, defaultRoute: str = "/") -> dict | Creates a router instance with reactive path tracking |
Route | (path: str, component: any, guard: any = None) -> dict | Creates a route configuration object |
Link | (props: dict) -> any | Renders a navigation link component |
navigate | (path: str) -> None | Programmatically navigates to a path |
useRouter | () -> dict | Returns the current router instance |
| Authentication | ||
jacSignup | async (username: str, password: str) -> dict | Creates a new user account and returns {success, token, username} or {success, error} |
jacLogin | async (username: str, password: str) -> bool | Authenticates user and stores token in localStorage |
jacLogout | () -> None | Clears authentication token from localStorage |
jacIsLoggedIn | () -> bool | Checks if user has valid token in localStorage |
Note: Functions prefixed with
__jac(like__jacJsx,__buildDom,__jacSpawn, etc.) are internal runtime functions automatically available in the client bundle. They should not be imported directly - use the public API functions listed above instead.
How Client Imports Work
Section titled “How Client Imports Work”When processing client imports (esast_gen_pass.py:317-325):
-
Parser detects
cl import from jac:client_runtimesyntax- The
jac:prefix indicates a special runtime import - The import is marked with
is_client_decl = True
- The
-
ESTree generation creates JavaScript import declaration:
import { renderJsxTree, jacLogin } from "jac:client_runtime"; -
Manifest tracking records the import in
ClientManifest.imports:- Key:
"client_runtime"(fromdot_path_str) - Value: Resolved path to
client_runtime.cl.jac
- Key:
-
Bundle generation compiles
client_runtime.cl.jacinto the bundle, making these functions available globally
Example Usage
Section titled “Example Usage”cl import from jac:client_runtime { jacLogin, jacLogout, jacIsLoggedIn,}
cl def LoginForm() { async def handleLogin(event: any) { event.preventDefault(); username = document.getElementById("username").value; password = document.getElementById("password").value;
success = await jacLogin(username, password); if success { print("Login successful!"); # Redirect or update UI } else { print("Login failed"); } }
return <form onsubmit={handleLogin}> <input id="username" type="text" placeholder="Username" /> <input id="password" type="password" placeholder="Password" /> <button type="submit">Login</button> </form>;}3. JSX Syntax
Section titled “3. JSX Syntax”JSX is fully supported in Jac with grammar defined in jac.lark:448-473. JSX elements are transpiled to __jacJsx(tag, props, children) calls by jsx_processor.py:30-129 via the EsJsxProcessor class.
JSX Features
Section titled “JSX Features”cl def render_example() { // Basic elements basic = <div>Hello World</div>;
// Elements with attributes with_attrs = <button id="submit" class="btn">Click</button>;
// Expression attributes and children name = "Alice"; greeting = <h1 data-user={name}>Welcome, {name}!</h1>;
// Spread attributes props = {"class": "card", "id": "main"}; with_spread = <div {...props}>Content</div>;
// Fragment syntax fragment = <> <div>First</div> <div>Second</div> </>;
// Component usage (capitalized names) component = <Button label="Click Me" />;
return <div>{greeting}{component}</div>;}JSX Transpilation
Section titled “JSX Transpilation”JSX elements compile to function calls:
<div>Hello</div>→__jacJsx("div", {}, ["Hello"])<Button {...props} />→__jacJsx(Button, Object.assign({}, props), [])- Tag names starting with lowercase become string literals
- Tag names starting with uppercase become identifier references
- Props merge via
Object.assign()when spreads are present
4. Reactive State Management
Section titled “4. Reactive State Management”Jac includes a built-in reactive state management system inspired by modern frameworks like SolidJS and Vue. The system provides automatic dependency tracking and efficient re-rendering without virtual DOM diffing.
Core Concepts
Section titled “Core Concepts”The reactive system is based on signals - reactive containers for values that automatically track their dependencies and notify subscribers when values change.
Global Reactive Context (client_runtime.cl.jac:79-87):
__jacReactiveContext = { signals: [], // Global signal storage pendingRenders: [], // Batched re-renders queue flushScheduled: false, // Debounce flag for batching rootComponent: null, // Root function to re-render currentComponent: null, // Current component ID being rendered currentEffect: null, // Current effect for dependency tracking router: null // Global router instance}createSignal - Reactive Primitives
Section titled “createSignal - Reactive Primitives”Creates a reactive signal for primitive values. Returns a [getter, setter] tuple.
Syntax (client_runtime.cl.jac:92-110):
cl import from jac:client_runtime { createSignal }
cl def Counter() { [count, setCount] = createSignal(0);
def increment() { setCount(count() + 1); // Read with (), set by calling setter }
return <div> <p>Count: {count()}</p> <button onclick={increment}>Increment</button> </div>;}How it works:
- Each signal stores its value and a list of subscribers
- When
count()is called (getter), it automatically tracks which component/effect is reading it - When
setCount(newValue)is called, it notifies all subscribers - Components are automatically re-rendered when their dependencies change
- Re-renders are batched using
requestAnimationFramefor efficiency
createState - Reactive Objects
Section titled “createState - Reactive Objects”Creates a reactive state object with shallow merge semantics. Ideal for managing component state with multiple properties.
Syntax (client_runtime.cl.jac:114-139):
cl import from jac:client_runtime { createState }
cl def TodoList() { [state, setState] = createState({ "todos": [], "filter": "all" });
def addTodo(text: str) { todos = state().todos; todos.append({"text": text, "done": False}); setState({"todos": todos}); // Shallow merge with existing state }
return <div> <ul> {[<li>{todo.text}</li> for todo in state().todos]} </ul> </div>;}Difference from createSignal:
setState(updates)performs shallow merge:newState = {...oldState, ...updates}- Useful for managing multiple related properties
- Still tracks dependencies automatically
createEffect - Side Effects
Section titled “createEffect - Side Effects”Runs a function whenever its reactive dependencies change. Automatically re-executes when any accessed signal/state updates.
Syntax (client_runtime.cl.jac:143-174):
cl import from jac:client_runtime { createSignal, createEffect }
cl def DataFetcher() { [userId, setUserId] = createSignal(1); [userData, setUserData] = createSignal(None);
createEffect(lambda -> None { id = userId(); // Track dependency! print("Fetching user", id); # In real app, would fetch from API setUserData({"id": id, "name": "User " + str(id)}); });
return <div> <button onclick={lambda: setUserId(userId() + 1)}>Next User</button> <p>Current: {userData() and userData().name or "Loading..."}</p> </div>;}How it works:
- Effect function executes immediately on creation
- Any signals/state accessed during execution are automatically tracked
- When tracked dependencies change, effect re-runs
- No need to manually specify dependency arrays (unlike React’s useEffect)
Automatic Re-rendering
Section titled “Automatic Re-rendering”Components that use signals are automatically re-rendered when dependencies change:
- Initial render: Component executes, tracking which signals it reads
- Dependency tracking: Each
getter()call registers the current component as a subscriber - Change notification: When
setter(newValue)is called, all subscribers are notified - Batched updates: Re-renders are queued and flushed on next
requestAnimationFrame - Re-execution: Component function re-runs, generating new JSX
- DOM update: New JSX is rendered to replace old content
5. Client-Side Routing
Section titled “5. Client-Side Routing”Jac includes a declarative routing system built on reactive signals. Routes are defined as configuration objects, and navigation automatically triggers re-renders.
createRouter - Router Setup
Section titled “createRouter - Router Setup”Creates a router instance with reactive path tracking using URL hash.
Syntax (client_runtime.cl.jac:283-345):
cl import from jac:client_runtime { createRouter, Route }
cl def App() { routes = [ Route("/", HomePage), Route("/about", AboutPage), Route("/profile", ProfilePage, guard=jacIsLoggedIn) ];
router = createRouter(routes, defaultRoute="/");
return <div> <nav> <Link href="/">Home</Link> <Link href="/about">About</Link> <Link href="/profile">Profile</Link> </nav> <main>{router.render()}</main> </div>;}Router API:
router.path()- Getter for current path (reactive)router.render()- Renders component for current routerouter.navigate(path)- Programmatically navigate to path
Route Configuration
Section titled “Route Configuration”The Route function creates route configuration objects (client_runtime.cl.jac:348-350):
Route(path, component, guard=None)path- URL path to match (e.g.,"/","/profile")component- Function that returns JSX to renderguard- Optional function that returns bool; if false, route is blocked
Link Component
Section titled “Link Component”Renders navigation links that update the router without full page reload (client_runtime.cl.jac:353-365):
<Link href="/about">About Page</Link>
# Equivalent to:<a href="#/about" onclick={handleClick}>About Page</a>Features:
- Automatically prefixes href with
#for hash routing - Prevents default link behavior
- Calls
navigate()to update router state - Spreads additional props to
<a>element
Programmatic Navigation
Section titled “Programmatic Navigation”Use navigate() to change routes from code (client_runtime.cl.jac:368-378):
cl import from jac:client_runtime { navigate }
cl def LoginForm() { def handleSubmit() { # After successful login navigate("/dashboard"); }
return <form onsubmit={handleSubmit}>...</form>;}Route Guards
Section titled “Route Guards”Protect routes with guard functions:
cl import from jac:client_runtime { Route, jacIsLoggedIn, navigate }
cl def AccessDenied() { return <div> <h1>Access Denied</h1> <button onclick={lambda: navigate("/login")}>Login</button> </div>;}
Route("/admin", AdminPanel, guard=jacIsLoggedIn)If guard returns False, the route renders AccessDenied component instead.
How Routing Works
Section titled “How Routing Works”- Hash-based routing: Uses
window.location.hash(e.g.,#/profile) - Reactive path: Current path is stored in a signal
- Event listeners:
hashchangeandpopstateevents update the signal - Automatic re-render: Changing the path signal triggers router re-render
- Component lookup: Router finds matching route and renders its component
No manual routing updates needed - the reactive system handles everything!
Implementation Details
Section titled “Implementation Details”Core Components
Section titled “Core Components”| Component | Implementation | Key Responsibilities |
|---|---|---|
| Compiler Passes | ||
| pyast_gen_pass.py | Python AST generation | Skips Python codegen for cl-marked nodes |
| esast_gen_pass.py | ECMAScript AST generation | Generates JavaScript for cl-marked nodes, JSX transpilation |
| es_unparse.py | JavaScript code generation | Converts ESTree AST to JavaScript source |
| Runtime Components | ||
| client_bundle.py | Bundle builder | Compiles runtime + module, generates registration code |
| client_runtime.cl.jac | Client runtime | JSX rendering (__jacJsx, renderJsxTree), walker spawning (__jacSpawn), auth helpers |
| server.py | HTTP server | Serves pages (/cl/<fn>), bundles (/static/client.js), walkers |
| Data Structures | ||
| ClientManifest | Metadata container (in codeinfo.py) | Stores exports (function names), globals (var names), params (arg order), globals_values (literal values), has_client (bool), imports (module mappings) |
Client Bundle Structure
Section titled “Client Bundle Structure”The bundle generated by ClientBundleBuilder contains (in order):
-
Polyfills - Browser compatibility shims (from client_runtime.cl.jac:227-253): The
__jacEnsureObjectGetPolyfill()function adds a Python-style.get()method toObject.prototype:Object.prototype.get = function(key, defaultValue) {if (this.hasOwnProperty(key)) {return this[key];}return defaultValue !== undefined ? defaultValue : null;};This polyfill is called automatically during module registration and hydration.
-
Client Runtime - Compiled from client_runtime.cl.jac:
- JSX Rendering:
__jacJsx(tag, props, children),renderJsxTree(node, container),__buildDom(node),__applyProp(element, key, value) - Reactive System:
createSignal(initialValue),createState(initialState),createEffect(effectFn),__jacTrackDependency(),__jacNotifySubscribers() - Router System:
createRouter(routes, defaultRoute),Route(path, component, guard),Link(props),navigate(path),useRouter() - Server Communication:
__jacSpawn(walker, fields)- Async walker invocation via/walker/<name>endpoint,__jacCallFunction(function_name, args)- Async server-side function calls via/function/<name>endpoint - Authentication:
jacSignup,jacLogin,jacLogout,jacIsLoggedIn - Hydration System:
__jacRegisterClientModule,__jacHydrateFromDom,__jacEnsureHydration
- JSX Rendering:
-
Application Module - Transpiled user code with
cldeclarations -
Registration Code - Generated by client_bundle.py:245-251:
__jacRegisterClientModule("module_name", ["homepage", "other_func"], {"API_URL": "value"});This calls the
__jacRegisterClientModulefunction from the runtime which:- Registers all exported client functions in the global registry
- Sets up client global variables with default values
- Creates a module record in
__jacClient.modules - Calls
__jacEnsureHydrationto set up DOMContentLoaded listener - Executes hydration automatically when DOM is ready
Server Endpoints
Section titled “Server Endpoints”From server.py:
| Endpoint | Method | Description | Implementation |
|---|---|---|---|
/cl/<fn> | GET | Render HTML page for client function | Lines 806-830 |
/static/client.js | GET | Serve compiled JavaScript bundle | Lines 772-781 |
/walker/<name> | POST | Spawn walker on node | Handled by ExecutionHandler |
/function/<name> | POST | Call server-side function | Handled by ExecutionHandler |
/user/create | POST | Create new user account | Handled by AuthHandler |
/user/login | POST | Authenticate and get token | Handled by AuthHandler |
/functions | GET | List available functions | Handled by IntrospectionHandler |
/walkers | GET | List available walkers | Handled by IntrospectionHandler |
Note: The server has been refactored to use handler classes (AuthHandler, IntrospectionHandler, ExecutionHandler) for better organization.
Page Rendering Flow
Section titled “Page Rendering Flow”When GET /cl/homepage?arg1=value1 is requested:
- Parse request - Extract function name and query params
- Authenticate - Check auth token, or create guest user
- Load module - Ensure module is loaded and manifest is available
- Validate function - Check function is in
client_exports - Build payload - Serialize args, globals, arg order
- Render HTML - Return shell with embedded payload and script tag
HTML template (from server.py:491-504):
<!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"/> <title>homepage</title></head><body> <div id="__jac_root"></div> <script id="__jac_init__" type="application/json"> {"module":"myapp","function":"homepage","args":{},"globals":{},"argOrder":[]} </script> <script type="module" src="/static/client.js?hash=abc123..."></script></body></html>Note: The JSON in __jac_init__ has </ escaped as <\/ to prevent script injection attacks.
Client-Side Execution
Section titled “Client-Side Execution”On page load in the browser (client_runtime.cl.jac:726-821):
- Wait for DOM -
__jacEnsureHydration()waits forDOMContentLoaded - Parse payload -
__jacHydrateFromDom()extracts__jac_init__JSON from script tag - Validate hydration - Check if already hydrated (prevent duplicate execution)
- Restore globals - Set global variables from
payload.globals - Lookup function - Find target function in module’s registered functions
- Order arguments - Map args dict to positional array using
argOrder - Setup reactive root - Create root component wrapper for reactive re-rendering
- Execute function - Call function with ordered arguments
- Handle result - If Promise, await; otherwise render immediately
- Render JSX - Call
renderJsxTree(result, __jac_root)with batched updates
Key Implementation Details:
# Set up reactive root component for automatic re-rendering__jacReactiveContext.rootComponent = lambda -> any { __jacReactiveContext.currentComponent = "__root__"; result = target(...orderedArgs); # Execute with dependency tracking return result;};
# Execute and rendercallOutcome = __jacSafeCallTarget(target, scope, orderedArgs, targetName);value = callOutcome.get("value");
if value and __isObject(value) and __isFunction(value.then) { # Async result - wait for promise value.then(lambda node: __jacApplyRender(renderer, rootEl, node));} else { # Sync result - render immediately __jacApplyRender(renderer, rootEl, value);}Hydration Safety: The system marks the __jac_init__ element with data-jac-hydrated="true" to prevent duplicate hydration if scripts execute multiple times.
JSX Rendering
Section titled “JSX Rendering”The renderJsxTree function (client_runtime.cl.jac:8-10) calls __buildDom (client_runtime.cl.jac:13-54) to recursively build DOM:
- Null/undefined → Empty text node (
document.createTextNode("")) - Primitive values → Text node with
String(value) - Object with callable
tag→ Execute component function with props (including children), recurse on result - Object with string
tag→ Create element:- Create element with
document.createElement(tag) - Apply props (attributes, event listeners, styles) via
__applyProp - Recursively build and append children
- Create element with
- Return DOM node → Attach to container via
container.replaceChildren(domNode)
Event handlers are bound in __applyProp (client_runtime.cl.jac:57-72):
- Props starting with
on(e.g.,onclick,onsubmit) becomeaddEventListener(eventName, handler)- Event name is extracted by removing
onprefix and converting to lowercase
- Event name is extracted by removing
classandclassNameboth setelement.classNamestyleobjects are applied toelement.style[key]for each key- Other props (except
children) are set viaelement.setAttribute(key, String(value))
Example Usage
Section titled “Example Usage”Complete Application
Section titled “Complete Application”# Server-side data modelnode User { has name: str; has email: str;}
# Client-side global configurationcl API_URL: str = "/api";
# Client-side componentcl obj CardProps { has title: str = "Untitled"; has content: str = "";}
# Client page - renders in browsercl def homepage() { return <div class="app"> <header> <h1>Welcome to Jac</h1> </header> <main> <p>Full-stack web development in one language!</p> <button onclick={load_users()}>Load Users</button> </main> </div>;}
# Server-side walker - called from client via spawnwalker LoadUsers { has users: list = [];
can process with Root entry { # Fetch users from database self.users = [{"name": "Alice"}, {"name": "Bob"}]; report self.users; }}Running the Application
Section titled “Running the Application”# Compile the Jac filejac myapp.jac
# Start the serverjac start myapp.jac
# Access the page# Browser: http://localhost:8000/cl/homepageClient-Server Interaction
Section titled “Client-Server Interaction”When the user clicks “Load Users”:
- Client:
spawn load_users()triggers__jacSpawn("LoadUsers", {}) - HTTP: Async
POST /walker/LoadUserswith{"nd": "root"}and Authorization header - Server: Authenticates user, spawns
LoadUserswalker on root node - Server: Walker executes, generates reports
- HTTP: Returns
{"result": {...}, "reports": [...]} - Client: Receives walker results as Promise (can be awaited to update UI)
The __jacSpawn function is async and retrieves the auth token from localStorage before making the request.
Reactive Application Example
Section titled “Reactive Application Example”Here’s a complete example using signals, state, and routing:
cl import from jac:client_runtime { createSignal, createState, createRouter, Route, Link, navigate,}
# Counter component with reactive signalcl def Counter() { [count, setCount] = createSignal(0);
return <div> <h2>Counter: {count()}</h2> <button onclick={lambda: setCount(count() + 1)}>+</button> <button onclick={lambda: setCount(count() - 1)}>-</button> </div>;}
# Todo list with reactive statecl def TodoApp() { [state, setState] = createState({ "todos": [], "input": "" });
def addTodo() { todos = state().todos; todos.append({"text": state().input, "done": False}); setState({"todos": todos, "input": ""}); }
return <div> <h2>Todos</h2> <input value={state().input} oninput={lambda e: setState({"input": e.target.value})} /> <button onclick={addTodo}>Add</button> <ul> {[<li>{todo.text}</li> for todo in state().todos]} </ul> </div>;}
# Main app with routingcl def littlex_app() { routes = [ Route("/", Counter), Route("/todos", TodoApp) ];
router = createRouter(routes, "/");
return <div> <nav> <Link href="/">Counter</Link> <Link href="/todos">Todos</Link> </nav> <main>{router.render()}</main> </div>;}Key Features Demonstrated:
- Reactive signals (
createSignal) for simple counters - Reactive state (
createState) for complex component state - Client-side routing without page reloads
- Automatic re-rendering when state changes
- No manual DOM manipulation needed
Test Coverage
Section titled “Test Coverage”| Test Suite | Location | Coverage |
|---|---|---|
| Client codegen tests | test_client_codegen.py | cl keyword detection, manifest generation |
| ESTree generation tests | test_esast_gen_pass.py | JavaScript AST generation |
| JavaScript generation tests | test_js_generation.py | JS code output from ESTree |
| Client bundle tests | test_client_bundle.py | Bundle building, caching, import resolution |
| Server endpoint tests | test_serve.py | HTTP endpoints, page rendering |
| JSX rendering tests | test_jsx_render.py | JSX parsing and rendering |
| Reactive signals tests | test_reactive_signals.py | Signal creation, effects, dependency tracking |
| Router tests | test_router.py | Routing, navigation, route guards |
| Closures tests | test_closures.py | Nested functions, closure semantics in JavaScript |
Example Test Fixtures
Section titled “Example Test Fixtures”- client_jsx.jac - Comprehensive client syntax examples
- jsx_elements.jac - JSX feature demonstrations
- test_reactive_signals.jac - Reactive signals and effects examples
- test_router.jac - Routing and navigation examples
- littleX_single_nodeps.jac - Full SPA with reactive state and routing