fix theme navigation and improve project scaffolding

This commit is contained in:
2026-07-12 16:26:06 +05:30
parent ee98026cc5
commit b21e298d85
33 changed files with 143 additions and 26 deletions
+1 -1
View File
@@ -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.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+3 -1
View File
@@ -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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1
View File
@@ -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 <name> # scaffold a new app
wrnexus generate page <Name> # scaffold a page (aliases: g p)
wrnexus generate component <name> | api <path> | schema <name>
+47
View File
@@ -14,6 +14,51 @@ const TEMPLATE: Record<string, string> = {
// 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<string, string> = {
"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",
+51
View File
@@ -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 });
}
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1
View File
@@ -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) {
+13
View File
@@ -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(`<div id="app"><a href="/about" id="lnk">About</a></div>`);
let boundRoot: unknown;
win.wireTheme = { bind: (root: unknown) => (boundRoot = root) };
nextHtml = `<html><body><div id="app"><button data-wire-theme-toggle>Theme</button></div></body></html>`;
win.document.getElementById("lnk").click();
await flush();
expect(boundRoot).toBe(win.document);
expect(win.document.querySelector("[data-wire-theme-toggle]")).not.toBeNull();
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.2.12",
"version": "0.2.13",
"type": "module",
"main": "src/index.ts",
"exports": {
+1
View File
@@ -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);
})();
+1
View File
@@ -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"');
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/uploader",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/validation",
"version": "0.2.12",
"version": "0.2.13",
"private": true,
"type": "module",
"main": "src/index.ts",