dinou 4.0.15 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +150 -57
- package/dinou/core/babel-esm-loader.js +87 -5
- package/dinou/core/build-static-pages.js +17 -97
- package/dinou/core/generate-static-page.js +21 -3
- package/dinou/core/generate-static-pages.js +30 -41
- package/dinou/core/generate-static-rsc.js +11 -5
- package/dinou/core/generate-static-rscs.js +11 -5
- package/dinou/core/generate-static.js +2 -3
- package/dinou/core/get-jsx.js +2 -1
- package/dinou/core/get-ssg-metadata.js +5 -13
- package/dinou/core/import-module.js +25 -6
- package/dinou/core/redirect.jsx +18 -11
- package/dinou/core/register-loader.mjs +1 -0
- package/dinou/core/render-app-to-html.js +209 -35
- package/dinou/core/render-html.js +144 -26
- package/dinou/core/render-jsx-to-client-jsx.js +6 -7
- package/dinou/core/request-context.js +11 -6
- package/dinou/core/revalidating.js +29 -25
- package/dinou/core/server.js +279 -88
- package/dinou/core/url-resolver.js +3 -0
- package/dinou/esbuild/dev.mjs +31 -2
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +0 -40
- package/dinou/index.d.ts +1 -1
- package/dinou/index.mjs +31 -10
- package/dinou/package.json +1 -1
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +88 -49
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +7 -9
- package/dinou/rollup/rollup.config.js +1 -2
- package/dinou/webpack/helpers/get-webpack-entries.js +0 -40
- package/package.json +3 -3
- package/dinou/core/babel-plugin-register-imports.js +0 -50
- package/dinou/core/get-ssg-jsx-or-jsx.js +0 -14
- package/dinou/core/get-ssg-jsx.js +0 -83
- package/dinou/core/jsx-json.js +0 -55
- package/dinou/core/register-hooks.js +0 -18
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
|
-
const { mkdirSync, createWriteStream } = require("fs");
|
|
2
|
+
const { mkdirSync, createWriteStream, existsSync } = require("fs");
|
|
3
3
|
const fs = require("fs").promises;
|
|
4
4
|
const renderAppToHtml = require("./render-app-to-html.js");
|
|
5
|
-
const
|
|
5
|
+
const { getStaticMetadata } = require("./build-static-pages.js");
|
|
6
|
+
const { processMetadata } = require("./get-ssg-metadata.js");
|
|
6
7
|
|
|
7
8
|
const OUT_DIR = path.resolve("dist2");
|
|
8
9
|
|
|
@@ -64,7 +65,11 @@ async function generateStaticPage(reqPath) {
|
|
|
64
65
|
capturedStatus
|
|
65
66
|
);
|
|
66
67
|
|
|
67
|
-
const
|
|
68
|
+
const metadata = getStaticMetadata(finalReqPath);
|
|
69
|
+
let sideEffectScripts = "";
|
|
70
|
+
if (metadata && metadata.effects) {
|
|
71
|
+
sideEffectScripts = processMetadata(metadata.effects);
|
|
72
|
+
}
|
|
68
73
|
|
|
69
74
|
await new Promise((resolve, reject) => {
|
|
70
75
|
if (sideEffectScripts) fileStream.write(sideEffectScripts);
|
|
@@ -81,6 +86,19 @@ async function generateStaticPage(reqPath) {
|
|
|
81
86
|
fileStream.on("error", reject);
|
|
82
87
|
});
|
|
83
88
|
|
|
89
|
+
if (metadata) {
|
|
90
|
+
const metadataPath = path.join(OUT_DIR, finalReqPath, "metadata.json");
|
|
91
|
+
await fs.writeFile(
|
|
92
|
+
metadataPath,
|
|
93
|
+
JSON.stringify({
|
|
94
|
+
revalidate: metadata.revalidate,
|
|
95
|
+
generatedAt: Date.now(),
|
|
96
|
+
effects: metadata.effects,
|
|
97
|
+
}, null, 2),
|
|
98
|
+
"utf8"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
84
102
|
const status = capturedStatus.value || 200;
|
|
85
103
|
const success = status !== 500;
|
|
86
104
|
|
|
@@ -1,80 +1,57 @@
|
|
|
1
1
|
// generate-static-pages.js
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { mkdirSync, createWriteStream } = require("fs");
|
|
4
|
+
const fs = require("fs").promises;
|
|
4
5
|
const renderAppToHtml = require("./render-app-to-html.js");
|
|
5
|
-
const
|
|
6
|
+
const { getStaticMetadata } = require("./build-static-pages.js");
|
|
7
|
+
const { processMetadata } = require("./get-ssg-metadata.js");
|
|
6
8
|
const { updateStatus } = require("./status-manifest.js");
|
|
7
9
|
|
|
8
10
|
const OUT_DIR = path.resolve("dist2");
|
|
9
11
|
|
|
10
12
|
async function generateStaticPages(routes) {
|
|
11
13
|
for (const route of routes) {
|
|
12
|
-
// Normalization of the route
|
|
13
14
|
const reqPath = route.endsWith("/") ? route : route + "/";
|
|
14
15
|
const htmlPath = path.join(OUT_DIR, reqPath, "index.html");
|
|
15
16
|
|
|
16
|
-
// Prepare Query params (empty by default in SSG, unless your router supports it)
|
|
17
17
|
const query = {};
|
|
18
18
|
const paramsString = JSON.stringify(query);
|
|
19
19
|
const capturedStatus = {};
|
|
20
|
-
// ---------------------------------------------------------
|
|
21
|
-
// 1. CREATE MOCK REQUEST (Solution to your second question)
|
|
22
|
-
// ---------------------------------------------------------
|
|
23
20
|
const contextForChild = {
|
|
24
21
|
req: {
|
|
25
22
|
query,
|
|
26
|
-
cookies: {},
|
|
23
|
+
cookies: {},
|
|
27
24
|
headers: {
|
|
28
25
|
"user-agent": "Dinou-SSG-Builder",
|
|
29
|
-
host: "localhost",
|
|
26
|
+
host: "localhost",
|
|
30
27
|
"x-forwarded-proto": "http",
|
|
31
28
|
},
|
|
32
|
-
path: reqPath,
|
|
29
|
+
path: reqPath,
|
|
33
30
|
method: "GET",
|
|
34
31
|
},
|
|
35
|
-
// We do not pass 'res' here, the child ignores it, we pass the mockRes as an argument
|
|
36
32
|
};
|
|
37
33
|
|
|
38
34
|
try {
|
|
39
35
|
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
40
36
|
const fileStream = createWriteStream(htmlPath);
|
|
41
|
-
let htmlStream = null;
|
|
37
|
+
let htmlStream = null;
|
|
42
38
|
|
|
43
|
-
// ---------------------------------------------------------
|
|
44
|
-
// 2. CREATE MOCK RESPONSE (With Fix for Webpack)
|
|
45
|
-
// ---------------------------------------------------------
|
|
46
39
|
const mockRes = {
|
|
47
|
-
headersSent: true,
|
|
48
|
-
_cookies: [],
|
|
49
|
-
|
|
50
|
-
// 👇 ADD THIS METHOD
|
|
40
|
+
headersSent: true,
|
|
41
|
+
_cookies: [],
|
|
51
42
|
cookie(name, value, options) {
|
|
52
|
-
// In SSG we don't do anything real, but we save a record if you want to debug
|
|
53
|
-
// console.log(`[SSG] Cookie set ignored: ${name}=${value}`);
|
|
54
43
|
this._cookies.push({ name, value, options });
|
|
55
44
|
},
|
|
56
|
-
|
|
57
45
|
write: (chunk) => {
|
|
58
46
|
if (!fileStream.writableEnded) fileStream.write(chunk);
|
|
59
47
|
},
|
|
60
|
-
|
|
61
48
|
end: (chunk) => {
|
|
62
|
-
// If they send us a last chunk (e.g., </script>)
|
|
63
49
|
if (chunk && !fileStream.writableEnded) fileStream.write(chunk);
|
|
64
|
-
|
|
65
|
-
// 🔥 WEBPACK "WRITE AFTER END" FIX:
|
|
66
|
-
// Disconnect the child's stream immediately.
|
|
67
|
-
// If Webpack sends garbage afterwards, it won't reach the fileStream.
|
|
68
50
|
if (htmlStream) {
|
|
69
51
|
htmlStream.unpipe(fileStream);
|
|
70
|
-
// Optional: Pause or destroy the child's stream to free memory
|
|
71
|
-
// htmlStream.destroy();
|
|
72
52
|
}
|
|
73
|
-
|
|
74
|
-
// Officially close the file
|
|
75
53
|
if (!fileStream.writableEnded) fileStream.end();
|
|
76
54
|
},
|
|
77
|
-
|
|
78
55
|
status: (code) => {
|
|
79
56
|
if (code !== 200)
|
|
80
57
|
console.warn(`[SSG] Status ${code} ignored for ${reqPath}`);
|
|
@@ -85,25 +62,26 @@ async function generateStaticPages(routes) {
|
|
|
85
62
|
redirect: () => {},
|
|
86
63
|
};
|
|
87
64
|
|
|
88
|
-
// 3. Execute Render
|
|
89
65
|
htmlStream = renderAppToHtml(
|
|
90
66
|
reqPath,
|
|
91
67
|
paramsString,
|
|
92
|
-
contextForChild,
|
|
68
|
+
contextForChild,
|
|
93
69
|
mockRes,
|
|
94
70
|
capturedStatus
|
|
95
71
|
);
|
|
96
72
|
|
|
97
|
-
const
|
|
73
|
+
const metadata = getStaticMetadata(reqPath);
|
|
74
|
+
let sideEffectScripts = "";
|
|
75
|
+
if (metadata && metadata.effects) {
|
|
76
|
+
sideEffectScripts = processMetadata(metadata.effects);
|
|
77
|
+
}
|
|
78
|
+
|
|
98
79
|
await new Promise((resolve, reject) => {
|
|
99
|
-
// 🟢 SCRIPT INJECTION
|
|
100
80
|
if (sideEffectScripts) {
|
|
101
81
|
fileStream.write(sideEffectScripts);
|
|
102
82
|
}
|
|
103
|
-
|
|
104
|
-
htmlStream.pipe(fileStream, { end: false }); // end: false gives us manual control in the mockRes
|
|
83
|
+
htmlStream.pipe(fileStream, { end: false });
|
|
105
84
|
|
|
106
|
-
// Event handling
|
|
107
85
|
htmlStream.on("end", () => {
|
|
108
86
|
if (!fileStream.writableEnded) fileStream.end();
|
|
109
87
|
updateStatus(reqPath, capturedStatus.value);
|
|
@@ -111,7 +89,6 @@ async function generateStaticPages(routes) {
|
|
|
111
89
|
});
|
|
112
90
|
|
|
113
91
|
htmlStream.on("error", (err) => {
|
|
114
|
-
// If the error is "write after end" and we already closed, ignore it (it's noise)
|
|
115
92
|
if (err.code === "ERR_STREAM_WRITE_AFTER_END") {
|
|
116
93
|
resolve();
|
|
117
94
|
} else {
|
|
@@ -122,9 +99,21 @@ async function generateStaticPages(routes) {
|
|
|
122
99
|
fileStream.on("error", reject);
|
|
123
100
|
});
|
|
124
101
|
|
|
102
|
+
if (metadata) {
|
|
103
|
+
const metadataPath = path.join(OUT_DIR, reqPath, "metadata.json");
|
|
104
|
+
await fs.writeFile(
|
|
105
|
+
metadataPath,
|
|
106
|
+
JSON.stringify({
|
|
107
|
+
revalidate: metadata.revalidate,
|
|
108
|
+
generatedAt: Date.now(),
|
|
109
|
+
effects: metadata.effects,
|
|
110
|
+
}, null, 2),
|
|
111
|
+
"utf8"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
125
115
|
console.log("✅ Generated HTML:", reqPath);
|
|
126
116
|
} catch (error) {
|
|
127
|
-
// Extra safety filter in case the error bubbles up here
|
|
128
117
|
if (error.code === "ERR_STREAM_WRITE_AFTER_END") {
|
|
129
118
|
console.log("⚠️ Ignored write-after-end race condition for:", reqPath);
|
|
130
119
|
} else {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { PassThrough } = require("stream");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const url = require("url");
|
|
5
|
+
const getJSX = require("./get-jsx.js");
|
|
6
|
+
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
7
|
+
const { renderToPipeableStream } = isWebpack
|
|
8
|
+
? require("react-server-dom-webpack/server")
|
|
9
|
+
: require("@roggc/react-server-dom-esm/server");
|
|
6
10
|
const { requestStorage } = require("./request-context.js");
|
|
7
11
|
|
|
8
12
|
const OUT_DIR = path.resolve("dist2");
|
|
9
|
-
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
13
|
+
// const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
10
14
|
|
|
11
15
|
async function generateStaticRSC(reqPath) {
|
|
12
16
|
const finalReqPath = reqPath.endsWith("/") ? reqPath : reqPath + "/";
|
|
@@ -94,8 +98,10 @@ async function generateStaticRSC(reqPath) {
|
|
|
94
98
|
const passThrough = new PassThrough();
|
|
95
99
|
|
|
96
100
|
await requestStorage.run(mockContext, async () => {
|
|
97
|
-
const jsx = await
|
|
98
|
-
const { pipe } =
|
|
101
|
+
const jsx = await getJSX(finalReqPath, {}, null, false);
|
|
102
|
+
const { pipe } = isWebpack
|
|
103
|
+
? renderToPipeableStream(jsx, manifest)
|
|
104
|
+
: renderToPipeableStream(jsx, url.pathToFileURL(process.cwd()).href + "/");
|
|
99
105
|
pipe(passThrough);
|
|
100
106
|
passThrough.pipe(fileStream);
|
|
101
107
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { PassThrough } = require("stream");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const url = require("url");
|
|
5
|
+
const getJSX = require("./get-jsx.js");
|
|
6
|
+
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
7
|
+
const { renderToPipeableStream } = isWebpack
|
|
8
|
+
? require("react-server-dom-webpack/server")
|
|
9
|
+
: require("@roggc/react-server-dom-esm/server");
|
|
6
10
|
|
|
7
11
|
// 👇 Your context storage
|
|
8
12
|
const { requestStorage } = require("./request-context.js");
|
|
9
13
|
|
|
10
14
|
const OUT_DIR = path.resolve("dist2");
|
|
11
|
-
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
15
|
+
// const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
12
16
|
|
|
13
17
|
async function generateStaticRSCs(routes) {
|
|
14
18
|
const manifest = JSON.parse(
|
|
@@ -110,8 +114,10 @@ async function generateStaticRSCs(routes) {
|
|
|
110
114
|
|
|
111
115
|
// 1. The entire lifecycle of the stream must be inside the storage
|
|
112
116
|
await requestStorage.run(mockContext, async () => {
|
|
113
|
-
const jsx = await
|
|
114
|
-
const { pipe } =
|
|
117
|
+
const jsx = await getJSX(reqPath, {}, null, false);
|
|
118
|
+
const { pipe } = isWebpack
|
|
119
|
+
? renderToPipeableStream(jsx, manifest)
|
|
120
|
+
: renderToPipeableStream(jsx, url.pathToFileURL(process.cwd()).href + "/");
|
|
115
121
|
|
|
116
122
|
pipe(passThrough);
|
|
117
123
|
passThrough.pipe(fileStream);
|
|
@@ -2,8 +2,7 @@ const path = require("path");
|
|
|
2
2
|
const { existsSync, rmSync } = require("fs");
|
|
3
3
|
const generateStaticRSCs = require("./generate-static-rscs");
|
|
4
4
|
const generateStaticPages = require("./generate-static-pages");
|
|
5
|
-
const { getStaticPaths } = require("./
|
|
6
|
-
const { buildStaticPages } = require("./build-static-pages");
|
|
5
|
+
const { buildStaticPages, getStaticPaths } = require("./build-static-pages");
|
|
7
6
|
|
|
8
7
|
async function generateStatic() {
|
|
9
8
|
const distFolder2 = path.resolve(process.cwd(), "dist2");
|
|
@@ -16,8 +15,8 @@ async function generateStatic() {
|
|
|
16
15
|
await buildStaticPages();
|
|
17
16
|
const routes = getStaticPaths();
|
|
18
17
|
console.log("Static paths:", routes);
|
|
19
|
-
await generateStaticPages(routes);
|
|
20
18
|
await generateStaticRSCs(routes);
|
|
19
|
+
await generateStaticPages(routes);
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
module.exports = generateStatic;
|
package/dinou/core/get-jsx.js
CHANGED
|
@@ -12,6 +12,7 @@ async function getJSX(
|
|
|
12
12
|
query,
|
|
13
13
|
isNotFound = null,
|
|
14
14
|
isDevelopment = false,
|
|
15
|
+
forceNotFound = false,
|
|
15
16
|
) {
|
|
16
17
|
const srcFolder = path.resolve(process.cwd(), "src");
|
|
17
18
|
const reqSegments = reqPath.split("/").filter(Boolean);
|
|
@@ -52,7 +53,7 @@ async function getJSX(
|
|
|
52
53
|
let jsx;
|
|
53
54
|
let pageFunctionsProps;
|
|
54
55
|
|
|
55
|
-
if (!pagePath) {
|
|
56
|
+
if (!pagePath || forceNotFound) {
|
|
56
57
|
if (isNotFound) isNotFound.value = true;
|
|
57
58
|
const [notFoundPath, dParams] = getFilePathAndDynamicParams(
|
|
58
59
|
reqSegments,
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
function processMetadata(effects) {
|
|
2
|
+
if (!effects) return "";
|
|
2
3
|
|
|
3
|
-
function processMetadata(metadata) {
|
|
4
|
-
if (!metadata || !metadata.effects) return "";
|
|
5
|
-
|
|
6
|
-
const { effects } = metadata;
|
|
7
4
|
let scriptContent = "";
|
|
8
5
|
|
|
9
6
|
if (effects.cookies && effects.cookies.length > 0) {
|
|
@@ -29,11 +26,6 @@ function processMetadata(metadata) {
|
|
|
29
26
|
return `<script>(function(){ ${scriptContent} })();</script>`;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const script = processMetadata(metadata);
|
|
36
|
-
return script;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = getSSGMetadata;
|
|
29
|
+
module.exports = {
|
|
30
|
+
processMetadata,
|
|
31
|
+
};
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
const { pathToFileURL } = require("url");
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
3
4
|
|
|
4
5
|
async function importModule(modulePath) {
|
|
6
|
+
const absPath = path.isAbsolute(modulePath)
|
|
7
|
+
? modulePath
|
|
8
|
+
: path.resolve(process.cwd(), modulePath);
|
|
9
|
+
|
|
10
|
+
if (!isWebpack) {
|
|
11
|
+
let fileUrl = pathToFileURL(absPath).href;
|
|
12
|
+
if (process.env.NODE_ENV !== "production") {
|
|
13
|
+
fileUrl += `?t=${Date.now()}`;
|
|
14
|
+
}
|
|
15
|
+
const mod = await import(fileUrl);
|
|
16
|
+
return mod;
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
try {
|
|
6
|
-
|
|
20
|
+
if (process.env.NODE_ENV !== "production") {
|
|
21
|
+
try {
|
|
22
|
+
const resolved = require.resolve(absPath);
|
|
23
|
+
delete require.cache[resolved];
|
|
24
|
+
} catch (e) {}
|
|
25
|
+
}
|
|
26
|
+
return require(absPath);
|
|
7
27
|
} catch (err) {
|
|
8
28
|
if (
|
|
9
29
|
err.code === "ERR_REQUIRE_ESM" ||
|
|
10
30
|
/require\(\) of ES Module/.test(err.message)
|
|
11
31
|
) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const fileUrl = pathToFileURL(absPath).href;
|
|
32
|
+
let fileUrl = pathToFileURL(absPath).href;
|
|
33
|
+
if (process.env.NODE_ENV !== "production") {
|
|
34
|
+
fileUrl += `?t=${Date.now()}`;
|
|
35
|
+
}
|
|
17
36
|
const mod = await import(fileUrl);
|
|
18
37
|
return mod;
|
|
19
38
|
}
|
package/dinou/core/redirect.jsx
CHANGED
|
@@ -8,18 +8,25 @@ export function redirect(destination) {
|
|
|
8
8
|
// 1. We try to get the server context
|
|
9
9
|
if (typeof window === "undefined") {
|
|
10
10
|
// getContext() must be accessible here
|
|
11
|
-
const dynamicRequire =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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();
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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} />;
|
|
29
|
+
}
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|