dinou 6.1.1 → 7.1.0

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +9352 -1081
  3. package/cli.js +314 -91
  4. package/dinou/adapters/bun.js +62 -0
  5. package/dinou/adapters/cloudflare.js +65 -0
  6. package/dinou/adapters/deno.js +48 -0
  7. package/dinou/adapters/netlify.js +85 -0
  8. package/dinou/adapters/node.mjs +31 -0
  9. package/dinou/bun/build.mjs +1077 -0
  10. package/dinou/cloudflare/build.mjs +1121 -0
  11. package/dinou/cloudflare/shims/child_process.js +14 -0
  12. package/dinou/cloudflare/shims/empty.js +2 -0
  13. package/dinou/cloudflare/shims/fs.js +99 -0
  14. package/dinou/cloudflare/shims/os.js +29 -0
  15. package/dinou/config.js +1 -0
  16. package/dinou/config.mjs +2 -0
  17. package/dinou/constants.js +29 -4
  18. package/dinou/core/babel-esm-loader.js +66 -37
  19. package/dinou/core/bailout-proxy.js +61 -0
  20. package/dinou/core/build-static-pages.js +322 -165
  21. package/dinou/core/cache-revalidate.js +471 -144
  22. package/dinou/core/cache-slot.js +387 -0
  23. package/dinou/core/client-redirect.jsx +12 -7
  24. package/dinou/core/client-webpack.jsx +457 -82
  25. package/dinou/core/client.jsx +452 -82
  26. package/dinou/core/config.js +11 -0
  27. package/dinou/core/dinou-paths.js +52 -0
  28. package/dinou/core/edge-ssr.js +122 -0
  29. package/dinou/core/export-static.mjs +64 -0
  30. package/dinou/core/generate-route-types.js +252 -0
  31. package/dinou/core/generate-static-page.js +55 -94
  32. package/dinou/core/generate-static-rsc.js +47 -15
  33. package/dinou/core/generate-static.js +212 -10
  34. package/dinou/core/generating-isg.js +5 -0
  35. package/dinou/core/get-asset-from-manifest.js +9 -3
  36. package/dinou/core/get-error-jsx.js +221 -41
  37. package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
  38. package/dinou/core/get-jsx.js +175 -96
  39. package/dinou/core/handler.js +2303 -0
  40. package/dinou/core/http-adapter.js +115 -0
  41. package/dinou/core/import-module.js +83 -53
  42. package/dinou/core/layout-functions.js +183 -0
  43. package/dinou/core/link.jsx +18 -6
  44. package/dinou/core/load-dinou-config.js +60 -0
  45. package/dinou/core/manifest-provider.js +261 -0
  46. package/dinou/core/navigation-utils.js +4 -0
  47. package/dinou/core/navigation.js +50 -39
  48. package/dinou/core/parse-exports.js +252 -2
  49. package/dinou/core/ppr-context.js +105 -0
  50. package/dinou/core/ppr-runtime.js +263 -0
  51. package/dinou/core/redirect.jsx +20 -19
  52. package/dinou/core/render-jsx-to-client-jsx.js +91 -87
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +34 -0
  55. package/dinou/core/revalidating.js +28 -9
  56. package/dinou/core/route-generator.js +107 -0
  57. package/dinou/core/rsc-renderer.js +90 -0
  58. package/dinou/core/run-ssg.js +19 -0
  59. package/dinou/core/scan-dependency-components.js +218 -0
  60. package/dinou/core/server-function-proxy-webpack.js +3 -0
  61. package/dinou/core/server-function-proxy.js +3 -0
  62. package/dinou/core/slot.js +302 -0
  63. package/dinou/core/status-manifest.js +41 -1
  64. package/dinou/core/storage-adapter.js +522 -0
  65. package/dinou/core/url-resolver.js +16 -16
  66. package/dinou/core/vfs.js +139 -68
  67. package/dinou/deno/build.mjs +1112 -0
  68. package/dinou/deno/seed-kv.js +115 -0
  69. package/dinou/esbuild/build.mjs +51 -1
  70. package/dinou/esbuild/dev.mjs +290 -224
  71. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
  72. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
  73. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
  74. package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
  75. package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
  76. package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
  77. package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
  78. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
  79. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
  80. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
  81. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
  82. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
  83. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
  84. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
  85. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
  86. package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
  87. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
  88. package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
  89. package/dinou/index.d.ts +315 -11
  90. package/dinou/index.js +12 -6
  91. package/dinou/index.mjs +9 -0
  92. package/dinou/node/build.mjs +195 -0
  93. package/dinou/node/bundle-dual-engine.mjs +1012 -0
  94. package/dinou/node/dev.mjs +2589 -0
  95. package/dinou/node/port-selector.mjs +164 -0
  96. package/dinou/node/terminal-status.mjs +345 -0
  97. package/dinou/package.json +16 -14
  98. package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
  99. package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
  100. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
  101. package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
  102. package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
  103. package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
  104. package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
  105. package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
  106. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
  107. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
  108. package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
  109. package/dinou/rollup/rollup.config.js +155 -43
  110. package/dinou/server.d.ts +114 -12
  111. package/dinou/server.js +35 -10
  112. package/dinou/server.mjs +30 -9
  113. package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
  114. package/dinou/webpack/loaders/server-functions-loader.js +22 -1
  115. package/dinou/webpack/loaders/swc-loader.js +76 -0
  116. package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
  117. package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
  118. package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
  119. package/dinou/webpack/webpack.config.js +227 -29
  120. package/eject.js +113 -34
  121. package/package.json +18 -16
  122. package/dinou/core/asset-require-hook.js +0 -58
  123. package/dinou/core/client-error-webpack.jsx +0 -432
  124. package/dinou/core/client-error.jsx +0 -432
  125. package/dinou/core/concurrency-manager.js +0 -48
  126. package/dinou/core/context-proxy.js +0 -68
  127. package/dinou/core/generate-static-pages.js +0 -130
  128. package/dinou/core/generate-static-rscs.js +0 -151
  129. package/dinou/core/get-ssg-metadata.js +0 -31
  130. package/dinou/core/register-loader.mjs +0 -11
  131. package/dinou/core/register-paths.js +0 -27
  132. package/dinou/core/render-app-to-html.js +0 -430
  133. package/dinou/core/render-html.js +0 -407
  134. package/dinou/core/server.js +0 -1542
  135. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
@@ -1,432 +0,0 @@
1
- import {
2
- use,
3
- useState,
4
- useEffect,
5
- useTransition,
6
- useLayoutEffect,
7
- useMemo,
8
- Component,
9
- } from "react";
10
- import { createFromFetch } from "@roggc/react-server-dom-esm/client";
11
- import { hydrateRoot } from "react-dom/client";
12
- import { RouterContext } from "./navigation.js";
13
- import { resolveUrl, isExternalUrl } from "./navigation-utils.js";
14
- import { createServerFunctionProxy } from "./server-function-proxy.js";
15
-
16
- // ====================================================================
17
- // 1. GLOBAL STATE (Outside the component)
18
- // ====================================================================
19
- const cache = new Map();
20
- const scrollCache = new Map();
21
-
22
- const getCurrentRoute = () => window.location.pathname + window.location.search;
23
-
24
- // ====================================================================
25
- // 2. PURE HELPERS
26
- // ====================================================================
27
-
28
- // Helper to detect if we only change the hash on the same page
29
- const isHashChangeOnly = (finalPath) => {
30
- const targetUrl = new URL(finalPath, window.location.origin);
31
- const normalize = (p) =>
32
- p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
33
-
34
- const targetPath = normalize(targetUrl.pathname);
35
- const currentPath = normalize(window.location.pathname);
36
-
37
- return (
38
- targetPath + targetUrl.search === currentPath + window.location.search &&
39
- targetUrl.hash !== ""
40
- );
41
- };
42
-
43
- let isInitialErrorLoad = true;
44
-
45
- const getRSCPayload = (rscKey, isPrefetch = false) => {
46
- const url = rscKey.split("::")[0];
47
- // 1. Check Idempotence (Avoids the infinite loop of React)
48
- if (cache.has(url)) return cache.get(url);
49
-
50
- let promise;
51
- if (isInitialErrorLoad && url === getCurrentRoute()) {
52
- isInitialErrorLoad = false;
53
- const payloadUrl = "/____rsc_payload_error____" + url;
54
- promise = createFromFetch(
55
- fetch(payloadUrl, {
56
- method: "POST",
57
- headers: {
58
- "Content-Type": "application/json",
59
- },
60
- body: JSON.stringify({
61
- error: {
62
- message: window.__DINOU_ERROR_MESSAGE__ || "Unknown Error",
63
- stack: window.__DINOU_ERROR_STACK__,
64
- name: window.__DINOU_ERROR_NAME__,
65
- },
66
- }),
67
- }).then((res) => {
68
- if (res.headers.has("x-rsc-redirect")) {
69
- const redirectUrl = res.headers.get("x-rsc-redirect");
70
- cache.delete(url);
71
- if (!isPrefetch) {
72
- if (window.__DINOU_ROUTER_NAVIGATE__) {
73
- window.__DINOU_ROUTER_NAVIGATE__(redirectUrl, { replace: true });
74
- } else {
75
- window.location.href = redirectUrl;
76
- }
77
- }
78
- return new Promise(() => {});
79
- }
80
- return res;
81
- }),
82
- {
83
- callServer: async (id, args) => {
84
- const proxy = createServerFunctionProxy(id);
85
- return proxy(...args);
86
- }
87
- }
88
- );
89
- } else {
90
- // Normal client.jsx load logic!
91
- let payloadUrl = "/____rsc_payload____" + url;
92
- const buildId = window.__DINOU_BUILD_ID__;
93
- if (buildId) {
94
- payloadUrl += (payloadUrl.includes("?") ? "&" : "?") + "buildId=" + buildId;
95
- window.__DINOU_BUILD_ID__ = undefined;
96
- }
97
-
98
- promise = createFromFetch(
99
- fetch(payloadUrl).then((res) => {
100
- if (res.headers.has("x-rsc-redirect")) {
101
- const redirectUrl = res.headers.get("x-rsc-redirect");
102
- cache.delete(url);
103
- if (!isPrefetch) {
104
- if (window.__DINOU_ROUTER_NAVIGATE__) {
105
- window.__DINOU_ROUTER_NAVIGATE__(redirectUrl, { replace: true });
106
- } else {
107
- window.location.href = redirectUrl;
108
- }
109
- }
110
- return new Promise(() => {});
111
- }
112
- return res;
113
- }),
114
- {
115
- callServer: async (id, args) => {
116
- const proxy = createServerFunctionProxy(id);
117
- return proxy(...args);
118
- }
119
- }
120
- );
121
- }
122
-
123
- cache.set(url, promise);
124
- return promise;
125
- };
126
-
127
- const getErrorRSCPayload = (route, error) => {
128
- const url = route.split("::")[0];
129
- const cacheKey = `error::${url}::${error.message || String(error)}`;
130
- if (cache.has(cacheKey)) {
131
- return cache.get(cacheKey);
132
- }
133
-
134
- const payloadUrl = "/____rsc_payload_error____" + url;
135
- const promise = createFromFetch(
136
- fetch(payloadUrl, {
137
- method: "POST",
138
- headers: {
139
- "Content-Type": "application/json",
140
- },
141
- body: JSON.stringify({
142
- error: {
143
- message: error.message || "Unknown Error",
144
- name: error.name,
145
- stack: error.stack,
146
- },
147
- }),
148
- }).then((res) => {
149
- if (res.headers.has("x-rsc-redirect")) {
150
- const redirectUrl = res.headers.get("x-rsc-redirect");
151
- cache.delete(cacheKey);
152
- if (window.__DINOU_ROUTER_NAVIGATE__) {
153
- window.__DINOU_ROUTER_NAVIGATE__(redirectUrl, { replace: true });
154
- } else {
155
- window.location.href = redirectUrl;
156
- }
157
- return new Promise(() => {});
158
- }
159
- return res;
160
- }),
161
- {
162
- callServer: async (id, args) => {
163
- const proxy = createServerFunctionProxy(id);
164
- return proxy(...args);
165
- }
166
- }
167
- );
168
- cache.set(cacheKey, promise);
169
- return promise;
170
- };
171
-
172
- class ErrorBoundary extends Component {
173
- constructor(props) {
174
- super(props);
175
- this.state = { hasError: false, error: null };
176
- }
177
-
178
- static getDerivedStateFromError(error) {
179
- return { hasError: true, error };
180
- }
181
-
182
- componentDidCatch(error, errorInfo) {
183
- console.error("[Dinou] ErrorBoundary caught error:", error, errorInfo);
184
- if (this.props.onError) {
185
- this.props.onError(error);
186
- }
187
- }
188
-
189
- render() {
190
- if (this.state.hasError) {
191
- if (this.props.fallback) {
192
- return this.props.fallback(this.state.error);
193
- }
194
- const isDev = process.env.NODE_ENV !== "production";
195
- return (
196
- <div style={{ padding: "20px", fontFamily: "sans-serif" }}>
197
- <h2>Application Error</h2>
198
- <p>An unexpected error occurred on the client.</p>
199
- <pre style={{ backgroundColor: "#f5f5f5", padding: "15px", borderRadius: "5px", overflowX: "auto" }}>
200
- {isDev ? (
201
- <>
202
- <div style={{ fontWeight: "bold", marginBottom: "10px" }}>
203
- {this.state.error?.name || "Error"}: {this.state.error?.message || String(this.state.error)}
204
- </div>
205
- <div>{this.state.error?.stack}</div>
206
- </>
207
- ) : (
208
- this.state.error?.message || String(this.state.error)
209
- )}
210
- </pre>
211
- </div>
212
- );
213
- }
214
- return this.props.children;
215
- }
216
- }
217
-
218
-
219
- // ====================================================================
220
- // 3. ROUTER COMPONENT
221
- // ====================================================================
222
-
223
- function Router() {
224
- const [route, setRoute] = useState(getCurrentRoute());
225
- const [isPopState, setIsPopState] = useState(false);
226
- const [isPending, startTransition] = useTransition();
227
- const [version, setVersion] = useState(0);
228
- const [navError, setNavError] = useState(null);
229
-
230
- // 🧭 NAVIGATE FUNCTION (Core Logic)
231
- const navigate = (href, options = {}) => {
232
- const finalPath = resolveUrl(href, window.location.pathname);
233
-
234
- // 🛡️ NAVIGATE PROTECTION: Hash Detection
235
- if (isHashChangeOnly(finalPath)) {
236
- if (options.replace) {
237
- window.history.replaceState(null, "", finalPath);
238
- } else {
239
- window.history.pushState(null, "", finalPath);
240
- }
241
-
242
- // Manual scroll
243
- const hash = new URL(finalPath, window.location.origin).hash;
244
- const id = hash.replace("#", "");
245
- const element = document.getElementById(id);
246
- if (element) {
247
- element.scrollIntoView({ behavior: "auto" });
248
- }
249
- return; // CRITICAL STOP
250
- }
251
-
252
- if (options.fresh) {
253
- // console.log(`[Router] Force refreshing: ${finalPath}`);
254
- cache.delete(finalPath);
255
- }
256
-
257
- // Normal RSC Navigation
258
- scrollCache.set(
259
- window.location.pathname + window.location.search,
260
- window.scrollY,
261
- );
262
- // cache.delete(finalPath);
263
- if (options.replace) {
264
- window.history.replaceState(null, "", finalPath);
265
- } else {
266
- window.history.pushState(null, "", finalPath);
267
- }
268
-
269
- startTransition(() => {
270
- setIsPopState(false);
271
- setRoute(finalPath);
272
- setNavError(null);
273
- });
274
- };
275
-
276
- // 🔌 EFFECT 1: Expose Global Prefetch & Navigation
277
- useEffect(() => {
278
- window.__DINOU_PREFETCH__ = (url) => {
279
- // 🛡️ PREFETCH PROTECTION: If it's a local hash, do nothing
280
- if (isHashChangeOnly(url)) return;
281
- getRSCPayload(url, true);
282
- };
283
-
284
- window.__DINOU_ROUTER_NAVIGATE__ = navigate;
285
-
286
- // Hydration
287
- document.body.setAttribute("data-hydrated", "true");
288
-
289
- return () => {
290
- if (window.__DINOU_ROUTER_NAVIGATE__ === navigate) {
291
- window.__DINOU_ROUTER_NAVIGATE__ = undefined;
292
- }
293
- };
294
- }, [navigate]);
295
-
296
- const back = () => window.history.back();
297
- const forward = () => window.history.forward();
298
- const refresh = () => {
299
- const currentPath = window.location.pathname + window.location.search;
300
- // console.log(`[Router] Soft Refreshing: ${currentPath}`);
301
-
302
- // 1. Delete cache to ensure fresh data
303
- cache.delete(currentPath);
304
-
305
- // 2. Start transition (to show isPending if you want)
306
- startTransition(() => {
307
- // 3. Increment version to force re-execution of useMemo
308
- setVersion((v) => v + 1);
309
- setNavError(null);
310
- });
311
- };
312
-
313
- // 🔌 EFFECT 2: Global Listeners (Click and PopState)
314
- useEffect(() => {
315
- if ("scrollRestoration" in window.history) {
316
- window.history.scrollRestoration = "manual";
317
- }
318
-
319
- const onNavigate = (e) => {
320
- // 🛡️ FIX: If the event was already processed (preventDefault called by Link), we ignore it.
321
- if (e.defaultPrevented) return;
322
- const anchor = e.target.closest("a");
323
- if (
324
- !anchor ||
325
- anchor.target ||
326
- e.metaKey ||
327
- e.ctrlKey ||
328
- e.shiftKey ||
329
- e.altKey
330
- ) {
331
- return;
332
- }
333
-
334
- const href = anchor.getAttribute("href");
335
- if (!href || href.startsWith("mailto:") || href.startsWith("tel:") || isExternalUrl(href))
336
- return;
337
-
338
- // We use the unified helper
339
- const finalPath = resolveUrl(href, window.location.pathname);
340
-
341
- // We use the same hash detection helper for consistency
342
- if (isHashChangeOnly(finalPath)) {
343
- return; // The browser handles it natively or navigate would handle it
344
- }
345
-
346
- e.preventDefault();
347
- navigate(href);
348
- };
349
-
350
- const onPopState = () => {
351
- const target = getCurrentRoute();
352
- // Optional: cache.delete(target); // Uncomment if you want refresh on going back
353
- startTransition(() => {
354
- setIsPopState(true);
355
- setRoute(target);
356
- setNavError(null);
357
- });
358
- };
359
-
360
- window.addEventListener("click", onNavigate);
361
- window.addEventListener("popstate", onPopState);
362
-
363
- return () => {
364
- window.removeEventListener("click", onNavigate);
365
- window.removeEventListener("popstate", onPopState);
366
- };
367
- }, []);
368
-
369
- // 🔌 EFFECT 3: Scroll Management (Restoration)
370
- useLayoutEffect(() => {
371
- requestAnimationFrame(() => {
372
- if (window.location.hash) return;
373
-
374
- if (isPopState) {
375
- const key = route;
376
- const savedY = scrollCache.get(key);
377
- if (savedY !== undefined) {
378
- window.scrollTo(0, savedY);
379
- }
380
- } else {
381
- window.scrollTo(0, 0);
382
- }
383
- });
384
- }, [route, isPopState]);
385
-
386
- // 🔌 EFFECT 4: Scroll Management (Hash on new page)
387
- useEffect(() => {
388
- const hash = window.location.hash;
389
- if (!hash) return;
390
-
391
- requestAnimationFrame(() => {
392
- const id = hash.replace("#", "");
393
- const element = document.getElementById(id);
394
- if (element) {
395
- element.scrollIntoView({ behavior: "auto" });
396
- }
397
- });
398
- }, [route]);
399
-
400
- // RSC Logic
401
- const rscKey = route + "::" + version;
402
- const content = navError ? getErrorRSCPayload(route, navError) : getRSCPayload(rscKey);
403
-
404
- const contextValue = useMemo(
405
- () => ({
406
- url: route,
407
- navigate,
408
- back,
409
- forward,
410
- refresh,
411
- isPending,
412
- }),
413
- [route, isPending],
414
- );
415
-
416
- return (
417
- <RouterContext.Provider value={contextValue}>
418
- <ErrorBoundary
419
- key={navError ? "error" : "normal"}
420
- onError={setNavError}
421
- >
422
- {use(content)}
423
- </ErrorBoundary>
424
- </RouterContext.Provider>
425
- );
426
- }
427
-
428
- hydrateRoot(document, <Router />);
429
-
430
- if (import.meta.hot) {
431
- import.meta.hot.accept();
432
- }
@@ -1,48 +0,0 @@
1
- class ConcurrencyManager {
2
- constructor(maxConcurrent) {
3
- this.maxConcurrent = maxConcurrent;
4
- this.activeCount = 0;
5
- this.queue = [];
6
- }
7
-
8
- /**
9
- * Executes an asynchronous task respecting the concurrency limit.
10
- * @param {Function} task - Function that returns a promise (e.g., your render/fork logic)
11
- */
12
- async run(task) {
13
- // If we are already at maximum, wait in the queue
14
- if (this.activeCount >= this.maxConcurrent) {
15
- await new Promise((resolve) => this.queue.push(resolve));
16
- }
17
-
18
- this.activeCount++;
19
-
20
- try {
21
- return await task();
22
- } finally {
23
- this.activeCount--;
24
-
25
- // If there is someone waiting, let them proceed
26
- if (this.queue.length > 0) {
27
- const nextResolve = this.queue.shift();
28
- nextResolve();
29
- }
30
- }
31
- }
32
-
33
- // Optional: For metrics
34
- getStatus() {
35
- return { active: this.activeCount, queued: this.queue.length };
36
- }
37
- }
38
-
39
- // Global instance (adjust according to your server's RAM)
40
- // A conservative general rule: CPUs * 2 or a fixed number like 10-20.
41
- const os = require("os");
42
- const MAX_PROCESSES =
43
- process.env.MAX_CONCURRENT_RENDERS || Math.max(1, os.cpus().length * 2);
44
- // const MAX_PROCESSES = process.env.MAX_CONCURRENT_RENDERS || 10;
45
-
46
- const processLimiter = new ConcurrencyManager(MAX_PROCESSES);
47
-
48
- module.exports = processLimiter;
@@ -1,68 +0,0 @@
1
- // core/context-proxy.js
2
-
3
- /**
4
- * Creates a proxy object that intercepts response method calls
5
- * and sends them to the parent process (Express Handler) through IPC.
6
- * @returns {object} The proxy object that simulates the Express response.
7
- */
8
- function createResponseProxy() {
9
- // const proxyId = Math.random().toString(36).substring(7);
10
- // console.log(`[Proxy] Created new proxy with ID: ${proxyId}`);
11
- // Central function to send commands to the parent process
12
- function sendCommand(command, args) {
13
- // console.log(`[Dinou] Sending context command to parent: ${command}`, args);
14
- if (typeof process.send === "function") {
15
- process.send({
16
- type: "DINOU_CONTEXT_COMMAND",
17
- command,
18
- args,
19
- });
20
- } else {
21
- console.warn(
22
- `[Dinou] Attempted to run context command "${command}" outside of a child process.`
23
- );
24
- }
25
- }
26
-
27
- return {
28
- // _proxyId: proxyId,
29
- // 1. Proxy to delete cookies
30
- clearCookie: (name, options) => {
31
- sendCommand("clearCookie", [name, options]);
32
- },
33
-
34
- // 2. 👇 ADDED: Proxy to set cookies
35
- // This will send [name, value, options] to 'render-app-to-html.js'
36
- cookie: (name, value, options) => {
37
- // console.warn(`[Dinou] Proxying cookie set command for cookie: ${name}`);
38
- sendCommand("cookie", [name, value, options]);
39
- },
40
-
41
- // 3. Proxy to set headers
42
- setHeader: (name, value) => {
43
- sendCommand("setHeader", [name, value]);
44
- },
45
-
46
- // 4. Proxy to redirect
47
- // IMPROVEMENT: Pass the arguments as is to the parent so that it applies
48
- // the security logic (safeRedirect) and decides the status.
49
- redirect: (arg1, arg2) => {
50
- // arg1 can be status or url
51
- // arg2 is url (if arg1 is status) or undefined
52
- if (arg2) {
53
- sendCommand("redirect", [arg1, arg2]); // [status, url]
54
- } else {
55
- sendCommand("redirect", [arg1]); // [url]
56
- }
57
- },
58
-
59
- // 5. Proxy for status code
60
- status: (code) => {
61
- sendCommand("status", [code]);
62
- },
63
- };
64
- }
65
-
66
- module.exports = {
67
- createResponseProxy,
68
- };
@@ -1,130 +0,0 @@
1
- // generate-static-pages.js
2
- const path = require("path");
3
- const { mkdirSync, createWriteStream } = require("fs");
4
- const fs = require("fs").promises;
5
- const renderAppToHtml = require("./render-app-to-html.js");
6
- const { getStaticMetadata } = require("./build-static-pages.js");
7
- const { processMetadata } = require("./get-ssg-metadata.js");
8
- const { updateStatus } = require("./status-manifest.js");
9
-
10
- const OUT_DIR = path.resolve(".dinou/dist2");
11
-
12
- async function generateStaticPages(routes) {
13
- for (const route of routes) {
14
- const reqPath = route.endsWith("/") ? route : route + "/";
15
- const htmlPath = path.join(OUT_DIR, reqPath, "index.html");
16
-
17
- const query = {};
18
- const paramsString = JSON.stringify(query);
19
- const capturedStatus = {};
20
- const contextForChild = {
21
- req: {
22
- query,
23
- cookies: {},
24
- headers: {
25
- "user-agent": "Dinou-SSG-Builder",
26
- host: "localhost",
27
- "x-forwarded-proto": "http",
28
- },
29
- path: reqPath,
30
- method: "GET",
31
- },
32
- };
33
-
34
- try {
35
- mkdirSync(path.dirname(htmlPath), { recursive: true });
36
- const fileStream = createWriteStream(htmlPath);
37
- let htmlStream = null;
38
-
39
- const mockRes = {
40
- headersSent: true,
41
- _cookies: [],
42
- cookie(name, value, options) {
43
- this._cookies.push({ name, value, options });
44
- },
45
- write: (chunk) => {
46
- if (!fileStream.writableEnded) fileStream.write(chunk);
47
- },
48
- end: (chunk) => {
49
- if (chunk && !fileStream.writableEnded) fileStream.write(chunk);
50
- if (htmlStream) {
51
- htmlStream.unpipe(fileStream);
52
- }
53
- if (!fileStream.writableEnded) fileStream.end();
54
- },
55
- status: (code) => {
56
- if (code !== 200)
57
- console.warn(`[SSG] Status ${code} ignored for ${reqPath}`);
58
- capturedStatus.value = code;
59
- },
60
- setHeader: () => { },
61
- clearCookie: () => { },
62
- redirect: () => { },
63
- };
64
-
65
- htmlStream = renderAppToHtml(
66
- reqPath,
67
- paramsString,
68
- contextForChild,
69
- mockRes,
70
- capturedStatus
71
- );
72
-
73
- const metadata = getStaticMetadata(reqPath);
74
- let sideEffectScripts = "";
75
- if (metadata && metadata.effects) {
76
- sideEffectScripts = processMetadata(metadata.effects);
77
- }
78
-
79
- await new Promise((resolve, reject) => {
80
- if (sideEffectScripts) {
81
- fileStream.write(sideEffectScripts);
82
- }
83
- htmlStream.pipe(fileStream, { end: false });
84
-
85
- htmlStream.on("end", () => {
86
- if (!fileStream.writableEnded) fileStream.end();
87
- updateStatus(reqPath, capturedStatus.value);
88
- resolve();
89
- });
90
-
91
- htmlStream.on("error", (err) => {
92
- if (err.code === "ERR_STREAM_WRITE_AFTER_END") {
93
- resolve();
94
- } else {
95
- reject(err);
96
- }
97
- });
98
-
99
- fileStream.on("error", reject);
100
- });
101
-
102
- if (metadata) {
103
- const metadataPath = path.join(OUT_DIR, reqPath, "metadata.json");
104
- await fs.writeFile(
105
- metadataPath,
106
- JSON.stringify({
107
- revalidate: metadata.revalidate,
108
- generatedAt: Date.now(),
109
- effects: metadata.effects,
110
- tags: metadata.tags || [],
111
- }, null, 2),
112
- "utf8"
113
- );
114
- }
115
-
116
- console.log("✅ Generated HTML:", reqPath);
117
- } catch (error) {
118
- if (error.code === "ERR_STREAM_WRITE_AFTER_END") {
119
- console.log("⚠️ Ignored write-after-end race condition for:", reqPath);
120
- } else {
121
- console.error("❌ Error rendering:", reqPath);
122
- console.error(error.message);
123
- }
124
- }
125
- }
126
-
127
- console.log("🟢 Static page generation complete.");
128
- }
129
-
130
- module.exports = generateStaticPages;