vite-react-ssg 0.0.3 → 0.1.1
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/LICENSE +21 -21
- package/README.md +307 -238
- package/bin/vite-react-ssg.js +3 -3
- package/dist/chunks/jsdomGlobal.cjs +79 -79
- package/dist/chunks/jsdomGlobal.mjs +79 -79
- package/dist/index.cjs +103 -4
- package/dist/index.d.ts +14 -5
- package/dist/index.mjs +108 -12
- 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 +5 -2
- package/dist/node.mjs +3 -1
- package/dist/shared/{vite-react-ssg.524ba00d.mjs → vite-react-ssg.5291bf32.mjs} +289 -69
- package/dist/shared/{vite-react-ssg.a5dfecac.cjs → vite-react-ssg.8cbf43cc.cjs} +289 -67
- package/dist/style-collectors.cjs +20 -0
- package/dist/style-collectors.d.ts +10 -0
- package/dist/style-collectors.mjs +18 -0
- package/dist/{types-82f34756.d.ts → types-408e974a.d.ts} +17 -1
- package/package.json +17 -2
|
@@ -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) {
|
|
@@ -900,6 +903,39 @@ function routesToPaths(routes) {
|
|
|
900
903
|
getPaths(routes);
|
|
901
904
|
return { paths: Array.from(paths), pathToEntry };
|
|
902
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.8cbf43cc.cjs', document.baseURI).href)))).toString()
|
|
938
|
+
);
|
|
903
939
|
|
|
904
940
|
async function getCritters(outDir, options = {}) {
|
|
905
941
|
try {
|
|
@@ -962,14 +998,16 @@ class WritableAsPromise extends node_stream.Writable {
|
|
|
962
998
|
}
|
|
963
999
|
}
|
|
964
1000
|
|
|
965
|
-
async function render(routes, request) {
|
|
1001
|
+
async function render(routes, request, styleCollector) {
|
|
966
1002
|
const { dataRoutes, query } = server_js.createStaticHandler(routes);
|
|
967
1003
|
const context = await query(request);
|
|
968
1004
|
const helmetContext = {};
|
|
969
1005
|
if (context instanceof Response)
|
|
970
1006
|
throw context;
|
|
971
1007
|
const router = server_js.createStaticRouter(dataRoutes, context);
|
|
972
|
-
|
|
1008
|
+
let 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 }));
|
|
1009
|
+
if (styleCollector)
|
|
1010
|
+
app = styleCollector.collect(app);
|
|
973
1011
|
const appHTML = await renderStaticApp(app);
|
|
974
1012
|
const { helmet } = helmetContext;
|
|
975
1013
|
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
@@ -980,6 +1018,8 @@ async function render(routes, request) {
|
|
|
980
1018
|
helmet.link.toString(),
|
|
981
1019
|
helmet.script.toString()
|
|
982
1020
|
];
|
|
1021
|
+
const styleTag = styleCollector?.toString?.(appHTML) ?? "";
|
|
1022
|
+
metaStrings.push(styleTag);
|
|
983
1023
|
const metaAttributes = metaStrings.filter(Boolean);
|
|
984
1024
|
return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
|
|
985
1025
|
}
|
|
@@ -1017,7 +1057,7 @@ function renderPreloadLink(document, file) {
|
|
|
1017
1057
|
});
|
|
1018
1058
|
}
|
|
1019
1059
|
}
|
|
1020
|
-
function createLink(document) {
|
|
1060
|
+
function createLink$1(document) {
|
|
1021
1061
|
return document.createElement("link");
|
|
1022
1062
|
}
|
|
1023
1063
|
function setAttrs(el, attrs) {
|
|
@@ -1029,11 +1069,68 @@ function appendLink(document, attrs) {
|
|
|
1029
1069
|
const exits = document.head.querySelector(`link[href='${attrs.file}']`);
|
|
1030
1070
|
if (exits)
|
|
1031
1071
|
return;
|
|
1032
|
-
const link = createLink(document);
|
|
1072
|
+
const link = createLink$1(document);
|
|
1033
1073
|
setAttrs(link, attrs);
|
|
1034
1074
|
document.head.appendChild(link);
|
|
1035
1075
|
}
|
|
1036
1076
|
|
|
1077
|
+
async function renderHTML({
|
|
1078
|
+
rootContainerId,
|
|
1079
|
+
indexHTML,
|
|
1080
|
+
appHTML,
|
|
1081
|
+
metaAttributes,
|
|
1082
|
+
bodyAttributes,
|
|
1083
|
+
htmlAttributes,
|
|
1084
|
+
initialState
|
|
1085
|
+
}) {
|
|
1086
|
+
const stateScript = initialState ? `
|
|
1087
|
+
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1088
|
+
const headStartTag = "<head>";
|
|
1089
|
+
const metaTags = metaAttributes.join("");
|
|
1090
|
+
indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
|
|
1091
|
+
const bodyStartTag = "<body";
|
|
1092
|
+
indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
|
|
1093
|
+
const htmlStartTag = "<html";
|
|
1094
|
+
indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
|
|
1095
|
+
const container = `<div id="${rootContainerId}"></div>`;
|
|
1096
|
+
if (indexHTML.includes(container)) {
|
|
1097
|
+
return indexHTML.replace(
|
|
1098
|
+
container,
|
|
1099
|
+
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
const html5Parser = await import('html5parser');
|
|
1103
|
+
const ast = html5Parser.parse(indexHTML);
|
|
1104
|
+
let renderedOutput;
|
|
1105
|
+
html5Parser.walk(ast, {
|
|
1106
|
+
enter: (node) => {
|
|
1107
|
+
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)) {
|
|
1108
|
+
const attributesStringified = [...node.attributes.map(({ name: { value: name }, value }) => `${name}="${value.value}"`)].join(" ");
|
|
1109
|
+
const indexHTMLBefore = indexHTML.slice(0, node.start);
|
|
1110
|
+
const indexHTMLAfter = indexHTML.slice(node.end);
|
|
1111
|
+
renderedOutput = `${indexHTMLBefore}<${node.name} ${attributesStringified} data-server-rendered="true">${appHTML}</${node.name}>${stateScript}${indexHTMLAfter}`;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
if (!renderedOutput)
|
|
1116
|
+
throw new Error(`Could not find a tag with id="${rootContainerId}" to replace it with server-side rendered HTML`);
|
|
1117
|
+
return renderedOutput;
|
|
1118
|
+
}
|
|
1119
|
+
async function detectEntry(root) {
|
|
1120
|
+
const scriptSrcReg = /<script(?:.*?)src=["'](.+?)["'](?!<)(?:.*)\>(?:[\n\r\s]*?)(?:<\/script>)/img;
|
|
1121
|
+
const html = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1122
|
+
const scripts = [...html.matchAll(scriptSrcReg)];
|
|
1123
|
+
const [, entry] = scripts.find((matchResult) => {
|
|
1124
|
+
const [script] = matchResult;
|
|
1125
|
+
const [, scriptType] = script.match(/.*\stype=(?:'|")?([^>'"\s]+)/i) || [];
|
|
1126
|
+
return scriptType === "module";
|
|
1127
|
+
}) || [];
|
|
1128
|
+
return entry || "src/main.ts";
|
|
1129
|
+
}
|
|
1130
|
+
function createLink(href) {
|
|
1131
|
+
return `<link rel="stylesheet" href="${href}">`;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1037
1134
|
function DefaultIncludedRoutes(paths, _routes) {
|
|
1038
1135
|
return paths.filter((i) => !i.includes(":") && !i.includes("*"));
|
|
1039
1136
|
}
|
|
@@ -1104,7 +1201,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1104
1201
|
const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
|
|
1105
1202
|
const ext = format === "esm" ? ".mjs" : ".cjs";
|
|
1106
1203
|
const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
|
|
1107
|
-
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.
|
|
1204
|
+
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.8cbf43cc.cjs', document.baseURI).href)));
|
|
1108
1205
|
const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
|
|
1109
1206
|
const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
|
|
1110
1207
|
const { routes } = await createRoot(false);
|
|
@@ -1124,19 +1221,20 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1124
1221
|
queue.add(async () => {
|
|
1125
1222
|
try {
|
|
1126
1223
|
const appCtx = await createRoot(false, route);
|
|
1127
|
-
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState } = appCtx;
|
|
1128
|
-
const
|
|
1224
|
+
const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState, getStyleCollector } = appCtx;
|
|
1225
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1226
|
+
const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
|
|
1129
1227
|
const url = new URL(route, "http://vite-react-ssg.com");
|
|
1130
1228
|
url.search = "";
|
|
1131
1229
|
url.hash = "";
|
|
1132
1230
|
url.pathname = route;
|
|
1133
1231
|
const request = new Request(url.href);
|
|
1134
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
|
|
1232
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request, styleCollector);
|
|
1135
1233
|
await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
|
|
1136
1234
|
const renderedHTML = await renderHTML({
|
|
1137
1235
|
rootContainerId,
|
|
1138
1236
|
appHTML,
|
|
1139
|
-
indexHTML,
|
|
1237
|
+
indexHTML: transformedIndexHTML,
|
|
1140
1238
|
metaAttributes,
|
|
1141
1239
|
bodyAttributes,
|
|
1142
1240
|
htmlAttributes,
|
|
@@ -1180,69 +1278,11 @@ ${kolorist.gray("[vite-react-ssg]")} ${kolorist.green("Build finished.")}`);
|
|
|
1180
1278
|
}, waitInSeconds * 1e3);
|
|
1181
1279
|
timeout.unref();
|
|
1182
1280
|
}
|
|
1183
|
-
async function detectEntry(root) {
|
|
1184
|
-
const scriptSrcReg = /<script(?:.*?)src=["'](.+?)["'](?!<)(?:.*)\>(?:[\n\r\s]*?)(?:<\/script>)/img;
|
|
1185
|
-
const html = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1186
|
-
const scripts = [...html.matchAll(scriptSrcReg)];
|
|
1187
|
-
const [, entry] = scripts.find((matchResult) => {
|
|
1188
|
-
const [script] = matchResult;
|
|
1189
|
-
const [, scriptType] = script.match(/.*\stype=(?:'|")?([^>'"\s]+)/i) || [];
|
|
1190
|
-
return scriptType === "module";
|
|
1191
|
-
}) || [];
|
|
1192
|
-
return entry || "src/main.ts";
|
|
1193
|
-
}
|
|
1194
|
-
async function resolveAlias(config, entry) {
|
|
1195
|
-
const resolver = config.createResolver();
|
|
1196
|
-
const result = await resolver(entry, config.root);
|
|
1197
|
-
return result || node_path.join(config.root, entry);
|
|
1198
|
-
}
|
|
1199
1281
|
function rewriteScripts(indexHTML, mode) {
|
|
1200
1282
|
if (!mode || mode === "sync")
|
|
1201
1283
|
return indexHTML;
|
|
1202
1284
|
return indexHTML.replace(/<script type="module" /g, `<script type="module" ${mode} `);
|
|
1203
1285
|
}
|
|
1204
|
-
async function renderHTML({
|
|
1205
|
-
rootContainerId,
|
|
1206
|
-
indexHTML,
|
|
1207
|
-
appHTML,
|
|
1208
|
-
metaAttributes,
|
|
1209
|
-
bodyAttributes,
|
|
1210
|
-
htmlAttributes,
|
|
1211
|
-
initialState
|
|
1212
|
-
}) {
|
|
1213
|
-
const stateScript = initialState ? `
|
|
1214
|
-
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1215
|
-
const headStartTag = "<head>";
|
|
1216
|
-
const metaTags = metaAttributes.join("");
|
|
1217
|
-
indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
|
|
1218
|
-
const bodyStartTag = "<body";
|
|
1219
|
-
indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
|
|
1220
|
-
const htmlStartTag = "<html";
|
|
1221
|
-
indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
|
|
1222
|
-
const container = `<div id="${rootContainerId}"></div>`;
|
|
1223
|
-
if (indexHTML.includes(container)) {
|
|
1224
|
-
return indexHTML.replace(
|
|
1225
|
-
container,
|
|
1226
|
-
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1227
|
-
);
|
|
1228
|
-
}
|
|
1229
|
-
const html5Parser = await import('html5parser');
|
|
1230
|
-
const ast = html5Parser.parse(indexHTML);
|
|
1231
|
-
let renderedOutput;
|
|
1232
|
-
html5Parser.walk(ast, {
|
|
1233
|
-
enter: (node) => {
|
|
1234
|
-
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)) {
|
|
1235
|
-
const attributesStringified = [...node.attributes.map(({ name: { value: name }, value }) => `${name}="${value.value}"`)].join(" ");
|
|
1236
|
-
const indexHTMLBefore = indexHTML.slice(0, node.start);
|
|
1237
|
-
const indexHTMLAfter = indexHTML.slice(node.end);
|
|
1238
|
-
renderedOutput = `${indexHTMLBefore}<${node.name} ${attributesStringified} data-server-rendered="true">${appHTML}</${node.name}>${stateScript}${indexHTMLAfter}`;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
if (!renderedOutput)
|
|
1243
|
-
throw new Error(`Could not find a tag with id="${rootContainerId}" to replace it with server-side rendered HTML`);
|
|
1244
|
-
return renderedOutput;
|
|
1245
|
-
}
|
|
1246
1286
|
async function formatHtml(html, formatting) {
|
|
1247
1287
|
if (formatting === "minify") {
|
|
1248
1288
|
const htmlMinifier = await import('html-minifier');
|
|
@@ -1278,4 +1318,186 @@ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
|
|
|
1278
1318
|
return mods;
|
|
1279
1319
|
}
|
|
1280
1320
|
|
|
1321
|
+
const SHORTCUTS = [
|
|
1322
|
+
{
|
|
1323
|
+
key: "u",
|
|
1324
|
+
description: "show server url",
|
|
1325
|
+
action(vite, _) {
|
|
1326
|
+
vite.config.logger.info("");
|
|
1327
|
+
printServerInfo(vite, true);
|
|
1328
|
+
}
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
key: "c",
|
|
1332
|
+
description: "clear console",
|
|
1333
|
+
action(vite, _) {
|
|
1334
|
+
vite.config.logger.clearScreen("error");
|
|
1335
|
+
}
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
key: "q",
|
|
1339
|
+
description: "quit",
|
|
1340
|
+
action(_, server) {
|
|
1341
|
+
server.close(() => process.exit());
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
];
|
|
1345
|
+
function bindShortcuts(viteServer, server) {
|
|
1346
|
+
if (!process.stdin.isTTY || process.env.CI)
|
|
1347
|
+
return;
|
|
1348
|
+
viteServer.config.logger.info(
|
|
1349
|
+
kolorist.dim(kolorist.green(" \u279C")) + kolorist.dim(" press ") + kolorist.bold("h") + kolorist.dim(" to show help")
|
|
1350
|
+
);
|
|
1351
|
+
const shortcuts = SHORTCUTS;
|
|
1352
|
+
let actionRunning = false;
|
|
1353
|
+
const onInput = async (input) => {
|
|
1354
|
+
if (input === "" || input === "") {
|
|
1355
|
+
try {
|
|
1356
|
+
await server.close();
|
|
1357
|
+
} finally {
|
|
1358
|
+
process.exit(1);
|
|
1359
|
+
}
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
if (actionRunning)
|
|
1363
|
+
return;
|
|
1364
|
+
if (input === "h") {
|
|
1365
|
+
viteServer.config.logger.info(
|
|
1366
|
+
[
|
|
1367
|
+
"",
|
|
1368
|
+
kolorist.bold(" Shortcuts"),
|
|
1369
|
+
...shortcuts.map(
|
|
1370
|
+
(shortcut2) => kolorist.dim(" press ") + kolorist.bold(shortcut2.key) + kolorist.dim(` to ${shortcut2.description}`)
|
|
1371
|
+
)
|
|
1372
|
+
].join("\n")
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
const shortcut = shortcuts.find((shortcut2) => shortcut2.key === input);
|
|
1376
|
+
if (!shortcut)
|
|
1377
|
+
return;
|
|
1378
|
+
actionRunning = true;
|
|
1379
|
+
await shortcut.action(viteServer, server);
|
|
1380
|
+
actionRunning = false;
|
|
1381
|
+
};
|
|
1382
|
+
process.stdin.setRawMode(true);
|
|
1383
|
+
process.stdin.on("data", onInput).setEncoding("utf8").resume();
|
|
1384
|
+
server.on("close", () => {
|
|
1385
|
+
process.stdin.off("data", onInput).pause();
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
async function dev(ssgOptions = {}, viteConfig = {}) {
|
|
1390
|
+
const mode = process.env.MODE || process.env.NODE_ENV || ssgOptions.mode || "development";
|
|
1391
|
+
const config = await vite.resolveConfig(viteConfig, "serve", mode, mode);
|
|
1392
|
+
const cwd = process.cwd();
|
|
1393
|
+
const root = config.root || cwd;
|
|
1394
|
+
const {
|
|
1395
|
+
entry = await detectEntry(root),
|
|
1396
|
+
onBeforePageRender,
|
|
1397
|
+
onPageRendered,
|
|
1398
|
+
rootContainerId = "root"
|
|
1399
|
+
} = Object.assign({}, config.ssgOptions || {}, ssgOptions);
|
|
1400
|
+
const ssrEntry = await resolveAlias(config, entry);
|
|
1401
|
+
const template = await fs__default.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
1402
|
+
let viteServer;
|
|
1403
|
+
globalThis.__ssr_start_time = performance.now();
|
|
1404
|
+
createServer().then((app) => {
|
|
1405
|
+
const port = viteServer.config.server.port || 5173;
|
|
1406
|
+
const server = app.listen(port, () => {
|
|
1407
|
+
printServerInfo(viteServer);
|
|
1408
|
+
bindShortcuts(viteServer, server);
|
|
1409
|
+
});
|
|
1410
|
+
});
|
|
1411
|
+
async function createServer() {
|
|
1412
|
+
process.env.__DEV_MODE_SSR = "true";
|
|
1413
|
+
const app = express__default();
|
|
1414
|
+
viteServer = await vite.createServer({
|
|
1415
|
+
// ...options,
|
|
1416
|
+
server: { middlewareMode: true },
|
|
1417
|
+
appType: "custom"
|
|
1418
|
+
});
|
|
1419
|
+
app.use(viteServer.middlewares);
|
|
1420
|
+
app.use("*", async (req, res) => {
|
|
1421
|
+
try {
|
|
1422
|
+
const url = req.originalUrl;
|
|
1423
|
+
const indexHTML = await viteServer.transformIndexHtml(url, template);
|
|
1424
|
+
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1425
|
+
const appCtx = await createRoot(false, url);
|
|
1426
|
+
const { routes, getStyleCollector } = appCtx;
|
|
1427
|
+
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1428
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1429
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes], createFetchRequest(req), styleCollector);
|
|
1430
|
+
const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
|
|
1431
|
+
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1432
|
+
const collectAssets = async (mod2) => {
|
|
1433
|
+
if (!mod2?.ssrTransformResult)
|
|
1434
|
+
return;
|
|
1435
|
+
const { deps = [], dynamicDeps = [] } = mod2?.ssrTransformResult;
|
|
1436
|
+
const allDeps = [...deps, ...dynamicDeps];
|
|
1437
|
+
for (const dep of allDeps) {
|
|
1438
|
+
if (dep.endsWith(".css")) {
|
|
1439
|
+
assetsUrls.add(dep);
|
|
1440
|
+
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1441
|
+
const depModule = await viteServer.moduleGraph.getModuleByUrl(dep);
|
|
1442
|
+
depModule && await collectAssets(depModule);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
await collectAssets(mod);
|
|
1447
|
+
const preloadLink = [...assetsUrls].map((item) => createLink(item));
|
|
1448
|
+
metaAttributes.push(...preloadLink);
|
|
1449
|
+
const renderedHTML = await renderHTML({
|
|
1450
|
+
rootContainerId,
|
|
1451
|
+
appHTML,
|
|
1452
|
+
indexHTML: transformedIndexHTML,
|
|
1453
|
+
metaAttributes,
|
|
1454
|
+
bodyAttributes,
|
|
1455
|
+
htmlAttributes,
|
|
1456
|
+
initialState: null
|
|
1457
|
+
});
|
|
1458
|
+
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1459
|
+
res.status(200).set({ "Content-Type": "text/html" }).end(transformed);
|
|
1460
|
+
} catch (e) {
|
|
1461
|
+
viteServer.ssrFixStacktrace(e);
|
|
1462
|
+
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1463
|
+
res.status(500).end(e.stack);
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
return app;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
async function printServerInfo(server, onlyUrl = false) {
|
|
1470
|
+
const info = server.config.logger.info;
|
|
1471
|
+
const port = server.config.server.port || 5173;
|
|
1472
|
+
const url = `http://localhost:${port}/`;
|
|
1473
|
+
if (!onlyUrl) {
|
|
1474
|
+
let ssrReadyMessage = " -- SSR";
|
|
1475
|
+
if (globalThis.__ssr_start_time) {
|
|
1476
|
+
ssrReadyMessage += ` ready in ${kolorist.reset(kolorist.bold(`${Math.round(
|
|
1477
|
+
// @ts-expect-error global var
|
|
1478
|
+
performance.now() - globalThis.__ssr_start_time
|
|
1479
|
+
)}ms`))}`;
|
|
1480
|
+
}
|
|
1481
|
+
info(
|
|
1482
|
+
`
|
|
1483
|
+
${kolorist.bgLightCyan(` VITE-REACT-SSG v${version} `)}`,
|
|
1484
|
+
{ clear: !server.config.logger.hasWarned }
|
|
1485
|
+
);
|
|
1486
|
+
info(
|
|
1487
|
+
`${kolorist.cyan(`
|
|
1488
|
+
VITE v${vite.version}`) + kolorist.dim(ssrReadyMessage)}
|
|
1489
|
+
`
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
info(
|
|
1493
|
+
kolorist.green(" dev server running at:")
|
|
1494
|
+
);
|
|
1495
|
+
printUrls(url, info);
|
|
1496
|
+
}
|
|
1497
|
+
function printUrls(url, info) {
|
|
1498
|
+
const colorUrl = (url2) => kolorist.cyan(url2.replace(/:(\d+)\//, (_, port) => `:${kolorist.bold(port)}/`));
|
|
1499
|
+
info(` ${kolorist.green("\u279C")} ${kolorist.bold("Local")}: ${colorUrl(url)}`);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1281
1502
|
exports.build = build;
|
|
1503
|
+
exports.dev = dev;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function ssrCollector() {
|
|
4
|
+
const { ServerStyleSheet } = await import('styled-components');
|
|
5
|
+
const sheet = new ServerStyleSheet();
|
|
6
|
+
return {
|
|
7
|
+
collect(app) {
|
|
8
|
+
return sheet.collectStyles(app);
|
|
9
|
+
},
|
|
10
|
+
toString() {
|
|
11
|
+
return sheet.getStyleTags();
|
|
12
|
+
},
|
|
13
|
+
cleanup() {
|
|
14
|
+
sheet.seal();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const styledComponents = undefined.SSR ? ssrCollector : null;
|
|
19
|
+
|
|
20
|
+
exports.getStyledComponentsCollector = styledComponents;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
declare function ssrCollector(): Promise<{
|
|
4
|
+
collect(app: ReactElement): JSX.Element;
|
|
5
|
+
toString(): string;
|
|
6
|
+
cleanup(): void;
|
|
7
|
+
}>;
|
|
8
|
+
declare const _default: typeof ssrCollector | null;
|
|
9
|
+
|
|
10
|
+
export { _default as getStyledComponentsCollector };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
async function ssrCollector() {
|
|
2
|
+
const { ServerStyleSheet } = await import('styled-components');
|
|
3
|
+
const sheet = new ServerStyleSheet();
|
|
4
|
+
return {
|
|
5
|
+
collect(app) {
|
|
6
|
+
return sheet.collectStyles(app);
|
|
7
|
+
},
|
|
8
|
+
toString() {
|
|
9
|
+
return sheet.getStyleTags();
|
|
10
|
+
},
|
|
11
|
+
cleanup() {
|
|
12
|
+
sheet.seal();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const styledComponents = import.meta.env.SSR ? ssrCollector : null;
|
|
17
|
+
|
|
18
|
+
export { styledComponents as getStyledComponentsCollector };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Options } from 'critters';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
2
3
|
import { NonIndexRouteObject, IndexRouteObject, createBrowserRouter } from 'react-router-dom';
|
|
3
4
|
|
|
4
5
|
type Router = ReturnType<typeof createBrowserRouter>;
|
|
@@ -116,6 +117,7 @@ interface ViteReactSSGContext<HasRouter extends boolean = true> {
|
|
|
116
117
|
* Current router path on SSG, `undefined` on client side.
|
|
117
118
|
*/
|
|
118
119
|
routePath?: string;
|
|
120
|
+
getStyleCollector: (() => StyleCollector | Promise<StyleCollector>) | null;
|
|
119
121
|
}
|
|
120
122
|
interface ViteReactSSGClientOptions {
|
|
121
123
|
transformState?: (state: any) => any;
|
|
@@ -126,6 +128,15 @@ interface ViteReactSSGClientOptions {
|
|
|
126
128
|
* @default `#root`
|
|
127
129
|
*/
|
|
128
130
|
rootContainer?: string | Element;
|
|
131
|
+
/**
|
|
132
|
+
* Use SSR during development.
|
|
133
|
+
*
|
|
134
|
+
* If false, the 'dev' script in 'package.json' needs to be set to `"vite"`.
|
|
135
|
+
*
|
|
136
|
+
* @default true
|
|
137
|
+
*/
|
|
138
|
+
ssrWhenDev?: boolean;
|
|
139
|
+
getStyleCollector?: (() => StyleCollector | Promise<StyleCollector>) | null;
|
|
129
140
|
}
|
|
130
141
|
interface CommonRouteOptions {
|
|
131
142
|
/**
|
|
@@ -144,10 +155,15 @@ interface RouterOptions {
|
|
|
144
155
|
routes: RouteRecord[];
|
|
145
156
|
createFetchRequest?: <T>(req: T) => Request;
|
|
146
157
|
}
|
|
158
|
+
interface StyleCollector {
|
|
159
|
+
collect: (app: ReactElement) => ReactElement;
|
|
160
|
+
toString: (html: string) => string;
|
|
161
|
+
cleanup?: () => void;
|
|
162
|
+
}
|
|
147
163
|
declare module 'vite' {
|
|
148
164
|
interface UserConfig {
|
|
149
165
|
ssgOptions?: ViteReactSSGOptions;
|
|
150
166
|
}
|
|
151
167
|
}
|
|
152
168
|
|
|
153
|
-
export { IndexRouteRecord as I, NonIndexRouteRecord as N, RouterOptions as R, ViteReactSSGContext as V, ViteReactSSGClientOptions as a, ViteReactSSGOptions as b, RouteRecord as c };
|
|
169
|
+
export { IndexRouteRecord as I, NonIndexRouteRecord as N, RouterOptions as R, StyleCollector as S, ViteReactSSGContext as V, ViteReactSSGClientOptions as a, ViteReactSSGOptions as b, RouteRecord as c };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-react-ssg",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"packageManager": "pnpm@8.6.6",
|
|
6
6
|
"description": "",
|
|
7
7
|
"author": "Riri <Daydreamerriri@outlook.com>",
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
"types": "./dist/node.d.ts",
|
|
31
31
|
"require": "./dist/node.cjs",
|
|
32
32
|
"import": "./dist/node.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./style-collectors": {
|
|
35
|
+
"types": "./dist/style-collectors.d.ts",
|
|
36
|
+
"require": "./dist/style-collectors.cjs",
|
|
37
|
+
"import": "./dist/style-collectors.mjs"
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"main": "./dist/index.mjs",
|
|
@@ -39,6 +44,9 @@
|
|
|
39
44
|
"*": {
|
|
40
45
|
"node": [
|
|
41
46
|
"./dist/node.d.ts"
|
|
47
|
+
],
|
|
48
|
+
"style-collectors": [
|
|
49
|
+
"./dist/style-collectors.d.ts"
|
|
42
50
|
]
|
|
43
51
|
}
|
|
44
52
|
},
|
|
@@ -64,6 +72,7 @@
|
|
|
64
72
|
"react": "^18.0.0",
|
|
65
73
|
"react-dom": "^18.0.0",
|
|
66
74
|
"react-router-dom": "^6.14.1",
|
|
75
|
+
"styled-components": "^6.0.0",
|
|
67
76
|
"vite": "^2.0.0 || ^3.0.0 || ^4.0.0"
|
|
68
77
|
},
|
|
69
78
|
"peerDependenciesMeta": {
|
|
@@ -72,9 +81,13 @@
|
|
|
72
81
|
},
|
|
73
82
|
"react-router-dom": {
|
|
74
83
|
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"styled-components": {
|
|
86
|
+
"optional": true
|
|
75
87
|
}
|
|
76
88
|
},
|
|
77
89
|
"dependencies": {
|
|
90
|
+
"express": "^4.18.2",
|
|
78
91
|
"fs-extra": "^11.1.1",
|
|
79
92
|
"html-minifier": "^4.0.0",
|
|
80
93
|
"html5parser": "^2.0.2",
|
|
@@ -86,6 +99,7 @@
|
|
|
86
99
|
},
|
|
87
100
|
"devDependencies": {
|
|
88
101
|
"@ririd/eslint-config": "0.6.0",
|
|
102
|
+
"@types/express": "^4.17.17",
|
|
89
103
|
"@types/fs-extra": "^11.0.1",
|
|
90
104
|
"@types/html-minifier": "^4.0.2",
|
|
91
105
|
"@types/jsdom": "^21.1.1",
|
|
@@ -104,10 +118,11 @@
|
|
|
104
118
|
"react-router-dom": "^6.14.1",
|
|
105
119
|
"rimraf": "5.0.1",
|
|
106
120
|
"simple-git-hooks": "^2.8.1",
|
|
121
|
+
"styled-components": "6.0.5",
|
|
107
122
|
"typescript": "5.1.6",
|
|
108
123
|
"unbuild": "^1.2.1",
|
|
109
124
|
"vite": "^4.4.0",
|
|
110
125
|
"vite-plugin-pwa": "^0.16.4",
|
|
111
126
|
"vitest": "0.33.0"
|
|
112
127
|
}
|
|
113
|
-
}
|
|
128
|
+
}
|