nukejs 0.0.16 → 0.0.17

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/dist/bundle.d.ts DELETED
@@ -1,85 +0,0 @@
1
- /**
2
- * bundle.ts — NukeJS Client Runtime
3
- *
4
- * This file is compiled by esbuild into /__n.js and served to every page.
5
- * It provides:
6
- *
7
- * initRuntime(data) — called once per page load to hydrate
8
- * "use client" components and wire up SPA nav
9
- * setupLocationChangeMonitor() — patches history.pushState/replaceState so
10
- * SPA navigation fires a 'locationchange' event
11
- *
12
- * Hydration model (partial hydration):
13
- * - The server renders the full page to HTML, wrapping each client component
14
- * in a <span data-hydrate-id="cc_…" data-hydrate-props="…"> marker.
15
- * - initRuntime loads the matching JS bundle for each marker and calls
16
- * hydrateRoot() on it, letting React take over just that subtree.
17
- * - Props serialized by the server may include nested React elements
18
- * (serialized as { __re: 'html'|'client', … }), which are reconstructed
19
- * back into React.createElement calls before mounting.
20
- *
21
- * SPA navigation:
22
- * - Link clicks / programmatic navigation dispatch a 'locationchange' event.
23
- * - The handler fetches the target URL as HTML, diffs the #app container,
24
- * unmounts the old React roots, and re-hydrates the new ones.
25
- * - HMR navigations add ?__hmr=1 so the server skips client-SSR (faster).
26
- *
27
- * Head tag management:
28
- * - The SSR renderer wraps every useHtml()-generated <meta>, <link>, <style>,
29
- * and <script> tag in <!--n-head-->…<!--/n-head--> sentinel comments.
30
- * - On each navigation the client diffs the live sentinel block against the
31
- * incoming one by fingerprint, adding new tags and removing gone ones.
32
- * Tags shared between pages (e.g. a layout stylesheet) are left untouched
33
- * so there is no removal/re-insertion flash.
34
- * - New tags are always inserted before <!--/n-head--> so they stay inside
35
- * the tracked block and remain visible to the diff on subsequent navigations.
36
- */
37
- /**
38
- * Patches history.pushState and history.replaceState to fire a custom
39
- * 'locationchange' event on window. Also listens to 'popstate' for
40
- * back/forward navigation.
41
- *
42
- * Called after initRuntime sets up the navigation listener so there is no
43
- * race between the event firing and the listener being registered.
44
- */
45
- export declare function setupLocationChangeMonitor(): void;
46
- type ClientDebugLevel = 'silent' | 'error' | 'info' | 'verbose';
47
- /** Shape of the JSON blob embedded as #__n_data in every SSR page. */
48
- export interface RuntimeData {
49
- /** IDs of client components actually rendered on this page (subset of allIds). */
50
- hydrateIds: string[];
51
- /** All client component IDs reachable from this page, including layouts.
52
- * Pre-loaded so SPA navigations to related pages feel instant. */
53
- allIds: string[];
54
- url: string;
55
- params: Record<string, any>;
56
- /** Query string parameters parsed from the URL. Multi-value keys are arrays. */
57
- query: Record<string, string | string[]>;
58
- /**
59
- * Safe subset of the incoming request headers (cookie, authorization, and
60
- * proxy-authorization are stripped before embedding in the HTML document).
61
- */
62
- headers: Record<string, string>;
63
- debug: ClientDebugLevel;
64
- }
65
- /**
66
- * Bootstraps the NukeJS client runtime.
67
- *
68
- * Called once per page load from the inline <script type="module"> injected
69
- * by the SSR renderer:
70
- *
71
- * ```js
72
- * const { initRuntime } = await import('nukejs');
73
- * const data = JSON.parse(document.getElementById('__n_data').textContent);
74
- * await initRuntime(data);
75
- * ```
76
- *
77
- * Order of operations:
78
- * 1. Create the logger at the configured debug level.
79
- * 2. Wire up SPA navigation listener.
80
- * 3. Load all client component bundles in parallel.
81
- * 4. Hydrate every [data-hydrate-id] node.
82
- * 5. Patch history.pushState/replaceState so Link clicks trigger navigation.
83
- */
84
- export declare function initRuntime(data: RuntimeData): Promise<void>;
85
- export {};
package/dist/bundle.js DELETED
@@ -1,322 +0,0 @@
1
- function setupLocationChangeMonitor() {
2
- const originalPushState = window.history.pushState.bind(window.history);
3
- const originalReplaceState = window.history.replaceState.bind(window.history);
4
- const dispatch = (href) => window.dispatchEvent(new CustomEvent("locationchange", { detail: { href } }));
5
- window.history.pushState = function(...args) {
6
- originalPushState(...args);
7
- dispatch(args[2]);
8
- };
9
- window.history.replaceState = function(...args) {
10
- originalReplaceState(...args);
11
- dispatch(args[2]);
12
- };
13
- window.addEventListener("popstate", () => dispatch(window.location.pathname + window.location.search));
14
- }
15
- function makeLogger(level) {
16
- return {
17
- verbose: (...a) => {
18
- if (level === "verbose") console.log(...a);
19
- },
20
- info: (...a) => {
21
- if (level === "verbose" || level === "info") console.log(...a);
22
- },
23
- warn: (...a) => {
24
- if (level === "verbose" || level === "info") console.warn(...a);
25
- },
26
- error: (...a) => {
27
- if (level !== "silent") console.error(...a);
28
- }
29
- };
30
- }
31
- async function reconstructElement(node, mods) {
32
- if (node === null || node === void 0) return node;
33
- if (typeof node !== "object") return node;
34
- if (Array.isArray(node)) {
35
- const items = await Promise.all(node.map((n) => reconstructElement(n, mods)));
36
- const React = await import("react");
37
- return items.map(
38
- (el, i) => el && typeof el === "object" && el.$$typeof ? React.default.cloneElement(el, { key: el.key ?? i }) : el
39
- );
40
- }
41
- if (node.__re === "client") {
42
- const n = node;
43
- const Comp = mods.get(n.componentId);
44
- if (!Comp) return null;
45
- const React = await import("react");
46
- return React.default.createElement(Comp, await reconstructProps(n.props, mods));
47
- }
48
- if (node.__re === "html") {
49
- const n = node;
50
- const React = await import("react");
51
- return React.default.createElement(n.tag, await reconstructProps(n.props, mods));
52
- }
53
- return node;
54
- }
55
- async function reconstructProps(props, mods) {
56
- if (!props || typeof props !== "object" || Array.isArray(props))
57
- return reconstructElement(props, mods);
58
- const out = {};
59
- for (const [k, v] of Object.entries(props))
60
- out[k] = await reconstructElement(v, mods);
61
- return out;
62
- }
63
- async function loadModules(ids, log, bust = "") {
64
- const mods = /* @__PURE__ */ new Map();
65
- await Promise.all(
66
- ids.map(async (id) => {
67
- try {
68
- const url = `/__client-component/${id}.js` + (bust ? `?t=${bust}` : "");
69
- const m = await import(url);
70
- mods.set(id, m.default);
71
- log.verbose("\u2713 Loaded:", id);
72
- } catch (err) {
73
- log.error("\u2717 Load failed:", id, err);
74
- }
75
- })
76
- );
77
- return mods;
78
- }
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
- }
94
- async function mountNodes(mods, log) {
95
- const { hydrateRoot, createRoot } = await import("react-dom/client");
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
- }
112
- const nodes = document.querySelectorAll("[data-hydrate-id]");
113
- log.verbose("Found", nodes.length, "hydration point(s)");
114
- for (const node of nodes) {
115
- if (node.parentElement?.closest("[data-hydrate-id]")) continue;
116
- const id = node.getAttribute("data-hydrate-id");
117
- const Comp = mods.get(id);
118
- if (!Comp) {
119
- log.warn("No module for", id);
120
- continue;
121
- }
122
- let rawProps = {};
123
- try {
124
- rawProps = JSON.parse(node.getAttribute("data-hydrate-props") || "{}");
125
- } catch (e) {
126
- log.error("Props parse error for", id, e);
127
- }
128
- try {
129
- const inner = React.default.createElement(Comp, await reconstructProps(rawProps, mods));
130
- const element = React.default.createElement(NukeErrorBoundary, null, inner);
131
- let root;
132
- if (node.innerHTML.trim()) {
133
- root = hydrateRoot(node, element);
134
- } else {
135
- const r = createRoot(node);
136
- r.render(element);
137
- root = r;
138
- }
139
- activeRoots.push(root);
140
- log.verbose("\u2713 Mounted:", id);
141
- } catch (err) {
142
- log.error("\u2717 Mount failed:", id, err);
143
- }
144
- }
145
- }
146
- function headBlock(head) {
147
- const nodes = [];
148
- let closeComment = null;
149
- let inside = false;
150
- for (const child of Array.from(head.childNodes)) {
151
- if (child.nodeType === Node.COMMENT_NODE) {
152
- const text = child.data.trim();
153
- if (text === "n-head") {
154
- inside = true;
155
- continue;
156
- }
157
- if (text === "/n-head") {
158
- closeComment = child;
159
- inside = false;
160
- continue;
161
- }
162
- }
163
- if (inside && child.nodeType === Node.ELEMENT_NODE)
164
- nodes.push(child);
165
- }
166
- return { nodes, closeComment };
167
- }
168
- function fingerprint(el) {
169
- return el.tagName + "|" + Array.from(el.attributes).sort((a, b) => a.name.localeCompare(b.name)).map((a) => `${a.name}=${a.value}`).join("&");
170
- }
171
- function syncHeadTags(doc) {
172
- const live = headBlock(document.head);
173
- const next = headBlock(doc.head);
174
- let anchor = live.closeComment;
175
- if (!anchor) {
176
- document.head.appendChild(document.createComment("n-head"));
177
- anchor = document.createComment("/n-head");
178
- document.head.appendChild(anchor);
179
- }
180
- for (const el of live.nodes)
181
- if (el.tagName === "SCRIPT") el.remove();
182
- for (const el of next.nodes) {
183
- if (el.tagName === "SCRIPT")
184
- document.head.insertBefore(cloneScriptForExecution(el), anchor);
185
- }
186
- const liveMap = /* @__PURE__ */ new Map();
187
- for (const el of live.nodes) if (el.tagName !== "SCRIPT") liveMap.set(fingerprint(el), el);
188
- const nextMap = /* @__PURE__ */ new Map();
189
- for (const el of next.nodes) if (el.tagName !== "SCRIPT") nextMap.set(fingerprint(el), el);
190
- for (const [fp, el] of nextMap)
191
- if (!liveMap.has(fp)) document.head.insertBefore(el, anchor);
192
- for (const [fp, el] of liveMap)
193
- if (!nextMap.has(fp)) el.remove();
194
- }
195
- function bodyScriptsBlock(body) {
196
- const nodes = [];
197
- let closeComment = null;
198
- let inside = false;
199
- for (const child of Array.from(body.childNodes)) {
200
- if (child.nodeType === Node.COMMENT_NODE) {
201
- const text = child.data.trim();
202
- if (text === "n-body-scripts") {
203
- inside = true;
204
- continue;
205
- }
206
- if (text === "/n-body-scripts") {
207
- closeComment = child;
208
- inside = false;
209
- continue;
210
- }
211
- }
212
- if (inside && child.nodeType === Node.ELEMENT_NODE)
213
- nodes.push(child);
214
- }
215
- return { nodes, closeComment };
216
- }
217
- function cloneScriptForExecution(src) {
218
- const el = document.createElement("script");
219
- for (const { name, value } of Array.from(src.attributes)) {
220
- if (name === "src") {
221
- const url = new URL(value, location.href);
222
- url.searchParams.set("t", String(Date.now()));
223
- el.setAttribute("src", url.toString());
224
- } else {
225
- el.setAttribute(name, value);
226
- }
227
- }
228
- if (src.textContent) el.textContent = src.textContent;
229
- return el;
230
- }
231
- function syncBodyScripts(doc) {
232
- const live = bodyScriptsBlock(document.body);
233
- const next = bodyScriptsBlock(doc.body);
234
- for (const el of live.nodes) el.remove();
235
- let anchor = live.closeComment;
236
- if (!anchor) {
237
- document.body.appendChild(document.createComment("n-body-scripts"));
238
- anchor = document.createComment("/n-body-scripts");
239
- document.body.appendChild(anchor);
240
- }
241
- for (const el of next.nodes)
242
- document.body.insertBefore(cloneScriptForExecution(el), anchor);
243
- }
244
- function syncAttrs(live, next) {
245
- for (const { name, value } of Array.from(next.attributes))
246
- live.setAttribute(name, value);
247
- for (const { name } of Array.from(live.attributes))
248
- if (!next.hasAttribute(name)) live.removeAttribute(name);
249
- }
250
- function setupNavigation(log) {
251
- let hmrNavPending = false;
252
- window.addEventListener("locationchange", async ({ detail: { href, hmr } }) => {
253
- if (hmr) {
254
- if (hmrNavPending) {
255
- log.info("[HMR] Navigation already in flight \u2014 skipping duplicate for", href);
256
- return;
257
- }
258
- hmrNavPending = true;
259
- }
260
- try {
261
- const fetchUrl = hmr ? href + (href.includes("?") ? "&" : "?") + "__hmr=1" : href;
262
- const response = await fetch(fetchUrl, { headers: { Accept: "text/html" } });
263
- if (!response.ok) {
264
- const ct = response.headers.get("content-type") ?? "";
265
- if (!ct.includes("text/html")) {
266
- log.error("Navigation fetch failed:", response.status, "\u2014 falling back to full reload");
267
- window.location.href = href;
268
- return;
269
- }
270
- log.info("Navigation returned", response.status, "\u2014 rendering error page in-place");
271
- }
272
- const parser = new DOMParser();
273
- const doc = parser.parseFromString(await response.text(), "text/html");
274
- const newApp = doc.getElementById("app");
275
- const currApp = document.getElementById("app");
276
- if (!newApp || !currApp) return;
277
- syncHeadTags(doc);
278
- syncBodyScripts(doc);
279
- syncAttrs(document.documentElement, doc.documentElement);
280
- syncAttrs(document.body, doc.body);
281
- currApp.innerHTML = newApp.innerHTML;
282
- const newTitle = doc.querySelector("title");
283
- if (newTitle) document.title = newTitle.textContent ?? "";
284
- const newDataEl = doc.getElementById("__n_data");
285
- const currDataEl = document.getElementById("__n_data");
286
- if (newDataEl && currDataEl) currDataEl.textContent = newDataEl.textContent;
287
- activeRoots.splice(0).forEach((r) => r.unmount());
288
- const navData = JSON.parse(currDataEl?.textContent ?? "{}");
289
- log.info("\u{1F504} Route \u2192", href, "\u2014 mounting", navData.hydrateIds?.length ?? 0, "component(s)");
290
- const mods = await loadModules(navData.allIds ?? [], log, hmr ? String(Date.now()) : "");
291
- await mountNodes(mods, log);
292
- window.scrollTo(0, 0);
293
- log.info("\u{1F389} Navigation complete:", href);
294
- } catch (err) {
295
- log.error("Navigation error, falling back to full reload:", err);
296
- window.location.href = href;
297
- } finally {
298
- if (hmr) hmrNavPending = false;
299
- clientErrorPending = false;
300
- }
301
- });
302
- }
303
- async function initRuntime(data) {
304
- const log = makeLogger(data.debug ?? "silent");
305
- log.info("\u{1F680} Partial hydration:", data.hydrateIds.length, "root component(s)");
306
- setupNavigation(log);
307
- window.onerror = (_msg, _src, _line, _col, err) => {
308
- navigateToClientError(err ?? _msg);
309
- return true;
310
- };
311
- window.onunhandledrejection = (e) => {
312
- navigateToClientError(e.reason);
313
- };
314
- const mods = await loadModules(data.allIds, log);
315
- await mountNodes(mods, log);
316
- log.info("\u{1F389} Done!");
317
- setupLocationChangeMonitor();
318
- }
319
- export {
320
- initRuntime,
321
- setupLocationChangeMonitor
322
- };
package/dist/bundler.js DELETED
@@ -1,102 +0,0 @@
1
- import path from "path";
2
- import { fileURLToPath } from "url";
3
- import { build } from "esbuild";
4
- import { log } from "./logger.js";
5
- import { getComponentById } from "./component-analyzer.js";
6
- let reactBundlePromise = null;
7
- let nukeBundlePromise = null;
8
- async function bundleClientComponent(filePath) {
9
- const result = await build({
10
- entryPoints: [filePath],
11
- bundle: true,
12
- format: "esm",
13
- platform: "browser",
14
- write: false,
15
- jsx: "automatic",
16
- // Keep React external — resolved by the importmap to /__react.js
17
- external: ["react", "react-dom/client", "react/jsx-runtime"]
18
- });
19
- return result.outputFiles[0].text;
20
- }
21
- async function serveClientComponentBundle(componentId, res) {
22
- const filePath = getComponentById(componentId);
23
- if (filePath) {
24
- log.verbose(`Bundling client component: ${componentId} (${path.basename(filePath)})`);
25
- res.setHeader("Content-Type", "application/javascript");
26
- res.end(await bundleClientComponent(filePath));
27
- return;
28
- }
29
- log.error(`Client component not found: ${componentId}`);
30
- res.statusCode = 404;
31
- res.end("Client component not found");
32
- }
33
- async function serveReactBundle(res) {
34
- log.verbose("Bundling React runtime");
35
- if (!reactBundlePromise) {
36
- reactBundlePromise = build({
37
- stdin: {
38
- contents: `
39
- import React, {
40
- createElement, cloneElement, createRef, isValidElement, Children,
41
- useState, useEffect, useContext, useReducer, useCallback, useMemo,
42
- useRef, useImperativeHandle, useLayoutEffect, useDebugValue,
43
- useDeferredValue, useTransition, useId, useSyncExternalStore,
44
- useInsertionEffect, createContext, forwardRef, memo, lazy,
45
- Suspense, Fragment, StrictMode, Component, PureComponent,
46
- createPortal
47
- } from 'react';
48
- import { jsx, jsxs } from 'react/jsx-runtime';
49
- import { hydrateRoot, createRoot } from 'react-dom/client';
50
-
51
- export {
52
- createElement, cloneElement, createRef, isValidElement, Children,
53
- useState, useEffect, useContext, useReducer, useCallback, useMemo,
54
- useRef, useImperativeHandle, useLayoutEffect, useDebugValue,
55
- useDeferredValue, useTransition, useId, useSyncExternalStore,
56
- useInsertionEffect, createContext, forwardRef, memo, lazy,
57
- Suspense, Fragment, StrictMode, Component, PureComponent,
58
- hydrateRoot, createRoot, jsx, jsxs
59
- };
60
- export default React;
61
- `,
62
- loader: "ts"
63
- },
64
- bundle: true,
65
- write: false,
66
- treeShaking: true,
67
- minify: false,
68
- format: "esm",
69
- jsx: "automatic",
70
- alias: {
71
- react: path.dirname(fileURLToPath(import.meta.resolve("react/package.json"))),
72
- "react-dom": path.dirname(fileURLToPath(import.meta.resolve("react-dom/package.json")))
73
- },
74
- define: { "process.env.NODE_ENV": '"development"' }
75
- }).then((r) => r.outputFiles[0].text);
76
- }
77
- res.setHeader("Content-Type", "application/javascript");
78
- res.end(await reactBundlePromise);
79
- }
80
- async function serveNukeBundle(res) {
81
- log.verbose("Bundling NukeJS runtime");
82
- if (!nukeBundlePromise) {
83
- const dir = path.dirname(fileURLToPath(import.meta.url));
84
- const entry = path.join(dir, `bundle.${dir.endsWith("dist") ? "js" : "ts"}`);
85
- nukeBundlePromise = build({
86
- entryPoints: [entry],
87
- write: false,
88
- format: "esm",
89
- minify: true,
90
- bundle: true,
91
- external: ["react", "react-dom/client"]
92
- }).then((r) => r.outputFiles[0].text);
93
- }
94
- res.setHeader("Content-Type", "application/javascript");
95
- res.end(await nukeBundlePromise);
96
- }
97
- export {
98
- bundleClientComponent,
99
- serveClientComponentBundle,
100
- serveNukeBundle,
101
- serveReactBundle
102
- };
@@ -1,125 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import { createHash } from "node:crypto";
4
- import { fileURLToPath } from "url";
5
- const componentCache = /* @__PURE__ */ new Map();
6
- function isClientComponent(filePath) {
7
- const content = fs.readFileSync(filePath, "utf-8");
8
- for (const line of content.split("\n").slice(0, 5)) {
9
- const trimmed = line.trim();
10
- if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*")) continue;
11
- if (/^["']use client["'];?$/.test(trimmed)) return true;
12
- break;
13
- }
14
- return false;
15
- }
16
- function getClientComponentId(filePath, pagesDir) {
17
- return "cc_" + createHash("md5").update(path.relative(pagesDir, filePath)).digest("hex").substring(0, 8);
18
- }
19
- function getExportedDefaultName(filePath) {
20
- const content = fs.readFileSync(filePath, "utf-8");
21
- let m = content.match(/export\s+default\s+(?:function\s+)?(\w+)/);
22
- if (m?.[1]) return m[1];
23
- m = content.match(/var\s+\w+_default\s*=\s*(\w+)/);
24
- if (m?.[1]) return m[1];
25
- m = content.match(/export\s*\{[^}]*\b(\w+)\s+as\s+default\b[^}]*\}/);
26
- if (m?.[1] && !m[1].endsWith("_default")) return m[1];
27
- return void 0;
28
- }
29
- function analyzeComponent(filePath, pagesDir) {
30
- if (componentCache.has(filePath)) return componentCache.get(filePath);
31
- const isClient = isClientComponent(filePath);
32
- const info = {
33
- filePath,
34
- isClientComponent: isClient,
35
- clientComponentId: isClient ? getClientComponentId(filePath, pagesDir) : void 0,
36
- exportedName: isClient ? getExportedDefaultName(filePath) : void 0
37
- };
38
- componentCache.set(filePath, info);
39
- return info;
40
- }
41
- function extractImports(filePath) {
42
- const content = fs.readFileSync(filePath, "utf-8");
43
- const dir = path.dirname(filePath);
44
- const imports = [];
45
- const importRegex = /(?:import|export)\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
46
- let match;
47
- while ((match = importRegex.exec(content)) !== null) {
48
- const spec = match[1];
49
- if (spec === "nukejs") {
50
- const selfDir = path.dirname(fileURLToPath(import.meta.url));
51
- for (const candidate of [
52
- path.join(selfDir, "index.ts"),
53
- path.join(selfDir, "index.js")
54
- ]) {
55
- if (fs.existsSync(candidate)) {
56
- imports.push(candidate);
57
- break;
58
- }
59
- }
60
- continue;
61
- }
62
- if (!spec.startsWith(".") && !spec.startsWith("/")) continue;
63
- let resolved = path.resolve(dir, spec);
64
- const EXTS = [".tsx", ".ts", ".jsx", ".js"];
65
- const isFile = (p) => fs.existsSync(p) && fs.statSync(p).isFile();
66
- if (!isFile(resolved)) {
67
- let found = false;
68
- for (const ext of EXTS) {
69
- if (isFile(resolved + ext)) {
70
- resolved += ext;
71
- found = true;
72
- break;
73
- }
74
- }
75
- if (!found && fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
76
- for (const ext of EXTS) {
77
- const candidate = path.join(resolved, `index${ext}`);
78
- if (isFile(candidate)) {
79
- resolved = candidate;
80
- found = true;
81
- break;
82
- }
83
- }
84
- }
85
- if (!found) continue;
86
- }
87
- imports.push(resolved);
88
- }
89
- return imports;
90
- }
91
- function findClientComponentsInTree(filePath, pagesDir, visited = /* @__PURE__ */ new Set()) {
92
- const found = /* @__PURE__ */ new Map();
93
- if (visited.has(filePath)) return found;
94
- visited.add(filePath);
95
- const info = analyzeComponent(filePath, pagesDir);
96
- if (info.isClientComponent && info.clientComponentId) {
97
- found.set(info.clientComponentId, filePath);
98
- return found;
99
- }
100
- for (const importPath of extractImports(filePath)) {
101
- for (const [id, p] of findClientComponentsInTree(importPath, pagesDir, visited)) {
102
- found.set(id, p);
103
- }
104
- }
105
- return found;
106
- }
107
- function getComponentById(id) {
108
- for (const [filePath, info] of componentCache) {
109
- if (info.clientComponentId === id) return filePath;
110
- }
111
- return void 0;
112
- }
113
- function getComponentCache() {
114
- return componentCache;
115
- }
116
- function invalidateComponentCache(filePath) {
117
- componentCache.delete(filePath);
118
- }
119
- export {
120
- analyzeComponent,
121
- findClientComponentsInTree,
122
- getComponentById,
123
- getComponentCache,
124
- invalidateComponentCache
125
- };
package/dist/config.js DELETED
@@ -1,29 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import { pathToFileURL } from "url";
4
- import { log } from "./logger.js";
5
- async function loadConfig() {
6
- const configPath = path.join(process.cwd(), "nuke.config.ts");
7
- if (!fs.existsSync(configPath)) {
8
- return {
9
- serverDir: "./server",
10
- port: 3e3,
11
- debug: false
12
- };
13
- }
14
- try {
15
- const mod = await import(pathToFileURL(configPath).href);
16
- const config = mod.default;
17
- return {
18
- serverDir: config.serverDir || "./server",
19
- port: config.port || 3e3,
20
- debug: config.debug ?? false
21
- };
22
- } catch (error) {
23
- log.error("Error loading config:", error);
24
- throw error;
25
- }
26
- }
27
- export {
28
- loadConfig
29
- };