Skip to content

Commit d2415bb

Browse files
committed
feat: server compile ssg
1 parent 90b3778 commit d2415bb

File tree

12 files changed

+143
-27
lines changed

12 files changed

+143
-27
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ coverage
1515
build
1616
dist
1717
.cache
18+
.temp
1819

1920
# misc
2021
.DS_Store

packages/react-render-ssg/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717
"react-dom": "17.0.2"
1818
},
1919
"devDependencies": {
20-
"@rspack/cli": "0.2.5",
2120
"@rollup/plugin-commonjs": "22.0.1",
2221
"@rollup/plugin-node-resolve": "13.3.0",
2322
"@rollup/plugin-replace": "5.0.4",
23+
"@rspack/cli": "0.2.5",
2424
"@types/react": "17.0.2",
2525
"@types/react-dom": "17.0.2",
2626
"anywhere": "1.6.0",
2727
"less": "4.2.0",
2828
"less-loader": "12.1.0",
29+
"null-loader": "^4.0.1",
2930
"rollup": "2.75.7",
3031
"rollup-plugin-postcss": "4.0.2",
3132
"rollup-plugin-typescript2": "0.32.1",

packages/react-render-ssg/public/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Template</title>
7+
<!-- INJECT STYLE -->
78
</head>
89
<body>
910
<div id="root">

packages/react-render-ssg/rspack.config.ts

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { Configuration } from "@rspack/cli";
22
import path from "path";
33

4-
const APP_NAME = "ReactSSG";
54
const isDev = process.env.NODE_ENV === "development";
65

76
const args = process.argv.slice(2);
@@ -12,10 +11,10 @@ const map = args.reduce((acc, arg) => {
1211
}, {} as Record<string, string>);
1312
const outputFileName = map["--output-filename"];
1413

15-
export default {
14+
const config: Configuration = {
1615
context: __dirname,
1716
entry: {
18-
index: "./src/rspack/app.tsx",
17+
index: "./.temp/client-side-entry.tsx",
1918
},
2019
externals: {
2120
"react": "React",
@@ -45,12 +44,6 @@ export default {
4544
style: false,
4645
},
4746
],
48-
banner: {
49-
banner: `ReactDOM.hydrate(React.createElement(${APP_NAME}.default), document.getElementById("root"));`,
50-
raw: true,
51-
footer: true,
52-
entryOnly: true,
53-
},
5447
},
5548
module: {
5649
rules: [
@@ -78,13 +71,18 @@ export default {
7871
output: {
7972
chunkLoading: "jsonp",
8073
chunkFormat: "array-push",
81-
library: { name: APP_NAME, type: "assign" },
8274
publicPath: isDev ? "" : "./",
8375
path: path.resolve(__dirname, "dist"),
84-
filename: isDev ? "[name].bundle.js" : `${outputFileName}.js` || "[name].[contenthash].js",
76+
filename: isDev
77+
? "[name].bundle.js"
78+
: outputFileName
79+
? `${outputFileName}.js`
80+
: "[name].[contenthash].js",
8581
chunkFilename: isDev ? "[name].chunk.js" : "[name].[contenthash].js",
8682
assetModuleFilename: isDev ? "[name].[ext]" : "[name].[contenthash].[ext]",
8783
},
88-
} as Configuration;
84+
};
85+
86+
export default config;
8987

9088
// https://www.rspack.dev/
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { Configuration } from "@rspack/cli";
2+
import path from "path";
3+
4+
const isDev = process.env.NODE_ENV === "development";
5+
6+
// eslint-disable-next-line @typescript-eslint/no-var-requires
7+
const packages = require("./package.json");
8+
const deps = { ...(packages.dependencies || {}), ...(packages.peerDependencies || {}) };
9+
const externals = Object.keys(deps).map(key => new RegExp(`(^${key}$)|(^${key}/.*)`));
10+
11+
const config: Configuration = {
12+
context: __dirname,
13+
entry: {
14+
index: "./src/rspack/app.tsx",
15+
},
16+
externals: externals,
17+
externalsType: "commonjs",
18+
externalsPresets: {
19+
node: true,
20+
},
21+
plugins: [],
22+
resolve: {
23+
alias: {
24+
"@": path.resolve(__dirname, "./src"),
25+
},
26+
},
27+
builtins: {
28+
define: {
29+
"__DEV__": JSON.stringify(isDev),
30+
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
31+
"process.env.PUBLIC_URL": JSON.stringify("."),
32+
},
33+
},
34+
module: {
35+
rules: [
36+
{ test: /\.svg$/, use: "null-loader" },
37+
{ test: /\.less$/, use: "null-loader" },
38+
],
39+
},
40+
devtool: false,
41+
output: {
42+
iife: false,
43+
libraryTarget: "commonjs",
44+
publicPath: isDev ? "" : "./",
45+
path: path.resolve(__dirname, ".temp"),
46+
filename: "node-side-entry.js",
47+
},
48+
};
49+
50+
export default config;
51+
52+
// https://www.rspack.dev/

packages/react-render-ssg/src/rollup/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ const exec = promisify(child.exec);
1414
const random = Math.random().toString(16).substring(7);
1515
const path = "./dist/";
1616
await exec(`npx rollup -c --file=${path + random}.js`);
17-
const jsPathName = `${random}.js`;
17+
const jsFileName = `${random}.js`;
1818
const html = template
1919
.replace(/<!-- INJECT HTML -->/, HTML)
20-
.replace(/<!-- INJECT SCRIPT -->/, `<script src="${jsPathName}"></script>`);
20+
.replace(/<!-- INJECT SCRIPT -->/, `<script src="${jsFileName}"></script>`);
2121
await fs.writeFile(`${path}index.html`, html);
2222
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
padding: 20px;
3+
}
+14-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1+
import "./app.less";
2+
3+
import { Button } from "@arco-design/web-react";
14
import React from "react";
25

3-
const App: React.FC = () => (
6+
const App: React.FC<{ name: string }> = props => (
47
<React.Fragment>
5-
<div>React Render SSG</div>
6-
<button onClick={() => alert("On Click")}>Button</button>
8+
<div>React Render SSG With {props.name}</div>
9+
<Button style={{ marginTop: 10 }} type="primary" onClick={() => alert("On Click")}>
10+
Button
11+
</Button>
712
</React.Fragment>
813
);
914

15+
export const getStaticProps = () => {
16+
return Promise.resolve({
17+
name: "Static Props",
18+
});
19+
};
20+
1021
export default App;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* eslint-disable @typescript-eslint/no-var-requires */
2+
3+
const Index = require(`<index placeholder>`);
4+
const props = JSON.parse(`<props placeholder>`);
5+
ReactDOM.hydrate(React.createElement(Index.default, { ...props }), document.getElementById("root"));
+33-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
1+
/* eslint-disable @typescript-eslint/no-var-requires */
12
import child from "child_process";
23
import fs from "fs/promises";
4+
import path from "path";
35
import React from "react";
46
import ReactDOMServer from "react-dom/server";
57
import { promisify } from "util";
68

7-
import App from "./app";
8-
99
const exec = promisify(child.exec);
1010

11+
const appPath = path.resolve(__dirname, "./app.tsx");
12+
const entryPath = path.resolve(__dirname, "./entry.tsx");
13+
require.extensions[".less"] = () => undefined;
14+
1115
(async () => {
12-
const HTML = ReactDOMServer.renderToString(React.createElement(App));
16+
const distPath = path.resolve("./dist");
17+
const tempPath = path.resolve("./.temp");
18+
await fs.mkdir(distPath, { recursive: true });
19+
await fs.mkdir(tempPath, { recursive: true });
20+
21+
await exec(`npx rspack -c ./rspack.server.ts`);
22+
const nodeSideAppPath = path.resolve(tempPath, "node-side-entry.js");
23+
const nodeSideApp = require(nodeSideAppPath);
24+
const App = nodeSideApp.default;
25+
const getStaticProps = nodeSideApp.getStaticProps;
26+
let defaultProps = {};
27+
if (getStaticProps) {
28+
defaultProps = await getStaticProps();
29+
}
30+
31+
const entry = await fs.readFile(entryPath, "utf-8");
32+
const tempEntry = entry
33+
.replace("<props placeholder>", JSON.stringify(defaultProps))
34+
.replace("<index placeholder>", appPath);
35+
await fs.writeFile(path.resolve(tempPath, "client-side-entry.tsx"), tempEntry);
36+
37+
const HTML = ReactDOMServer.renderToString(React.createElement(App, defaultProps));
1338
const template = await fs.readFile("./public/index.html", "utf-8");
1439
const random = Math.random().toString(16).substring(7);
15-
const path = "./dist/";
1640
await exec(`npx rspack build -- --output-filename=${random}`);
17-
const jsPathName = `${random}.js`;
41+
const jsFileName = `${random}.js`;
42+
1843
const html = template
1944
.replace(/<!-- INJECT HTML -->/, HTML)
20-
.replace(/<!-- INJECT SCRIPT -->/, `<script src="${jsPathName}"></script>`);
21-
await fs.writeFile(`${path}index.html`, html);
45+
.replace(/<!-- INJECT STYLE -->/, `<link rel="stylesheet" href="${random}.css">`)
46+
.replace(/<!-- INJECT SCRIPT -->/, `<script src="${jsFileName}"></script>`);
47+
await fs.writeFile(path.resolve(distPath, "index.html"), html);
2248
})();

packages/react-render-ssg/tsconfig.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
"include": [
44
"./src/**/*",
55
],
6+
"compilerOptions": {
7+
"module": "esnext"
8+
},
69
"ts-node": {
710
"compilerOptions": {
8-
"module": "commonjs"
11+
"module": "commonjs",
12+
"esModuleInterop": true
913
}
1014
}
1115
}

pnpm-lock.yaml

+15-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)