From b21e298d8554ccec933f7433d4e0937d1d218809 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Sun, 12 Jul 2026 16:26:06 +0530 Subject: [PATCH] fix theme navigation and improve project scaffolding --- packages/ai/package.json | 2 +- packages/authz/package.json | 2 +- packages/cli/README.md | 4 ++- packages/cli/package.json | 2 +- packages/cli/src/ai-guide.ts | 1 + packages/cli/src/create.ts | 47 +++++++++++++++++++++++++++ packages/cli/test/create.test.ts | 51 ++++++++++++++++++++++++++++++ packages/compiler/package.json | 2 +- packages/core/package.json | 2 +- packages/csr/package.json | 2 +- packages/csr/src/nav-runtime.ts | 1 + packages/csr/test/nav.test.ts | 13 ++++++++ packages/db/package.json | 2 +- packages/dev-server/package.json | 2 +- packages/encryption/package.json | 2 +- packages/i18n/package.json | 2 +- packages/jwt/package.json | 2 +- packages/mobile/package.json | 2 +- packages/native/package.json | 2 +- packages/oauth/package.json | 2 +- packages/pubsub/package.json | 2 +- packages/queue/package.json | 2 +- packages/reactive/package.json | 2 +- packages/router/package.json | 2 +- packages/ssr/package.json | 2 +- packages/styles/package.json | 2 +- packages/styles/src/theme.ts | 1 + packages/styles/test/theme.test.ts | 1 + packages/test/package.json | 2 +- packages/tracking/package.json | 2 +- packages/ui/package.json | 2 +- packages/uploader/package.json | 2 +- packages/validation/package.json | 2 +- 33 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 packages/cli/test/create.test.ts diff --git a/packages/ai/package.json b/packages/ai/package.json index e25947d9..6b941e7b 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", diff --git a/packages/authz/package.json b/packages/authz/package.json index c6c2ffc0..7a74e95a 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/cli/README.md b/packages/cli/README.md index 191520b1..9aace6f0 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -68,11 +68,13 @@ Before bundling, it regenerates typed queries for the default and every named da ```bash bun dist/server.js # PORT env var optional +# Generated apps also provide: npm start +# Build and start together: npm run production ``` ### `wrnexus create` -Scaffolds a new app from an inline (dependency-free) template — `package.json`, config, and starter `app/` files. Run `wrnexus dev` in the new directory to start. +Scaffolds a new app from an inline (dependency-free) template — `package.json`, `.gitignore`, config, and starter `app/` files. Use `npm run dev` during development, `npm run build && npm start` for production, or `npm run production` to build and start in one command. The generated production server currently requires Bun even when npm is used to manage packages and scripts. ```bash wrnexus create my-app diff --git a/packages/cli/package.json b/packages/cli/package.json index 4f7b9ae5..3c70993d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/cli/src/ai-guide.ts b/packages/cli/src/ai-guide.ts index 7d6504f5..f50f1b92 100644 --- a/packages/cli/src/ai-guide.ts +++ b/packages/cli/src/ai-guide.ts @@ -250,6 +250,7 @@ export const POST = async (ctx) => { \`\`\` wrnexus dev . # dev server + HMR wrnexus build . # production build → dist/server.js +bun dist/server.js # run the production server (or npm start) wrnexus create # scaffold a new app wrnexus generate page # scaffold a page (aliases: g p) wrnexus generate component | api | schema diff --git a/packages/cli/src/create.ts b/packages/cli/src/create.ts index f1992811..d170d2e8 100644 --- a/packages/cli/src/create.ts +++ b/packages/cli/src/create.ts @@ -14,6 +14,51 @@ const TEMPLATE: Record = { // AI/agent context: teaches Claude Code / Cursor / Copilot the WrNexus conventions. "CLAUDE.md": CLAUDE_MD, "llms.txt": AI_GUIDE, + ".gitignore": `# Dependencies +node_modules/ + +# WRNexusJS and production builds +dist/ +.wrnexus/ +**/.wrnexus/ +coverage/ + +# Environment files and local secrets +.env +.env.* +!.env.example + +# Logs and runtime files +*.log +logs/ +*.pid +*.pid.lock + +# Local databases +*.db +*.db-shm +*.db-wal +*.sqlite +*.sqlite3 + +# Generated native projects +mobile/android/ +mobile/ios/ +mobile/.expo/ + +# Editors and operating systems +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +Thumbs.db + +# TypeScript and test caches +*.tsbuildinfo +.eslintcache +.nyc_output/ +`, "package.json": `{ "name": "APP_NAME", "version": "0.1.0", @@ -22,6 +67,8 @@ const TEMPLATE: Record = { "scripts": { "dev": "wrnexus dev .", "build": "wrnexus build .", + "start": "bun dist/server.js", + "production": "bun run build && bun run start", "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier . --write", diff --git a/packages/cli/test/create.test.ts b/packages/cli/test/create.test.ts new file mode 100644 index 00000000..202448b0 --- /dev/null +++ b/packages/cli/test/create.test.ts @@ -0,0 +1,51 @@ +import { expect, test } from "bun:test"; +import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { scaffoldApp } from "../src/create.ts"; + +test("scaffoldApp creates a comprehensive .gitignore", () => { + const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-")); + const root = join(parent, "demo-app"); + + try { + scaffoldApp(root, "demo-app"); + + const file = join(root, ".gitignore"); + expect(existsSync(file)).toBe(true); + const contents = readFileSync(file, "utf8"); + for (const entry of [ + "node_modules/", + "dist/", + ".wrnexus/", + ".env.*", + "!.env.example", + "*.log", + "*.db", + "coverage/", + "mobile/android/", + ".vscode/", + "*.tsbuildinfo", + ]) { + expect(contents).toContain(entry); + } + } finally { + rmSync(parent, { recursive: true, force: true }); + } +}); + +test("scaffoldApp includes production build and start scripts", () => { + const parent = mkdtempSync(join(tmpdir(), "wrnexus-create-")); + const root = join(parent, "production-app"); + + try { + scaffoldApp(root, "production-app"); + const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); + + expect(pkg.scripts.build).toBe("wrnexus build ."); + expect(pkg.scripts.start).toBe("bun dist/server.js"); + expect(pkg.scripts.production).toBe("bun run build && bun run start"); + } finally { + rmSync(parent, { recursive: true, force: true }); + } +}); diff --git a/packages/compiler/package.json b/packages/compiler/package.json index b3fc1f20..e6d0ab63 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/core/package.json b/packages/core/package.json index 8cd77775..2532d384 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/package.json b/packages/csr/package.json index 368e9e16..d167726a 100644 --- a/packages/csr/package.json +++ b/packages/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/src/nav-runtime.ts b/packages/csr/src/nav-runtime.ts index fef10757..00b5d3c6 100644 --- a/packages/csr/src/nav-runtime.ts +++ b/packages/csr/src/nav-runtime.ts @@ -65,6 +65,7 @@ export const NAV_RUNTIME = String.raw` try { if (window.__wrnexusHydrateScopes) window.__wrnexusHydrateScopes(document); } catch (e) {} try { if (window.__wrnexusHydrateCsrFetches) window.__wrnexusHydrateCsrFetches(document); } catch (e) {} try { if (window.__wireValidate) window.__wireValidate.init(document); } catch (e) {} + try { if (window.wireTheme) window.wireTheme.bind(document); } catch (e) {} } function render(html, url, isPop) { diff --git a/packages/csr/test/nav.test.ts b/packages/csr/test/nav.test.ts index abf2dfb4..ff398bc5 100644 --- a/packages/csr/test/nav.test.ts +++ b/packages/csr/test/nav.test.ts @@ -91,3 +91,16 @@ test("exposes programmatic navigation", async () => { expect(fetchCalls.length).toBe(1); expect(win.document.getElementById("app").innerHTML).toContain("Dashboard"); }); + +test("rebinds theme controls after swapping the page", async () => { + install(``); + let boundRoot: unknown; + win.wireTheme = { bind: (root: unknown) => (boundRoot = root) }; + nextHtml = `
`; + + win.document.getElementById("lnk").click(); + await flush(); + + expect(boundRoot).toBe(win.document); + expect(win.document.querySelector("[data-wire-theme-toggle]")).not.toBeNull(); +}); diff --git a/packages/db/package.json b/packages/db/package.json index a24fd5c1..b4b63b6b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 2ea9280d..8fb15d09 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/encryption/package.json b/packages/encryption/package.json index 96c7b226..6b46e3d1 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index a90f0476..4c5343a0 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/jwt/package.json b/packages/jwt/package.json index fa19961e..f9d8f03a 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index f94a3470..dc5f464d 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/native/package.json b/packages/native/package.json index b488e42d..87f0b51b 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/oauth/package.json b/packages/oauth/package.json index bae0b7ed..fe72f137 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index c867b070..3fe953ac 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/queue/package.json b/packages/queue/package.json index 5847d5d5..13e763ce 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/reactive/package.json b/packages/reactive/package.json index eaf16316..df58f5e8 100644 --- a/packages/reactive/package.json +++ b/packages/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/router/package.json b/packages/router/package.json index cb3c861e..90ab8a28 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/ssr/package.json b/packages/ssr/package.json index 72c0a575..359bf74d 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/styles/package.json b/packages/styles/package.json index 66513718..e230f693 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.12", + "version": "0.2.13", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/styles/src/theme.ts b/packages/styles/src/theme.ts index 2a72eecc..25042bc9 100644 --- a/packages/styles/src/theme.ts +++ b/packages/styles/src/theme.ts @@ -142,6 +142,7 @@ export function renderThemeRuntime(theme: ResolvedTheme): string { }); } window.wireTheme={get:get,set:set,toggle:toggle,bind:bind,themes:THEMES}; + window.addEventListener("wrnexus:navigated",function(){bind(document);}); if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",function(){bind(document);}); else bind(document); })(); diff --git a/packages/styles/test/theme.test.ts b/packages/styles/test/theme.test.ts index aede2c8f..a238e21f 100644 --- a/packages/styles/test/theme.test.ts +++ b/packages/styles/test/theme.test.ts @@ -39,4 +39,5 @@ test("renderThemeRuntime bakes the theme names for cycling", () => { const js = renderThemeRuntime(t); expect(js).toContain("data-wire-theme-toggle"); expect(js).toContain(JSON.stringify(t.names)); + expect(js).toContain('addEventListener("wrnexus:navigated"'); }); diff --git a/packages/test/package.json b/packages/test/package.json index b95bad0a..411a0a02 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/tracking/package.json b/packages/tracking/package.json index f4fd7904..097c8e19 100644 --- a/packages/tracking/package.json +++ b/packages/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index a4c79063..bdeb1b64 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/uploader/package.json b/packages/uploader/package.json index 03989aa7..eff3a2db 100644 --- a/packages/uploader/package.json +++ b/packages/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/validation/package.json b/packages/validation/package.json index 4adf7d59..df5ea8e9 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.12", + "version": "0.2.13", "private": true, "type": "module", "main": "src/index.ts",