dinou 6.1.0 → 7.0.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 +99 -0
  2. package/README.md +9461 -1080
  3. package/cli.js +392 -169
  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 +309 -157
  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 +172 -95
  39. package/dinou/core/handler.js +2296 -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 +181 -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 +207 -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 +133 -15
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +32 -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 +17 -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 +173 -94
  121. package/package.json +110 -107
  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
@@ -4,19 +4,39 @@ const DINOU_CONTEXT_KEY = Symbol.for("dinou.request.context.storage");
4
4
  let requestStorage;
5
5
 
6
6
  if (typeof window === "undefined") {
7
- const nodeRequire =
8
- typeof module !== "undefined" && typeof module.require === "function"
9
- ? module.require.bind(module)
10
- : null;
7
+ let AsyncLocalStorageClass;
11
8
 
12
- if (nodeRequire) {
13
- const { AsyncLocalStorage } = nodeRequire("node:async_hooks");
9
+ if (typeof globalThis.AsyncLocalStorage !== "undefined") {
10
+ AsyncLocalStorageClass = globalThis.AsyncLocalStorage;
11
+ }
14
12
 
15
- if (!global[DINOU_CONTEXT_KEY]) {
16
- global[DINOU_CONTEXT_KEY] = new AsyncLocalStorage();
17
- }
13
+ if (!AsyncLocalStorageClass) {
14
+ try {
15
+ if (typeof globalThis.__dinou_require__ === "function") {
16
+ AsyncLocalStorageClass = globalThis.__dinou_require__("node:async_hooks")?.AsyncLocalStorage;
17
+ } else if (typeof process !== "undefined" && process.versions && (process.versions.node || process.versions.bun)) {
18
+ const getRequire = new Function("return typeof require !== 'undefined' ? require : null");
19
+ const nodeRequire = getRequire();
20
+ if (nodeRequire) {
21
+ AsyncLocalStorageClass = nodeRequire("node:async_hooks")?.AsyncLocalStorage;
22
+ }
23
+ }
24
+ } catch (e) {}
25
+ }
18
26
 
19
- requestStorage = global[DINOU_CONTEXT_KEY];
27
+ if (AsyncLocalStorageClass) {
28
+ if (typeof globalThis.AsyncLocalStorage === "undefined") {
29
+ globalThis.AsyncLocalStorage = AsyncLocalStorageClass;
30
+ }
31
+ if (!globalThis[DINOU_CONTEXT_KEY]) {
32
+ globalThis[DINOU_CONTEXT_KEY] = new AsyncLocalStorageClass();
33
+ }
34
+ requestStorage = globalThis[DINOU_CONTEXT_KEY];
35
+ } else {
36
+ requestStorage = {
37
+ run: (store, callback) => callback(),
38
+ getStore: () => undefined,
39
+ };
20
40
  }
21
41
  } else {
22
42
  requestStorage = {
@@ -25,6 +45,19 @@ if (typeof window === "undefined") {
25
45
  };
26
46
  }
27
47
 
48
+ function setCurrentContext(ctx) {
49
+ if (typeof globalThis !== "undefined") {
50
+ globalThis[Symbol.for("dinou.request.context.current")] = ctx;
51
+ }
52
+ }
53
+
54
+ function getCurrentContext() {
55
+ if (typeof globalThis !== "undefined") {
56
+ return globalThis[Symbol.for("dinou.request.context.current")];
57
+ }
58
+ return undefined;
59
+ }
60
+
28
61
  function getContext() {
29
62
  if (typeof window !== "undefined") {
30
63
  console.error(
@@ -32,12 +65,13 @@ function getContext() {
32
65
  );
33
66
  return {};
34
67
  }
35
- if (!requestStorage) return undefined;
36
- const store = requestStorage.getStore();
37
- return store;
68
+ const store = requestStorage?.getStore();
69
+ return store || getCurrentContext() || {};
38
70
  }
39
71
 
40
72
  module.exports = {
41
73
  requestStorage,
42
74
  getContext,
75
+ setCurrentContext,
76
+ getCurrentContext,
43
77
  };
@@ -0,0 +1,32 @@
1
+ // dinou/core/resolve-module-ppr.js
2
+ // Resolves the PPR configuration from an imported module (page_functions, layout_functions).
3
+ // Supports constants (export const ppr = true/false), sync functions (export function ppr()),
4
+ // and async functions (export async function ppr()).
5
+
6
+ async function resolveModulePpr(mod) {
7
+ if (!mod) return null;
8
+ const raw =
9
+ mod.ppr !== undefined
10
+ ? mod.ppr
11
+ : mod.default?.ppr !== undefined
12
+ ? mod.default.ppr
13
+ : undefined;
14
+
15
+ if (raw === undefined || raw === null) return null;
16
+
17
+ if (typeof raw === "function") {
18
+ try {
19
+ const res = await raw();
20
+ return res !== null && res !== undefined ? Boolean(res) : null;
21
+ } catch (e) {
22
+ console.error("[Dinou PPR] Error evaluating ppr() function:", e);
23
+ return null;
24
+ }
25
+ }
26
+
27
+ return Boolean(raw);
28
+ }
29
+
30
+ module.exports = {
31
+ resolveModulePpr,
32
+ };
@@ -1,9 +1,21 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs").promises;
3
3
  const { existsSync, copyFileSync } = require("fs");
4
- const generateStaticPage = require("./generate-static-page");
5
- const { buildStaticPage } = require("./build-static-pages");
6
- const generateStaticRSC = require("./generate-static-rsc");
4
+ let _generateStaticPage = null;
5
+ function getGenerateStaticPage() {
6
+ if (!_generateStaticPage) _generateStaticPage = require("./generate-static-page");
7
+ return _generateStaticPage;
8
+ }
9
+ let _buildStaticPage = null;
10
+ function getBuildStaticPage() {
11
+ if (!_buildStaticPage) _buildStaticPage = require("./build-static-pages").buildStaticPage;
12
+ return _buildStaticPage;
13
+ }
14
+ let _generateStaticRSC = null;
15
+ function getGenerateStaticRSC() {
16
+ if (!_generateStaticRSC) _generateStaticRSC = require("./generate-static-rsc");
17
+ return _generateStaticRSC;
18
+ }
7
19
  const { safeRename } = require("./safe-rename");
8
20
  const { updateStatus } = require("./status-manifest");
9
21
 
@@ -22,8 +34,8 @@ function revalidating(reqPath, isDynamicFromServer) {
22
34
 
23
35
  const isExpired =
24
36
  typeof revalidate === "number" &&
25
- revalidate > 0 &&
26
- Date.now() > generatedAt + revalidate;
37
+ revalidate >= 1 &&
38
+ Date.now() > generatedAt + revalidate * 1000;
27
39
 
28
40
  if (isExpired && !regenerating.has(reqPath)) {
29
41
  try {
@@ -32,6 +44,11 @@ function revalidating(reqPath, isDynamicFromServer) {
32
44
  path.join(dist2Folder, reqPath, "index.html"),
33
45
  path.join(dist2Folder, reqPath, "index._old.html")
34
46
  );
47
+ if (existsSync(path.join(dist2Folder, reqPath, "page.rsc")))
48
+ copyFileSync(
49
+ path.join(dist2Folder, reqPath, "page.rsc"),
50
+ path.join(dist2Folder, reqPath, "page._old.rsc")
51
+ );
35
52
  if (existsSync(path.join(dist2Folder, reqPath, "rsc.rsc")))
36
53
  copyFileSync(
37
54
  path.join(dist2Folder, reqPath, "rsc.rsc"),
@@ -47,7 +64,7 @@ function revalidating(reqPath, isDynamicFromServer) {
47
64
  try {
48
65
  console.log(`[ISR] Starting regeneration for ${reqPath}...`);
49
66
  const isDynamic = {};
50
- await buildStaticPage(reqPath, isDynamic);
67
+ await getBuildStaticPage()(reqPath, isDynamic);
51
68
  if (isDynamic.value) {
52
69
  isDynamicFromServer.value = true;
53
70
  console.log(
@@ -57,7 +74,7 @@ function revalidating(reqPath, isDynamicFromServer) {
57
74
  return;
58
75
  }
59
76
 
60
- const rscResult = await generateStaticRSC(reqPath);
77
+ const rscResult = await getGenerateStaticRSC()(reqPath);
61
78
  if (!rscResult.success) {
62
79
  console.warn(`⚠️ [ISR] RSC generation failed for ${reqPath}. Aborting.`);
63
80
  await fs.unlink(rscResult.tempPath).catch(() => { });
@@ -67,7 +84,7 @@ function revalidating(reqPath, isDynamicFromServer) {
67
84
  // Commit the RSC payload immediately so that generateStaticPage can read it
68
85
  await safeRename(rscResult.tempPath, rscResult.finalPath);
69
86
 
70
- const pageResult = await generateStaticPage(reqPath);
87
+ const pageResult = await getGenerateStaticPage()(reqPath);
71
88
  if (pageResult.success) {
72
89
  await safeRename(pageResult.tempPath, pageResult.finalPath);
73
90
  updateStatus(reqPath, pageResult.status);
@@ -91,4 +108,6 @@ function revalidating(reqPath, isDynamicFromServer) {
91
108
  }).catch((err) => { });
92
109
  }
93
110
 
94
- module.exports = { revalidating, regenerating };
111
+ const inFlightGenerations = new Map();
112
+
113
+ module.exports = { revalidating, regenerating, inFlightGenerations };
@@ -0,0 +1,107 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const parseExports = require("./parse-exports.js");
4
+
5
+ function collectSourceFiles(dir, baseDir = dir) {
6
+ if (!fs.existsSync(dir)) return [];
7
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
8
+ let files = [];
9
+
10
+ for (const entry of entries) {
11
+ const fullPath = path.join(dir, entry.name);
12
+ if (entry.isDirectory()) {
13
+ files = files.concat(collectSourceFiles(fullPath, baseDir));
14
+ } else if (/\.[jt]sx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
15
+ const relPath = path.relative(baseDir, fullPath).replace(/\\/g, "/");
16
+ files.push({
17
+ relPath: "src/" + relPath,
18
+ fullPath: fullPath.replace(/\\/g, "/"),
19
+ });
20
+ }
21
+ }
22
+ return files;
23
+ }
24
+
25
+ /**
26
+ * Generates JS code that registers all src/ modules into globalThis.__DINOU_ROUTE_MODULES__
27
+ * and their metadata (such as PPR support) into globalThis.__DINOU_ROUTE_METADATA__.
28
+ * @param {string} projectRoot Root directory of the project
29
+ * @param {string} relativeImportBase Relative path from generated output file to projectRoot
30
+ * @returns {string} JavaScript code
31
+ */
32
+ function generateRouteModulesCode(projectRoot = process.cwd(), relativeImportBase = "../..") {
33
+ const srcDir = path.resolve(projectRoot, "src");
34
+ const files = collectSourceFiles(srcDir);
35
+
36
+ const configFilenames = ["dinou.config.js", "dinou.config.mjs", "dinou.config.cjs", "dinou.config.ts"];
37
+ let foundConfig = null;
38
+ for (const cf of configFilenames) {
39
+ if (fs.existsSync(path.resolve(projectRoot, cf))) {
40
+ foundConfig = cf;
41
+ break;
42
+ }
43
+ }
44
+
45
+ const lines = [
46
+ "// Auto-generated by Dinou Route Generator for Edge/Workers bundling",
47
+ ];
48
+
49
+ if (foundConfig) {
50
+ const configImportPath = `${relativeImportBase}/${foundConfig}`.replace(/\/+/g, "/");
51
+ lines.push(`import dinouConfigModule from ${JSON.stringify(configImportPath)};`);
52
+ }
53
+
54
+ lines.push("export const routeModules = {");
55
+
56
+ const routeMetadata = {};
57
+
58
+ for (const file of files) {
59
+ const importPath = `${relativeImportBase}/${file.relPath}`.replace(/\/+/g, "/");
60
+ lines.push(` ${JSON.stringify(file.relPath)}: () => import(${JSON.stringify(importPath)}),`);
61
+
62
+ // Detect PPR from page_functions or layout_functions ONLY
63
+ if (/(?:^|\/)(?:page_functions|layout_functions|page\.functions|layout\.functions)\.[jt]sx?$/.test(file.relPath)) {
64
+ try {
65
+ const content = fs.readFileSync(file.fullPath, "utf8");
66
+ const isPpr = parseExports.parsePprConfig ? parseExports.parsePprConfig(content) : null;
67
+ if (isPpr !== null) {
68
+ routeMetadata[file.relPath] = { ppr: Boolean(isPpr) };
69
+ }
70
+ } catch (e) {}
71
+ }
72
+ }
73
+
74
+ lines.push("};");
75
+ lines.push("");
76
+ lines.push("export const routeMetadata = " + JSON.stringify(routeMetadata, null, 2) + ";");
77
+ lines.push("");
78
+ lines.push("if (typeof globalThis !== 'undefined') {");
79
+ lines.push(" globalThis.__DINOU_ROUTE_MODULES__ = {");
80
+ lines.push(" ...(globalThis.__DINOU_ROUTE_MODULES__ || {}),");
81
+ lines.push(" ...routeModules,");
82
+ lines.push(" };");
83
+ lines.push(" globalThis.__DINOU_ROUTE_METADATA__ = {");
84
+ lines.push(" ...(globalThis.__DINOU_ROUTE_METADATA__ || {}),");
85
+ lines.push(" ...routeMetadata,");
86
+ lines.push(" };");
87
+ if (foundConfig) {
88
+ lines.push(" const resolvedConfig = (dinouConfigModule && dinouConfigModule.default) ? dinouConfigModule.default : dinouConfigModule;");
89
+ lines.push(" globalThis.__DINOU_CONFIG__ = resolvedConfig;");
90
+ }
91
+ lines.push("}");
92
+ lines.push("");
93
+ if (foundConfig) {
94
+ lines.push("export const dinouConfig = (dinouConfigModule && dinouConfigModule.default) ? dinouConfigModule.default : dinouConfigModule;");
95
+ }
96
+ lines.push("export default routeModules;");
97
+
98
+ return lines.join("\n");
99
+ }
100
+
101
+ const { generateRouteTypes } = require("./generate-route-types.js");
102
+
103
+ module.exports = {
104
+ collectSourceFiles,
105
+ generateRouteModulesCode,
106
+ generateRouteTypes,
107
+ };
@@ -0,0 +1,90 @@
1
+ // dinou/core/rsc-renderer.js
2
+ // Universal RSC Stream Renderer supporting Node Streams and Web Streams (Cloudflare Workers / Edge).
3
+
4
+ const { pathToFileURL } = require("url");
5
+
6
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
7
+
8
+ function isEdgeRuntime(options = {}) {
9
+ const rt =
10
+ (options && options.runtime) ||
11
+ (typeof globalThis !== "undefined" && globalThis.__DINOU_RUNTIME__) ||
12
+ (typeof process !== "undefined" && process.env && process.env.DINOU_RUNTIME);
13
+ if (
14
+ typeof rt === "string" &&
15
+ (rt === "edge" || rt === "deno-edge" || rt === "bun" || rt === "bun-bundle" || rt === "node-bundle" || rt.includes("edge") || rt.includes("bundle"))
16
+ ) {
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+
22
+ let nodeRender = null;
23
+ function getNodeRenderer() {
24
+ if (!nodeRender) {
25
+ nodeRender = isWebpack
26
+ ? require("react-server-dom-webpack/server")
27
+ : require("@roggc/react-server-dom-esm/server");
28
+ }
29
+ return nodeRender;
30
+ }
31
+
32
+ let edgeRender = null;
33
+ function getEdgeRenderer() {
34
+ if (!edgeRender) {
35
+ try {
36
+ edgeRender = require("react-server-dom-webpack/server.edge");
37
+ } catch (e) {
38
+ edgeRender = require("react-server-dom-webpack/server");
39
+ }
40
+ }
41
+ return edgeRender;
42
+ }
43
+
44
+ /**
45
+ * Renders JSX / Model to an RSC Stream based on runtime environment.
46
+ * @param {*} model The React element tree or value to render
47
+ * @param {object} manifest The client component manifest
48
+ * @param {object} options Runtime options ({ runtime: "node" | "edge" })
49
+ * @returns {ReadableStream | { pipe: Function, abort: Function }}
50
+ */
51
+ function renderRSCStream(model, manifest, options = {}) {
52
+ if (isEdgeRuntime(options)) {
53
+ const edge = getEdgeRenderer();
54
+ const clientManifest =
55
+ manifest ||
56
+ (typeof globalThis !== "undefined" && globalThis.__DINOU_CLIENT_MANIFEST__) ||
57
+ {};
58
+ return edge.renderToReadableStream(model, clientManifest, options);
59
+ }
60
+
61
+ const node = getNodeRenderer();
62
+ const manifestOrUrl = isWebpack
63
+ ? manifest
64
+ : (pathToFileURL(process.cwd()).href + "/");
65
+ return node.renderToPipeableStream(model, manifestOrUrl, options);
66
+ }
67
+
68
+ /**
69
+ * Pipes RSC rendering to a response bridge or returns a native Web Response directly.
70
+ * @param {*} model
71
+ * @param {object} bridge WebResponseBridge instance
72
+ * @param {object} manifest
73
+ * @param {object} options
74
+ */
75
+ function pipeRSC(model, bridge, manifest, options = {}) {
76
+ if (isEdgeRuntime(options)) {
77
+ const stream = renderRSCStream(model, manifest, options);
78
+ bridge.setEdgeStream(stream);
79
+ return;
80
+ }
81
+
82
+ const { pipe } = renderRSCStream(model, manifest, options);
83
+ pipe(bridge);
84
+ }
85
+
86
+ module.exports = {
87
+ isEdgeRuntime,
88
+ renderRSCStream,
89
+ pipeRSC,
90
+ };
@@ -0,0 +1,19 @@
1
+ // dinou/core/run-ssg.js
2
+ // Standalone Static Site Generation (SSG) runner for Dinou.
3
+ process.env.NODE_ENV = "production";
4
+ process.env.DINOU_RUNTIME = "node-bundle";
5
+
6
+ const generateStatic = require("./generate-static.js");
7
+
8
+ async function runSSG() {
9
+ await generateStatic();
10
+ }
11
+
12
+ if (require.main === module) {
13
+ runSSG().catch((err) => {
14
+ console.error("❌ [SSG] Static generation failed:", err);
15
+ process.exit(1);
16
+ });
17
+ }
18
+
19
+ module.exports = { runSSG };
@@ -0,0 +1,218 @@
1
+ // dinou/core/scan-dependency-components.js
2
+ // Scans dependencies for React "use client" directives without recursively walking massive trees.
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ function isSupportedClientModule(filePath, content) {
8
+ const norm = filePath.replace(/\\/g, "/");
9
+ if (!norm.includes("node_modules")) return true;
10
+ if (content === undefined && fs.existsSync(filePath)) {
11
+ try {
12
+ content = fs.readFileSync(filePath, "utf8");
13
+ } catch (e) {
14
+ return false;
15
+ }
16
+ }
17
+ if (typeof content !== "string") return false;
18
+ if (content.includes("System.register(") || content.includes("System.registerDynamic(")) return false;
19
+ if (content.includes("define.amd") && !content.includes("export ") && !content.includes("module.exports")) return false;
20
+ return true;
21
+ }
22
+
23
+ function scanDependencyForClientComponents(depDir, clientFiles, useClientRegex) {
24
+ const pkgJsonPath = path.join(depDir, "package.json");
25
+ if (!fs.existsSync(pkgJsonPath)) return;
26
+ let depPkg;
27
+ try {
28
+ depPkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
29
+ } catch (e) {
30
+ return;
31
+ }
32
+
33
+ const depName = depPkg.name || "";
34
+ const hasReact =
35
+ depPkg.dependencies?.react ||
36
+ depPkg.peerDependencies?.react ||
37
+ depPkg.devDependencies?.react ||
38
+ (Array.isArray(depPkg.keywords) && depPkg.keywords.includes("react")) ||
39
+ depName.toLowerCase().includes("react");
40
+ if (!hasReact) return;
41
+
42
+ const candidateFiles = new Set();
43
+ if (typeof depPkg.main === "string") candidateFiles.add(path.resolve(depDir, depPkg.main));
44
+ if (typeof depPkg.module === "string") candidateFiles.add(path.resolve(depDir, depPkg.module));
45
+ if (depPkg.exports) {
46
+ function addExport(exp) {
47
+ if (typeof exp === "string") {
48
+ if (/\.[jt]sx?$/.test(exp)) candidateFiles.add(path.resolve(depDir, exp));
49
+ } else if (typeof exp === "object" && exp !== null) {
50
+ for (const v of Object.values(exp)) addExport(v);
51
+ }
52
+ }
53
+ addExport(depPkg.exports);
54
+ }
55
+
56
+ try {
57
+ const rootEntries = fs.readdirSync(depDir, { withFileTypes: true });
58
+ for (const entry of rootEntries) {
59
+ if (!entry.isDirectory() && /\.[jt]sx?$/.test(entry.name)) {
60
+ candidateFiles.add(path.join(depDir, entry.name));
61
+ }
62
+ }
63
+ } catch (e) {}
64
+
65
+ for (const filePath of candidateFiles) {
66
+ if (!fs.existsSync(filePath)) continue;
67
+ try {
68
+ const content = fs.readFileSync(filePath, "utf8");
69
+ if (useClientRegex.test(content.trim()) && isSupportedClientModule(filePath, content)) {
70
+ clientFiles.add(path.resolve(filePath));
71
+ }
72
+ } catch (e) {}
73
+ }
74
+ }
75
+
76
+ function scanProjectDependenciesForClientComponents(projectRoot, clientFiles, useClientRegex) {
77
+ try {
78
+ const pkgPath = path.resolve(projectRoot, "package.json");
79
+ if (!fs.existsSync(pkgPath)) return;
80
+
81
+ // Cache check based on package-lock.json or package.json
82
+ const cacheDir = path.resolve(projectRoot, ".dinou");
83
+ const cacheFile = path.join(cacheDir, "deps-client-cache.json");
84
+
85
+ const lockPath = path.resolve(projectRoot, "package-lock.json");
86
+ const targetLock = fs.existsSync(lockPath) ? lockPath : pkgPath;
87
+ let lockStat;
88
+ try {
89
+ lockStat = fs.statSync(targetLock);
90
+ } catch (e) {}
91
+
92
+ const signature = lockStat ? `${lockStat.size}-${Math.round(lockStat.mtimeMs)}` : null;
93
+
94
+ if (signature && fs.existsSync(cacheFile)) {
95
+ try {
96
+ const cache = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
97
+ if (cache.signature === signature && Array.isArray(cache.clientFiles)) {
98
+ let allExist = true;
99
+ for (const f of cache.clientFiles) {
100
+ if (!fs.existsSync(f)) {
101
+ allExist = false;
102
+ break;
103
+ }
104
+ }
105
+ if (allExist) {
106
+ for (const f of cache.clientFiles) {
107
+ clientFiles.add(path.resolve(f));
108
+ }
109
+ return;
110
+ }
111
+ }
112
+ } catch (e) {}
113
+ }
114
+
115
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
116
+ const deps = Object.keys(pkg.dependencies || {});
117
+ for (const dep of deps) {
118
+ if (dep === "react" || dep === "react-dom" || dep === "dinou") continue;
119
+ const depDir = path.resolve(projectRoot, "node_modules", dep);
120
+ if (fs.existsSync(depDir)) {
121
+ scanDependencyForClientComponents(depDir, clientFiles, useClientRegex);
122
+ }
123
+ }
124
+
125
+ // Save discovered files to cache
126
+ if (signature) {
127
+ try {
128
+ if (!fs.existsSync(cacheDir)) {
129
+ fs.mkdirSync(cacheDir, { recursive: true });
130
+ }
131
+ const discovered = [];
132
+ for (const f of clientFiles) {
133
+ const norm = f.replace(/\\/g, "/");
134
+ if (norm.includes("/node_modules/")) {
135
+ discovered.push(f);
136
+ }
137
+ }
138
+ fs.writeFileSync(cacheFile, JSON.stringify({ signature, clientFiles: discovered }), "utf8");
139
+ } catch (e) {}
140
+ }
141
+ } catch (e) {}
142
+ }
143
+
144
+ async function scanProjectDependenciesForClientComponentsAsync(projectRoot, clientFiles, useClientRegex) {
145
+ try {
146
+ const pkgPath = path.resolve(projectRoot, "package.json");
147
+ if (!fs.existsSync(pkgPath)) return;
148
+
149
+ // Cache check based on package-lock.json or package.json
150
+ const cacheDir = path.resolve(projectRoot, ".dinou");
151
+ const cacheFile = path.join(cacheDir, "deps-client-cache.json");
152
+
153
+ const lockPath = path.resolve(projectRoot, "package-lock.json");
154
+ const targetLock = fs.existsSync(lockPath) ? lockPath : pkgPath;
155
+ let lockStat;
156
+ try {
157
+ lockStat = fs.statSync(targetLock);
158
+ } catch (e) {}
159
+
160
+ const signature = lockStat ? `${lockStat.size}-${Math.round(lockStat.mtimeMs)}` : null;
161
+
162
+ if (signature && fs.existsSync(cacheFile)) {
163
+ try {
164
+ const cache = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
165
+ if (cache.signature === signature && Array.isArray(cache.clientFiles)) {
166
+ let allExist = true;
167
+ for (const f of cache.clientFiles) {
168
+ if (!fs.existsSync(f)) {
169
+ allExist = false;
170
+ break;
171
+ }
172
+ }
173
+ if (allExist) {
174
+ for (const f of cache.clientFiles) {
175
+ clientFiles.add(path.resolve(f));
176
+ }
177
+ return;
178
+ }
179
+ }
180
+ } catch (e) {}
181
+ }
182
+
183
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
184
+ const deps = Object.keys(pkg.dependencies || {});
185
+ for (const dep of deps) {
186
+ if (dep === "react" || dep === "react-dom" || dep === "dinou") continue;
187
+ const depDir = path.resolve(projectRoot, "node_modules", dep);
188
+ if (fs.existsSync(depDir)) {
189
+ scanDependencyForClientComponents(depDir, clientFiles, useClientRegex);
190
+ await new Promise((resolve) => setImmediate(resolve));
191
+ }
192
+ }
193
+
194
+ // Save discovered files to cache
195
+ if (signature) {
196
+ try {
197
+ if (!fs.existsSync(cacheDir)) {
198
+ fs.mkdirSync(cacheDir, { recursive: true });
199
+ }
200
+ const discovered = [];
201
+ for (const f of clientFiles) {
202
+ const norm = f.replace(/\\/g, "/");
203
+ if (norm.includes("/node_modules/")) {
204
+ discovered.push(f);
205
+ }
206
+ }
207
+ fs.writeFileSync(cacheFile, JSON.stringify({ signature, clientFiles: discovered }), "utf8");
208
+ } catch (e) {}
209
+ }
210
+ } catch (e) {}
211
+ }
212
+
213
+ module.exports = {
214
+ isSupportedClientModule,
215
+ scanDependencyForClientComponents,
216
+ scanProjectDependenciesForClientComponents,
217
+ scanProjectDependenciesForClientComponentsAsync,
218
+ };
@@ -21,6 +21,9 @@ function createServerFunctionProxy(id) {
21
21
  const headers = {
22
22
  "x-server-function-call": "1",
23
23
  };
24
+ if (typeof window !== "undefined" && window.location) {
25
+ headers["x-dinou-current-path"] = window.location.pathname;
26
+ }
24
27
 
25
28
  const formDataIndex = args.findIndex((arg) => arg instanceof FormData);
26
29
 
@@ -21,6 +21,9 @@ export function createServerFunctionProxy(id) {
21
21
  const headers = {
22
22
  "x-server-function-call": "1",
23
23
  };
24
+ if (typeof window !== "undefined" && window.location) {
25
+ headers["x-dinou-current-path"] = window.location.pathname;
26
+ }
24
27
 
25
28
  const formDataIndex = args.findIndex((arg) => arg instanceof FormData);
26
29