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.
- package/CHANGELOG.md +99 -0
- package/README.md +9461 -1080
- package/cli.js +392 -169
- package/dinou/adapters/bun.js +62 -0
- package/dinou/adapters/cloudflare.js +65 -0
- package/dinou/adapters/deno.js +48 -0
- package/dinou/adapters/netlify.js +85 -0
- package/dinou/adapters/node.mjs +31 -0
- package/dinou/bun/build.mjs +1077 -0
- package/dinou/cloudflare/build.mjs +1121 -0
- package/dinou/cloudflare/shims/child_process.js +14 -0
- package/dinou/cloudflare/shims/empty.js +2 -0
- package/dinou/cloudflare/shims/fs.js +99 -0
- package/dinou/cloudflare/shims/os.js +29 -0
- package/dinou/config.js +1 -0
- package/dinou/config.mjs +2 -0
- package/dinou/constants.js +29 -4
- package/dinou/core/babel-esm-loader.js +66 -37
- package/dinou/core/bailout-proxy.js +61 -0
- package/dinou/core/build-static-pages.js +309 -157
- package/dinou/core/cache-revalidate.js +471 -144
- package/dinou/core/cache-slot.js +387 -0
- package/dinou/core/client-redirect.jsx +12 -7
- package/dinou/core/client-webpack.jsx +457 -82
- package/dinou/core/client.jsx +452 -82
- package/dinou/core/config.js +11 -0
- package/dinou/core/dinou-paths.js +52 -0
- package/dinou/core/edge-ssr.js +122 -0
- package/dinou/core/export-static.mjs +64 -0
- package/dinou/core/generate-route-types.js +252 -0
- package/dinou/core/generate-static-page.js +55 -94
- package/dinou/core/generate-static-rsc.js +47 -15
- package/dinou/core/generate-static.js +212 -10
- package/dinou/core/generating-isg.js +5 -0
- package/dinou/core/get-asset-from-manifest.js +9 -3
- package/dinou/core/get-error-jsx.js +221 -41
- package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
- package/dinou/core/get-jsx.js +172 -95
- package/dinou/core/handler.js +2296 -0
- package/dinou/core/http-adapter.js +115 -0
- package/dinou/core/import-module.js +83 -53
- package/dinou/core/layout-functions.js +181 -0
- package/dinou/core/link.jsx +18 -6
- package/dinou/core/load-dinou-config.js +60 -0
- package/dinou/core/manifest-provider.js +261 -0
- package/dinou/core/navigation-utils.js +4 -0
- package/dinou/core/navigation.js +50 -39
- package/dinou/core/parse-exports.js +207 -2
- package/dinou/core/ppr-context.js +105 -0
- package/dinou/core/ppr-runtime.js +263 -0
- package/dinou/core/redirect.jsx +20 -19
- package/dinou/core/render-jsx-to-client-jsx.js +133 -15
- package/dinou/core/request-context.js +47 -13
- package/dinou/core/resolve-module-ppr.js +32 -0
- package/dinou/core/revalidating.js +28 -9
- package/dinou/core/route-generator.js +107 -0
- package/dinou/core/rsc-renderer.js +90 -0
- package/dinou/core/run-ssg.js +19 -0
- package/dinou/core/scan-dependency-components.js +218 -0
- package/dinou/core/server-function-proxy-webpack.js +3 -0
- package/dinou/core/server-function-proxy.js +3 -0
- package/dinou/core/slot.js +302 -0
- package/dinou/core/status-manifest.js +41 -1
- package/dinou/core/storage-adapter.js +522 -0
- package/dinou/core/url-resolver.js +16 -16
- package/dinou/core/vfs.js +139 -68
- package/dinou/deno/build.mjs +1112 -0
- package/dinou/deno/seed-kv.js +115 -0
- package/dinou/esbuild/build.mjs +51 -1
- package/dinou/esbuild/dev.mjs +290 -224
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
- package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
- package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
- package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
- package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
- package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
- package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
- package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
- package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
- package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
- package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
- package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
- package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
- package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
- package/dinou/index.d.ts +315 -11
- package/dinou/index.js +12 -6
- package/dinou/index.mjs +9 -0
- package/dinou/node/build.mjs +195 -0
- package/dinou/node/bundle-dual-engine.mjs +1012 -0
- package/dinou/node/dev.mjs +2589 -0
- package/dinou/node/port-selector.mjs +164 -0
- package/dinou/node/terminal-status.mjs +345 -0
- package/dinou/package.json +17 -14
- package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
- package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
- package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
- package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
- package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
- package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
- package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
- package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
- package/dinou/rollup/rollup.config.js +155 -43
- package/dinou/server.d.ts +114 -12
- package/dinou/server.js +35 -10
- package/dinou/server.mjs +30 -9
- package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
- package/dinou/webpack/loaders/server-functions-loader.js +22 -1
- package/dinou/webpack/loaders/swc-loader.js +76 -0
- package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
- package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
- package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
- package/dinou/webpack/webpack.config.js +227 -29
- package/eject.js +173 -94
- package/package.json +110 -107
- package/dinou/core/asset-require-hook.js +0 -58
- package/dinou/core/client-error-webpack.jsx +0 -432
- package/dinou/core/client-error.jsx +0 -432
- package/dinou/core/concurrency-manager.js +0 -48
- package/dinou/core/context-proxy.js +0 -68
- package/dinou/core/generate-static-pages.js +0 -130
- package/dinou/core/generate-static-rscs.js +0 -151
- package/dinou/core/get-ssg-metadata.js +0 -31
- package/dinou/core/register-loader.mjs +0 -11
- package/dinou/core/register-paths.js +0 -27
- package/dinou/core/render-app-to-html.js +0 -430
- package/dinou/core/render-html.js +0 -407
- package/dinou/core/server.js +0 -1542
- 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
|
-
|
|
8
|
-
typeof module !== "undefined" && typeof module.require === "function"
|
|
9
|
-
? module.require.bind(module)
|
|
10
|
-
: null;
|
|
7
|
+
let AsyncLocalStorageClass;
|
|
11
8
|
|
|
12
|
-
if (
|
|
13
|
-
|
|
9
|
+
if (typeof globalThis.AsyncLocalStorage !== "undefined") {
|
|
10
|
+
AsyncLocalStorageClass = globalThis.AsyncLocalStorage;
|
|
11
|
+
}
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|