vite-react-ssg 0.0.2 → 0.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.
- package/README.md +124 -3
- package/dist/index.cjs +37 -2
- package/dist/index.d.ts +9 -3
- package/dist/index.mjs +44 -10
- package/dist/node/cli.cjs +29 -5
- package/dist/node/cli.mjs +28 -4
- package/dist/node.cjs +5 -2
- package/dist/node.d.ts +4 -2
- package/dist/node.mjs +3 -1
- package/dist/shared/{vite-react-ssg.f66e4ba5.mjs → vite-react-ssg.7ee042ba.mjs} +295 -62
- package/dist/shared/{vite-react-ssg.0e1b881e.cjs → vite-react-ssg.823b31c9.cjs} +295 -60
- package/dist/{types-82f34756.d.ts → types-25b96ed0.d.ts} +8 -0
- package/package.json +3 -1
|
@@ -7,6 +7,8 @@ const fs = require('fs-extra');
|
|
|
7
7
|
const vite = require('vite');
|
|
8
8
|
const JSDOM = require('jsdom');
|
|
9
9
|
const SiteMetadataDefaults = require('./vite-react-ssg.4ca822c0.cjs');
|
|
10
|
+
const express = require('express');
|
|
11
|
+
const node_fs = require('node:fs');
|
|
10
12
|
const React = require('react');
|
|
11
13
|
const reactHelmetAsync = require('react-helmet-async');
|
|
12
14
|
const server_js = require('react-router-dom/server.js');
|
|
@@ -16,6 +18,7 @@ const server = require('react-dom/server');
|
|
|
16
18
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
17
19
|
|
|
18
20
|
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
21
|
+
const express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
19
22
|
const React__default = /*#__PURE__*/_interopDefaultCompat(React);
|
|
20
23
|
|
|
21
24
|
function getDefaultExportFromCjs (x) {
|
|
@@ -875,9 +878,10 @@ function routesToPaths(routes) {
|
|
|
875
878
|
pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
|
|
876
879
|
}
|
|
877
880
|
if (!routes)
|
|
878
|
-
return { paths: ["/"] };
|
|
881
|
+
return { paths: ["/"], pathToEntry };
|
|
879
882
|
const paths = /* @__PURE__ */ new Set();
|
|
880
883
|
const getPaths = (routes2, prefix = "") => {
|
|
884
|
+
const parentPath = prefix;
|
|
881
885
|
prefix = prefix.replace(/\/$/g, "");
|
|
882
886
|
for (const route of routes2) {
|
|
883
887
|
let path = route.path;
|
|
@@ -885,6 +889,10 @@ function routesToPaths(routes) {
|
|
|
885
889
|
path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
|
|
886
890
|
paths.add(path);
|
|
887
891
|
addEntry(path, route.entry);
|
|
892
|
+
if (pathToEntry[parentPath]) {
|
|
893
|
+
const pathCopy = path;
|
|
894
|
+
pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
|
|
895
|
+
}
|
|
888
896
|
}
|
|
889
897
|
if (route.index)
|
|
890
898
|
addEntry(prefix, route.entry);
|
|
@@ -895,6 +903,39 @@ function routesToPaths(routes) {
|
|
|
895
903
|
getPaths(routes);
|
|
896
904
|
return { paths: Array.from(paths), pathToEntry };
|
|
897
905
|
}
|
|
906
|
+
function createFetchRequest(req) {
|
|
907
|
+
const origin = `${req.protocol}://${req.get("host")}`;
|
|
908
|
+
const url = new URL(req.originalUrl || req.url, origin);
|
|
909
|
+
const controller = new AbortController();
|
|
910
|
+
req.on("close", () => controller.abort());
|
|
911
|
+
const headers = new Headers();
|
|
912
|
+
for (const [key, values] of Object.entries(req.headers)) {
|
|
913
|
+
if (values) {
|
|
914
|
+
if (Array.isArray(values)) {
|
|
915
|
+
for (const value of values)
|
|
916
|
+
headers.append(key, value);
|
|
917
|
+
} else {
|
|
918
|
+
headers.set(key, values);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const init = {
|
|
923
|
+
method: req.method,
|
|
924
|
+
headers,
|
|
925
|
+
signal: controller.signal
|
|
926
|
+
};
|
|
927
|
+
if (req.method !== "GET" && req.method !== "HEAD")
|
|
928
|
+
init.body = req.body;
|
|
929
|
+
return new Request(url.href, init);
|
|
930
|
+
}
|
|
931
|
+
async function resolveAlias(config, entry) {
|
|
932
|
+
const resolver = config.createResolver();
|
|
933
|
+
const result = await resolver(entry, config.root);
|
|
934
|
+
return result || node_path.join(config.root, entry);
|
|
935
|
+
}
|
|
936
|
+
const { version } = JSON.parse(
|
|
937
|
+
node_fs.readFileSync(new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.823b31c9.cjs', document.baseURI).href)))).toString()
|
|
938
|
+
);
|
|
898
939
|
|
|
899
940
|
async function getCritters(outDir, options = {}) {
|
|
900
941
|
try {
|
|
@@ -965,17 +1006,18 @@ async function render(routes, request) {
|
|
|
965
1006
|
throw context;
|
|
966
1007
|
const router = server_js.createStaticRouter(dataRoutes, context);
|
|
967
1008
|
const app = /* @__PURE__ */ React__default.createElement(reactHelmetAsync.HelmetProvider, { context: helmetContext }, /* @__PURE__ */ React__default.createElement(SiteMetadataDefaults.SiteMetadataDefaults, null), /* @__PURE__ */ React__default.createElement(server_js.StaticRouterProvider, { router, context }));
|
|
968
|
-
const
|
|
1009
|
+
const appHTML = await renderStaticApp(app);
|
|
969
1010
|
const { helmet } = helmetContext;
|
|
970
|
-
helmet.htmlAttributes.toString();
|
|
971
|
-
helmet.bodyAttributes.toString();
|
|
972
|
-
[
|
|
1011
|
+
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
1012
|
+
const bodyAttributes = helmet.bodyAttributes.toString();
|
|
1013
|
+
const metaStrings = [
|
|
973
1014
|
helmet.title.toString(),
|
|
974
1015
|
helmet.meta.toString(),
|
|
975
1016
|
helmet.link.toString(),
|
|
976
1017
|
helmet.script.toString()
|
|
977
1018
|
];
|
|
978
|
-
|
|
1019
|
+
const metaAttributes = metaStrings.filter(Boolean);
|
|
1020
|
+
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
|
|
979
1021
|
}
|
|
980
1022
|
|
|
981
1023
|
function renderPreloadLinks(document, modules, ssrManifest) {
|
|
@@ -1011,7 +1053,7 @@ function renderPreloadLink(document, file) {
|
|
|
1011
1053
|
});
|
|
1012
1054
|
}
|
|
1013
1055
|
}
|
|
1014
|
-
function createLink(document) {
|
|
1056
|
+
function createLink$1(document) {
|
|
1015
1057
|
return document.createElement("link");
|
|
1016
1058
|
}
|
|
1017
1059
|
function setAttrs(el, attrs) {
|
|
@@ -1023,11 +1065,68 @@ function appendLink(document, attrs) {
|
|
|
1023
1065
|
const exits = document.head.querySelector(`link[href='${attrs.file}']`);
|
|
1024
1066
|
if (exits)
|
|
1025
1067
|
return;
|
|
1026
|
-
const link = createLink(document);
|
|
1068
|
+
const link = createLink$1(document);
|
|
1027
1069
|
setAttrs(link, attrs);
|
|
1028
1070
|
document.head.appendChild(link);
|
|
1029
1071
|
}
|
|
1030
1072
|
|
|
1073
|
+
async function renderHTML({
|
|
1074
|
+
rootContainerId,
|
|
1075
|
+
indexHTML,
|
|
1076
|
+
appHTML,
|
|
1077
|
+
metaAttributes,
|
|
1078
|
+
bodyAttributes,
|
|
1079
|
+
htmlAttributes,
|
|
1080
|
+
initialState
|
|
1081
|
+
}) {
|
|
1082
|
+
const stateScript = initialState ? `
|
|
1083
|
+
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1084
|
+
const headStartTag = "<head>";
|
|
1085
|
+
const metaTags = metaAttributes.join("");
|
|
1086
|
+
indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
|
|
1087
|
+
const bodyStartTag = "<body";
|
|
1088
|
+
indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
|
|
1089
|
+
const htmlStartTag = "<html";
|
|
1090
|
+
indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
|
|
1091
|
+
const container = `<div id="${rootContainerId}"></div>`;
|
|
1092
|
+
if (indexHTML.includes(container)) {
|
|
1093
|
+
return indexHTML.replace(
|
|
1094
|
+
container,
|
|
1095
|
+
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
const html5Parser = await import('html5parser');
|
|
1099
|
+
const ast = html5Parser.parse(indexHTML);
|
|
1100
|
+
let renderedOutput;
|
|
1101
|
+
html5Parser.walk(ast, {
|
|
1102
|
+
enter: (node) => {
|
|
1103
|
+
if (!renderedOutput && node?.type === html5Parser.SyntaxKind.Tag && Array.isArray(node.attributes) && node.attributes.length > 0 && node.attributes.some((attr) => attr.name.value === "id" && attr.value?.value === rootContainerId)) {
|
|
1104
|
+
const attributesStringified = [...node.attributes.map(({ name: { value: name }, value }) => `${name}="${value.value}"`)].join(" ");
|
|
1105
|
+
const indexHTMLBefore = indexHTML.slice(0, node.start);
|
|
1106
|
+
const indexHTMLAfter = indexHTML.slice(node.end);
|
|
1107
|
+
renderedOutput = `${indexHTMLBefore}<${node.name} ${attributesStringified} data-server-rendered="true">${appHTML}</${node.name}>${stateScript}${indexHTMLAfter}`;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
if (!renderedOutput)
|
|
1112
|
+
throw new Error(`Could not find a tag with id="${rootContainerId}" to replace it with server-side rendered HTML`);
|
|
1113
|
+
return renderedOutput;
|
|
1114
|
+
}
|
|
1115
|
+
async function detectEntry(root) {
|
|
1116
|
+
const scriptSrcReg = /<script(?:.*?)src=["'](.+?)["'](?!<)(?:.*)\>(?:[\n\r\s]*?)(?:<\/script>)/img;
|
|
1117
|
+
const html = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1118
|
+
const scripts = [...html.matchAll(scriptSrcReg)];
|
|
1119
|
+
const [, entry] = scripts.find((matchResult) => {
|
|
1120
|
+
const [script] = matchResult;
|
|
1121
|
+
const [, scriptType] = script.match(/.*\stype=(?:'|")?([^>'"\s]+)/i) || [];
|
|
1122
|
+
return scriptType === "module";
|
|
1123
|
+
}) || [];
|
|
1124
|
+
return entry || "src/main.ts";
|
|
1125
|
+
}
|
|
1126
|
+
function createLink(href) {
|
|
1127
|
+
return `<link rel="stylesheet" href="${href}">`;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1031
1130
|
function DefaultIncludedRoutes(paths, _routes) {
|
|
1032
1131
|
return paths.filter((i) => !i.includes(":") && !i.includes("*"));
|
|
1033
1132
|
}
|
|
@@ -1098,7 +1197,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1098
1197
|
const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
|
|
1099
1198
|
const ext = format === "esm" ? ".mjs" : ".cjs";
|
|
1100
1199
|
const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
|
|
1101
|
-
const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.
|
|
1200
|
+
const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.823b31c9.cjs', document.baseURI).href)));
|
|
1102
1201
|
const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
|
|
1103
1202
|
const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
|
|
1104
1203
|
const { routes } = await createRoot(false);
|
|
@@ -1119,18 +1218,21 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1119
1218
|
try {
|
|
1120
1219
|
const appCtx = await createRoot(false, route);
|
|
1121
1220
|
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState } = appCtx;
|
|
1122
|
-
const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx);
|
|
1221
|
+
const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
|
|
1123
1222
|
const url = new URL(route, "http://vite-react-ssg.com");
|
|
1124
1223
|
url.search = "";
|
|
1125
1224
|
url.hash = "";
|
|
1126
1225
|
url.pathname = route;
|
|
1127
1226
|
const request = new Request(url.href);
|
|
1128
|
-
const appHTML = await render([...routes2], request);
|
|
1227
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
|
|
1129
1228
|
await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
|
|
1130
1229
|
const renderedHTML = await renderHTML({
|
|
1131
1230
|
rootContainerId,
|
|
1132
1231
|
appHTML,
|
|
1133
|
-
indexHTML,
|
|
1232
|
+
indexHTML: transformedIndexHTML,
|
|
1233
|
+
metaAttributes,
|
|
1234
|
+
bodyAttributes,
|
|
1235
|
+
htmlAttributes,
|
|
1134
1236
|
initialState: null
|
|
1135
1237
|
});
|
|
1136
1238
|
const jsdom = new JSDOM.JSDOM(renderedHTML);
|
|
@@ -1171,59 +1273,11 @@ ${kolorist.gray("[vite-react-ssg]")} ${kolorist.green("Build finished.")}`);
|
|
|
1171
1273
|
}, waitInSeconds * 1e3);
|
|
1172
1274
|
timeout.unref();
|
|
1173
1275
|
}
|
|
1174
|
-
async function detectEntry(root) {
|
|
1175
|
-
const scriptSrcReg = /<script(?:.*?)src=["'](.+?)["'](?!<)(?:.*)\>(?:[\n\r\s]*?)(?:<\/script>)/img;
|
|
1176
|
-
const html = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1177
|
-
const scripts = [...html.matchAll(scriptSrcReg)];
|
|
1178
|
-
const [, entry] = scripts.find((matchResult) => {
|
|
1179
|
-
const [script] = matchResult;
|
|
1180
|
-
const [, scriptType] = script.match(/.*\stype=(?:'|")?([^>'"\s]+)/i) || [];
|
|
1181
|
-
return scriptType === "module";
|
|
1182
|
-
}) || [];
|
|
1183
|
-
return entry || "src/main.ts";
|
|
1184
|
-
}
|
|
1185
|
-
async function resolveAlias(config, entry) {
|
|
1186
|
-
const resolver = config.createResolver();
|
|
1187
|
-
const result = await resolver(entry, config.root);
|
|
1188
|
-
return result || node_path.join(config.root, entry);
|
|
1189
|
-
}
|
|
1190
1276
|
function rewriteScripts(indexHTML, mode) {
|
|
1191
1277
|
if (!mode || mode === "sync")
|
|
1192
1278
|
return indexHTML;
|
|
1193
1279
|
return indexHTML.replace(/<script type="module" /g, `<script type="module" ${mode} `);
|
|
1194
1280
|
}
|
|
1195
|
-
async function renderHTML({
|
|
1196
|
-
rootContainerId,
|
|
1197
|
-
indexHTML,
|
|
1198
|
-
appHTML,
|
|
1199
|
-
initialState
|
|
1200
|
-
}) {
|
|
1201
|
-
const stateScript = initialState ? `
|
|
1202
|
-
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1203
|
-
const container = `<div id="${rootContainerId}"></div>`;
|
|
1204
|
-
if (indexHTML.includes(container)) {
|
|
1205
|
-
return indexHTML.replace(
|
|
1206
|
-
container,
|
|
1207
|
-
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
const html5Parser = await import('html5parser');
|
|
1211
|
-
const ast = html5Parser.parse(indexHTML);
|
|
1212
|
-
let renderedOutput;
|
|
1213
|
-
html5Parser.walk(ast, {
|
|
1214
|
-
enter: (node) => {
|
|
1215
|
-
if (!renderedOutput && node?.type === html5Parser.SyntaxKind.Tag && Array.isArray(node.attributes) && node.attributes.length > 0 && node.attributes.some((attr) => attr.name.value === "id" && attr.value?.value === rootContainerId)) {
|
|
1216
|
-
const attributesStringified = [...node.attributes.map(({ name: { value: name }, value }) => `${name}="${value.value}"`)].join(" ");
|
|
1217
|
-
const indexHTMLBefore = indexHTML.slice(0, node.start);
|
|
1218
|
-
const indexHTMLAfter = indexHTML.slice(node.end);
|
|
1219
|
-
renderedOutput = `${indexHTMLBefore}<${node.name} ${attributesStringified} data-server-rendered="true">${appHTML}</${node.name}>${stateScript}${indexHTMLAfter}`;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
});
|
|
1223
|
-
if (!renderedOutput)
|
|
1224
|
-
throw new Error(`Could not find a tag with id="${rootContainerId}" to replace it with server-side rendered HTML`);
|
|
1225
|
-
return renderedOutput;
|
|
1226
|
-
}
|
|
1227
1281
|
async function formatHtml(html, formatting) {
|
|
1228
1282
|
if (formatting === "minify") {
|
|
1229
1283
|
const htmlMinifier = await import('html-minifier');
|
|
@@ -1259,4 +1313,185 @@ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
|
|
|
1259
1313
|
return mods;
|
|
1260
1314
|
}
|
|
1261
1315
|
|
|
1316
|
+
const SHORTCUTS = [
|
|
1317
|
+
{
|
|
1318
|
+
key: "u",
|
|
1319
|
+
description: "show server url",
|
|
1320
|
+
action(vite, _) {
|
|
1321
|
+
vite.config.logger.info("");
|
|
1322
|
+
printServerInfo(vite, true);
|
|
1323
|
+
}
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
key: "c",
|
|
1327
|
+
description: "clear console",
|
|
1328
|
+
action(vite, _) {
|
|
1329
|
+
vite.config.logger.clearScreen("error");
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
key: "q",
|
|
1334
|
+
description: "quit",
|
|
1335
|
+
action(_, server) {
|
|
1336
|
+
server.close(() => process.exit());
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
];
|
|
1340
|
+
function bindShortcuts(viteServer, server) {
|
|
1341
|
+
if (!process.stdin.isTTY || process.env.CI)
|
|
1342
|
+
return;
|
|
1343
|
+
viteServer.config.logger.info(
|
|
1344
|
+
kolorist.dim(kolorist.green(" \u279C")) + kolorist.dim(" press ") + kolorist.bold("h") + kolorist.dim(" to show help")
|
|
1345
|
+
);
|
|
1346
|
+
const shortcuts = SHORTCUTS;
|
|
1347
|
+
let actionRunning = false;
|
|
1348
|
+
const onInput = async (input) => {
|
|
1349
|
+
if (input === "" || input === "") {
|
|
1350
|
+
try {
|
|
1351
|
+
await server.close();
|
|
1352
|
+
} finally {
|
|
1353
|
+
process.exit(1);
|
|
1354
|
+
}
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
if (actionRunning)
|
|
1358
|
+
return;
|
|
1359
|
+
if (input === "h") {
|
|
1360
|
+
viteServer.config.logger.info(
|
|
1361
|
+
[
|
|
1362
|
+
"",
|
|
1363
|
+
kolorist.bold(" Shortcuts"),
|
|
1364
|
+
...shortcuts.map(
|
|
1365
|
+
(shortcut2) => kolorist.dim(" press ") + kolorist.bold(shortcut2.key) + kolorist.dim(` to ${shortcut2.description}`)
|
|
1366
|
+
)
|
|
1367
|
+
].join("\n")
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
const shortcut = shortcuts.find((shortcut2) => shortcut2.key === input);
|
|
1371
|
+
if (!shortcut)
|
|
1372
|
+
return;
|
|
1373
|
+
actionRunning = true;
|
|
1374
|
+
await shortcut.action(viteServer, server);
|
|
1375
|
+
actionRunning = false;
|
|
1376
|
+
};
|
|
1377
|
+
process.stdin.setRawMode(true);
|
|
1378
|
+
process.stdin.on("data", onInput).setEncoding("utf8").resume();
|
|
1379
|
+
server.on("close", () => {
|
|
1380
|
+
process.stdin.off("data", onInput).pause();
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
async function dev(ssgOptions = {}, viteConfig = {}) {
|
|
1385
|
+
const mode = process.env.MODE || process.env.NODE_ENV || ssgOptions.mode || "development";
|
|
1386
|
+
const config = await vite.resolveConfig(viteConfig, "serve", mode, mode);
|
|
1387
|
+
const cwd = process.cwd();
|
|
1388
|
+
const root = config.root || cwd;
|
|
1389
|
+
const {
|
|
1390
|
+
entry = await detectEntry(root),
|
|
1391
|
+
onBeforePageRender,
|
|
1392
|
+
onPageRendered,
|
|
1393
|
+
rootContainerId = "root"
|
|
1394
|
+
} = Object.assign({}, config.ssgOptions || {}, ssgOptions);
|
|
1395
|
+
const ssrEntry = await resolveAlias(config, entry);
|
|
1396
|
+
const template = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1397
|
+
let viteServer;
|
|
1398
|
+
globalThis.__ssr_start_time = performance.now();
|
|
1399
|
+
createServer().then((app) => {
|
|
1400
|
+
const port = viteServer.config.server.port || 5173;
|
|
1401
|
+
const server = app.listen(port, () => {
|
|
1402
|
+
printServerInfo(viteServer);
|
|
1403
|
+
bindShortcuts(viteServer, server);
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
async function createServer() {
|
|
1407
|
+
process.env.__DEV_MODE_SSR = "true";
|
|
1408
|
+
const app = express__default();
|
|
1409
|
+
viteServer = await vite.createServer({
|
|
1410
|
+
// ...options,
|
|
1411
|
+
server: { middlewareMode: true },
|
|
1412
|
+
appType: "custom"
|
|
1413
|
+
});
|
|
1414
|
+
app.use(viteServer.middlewares);
|
|
1415
|
+
app.use("*", async (req, res) => {
|
|
1416
|
+
try {
|
|
1417
|
+
const url = req.originalUrl;
|
|
1418
|
+
const indexHTML = await viteServer.transformIndexHtml(url, template);
|
|
1419
|
+
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1420
|
+
const appCtx = await createRoot(false, url);
|
|
1421
|
+
const { routes } = appCtx;
|
|
1422
|
+
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1423
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes], createFetchRequest(req));
|
|
1424
|
+
const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
|
|
1425
|
+
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1426
|
+
const collectAssets = async (mod2) => {
|
|
1427
|
+
if (!mod2?.ssrTransformResult)
|
|
1428
|
+
return;
|
|
1429
|
+
const { deps = [], dynamicDeps = [] } = mod2?.ssrTransformResult;
|
|
1430
|
+
const allDeps = [...deps, ...dynamicDeps];
|
|
1431
|
+
for (const dep of allDeps) {
|
|
1432
|
+
if (dep.endsWith(".css")) {
|
|
1433
|
+
assetsUrls.add(dep);
|
|
1434
|
+
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1435
|
+
const depModule = await viteServer.moduleGraph.getModuleByUrl(dep);
|
|
1436
|
+
depModule && await collectAssets(depModule);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
await collectAssets(mod);
|
|
1441
|
+
const preloadLink = [...assetsUrls].map((item) => createLink(item));
|
|
1442
|
+
metaAttributes.push(...preloadLink);
|
|
1443
|
+
const renderedHTML = await renderHTML({
|
|
1444
|
+
rootContainerId,
|
|
1445
|
+
appHTML,
|
|
1446
|
+
indexHTML: transformedIndexHTML,
|
|
1447
|
+
metaAttributes,
|
|
1448
|
+
bodyAttributes,
|
|
1449
|
+
htmlAttributes,
|
|
1450
|
+
initialState: null
|
|
1451
|
+
});
|
|
1452
|
+
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1453
|
+
res.status(200).set({ "Content-Type": "text/html" }).end(transformed);
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
viteServer.ssrFixStacktrace(e);
|
|
1456
|
+
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1457
|
+
res.status(500).end(e.stack);
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
return app;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
async function printServerInfo(server, onlyUrl = false) {
|
|
1464
|
+
const info = server.config.logger.info;
|
|
1465
|
+
const port = server.config.server.port || 5173;
|
|
1466
|
+
const url = `http://localhost:${port}/`;
|
|
1467
|
+
if (!onlyUrl) {
|
|
1468
|
+
let ssrReadyMessage = " -- SSR";
|
|
1469
|
+
if (globalThis.__ssr_start_time) {
|
|
1470
|
+
ssrReadyMessage += ` ready in ${kolorist.reset(kolorist.bold(`${Math.round(
|
|
1471
|
+
// @ts-expect-error global var
|
|
1472
|
+
performance.now() - globalThis.__ssr_start_time
|
|
1473
|
+
)}ms`))}`;
|
|
1474
|
+
}
|
|
1475
|
+
info(
|
|
1476
|
+
`
|
|
1477
|
+
${kolorist.bgLightCyan(` VITE-REACT-SSG v${version} `)}`,
|
|
1478
|
+
{ clear: !server.config.logger.hasWarned }
|
|
1479
|
+
);
|
|
1480
|
+
info(
|
|
1481
|
+
`${kolorist.cyan(`
|
|
1482
|
+
VITE v${vite.version}`) + kolorist.dim(ssrReadyMessage)}
|
|
1483
|
+
`
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
info(
|
|
1487
|
+
kolorist.green(" dev server running at:")
|
|
1488
|
+
);
|
|
1489
|
+
printUrls(url, info);
|
|
1490
|
+
}
|
|
1491
|
+
function printUrls(url, info) {
|
|
1492
|
+
const colorUrl = (url2) => kolorist.cyan(url2.replace(/:(\d+)\//, (_, port) => `:${kolorist.bold(port)}/`));
|
|
1493
|
+
info(` ${kolorist.green("\u279C")} ${kolorist.bold("Local")}: ${colorUrl(url)}`);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1262
1496
|
exports.build = build;
|
|
1497
|
+
exports.dev = dev;
|
|
@@ -126,6 +126,14 @@ interface ViteReactSSGClientOptions {
|
|
|
126
126
|
* @default `#root`
|
|
127
127
|
*/
|
|
128
128
|
rootContainer?: string | Element;
|
|
129
|
+
/**
|
|
130
|
+
* Use SSR during development.
|
|
131
|
+
*
|
|
132
|
+
* If false, the 'dev' script in 'package.json' needs to be set to `"vite"`.
|
|
133
|
+
*
|
|
134
|
+
* @default true
|
|
135
|
+
*/
|
|
136
|
+
ssrWhenDev?: boolean;
|
|
129
137
|
}
|
|
130
138
|
interface CommonRouteOptions {
|
|
131
139
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-react-ssg",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"packageManager": "pnpm@8.6.6",
|
|
6
6
|
"description": "",
|
|
7
7
|
"author": "Riri <Daydreamerriri@outlook.com>",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
+
"express": "^4.18.2",
|
|
78
79
|
"fs-extra": "^11.1.1",
|
|
79
80
|
"html-minifier": "^4.0.0",
|
|
80
81
|
"html5parser": "^2.0.2",
|
|
@@ -86,6 +87,7 @@
|
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
89
|
"@ririd/eslint-config": "0.6.0",
|
|
90
|
+
"@types/express": "^4.17.17",
|
|
89
91
|
"@types/fs-extra": "^11.0.1",
|
|
90
92
|
"@types/html-minifier": "^4.0.2",
|
|
91
93
|
"@types/jsdom": "^21.1.1",
|