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
@@ -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) {
@@ -10,21 +17,23 @@ function isClientComponent(type) {
10
17
  );
11
18
  }
12
19
 
13
- function renderJSXToClientJSX(jsx, key = null) {
20
+ async function asyncRenderJSXToClientJSX(jsx, key = null) {
14
21
  if (
15
22
  typeof jsx === "string" ||
16
23
  typeof jsx === "number" ||
17
24
  typeof jsx === "boolean" ||
18
25
  typeof jsx === "function" ||
19
26
  typeof jsx === "undefined" ||
20
- jsx == null
27
+ jsx === null
21
28
  ) {
22
29
  return jsx;
23
30
  } else if (Array.isArray(jsx)) {
24
- return jsx.map((child, i) =>
25
- renderJSXToClientJSX(
26
- child,
27
- i + (typeof child?.type === "string" ? "_" + child?.type : "")
31
+ return await Promise.all(
32
+ jsx.map((child) =>
33
+ asyncRenderJSXToClientJSX(
34
+ child,
35
+ child?.key ?? null
36
+ )
28
37
  )
29
38
  );
30
39
  } else if (typeof jsx === "symbol") {
@@ -34,21 +43,64 @@ function renderJSXToClientJSX(jsx, key = null) {
34
43
  $$typeof: Symbol.for("react.transitional.element"),
35
44
  type: Symbol.for("react.fragment"),
36
45
  props: {},
37
- key: key,
46
+ key,
38
47
  };
39
48
  }
40
49
  console.error("Unsupported symbol:", String(jsx));
41
50
  throw new Error(`Unsupported symbol: ${String(jsx)}`);
42
51
  } else if (typeof jsx === "object") {
43
52
  if (jsx.$$typeof === Symbol.for("react.transitional.element")) {
44
- if (
53
+ if (jsx.type === Symbol.for("react.suspense")) {
54
+ if (isPprBuildActive()) {
55
+ const holeId = enterSuspense();
56
+ try {
57
+ const renderedChildren = await asyncRenderJSXToClientJSX(jsx.props?.children);
58
+ exitSuspense();
59
+ return {
60
+ ...jsx,
61
+ props: {
62
+ ...jsx.props,
63
+ children: renderedChildren,
64
+ fallback: await asyncRenderJSXToClientJSX(jsx.props?.fallback),
65
+ },
66
+ key: key ?? jsx.key,
67
+ };
68
+ } catch (err) {
69
+ exitSuspense();
70
+ if (
71
+ (err && err.$$typeof === Symbol.for("dinou.ppr.postpone")) ||
72
+ (err && typeof err?.then === "function") ||
73
+ (err && err?.$$typeof === Symbol.for("react.postpone"))
74
+ ) {
75
+ registerHole(holeId, { type: err.pprType || "suspense" });
76
+ const renderedFallback = await asyncRenderJSXToClientJSX(jsx.props?.fallback);
77
+ return {
78
+ $$typeof: Symbol.for("react.transitional.element"),
79
+ type: "div",
80
+ props: {
81
+ "data-ppr-hole": holeId,
82
+ style: { display: "contents" },
83
+ children: renderedFallback,
84
+ },
85
+ key: key ?? jsx.key,
86
+ };
87
+ }
88
+ throw err;
89
+ }
90
+ }
91
+ return {
92
+ ...jsx,
93
+ props: await asyncRenderJSXToClientJSX(jsx.props),
94
+ key: key ?? jsx.key,
95
+ };
96
+ } else if (
45
97
  jsx.type === Symbol.for("react.fragment") ||
46
- jsx.type === Symbol.for("react.suspense") ||
98
+ jsx.type === Symbol.for("react.view_transition") ||
47
99
  typeof jsx.type === "string"
48
100
  ) {
49
101
  return {
50
102
  ...jsx,
51
- props: renderJSXToClientJSX(jsx.props),
103
+ props: await asyncRenderJSXToClientJSX(jsx.props),
52
104
  key: key ?? jsx.key,
53
105
  };
54
106
  } else if (typeof jsx.type === "function") {
@@ -59,94 +111,47 @@ function renderJSXToClientJSX(jsx, key = null) {
59
111
  ...jsx,
60
112
  $$typeof: Symbol.for("react.transitional.element"),
61
113
  type: Component,
62
- props: renderJSXToClientJSX(props),
114
+ props: await asyncRenderJSXToClientJSX(props),
63
115
  key: key ?? jsx.key,
64
116
  };
65
117
  } else {
66
118
  // Server component: execute and process
67
- const returnedJsx = Component(props);
68
- return renderJSXToClientJSX(returnedJsx, key ?? jsx.key);
119
+ const returnedJsx = await Component(props);
120
+ return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
69
121
  }
70
- } else {
71
- console.error("Unsupported JSX type:", jsx.type);
72
- throw new Error("Unsupported JSX type");
73
- }
74
- } else if (jsx instanceof Promise) {
75
- return jsx;
76
- } else {
77
- // Process object props (e.g., { className: "foo" })
78
- return Object.fromEntries(
79
- Object.entries(jsx).map(([propName, value]) => [
80
- propName,
81
- renderJSXToClientJSX(value),
82
- ])
83
- );
84
- }
85
- } else {
86
- throw new Error("Not implemented");
87
- }
88
- }
89
-
90
- async function asyncRenderJSXToClientJSX(jsx, key = null) {
91
- if (
92
- typeof jsx === "string" ||
93
- typeof jsx === "number" ||
94
- typeof jsx === "boolean" ||
95
- typeof jsx === "function" ||
96
- typeof jsx === "undefined" ||
97
- jsx === null
98
- ) {
99
- return jsx;
100
- } else if (Array.isArray(jsx)) {
101
- return await Promise.all(
102
- jsx.map((child, i) =>
103
- asyncRenderJSXToClientJSX(
104
- child,
105
- i + (typeof child?.type === "string" ? "_" + child?.type : "")
106
- )
107
- )
108
- );
109
- } else if (typeof jsx === "symbol") {
110
- if (jsx === Symbol.for("react.fragment")) {
111
- // Handle Fragment as an empty props object
112
- return {
113
- $$typeof: Symbol.for("react.transitional.element"),
114
- type: Symbol.for("react.fragment"),
115
- props: { key },
116
- };
117
- }
118
- console.error("Unsupported symbol:", String(jsx));
119
- throw new Error(`Unsupported symbol: ${String(jsx)}`);
120
- } else if (typeof jsx === "object") {
121
- if (jsx.$$typeof === Symbol.for("react.transitional.element")) {
122
- if (
123
- jsx.type === Symbol.for("react.fragment") ||
124
- jsx.type === Symbol.for("react.suspense") ||
125
- typeof jsx.type === "string"
122
+ } else if (
123
+ typeof jsx.type === "object" &&
124
+ jsx.type !== null &&
125
+ jsx.type.$$typeof === Symbol.for("react.forward_ref")
126
126
  ) {
127
- return {
128
- ...jsx,
129
- props: {
130
- ...(await asyncRenderJSXToClientJSX(jsx.props, key ?? jsx.key)),
127
+ if (isClientComponent(jsx.type)) {
128
+ return {
129
+ ...jsx,
130
+ $$typeof: Symbol.for("react.transitional.element"),
131
+ type: jsx.type,
132
+ props: await asyncRenderJSXToClientJSX(jsx.props),
131
133
  key: key ?? jsx.key,
132
- },
133
- };
134
- } else if (typeof jsx.type === "function") {
135
- const Component = jsx.type;
136
- const props = jsx.props;
137
- if (isClientComponent(Component)) {
134
+ };
135
+ } else {
136
+ const returnedJsx = await jsx.type.render(jsx.props, jsx.ref);
137
+ return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
138
+ }
139
+ } else if (
140
+ typeof jsx.type === "object" &&
141
+ jsx.type !== null &&
142
+ jsx.type.$$typeof === Symbol.for("react.memo")
143
+ ) {
144
+ if (isClientComponent(jsx.type)) {
138
145
  return {
139
146
  ...jsx,
140
147
  $$typeof: Symbol.for("react.transitional.element"),
141
- type: Component,
142
- props: {
143
- ...(await asyncRenderJSXToClientJSX(props, key ?? jsx.key)),
144
- key: key ?? jsx.key,
145
- },
148
+ type: jsx.type,
149
+ props: await asyncRenderJSXToClientJSX(jsx.props),
150
+ key: key ?? jsx.key,
146
151
  };
147
152
  } else {
148
- // Server component: execute and process
149
- const returnedJsx = await Component(props);
153
+ const innerComp = jsx.type.type;
154
+ const returnedJsx = typeof innerComp === "function" ? await innerComp(jsx.props) : null;
150
155
  return await asyncRenderJSXToClientJSX(returnedJsx, key ?? jsx.key);
151
156
  }
152
157
  } else {
@@ -172,6 +177,5 @@ async function asyncRenderJSXToClientJSX(jsx, key = null) {
172
177
  }
173
178
 
174
179
  module.exports = {
175
- renderJSXToClientJSX,
176
180
  asyncRenderJSXToClientJSX,
177
181
  };