dinou 6.1.1 → 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 +94 -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 +16 -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 -108
- 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
|
@@ -1,144 +1,471 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const fs = require("fs").promises;
|
|
3
|
-
const { existsSync, copyFileSync } = require("fs");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
};
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs").promises;
|
|
3
|
+
const { existsSync, copyFileSync } = require("fs");
|
|
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
|
+
}
|
|
19
|
+
let _safeRename = null;
|
|
20
|
+
function getSafeRename() {
|
|
21
|
+
if (!_safeRename) _safeRename = require("./safe-rename").safeRename;
|
|
22
|
+
return _safeRename;
|
|
23
|
+
}
|
|
24
|
+
let _updateStatus = null;
|
|
25
|
+
function getUpdateStatus() {
|
|
26
|
+
if (!_updateStatus) _updateStatus = require("./status-manifest").updateStatus;
|
|
27
|
+
return _updateStatus;
|
|
28
|
+
}
|
|
29
|
+
const { getContext } = require("./request-context");
|
|
30
|
+
const { resolveRelativeUrl } = require("./url-resolver");
|
|
31
|
+
const { getStorageAdapter } = require("./storage-adapter");
|
|
32
|
+
const { isEdgeRuntime } = require("./rsc-renderer");
|
|
33
|
+
|
|
34
|
+
async function walkMetadataFiles(dir, fileList = []) {
|
|
35
|
+
try {
|
|
36
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const entryPath = path.join(dir, entry.name);
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
await walkMetadataFiles(entryPath, fileList);
|
|
41
|
+
} else if (
|
|
42
|
+
entry.name === "metadata.json" ||
|
|
43
|
+
entry.name === "layout.metadata.json" ||
|
|
44
|
+
entry.name === "slot.metadata.json"
|
|
45
|
+
) {
|
|
46
|
+
fileList.push(entryPath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
// Ignore read errors for individual folders/files
|
|
51
|
+
}
|
|
52
|
+
return fileList;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeRevalidatePath(reqPath) {
|
|
56
|
+
let targetPath = reqPath;
|
|
57
|
+
|
|
58
|
+
if (targetPath && !targetPath.startsWith("/") && !targetPath.includes("://")) {
|
|
59
|
+
const ctx = getContext();
|
|
60
|
+
let currentPathname = "/";
|
|
61
|
+
if (ctx && ctx.req) {
|
|
62
|
+
const headerPath =
|
|
63
|
+
ctx.req.headers?.["x-dinou-current-path"] ||
|
|
64
|
+
ctx.req.headers?.["X-Dinou-Current-Path"];
|
|
65
|
+
const referer = ctx.req.headers?.referer;
|
|
66
|
+
if (headerPath) {
|
|
67
|
+
currentPathname = headerPath;
|
|
68
|
+
} else if (referer) {
|
|
69
|
+
try {
|
|
70
|
+
currentPathname = new URL(referer).pathname;
|
|
71
|
+
} catch (e) { }
|
|
72
|
+
} else {
|
|
73
|
+
currentPathname = ctx.req.path || "/";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
targetPath = resolveRelativeUrl(targetPath, currentPathname);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let cleanPath = targetPath;
|
|
80
|
+
if (!cleanPath.startsWith("/")) {
|
|
81
|
+
cleanPath = "/" + cleanPath;
|
|
82
|
+
}
|
|
83
|
+
if (cleanPath !== "/" && cleanPath.endsWith("/")) {
|
|
84
|
+
cleanPath = cleanPath.slice(0, -1);
|
|
85
|
+
}
|
|
86
|
+
return cleanPath;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function revalidatePage(reqPath) {
|
|
90
|
+
const cleanPath = normalizeRevalidatePath(reqPath);
|
|
91
|
+
|
|
92
|
+
if (isEdgeRuntime()) {
|
|
93
|
+
const storage = getStorageAdapter();
|
|
94
|
+
const cleanPathKey = cleanPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
95
|
+
const htmlKey = cleanPathKey ? `${cleanPathKey}/index.html` : "index.html";
|
|
96
|
+
const metaKey = cleanPathKey ? `${cleanPathKey}/metadata.json` : "metadata.json";
|
|
97
|
+
const rscKey = cleanPathKey ? `${cleanPathKey}/page.rsc` : "page.rsc";
|
|
98
|
+
const legacyRscKey = cleanPathKey ? `${cleanPathKey}/rsc.rsc` : "rsc.rsc";
|
|
99
|
+
|
|
100
|
+
let cached = await storage.get(htmlKey);
|
|
101
|
+
if (!cached && cleanPathKey) {
|
|
102
|
+
cached = await storage.get(cleanPathKey);
|
|
103
|
+
}
|
|
104
|
+
let currentMeta = (cached && cached.metadata) || {};
|
|
105
|
+
try {
|
|
106
|
+
const metaItem = await storage.get(metaKey);
|
|
107
|
+
if (metaItem && metaItem.content) {
|
|
108
|
+
currentMeta = JSON.parse(metaItem.content);
|
|
109
|
+
}
|
|
110
|
+
} catch (e) {}
|
|
111
|
+
|
|
112
|
+
const prevGenTime = (currentMeta && currentMeta.generatedAt) || 0;
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
const newGenTime = now <= prevGenTime ? prevGenTime + 100 : now;
|
|
115
|
+
currentMeta.generatedAt = newGenTime;
|
|
116
|
+
|
|
117
|
+
let newHtml = cached ? cached.content : "";
|
|
118
|
+
if (newHtml) {
|
|
119
|
+
newHtml = newHtml.replace(
|
|
120
|
+
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/g,
|
|
121
|
+
new Date(newGenTime).toISOString()
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
await storage.set(htmlKey, newHtml, currentMeta);
|
|
125
|
+
if (cleanPathKey) {
|
|
126
|
+
await storage.set(cleanPathKey, newHtml, currentMeta);
|
|
127
|
+
}
|
|
128
|
+
await storage.set(metaKey, JSON.stringify(currentMeta));
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
let cachedRsc = await storage.get(rscKey);
|
|
132
|
+
if (!cachedRsc) {
|
|
133
|
+
cachedRsc = await storage.get(legacyRscKey);
|
|
134
|
+
}
|
|
135
|
+
if (cachedRsc && cachedRsc.content) {
|
|
136
|
+
const newRsc = cachedRsc.content.replace(
|
|
137
|
+
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/g,
|
|
138
|
+
new Date(newGenTime).toISOString()
|
|
139
|
+
);
|
|
140
|
+
await storage.set(rscKey, newRsc);
|
|
141
|
+
}
|
|
142
|
+
} catch (e) {}
|
|
143
|
+
|
|
144
|
+
console.log(`✅ [Edge Revalidate] Successfully revalidated page ${cleanPath} (generatedAt: ${newGenTime})`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
|
|
149
|
+
const reqPathWithSlash = cleanPath.endsWith("/") ? cleanPath : cleanPath + "/";
|
|
150
|
+
|
|
151
|
+
// Check if there is an existing page to copy to _old
|
|
152
|
+
try {
|
|
153
|
+
if (existsSync(path.join(dist2Folder, reqPathWithSlash, "index.html"))) {
|
|
154
|
+
copyFileSync(
|
|
155
|
+
path.join(dist2Folder, reqPathWithSlash, "index.html"),
|
|
156
|
+
path.join(dist2Folder, reqPathWithSlash, "index._old.html")
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (existsSync(path.join(dist2Folder, reqPathWithSlash, "page.rsc"))) {
|
|
160
|
+
copyFileSync(
|
|
161
|
+
path.join(dist2Folder, reqPathWithSlash, "page.rsc"),
|
|
162
|
+
path.join(dist2Folder, reqPathWithSlash, "page._old.rsc")
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (existsSync(path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"))) {
|
|
166
|
+
copyFileSync(
|
|
167
|
+
path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"),
|
|
168
|
+
path.join(dist2Folder, reqPathWithSlash, "rsc._old.rsc")
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
// Ignore copy errors
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log(`[Revalidate] Starting on-demand page revalidation for ${cleanPath}...`);
|
|
176
|
+
try {
|
|
177
|
+
const isDynamic = {};
|
|
178
|
+
await getBuildStaticPage()(cleanPath, isDynamic);
|
|
179
|
+
if (isDynamic.value) {
|
|
180
|
+
console.log(`[Revalidate] Bailout detected for ${cleanPath}. Switching to dynamic (skipping static write).`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const rscResult = await getGenerateStaticRSC()(cleanPath);
|
|
185
|
+
if (!rscResult.success) {
|
|
186
|
+
console.warn(`⚠️ [Revalidate] RSC generation failed for ${cleanPath}.`);
|
|
187
|
+
if (rscResult.tempPath && existsSync(rscResult.tempPath)) {
|
|
188
|
+
await fs.unlink(rscResult.tempPath).catch(() => { });
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await getSafeRename()(rscResult.tempPath, rscResult.finalPath);
|
|
194
|
+
|
|
195
|
+
const pageResult = await getGenerateStaticPage()(cleanPath);
|
|
196
|
+
if (pageResult.success) {
|
|
197
|
+
await getSafeRename()(pageResult.tempPath, pageResult.finalPath);
|
|
198
|
+
getUpdateStatus()(cleanPath, pageResult.status);
|
|
199
|
+
console.log(`✅ [Revalidate] Successfully revalidated page ${cleanPath} (Status: ${pageResult.status})`);
|
|
200
|
+
} else {
|
|
201
|
+
console.warn(`⚠️ [Revalidate] HTML generation failed for ${cleanPath}.`);
|
|
202
|
+
if (pageResult.tempPath && existsSync(pageResult.tempPath)) {
|
|
203
|
+
await fs.unlink(pageResult.tempPath).catch(() => { });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
console.warn(`⚠️ [Revalidate] Failed to revalidate page ${cleanPath}:`, e.message || e);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function revalidatePath(reqPath, options = {}) {
|
|
212
|
+
const isCascade = options === "layout" || (typeof options === "object" && options?.cascade === true);
|
|
213
|
+
const cleanPath = normalizeRevalidatePath(reqPath);
|
|
214
|
+
let layoutRevalidated = false;
|
|
215
|
+
|
|
216
|
+
if (isEdgeRuntime()) {
|
|
217
|
+
// 1. Revalidar la página
|
|
218
|
+
await revalidatePage(cleanPath);
|
|
219
|
+
|
|
220
|
+
// 2. Paridad Edge: Revalidar layout si existe para este segmento
|
|
221
|
+
const storage = getStorageAdapter();
|
|
222
|
+
const cleanPathKey = cleanPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
223
|
+
const layoutRscKey = cleanPathKey ? `${cleanPathKey}/layout.rsc` : "layout.rsc";
|
|
224
|
+
const layoutMetaKey = cleanPathKey ? `${cleanPathKey}/layout.metadata.json` : "layout.metadata.json";
|
|
225
|
+
|
|
226
|
+
const cachedLayout = await storage.get(layoutRscKey);
|
|
227
|
+
const cachedLayoutMeta = await storage.get(layoutMetaKey);
|
|
228
|
+
if (cachedLayout || cachedLayoutMeta) {
|
|
229
|
+
layoutRevalidated = await revalidateLayout(cleanPath);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 3. Cascada en Edge: solo si se solicitó cascade y se revalidó un layout
|
|
233
|
+
if (isCascade && layoutRevalidated && typeof storage.keys === "function") {
|
|
234
|
+
const allKeys = await storage.keys();
|
|
235
|
+
const prefix = cleanPathKey ? `${cleanPathKey}/` : "";
|
|
236
|
+
const childPaths = new Set();
|
|
237
|
+
for (const key of allKeys) {
|
|
238
|
+
if (key.startsWith(prefix) && (key.endsWith("metadata.json") || key.endsWith("index.html"))) {
|
|
239
|
+
if (!key.endsWith("layout.metadata.json") && !key.endsWith("slot.metadata.json")) {
|
|
240
|
+
const relKey = key.replace(/\/(?:metadata\.json|index\.html)$/, "").replace(/^(?:metadata\.json|index\.html)$/, "");
|
|
241
|
+
const childPath = "/" + relKey;
|
|
242
|
+
if (childPath !== cleanPath) {
|
|
243
|
+
childPaths.add(childPath);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
await Promise.all(Array.from(childPaths).map((p) => revalidatePage(p)));
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
|
|
254
|
+
const reqPathWithSlash = cleanPath.endsWith("/") ? cleanPath : cleanPath + "/";
|
|
255
|
+
const targetDir = path.join(dist2Folder, reqPathWithSlash);
|
|
256
|
+
|
|
257
|
+
// 1. Revalidar la página
|
|
258
|
+
await revalidatePage(cleanPath);
|
|
259
|
+
|
|
260
|
+
// 2. Revalidar el layout si existe en este segmento
|
|
261
|
+
const layoutFinalPath = path.join(targetDir, "layout.rsc");
|
|
262
|
+
const layoutOldPath = path.join(targetDir, "layout._old.rsc");
|
|
263
|
+
if (existsSync(layoutFinalPath) || existsSync(layoutOldPath)) {
|
|
264
|
+
layoutRevalidated = await revalidateLayout(cleanPath);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 3. Cascada en Node: solo si se solicitó cascade y se revalidó un layout
|
|
268
|
+
if (isCascade && layoutRevalidated) {
|
|
269
|
+
if (existsSync(targetDir)) {
|
|
270
|
+
const childFiles = await walkMetadataFiles(targetDir);
|
|
271
|
+
const childPaths = new Set();
|
|
272
|
+
for (const fileOfMeta of childFiles) {
|
|
273
|
+
if (path.basename(fileOfMeta) === "metadata.json") {
|
|
274
|
+
const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
|
|
275
|
+
const childPath = "/" + relative.replace(/\\/g, "/");
|
|
276
|
+
if (childPath !== cleanPath) {
|
|
277
|
+
childPaths.add(childPath);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
await Promise.all(Array.from(childPaths).map((p) => revalidatePage(p)));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function revalidateLayout(cleanPath) {
|
|
287
|
+
let targetPath = cleanPath;
|
|
288
|
+
if (!targetPath.startsWith("/")) {
|
|
289
|
+
targetPath = "/" + targetPath;
|
|
290
|
+
}
|
|
291
|
+
if (targetPath !== "/" && targetPath.endsWith("/")) {
|
|
292
|
+
targetPath = targetPath.slice(0, -1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (isEdgeRuntime()) {
|
|
296
|
+
const storage = getStorageAdapter();
|
|
297
|
+
const cleanPathKey = targetPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
298
|
+
const layoutRscKey = cleanPathKey ? `${cleanPathKey}/layout.rsc` : "layout.rsc";
|
|
299
|
+
const layoutMetaKey = cleanPathKey ? `${cleanPathKey}/layout.metadata.json` : "layout.metadata.json";
|
|
300
|
+
|
|
301
|
+
let cached = await storage.get(layoutRscKey);
|
|
302
|
+
let currentMeta = (cached && cached.metadata) || {};
|
|
303
|
+
try {
|
|
304
|
+
const metaItem = await storage.get(layoutMetaKey);
|
|
305
|
+
if (metaItem && metaItem.content) {
|
|
306
|
+
currentMeta = JSON.parse(metaItem.content);
|
|
307
|
+
}
|
|
308
|
+
} catch (e) {}
|
|
309
|
+
|
|
310
|
+
const prevGenTime = (currentMeta && currentMeta.generatedAt) || 0;
|
|
311
|
+
const now = Date.now();
|
|
312
|
+
const newGenTime = now <= prevGenTime ? prevGenTime + 100 : now;
|
|
313
|
+
currentMeta.generatedAt = newGenTime;
|
|
314
|
+
|
|
315
|
+
await storage.set(layoutMetaKey, JSON.stringify(currentMeta));
|
|
316
|
+
if (cached && cached.content) {
|
|
317
|
+
await storage.set(layoutRscKey, cached.content, currentMeta);
|
|
318
|
+
}
|
|
319
|
+
console.log(`✅ [Edge Revalidate] Successfully revalidated layout ${targetPath} (generatedAt: ${newGenTime})`);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
|
|
324
|
+
const reqPathWithSlash = targetPath.endsWith("/") ? targetPath : targetPath + "/";
|
|
325
|
+
const layoutFinalPath = path.join(dist2Folder, reqPathWithSlash, "layout.rsc");
|
|
326
|
+
const layoutOldPath = path.join(dist2Folder, reqPathWithSlash, "layout._old.rsc");
|
|
327
|
+
|
|
328
|
+
if (existsSync(layoutFinalPath)) {
|
|
329
|
+
try {
|
|
330
|
+
copyFileSync(layoutFinalPath, layoutOldPath);
|
|
331
|
+
} catch (e) {}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log(`[Revalidate] Starting on-demand layout revalidation for ${targetPath}...`);
|
|
335
|
+
try {
|
|
336
|
+
const layoutRscResult = await getGenerateStaticRSC()(targetPath, { segment: "layout" });
|
|
337
|
+
if (layoutRscResult && layoutRscResult.success) {
|
|
338
|
+
await getSafeRename()(layoutRscResult.tempPath, layoutRscResult.finalPath);
|
|
339
|
+
console.log(`✅ [Revalidate] Successfully revalidated layout ${targetPath}`);
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.warn(`⚠️ [Revalidate] Failed to revalidate layout ${targetPath}:`, err.message || err);
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function revalidateTag(tag, options = {}) {
|
|
349
|
+
const isCascade = options === "layout" || (typeof options === "object" && options?.cascade === true);
|
|
350
|
+
console.log(`[Revalidate] Starting on-demand revalidation for tag: "${tag}" (cascade: ${isCascade})...`);
|
|
351
|
+
|
|
352
|
+
if (isEdgeRuntime()) {
|
|
353
|
+
const storage = getStorageAdapter();
|
|
354
|
+
if (typeof storage.keys === "function") {
|
|
355
|
+
const allKeys = await storage.keys();
|
|
356
|
+
const targetPaths = new Set();
|
|
357
|
+
const targetLayoutPaths = new Set();
|
|
358
|
+
const targetSlotKeys = new Set();
|
|
359
|
+
for (const rawKey of allKeys) {
|
|
360
|
+
const key = String(rawKey || "").replace(/\\/g, "/");
|
|
361
|
+
if (key.includes("slot")) {
|
|
362
|
+
try {
|
|
363
|
+
const item = await storage.get(key);
|
|
364
|
+
let meta = item?.metadata;
|
|
365
|
+
if (!meta && item?.content) {
|
|
366
|
+
try { meta = JSON.parse(item.content); } catch (e) {}
|
|
367
|
+
}
|
|
368
|
+
if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
|
|
369
|
+
targetSlotKeys.add(key);
|
|
370
|
+
}
|
|
371
|
+
} catch (e) {}
|
|
372
|
+
} else if (key.endsWith("layout.metadata.json") || key.endsWith("layout.rsc")) {
|
|
373
|
+
try {
|
|
374
|
+
const item = await storage.get(key);
|
|
375
|
+
let meta = item?.metadata;
|
|
376
|
+
if (!meta && item?.content) {
|
|
377
|
+
try { meta = JSON.parse(item.content); } catch (e) {}
|
|
378
|
+
}
|
|
379
|
+
if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
|
|
380
|
+
const cleanKey = key
|
|
381
|
+
.replace(/\/(?:layout\.metadata\.json|layout\.rsc)$/, "")
|
|
382
|
+
.replace(/^(?:layout\.metadata\.json|layout\.rsc)$/, "");
|
|
383
|
+
const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
|
|
384
|
+
targetLayoutPaths.add(normPath);
|
|
385
|
+
}
|
|
386
|
+
} catch (e) {}
|
|
387
|
+
} else if (key.endsWith("metadata.json")) {
|
|
388
|
+
try {
|
|
389
|
+
const item = await storage.get(key);
|
|
390
|
+
let meta = item?.metadata;
|
|
391
|
+
if (!meta && item?.content) {
|
|
392
|
+
try { meta = JSON.parse(item.content); } catch (e) {}
|
|
393
|
+
}
|
|
394
|
+
if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
|
|
395
|
+
const cleanKey = key
|
|
396
|
+
.replace(/\/metadata\.json$/, "")
|
|
397
|
+
.replace(/^metadata\.json$/, "");
|
|
398
|
+
const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
|
|
399
|
+
targetPaths.add(normPath);
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {}
|
|
402
|
+
} else if (key.endsWith("index.html")) {
|
|
403
|
+
try {
|
|
404
|
+
const item = await storage.get(key);
|
|
405
|
+
const meta = item?.metadata;
|
|
406
|
+
if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
|
|
407
|
+
const cleanKey = key
|
|
408
|
+
.replace(/\/index\.html$/, "")
|
|
409
|
+
.replace(/^index\.html$/, "");
|
|
410
|
+
const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
|
|
411
|
+
targetPaths.add(normPath);
|
|
412
|
+
}
|
|
413
|
+
} catch (e) {}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
await Promise.all([
|
|
417
|
+
...Array.from(targetPaths).map((p) => revalidatePage(p)),
|
|
418
|
+
...Array.from(targetLayoutPaths).map((p) =>
|
|
419
|
+
isCascade ? revalidatePath(p, { cascade: true }) : revalidateLayout(p)
|
|
420
|
+
),
|
|
421
|
+
...Array.from(targetSlotKeys).map((k) => storage.delete(k)),
|
|
422
|
+
]);
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
|
|
428
|
+
if (!existsSync(dist2Folder)) return;
|
|
429
|
+
|
|
430
|
+
const metadataFiles = await walkMetadataFiles(dist2Folder);
|
|
431
|
+
const revalidatePromises = [];
|
|
432
|
+
|
|
433
|
+
for (const fileOfMeta of metadataFiles) {
|
|
434
|
+
try {
|
|
435
|
+
const content = await fs.readFile(fileOfMeta, "utf8");
|
|
436
|
+
const metadata = JSON.parse(content);
|
|
437
|
+
if (metadata && Array.isArray(metadata.tags) && metadata.tags.includes(tag)) {
|
|
438
|
+
if (path.basename(fileOfMeta) === "slot.metadata.json") {
|
|
439
|
+
const slotFolder = path.dirname(fileOfMeta);
|
|
440
|
+
console.log(`✅ [Revalidate] Invalidating cache slot directory: ${slotFolder}`);
|
|
441
|
+
revalidatePromises.push(
|
|
442
|
+
fs.rm(slotFolder, { recursive: true, force: true }).catch(() => {})
|
|
443
|
+
);
|
|
444
|
+
} else if (path.basename(fileOfMeta) === "layout.metadata.json") {
|
|
445
|
+
const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
|
|
446
|
+
const reqPath = "/" + relative.replace(/\\/g, "/");
|
|
447
|
+
if (isCascade) {
|
|
448
|
+
revalidatePromises.push(revalidatePath(reqPath, { cascade: true }));
|
|
449
|
+
} else {
|
|
450
|
+
revalidatePromises.push(revalidateLayout(reqPath));
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
|
|
454
|
+
const reqPath = "/" + relative.replace(/\\/g, "/");
|
|
455
|
+
revalidatePromises.push(revalidatePage(reqPath));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} catch (err) {
|
|
459
|
+
console.error(`[Revalidate] Error reading tags from ${fileOfMeta}:`, err);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
await Promise.all(revalidatePromises);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
module.exports = {
|
|
467
|
+
revalidatePath,
|
|
468
|
+
revalidatePage,
|
|
469
|
+
revalidateTag,
|
|
470
|
+
revalidateLayout,
|
|
471
|
+
};
|