dinou 6.1.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +9461 -1080
  3. package/cli.js +392 -169
  4. package/dinou/adapters/bun.js +62 -0
  5. package/dinou/adapters/cloudflare.js +65 -0
  6. package/dinou/adapters/deno.js +48 -0
  7. package/dinou/adapters/netlify.js +85 -0
  8. package/dinou/adapters/node.mjs +31 -0
  9. package/dinou/bun/build.mjs +1077 -0
  10. package/dinou/cloudflare/build.mjs +1121 -0
  11. package/dinou/cloudflare/shims/child_process.js +14 -0
  12. package/dinou/cloudflare/shims/empty.js +2 -0
  13. package/dinou/cloudflare/shims/fs.js +99 -0
  14. package/dinou/cloudflare/shims/os.js +29 -0
  15. package/dinou/config.js +1 -0
  16. package/dinou/config.mjs +2 -0
  17. package/dinou/constants.js +29 -4
  18. package/dinou/core/babel-esm-loader.js +66 -37
  19. package/dinou/core/bailout-proxy.js +61 -0
  20. package/dinou/core/build-static-pages.js +309 -157
  21. package/dinou/core/cache-revalidate.js +471 -144
  22. package/dinou/core/cache-slot.js +387 -0
  23. package/dinou/core/client-redirect.jsx +12 -7
  24. package/dinou/core/client-webpack.jsx +457 -82
  25. package/dinou/core/client.jsx +452 -82
  26. package/dinou/core/config.js +11 -0
  27. package/dinou/core/dinou-paths.js +52 -0
  28. package/dinou/core/edge-ssr.js +122 -0
  29. package/dinou/core/export-static.mjs +64 -0
  30. package/dinou/core/generate-route-types.js +252 -0
  31. package/dinou/core/generate-static-page.js +55 -94
  32. package/dinou/core/generate-static-rsc.js +47 -15
  33. package/dinou/core/generate-static.js +212 -10
  34. package/dinou/core/generating-isg.js +5 -0
  35. package/dinou/core/get-asset-from-manifest.js +9 -3
  36. package/dinou/core/get-error-jsx.js +221 -41
  37. package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
  38. package/dinou/core/get-jsx.js +172 -95
  39. package/dinou/core/handler.js +2296 -0
  40. package/dinou/core/http-adapter.js +115 -0
  41. package/dinou/core/import-module.js +83 -53
  42. package/dinou/core/layout-functions.js +181 -0
  43. package/dinou/core/link.jsx +18 -6
  44. package/dinou/core/load-dinou-config.js +60 -0
  45. package/dinou/core/manifest-provider.js +261 -0
  46. package/dinou/core/navigation-utils.js +4 -0
  47. package/dinou/core/navigation.js +50 -39
  48. package/dinou/core/parse-exports.js +207 -2
  49. package/dinou/core/ppr-context.js +105 -0
  50. package/dinou/core/ppr-runtime.js +263 -0
  51. package/dinou/core/redirect.jsx +20 -19
  52. package/dinou/core/render-jsx-to-client-jsx.js +133 -15
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +32 -0
  55. package/dinou/core/revalidating.js +28 -9
  56. package/dinou/core/route-generator.js +107 -0
  57. package/dinou/core/rsc-renderer.js +90 -0
  58. package/dinou/core/run-ssg.js +19 -0
  59. package/dinou/core/scan-dependency-components.js +218 -0
  60. package/dinou/core/server-function-proxy-webpack.js +3 -0
  61. package/dinou/core/server-function-proxy.js +3 -0
  62. package/dinou/core/slot.js +302 -0
  63. package/dinou/core/status-manifest.js +41 -1
  64. package/dinou/core/storage-adapter.js +522 -0
  65. package/dinou/core/url-resolver.js +16 -16
  66. package/dinou/core/vfs.js +139 -68
  67. package/dinou/deno/build.mjs +1112 -0
  68. package/dinou/deno/seed-kv.js +115 -0
  69. package/dinou/esbuild/build.mjs +51 -1
  70. package/dinou/esbuild/dev.mjs +290 -224
  71. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
  72. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
  73. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
  74. package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
  75. package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
  76. package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
  77. package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
  78. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
  79. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
  80. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
  81. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
  82. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
  83. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
  84. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
  85. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
  86. package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
  87. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
  88. package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
  89. package/dinou/index.d.ts +315 -11
  90. package/dinou/index.js +12 -6
  91. package/dinou/index.mjs +9 -0
  92. package/dinou/node/build.mjs +195 -0
  93. package/dinou/node/bundle-dual-engine.mjs +1012 -0
  94. package/dinou/node/dev.mjs +2589 -0
  95. package/dinou/node/port-selector.mjs +164 -0
  96. package/dinou/node/terminal-status.mjs +345 -0
  97. package/dinou/package.json +17 -14
  98. package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
  99. package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
  100. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
  101. package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
  102. package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
  103. package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
  104. package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
  105. package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
  106. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
  107. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
  108. package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
  109. package/dinou/rollup/rollup.config.js +155 -43
  110. package/dinou/server.d.ts +114 -12
  111. package/dinou/server.js +35 -10
  112. package/dinou/server.mjs +30 -9
  113. package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
  114. package/dinou/webpack/loaders/server-functions-loader.js +22 -1
  115. package/dinou/webpack/loaders/swc-loader.js +76 -0
  116. package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
  117. package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
  118. package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
  119. package/dinou/webpack/webpack.config.js +227 -29
  120. package/eject.js +173 -94
  121. package/package.json +110 -107
  122. package/dinou/core/asset-require-hook.js +0 -58
  123. package/dinou/core/client-error-webpack.jsx +0 -432
  124. package/dinou/core/client-error.jsx +0 -432
  125. package/dinou/core/concurrency-manager.js +0 -48
  126. package/dinou/core/context-proxy.js +0 -68
  127. package/dinou/core/generate-static-pages.js +0 -130
  128. package/dinou/core/generate-static-rscs.js +0 -151
  129. package/dinou/core/get-ssg-metadata.js +0 -31
  130. package/dinou/core/register-loader.mjs +0 -11
  131. package/dinou/core/register-paths.js +0 -27
  132. package/dinou/core/render-app-to-html.js +0 -430
  133. package/dinou/core/render-html.js +0 -407
  134. package/dinou/core/server.js +0 -1542
  135. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
@@ -0,0 +1,105 @@
1
+ // dinou/core/ppr-context.js
2
+ // PPR Pre-rendering Context and Suspense Hole Tracker for Dinou v7.2.
3
+
4
+ const { AsyncLocalStorage } = require("async_hooks");
5
+
6
+ const pprStorage = new AsyncLocalStorage();
7
+
8
+ /**
9
+ * Executes a function within a PPR pre-render context.
10
+ * @param {object} options
11
+ * @param {Function} callback
12
+ * @returns {Promise<any>}
13
+ */
14
+ function runWithPprContext(options, callback) {
15
+ const store = {
16
+ isPpr: Boolean(options?.isPpr),
17
+ suspenseDepth: 0,
18
+ currentHoleId: null,
19
+ holes: new Map(),
20
+ nextHoleIndex: 0,
21
+ ...options,
22
+ };
23
+ return pprStorage.run(store, callback);
24
+ }
25
+
26
+ function getPprStore() {
27
+ return pprStorage.getStore() || null;
28
+ }
29
+
30
+ function isPprBuildActive() {
31
+ const store = getPprStore();
32
+ return Boolean(store && store.isPpr);
33
+ }
34
+
35
+ function isInsideSuspense() {
36
+ const store = getPprStore();
37
+ return Boolean(store && store.suspenseDepth > 0);
38
+ }
39
+
40
+ function enterSuspense(holeId = null) {
41
+ const store = getPprStore();
42
+ if (store) {
43
+ store.suspenseDepth++;
44
+ if (!holeId) {
45
+ store.nextHoleIndex++;
46
+ holeId = `ppr-hole-${store.nextHoleIndex}`;
47
+ }
48
+ store.currentHoleId = holeId;
49
+ return holeId;
50
+ }
51
+ return null;
52
+ }
53
+
54
+ function exitSuspense() {
55
+ const store = getPprStore();
56
+ if (store) {
57
+ store.suspenseDepth = Math.max(0, store.suspenseDepth - 1);
58
+ if (store.suspenseDepth === 0) {
59
+ store.currentHoleId = null;
60
+ }
61
+ }
62
+ }
63
+
64
+ function markDynamicAccess(type = "dynamic") {
65
+ const store = getPprStore();
66
+ if (store && store.isPpr && store.suspenseDepth > 0) {
67
+ const holeId = store.currentHoleId || `ppr-hole-${store.nextHoleIndex}`;
68
+ const error = new Error(`[Dinou PPR] Dynamic access to '${type}' postponed inside Suspense`);
69
+ error.$$typeof = Symbol.for("dinou.ppr.postpone");
70
+ error.pprType = type;
71
+ error.holeId = holeId;
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ function registerHole(holeId, meta = {}) {
77
+ const store = getPprStore();
78
+ if (store) {
79
+ store.holes.set(holeId, {
80
+ id: holeId,
81
+ registeredAt: Date.now(),
82
+ ...meta,
83
+ });
84
+ }
85
+ }
86
+
87
+ function getRegisteredHoles() {
88
+ const store = getPprStore();
89
+ if (store) {
90
+ return Array.from(store.holes.keys());
91
+ }
92
+ return [];
93
+ }
94
+
95
+ module.exports = {
96
+ runWithPprContext,
97
+ getPprStore,
98
+ isPprBuildActive,
99
+ isInsideSuspense,
100
+ enterSuspense,
101
+ exitSuspense,
102
+ markDynamicAccess,
103
+ registerHole,
104
+ getRegisteredHoles,
105
+ };
@@ -0,0 +1,263 @@
1
+ // dinou/core/ppr-runtime.js
2
+ // Dinou Partial Prerendering (PPR) Runtime Streaming Resume Engine.
3
+ // Sends the pre-rendered shell immediately (0ms TTFB) and progressively
4
+ // streams dynamic Suspense holes as they resolve over the same HTTP connection.
5
+ // Designed for the Dual-Bundle architecture (Pass A: RSC, Pass B: SSR).
6
+
7
+ const React = require("react");
8
+ const { renderRSCStream } = require("./rsc-renderer.js");
9
+ const { getClientManifest } = require("./manifest-provider.js");
10
+ const { requestStorage, setCurrentContext } = require("./request-context.js");
11
+ const getJSX = require("./get-jsx.js");
12
+
13
+ /**
14
+ * Traverses a React JSX element tree to find Suspense boundaries,
15
+ * evaluates their dynamic children, and streams replacement chunks.
16
+ *
17
+ * @param {object} node - Root JSX node (layout + page)
18
+ * @param {Array<string|object>} expectedHoles - Holes registered at build time
19
+ * @param {WritableStreamDefaultWriter} writer - Response stream writer
20
+ * @param {TextEncoder} encoder - Text encoder
21
+ * @param {object} platformContext - Dinou platform context (containing renderHtmlStream)
22
+ * @param {boolean} isDevelopment - Dev mode flag
23
+ */
24
+ async function streamDynamicHoles(
25
+ node,
26
+ expectedHoles,
27
+ writer,
28
+ encoder,
29
+ platformContext,
30
+ isDevelopment = false
31
+ ) {
32
+ let holeIndex = 0;
33
+ const holePromises = [];
34
+
35
+ const knownHoleIds = new Set(
36
+ (expectedHoles || []).map((h) => (typeof h === "object" ? h.id : h))
37
+ );
38
+
39
+ const clientManifest = getClientManifest();
40
+
41
+ function isClientComponent(type) {
42
+ if (!type) return false;
43
+ return (
44
+ type.$$typeof === Symbol.for("react.client.reference") ||
45
+ Boolean(type.$$id) ||
46
+ Boolean(type.filepath)
47
+ );
48
+ }
49
+
50
+ async function traverse(current) {
51
+ if (!current) return;
52
+
53
+ // Handle Client Component: do NOT execute, only traverse children
54
+ if (isClientComponent(current?.type) || current?.$$typeof === Symbol.for("react.client.reference")) {
55
+ if (current?.props?.children) {
56
+ await traverse(current.props.children);
57
+ }
58
+ return;
59
+ }
60
+
61
+ // Handle React Server Component function
62
+ if (typeof current?.type === "function") {
63
+ try {
64
+ const evaluated = await current.type(current.props || {});
65
+ await traverse(evaluated);
66
+ } catch (err) {
67
+ console.error("[Dinou PPR] Error evaluating component in hole traversal:", err);
68
+ }
69
+ return;
70
+ }
71
+
72
+ // Handle React.forward_ref
73
+ if (
74
+ typeof current?.type === "object" &&
75
+ current.type !== null &&
76
+ current.type.$$typeof === Symbol.for("react.forward_ref")
77
+ ) {
78
+ try {
79
+ const rendered = current.type.render(current.props || {}, current.ref);
80
+ await traverse(rendered);
81
+ } catch (err) {
82
+ console.error("[Dinou PPR] Error evaluating forward_ref in hole traversal:", err);
83
+ }
84
+ return;
85
+ }
86
+
87
+ // Handle React.Suspense
88
+ if (
89
+ current?.type === Symbol.for("react.suspense") ||
90
+ current?.type === React.Suspense
91
+ ) {
92
+ holeIndex++;
93
+ const currentHoleId = `ppr-hole-${holeIndex}`;
94
+
95
+ // Check if this boundary was registered as a dynamic hole
96
+ const shouldStream =
97
+ knownHoleIds.size === 0 || knownHoleIds.has(currentHoleId);
98
+
99
+ if (shouldStream) {
100
+ const children = current.props?.children;
101
+ const promise = (async () => {
102
+ try {
103
+ let html = "";
104
+ if (platformContext && platformContext.renderHtmlStream) {
105
+ const rscStream = renderRSCStream(children, clientManifest, { runtime: "edge" });
106
+ const htmlStream = await platformContext.renderHtmlStream(rscStream, {
107
+ bootstrapModules: [],
108
+ });
109
+ html = await new Response(htmlStream).text();
110
+ }
111
+
112
+ const chunk = `\n<template id="ppr-content-${currentHoleId}">${html}</template>\n<script>(function(){var c=document.getElementById("ppr-content-${currentHoleId}");if(!c)return;var b=document.getElementById("B:${holeIndex - 1}");var t=document.querySelector('[data-ppr-hole="${currentHoleId}"]')||(b?b.nextElementSibling:null);try{if(t){t.replaceWith(c.content?c.content:...c.childNodes);}}finally{c.remove();}})();</script>\n`;
113
+ await writer.write(encoder.encode(chunk));
114
+ } catch (holeErr) {
115
+ console.error(`[Dinou PPR] Error resolving dynamic hole ${currentHoleId}:`, holeErr);
116
+ const errMsg = isDevelopment
117
+ ? (holeErr?.message || "Unknown dynamic error")
118
+ : "Error loading dynamic content";
119
+ const errChunk = `\n<template id="ppr-content-${currentHoleId}"><div style="color:#e11d48;padding:8px 12px;background:#fff1f2;border:1px solid #fecdd3;border-radius:6px;font-size:0.875rem;font-family:system-ui,sans-serif;">[PPR Error: ${errMsg}]</div></template>\n<script>(function(){var c=document.getElementById("ppr-content-${currentHoleId}");if(!c)return;var b=document.getElementById("B:${holeIndex - 1}");var t=document.querySelector('[data-ppr-hole="${currentHoleId}"]')||(b?b.nextElementSibling:null);try{if(t){t.replaceWith(c.content?c.content:...c.childNodes);}}finally{c.remove();}})();</script>\n`;
120
+ await writer.write(encoder.encode(errChunk));
121
+ }
122
+ })();
123
+ holePromises.push(promise);
124
+ }
125
+ return;
126
+ }
127
+
128
+ // Handle Arrays (children)
129
+ if (Array.isArray(current)) {
130
+ for (const item of current) {
131
+ await traverse(item);
132
+ }
133
+ return;
134
+ }
135
+
136
+ // Traverse children props
137
+ if (current?.props?.children) {
138
+ await traverse(current.props.children);
139
+ }
140
+ }
141
+
142
+ await traverse(node);
143
+ await Promise.all(holePromises);
144
+ }
145
+
146
+ /**
147
+ * Handles PPR Runtime Streaming Resume.
148
+ * Immediately flushes shellPrelude to the client, then resolves dynamic holes in parallel.
149
+ */
150
+ async function handlePprResume({
151
+ reqPath,
152
+ queryObj,
153
+ simReq,
154
+ bridge,
155
+ platformContext,
156
+ rootContext,
157
+ cachedItem,
158
+ metadata,
159
+ isDevelopment,
160
+ getDiscoveredLayouts,
161
+ createRequestContext,
162
+ copyCustomContextProperties,
163
+ }) {
164
+ let shellHtml = cachedItem.content;
165
+ if (typeof shellHtml === "string") {
166
+ if (!shellHtml.includes("window.__DINOU_LAYOUTS__")) {
167
+ const knownLayouts = typeof getDiscoveredLayouts === "function" ? getDiscoveredLayouts() : {};
168
+ shellHtml = shellHtml.replace(
169
+ "</head>",
170
+ `<script>window.__DINOU_LAYOUTS__=${JSON.stringify(knownLayouts)};</script></head>`
171
+ );
172
+ }
173
+ shellHtml = shellHtml.replace(
174
+ /window\.__DINOU_USE_STATIC__\s*=\s*true;?/g,
175
+ "window.__DINOU_USE_STATIC__ = false; window.__DINOU_PPR__ = true;"
176
+ );
177
+ if (!shellHtml.includes("window.__DINOU_PPR__")) {
178
+ shellHtml = shellHtml.replace(
179
+ "</head>",
180
+ `<script>window.__DINOU_USE_STATIC__ = false; window.__DINOU_PPR__ = true;</script></head>`
181
+ );
182
+ }
183
+ }
184
+
185
+ // Separate shell into prelude (up to </body>) and postlude
186
+ const lowerHtml = shellHtml.toLowerCase();
187
+ const bodyCloseIndex = lowerHtml.lastIndexOf("</body>");
188
+ let shellPrelude =
189
+ bodyCloseIndex !== -1 ? shellHtml.slice(0, bodyCloseIndex) : shellHtml;
190
+ let shellPostlude =
191
+ bodyCloseIndex !== -1 ? shellHtml.slice(bodyCloseIndex) : "</body></html>";
192
+
193
+ const { readable, writable } = new TransformStream();
194
+ const writer = writable.getWriter();
195
+ const encoder = new TextEncoder();
196
+
197
+ // Stream dynamic holes in parallel with the prelude
198
+ const resumePromise = (async () => {
199
+ try {
200
+ // 1. Immediately flush static shell prelude (0ms TTFB)
201
+ await writer.write(encoder.encode(shellPrelude));
202
+
203
+ const dynamicState = { value: true };
204
+ const context = createRequestContext(simReq, bridge, platformContext, dynamicState);
205
+ if (typeof copyCustomContextProperties === "function") {
206
+ copyCustomContextProperties(rootContext, context);
207
+ }
208
+ if (typeof setCurrentContext === "function") {
209
+ setCurrentContext(context);
210
+ }
211
+
212
+ await requestStorage.run(context, async () => {
213
+ const isNotFound = { value: false };
214
+ const jsx = await getJSX(
215
+ reqPath,
216
+ queryObj,
217
+ isNotFound,
218
+ isDevelopment,
219
+ false
220
+ );
221
+
222
+ const holes = metadata?.holes || [];
223
+ await streamDynamicHoles(
224
+ jsx,
225
+ holes,
226
+ writer,
227
+ encoder,
228
+ platformContext,
229
+ isDevelopment
230
+ );
231
+ });
232
+ } catch (streamErr) {
233
+ console.error("[Dinou PPR] Runtime streaming resume error:", streamErr);
234
+ } finally {
235
+ try {
236
+ await writer.write(encoder.encode(shellPostlude));
237
+ await writer.close();
238
+ } catch (_) {}
239
+ }
240
+ })();
241
+
242
+ if (
243
+ platformContext &&
244
+ platformContext.ctx &&
245
+ typeof platformContext.ctx.waitUntil === "function"
246
+ ) {
247
+ platformContext.ctx.waitUntil(resumePromise);
248
+ }
249
+
250
+ const headers = new Headers(bridge.headers);
251
+ headers.set("Content-Type", "text/html; charset=utf-8");
252
+ headers.set("Cache-Control", "no-store, no-cache, must-revalidate");
253
+
254
+ return new Response(readable, {
255
+ status: metadata?.status || 200,
256
+ headers,
257
+ });
258
+ }
259
+
260
+ module.exports = {
261
+ streamDynamicHoles,
262
+ handlePprResume,
263
+ };
@@ -1,5 +1,7 @@
1
1
  import { ClientRedirect } from "./client-redirect.jsx";
2
2
 
3
+ const DINOU_CONTEXT_KEY = Symbol.for("dinou.request.context.storage");
4
+
3
5
  /**
4
6
  * Universal redirection function.
5
7
  * Use it with 'return': return redirect('/login');
@@ -7,26 +9,23 @@ import { ClientRedirect } from "./client-redirect.jsx";
7
9
  export function redirect(destination) {
8
10
  // 1. We try to get the server context
9
11
  if (typeof window === "undefined") {
10
- // getContext() must be accessible here
11
- const dynamicRequire =
12
- typeof __dinou_require__ !== "undefined"
13
- ? __dinou_require__
14
- : typeof module !== "undefined" && typeof module.require === "function"
15
- ? module.require.bind(module)
16
- : null;
17
- if (dynamicRequire) {
18
- const { getContext } = dynamicRequire(
19
- /* webpackIgnore: true */ "./request-context.js"
20
- );
21
- const ctx = getContext();
22
-
23
- // 2. If we are on the server and headers have NOT been sent yet...
24
- // We can do a real HTTP redirect (Status 307).
25
- // This is better for SEO and speed in Hard Navigation.
26
- if (ctx && ctx.res) {
27
- ctx.res.redirect(destination);
28
- return <ClientRedirect to={destination} />;
12
+ let ctx;
13
+ try {
14
+ const storage = globalThis[DINOU_CONTEXT_KEY];
15
+ if (storage && typeof storage.getStore === "function") {
16
+ ctx = storage.getStore();
17
+ }
18
+ if (!ctx && typeof globalThis !== "undefined") {
19
+ ctx = globalThis[Symbol.for("dinou.request.context.current")];
29
20
  }
21
+ } catch (e) { }
22
+
23
+ // 2. If we are on the server and headers have NOT been sent yet...
24
+ // We can do a real HTTP redirect (Status 307 or x-rsc-redirect).
25
+ // This is better for SEO and speed in Hard Navigation.
26
+ if (ctx && ctx.res && typeof ctx.res.redirect === "function") {
27
+ ctx.res.redirect(destination);
28
+ return <ClientRedirect to={destination} />;
30
29
  }
31
30
  }
32
31
 
@@ -34,3 +33,5 @@ export function redirect(destination) {
34
33
  // We return the component that will force the redirection in the browser.
35
34
  return <ClientRedirect to={destination} />;
36
35
  }
36
+
37
+ export default redirect;
@@ -1,3 +1,10 @@
1
+ const {
2
+ isPprBuildActive,
3
+ enterSuspense,
4
+ exitSuspense,
5
+ registerHole,
6
+ } = require("./ppr-context.js");
7
+
1
8
  // Function to check if a component is a client component
2
9
  function isClientComponent(type) {
3
10
  if (!type) {
@@ -21,10 +28,10 @@ function renderJSXToClientJSX(jsx, key = null) {
21
28
  ) {
22
29
  return jsx;
23
30
  } else if (Array.isArray(jsx)) {
24
- return jsx.map((child, i) =>
31
+ return jsx.map((child) =>
25
32
  renderJSXToClientJSX(
26
33
  child,
27
- i + (typeof child?.type === "string" ? "_" + child?.type : "")
34
+ child?.key ?? null
28
35
  )
29
36
  );
30
37
  } else if (typeof jsx === "symbol") {
@@ -44,6 +51,7 @@ function renderJSXToClientJSX(jsx, key = null) {
44
51
  if (
45
52
  jsx.type === Symbol.for("react.fragment") ||
46
53
  jsx.type === Symbol.for("react.suspense") ||
54
+ jsx.type === Symbol.for("react.view_transition") ||
47
55
  typeof jsx.type === "string"
48
56
  ) {
49
57
  return {
@@ -67,6 +75,41 @@ function renderJSXToClientJSX(jsx, key = null) {
67
75
  const returnedJsx = Component(props);
68
76
  return renderJSXToClientJSX(returnedJsx, key ?? jsx.key);
69
77
  }
78
+ } else if (
79
+ typeof jsx.type === "object" &&
80
+ jsx.type !== null &&
81
+ jsx.type.$$typeof === Symbol.for("react.forward_ref")
82
+ ) {
83
+ if (isClientComponent(jsx.type)) {
84
+ return {
85
+ ...jsx,
86
+ $$typeof: Symbol.for("react.transitional.element"),
87
+ type: jsx.type,
88
+ props: renderJSXToClientJSX(jsx.props),
89
+ key: key ?? jsx.key,
90
+ };
91
+ } else {
92
+ const returnedJsx = jsx.type.render(jsx.props, jsx.ref);
93
+ return renderJSXToClientJSX(returnedJsx, key ?? jsx.key);
94
+ }
95
+ } else if (
96
+ typeof jsx.type === "object" &&
97
+ jsx.type !== null &&
98
+ jsx.type.$$typeof === Symbol.for("react.memo")
99
+ ) {
100
+ if (isClientComponent(jsx.type)) {
101
+ return {
102
+ ...jsx,
103
+ $$typeof: Symbol.for("react.transitional.element"),
104
+ type: jsx.type,
105
+ props: renderJSXToClientJSX(jsx.props),
106
+ key: key ?? jsx.key,
107
+ };
108
+ } else {
109
+ const innerComp = jsx.type.type;
110
+ const returnedJsx = typeof innerComp === "function" ? innerComp(jsx.props) : null;
111
+ return renderJSXToClientJSX(returnedJsx, key ?? jsx.key);
112
+ }
70
113
  } else {
71
114
  console.error("Unsupported JSX type:", jsx.type);
72
115
  throw new Error("Unsupported JSX type");
@@ -99,10 +142,10 @@ async function asyncRenderJSXToClientJSX(jsx, key = null) {
99
142
  return jsx;
100
143
  } else if (Array.isArray(jsx)) {
101
144
  return await Promise.all(
102
- jsx.map((child, i) =>
145
+ jsx.map((child) =>
103
146
  asyncRenderJSXToClientJSX(
104
147
  child,
105
- i + (typeof child?.type === "string" ? "_" + child?.type : "")
148
+ child?.key ?? null
106
149
  )
107
150
  )
108
151
  );
@@ -112,24 +155,66 @@ async function asyncRenderJSXToClientJSX(jsx, key = null) {
112
155
  return {
113
156
  $$typeof: Symbol.for("react.transitional.element"),
114
157
  type: Symbol.for("react.fragment"),
115
- props: { key },
158
+ props: {},
159
+ key,
116
160
  };
117
161
  }
118
162
  console.error("Unsupported symbol:", String(jsx));
119
163
  throw new Error(`Unsupported symbol: ${String(jsx)}`);
120
164
  } else if (typeof jsx === "object") {
121
165
  if (jsx.$$typeof === Symbol.for("react.transitional.element")) {
122
- if (
166
+ if (jsx.type === Symbol.for("react.suspense")) {
167
+ if (isPprBuildActive()) {
168
+ const holeId = enterSuspense();
169
+ try {
170
+ const renderedChildren = await asyncRenderJSXToClientJSX(jsx.props?.children);
171
+ exitSuspense();
172
+ return {
173
+ ...jsx,
174
+ props: {
175
+ ...jsx.props,
176
+ children: renderedChildren,
177
+ fallback: await asyncRenderJSXToClientJSX(jsx.props?.fallback),
178
+ },
179
+ key: key ?? jsx.key,
180
+ };
181
+ } catch (err) {
182
+ exitSuspense();
183
+ if (
184
+ (err && err.$$typeof === Symbol.for("dinou.ppr.postpone")) ||
185
+ (err && typeof err?.then === "function") ||
186
+ (err && err?.$$typeof === Symbol.for("react.postpone"))
187
+ ) {
188
+ registerHole(holeId, { type: err.pprType || "suspense" });
189
+ const renderedFallback = await asyncRenderJSXToClientJSX(jsx.props?.fallback);
190
+ return {
191
+ $$typeof: Symbol.for("react.transitional.element"),
192
+ type: "div",
193
+ props: {
194
+ "data-ppr-hole": holeId,
195
+ style: { display: "contents" },
196
+ children: renderedFallback,
197
+ },
198
+ key: key ?? jsx.key,
199
+ };
200
+ }
201
+ throw err;
202
+ }
203
+ }
204
+ return {
205
+ ...jsx,
206
+ props: await asyncRenderJSXToClientJSX(jsx.props),
207
+ key: key ?? jsx.key,
208
+ };
209
+ } else if (
123
210
  jsx.type === Symbol.for("react.fragment") ||
124
- jsx.type === Symbol.for("react.suspense") ||
211
+ jsx.type === Symbol.for("react.view_transition") ||
125
212
  typeof jsx.type === "string"
126
213
  ) {
127
214
  return {
128
215
  ...jsx,
129
- props: {
130
- ...(await asyncRenderJSXToClientJSX(jsx.props, key ?? jsx.key)),
131
- key: key ?? jsx.key,
132
- },
216
+ props: await asyncRenderJSXToClientJSX(jsx.props),
217
+ key: key ?? jsx.key,
133
218
  };
134
219
  } else if (typeof jsx.type === "function") {
135
220
  const Component = jsx.type;
@@ -139,16 +224,49 @@ async function asyncRenderJSXToClientJSX(jsx, key = null) {
139
224
  ...jsx,
140
225
  $$typeof: Symbol.for("react.transitional.element"),
141
226
  type: Component,
142
- props: {
143
- ...(await asyncRenderJSXToClientJSX(props, key ?? jsx.key)),
144
- key: key ?? jsx.key,
145
- },
227
+ props: await asyncRenderJSXToClientJSX(props),
228
+ key: key ?? jsx.key,
146
229
  };
147
230
  } else {
148
231
  // Server component: execute and process
149
232
  const returnedJsx = await Component(props);
150
233
  return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
151
234
  }
235
+ } else if (
236
+ typeof jsx.type === "object" &&
237
+ jsx.type !== null &&
238
+ jsx.type.$$typeof === Symbol.for("react.forward_ref")
239
+ ) {
240
+ if (isClientComponent(jsx.type)) {
241
+ return {
242
+ ...jsx,
243
+ $$typeof: Symbol.for("react.transitional.element"),
244
+ type: jsx.type,
245
+ props: await asyncRenderJSXToClientJSX(jsx.props),
246
+ key: key ?? jsx.key,
247
+ };
248
+ } else {
249
+ const returnedJsx = await jsx.type.render(jsx.props, jsx.ref);
250
+ return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
251
+ }
252
+ } else if (
253
+ typeof jsx.type === "object" &&
254
+ jsx.type !== null &&
255
+ jsx.type.$$typeof === Symbol.for("react.memo")
256
+ ) {
257
+ if (isClientComponent(jsx.type)) {
258
+ return {
259
+ ...jsx,
260
+ $$typeof: Symbol.for("react.transitional.element"),
261
+ type: jsx.type,
262
+ props: await asyncRenderJSXToClientJSX(jsx.props),
263
+ key: key ?? jsx.key,
264
+ };
265
+ } else {
266
+ const innerComp = jsx.type.type;
267
+ const returnedJsx = typeof innerComp === "function" ? await innerComp(jsx.props) : null;
268
+ return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
269
+ }
152
270
  } else {
153
271
  console.error("Unsupported JSX type:", jsx.type);
154
272
  throw new Error("Unsupported JSX type");