nukejs 0.0.11 → 0.0.13
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/README.md +147 -0
- package/dist/Link.js +0 -1
- package/dist/app.js +0 -1
- package/dist/build-common.js +64 -14
- package/dist/build-node.js +63 -5
- package/dist/build-vercel.js +76 -9
- package/dist/builder.js +32 -4
- package/dist/bundle.js +47 -4
- package/dist/bundler.js +0 -1
- package/dist/component-analyzer.js +0 -1
- package/dist/config.js +0 -1
- package/dist/hmr-bundle.js +10 -1
- package/dist/hmr.js +7 -1
- package/dist/html-store.js +0 -1
- package/dist/http-server.js +0 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +0 -1
- package/dist/logger.js +0 -1
- package/dist/metadata.js +0 -1
- package/dist/middleware-loader.js +0 -1
- package/dist/middleware.example.js +0 -1
- package/dist/middleware.js +0 -1
- package/dist/renderer.js +3 -9
- package/dist/request-store.js +0 -1
- package/dist/router.js +0 -1
- package/dist/ssr.js +73 -16
- package/dist/use-html.js +0 -1
- package/dist/use-request.js +0 -1
- package/dist/use-router.js +0 -1
- package/dist/utils.js +0 -1
- package/package.json +1 -1
- package/dist/Link.js.map +0 -7
- package/dist/app.d.ts +0 -19
- package/dist/app.js.map +0 -7
- package/dist/build-common.d.ts +0 -178
- package/dist/build-common.js.map +0 -7
- package/dist/build-node.d.ts +0 -15
- package/dist/build-node.js.map +0 -7
- package/dist/build-vercel.d.ts +0 -19
- package/dist/build-vercel.js.map +0 -7
- package/dist/builder.d.ts +0 -11
- package/dist/builder.js.map +0 -7
- package/dist/bundle.js.map +0 -7
- package/dist/bundler.d.ts +0 -58
- package/dist/bundler.js.map +0 -7
- package/dist/component-analyzer.d.ts +0 -75
- package/dist/component-analyzer.js.map +0 -7
- package/dist/config.d.ts +0 -35
- package/dist/config.js.map +0 -7
- package/dist/hmr-bundle.d.ts +0 -25
- package/dist/hmr-bundle.js.map +0 -7
- package/dist/hmr.d.ts +0 -55
- package/dist/hmr.js.map +0 -7
- package/dist/html-store.js.map +0 -7
- package/dist/http-server.d.ts +0 -92
- package/dist/http-server.js.map +0 -7
- package/dist/index.js.map +0 -7
- package/dist/logger.js.map +0 -7
- package/dist/metadata.d.ts +0 -51
- package/dist/metadata.js.map +0 -7
- package/dist/middleware-loader.d.ts +0 -50
- package/dist/middleware-loader.js.map +0 -7
- package/dist/middleware.d.ts +0 -22
- package/dist/middleware.example.d.ts +0 -8
- package/dist/middleware.example.js.map +0 -7
- package/dist/middleware.js.map +0 -7
- package/dist/renderer.d.ts +0 -44
- package/dist/renderer.js.map +0 -7
- package/dist/request-store.js.map +0 -7
- package/dist/router.d.ts +0 -92
- package/dist/router.js.map +0 -7
- package/dist/ssr.d.ts +0 -46
- package/dist/ssr.js.map +0 -7
- package/dist/use-html.js.map +0 -7
- package/dist/use-request.js.map +0 -7
- package/dist/use-router.js.map +0 -7
- package/dist/utils.js.map +0 -7
package/dist/bundle.js
CHANGED
|
@@ -77,9 +77,38 @@ async function loadModules(ids, log, bust = "") {
|
|
|
77
77
|
return mods;
|
|
78
78
|
}
|
|
79
79
|
const activeRoots = [];
|
|
80
|
+
let clientErrorPending = false;
|
|
81
|
+
function navigateToClientError(err) {
|
|
82
|
+
if (clientErrorPending) return;
|
|
83
|
+
if (window.location.search.includes("__clientError")) return;
|
|
84
|
+
clientErrorPending = true;
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
const stack = err instanceof Error && err.stack ? err.stack : void 0;
|
|
87
|
+
const params = new URLSearchParams();
|
|
88
|
+
params.set("__clientError", message);
|
|
89
|
+
if (stack) params.set("__clientStack", stack);
|
|
90
|
+
window.dispatchEvent(new CustomEvent("locationchange", {
|
|
91
|
+
detail: { href: window.location.pathname + "?" + params.toString() }
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
80
94
|
async function mountNodes(mods, log) {
|
|
81
95
|
const { hydrateRoot, createRoot } = await import("react-dom/client");
|
|
82
96
|
const React = await import("react");
|
|
97
|
+
class NukeErrorBoundary extends React.default.Component {
|
|
98
|
+
constructor(props) {
|
|
99
|
+
super(props);
|
|
100
|
+
this.state = { caught: false };
|
|
101
|
+
}
|
|
102
|
+
static getDerivedStateFromError() {
|
|
103
|
+
return { caught: true };
|
|
104
|
+
}
|
|
105
|
+
componentDidCatch(error) {
|
|
106
|
+
navigateToClientError(error);
|
|
107
|
+
}
|
|
108
|
+
render() {
|
|
109
|
+
return this.state.caught ? null : this.props.children;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
83
112
|
const nodes = document.querySelectorAll("[data-hydrate-id]");
|
|
84
113
|
log.verbose("Found", nodes.length, "hydration point(s)");
|
|
85
114
|
for (const node of nodes) {
|
|
@@ -97,7 +126,8 @@ async function mountNodes(mods, log) {
|
|
|
97
126
|
log.error("Props parse error for", id, e);
|
|
98
127
|
}
|
|
99
128
|
try {
|
|
100
|
-
const
|
|
129
|
+
const inner = React.default.createElement(Comp, await reconstructProps(rawProps, mods));
|
|
130
|
+
const element = React.default.createElement(NukeErrorBoundary, null, inner);
|
|
101
131
|
let root;
|
|
102
132
|
if (node.innerHTML.trim()) {
|
|
103
133
|
root = hydrateRoot(node, element);
|
|
@@ -223,8 +253,13 @@ function setupNavigation(log) {
|
|
|
223
253
|
const fetchUrl = hmr ? href + (href.includes("?") ? "&" : "?") + "__hmr=1" : href;
|
|
224
254
|
const response = await fetch(fetchUrl, { headers: { Accept: "text/html" } });
|
|
225
255
|
if (!response.ok) {
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
257
|
+
if (!ct.includes("text/html")) {
|
|
258
|
+
log.error("Navigation fetch failed:", response.status, "\u2014 falling back to full reload");
|
|
259
|
+
window.location.href = href;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
log.info("Navigation returned", response.status, "\u2014 rendering error page in-place");
|
|
228
263
|
}
|
|
229
264
|
const parser = new DOMParser();
|
|
230
265
|
const doc = parser.parseFromString(await response.text(), "text/html");
|
|
@@ -251,6 +286,8 @@ function setupNavigation(log) {
|
|
|
251
286
|
} catch (err) {
|
|
252
287
|
log.error("Navigation error, falling back to full reload:", err);
|
|
253
288
|
window.location.href = href;
|
|
289
|
+
} finally {
|
|
290
|
+
clientErrorPending = false;
|
|
254
291
|
}
|
|
255
292
|
});
|
|
256
293
|
}
|
|
@@ -258,6 +295,13 @@ async function initRuntime(data) {
|
|
|
258
295
|
const log = makeLogger(data.debug ?? "silent");
|
|
259
296
|
log.info("\u{1F680} Partial hydration:", data.hydrateIds.length, "root component(s)");
|
|
260
297
|
setupNavigation(log);
|
|
298
|
+
window.onerror = (_msg, _src, _line, _col, err) => {
|
|
299
|
+
navigateToClientError(err ?? _msg);
|
|
300
|
+
return true;
|
|
301
|
+
};
|
|
302
|
+
window.onunhandledrejection = (e) => {
|
|
303
|
+
navigateToClientError(e.reason);
|
|
304
|
+
};
|
|
261
305
|
const mods = await loadModules(data.allIds, log);
|
|
262
306
|
await mountNodes(mods, log);
|
|
263
307
|
log.info("\u{1F389} Done!");
|
|
@@ -267,4 +311,3 @@ export {
|
|
|
267
311
|
initRuntime,
|
|
268
312
|
setupLocationChangeMonitor
|
|
269
313
|
};
|
|
270
|
-
//# sourceMappingURL=bundle.js.map
|
package/dist/bundler.js
CHANGED
package/dist/config.js
CHANGED
package/dist/hmr-bundle.js
CHANGED
|
@@ -28,6 +28,16 @@ function hmr() {
|
|
|
28
28
|
}
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
+
if (msg.type === "layout-reload") {
|
|
32
|
+
const base = msg.base === "/" ? "" : msg.base;
|
|
33
|
+
const pathname = window.location.pathname;
|
|
34
|
+
const isUnder = pathname === (base || "/") || pathname.startsWith(base + "/");
|
|
35
|
+
if (isUnder) {
|
|
36
|
+
log.info("[HMR] Layout changed:", msg.base);
|
|
37
|
+
navigate(pathname + window.location.search);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
31
41
|
if (msg.type === "replace") {
|
|
32
42
|
log.info("[HMR] Component changed:", msg.component);
|
|
33
43
|
navigate(window.location.pathname + window.location.search);
|
|
@@ -86,4 +96,3 @@ hmr();
|
|
|
86
96
|
export {
|
|
87
97
|
hmr as default
|
|
88
98
|
};
|
|
89
|
-
//# sourceMappingURL=hmr-bundle.js.map
|
package/dist/hmr.js
CHANGED
|
@@ -24,7 +24,14 @@ function pageFileToUrl(filename) {
|
|
|
24
24
|
function buildPayload(filename) {
|
|
25
25
|
const normalized = filename.replace(/\\/g, "/");
|
|
26
26
|
if (normalized.startsWith("pages/")) {
|
|
27
|
+
const stem = path.basename(normalized, path.extname(normalized));
|
|
28
|
+
if (stem === "_404" || stem === "_500") {
|
|
29
|
+
return { type: "replace", component: stem };
|
|
30
|
+
}
|
|
27
31
|
const url = pageFileToUrl(normalized);
|
|
32
|
+
if (stem === "layout") {
|
|
33
|
+
return { type: "layout-reload", base: url };
|
|
34
|
+
}
|
|
28
35
|
return { type: "reload", url };
|
|
29
36
|
}
|
|
30
37
|
const ext = path.extname(filename).toLowerCase();
|
|
@@ -59,4 +66,3 @@ export {
|
|
|
59
66
|
hmrClients,
|
|
60
67
|
watchDir
|
|
61
68
|
};
|
|
62
|
-
//# sourceMappingURL=hmr.js.map
|
package/dist/html-store.js
CHANGED
package/dist/http-server.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { useHtml } from './use-html';
|
|
2
|
-
export type { HtmlOptions
|
|
2
|
+
export type { HtmlOptions } from './use-html';
|
|
3
|
+
export type { TitleValue, HtmlAttrs, BodyAttrs, MetaTag, LinkTag, ScriptTag, StyleTag, } from './html-store';
|
|
3
4
|
export { default as useRouter } from './use-router';
|
|
4
5
|
export { useRequest } from './use-request';
|
|
5
6
|
export type { RequestContext } from './use-request';
|
package/dist/index.js
CHANGED
package/dist/logger.js
CHANGED
package/dist/metadata.js
CHANGED
package/dist/middleware.js
CHANGED
package/dist/renderer.js
CHANGED
|
@@ -96,14 +96,9 @@ async function renderFunctionComponent(type, props, ctx) {
|
|
|
96
96
|
return `<div style="color:red">Error rendering client component: ${escapeHtml(String(err))}</div>`;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return renderElementToHtml(resolved, ctx);
|
|
103
|
-
} catch (err) {
|
|
104
|
-
log.error("Error rendering component:", err);
|
|
105
|
-
return `<div style="color:red">Error rendering component: ${escapeHtml(String(err))}</div>`;
|
|
106
|
-
}
|
|
99
|
+
const result = type(props);
|
|
100
|
+
const resolved = result?.then ? await result : result;
|
|
101
|
+
return renderElementToHtml(resolved, ctx);
|
|
107
102
|
}
|
|
108
103
|
function serializePropsForHydration(props, registry) {
|
|
109
104
|
if (!props || typeof props !== "object") return props;
|
|
@@ -153,4 +148,3 @@ function serializeReactElement(element, registry) {
|
|
|
153
148
|
export {
|
|
154
149
|
renderElementToHtml
|
|
155
150
|
};
|
|
156
|
-
//# sourceMappingURL=renderer.js.map
|
package/dist/request-store.js
CHANGED
package/dist/router.js
CHANGED
package/dist/ssr.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
2
3
|
import { createElement } from "react";
|
|
3
4
|
import { pathToFileURL } from "url";
|
|
4
5
|
import { tsImport } from "tsx/esm/api";
|
|
@@ -90,18 +91,8 @@ function renderManagedBodyScripts(store) {
|
|
|
90
91
|
if (bodyScripts.length === 0) return [];
|
|
91
92
|
return [" <!--n-body-scripts-->", ...bodyScripts.map(renderScriptTag), " <!--/n-body-scripts-->"];
|
|
92
93
|
}
|
|
93
|
-
async function
|
|
94
|
-
const skipClientSSR = url.includes("__hmr=1");
|
|
94
|
+
async function renderFile(filePath, params, url, pagesDir, isDev, res, req, statusCode, skipClientSSR) {
|
|
95
95
|
const cleanUrl = url.split("?")[0];
|
|
96
|
-
const routeMatch = matchRoute(cleanUrl, pagesDir);
|
|
97
|
-
if (!routeMatch) {
|
|
98
|
-
log.verbose(`No route found for: ${url}`);
|
|
99
|
-
res.statusCode = 404;
|
|
100
|
-
res.end("Page not found");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const { filePath, params, routePattern } = routeMatch;
|
|
104
|
-
log.verbose(`SSR ${cleanUrl} -> ${path.relative(process.cwd(), filePath)}`);
|
|
105
96
|
const searchParams = new URL(url, "http://localhost").searchParams;
|
|
106
97
|
const queryParams = {};
|
|
107
98
|
searchParams.forEach((_, k) => {
|
|
@@ -129,10 +120,6 @@ async function serverSideRender(url, res, pagesDir, isDev = false, req) {
|
|
|
129
120
|
for (const layoutPath of layoutPaths)
|
|
130
121
|
for (const [id, p] of findClientComponentsInTree(layoutPath, pagesDir))
|
|
131
122
|
registry.set(id, p);
|
|
132
|
-
log.verbose(
|
|
133
|
-
`Page ${routePattern}: found ${registry.size} client component(s)`,
|
|
134
|
-
`[${[...registry.keys()].join(", ")}]`
|
|
135
|
-
);
|
|
136
123
|
const ctx = { registry, hydrated: /* @__PURE__ */ new Set(), skipClientSSR };
|
|
137
124
|
let appHtml = "";
|
|
138
125
|
const store = await runWithRequestStore(
|
|
@@ -196,10 +183,80 @@ ${openTag("body", store.bodyAttrs)}
|
|
|
196
183
|
${isDev ? '<script type="module" src="/__hmr.js"></script>' : ""}
|
|
197
184
|
${bodyScriptsHtml}</body>
|
|
198
185
|
</html>`;
|
|
186
|
+
res.statusCode = statusCode;
|
|
199
187
|
res.setHeader("Content-Type", "text/html");
|
|
200
188
|
res.end(html);
|
|
201
189
|
}
|
|
190
|
+
function serializeError(err) {
|
|
191
|
+
if (err instanceof Error) {
|
|
192
|
+
const props = {
|
|
193
|
+
errorMessage: err.message
|
|
194
|
+
};
|
|
195
|
+
if (process.env.NODE_ENV !== "production" && err.stack)
|
|
196
|
+
props.errorStack = err.stack;
|
|
197
|
+
const status = err.status ?? err.statusCode;
|
|
198
|
+
if (status != null)
|
|
199
|
+
props.errorStatus = String(status);
|
|
200
|
+
return props;
|
|
201
|
+
}
|
|
202
|
+
return { errorMessage: String(err) };
|
|
203
|
+
}
|
|
204
|
+
async function tryRenderErrorPage(statusCode, pagesDir, res, isDev, req, error) {
|
|
205
|
+
const errorFile = path.join(pagesDir, `_${statusCode}.tsx`);
|
|
206
|
+
if (!fs.existsSync(errorFile)) return false;
|
|
207
|
+
const errorProps = error != null ? serializeError(error) : {};
|
|
208
|
+
try {
|
|
209
|
+
await renderFile(errorFile, errorProps, "/", pagesDir, isDev, res, req, statusCode, false);
|
|
210
|
+
return true;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
log.error(`Error rendering _${statusCode}.tsx:`, err);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function serverSideRender(url, res, pagesDir, isDev = false, req) {
|
|
217
|
+
const skipClientSSR = url.includes("__hmr=1");
|
|
218
|
+
const cleanUrl = url.split("?")[0];
|
|
219
|
+
const searchParams = new URL(url, "http://localhost").searchParams;
|
|
220
|
+
if (searchParams.has("__clientError")) {
|
|
221
|
+
const errorProps = {
|
|
222
|
+
errorMessage: searchParams.get("__clientError") ?? "Client error"
|
|
223
|
+
};
|
|
224
|
+
const stack = searchParams.get("__clientStack");
|
|
225
|
+
if (stack) errorProps.errorStack = stack;
|
|
226
|
+
const errorFile = path.join(pagesDir, "_500.tsx");
|
|
227
|
+
if (fs.existsSync(errorFile)) {
|
|
228
|
+
try {
|
|
229
|
+
await renderFile(errorFile, errorProps, url, pagesDir, isDev, res, req, 500, false);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
log.error("Error rendering _500.tsx for client error:", err);
|
|
232
|
+
res.statusCode = 500;
|
|
233
|
+
res.end("Internal Server Error");
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
res.statusCode = 500;
|
|
237
|
+
res.end("Internal Server Error");
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const routeMatch = matchRoute(cleanUrl, pagesDir);
|
|
242
|
+
if (!routeMatch) {
|
|
243
|
+
log.verbose(`No route found for: ${url}`);
|
|
244
|
+
if (await tryRenderErrorPage(404, pagesDir, res, isDev, req)) return;
|
|
245
|
+
res.statusCode = 404;
|
|
246
|
+
res.end("Page not found");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const { filePath, params, routePattern } = routeMatch;
|
|
250
|
+
log.verbose(`SSR ${cleanUrl} -> ${path.relative(process.cwd(), filePath)}`);
|
|
251
|
+
try {
|
|
252
|
+
await renderFile(filePath, params, url, pagesDir, isDev, res, req, 200, skipClientSSR);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
log.error("SSR render error:", err);
|
|
255
|
+
if (await tryRenderErrorPage(500, pagesDir, res, isDev, req, err)) return;
|
|
256
|
+
res.statusCode = 500;
|
|
257
|
+
res.end("Internal Server Error");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
202
260
|
export {
|
|
203
261
|
serverSideRender
|
|
204
262
|
};
|
|
205
|
-
//# sourceMappingURL=ssr.js.map
|
package/dist/use-html.js
CHANGED
package/dist/use-request.js
CHANGED
package/dist/use-router.js
CHANGED
package/dist/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nukejs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "A minimal, opinionated full-stack React framework on Node.js that server-renders everything and hydrates only interactive parts.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
package/dist/Link.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/Link.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\r\nimport useRouter from \"./use-router\";\r\n\r\ninterface LinkProps {\r\n href: string;\r\n children: React.ReactNode;\r\n className?: string;\r\n}\r\n\r\n/**\r\n * Client-side navigation link.\r\n * Intercepts clicks and delegates to useRouter().push() so the SPA router\r\n * handles the transition without a full page reload.\r\n */\r\nconst Link = ({ href, children, className }: LinkProps) => {\r\n const { push } = useRouter();\r\n\r\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n e.preventDefault();\r\n push(href);\r\n };\r\n\r\n return (\r\n <a href={href} onClick={handleClick} className={className}>\r\n {children}\r\n </a>\r\n );\r\n};\r\n\r\nexport default Link;"],
|
|
5
|
-
"mappings": ";AAuBI;AAtBJ,OAAO,eAAe;AAatB,MAAM,OAAO,CAAC,EAAE,MAAM,UAAU,UAAU,MAAiB;AACzD,QAAM,EAAE,KAAK,IAAI,UAAU;AAE3B,QAAM,cAAc,CAAC,MAA2C;AAC9D,MAAE,eAAe;AACjB,SAAK,IAAI;AAAA,EACX;AAEA,SACE,oBAAC,OAAE,MAAY,SAAS,aAAa,WAClC,UACH;AAEJ;AAEA,IAAO,eAAQ;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/app.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* app.ts — NukeJS Dev Server Entry Point
|
|
3
|
-
*
|
|
4
|
-
* This is the runtime that powers `nuke dev`. It:
|
|
5
|
-
* 1. Loads your nuke.config.ts (or uses sensible defaults)
|
|
6
|
-
* 2. Discovers API route prefixes from your server directory
|
|
7
|
-
* 3. Starts an HTTP server that handles:
|
|
8
|
-
* app/public/** — static files (highest priority, via middleware)
|
|
9
|
-
* /__hmr_ping — heartbeat for HMR reconnect polling
|
|
10
|
-
* /__react.js — bundled React + ReactDOM (resolved via importmap)
|
|
11
|
-
* /__n.js — NukeJS client runtime bundle
|
|
12
|
-
* /__client-component/* — on-demand "use client" component bundles
|
|
13
|
-
* server/** — API route handlers from serverDir
|
|
14
|
-
* /** — SSR pages from app/pages (lowest priority)
|
|
15
|
-
* 4. Watches for file changes and broadcasts HMR events to connected browsers
|
|
16
|
-
*
|
|
17
|
-
* In production (ENVIRONMENT=production), HMR and all file watching are skipped.
|
|
18
|
-
*/
|
|
19
|
-
export {};
|
package/dist/app.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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 * app/public/** \u2014 static files (highest priority, via middleware)\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 * server/** \u2014 API route handlers from serverDir\r\n * /** \u2014 SSR pages from app/pages (lowest priority)\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, isDev });\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 // \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 // Framework files are checked before server routes so a user route can\r\n // never accidentally shadow /__n.js, /__react.js, etc.\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 Server 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n // API routes from serverDir \u2014 checked after framework files, before pages.\r\n if (matchApiPrefix(url, apiPrefixes))\r\n return await handleApiRoute(url, req, res);\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 // Nothing above matched \u2014 render a page from app/pages.\r\n return await serverSideRender(url, res, PAGES_DIR, isDev, req);\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
|
-
}
|
package/dist/build-common.d.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* build-common.ts — Shared Build Logic
|
|
3
|
-
*
|
|
4
|
-
* Used by both build-node.ts and build-vercel.ts.
|
|
5
|
-
*
|
|
6
|
-
* Exports:
|
|
7
|
-
* — types : AnalyzedRoute, ServerPage, BuiltPage,
|
|
8
|
-
* PageAdapterOptions, PageBundleOptions
|
|
9
|
-
* — utility helpers : walkFiles, analyzeFile, isServerComponent,
|
|
10
|
-
* findPageLayouts, extractDefaultExportName
|
|
11
|
-
* — collection : collectServerPages, collectGlobalClientRegistry,
|
|
12
|
-
* buildPerPageRegistry
|
|
13
|
-
* — template codegen : makeApiAdapterSource, makePageAdapterSource
|
|
14
|
-
* — bundle ops : bundleApiHandler, bundlePageHandler,
|
|
15
|
-
* bundleClientComponents, buildPages,
|
|
16
|
-
* buildCombinedBundle, copyPublicFiles
|
|
17
|
-
*/
|
|
18
|
-
export interface AnalyzedRoute {
|
|
19
|
-
/** Regex string matching the URL path, e.g. '^/users/([^/]+)$' */
|
|
20
|
-
srcRegex: string;
|
|
21
|
-
/** Names of captured groups in srcRegex order */
|
|
22
|
-
paramNames: string[];
|
|
23
|
-
/**
|
|
24
|
-
* Subset of paramNames that are catch-all ([...slug] or [[...path]]).
|
|
25
|
-
* Their runtime values are string[] not string.
|
|
26
|
-
*/
|
|
27
|
-
catchAllNames: string[];
|
|
28
|
-
/** Function namespace path, e.g. '/api/users' or '/page/about' */
|
|
29
|
-
funcPath: string;
|
|
30
|
-
specificity: number;
|
|
31
|
-
}
|
|
32
|
-
export interface ServerPage extends AnalyzedRoute {
|
|
33
|
-
absPath: string;
|
|
34
|
-
}
|
|
35
|
-
export interface BuiltPage extends ServerPage {
|
|
36
|
-
bundleText: string;
|
|
37
|
-
}
|
|
38
|
-
export declare function walkFiles(dir: string, base?: string): string[];
|
|
39
|
-
/**
|
|
40
|
-
* Parses dynamic-route segments from a relative file path and returns a regex,
|
|
41
|
-
* captured param names, catch-all param names, a function path, and a
|
|
42
|
-
* specificity score.
|
|
43
|
-
*
|
|
44
|
-
* Supported patterns per segment:
|
|
45
|
-
* [[...name]] optional catch-all → regex (.*) → string[]
|
|
46
|
-
* [...name] required catch-all → regex (.+) → string[]
|
|
47
|
-
* [[name]] optional single → regex ([^/]*)? → string
|
|
48
|
-
* [name] required single → regex ([^/]+) → string
|
|
49
|
-
* literal static → escaped literal
|
|
50
|
-
*
|
|
51
|
-
* @param relPath Relative path from the dir root (e.g. 'users/[id].tsx').
|
|
52
|
-
* @param prefix Namespace for funcPath ('api' | 'page').
|
|
53
|
-
*/
|
|
54
|
-
export declare function analyzeFile(relPath: string, prefix?: string): AnalyzedRoute;
|
|
55
|
-
/**
|
|
56
|
-
* Returns true when a file does NOT begin with a "use client" directive,
|
|
57
|
-
* i.e. it is a server component.
|
|
58
|
-
*/
|
|
59
|
-
export declare function isServerComponent(filePath: string): boolean;
|
|
60
|
-
/**
|
|
61
|
-
* Walks from the pages root to the directory containing `routeFilePath` and
|
|
62
|
-
* returns every layout.tsx found, in outermost-first order.
|
|
63
|
-
*/
|
|
64
|
-
export declare function findPageLayouts(routeFilePath: string, pagesDir: string): string[];
|
|
65
|
-
/**
|
|
66
|
-
* Extracts the identifier used as the default export from a component file.
|
|
67
|
-
* Returns null when no default export is found.
|
|
68
|
-
*
|
|
69
|
-
* Handles three formats so that components compiled by esbuild are recognised
|
|
70
|
-
* alongside hand-written source files:
|
|
71
|
-
* 1. Source: `export default function Foo` / `export default Foo`
|
|
72
|
-
* 2. esbuild: `var Foo_default = Foo` (compiled arrow-function component)
|
|
73
|
-
* 3. Re-export: `export { Foo as default }`
|
|
74
|
-
*/
|
|
75
|
-
export declare function extractDefaultExportName(filePath: string): string | null;
|
|
76
|
-
/**
|
|
77
|
-
* Returns all server-component pages inside `pagesDir`, sorted most-specific
|
|
78
|
-
* first so precise routes shadow catch-alls in routers.
|
|
79
|
-
* layout.tsx files and "use client" files are excluded.
|
|
80
|
-
*/
|
|
81
|
-
export declare function collectServerPages(pagesDir: string): ServerPage[];
|
|
82
|
-
/**
|
|
83
|
-
* Walks every server page and its layout chain to collect all client component
|
|
84
|
-
* IDs reachable anywhere in the app.
|
|
85
|
-
*/
|
|
86
|
-
export declare function collectGlobalClientRegistry(serverPages: ServerPage[], pagesDir: string): Map<string, string>;
|
|
87
|
-
/**
|
|
88
|
-
* Builds the per-page client component registry (page + its layout chain)
|
|
89
|
-
* and returns both the id→path map and the name→id map needed by
|
|
90
|
-
* bundlePageHandler.
|
|
91
|
-
*/
|
|
92
|
-
export declare function buildPerPageRegistry(absPath: string, layoutPaths: string[], pagesDir: string): {
|
|
93
|
-
registry: Map<string, string>;
|
|
94
|
-
clientComponentNames: Record<string, string>;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Runs both passes of the page build:
|
|
98
|
-
*
|
|
99
|
-
* Pass 1 — bundles all client components to `staticDir/__client-component/`
|
|
100
|
-
* and collects pre-rendered HTML for each.
|
|
101
|
-
* Pass 2 — bundles every server-component page into a self-contained ESM
|
|
102
|
-
* handler and returns the results as `BuiltPage[]`.
|
|
103
|
-
*/
|
|
104
|
-
export declare function buildPages(pagesDir: string, staticDir: string): Promise<BuiltPage[]>;
|
|
105
|
-
/**
|
|
106
|
-
* Returns the TypeScript source for a thin HTTP adapter that wraps an API
|
|
107
|
-
* route module and exposes a single `handler(req, res)` default export.
|
|
108
|
-
*/
|
|
109
|
-
export declare function makeApiAdapterSource(handlerFilename: string): string;
|
|
110
|
-
export interface PageAdapterOptions {
|
|
111
|
-
/** e.g. './home.tsx' — relative import for the page default export */
|
|
112
|
-
pageImport: string;
|
|
113
|
-
/** Newline-joined import statements for layout components */
|
|
114
|
-
layoutImports: string;
|
|
115
|
-
/** function-name → cc_id map, computed at build time */
|
|
116
|
-
clientComponentNames: Record<string, string>;
|
|
117
|
-
/** All client component IDs reachable from this page */
|
|
118
|
-
allClientIds: string[];
|
|
119
|
-
/** Comma-separated list of __layout_N__ identifiers */
|
|
120
|
-
layoutArrayItems: string;
|
|
121
|
-
/** Pre-rendered HTML per client component ID, computed at build time */
|
|
122
|
-
prerenderedHtml: Record<string, string>;
|
|
123
|
-
/**
|
|
124
|
-
* All dynamic route param names for this page (e.g. ['id', 'slug']).
|
|
125
|
-
* Used to distinguish route segments from real query-string params at runtime.
|
|
126
|
-
*/
|
|
127
|
-
routeParamNames: string[];
|
|
128
|
-
/** Subset of routeParamNames whose values are string[] (catch-all segments) */
|
|
129
|
-
catchAllNames: string[];
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Returns the TypeScript source for a fully self-contained page handler.
|
|
133
|
-
*
|
|
134
|
-
* The adapter:
|
|
135
|
-
* • Inlines the html-store so useHtml() works without external deps.
|
|
136
|
-
* • Contains an async recursive renderer for server + client components.
|
|
137
|
-
* • Client components are identified via the pre-computed CLIENT_COMPONENTS
|
|
138
|
-
* map — no fs.readFileSync at runtime.
|
|
139
|
-
* • Emits the full HTML document including the __n_data blob and bootstrap.
|
|
140
|
-
*/
|
|
141
|
-
export declare function makePageAdapterSource(opts: PageAdapterOptions): string;
|
|
142
|
-
/**
|
|
143
|
-
* Bundles an API route handler into a single self-contained ESM string.
|
|
144
|
-
* node_modules are kept external — they exist at runtime on both Node and
|
|
145
|
-
* Vercel (Vercel bundles them separately via the pages dispatcher).
|
|
146
|
-
*/
|
|
147
|
-
export declare function bundleApiHandler(absPath: string): Promise<string>;
|
|
148
|
-
export interface PageBundleOptions {
|
|
149
|
-
absPath: string;
|
|
150
|
-
pagesDir: string;
|
|
151
|
-
clientComponentNames: Record<string, string>;
|
|
152
|
-
allClientIds: string[];
|
|
153
|
-
layoutPaths: string[];
|
|
154
|
-
prerenderedHtml: Record<string, string>;
|
|
155
|
-
routeParamNames: string[];
|
|
156
|
-
catchAllNames: string[];
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Bundles a server-component page into a single self-contained ESM string.
|
|
160
|
-
* All npm packages are kept external — the Node production server has
|
|
161
|
-
* node_modules available at runtime.
|
|
162
|
-
*/
|
|
163
|
-
export declare function bundlePageHandler(opts: PageBundleOptions): Promise<string>;
|
|
164
|
-
/**
|
|
165
|
-
* Bundles every client component in `globalRegistry` to
|
|
166
|
-
* `<staticDir>/__client-component/<id>.js` and pre-renders each to HTML.
|
|
167
|
-
*/
|
|
168
|
-
export declare function bundleClientComponents(globalRegistry: Map<string, string>, pagesDir: string, staticDir: string): Promise<Map<string, string>>;
|
|
169
|
-
/**
|
|
170
|
-
* Builds the combined browser bundle (__n.js) that contains the full React
|
|
171
|
-
* runtime + NukeJS client runtime in a single file.
|
|
172
|
-
*/
|
|
173
|
-
export declare function buildCombinedBundle(staticDir: string): Promise<void>;
|
|
174
|
-
/**
|
|
175
|
-
* Recursively copies every file from `publicDir` into `destDir`, preserving
|
|
176
|
-
* the directory structure. Skips silently when `publicDir` does not exist.
|
|
177
|
-
*/
|
|
178
|
-
export declare function copyPublicFiles(publicDir: string, destDir: string): void;
|