fix theme navigation and improve project scaffolding
This commit is contained in:
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -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);
|
||||
})();
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Reference in New Issue
Block a user