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.
@@ -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
- 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 }));
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.a5dfecac.cjs', document.baseURI).href)));
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 transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx);
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.0.3",
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
+ }