nukejs 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +529 -0
- package/bin/index.mjs +126 -0
- package/dist/app.d.ts +18 -0
- package/dist/app.js +124 -0
- package/dist/app.js.map +7 -0
- package/dist/as-is/Link.d.ts +6 -0
- package/dist/as-is/Link.tsx +20 -0
- package/dist/as-is/useRouter.d.ts +7 -0
- package/dist/as-is/useRouter.ts +33 -0
- package/dist/build-common.d.ts +192 -0
- package/dist/build-common.js +737 -0
- package/dist/build-common.js.map +7 -0
- package/dist/build-node.d.ts +1 -0
- package/dist/build-node.js +170 -0
- package/dist/build-node.js.map +7 -0
- package/dist/build-vercel.d.ts +1 -0
- package/dist/build-vercel.js +65 -0
- package/dist/build-vercel.js.map +7 -0
- package/dist/builder.d.ts +1 -0
- package/dist/builder.js +97 -0
- package/dist/builder.js.map +7 -0
- package/dist/bundle.d.ts +68 -0
- package/dist/bundle.js +166 -0
- package/dist/bundle.js.map +7 -0
- package/dist/bundler.d.ts +58 -0
- package/dist/bundler.js +100 -0
- package/dist/bundler.js.map +7 -0
- package/dist/component-analyzer.d.ts +72 -0
- package/dist/component-analyzer.js +102 -0
- package/dist/component-analyzer.js.map +7 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +30 -0
- package/dist/config.js.map +7 -0
- package/dist/hmr-bundle.d.ts +25 -0
- package/dist/hmr-bundle.js +76 -0
- package/dist/hmr-bundle.js.map +7 -0
- package/dist/hmr.d.ts +55 -0
- package/dist/hmr.js +62 -0
- package/dist/hmr.js.map +7 -0
- package/dist/html-store.d.ts +121 -0
- package/dist/html-store.js +42 -0
- package/dist/html-store.js.map +7 -0
- package/dist/http-server.d.ts +99 -0
- package/dist/http-server.js +166 -0
- package/dist/http-server.js.map +7 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +7 -0
- package/dist/logger.d.ts +58 -0
- package/dist/logger.js +53 -0
- package/dist/logger.js.map +7 -0
- package/dist/metadata.d.ts +50 -0
- package/dist/metadata.js +43 -0
- package/dist/metadata.js.map +7 -0
- package/dist/middleware-loader.d.ts +50 -0
- package/dist/middleware-loader.js +50 -0
- package/dist/middleware-loader.js.map +7 -0
- package/dist/middleware.d.ts +22 -0
- package/dist/middleware.example.d.ts +8 -0
- package/dist/middleware.example.js +58 -0
- package/dist/middleware.example.js.map +7 -0
- package/dist/middleware.js +72 -0
- package/dist/middleware.js.map +7 -0
- package/dist/renderer.d.ts +44 -0
- package/dist/renderer.js +130 -0
- package/dist/renderer.js.map +7 -0
- package/dist/router.d.ts +84 -0
- package/dist/router.js +104 -0
- package/dist/router.js.map +7 -0
- package/dist/ssr.d.ts +39 -0
- package/dist/ssr.js +168 -0
- package/dist/ssr.js.map +7 -0
- package/dist/use-html.d.ts +64 -0
- package/dist/use-html.js +125 -0
- package/dist/use-html.js.map +7 -0
- package/dist/utils.d.ts +26 -0
- package/dist/utils.js +62 -0
- package/dist/utils.js.map +7 -0
- package/package.json +63 -12
package/dist/app.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { existsSync, watch } from "fs";
|
|
4
|
+
import { ansi, c, log, setDebugLevel, getDebugLevel } from "./logger.js";
|
|
5
|
+
import { loadConfig } from "./config.js";
|
|
6
|
+
import { discoverApiPrefixes, matchApiPrefix, createApiHandler } from "./http-server.js";
|
|
7
|
+
import { loadMiddleware, runMiddleware } from "./middleware-loader.js";
|
|
8
|
+
import { serveReactBundle, serveNukeBundle, serveClientComponentBundle } from "./bundler.js";
|
|
9
|
+
import { serverSideRender } from "./ssr.js";
|
|
10
|
+
import { watchDir, broadcastRestart } from "./hmr.js";
|
|
11
|
+
const isDev = process.env.ENVIRONMENT !== "production";
|
|
12
|
+
if (isDev) {
|
|
13
|
+
const React = await import("react");
|
|
14
|
+
global.React = React;
|
|
15
|
+
}
|
|
16
|
+
const config = await loadConfig();
|
|
17
|
+
setDebugLevel(config.debug ?? false);
|
|
18
|
+
const PAGES_DIR = path.resolve("./app/pages");
|
|
19
|
+
const SERVER_DIR = path.resolve(config.serverDir);
|
|
20
|
+
const PORT = config.port;
|
|
21
|
+
log.info("Configuration loaded:");
|
|
22
|
+
log.info(` - Pages directory: ${PAGES_DIR}`);
|
|
23
|
+
log.info(` - Server directory: ${SERVER_DIR}`);
|
|
24
|
+
log.info(` - Port: ${PORT}`);
|
|
25
|
+
log.info(` - Debug level: ${String(getDebugLevel())}`);
|
|
26
|
+
log.info(` - Dev mode: ${String(isDev)}`);
|
|
27
|
+
if (isDev) watchDir(path.resolve("./app"), "App");
|
|
28
|
+
const apiPrefixes = discoverApiPrefixes(SERVER_DIR);
|
|
29
|
+
const handleApiRoute = createApiHandler({ apiPrefixes, port: PORT });
|
|
30
|
+
if (isDev && existsSync(SERVER_DIR)) {
|
|
31
|
+
watch(SERVER_DIR, { recursive: true }, (_event, filename) => {
|
|
32
|
+
if (!filename) return;
|
|
33
|
+
const ext = path.extname(filename);
|
|
34
|
+
if (ext !== ".ts" && ext !== ".tsx") return;
|
|
35
|
+
const fresh = discoverApiPrefixes(SERVER_DIR);
|
|
36
|
+
apiPrefixes.splice(0, apiPrefixes.length, ...fresh);
|
|
37
|
+
log.info("[Server] Routes updated (" + fresh.length + " prefix" + (fresh.length === 1 ? "" : "es") + ")");
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
log.info(`API prefixes discovered: ${apiPrefixes.length === 0 ? "none" : ""}`);
|
|
41
|
+
apiPrefixes.forEach((p) => {
|
|
42
|
+
log.info(` - ${p.prefix || "/"} -> ${path.relative(process.cwd(), p.directory)}`);
|
|
43
|
+
});
|
|
44
|
+
if (isDev) {
|
|
45
|
+
const RESTART_EXIT_CODE = 75;
|
|
46
|
+
const restartFiles = [
|
|
47
|
+
path.resolve("./middleware.ts"),
|
|
48
|
+
path.resolve("./nuke.config.ts")
|
|
49
|
+
];
|
|
50
|
+
for (const filePath of restartFiles) {
|
|
51
|
+
if (!existsSync(filePath)) continue;
|
|
52
|
+
watch(filePath, async () => {
|
|
53
|
+
log.info(`[Server] ${path.basename(filePath)} changed \u2014 restarting...`);
|
|
54
|
+
await broadcastRestart();
|
|
55
|
+
process.exit(RESTART_EXIT_CODE);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await loadMiddleware();
|
|
60
|
+
const server = http.createServer(async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const middlewareHandled = await runMiddleware(req, res);
|
|
63
|
+
if (middlewareHandled) return;
|
|
64
|
+
const url = req.url || "/";
|
|
65
|
+
if (matchApiPrefix(url, apiPrefixes))
|
|
66
|
+
return await handleApiRoute(url, req, res);
|
|
67
|
+
if (url === "/__hmr_ping") {
|
|
68
|
+
res.setHeader("Content-Type", "text/plain");
|
|
69
|
+
res.end("ok");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (url === "/__react.js")
|
|
73
|
+
return await serveReactBundle(res);
|
|
74
|
+
if (url === "/__n.js")
|
|
75
|
+
return await serveNukeBundle(res);
|
|
76
|
+
if (url.startsWith("/__client-component/"))
|
|
77
|
+
return await serveClientComponentBundle(
|
|
78
|
+
url.slice(20).split("?")[0].replace(".js", ""),
|
|
79
|
+
res
|
|
80
|
+
);
|
|
81
|
+
return await serverSideRender(url, res, PAGES_DIR, isDev);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
log.error("Server error:", error);
|
|
84
|
+
res.statusCode = 500;
|
|
85
|
+
res.end("Internal server error");
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
function tryListen(port) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
server.once("error", (err) => {
|
|
91
|
+
if (err.code === "EADDRINUSE") resolve(tryListen(port + 1));
|
|
92
|
+
else reject(err);
|
|
93
|
+
});
|
|
94
|
+
server.listen(port, () => resolve(port));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function printStartupBanner(port, isDev2) {
|
|
98
|
+
const url = `http://localhost:${port}`;
|
|
99
|
+
const level = getDebugLevel();
|
|
100
|
+
const debugStr = String(level);
|
|
101
|
+
const innerWidth = 42;
|
|
102
|
+
const line = "\u2500".repeat(innerWidth);
|
|
103
|
+
const pad = (text, width) => {
|
|
104
|
+
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
105
|
+
return text + " ".repeat(Math.max(0, width - visibleLen));
|
|
106
|
+
};
|
|
107
|
+
const row = (content, w = 2) => `${ansi.gray}\u2502${ansi.reset} ${pad(content, innerWidth - w)} ${ansi.gray}\u2502${ansi.reset}`;
|
|
108
|
+
const label = (key, val) => row(`${c("gray", key)} ${val}`);
|
|
109
|
+
console.log("");
|
|
110
|
+
console.log(`${ansi.gray}\u250C${line}\u2510${ansi.reset}`);
|
|
111
|
+
console.log(row(` ${c("red", "\u2622\uFE0F nukejs ", true)}`, 1));
|
|
112
|
+
console.log(`${ansi.gray}\u251C${line}\u2524${ansi.reset}`);
|
|
113
|
+
console.log(label(" Local ", c("cyan", url, true)));
|
|
114
|
+
console.log(`${ansi.gray}\u251C${line}\u2524${ansi.reset}`);
|
|
115
|
+
console.log(label(" Pages ", c("white", path.relative(process.cwd(), PAGES_DIR))));
|
|
116
|
+
console.log(label(" Server ", c("white", path.relative(process.cwd(), SERVER_DIR))));
|
|
117
|
+
console.log(label(" Dev ", isDev2 ? c("green", "yes") : c("gray", "no")));
|
|
118
|
+
console.log(label(" Debug ", level === false ? c("gray", "off") : level === true ? c("green", "verbose") : c("yellow", debugStr)));
|
|
119
|
+
console.log(`${ansi.gray}\u2514${line}\u2518${ansi.reset}`);
|
|
120
|
+
console.log("");
|
|
121
|
+
}
|
|
122
|
+
const actualPort = await tryListen(PORT);
|
|
123
|
+
printStartupBanner(actualPort, isDev);
|
|
124
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/app.ts"],
|
|
4
|
+
"sourcesContent": ["/**\r\n * app.ts \u2014 NukeJS Dev Server Entry Point\r\n *\r\n * This is the runtime that powers `nuke dev`. It:\r\n * 1. Loads your nuke.config.ts (or uses sensible defaults)\r\n * 2. Discovers API route prefixes from your server directory\r\n * 3. Starts an HTTP server that handles:\r\n * /__hmr_ping \u2014 heartbeat for HMR reconnect polling\r\n * /__react.js \u2014 bundled React + ReactDOM (resolved via importmap)\r\n * /__n.js \u2014 NukeJS client runtime bundle\r\n * /__client-component/* \u2014 on-demand \"use client\" component bundles\r\n * /api/** \u2014 API route handlers from serverDir\r\n * /** \u2014 SSR pages from app/pages\r\n * 4. Watches for file changes and broadcasts HMR events to connected browsers\r\n *\r\n * In production (ENVIRONMENT=production), HMR and all file watching are skipped.\r\n */\r\n\r\nimport http from 'http';\r\nimport path from 'path';\r\nimport { existsSync, watch } from 'fs';\r\n\r\nimport { ansi, c, log, setDebugLevel, getDebugLevel } from './logger';\r\nimport { loadConfig } from './config';\r\nimport { discoverApiPrefixes, matchApiPrefix, createApiHandler } from './http-server';\r\nimport { loadMiddleware, runMiddleware } from './middleware-loader';\r\nimport { serveReactBundle, serveNukeBundle, serveClientComponentBundle } from './bundler';\r\nimport { serverSideRender } from './ssr';\r\nimport { watchDir, broadcastRestart } from './hmr';\r\n\r\n// \u2500\u2500\u2500 Environment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst isDev = process.env.ENVIRONMENT !== 'production';\r\n\r\n// React must live on globalThis so dynamically-imported page modules can share\r\n// the same React instance without each bundling their own copy.\r\nif (isDev) {\r\n const React = await import('react');\r\n (global as any).React = React;\r\n}\r\n\r\n// \u2500\u2500\u2500 Config & paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst config = await loadConfig();\r\nsetDebugLevel(config.debug ?? false);\r\n\r\nconst PAGES_DIR = path.resolve('./app/pages');\r\nconst SERVER_DIR = path.resolve(config.serverDir);\r\nconst PORT = config.port;\r\n\r\nlog.info('Configuration loaded:');\r\nlog.info(` - Pages directory: ${PAGES_DIR}`);\r\nlog.info(` - Server directory: ${SERVER_DIR}`);\r\nlog.info(` - Port: ${PORT}`);\r\nlog.info(` - Debug level: ${String(getDebugLevel())}`);\r\nlog.info(` - Dev mode: ${String(isDev)}`);\r\n\r\n// \u2500\u2500\u2500 API route discovery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// Start watching the app directory for HMR.\r\nif (isDev) watchDir(path.resolve('./app'), 'App');\r\n\r\n// apiPrefixes is a live, mutable array. In dev, we splice it in-place whenever\r\n// the server directory changes so handlers always see the latest routes without\r\n// a full restart.\r\nconst apiPrefixes = discoverApiPrefixes(SERVER_DIR);\r\nconst handleApiRoute = createApiHandler({ apiPrefixes, port: PORT });\r\n\r\nif (isDev && existsSync(SERVER_DIR)) {\r\n watch(SERVER_DIR, { recursive: true }, (_event, filename) => {\r\n if (!filename) return;\r\n\r\n // Only react to TypeScript source changes, not compiled output or assets.\r\n const ext = path.extname(filename);\r\n if (ext !== '.ts' && ext !== '.tsx') return;\r\n\r\n const fresh = discoverApiPrefixes(SERVER_DIR);\r\n apiPrefixes.splice(0, apiPrefixes.length, ...fresh);\r\n log.info('[Server] Routes updated (' + fresh.length + ' prefix' + (fresh.length === 1 ? '' : 'es') + ')');\r\n });\r\n}\r\n\r\nlog.info(`API prefixes discovered: ${apiPrefixes.length === 0 ? 'none' : ''}`);\r\napiPrefixes.forEach(p => {\r\n log.info(` - ${p.prefix || '/'} -> ${path.relative(process.cwd(), p.directory)}`);\r\n});\r\n\r\n// \u2500\u2500\u2500 Full-restart file watchers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// Some changes can't be hot-patched: middleware exports change the request\r\n// pipeline, and nuke.config.ts may change the port or serverDir. On change we\r\n// broadcast a 'restart' SSE event so browsers reconnect automatically, then\r\n// exit with code 75 \u2014 the CLI watches for this to respawn the process.\r\nif (isDev) {\r\n const RESTART_EXIT_CODE = 75;\r\n const restartFiles = [\r\n path.resolve('./middleware.ts'),\r\n path.resolve('./nuke.config.ts'),\r\n ];\r\n\r\n for (const filePath of restartFiles) {\r\n if (!existsSync(filePath)) continue;\r\n watch(filePath, async () => {\r\n log.info(`[Server] ${path.basename(filePath)} changed \u2014 restarting...`);\r\n await broadcastRestart();\r\n process.exit(RESTART_EXIT_CODE);\r\n });\r\n }\r\n}\r\n\r\n// \u2500\u2500\u2500 Middleware \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n// Loads built-in middleware (HMR SSE/JS endpoints) and the user-supplied\r\n// middleware.ts from the project root (if it exists).\r\nawait loadMiddleware();\r\n\r\n// \u2500\u2500\u2500 Request handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nconst server = http.createServer(async (req, res) => {\r\n try {\r\n // Middleware runs first. If it calls res.end() the request is fully\r\n // handled and we bail out immediately.\r\n const middlewareHandled = await runMiddleware(req, res);\r\n if (middlewareHandled) return;\r\n\r\n const url = req.url || '/';\r\n\r\n // API routes \u2014 prefixes discovered from serverDir take priority over pages.\r\n if (matchApiPrefix(url, apiPrefixes))\r\n return await handleApiRoute(url, req, res);\r\n\r\n // \u2500\u2500 Internal NukeJS routes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n // Heartbeat polled by the HMR client to know when the server is back up.\r\n if (url === '/__hmr_ping') {\r\n res.setHeader('Content-Type', 'text/plain');\r\n res.end('ok');\r\n return;\r\n }\r\n\r\n // Unified React bundle (react + react-dom/client + react/jsx-runtime).\r\n // Resolved by the importmap injected into every SSR page, so client\r\n // components never bundle React themselves.\r\n if (url === '/__react.js')\r\n return await serveReactBundle(res);\r\n\r\n // NukeJS browser runtime: initRuntime, SPA navigation, partial hydration.\r\n if (url === '/__n.js')\r\n return await serveNukeBundle(res);\r\n\r\n // On-demand bundles for individual \"use client\" components.\r\n // Strip the prefix, the .js extension, and any query string (cache buster).\r\n if (url.startsWith('/__client-component/'))\r\n return await serveClientComponentBundle(\r\n url.slice(20).split('?')[0].replace('.js', ''),\r\n res,\r\n );\r\n\r\n // \u2500\u2500 Page SSR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n // No API prefix matched \u2014 render a page from app/pages.\r\n return await serverSideRender(url, res, PAGES_DIR, isDev);\r\n\r\n } catch (error) {\r\n log.error('Server error:', error);\r\n res.statusCode = 500;\r\n res.end('Internal server error');\r\n }\r\n});\r\n\r\n// \u2500\u2500\u2500 Port binding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Tries to listen on `port`. If the port is already in use (EADDRINUSE),\r\n * increments and tries the next port until one is free.\r\n *\r\n * Returns the port that was actually bound.\r\n */\r\nfunction tryListen(port: number): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n server.once('error', (err: NodeJS.ErrnoException) => {\r\n if (err.code === 'EADDRINUSE') resolve(tryListen(port + 1));\r\n else reject(err);\r\n });\r\n server.listen(port, () => resolve(port));\r\n });\r\n}\r\n\r\n// \u2500\u2500\u2500 Startup banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * Renders the \u2622\uFE0F NukeJS startup box to stdout.\r\n * Uses box-drawing characters and ANSI colour codes for a clean terminal UI.\r\n */\r\nfunction printStartupBanner(port: number, isDev: boolean): void {\r\n const url = `http://localhost:${port}`;\r\n const level = getDebugLevel();\r\n const debugStr = String(level);\r\n const innerWidth = 42;\r\n const line = '\u2500'.repeat(innerWidth);\r\n\r\n /** Right-pads `text` to `width` columns, ignoring invisible ANSI sequences. */\r\n const pad = (text: string, width: number) => {\r\n const visibleLen = text.replace(/\\x1b\\[[0-9;]*m/g, '').length;\r\n return text + ' '.repeat(Math.max(0, width - visibleLen));\r\n };\r\n\r\n const row = (content: string, w = 2) =>\r\n `${ansi.gray}\u2502${ansi.reset} ${pad(content, innerWidth - w)} ${ansi.gray}\u2502${ansi.reset}`;\r\n const label = (key: string, val: string) =>\r\n row(`${c('gray', key)} ${val}`);\r\n\r\n console.log('');\r\n console.log(`${ansi.gray}\u250C${line}\u2510${ansi.reset}`);\r\n console.log(row(` ${c('red', '\u2622\uFE0F nukejs ', true)}`, 1));\r\n console.log(`${ansi.gray}\u251C${line}\u2524${ansi.reset}`);\r\n console.log(label(' Local ', c('cyan', url, true)));\r\n console.log(`${ansi.gray}\u251C${line}\u2524${ansi.reset}`);\r\n console.log(label(' Pages ', c('white', path.relative(process.cwd(), PAGES_DIR))));\r\n console.log(label(' Server ', c('white', path.relative(process.cwd(), SERVER_DIR))));\r\n console.log(label(' Dev ', isDev ? c('green', 'yes') : c('gray', 'no')));\r\n console.log(label(' Debug ', level === false\r\n ? c('gray', 'off')\r\n : level === true\r\n ? c('green', 'verbose')\r\n : c('yellow', debugStr)));\r\n console.log(`${ansi.gray}\u2514${line}\u2518${ansi.reset}`);\r\n console.log('');\r\n}\r\n\r\nconst actualPort = await tryListen(PORT);\r\nprintStartupBanner(actualPort, isDev);"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["isDev"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
const Link = ({ href, children, className }: {
|
|
4
|
+
href: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
}) => {
|
|
8
|
+
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
9
|
+
e.preventDefault();
|
|
10
|
+
window.history.pushState({}, '', href);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<a href={href} onClick={handleClick} className={className}>
|
|
15
|
+
{children}
|
|
16
|
+
</a>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default Link;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
type Router = {
|
|
4
|
+
path: string;
|
|
5
|
+
push: (url: string) => void;
|
|
6
|
+
replace: (url: string) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function useRouter(): Router {
|
|
10
|
+
try {
|
|
11
|
+
const [path, setPath] = useState(() => window.location.pathname);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const handleLocationChange = () => setPath(window.location.pathname);
|
|
15
|
+
window.addEventListener("locationchange", handleLocationChange);
|
|
16
|
+
return () => window.removeEventListener("locationchange", handleLocationChange);
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
const push = useCallback((url: string) => {
|
|
20
|
+
window.history.pushState({}, "", url);
|
|
21
|
+
setPath(url);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const replace = useCallback((url: string) => {
|
|
25
|
+
window.history.replaceState({}, "", url);
|
|
26
|
+
setPath(url);
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return { path, push, replace };
|
|
30
|
+
} catch {
|
|
31
|
+
return { push: () => {}, replace: () => {}, path: "" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* build-common.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared build logic used by both build-vercel.ts and build-node.ts.
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* — utility helpers : walkFiles, analyzeFile, isServerComponent,
|
|
8
|
+
* findPageLayouts, extractDefaultExportName
|
|
9
|
+
* — collection : collectServerPages, collectGlobalClientRegistry
|
|
10
|
+
* — template codegen : makeApiAdapterSource, makePageAdapterSource
|
|
11
|
+
* — bundle operations : bundleApiHandler, bundlePageHandler,
|
|
12
|
+
* bundleClientComponents, buildReactBundle, buildNukeBundle
|
|
13
|
+
*/
|
|
14
|
+
export interface AnalyzedRoute {
|
|
15
|
+
srcRegex: string;
|
|
16
|
+
paramNames: string[];
|
|
17
|
+
/** Path used as function namespace, e.g. '/api/users' or '/page/about'. */
|
|
18
|
+
funcPath: string;
|
|
19
|
+
specificity: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ServerPage extends AnalyzedRoute {
|
|
22
|
+
absPath: string;
|
|
23
|
+
}
|
|
24
|
+
/** A server page together with its fully bundled ESM text, ready to emit. */
|
|
25
|
+
export interface BuiltPage extends ServerPage {
|
|
26
|
+
bundleText: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function walkFiles(dir: string, base?: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Parses dynamic-route segments from a relative file path and returns a regex,
|
|
31
|
+
* captured param names, a function path, and a specificity score.
|
|
32
|
+
*
|
|
33
|
+
* @param relPath Relative path from the dir root (e.g. 'users/[id].tsx').
|
|
34
|
+
* @param prefix Namespace for funcPath ('api' | 'page').
|
|
35
|
+
*/
|
|
36
|
+
export declare function analyzeFile(relPath: string, prefix?: string): AnalyzedRoute;
|
|
37
|
+
/**
|
|
38
|
+
* Returns true when a file does NOT begin with a "use client" directive —
|
|
39
|
+
* i.e. it is a server component.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isServerComponent(filePath: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Walks from the pages root to the directory containing `routeFilePath` and
|
|
44
|
+
* returns every layout.tsx found, in outermost-first order.
|
|
45
|
+
*/
|
|
46
|
+
export declare function findPageLayouts(routeFilePath: string, pagesDir: string): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Extracts the identifier used as the default export from a component file.
|
|
49
|
+
* Returns null when no default export is found.
|
|
50
|
+
*/
|
|
51
|
+
export declare function extractDefaultExportName(filePath: string): string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Returns all server-component pages inside `pagesDir`, sorted by specificity
|
|
54
|
+
* (most-specific first so more-precise routes shadow catch-alls in routers).
|
|
55
|
+
* layout.tsx files and "use client" files are excluded.
|
|
56
|
+
*/
|
|
57
|
+
export declare function collectServerPages(pagesDir: string): ServerPage[];
|
|
58
|
+
/**
|
|
59
|
+
* Walks every server page and its layout chain to collect all client component
|
|
60
|
+
* IDs reachable anywhere in the app. Deduplication is automatic because the
|
|
61
|
+
* Map key is the stable content-hash ID produced by component-analyzer.ts.
|
|
62
|
+
*/
|
|
63
|
+
export declare function collectGlobalClientRegistry(serverPages: ServerPage[], pagesDir: string): Map<string, string>;
|
|
64
|
+
/**
|
|
65
|
+
* Builds the per-page client component registry (page + its layout chain) and
|
|
66
|
+
* returns both the id→path map and the name→id map needed by bundlePageHandler.
|
|
67
|
+
*
|
|
68
|
+
* Extracted here to eliminate the identical loop duplicated across
|
|
69
|
+
* build-node.ts and build-vercel.ts.
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildPerPageRegistry(absPath: string, layoutPaths: string[], pagesDir: string): {
|
|
72
|
+
registry: Map<string, string>;
|
|
73
|
+
clientComponentNames: Record<string, string>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Runs both passes of the page build:
|
|
77
|
+
*
|
|
78
|
+
* Pass 1 — bundles all client components to `staticDir/__client-component/`
|
|
79
|
+
* and collects pre-rendered HTML for each.
|
|
80
|
+
* Pass 2 — bundles every server-component page into a self-contained ESM
|
|
81
|
+
* handler and returns the results as `BuiltPage[]`.
|
|
82
|
+
*
|
|
83
|
+
* Callers (build-node, build-vercel) only need to write the bundled text to
|
|
84
|
+
* their respective output destinations — the format-specific logic stays local.
|
|
85
|
+
*
|
|
86
|
+
* Returns an empty array when there are no server pages.
|
|
87
|
+
*/
|
|
88
|
+
export declare function buildPages(pagesDir: string, staticDir: string): Promise<BuiltPage[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Returns the TypeScript source for a thin HTTP adapter that wraps an API
|
|
91
|
+
* route module and exposes a single `handler(req, res)` default export.
|
|
92
|
+
*
|
|
93
|
+
* @param handlerFilename Basename of the handler file relative to the adapter
|
|
94
|
+
* (e.g. 'users.ts'). Must be in the same directory.
|
|
95
|
+
*/
|
|
96
|
+
export declare function makeApiAdapterSource(handlerFilename: string): string;
|
|
97
|
+
export interface PageAdapterOptions {
|
|
98
|
+
/** e.g. './home.tsx' — relative import for the page default export */
|
|
99
|
+
pageImport: string;
|
|
100
|
+
/** Newline-joined import statements for layout components */
|
|
101
|
+
layoutImports: string;
|
|
102
|
+
/** function-name → cc_id map, computed at build time */
|
|
103
|
+
clientComponentNames: Record<string, string>;
|
|
104
|
+
/** All client component IDs reachable from this page */
|
|
105
|
+
allClientIds: string[];
|
|
106
|
+
/** Comma-separated list of __layout_N__ identifiers */
|
|
107
|
+
layoutArrayItems: string;
|
|
108
|
+
/** Pre-rendered HTML per client component ID, computed at build time */
|
|
109
|
+
prerenderedHtml: Record<string, string>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Returns the TypeScript source for a fully self-contained page handler.
|
|
113
|
+
*
|
|
114
|
+
* The adapter:
|
|
115
|
+
* • Inlines the html-store so useHtml() works without external deps.
|
|
116
|
+
* • Contains an async recursive renderer that handles server + client
|
|
117
|
+
* components without react-dom/server.
|
|
118
|
+
* • Client components are identified via the pre-computed CLIENT_COMPONENTS
|
|
119
|
+
* map (no fs.readFileSync at runtime).
|
|
120
|
+
* • Emits the same full HTML document structure as ssr.ts including the
|
|
121
|
+
* __n_data blob, importmap, and initRuntime bootstrap.
|
|
122
|
+
*/
|
|
123
|
+
export declare function makePageAdapterSource(opts: PageAdapterOptions): string;
|
|
124
|
+
/**
|
|
125
|
+
* Bundles an API route handler into a single self-contained ESM string.
|
|
126
|
+
*
|
|
127
|
+
* Writes a temporary adapter next to `absPath`, bundles them together with
|
|
128
|
+
* esbuild (node_modules kept external), then removes the temp file.
|
|
129
|
+
*
|
|
130
|
+
* @returns The bundled ESM text ready to write to disk.
|
|
131
|
+
*/
|
|
132
|
+
export declare function bundleApiHandler(absPath: string): Promise<string>;
|
|
133
|
+
export interface PageBundleOptions {
|
|
134
|
+
absPath: string;
|
|
135
|
+
pagesDir: string;
|
|
136
|
+
clientComponentNames: Record<string, string>;
|
|
137
|
+
allClientIds: string[];
|
|
138
|
+
layoutPaths: string[];
|
|
139
|
+
prerenderedHtml: Record<string, string>;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Bundles a server-component page into a single self-contained ESM string.
|
|
143
|
+
*
|
|
144
|
+
* Writes a temporary adapter next to `absPath` (so relative imports inside
|
|
145
|
+
* the component resolve from the correct base directory), bundles it with
|
|
146
|
+
* esbuild (React and all npm deps inlined, only Node built-ins stay external),
|
|
147
|
+
* then removes the temp file.
|
|
148
|
+
*
|
|
149
|
+
* @returns The bundled ESM text ready to write to disk.
|
|
150
|
+
*/
|
|
151
|
+
export declare function bundlePageHandler(opts: PageBundleOptions): Promise<string>;
|
|
152
|
+
/**
|
|
153
|
+
* Bundles every client component in `globalRegistry` to
|
|
154
|
+
* `<staticDir>/__client-component/<id>.js`.
|
|
155
|
+
*
|
|
156
|
+
* Mirrors bundleClientComponent() in bundler.ts:
|
|
157
|
+
* • browser ESM, JSX automatic
|
|
158
|
+
* • react / react-dom/client / react/jsx-runtime kept external so the
|
|
159
|
+
* importmap can resolve them to the already-loaded /__react.js bundle.
|
|
160
|
+
*/
|
|
161
|
+
export declare function bundleClientComponents(globalRegistry: Map<string, string>, pagesDir: string, staticDir: string): Promise<Map<string, string>>;
|
|
162
|
+
/**
|
|
163
|
+
* Builds the full React + ReactDOM browser bundle to `<staticDir>/__react.js`.
|
|
164
|
+
* Exports every public hook and helper so client component bundles can import
|
|
165
|
+
* them via the importmap without bundling React a second time.
|
|
166
|
+
*/
|
|
167
|
+
export declare function buildReactBundle(staticDir: string): Promise<void>;
|
|
168
|
+
/**
|
|
169
|
+
* Builds the nukejs client runtime to `<staticDir>/__n.js`.
|
|
170
|
+
* React and react-dom/client are kept external (resolved via importmap).
|
|
171
|
+
*/
|
|
172
|
+
export declare function buildNukeBundle(staticDir: string): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Recursively copies every file from `app/public/` into `destDir`,
|
|
175
|
+
* preserving the directory structure.
|
|
176
|
+
*
|
|
177
|
+
* Called by both build-vercel.ts (dest = .vercel/output/static/) and
|
|
178
|
+
* build-node.ts (dest = dist/static/) so that:
|
|
179
|
+
*
|
|
180
|
+
* app/public/favicon.ico → <destDir>/favicon.ico
|
|
181
|
+
* app/public/images/logo.png → <destDir>/images/logo.png
|
|
182
|
+
*
|
|
183
|
+
* On Vercel, the Build Output API v3 serves everything in .vercel/output/static/
|
|
184
|
+
* directly — no route entry needed, same as __react.js and __n.js.
|
|
185
|
+
*
|
|
186
|
+
* On Node, the serverEntry template serves files from dist/static/ with the
|
|
187
|
+
* same MIME-type logic as the dev middleware.
|
|
188
|
+
*
|
|
189
|
+
* Skips silently when the public directory does not exist so projects without
|
|
190
|
+
* one don't need any special configuration.
|
|
191
|
+
*/
|
|
192
|
+
export declare function copyPublicFiles(publicDir: string, destDir: string): void;
|