dinou 4.0.16 → 5.0.1

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 (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +150 -57
  3. package/dinou/core/babel-esm-loader.js +87 -5
  4. package/dinou/core/build-static-pages.js +17 -97
  5. package/dinou/core/generate-static-page.js +21 -3
  6. package/dinou/core/generate-static-pages.js +30 -41
  7. package/dinou/core/generate-static-rsc.js +11 -5
  8. package/dinou/core/generate-static-rscs.js +11 -5
  9. package/dinou/core/generate-static.js +2 -3
  10. package/dinou/core/get-jsx.js +2 -1
  11. package/dinou/core/get-ssg-metadata.js +5 -13
  12. package/dinou/core/import-module.js +25 -6
  13. package/dinou/core/navigation.js +42 -27
  14. package/dinou/core/redirect.jsx +18 -11
  15. package/dinou/core/register-loader.mjs +1 -0
  16. package/dinou/core/render-app-to-html.js +198 -31
  17. package/dinou/core/render-html.js +144 -26
  18. package/dinou/core/render-jsx-to-client-jsx.js +6 -7
  19. package/dinou/core/request-context.js +11 -6
  20. package/dinou/core/revalidating.js +29 -25
  21. package/dinou/core/server.js +270 -85
  22. package/dinou/esbuild/dev.mjs +31 -2
  23. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +0 -40
  24. package/dinou/index.mjs +31 -10
  25. package/dinou/package.json +1 -1
  26. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +88 -49
  27. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +7 -9
  28. package/dinou/rollup/rollup.config.js +1 -2
  29. package/dinou/webpack/helpers/get-webpack-entries.js +0 -40
  30. package/package.json +2 -2
  31. package/dinou/core/babel-plugin-register-imports.js +0 -50
  32. package/dinou/core/get-ssg-jsx-or-jsx.js +0 -14
  33. package/dinou/core/get-ssg-jsx.js +0 -83
  34. package/dinou/core/jsx-json.js +0 -55
  35. package/dinou/core/register-hooks.js +0 -18
@@ -1,10 +1,45 @@
1
1
  const path = require("path");
2
- const { fork } = require("child_process"); // ⬅️ CHANGE 1: We use fork
2
+ const { fork } = require("child_process");
3
3
  const url = require("url");
4
- const { getJSXJSON, hasJSXJSON } = require("./jsx-json");
4
+ const fs = require("fs");
5
+ const getJSX = require("./get-jsx.js");
6
+ const { requestStorage } = require("./request-context.js");
7
+
8
+ const isDevelopment = process.env.NODE_ENV !== "production";
9
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
10
+
11
+ const { renderToPipeableStream } = isWebpack
12
+ ? require("react-server-dom-webpack/server")
13
+ : require("@roggc/react-server-dom-esm/server");
14
+
15
+ const manifestPath = path.resolve(
16
+ process.cwd(),
17
+ isWebpack
18
+ ? (isDevelopment ? "public/react-client-manifest.json" : "dist3/react-client-manifest.json")
19
+ : "react_client_manifest/react-client-manifest.json"
20
+ );
21
+
22
+ let cachedManifest = null;
23
+ function getManifest() {
24
+ if (!isDevelopment && cachedManifest) return cachedManifest;
25
+ try {
26
+ const content = fs.readFileSync(manifestPath, "utf8");
27
+ const parsed = JSON.parse(content);
28
+ if (parsed && Object.keys(parsed).length > 0) {
29
+ cachedManifest = parsed;
30
+ }
31
+ return cachedManifest || parsed;
32
+ } catch (e) {
33
+ if (cachedManifest) {
34
+ console.warn("Using cached client manifest due to read error:", e.message);
35
+ return cachedManifest;
36
+ }
37
+ console.error("Error reading client manifest:", e);
38
+ return {};
39
+ }
40
+ }
5
41
 
6
42
  function toFileUrl(p) {
7
- // Converts to file://, cross-platform
8
43
  return url.pathToFileURL(p).href;
9
44
  }
10
45
 
@@ -13,22 +48,123 @@ const registerLoaderPath = toFileUrl(
13
48
  );
14
49
  const renderHtmlPath = path.resolve(__dirname, "render-html.js");
15
50
 
16
- // ----------------------------------------------------
17
- // 💡 WHITELIST STRATEGY
18
- // ----------------------------------------------------
19
-
20
- // 1. Define ESSENTIAL Node.js flags that the child must have.
21
- // (We leave the list empty to avoid inheriting problematic flags)
22
51
  const ESSENTIAL_NODE_ARGS = [];
23
-
24
- // 2. Add the loader --import, which is the only confirmed necessity.
25
52
  const loaderArg = `--import=${registerLoaderPath}`;
26
53
  const childExecArgv = ESSENTIAL_NODE_ARGS.concat(loaderArg);
27
54
 
28
- // ----------------------------------------------------
29
-
30
55
  const { resolveRelativeUrl } = require("./url-resolver");
31
56
 
57
+ function createParentResponseWrapper(reqPath, res, child) {
58
+ let hasRedirected = false;
59
+
60
+ const safeRedirect = (targetUrl) => {
61
+ if (hasRedirected) return;
62
+ hasRedirected = true;
63
+
64
+ const resolvedUrl = resolveRelativeUrl(targetUrl, reqPath);
65
+ let finalUrl = "/";
66
+ if (
67
+ typeof resolvedUrl === "string" &&
68
+ resolvedUrl.startsWith("/") &&
69
+ !resolvedUrl.startsWith("//")
70
+ ) {
71
+ finalUrl = resolvedUrl;
72
+ } else {
73
+ console.warn(
74
+ `[Dinou Security] Blocked unsafe redirect to: ${targetUrl}`,
75
+ );
76
+ }
77
+
78
+ if (res.headersSent) {
79
+ console.log(
80
+ `[Dinou] Streaming active. Redirecting via JavaScript to: ${finalUrl}`,
81
+ );
82
+ const safeUrl = JSON.stringify(finalUrl);
83
+ res.write(`<script>window.location.href = ${safeUrl};</script>`);
84
+ res.end();
85
+ child.stdout.unpipe(res);
86
+ child.kill();
87
+ } else {
88
+ res.redirect(302, finalUrl);
89
+ child.stdout.unpipe(res);
90
+ child.kill();
91
+ }
92
+ };
93
+
94
+ return {
95
+ setHeader: (name, value) => {
96
+ if (res.headersSent) {
97
+ console.warn(
98
+ `[Dinou Warning] Cannot set header '${name}' because streaming started.`,
99
+ );
100
+ } else {
101
+ res.setHeader(name, value);
102
+ }
103
+ },
104
+ cookie: (name, value, options) => {
105
+ if (res.headersSent) {
106
+ if (options && options.httpOnly) {
107
+ console.error(
108
+ `[Dinou Error] Cannot set HttpOnly cookie '${name}' because streaming has already started.`,
109
+ );
110
+ return;
111
+ }
112
+ console.log(
113
+ `[Dinou] Streaming active. Setting cookie '${name}' via JS.`,
114
+ );
115
+ let cookieStr = `${name}=${encodeURIComponent(value)}`;
116
+ if (options) {
117
+ if (options.path) cookieStr += `; path=${options.path}`;
118
+ if (options.domain) cookieStr += `; domain=${options.domain}`;
119
+ if (options.maxAge) cookieStr += `; max-age=${options.maxAge}`;
120
+ if (options.expires)
121
+ cookieStr += `; expires=${new Date(options.expires).toUTCString()}`;
122
+ if (options.secure) cookieStr += `; secure`;
123
+ if (options.sameSite)
124
+ cookieStr += `; samesite=${options.sameSite}`;
125
+ }
126
+ const safeCookieStr = JSON.stringify(cookieStr);
127
+ res.write(`<script>document.cookie = ${safeCookieStr};</script>`);
128
+ } else {
129
+ res.cookie(name, value, options);
130
+ }
131
+ },
132
+ clearCookie: (name, options) => {
133
+ if (res.headersSent) {
134
+ console.log(
135
+ `[Dinou] Streaming active. Clearing cookie '${name}' via JS.`,
136
+ );
137
+ let cookieStr = `${name}=; Max-Age=0`;
138
+ const path = options?.path || "/";
139
+ cookieStr += `; path=${path}`;
140
+ if (options) {
141
+ if (options.domain) cookieStr += `; domain=${options.domain}`;
142
+ if (options.secure) cookieStr += `; secure`;
143
+ if (options.sameSite) cookieStr += `; samesite=${options.sameSite}`;
144
+ }
145
+ cookieStr += ";";
146
+ const safeCookieStr = JSON.stringify(cookieStr);
147
+ res.write(`<script>document.cookie = ${safeCookieStr};</script>`);
148
+ } else {
149
+ res.clearCookie(name, options);
150
+ }
151
+ },
152
+ redirect: (arg1, arg2) => {
153
+ const url = arg2 || arg1;
154
+ safeRedirect(url);
155
+ },
156
+ status: (code) => {
157
+ if (res.headersSent) {
158
+ console.warn(
159
+ `[Dinou Warning] HTTP status '${code}' ignored because streaming started.`,
160
+ );
161
+ } else {
162
+ res.status(code);
163
+ }
164
+ },
165
+ };
166
+ }
167
+
32
168
  function renderAppToHtml(
33
169
  reqPath,
34
170
  paramsString,
@@ -36,31 +172,62 @@ function renderAppToHtml(
36
172
  res,
37
173
  capturedStatus = null,
38
174
  isDynamic = false,
175
+ forceNotFound = false,
39
176
  ) {
40
- const jsxJson = getJSXJSON(reqPath);
41
- const hasJsxJson = hasJSXJSON(reqPath);
42
- // We replicate the array of positional arguments passed to the script
43
- // [renderHtmlPath, reqPath, paramsString, cookiesString]
44
- const scriptArgs = [
45
- reqPath,
46
- paramsString,
47
- contextForChild ? JSON.stringify(contextForChild) : JSON.stringify({}),
48
- isDynamic ? "true" : "false",
49
- hasJsxJson ? "true" : "false",
50
- JSON.stringify(hasJsxJson ? jsxJson : {}),
51
- ];
52
-
53
177
  const child = fork(
54
- renderHtmlPath, // ⬅️ CHANGE 2: The script (path) is the first argument of fork (no need for "node")
55
- scriptArgs, // Positional arguments for the script (process.argv)
178
+ renderHtmlPath,
179
+ [
180
+ reqPath,
181
+ paramsString,
182
+ contextForChild ? JSON.stringify(contextForChild) : JSON.stringify({}),
183
+ isDynamic ? "true" : "false",
184
+ ],
56
185
  {
57
- // ⬅️ CHANGE 3: Apply Whitelist to execArgv, resetting inherited options
58
186
  execArgv: childExecArgv,
59
- // ⬅️ CHANGE 4: stdio needs 'ipc' for fork to work and for the future communication channel
60
- stdio: ["ignore", "pipe", "pipe", "ipc"], // stdin, stdout, stderr, ipc
187
+ stdio: ["ignore", "pipe", "pipe", "ipc", "pipe"], // fd 4 is the RSC stream pipe
61
188
  },
62
189
  );
63
190
 
191
+ const query = JSON.parse(paramsString || "{}");
192
+ const rscPath = path.resolve(process.cwd(), "dist2", reqPath.replace(/^\//, ""), "rsc.rsc");
193
+ const hasStaticRsc = !isDynamic && fs.existsSync(rscPath);
194
+
195
+ if (hasStaticRsc) {
196
+ try {
197
+ const rscBuffer = fs.readFileSync(rscPath);
198
+ child.stdio[4].write(rscBuffer);
199
+ child.stdio[4].end();
200
+ } catch (err) {
201
+ console.error(`[Dinou] Failed to read static RSC from ${rscPath}:`, err.message);
202
+ if (child.stdio[4]) child.stdio[4].destroy();
203
+ }
204
+ } else {
205
+ // Dynamic SSR render: render RSC in parent and pipe to fd 4
206
+ const isNotFound = {};
207
+ const parentRes = createParentResponseWrapper(reqPath, res, child);
208
+ const context = {
209
+ req: contextForChild ? contextForChild.req : {},
210
+ res: parentRes,
211
+ };
212
+ requestStorage.run(context, () => {
213
+ getJSX(reqPath, query, isNotFound, isDevelopment, forceNotFound)
214
+ .then((jsx) => {
215
+ if (isNotFound.value) {
216
+ parentRes.status(404);
217
+ }
218
+ const manifest = getManifest();
219
+ const { pipe } = isWebpack
220
+ ? renderToPipeableStream(jsx, manifest)
221
+ : renderToPipeableStream(jsx, url.pathToFileURL(process.cwd()).href + "/");
222
+ pipe(child.stdio[4]);
223
+ })
224
+ .catch((err) => {
225
+ console.error("Error rendering JSX in parent renderAppToHtml:", err);
226
+ if (child.stdio[4]) child.stdio[4].destroy();
227
+ });
228
+ });
229
+ }
230
+
64
231
  // 💡 on('message') Implementation (IPC Channel)
65
232
  // ----------------------------------------------------
66
233
  child.on("message", (message) => {
@@ -1,6 +1,18 @@
1
+ const path = require("path");
2
+ global.__webpack_require__ = function (id) {
3
+ if (global.__webpack_require_map__ && global.__webpack_require_map__[id]) {
4
+ return require(global.__webpack_require_map__[id]);
5
+ }
6
+ if (typeof id === "string" && id.startsWith("./")) {
7
+ id = path.resolve(process.cwd(), id);
8
+ }
9
+ return require(id);
10
+ };
11
+ global.__webpack_chunk_load__ = function (chunkId) {
12
+ return Promise.resolve();
13
+ };
14
+
1
15
  require("./register-paths");
2
- require("./register-hooks.js");
3
- const babelPluginRegisterImports = require("./babel-plugin-register-imports.js");
4
16
  const babelRegister = require("@babel/register");
5
17
  babelRegister({
6
18
  ignore: [/node_modules[\\/](?!dinou)/],
@@ -8,7 +20,7 @@ babelRegister({
8
20
  ["@babel/preset-react", { runtime: "automatic" }],
9
21
  "@babel/preset-typescript",
10
22
  ],
11
- plugins: [babelPluginRegisterImports, "@babel/transform-modules-commonjs"],
23
+ plugins: ["@babel/transform-modules-commonjs"],
12
24
  extensions: [".js", ".jsx", ".ts", ".tsx"],
13
25
  });
14
26
  const addHook = require("./asset-require-hook.js");
@@ -26,9 +38,7 @@ addHook({
26
38
  const getAssetFromManifest = require("./get-asset-from-manifest.js");
27
39
  const { renderToPipeableStream } = require("react-dom/server");
28
40
  const getJSX = require("./get-jsx");
29
- const getSSGJSX = require("./get-ssg-jsx.js");
30
41
  const { getErrorJSX } = require("./get-error-jsx");
31
- const { renderJSXToClientJSX } = require("./render-jsx-to-client-jsx");
32
42
  const isDevelopment = process.env.NODE_ENV !== "production";
33
43
  const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
34
44
  const { requestStorage } = require("./request-context.js");
@@ -124,13 +134,121 @@ function writeErrorOutput(error, isProd) {
124
134
  );
125
135
  }
126
136
 
137
+ function getImportMapHtml() {
138
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
139
+ if (isWebpack) return "";
140
+
141
+ const fs = require("fs");
142
+ const path = require("path");
143
+ const { pathToFileURL } = require("url");
144
+
145
+ const manifestPath = path.resolve(
146
+ process.cwd(),
147
+ "react_client_manifest/react-client-manifest.json"
148
+ );
149
+
150
+ if (!fs.existsSync(manifestPath)) {
151
+ return "";
152
+ }
153
+
154
+ try {
155
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
156
+ const imports = {};
157
+ const moduleBasePath = pathToFileURL(process.cwd()).href + "/";
158
+
159
+ for (const [key, val] of Object.entries(manifest)) {
160
+ let specifier = key;
161
+ if (specifier.startsWith(moduleBasePath)) {
162
+ specifier = specifier.slice(moduleBasePath.length);
163
+ }
164
+ const hashIdx = specifier.indexOf("#");
165
+ if (hashIdx !== -1) {
166
+ specifier = specifier.slice(0, hashIdx);
167
+ }
168
+ imports[specifier] = val.id;
169
+ }
170
+
171
+ return `<script type="importmap">{"imports":${JSON.stringify(imports)}}</script><script>(function(){const map=document.querySelector('script[type="importmap"]');if(map)map.remove();if(document.currentScript)document.currentScript.remove();})();</script>`;
172
+ } catch (err) {
173
+ console.error("Error generating importmap:", err);
174
+ return "";
175
+ }
176
+ }
177
+
178
+ let cachedSsrManifest = null;
179
+ let cachedRequireMap = null;
180
+ function getSsrManifest() {
181
+ if (!isDevelopment && cachedSsrManifest) return cachedSsrManifest;
182
+ try {
183
+ const fs = require("fs");
184
+ const path = require("path");
185
+ const ssrManifestPath = path.resolve(
186
+ process.cwd(),
187
+ isDevelopment ? "public/react-ssr-manifest.json" : "dist3/react-ssr-manifest.json"
188
+ );
189
+ const clientManifestPath = path.resolve(
190
+ process.cwd(),
191
+ isDevelopment ? "public/react-client-manifest.json" : "dist3/react-client-manifest.json"
192
+ );
193
+
194
+ const ssrContent = fs.readFileSync(ssrManifestPath, "utf8");
195
+ const ssrManifest = JSON.parse(ssrContent);
196
+
197
+ if (fs.existsSync(clientManifestPath)) {
198
+ const clientContent = fs.readFileSync(clientManifestPath, "utf8");
199
+ const clientManifest = JSON.parse(clientContent);
200
+
201
+ const { fileURLToPath } = require("url");
202
+ const requireMap = {};
203
+ for (const [fileUrl, entry] of Object.entries(clientManifest)) {
204
+ if (entry && entry.id !== undefined) {
205
+ try {
206
+ requireMap[entry.id] = fileURLToPath(fileUrl);
207
+ } catch {}
208
+ }
209
+ }
210
+ global.__webpack_require_map__ = requireMap;
211
+ cachedRequireMap = requireMap;
212
+
213
+ if (ssrManifest.moduleMap) {
214
+ for (const [modId, exports] of Object.entries(ssrManifest.moduleMap)) {
215
+ for (const [expName, expData] of Object.entries(exports)) {
216
+ if (expData && expData.specifier) {
217
+ const clientEntry = clientManifest[expData.specifier];
218
+ if (clientEntry) {
219
+ expData.id = clientEntry.id;
220
+ expData.chunks = clientEntry.chunks;
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ cachedSsrManifest = ssrManifest;
229
+ return cachedSsrManifest;
230
+ } catch (e) {
231
+ if (cachedSsrManifest) {
232
+ console.warn("Using cached SSR manifest due to read error:", e.message);
233
+ if (cachedRequireMap) {
234
+ global.__webpack_require_map__ = cachedRequireMap;
235
+ }
236
+ return cachedSsrManifest;
237
+ }
238
+ console.error("Error reading SSR manifest:", e);
239
+ return {};
240
+ }
241
+ }
242
+
243
+ const { createFromNodeStream } = isWebpack
244
+ ? require("react-server-dom-webpack/client")
245
+ : require("@roggc/react-server-dom-esm/client");
246
+
127
247
  async function renderToStream(
128
248
  reqPath,
129
249
  query,
130
250
  serializedBox,
131
251
  isDynamic,
132
- hasJsxJson,
133
- jsxJson,
134
252
  ) {
135
253
  const context = {
136
254
  req: serializedBox.req,
@@ -138,21 +256,15 @@ async function renderToStream(
138
256
  };
139
257
  await requestStorage.run(context, async () => {
140
258
  try {
141
- const isNotFound = {};
142
- const jsx =
143
- isDevelopment ||
144
- isDynamic ||
145
- !hasJsxJson
146
- ? renderJSXToClientJSX(
147
- await getJSX(reqPath, query, isNotFound, isDevelopment),
148
- )
149
- : ((await getSSGJSX(jsxJson)) ??
150
- renderJSXToClientJSX(
151
- await getJSX(reqPath, query, isNotFound, isDevelopment),
152
- ));
153
- if (isNotFound.value) {
154
- context.res.status(404);
155
- }
259
+ const { createReadStream } = require("fs");
260
+ const rscStream = createReadStream(null, { fd: 4 });
261
+ const { pathToFileURL } = require("url");
262
+ const baseUrl = pathToFileURL(process.cwd()).href + "/";
263
+
264
+ const jsx = isWebpack
265
+ ? await createFromNodeStream(rscStream, getSsrManifest())
266
+ : await createFromNodeStream(rscStream, baseUrl, baseUrl);
267
+
156
268
  const stream = renderToPipeableStream(jsx, {
157
269
  onError(error) {
158
270
  process.nextTick(async () => {
@@ -181,6 +293,11 @@ async function renderToStream(
181
293
 
182
294
  const errorStream = renderToPipeableStream(errorJSX, {
183
295
  onShellReady() {
296
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
297
+ if (!isWebpack) {
298
+ const importMapHtml = getImportMapHtml();
299
+ process.stdout.write(importMapHtml);
300
+ }
184
301
  errorStream.pipe(process.stdout);
185
302
  },
186
303
  onError(err) {
@@ -216,6 +333,11 @@ async function renderToStream(
216
333
  });
217
334
  },
218
335
  onShellReady() {
336
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
337
+ if (!isWebpack) {
338
+ const importMapHtml = getImportMapHtml();
339
+ process.stdout.write(importMapHtml);
340
+ }
219
341
  stream.pipe(process.stdout);
220
342
  },
221
343
  bootstrapModules: isDevelopment
@@ -250,8 +372,6 @@ const reqPath = process.argv[2] || "/";
250
372
  const query = JSON.parse(process.argv[3] || "{}");
251
373
  const serializedBox = JSON.parse(process.argv[4] || "{}");
252
374
  const isDynamic = process.argv[5] === "true";
253
- const hasJsxJson = process.argv[6] === "true";
254
- const jsxJson = JSON.parse(process.argv[7] || "{}");
255
375
 
256
376
  process.on("uncaughtException", (error) => {
257
377
  process.stdout.write(formatErrorHtml(error));
@@ -281,8 +401,6 @@ renderToStream(
281
401
  query,
282
402
  serializedBox,
283
403
  isDynamic,
284
- hasJsxJson,
285
- jsxJson,
286
404
  ).catch((err) => {
287
405
  console.error("❌ Fatal error starting render stream:", err);
288
406
  process.exit(1);
@@ -1,14 +1,13 @@
1
1
  // Function to check if a component is a client component
2
2
  function isClientComponent(type) {
3
- if (typeof type !== "function") {
3
+ if (!type) {
4
4
  return false;
5
5
  }
6
- const isAsync =
7
- type instanceof Object.getPrototypeOf(async function () {}).constructor;
8
- if (isAsync) {
9
- return false;
10
- }
11
- return true;
6
+ const CLIENT_REFERENCE = Symbol.for("react.client.reference");
7
+ return (
8
+ (typeof type === "function" && type.$$typeof === CLIENT_REFERENCE) ||
9
+ (typeof type === "object" && type.$$typeof === CLIENT_REFERENCE)
10
+ );
12
11
  }
13
12
 
14
13
  function renderJSXToClientJSX(jsx, key = null) {
@@ -4,15 +4,20 @@ 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 = eval("require");
7
+ const nodeRequire =
8
+ typeof module !== "undefined" && typeof module.require === "function"
9
+ ? module.require.bind(module)
10
+ : null;
8
11
 
9
- const { AsyncLocalStorage } = nodeRequire("node:async_hooks");
12
+ if (nodeRequire) {
13
+ const { AsyncLocalStorage } = nodeRequire("node:async_hooks");
10
14
 
11
- if (!global[DINOU_CONTEXT_KEY]) {
12
- global[DINOU_CONTEXT_KEY] = new AsyncLocalStorage();
13
- }
15
+ if (!global[DINOU_CONTEXT_KEY]) {
16
+ global[DINOU_CONTEXT_KEY] = new AsyncLocalStorage();
17
+ }
14
18
 
15
- requestStorage = global[DINOU_CONTEXT_KEY];
19
+ requestStorage = global[DINOU_CONTEXT_KEY];
20
+ }
16
21
  } else {
17
22
  requestStorage = {
18
23
  run: (store, callback) => callback(),
@@ -6,41 +6,44 @@ const { buildStaticPage } = require("./build-static-pages");
6
6
  const generateStaticRSC = require("./generate-static-rsc");
7
7
  const { safeRename } = require("./safe-rename");
8
8
  const { updateStatus } = require("./status-manifest");
9
- const { getJSXJSON } = require("./jsx-json");
10
9
 
11
10
  const regenerating = new Set();
12
11
 
13
12
  function revalidating(reqPath, isDynamicFromServer) {
14
13
  const dist2Folder = path.resolve(process.cwd(), "dist2");
15
- const jsxJSON = getJSXJSON(reqPath);
16
- if (!jsxJSON) return;
14
+ const metadataPath = path.join(dist2Folder, reqPath, "metadata.json");
17
15
 
18
- const { revalidate, generatedAt } = jsxJSON;
16
+ if (regenerating.has(reqPath)) return;
19
17
 
20
- const isExpired =
21
- typeof revalidate === "number" &&
22
- revalidate > 0 &&
23
- Date.now() > generatedAt + revalidate;
18
+ fs.readFile(metadataPath, "utf8")
19
+ .then((content) => {
20
+ const metadata = JSON.parse(content);
21
+ const { revalidate, generatedAt } = metadata;
24
22
 
25
- if (isExpired && !regenerating.has(reqPath)) {
26
- try {
27
- if (existsSync(path.join(dist2Folder, reqPath, "index.html")))
28
- copyFileSync(
29
- path.join(dist2Folder, reqPath, "index.html"),
30
- path.join(dist2Folder, reqPath, "index._old.html")
31
- );
32
- if (existsSync(path.join(dist2Folder, reqPath, "rsc.rsc")))
33
- copyFileSync(
34
- path.join(dist2Folder, reqPath, "rsc.rsc"),
35
- path.join(dist2Folder, reqPath, "rsc._old.rsc")
36
- );
37
- } catch (e) {}
23
+ const isExpired =
24
+ typeof revalidate === "number" &&
25
+ revalidate > 0 &&
26
+ Date.now() > generatedAt + revalidate;
38
27
 
39
- regenerating.add(reqPath);
28
+ if (isExpired && !regenerating.has(reqPath)) {
29
+ try {
30
+ if (existsSync(path.join(dist2Folder, reqPath, "index.html")))
31
+ copyFileSync(
32
+ path.join(dist2Folder, reqPath, "index.html"),
33
+ path.join(dist2Folder, reqPath, "index._old.html")
34
+ );
35
+ if (existsSync(path.join(dist2Folder, reqPath, "rsc.rsc")))
36
+ copyFileSync(
37
+ path.join(dist2Folder, reqPath, "rsc.rsc"),
38
+ path.join(dist2Folder, reqPath, "rsc._old.rsc")
39
+ );
40
+ } catch (e) {}
40
41
 
41
- (async () => {
42
- try {
43
- console.log(`[ISR] Starting regeneration for ${reqPath}...`);
42
+ regenerating.add(reqPath);
43
+
44
+ (async () => {
45
+ try {
46
+ console.log(`[ISR] Starting regeneration for ${reqPath}...`);
44
47
  const isDynamic = {};
45
48
  await buildStaticPage(reqPath, isDynamic);
46
49
  if (isDynamic.value) {
@@ -80,6 +83,7 @@ function revalidating(reqPath, isDynamicFromServer) {
80
83
  }
81
84
  })();
82
85
  }
86
+ }).catch((err) => {});
83
87
  }
84
88
 
85
89
  module.exports = { revalidating, regenerating };