Skip to content

Commit 858bc13

Browse files
committed
support generate static page
1 parent 8381400 commit 858bc13

File tree

19 files changed

+178
-66
lines changed

19 files changed

+178
-66
lines changed

global.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ declare global {
1818
__ENV__: {
1919
LANG: string;
2020
isSSR: boolean;
21-
isPure_CSR: boolean;
22-
CRYPTO_KEY: string;
23-
PUBLIC_API_HOST: string;
21+
isSTATIC: boolean;
22+
isPURE_CSR: boolean;
2423
isMIDDLEWARE: boolean;
2524
isDEVELOPMENT: boolean;
2625
isANIMATE_ROUTER: boolean;
26+
CRYPTO_KEY: string;
27+
PUBLIC_API_HOST: string;
2728
UI: "chakra";
2829
};
2930
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-ssr",
3-
"version": "3.0.1",
3+
"version": "3.0.2",
44
"author": "mrwang",
55
"license": "MIT",
66
"scripts": {

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@ yarn run start
7676

7777
## new ssr for react-18
7878

79-
## todo 静态生成...
79+
## 静态页面生成 build:static

script/free-port.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const freePort = (port) => {
3636
resolve();
3737
}
3838
} else {
39-
console.log(chalk.red("不支持的平台"));
39+
console.log(chalk.red("unSupport platform"));
4040
resolve();
4141
}
4242
});

script/generate.js

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
1-
const fs = require("fs");
2-
const http = require("http");
31
const { spawn } = require("child_process");
2+
const { freePort } = require("./free-port");
43

5-
const getDepsJsonFile = () => fs.promises.readFile("../dist/client/manifest-deps.json", { encoding: "utf-8" });
6-
7-
const startApp = () =>
8-
spawn("node", ["./dist/server/app.js"], {
9-
stdio: "inherit",
10-
shell: true,
11-
});
12-
13-
const getStaticRouter = (allRouters) =>
14-
Object.keys(allRouters).filter((path) => {
15-
const allPathArray = path.split("/");
16-
return allPathArray.every((p) => !p.startsWith(":"));
17-
});
18-
19-
const getCurrentStaticPage = (path) => http.get(path, (res) => {});
20-
21-
const runAllStaticRouter = async () => {
22-
// start app
23-
const app = startApp();
24-
const allRouter = await getDepsJsonFile();
25-
const allStaticRouter = getStaticRouter(allRouter);
26-
27-
// close app
28-
app.kill();
4+
const runStaticGenerate = async () => {
5+
await freePort(process.env.PROD_PORT);
6+
// start server and generate static page
7+
spawn("cross-env STATIC_GENERATE=true node", ["./dist/server/app.js"], { shell: true, stdio: "inherit" });
298
};
9+
10+
module.exports.runStaticGenerate = runStaticGenerate;

script/start-static

100644100755
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
#!/usr/bin/env node
22

3-
/* todo 静态生成支持 */
4-
53
require("dotenv").config();
64
const { withPromise } = require("./prod-entry");
5+
const { runStaticGenerate } = require("./generate");
76

87
process.env.NODE_ENV = "production";
98

10-
withPromise().then(() => {
11-
console.log("start static page generate...");
12-
});
9+
withPromise().then(runStaticGenerate);

src/client/entry.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ safeData(window.__PRELOAD_STORE_STATE__);
2929

3030
safeData(window as unknown as Record<string, unknown>, "__PRELOAD_STORE_STATE__");
3131

32-
if (window.__ENV__.isPure_CSR) {
32+
if (window.__ENV__.isPURE_CSR) {
3333
log("pure render by client", "warn");
3434
const { preLoadLang } = require("utils/preLoad");
3535
const root = createRoot(place);

src/server/api/index.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import fs from "fs";
22
import path from "path";
33

44
import { apiName } from "config/api";
5-
import { fail, success, wrapperMiddlewareRequest } from "server/middleware/apiHandler";
5+
import { catchMiddlewareHandler, compose, defaultRunRequestMiddleware, fail, success, wrapperMiddlewareRequest } from "server/middleware/apiHandler";
66
import { ServerError } from "server/utils/error";
7+
import { getIsStaticGenerate } from "utils/env";
78

89
import type { Request, Response, NextFunction } from "express";
910

@@ -16,21 +17,24 @@ const getFileExist = (resolvePath: string) => {
1617
});
1718
};
1819

19-
const getI18nFile = wrapperMiddlewareRequest({
20-
requestHandler: async function getI18nFile({ req, res }) {
21-
const { lang } = req.query;
22-
const relativePath = path.resolve(process.cwd(), "lang", `${lang}.json`);
23-
const isExist = await getFileExist(relativePath);
24-
if (isExist) {
25-
const content = await fs.promises.readFile(relativePath, { encoding: "utf-8" });
26-
success({ res, resDate: { data: { [lang as string]: JSON.parse(content) } } });
27-
} else {
28-
throw new ServerError(`${lang} 语言文件不存在`, 404);
29-
}
20+
const getI18nFile = wrapperMiddlewareRequest(
21+
{
22+
requestHandler: async function getI18nFile({ req, res }) {
23+
const { lang } = req.query;
24+
const relativePath = path.resolve(process.cwd(), "lang", `${lang}.json`);
25+
const isExist = await getFileExist(relativePath);
26+
if (isExist) {
27+
const content = await fs.promises.readFile(relativePath, { encoding: "utf-8" });
28+
success({ res, resDate: { data: { [lang as string]: JSON.parse(content) } } });
29+
} else {
30+
throw new ServerError(`${lang} 语言文件不存在`, 404);
31+
}
32+
},
33+
cacheConfig: { needCache: true },
34+
paramsConfig: { fromQuery: ["lang"] },
3035
},
31-
cacheConfig: { needCache: true },
32-
paramsConfig: { fromQuery: ["lang"] },
33-
});
36+
getIsStaticGenerate() ? compose(catchMiddlewareHandler, defaultRunRequestMiddleware) : undefined
37+
);
3438

3539
const actionObject: { [props: string]: typeof getI18nFile } = {
3640
[apiName.lang]: getI18nFile,

src/server/entry.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import express from "express";
55
import { catchMiddlewareHandler, compose, defaultRunRequestMiddleware, wrapperMiddlewareRequest } from "server/middleware/apiHandler";
66
import { develop } from "server/middleware/develop";
77
import { renderError } from "server/middleware/renderError";
8+
import { getIsStaticGenerate } from "utils/env";
89
import { log } from "utils/log";
910

1011
import { apiHandler } from "./api";
12+
import { generateStaticPage } from "./generator";
1113
import { init } from "./init";
1214
import { setUp } from "./setup";
15+
import { page } from "./static";
1316

1417
// eslint-disable-next-line import/no-named-as-default-member
1518
dotenv.config();
@@ -20,6 +23,8 @@ const port = __DEVELOPMENT__ ? process.env.DEV_PORT || 3000 : process.env.PROD_P
2023

2124
setUp(app);
2225

26+
page(app);
27+
2328
init(app);
2429

2530
app.use("/api", apiHandler);
@@ -50,6 +55,13 @@ if (__CSR__) {
5055
compose(catchMiddlewareHandler, defaultRunRequestMiddleware)
5156
)
5257
);
53-
app.listen(port, () => log(`App is running: http://localhost:${port}`, "warn"));
58+
app.listen(port, () => {
59+
log(`App is running: http://localhost:${port}`, "warn");
60+
if (getIsStaticGenerate()) {
61+
generateStaticPage().then(() => {
62+
process.exit(0);
63+
});
64+
}
65+
});
5466
});
5567
}

src/server/generator/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { manifestStaticPageFile } from "utils/manifest";
2+
3+
import {
4+
getAllStaticRouters,
5+
getPageManifestContent,
6+
generateStaticPage as _generateStaticPage,
7+
getFileNameFromPath,
8+
prepareOutputPath,
9+
writeContentToFilePath,
10+
getStaticPageOutputPath,
11+
} from "./util";
12+
13+
export const generateStaticPage = async () => {
14+
const allRouters = await getPageManifestContent();
15+
const allStaticRouters = getAllStaticRouters(allRouters);
16+
const allStaticRoutersPage = await Promise.all(allStaticRouters.map((pathConfig) => _generateStaticPage(pathConfig)));
17+
const allStaticRoutersPageWithFilePath = allStaticRoutersPage
18+
.filter((config) => config.rawData)
19+
.map((config) => ({
20+
...config,
21+
pathConfig: { ...config.pathConfig, ...getFileNameFromPath(config.pathConfig) },
22+
}))
23+
.map((config) => ({ ...config, pathConfig: { ...config.pathConfig, filePath: getStaticPageOutputPath(config.pathConfig.fileName) } }));
24+
const generateManifest = await Promise.all(
25+
allStaticRoutersPageWithFilePath.map((config) =>
26+
prepareOutputPath(config.pathConfig.filePath).then(() =>
27+
writeContentToFilePath(config.pathConfig.filePath, config.rawData!)
28+
.then(() => ({ config, state: true }))
29+
.catch(() => ({ config, state: false }))
30+
)
31+
)
32+
);
33+
34+
await writeContentToFilePath(
35+
manifestStaticPageFile("client"),
36+
JSON.stringify(
37+
generateManifest
38+
.filter((config) => config.state)
39+
.map((config) => ({ [config.config.pathConfig.p]: config.config.pathConfig.filePath }))
40+
.reduce((p, c) => ({ ...p, ...c }), {})
41+
)
42+
);
43+
};

0 commit comments

Comments
 (0)