dinou 6.1.1 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +9352 -1081
  3. package/cli.js +314 -91
  4. package/dinou/adapters/bun.js +62 -0
  5. package/dinou/adapters/cloudflare.js +65 -0
  6. package/dinou/adapters/deno.js +48 -0
  7. package/dinou/adapters/netlify.js +85 -0
  8. package/dinou/adapters/node.mjs +31 -0
  9. package/dinou/bun/build.mjs +1077 -0
  10. package/dinou/cloudflare/build.mjs +1121 -0
  11. package/dinou/cloudflare/shims/child_process.js +14 -0
  12. package/dinou/cloudflare/shims/empty.js +2 -0
  13. package/dinou/cloudflare/shims/fs.js +99 -0
  14. package/dinou/cloudflare/shims/os.js +29 -0
  15. package/dinou/config.js +1 -0
  16. package/dinou/config.mjs +2 -0
  17. package/dinou/constants.js +29 -4
  18. package/dinou/core/babel-esm-loader.js +66 -37
  19. package/dinou/core/bailout-proxy.js +61 -0
  20. package/dinou/core/build-static-pages.js +322 -165
  21. package/dinou/core/cache-revalidate.js +471 -144
  22. package/dinou/core/cache-slot.js +387 -0
  23. package/dinou/core/client-redirect.jsx +12 -7
  24. package/dinou/core/client-webpack.jsx +457 -82
  25. package/dinou/core/client.jsx +452 -82
  26. package/dinou/core/config.js +11 -0
  27. package/dinou/core/dinou-paths.js +52 -0
  28. package/dinou/core/edge-ssr.js +122 -0
  29. package/dinou/core/export-static.mjs +64 -0
  30. package/dinou/core/generate-route-types.js +252 -0
  31. package/dinou/core/generate-static-page.js +55 -94
  32. package/dinou/core/generate-static-rsc.js +47 -15
  33. package/dinou/core/generate-static.js +212 -10
  34. package/dinou/core/generating-isg.js +5 -0
  35. package/dinou/core/get-asset-from-manifest.js +9 -3
  36. package/dinou/core/get-error-jsx.js +221 -41
  37. package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
  38. package/dinou/core/get-jsx.js +175 -96
  39. package/dinou/core/handler.js +2303 -0
  40. package/dinou/core/http-adapter.js +115 -0
  41. package/dinou/core/import-module.js +83 -53
  42. package/dinou/core/layout-functions.js +183 -0
  43. package/dinou/core/link.jsx +18 -6
  44. package/dinou/core/load-dinou-config.js +60 -0
  45. package/dinou/core/manifest-provider.js +261 -0
  46. package/dinou/core/navigation-utils.js +4 -0
  47. package/dinou/core/navigation.js +50 -39
  48. package/dinou/core/parse-exports.js +252 -2
  49. package/dinou/core/ppr-context.js +105 -0
  50. package/dinou/core/ppr-runtime.js +263 -0
  51. package/dinou/core/redirect.jsx +20 -19
  52. package/dinou/core/render-jsx-to-client-jsx.js +91 -87
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +34 -0
  55. package/dinou/core/revalidating.js +28 -9
  56. package/dinou/core/route-generator.js +107 -0
  57. package/dinou/core/rsc-renderer.js +90 -0
  58. package/dinou/core/run-ssg.js +19 -0
  59. package/dinou/core/scan-dependency-components.js +218 -0
  60. package/dinou/core/server-function-proxy-webpack.js +3 -0
  61. package/dinou/core/server-function-proxy.js +3 -0
  62. package/dinou/core/slot.js +302 -0
  63. package/dinou/core/status-manifest.js +41 -1
  64. package/dinou/core/storage-adapter.js +522 -0
  65. package/dinou/core/url-resolver.js +16 -16
  66. package/dinou/core/vfs.js +139 -68
  67. package/dinou/deno/build.mjs +1112 -0
  68. package/dinou/deno/seed-kv.js +115 -0
  69. package/dinou/esbuild/build.mjs +51 -1
  70. package/dinou/esbuild/dev.mjs +290 -224
  71. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
  72. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
  73. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
  74. package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
  75. package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
  76. package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
  77. package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
  78. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
  79. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
  80. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
  81. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
  82. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
  83. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
  84. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
  85. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
  86. package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
  87. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
  88. package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
  89. package/dinou/index.d.ts +315 -11
  90. package/dinou/index.js +12 -6
  91. package/dinou/index.mjs +9 -0
  92. package/dinou/node/build.mjs +195 -0
  93. package/dinou/node/bundle-dual-engine.mjs +1012 -0
  94. package/dinou/node/dev.mjs +2589 -0
  95. package/dinou/node/port-selector.mjs +164 -0
  96. package/dinou/node/terminal-status.mjs +345 -0
  97. package/dinou/package.json +16 -14
  98. package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
  99. package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
  100. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
  101. package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
  102. package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
  103. package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
  104. package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
  105. package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
  106. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
  107. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
  108. package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
  109. package/dinou/rollup/rollup.config.js +155 -43
  110. package/dinou/server.d.ts +114 -12
  111. package/dinou/server.js +35 -10
  112. package/dinou/server.mjs +30 -9
  113. package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
  114. package/dinou/webpack/loaders/server-functions-loader.js +22 -1
  115. package/dinou/webpack/loaders/swc-loader.js +76 -0
  116. package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
  117. package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
  118. package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
  119. package/dinou/webpack/webpack.config.js +227 -29
  120. package/eject.js +113 -34
  121. package/package.json +18 -16
  122. package/dinou/core/asset-require-hook.js +0 -58
  123. package/dinou/core/client-error-webpack.jsx +0 -432
  124. package/dinou/core/client-error.jsx +0 -432
  125. package/dinou/core/concurrency-manager.js +0 -48
  126. package/dinou/core/context-proxy.js +0 -68
  127. package/dinou/core/generate-static-pages.js +0 -130
  128. package/dinou/core/generate-static-rscs.js +0 -151
  129. package/dinou/core/get-ssg-metadata.js +0 -31
  130. package/dinou/core/register-loader.mjs +0 -11
  131. package/dinou/core/register-paths.js +0 -27
  132. package/dinou/core/render-app-to-html.js +0 -430
  133. package/dinou/core/render-html.js +0 -407
  134. package/dinou/core/server.js +0 -1542
  135. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
@@ -7,9 +7,60 @@ const {
7
7
  } = require("./get-file-path-and-dynamic-params");
8
8
  const importModule = require("./import-module");
9
9
  const { requestStorage } = require("./request-context.js");
10
+ const { getLayoutProps, resolveLayoutFunctionsConfig } = require("./layout-functions");
11
+ const { runWithPprContext, getRegisteredHoles } = require("./ppr-context.js");
12
+ const { resolveModulePpr } = require("./resolve-module-ppr");
13
+ const { getDinouConfig } = require("./load-dinou-config");
14
+
15
+ async function resolveBuildPprForRoute(segments, pagePath, pageFunctionsPath, srcFolder) {
16
+ const globalCfg = await getDinouConfig();
17
+ let inheritedPpr = Boolean(globalCfg?.ppr);
18
+ try {
19
+ const parentLayouts = getFilePathAndDynamicParams(
20
+ segments || [],
21
+ {},
22
+ srcFolder,
23
+ "layout",
24
+ true,
25
+ false,
26
+ undefined,
27
+ 0,
28
+ {},
29
+ true
30
+ );
31
+ if (parentLayouts && Array.isArray(parentLayouts)) {
32
+ for (const [lPath, lParams] of parentLayouts) {
33
+ const lConfig = await resolveLayoutFunctionsConfig(lPath, lParams);
34
+ if (lConfig && lConfig.ppr !== null && lConfig.ppr !== undefined) {
35
+ inheritedPpr = Boolean(lConfig.ppr);
36
+ }
37
+ }
38
+ }
39
+ } catch (e) {}
40
+
41
+ let explicitPagePpr = null;
42
+ if (pageFunctionsPath) {
43
+ try {
44
+ const module = await importModule(pageFunctionsPath);
45
+ explicitPagePpr = await resolveModulePpr(module);
46
+ } catch (e) {}
47
+ }
48
+ if (explicitPagePpr === null && typeof globalThis !== "undefined" && globalThis.__DINOU_ROUTE_METADATA__) {
49
+ const normKey = pageFunctionsPath ? pageFunctionsPath.replace(/\\/g, "/") : "";
50
+ for (const [k, meta] of Object.entries(globalThis.__DINOU_ROUTE_METADATA__)) {
51
+ if (meta.ppr !== undefined && (normKey === k || normKey.endsWith("/" + k) || normKey.endsWith(k))) {
52
+ explicitPagePpr = Boolean(meta.ppr);
53
+ break;
54
+ }
55
+ }
56
+ }
57
+
58
+ return explicitPagePpr !== null ? explicitPagePpr : inheritedPpr;
59
+ }
10
60
 
11
61
  const staticRoutes = new Set();
12
62
  const staticMetadata = new Map();
63
+ const staticLayouts = new Map();
13
64
 
14
65
  function safeDecode(val) {
15
66
  try {
@@ -19,65 +70,21 @@ function safeDecode(val) {
19
70
  }
20
71
  }
21
72
 
22
- /**
23
- * Creates a spy that detects access to properties and marks the page as dynamic.
24
- * @param {Object} target - The real object (cookies, headers, query...)
25
- * @param {string} label - Name for the log (e.g., "Headers", "Cookies")
26
- * @param {Function} onBailout - Callback to execute when access is detected
27
- */
28
- function createBailoutProxy(target, label, onBailout) {
29
- // If there is no target, use an empty object to avoid crashes,
30
- // but ideally pass whatever you have.
31
- const safeTarget = target || {};
32
-
33
- return new Proxy(safeTarget, {
34
- get(t, prop, receiver) {
35
- // Ignore internal Node/Console symbols
36
- if (
37
- typeof prop === "symbol" ||
38
- prop === "inspect" ||
39
- prop === "valueOf" ||
40
- prop === "toString" // Sometimes useful to ignore
41
- ) {
42
- return Reflect.get(t, prop, receiver);
43
- }
44
-
45
- // 🚨 ALARM: Access detected
46
- console.log(
47
- `[StaticBailout] Access to ${label} detected: "${String(prop)}".`,
48
- );
49
-
50
- // Execute logic to mark as dynamic
51
- onBailout();
52
-
53
- // IMPORTANT: Return the real value of the original object
54
- return Reflect.get(t, prop, receiver);
55
- },
56
-
57
- ownKeys(t) {
58
- console.log(`[StaticBailout] Iteration of ${label} detected.`);
59
- onBailout();
60
- return Reflect.ownKeys(t);
61
- },
62
-
63
- // Optional: Detect if they try to ask "prop in headers"
64
- has(t, prop) {
65
- console.log(
66
- `[StaticBailout] Existence check (IN) in ${label}: "${String(prop)}".`,
67
- );
68
- onBailout();
69
- return Reflect.has(t, prop);
70
- },
71
- });
73
+ async function isDynamicRoute(revalidate) {
74
+ if (revalidate === undefined || revalidate === false) return false;
75
+ const val = typeof revalidate === "function" ? await revalidate() : revalidate;
76
+ return typeof val === "number" && val < 1;
72
77
  }
73
78
 
79
+ const { createBailoutProxy } = require("./bailout-proxy.js");
80
+
74
81
  /**
75
82
  * Compiles all static pages in the application.
76
83
  * Crawls the "src/" folder to collect all routes, resolves dynamic path segments,
77
84
  * mock-renders them inside a server context, and serializes successful static renders
78
85
  * into RSC JSON payloads.
79
86
  */
80
- async function buildStaticPages() {
87
+ async function buildStaticPages(onProgress = null) {
81
88
  const srcFolder = path.resolve(process.cwd(), "src");
82
89
 
83
90
  /**
@@ -95,8 +102,32 @@ async function buildStaticPages() {
95
102
  const entries = readdirSync(currentPath, { withFileTypes: true });
96
103
  const pages = [];
97
104
 
105
+ // 🛡️ Descubrir layout en esta carpeta si existe
106
+ const layoutExtensions = [".tsx", ".jsx", ".ts", ".js"];
107
+ for (const ext of layoutExtensions) {
108
+ const candidateLayout = path.join(currentPath, `layout${ext}`);
109
+ if (existsSync(candidateLayout)) {
110
+ const routePath = segments.length ? "/" + segments.join("/") : "/";
111
+ const { resolveLayoutFunctionsConfig } = require("./layout-functions");
112
+ const layoutConfig = await resolveLayoutFunctionsConfig(candidateLayout, params);
113
+ if (!layoutConfig.isDynamic) {
114
+ staticLayouts.set(routePath, {
115
+ folderPath: currentPath,
116
+ layoutPath: candidateLayout,
117
+ routePath,
118
+ segments,
119
+ params,
120
+ });
121
+ }
122
+ break;
123
+ }
124
+ }
125
+
98
126
  for (const entry of entries) {
99
127
  if (entry.isDirectory()) {
128
+ if (entry.name.startsWith("_")) {
129
+ continue;
130
+ }
100
131
  if (entry.name.startsWith("(") && entry.name.endsWith(")")) {
101
132
  pages.push(
102
133
  ...(await collectPages(
@@ -134,22 +165,26 @@ async function buildStaticPages() {
134
165
  segments.length,
135
166
  );
136
167
 
137
- let dynamic, getStaticPaths;
168
+ let revalidate, getStaticPaths;
138
169
  if (pageFunctionsPath) {
139
170
  const module = await importModule(pageFunctionsPath);
140
- getStaticPaths = module.getStaticPaths;
141
- dynamic = module.dynamic;
171
+ getStaticPaths = module.getStaticPaths || module.default?.getStaticPaths;
172
+ revalidate = module.revalidate || module.default?.revalidate;
142
173
  }
143
174
  const isLocalPage =
144
175
  pagePath && path.dirname(pagePath) === dynamicPath;
145
- if (isLocalPage && !dynamic?.()) {
146
- console.log(
147
- `Found optional catch-all route: ${segments.join("/") ?? ""
148
- }/[[...${paramName}]]`,
149
- );
176
+ if (isLocalPage && !(await isDynamicRoute(revalidate))) {
177
+ if (process.env.DINOU_DEBUG) {
178
+ console.log(
179
+ `Found optional catch-all route: ${segments.join("/") ?? ""
180
+ }/[[...${paramName}]]`,
181
+ );
182
+ }
150
183
  try {
151
184
  if (getStaticPaths) {
152
- const paths = await getStaticPaths();
185
+ const paths = await (typeof getStaticPaths === "function"
186
+ ? getStaticPaths()
187
+ : getStaticPaths);
153
188
  for (const pathItem of paths) {
154
189
  // 1. Preparation of Structure and Extraction
155
190
  const currentStructure = [...dynamicStructure, paramName];
@@ -275,22 +310,26 @@ async function buildStaticPages() {
275
310
  segments.length,
276
311
  );
277
312
 
278
- let dynamic, getStaticPaths;
313
+ let revalidate, getStaticPaths;
279
314
  if (pageFunctionsPath) {
280
315
  const module = await importModule(pageFunctionsPath);
281
- getStaticPaths = module.getStaticPaths;
282
- dynamic = module.dynamic;
316
+ getStaticPaths = module.getStaticPaths || module.default?.getStaticPaths;
317
+ revalidate = module.revalidate || module.default?.revalidate;
283
318
  }
284
319
  const isLocalPage =
285
320
  pagePath && path.dirname(pagePath) === dynamicPath;
286
- if (isLocalPage && !dynamic?.()) {
287
- console.log(
288
- `Found catch-all route: ${segments.join("/") ?? ""
289
- }/[...${paramName}]`,
290
- );
321
+ if (isLocalPage && !(await isDynamicRoute(revalidate))) {
322
+ if (process.env.DINOU_DEBUG) {
323
+ console.log(
324
+ `Found catch-all route: ${segments.join("/") ?? ""
325
+ }/[...${paramName}]`,
326
+ );
327
+ }
291
328
  try {
292
329
  if (getStaticPaths) {
293
- const paths = await getStaticPaths();
330
+ const paths = await (typeof getStaticPaths === "function"
331
+ ? getStaticPaths()
332
+ : getStaticPaths);
294
333
  for (const pathItem of paths) {
295
334
  const currentStructure = [...dynamicStructure, paramName];
296
335
  const isObject =
@@ -379,22 +418,26 @@ async function buildStaticPages() {
379
418
  segments.length,
380
419
  );
381
420
 
382
- let dynamic, getStaticPaths;
421
+ let revalidate, getStaticPaths;
383
422
  if (pageFunctionsPath) {
384
423
  const module = await importModule(pageFunctionsPath);
385
- getStaticPaths = module.getStaticPaths;
386
- dynamic = module.dynamic;
424
+ getStaticPaths = module.getStaticPaths || module.default?.getStaticPaths;
425
+ revalidate = module.revalidate || module.default?.revalidate;
387
426
  }
388
427
  const isLocalPage =
389
428
  pagePath && path.dirname(pagePath) === dynamicPath;
390
- if (isLocalPage && !dynamic?.()) {
391
- console.log(
392
- `Found optional dynamic route: ${segments.join("/") ?? ""
393
- }/[[${paramName}]]`,
394
- );
429
+ if (isLocalPage && !(await isDynamicRoute(revalidate))) {
430
+ if (process.env.DINOU_DEBUG) {
431
+ console.log(
432
+ `Found optional dynamic route: ${segments.join("/") ?? ""
433
+ }/[[${paramName}]]`,
434
+ );
435
+ }
395
436
  try {
396
437
  if (getStaticPaths) {
397
- const paths = await getStaticPaths();
438
+ const paths = await (typeof getStaticPaths === "function"
439
+ ? getStaticPaths()
440
+ : getStaticPaths);
398
441
  for (const pathItem of paths) {
399
442
  // 1. Preparation of Structure and Extraction
400
443
  const currentStructure = [...dynamicStructure, paramName];
@@ -502,20 +545,51 @@ async function buildStaticPages() {
502
545
  );
503
546
  let dynamic;
504
547
  let getStaticPaths;
548
+ let revalidate;
505
549
  if (pageFunctionsPath) {
506
550
  const module = await importModule(pageFunctionsPath);
507
- getStaticPaths = module.getStaticPaths;
508
- dynamic = module.dynamic;
551
+ const defMod = module.default?.default ? module.default.default : module.default;
552
+ getStaticPaths = module.getStaticPaths || defMod?.getStaticPaths;
553
+ revalidate = module.revalidate || defMod?.revalidate;
554
+ }
555
+ const { getLayoutFunctionsPath } = require("./layout-functions");
556
+ const lfPath = getLayoutFunctionsPath(dynamicPath);
557
+ if (lfPath) {
558
+ const lmod = await importModule(lfPath);
559
+ const lmodGetStaticPaths = lmod.getStaticPaths || lmod.default?.getStaticPaths;
560
+ const lmodRevalidate = lmod.revalidate || lmod.default?.revalidate;
561
+ if (!getStaticPaths && lmodGetStaticPaths) {
562
+ getStaticPaths = lmodGetStaticPaths;
563
+ }
564
+ if (revalidate === undefined && lmodRevalidate !== undefined) {
565
+ revalidate = lmodRevalidate;
566
+ }
509
567
  }
510
568
  const isLocalPage =
511
569
  pagePath && path.dirname(pagePath) === dynamicPath;
512
- if (isLocalPage && !dynamic?.()) {
513
- console.log(
514
- `Found dynamic route: ${segments.join("/") ?? ""}/[${paramName}]`,
515
- );
570
+ const [layoutPath] = getFilePathAndDynamicParams(
571
+ segments,
572
+ {},
573
+ dynamicPath,
574
+ "layout",
575
+ true,
576
+ true,
577
+ undefined,
578
+ segments.length,
579
+ );
580
+ const hasLocalLayout =
581
+ layoutPath && path.dirname(layoutPath) === dynamicPath;
582
+ if ((isLocalPage || hasLocalLayout) && !(await isDynamicRoute(revalidate))) {
583
+ if (process.env.DINOU_DEBUG) {
584
+ console.log(
585
+ `Found dynamic route: ${segments.join("/") ?? ""}/[${paramName}]`,
586
+ );
587
+ }
516
588
  try {
517
589
  if (getStaticPaths) {
518
- const paths = await getStaticPaths();
590
+ const paths = await (typeof getStaticPaths === "function"
591
+ ? getStaticPaths()
592
+ : getStaticPaths);
519
593
  for (const pathItem of paths) {
520
594
  const currentStructure = [...dynamicStructure, paramName];
521
595
  const isObject =
@@ -598,19 +672,22 @@ async function buildStaticPages() {
598
672
  undefined,
599
673
  segments.length,
600
674
  );
601
- let dynamic;
675
+ let revalidate;
602
676
  let getStaticPaths;
603
677
  if (pageFunctionsPath) {
604
678
  const module = await importModule(pageFunctionsPath);
605
- getStaticPaths = module.getStaticPaths;
606
- dynamic = module.dynamic;
679
+ const defMod = module.default?.default ? module.default.default : module.default;
680
+ getStaticPaths = module.getStaticPaths || defMod?.getStaticPaths;
681
+ revalidate = module.revalidate || defMod?.revalidate;
607
682
  }
608
683
  const isLocalPage =
609
684
  pagePath && path.dirname(pagePath) === dynamicPath;
610
- if (isLocalPage && !dynamic?.()) {
685
+ if (isLocalPage && !(await isDynamicRoute(revalidate))) {
611
686
  try {
612
687
  if (getStaticPaths) {
613
- const paths = await getStaticPaths();
688
+ const paths = await (typeof getStaticPaths === "function"
689
+ ? getStaticPaths()
690
+ : getStaticPaths);
614
691
  for (const pathItem of paths) {
615
692
  const currentStructure = dynamicStructure;
616
693
  const isObject =
@@ -707,19 +784,30 @@ async function buildStaticPages() {
707
784
  undefined,
708
785
  segments.length,
709
786
  );
710
- let dynamic;
787
+ let revalidate;
711
788
  if (pageFunctionsPath) {
712
- const module = await importModule(pageFunctionsPath);
713
- dynamic = module.dynamic;
789
+ try {
790
+ const module = await importModule(pageFunctionsPath);
791
+ revalidate = module.revalidate || module.default?.revalidate;
792
+ } catch (e) {}
714
793
  }
715
794
 
716
- if (pagePath && !dynamic?.() && !doNotPushAtEnd) {
795
+ const isPpr = await resolveBuildPprForRoute(segments, pagePath, pageFunctionsPath, srcFolder);
796
+
797
+ if (pagePath && (!(await isDynamicRoute(revalidate)) || isPpr) && !doNotPushAtEnd) {
717
798
  pages.push({
718
799
  path: currentPath,
719
800
  segments,
720
801
  params: dParams,
802
+ ppr: isPpr,
721
803
  });
722
- console.log(`Found static route: ${segments.join("/") || "/"}`);
804
+ const routeName = segments.join("/") ? "/" + segments.join("/") : "/";
805
+ if (typeof onProgress === "function") {
806
+ onProgress({ phase: "crawling", route: routeName, count: pages.length });
807
+ }
808
+ if (process.env.DINOU_DEBUG) {
809
+ console.log(`Found static route: ${routeName}${isPpr ? " (PPR)" : ""}`);
810
+ }
723
811
  }
724
812
 
725
813
  return pages;
@@ -728,9 +816,14 @@ async function buildStaticPages() {
728
816
  // Crawl complete, start rendering and serialization loop for all discovered static pages
729
817
  const pages = await collectPages(srcFolder);
730
818
 
731
- for (const { path: folderPath, segments, params } of pages) {
819
+ let pageIdx = 0;
820
+ for (const { path: folderPath, segments, params, ppr: pageIsPpr } of pages) {
821
+ pageIdx++;
732
822
  try {
733
823
  const reqPath = segments.length ? "/" + segments.join("/") + "/" : "/";
824
+ if (typeof onProgress === "function") {
825
+ onProgress({ phase: "discovering", route: reqPath, current: pageIdx, total: pages.length });
826
+ }
734
827
  // ====================================================================
735
828
  // 1. MOCK RES: Fulfilling ResponseProxy interface
736
829
  // ====================================================================
@@ -780,9 +873,11 @@ async function buildStaticPages() {
780
873
  this._redirectUrl = url;
781
874
 
782
875
  // Log warning because a redirect in SSG is usually problematic
783
- console.warn(
784
- `⚠️ [SSG] Redirect detected in ${reqPath} -> ${url} (${status})`,
785
- );
876
+ if (process.env.DINOU_DEBUG) {
877
+ console.warn(
878
+ `⚠️ [SSG] Redirect detected in ${reqPath} -> ${url} (${status})`,
879
+ );
880
+ }
786
881
  },
787
882
  };
788
883
 
@@ -826,7 +921,8 @@ async function buildStaticPages() {
826
921
  let revalidate;
827
922
  let cacheTags = [];
828
923
  const jsx = await requestStorage.run(mockContext, async () => {
829
- const [pagePath] = getFilePathAndDynamicParams(
924
+ return await runWithPprContext({ isPpr: Boolean(pageIsPpr) }, async () => {
925
+ const [pagePath] = getFilePathAndDynamicParams(
830
926
  segments,
831
927
  {},
832
928
  folderPath,
@@ -856,17 +952,21 @@ async function buildStaticPages() {
856
952
 
857
953
  if (pageFunctionsPath) {
858
954
  const pageFunctionsModule = await importModule(pageFunctionsPath);
859
- const getProps = pageFunctionsModule.getProps;
860
- revalidate = pageFunctionsModule.revalidate;
861
- if (pageFunctionsModule.getCacheTags) {
955
+ const defMod = pageFunctionsModule.default?.default ? pageFunctionsModule.default.default : pageFunctionsModule.default;
956
+ const getProps = pageFunctionsModule.getProps || defMod?.getProps;
957
+ revalidate = pageFunctionsModule.revalidate || defMod?.revalidate;
958
+ const rawTags = pageFunctionsModule.getCacheTags ?? defMod?.getCacheTags;
959
+ if (typeof rawTags === "function") {
862
960
  try {
863
- cacheTags = await pageFunctionsModule.getCacheTags(params);
961
+ cacheTags = await rawTags(params);
864
962
  } catch (e) {
865
963
  console.error("Error running getCacheTags in buildStaticPages:", e);
866
964
  }
965
+ } else if (Array.isArray(rawTags)) {
966
+ cacheTags = rawTags;
867
967
  }
868
968
  pageFunctionsProps = await getProps?.(params);
869
- props = { ...props, ...(pageFunctionsProps?.page ?? {}) };
969
+ props = { ...props, ...(pageFunctionsProps ?? {}) };
870
970
  }
871
971
 
872
972
  let jsx = React.createElement(Page, props);
@@ -902,19 +1002,30 @@ async function buildStaticPages() {
902
1002
  false,
903
1003
  )[0];
904
1004
  const updatedSlots = {};
905
- for (const [slotName, slotElement] of Object.entries(slots)) {
906
- const alreadyFoundPath = slotElement.props?.__modulePath;
907
- const slotElementWithModule = {
908
- ...slotElement,
909
- __modulePath: alreadyFoundPath ?? null,
910
- };
911
-
1005
+ for (const [slotName, slotValue] of Object.entries(slots)) {
912
1006
  let updatedSlotElement;
1007
+ const slotFilePath = slotValue?.slotPath || slotValue?.props?.__modulePath;
1008
+ const slotParams = slotValue?.slotParams || slotValue?.props?.params || {};
1009
+
913
1010
  try {
914
- await asyncRenderJSXToClientJSX(slotElementWithModule);
915
- updatedSlotElement = slotElementWithModule;
1011
+ let elementToRender;
1012
+ if (slotValue && slotValue.slotPath) {
1013
+ const slotModule = await importModule(slotValue.slotPath);
1014
+ const Slot = slotModule.default ?? slotModule;
1015
+ elementToRender = React.createElement(Slot, {
1016
+ params: slotParams,
1017
+ key: slotName,
1018
+ __modulePath: slotValue.slotPath,
1019
+ });
1020
+ } else {
1021
+ elementToRender = {
1022
+ ...slotValue,
1023
+ __modulePath: slotFilePath ?? null,
1024
+ };
1025
+ }
1026
+ await asyncRenderJSXToClientJSX(elementToRender);
1027
+ updatedSlotElement = elementToRender;
916
1028
  } catch (e) {
917
- const slotFilePath = alreadyFoundPath;
918
1029
  if (slotFilePath) {
919
1030
  const realSlotFolder = path.dirname(slotFilePath);
920
1031
  const [slotErrorPath, slotErrorParams] =
@@ -959,13 +1070,12 @@ async function buildStaticPages() {
959
1070
  }
960
1071
  updatedSlots[slotName] = updatedSlotElement;
961
1072
  }
1073
+ const layoutProps = await getLayoutProps(layoutPath, dParams);
962
1074
  let props = {
963
1075
  params: dParams,
964
1076
  ...updatedSlots,
1077
+ ...(layoutProps ?? {}),
965
1078
  };
966
- if (index === layouts.length - 1 || resetLayoutPath) {
967
- props = { ...props, ...(pageFunctionsProps?.layout ?? {}) };
968
- }
969
1079
  jsx = React.createElement(Layout, props, jsx);
970
1080
  jsx = { ...jsx, __modulePath: layoutPath };
971
1081
  if (resetLayoutPath) {
@@ -978,16 +1088,19 @@ async function buildStaticPages() {
978
1088
 
979
1089
  // Render the layout-nested React element tree to client-side RSC JSON format
980
1090
  return await asyncRenderJSXToClientJSX(jsx);
1091
+ });
981
1092
  });
982
1093
 
983
- if (!isStatic) {
1094
+ if (!isStatic && !pageIsPpr) {
984
1095
  // ❌ DO NOT save file.
985
1096
  // Will behave as pure SSR at runtime.
986
- console.log(
987
- `Skipping static generation for ${segments.join(
988
- "/",
989
- )} due to dynamic usage.`,
990
- );
1097
+ if (process.env.DINOU_DEBUG) {
1098
+ console.log(
1099
+ `Skipping static generation for ${segments.join(
1100
+ "/",
1101
+ )} due to dynamic usage.`,
1102
+ );
1103
+ }
991
1104
  continue;
992
1105
  }
993
1106
 
@@ -996,20 +1109,27 @@ async function buildStaticPages() {
996
1109
  cookies: mockRes._cookies,
997
1110
  };
998
1111
 
1112
+ const holes = pageIsPpr ? getRegisteredHoles() : [];
999
1113
  staticRoutes.add(reqPath);
1000
1114
  staticMetadata.set(reqPath, {
1001
- revalidate: revalidate?.(),
1115
+ revalidate: typeof revalidate === "function" ? await revalidate() : revalidate,
1002
1116
  effects: sideEffects,
1003
1117
  tags: cacheTags,
1118
+ ppr: Boolean(pageIsPpr),
1119
+ holes,
1004
1120
  });
1005
- console.log(`Registered static page at ${reqPath}`);
1121
+ if (process.env.DINOU_DEBUG) {
1122
+ console.log(`Registered static page at ${reqPath}${pageIsPpr ? " (PPR)" : ""}`);
1123
+ }
1006
1124
  } catch (err) {
1007
1125
  console.error(`Error building page ${segments.join("/")}:`, err);
1008
1126
  continue;
1009
1127
  }
1010
1128
  }
1011
1129
 
1012
- console.log(`Static site generated with ${pages.length} pages`);
1130
+ if (process.env.DINOU_DEBUG) {
1131
+ console.log(`Static site generated with ${pages.length} pages`);
1132
+ }
1013
1133
  }
1014
1134
 
1015
1135
  /**
@@ -1031,7 +1151,8 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1031
1151
  segment &&
1032
1152
  ((segment.startsWith("(") && segment.endsWith(")")) ||
1033
1153
  (segment.startsWith("[") && segment.endsWith("]")) ||
1034
- segment.startsWith("@"));
1154
+ segment.startsWith("@") ||
1155
+ segment.startsWith("_"));
1035
1156
  const currentPath = path.join(folderPath, segment);
1036
1157
  if (existsSync(currentPath) && !isRouterSyntaxInSegment) {
1037
1158
  folderPath = currentPath;
@@ -1110,9 +1231,11 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1110
1231
  this._redirectUrl = url;
1111
1232
 
1112
1233
  // We log a warning because a redirect in SSG is usually problematic
1113
- console.warn(
1114
- `⚠️ [SSG] Redirect detected in ${reqPath} -> ${url} (${status})`,
1115
- );
1234
+ if (process.env.DINOU_DEBUG) {
1235
+ console.warn(
1236
+ `⚠️ [SSG] Redirect detected in ${reqPath} -> ${url} (${status})`,
1237
+ );
1238
+ }
1116
1239
  },
1117
1240
  };
1118
1241
 
@@ -1155,6 +1278,7 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1155
1278
 
1156
1279
  let revalidate;
1157
1280
  let cacheTags = [];
1281
+ let pageIsPpr = false;
1158
1282
  const jsx = await requestStorage.run(mockContext, async () => {
1159
1283
  const [pagePath, dParams] = getFilePathAndDynamicParams(
1160
1284
  segments,
@@ -1171,8 +1295,6 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1171
1295
 
1172
1296
  const pageModule = await importModule(pagePath);
1173
1297
  const Page = pageModule.default ?? pageModule;
1174
-
1175
- let props = { params: dParams };
1176
1298
  const [pageFunctionsPath] = getFilePathAndDynamicParams(
1177
1299
  segments,
1178
1300
  {},
@@ -1184,22 +1306,31 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1184
1306
  segments.length,
1185
1307
  );
1186
1308
 
1309
+ pageIsPpr = await resolveBuildPprForRoute(segments, pagePath, pageFunctionsPath, srcFolder);
1310
+
1311
+ return await runWithPprContext({ isPpr: pageIsPpr }, async () => {
1312
+ let props = { params: dParams };
1313
+
1187
1314
  let pageFunctionsProps;
1188
1315
  if (pageFunctionsPath) {
1189
1316
  const pageFunctionsModule = await importModule(pageFunctionsPath);
1190
- const getProps = pageFunctionsModule.getProps;
1191
- if (isDynamic && (isDynamic.value = pageFunctionsModule.dynamic?.()))
1317
+ const defMod = pageFunctionsModule.default?.default ? pageFunctionsModule.default.default : pageFunctionsModule.default;
1318
+ const getProps = pageFunctionsModule.getProps || defMod?.getProps;
1319
+ revalidate = pageFunctionsModule.revalidate || defMod?.revalidate;
1320
+ if (isDynamic && (isDynamic.value = await isDynamicRoute(revalidate)))
1192
1321
  return;
1193
- revalidate = pageFunctionsModule.revalidate;
1194
- if (pageFunctionsModule.getCacheTags) {
1322
+ const rawTags = pageFunctionsModule.getCacheTags ?? defMod?.getCacheTags;
1323
+ if (typeof rawTags === "function") {
1195
1324
  try {
1196
- cacheTags = await pageFunctionsModule.getCacheTags(dParams);
1325
+ cacheTags = await rawTags(dParams);
1197
1326
  } catch (e) {
1198
1327
  console.error("Error running getCacheTags:", e);
1199
1328
  }
1329
+ } else if (Array.isArray(rawTags)) {
1330
+ cacheTags = rawTags;
1200
1331
  }
1201
1332
  pageFunctionsProps = await getProps?.(dParams);
1202
- props = { ...props, ...(pageFunctionsProps?.page ?? {}) };
1333
+ props = { ...props, ...(pageFunctionsProps ?? {}) };
1203
1334
  }
1204
1335
 
1205
1336
  let jsx = React.createElement(Page, props);
@@ -1233,19 +1364,30 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1233
1364
  false,
1234
1365
  )[0];
1235
1366
  const updatedSlots = {};
1236
- for (const [slotName, slotElement] of Object.entries(slots)) {
1237
- const alreadyFoundPath = slotElement.props?.__modulePath;
1238
- const slotElementWithModule = {
1239
- ...slotElement,
1240
- __modulePath: alreadyFoundPath ?? null,
1241
- };
1242
-
1367
+ for (const [slotName, slotValue] of Object.entries(slots)) {
1243
1368
  let updatedSlotElement;
1369
+ const slotFilePath = slotValue?.slotPath || slotValue?.props?.__modulePath;
1370
+ const slotParams = slotValue?.slotParams || slotValue?.props?.params || {};
1371
+
1244
1372
  try {
1245
- await asyncRenderJSXToClientJSX(slotElementWithModule);
1246
- updatedSlotElement = slotElementWithModule;
1373
+ let elementToRender;
1374
+ if (slotValue && slotValue.slotPath) {
1375
+ const slotModule = await importModule(slotValue.slotPath);
1376
+ const Slot = slotModule.default ?? slotModule;
1377
+ elementToRender = React.createElement(Slot, {
1378
+ params: slotParams,
1379
+ key: slotName,
1380
+ __modulePath: slotValue.slotPath,
1381
+ });
1382
+ } else {
1383
+ elementToRender = {
1384
+ ...slotValue,
1385
+ __modulePath: slotFilePath ?? null,
1386
+ };
1387
+ }
1388
+ await asyncRenderJSXToClientJSX(elementToRender);
1389
+ updatedSlotElement = elementToRender;
1247
1390
  } catch (e) {
1248
- const slotFilePath = alreadyFoundPath;
1249
1391
  if (slotFilePath) {
1250
1392
  const realSlotFolder = path.dirname(slotFilePath);
1251
1393
  const [slotErrorPath, slotErrorParams] =
@@ -1294,12 +1436,11 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1294
1436
  params: dParams,
1295
1437
  ...updatedSlots,
1296
1438
  };
1297
- if (index === layouts.length - 1 || resetLayoutPath) {
1298
- layoutProps = {
1299
- ...layoutProps,
1300
- ...(pageFunctionsProps?.layout ?? {}),
1301
- };
1302
- }
1439
+ const customLayoutProps = await getLayoutProps(layoutPath, dParams);
1440
+ layoutProps = {
1441
+ ...layoutProps,
1442
+ ...(customLayoutProps ?? {}),
1443
+ };
1303
1444
  jsx = React.createElement(Layout, layoutProps, jsx);
1304
1445
  jsx = { ...jsx, __modulePath: layoutPath };
1305
1446
  if (resetLayoutPath) {
@@ -1310,17 +1451,20 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1310
1451
  }
1311
1452
  }
1312
1453
 
1313
- return await asyncRenderJSXToClientJSX(jsx);
1454
+ return await asyncRenderJSXToClientJSX(jsx);
1455
+ });
1314
1456
  });
1315
1457
 
1316
- if (!isStatic) {
1458
+ if (!isStatic && !pageIsPpr) {
1317
1459
  // ❌ DO NOT save file.
1318
1460
  // It will behave as pure SSR at runtime.
1319
- console.log(
1320
- `Skipping static generation for ${segments.join(
1321
- "/",
1322
- )} due to dynamic usage.`,
1323
- );
1461
+ if (process.env.DINOU_DEBUG) {
1462
+ console.log(
1463
+ `Skipping static generation for ${segments.join(
1464
+ "/",
1465
+ )} due to dynamic usage.`,
1466
+ );
1467
+ }
1324
1468
  if (isDynamic) {
1325
1469
  isDynamic.value = true;
1326
1470
  }
@@ -1332,13 +1476,16 @@ async function buildStaticPage(reqPath, isDynamic = null) {
1332
1476
  cookies: mockRes._cookies,
1333
1477
  };
1334
1478
 
1479
+ const holes = pageIsPpr ? getRegisteredHoles() : [];
1335
1480
  staticRoutes.add(reqPath);
1336
1481
  staticMetadata.set(reqPath, {
1337
- revalidate: revalidate?.(),
1482
+ revalidate: typeof revalidate === "function" ? await revalidate() : revalidate,
1338
1483
  effects: sideEffects,
1339
1484
  tags: cacheTags,
1485
+ ppr: pageIsPpr,
1486
+ holes,
1340
1487
  });
1341
- console.warn(`Registered rebuilt static page at ${reqPath}`);
1488
+ console.warn(`Registered rebuilt static page at ${reqPath}${pageIsPpr ? " (PPR)" : ""}`);
1342
1489
  } catch (error) {
1343
1490
  console.error(`Error building page ${reqPath}:`, error);
1344
1491
  throw error;
@@ -1350,7 +1497,14 @@ function getStaticPaths() {
1350
1497
  }
1351
1498
 
1352
1499
  function getStaticMetadata(reqPath) {
1353
- return staticMetadata.get(reqPath);
1500
+ if (!reqPath) return null;
1501
+ const noSlash = reqPath.endsWith("/") ? reqPath.slice(0, -1) : reqPath;
1502
+ const withSlash = noSlash + "/";
1503
+ return staticMetadata.get(reqPath) || staticMetadata.get(noSlash) || staticMetadata.get(withSlash) || null;
1504
+ }
1505
+
1506
+ function getStaticLayouts() {
1507
+ return Array.from(staticLayouts.values());
1354
1508
  }
1355
1509
 
1356
1510
  module.exports = {
@@ -1358,4 +1512,7 @@ module.exports = {
1358
1512
  buildStaticPage,
1359
1513
  getStaticPaths,
1360
1514
  getStaticMetadata,
1515
+ getStaticLayouts,
1516
+ isDynamicRoute,
1361
1517
  };
1518
+