vite-react-ssg 0.7.0-beta.2 → 0.7.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 CHANGED
@@ -71,8 +71,7 @@ export const createRoot = ViteReactSSG(
71
71
  import React from 'react'
72
72
  import type { RouteRecord } from 'vite-react-ssg'
73
73
  import './App.css'
74
-
75
- const Layout = React.lazy(() => import('./Layout'))
74
+ import Layout from './Layout'
76
75
 
77
76
  export const routes: RouteRecord[] = [
78
77
  {
@@ -82,19 +81,18 @@ export const routes: RouteRecord[] = [
82
81
  children: [
83
82
  {
84
83
  path: 'a',
85
- Component: React.lazy(() => import('./pages/a')),
86
- entry: 'src/pages/a.tsx',
84
+ lazy: () => import('./pages/a'),
87
85
  },
88
86
  {
89
87
  index: true,
90
88
  Component: React.lazy(() => import('./pages/index')),
91
- // Used to obtain static resources through manifest
92
- entry: 'src/pages/index.tsx',
93
89
  },
94
90
  {
95
91
  path: 'nest/:b',
96
- Component: React.lazy(() => import('./pages/nest/[b]')),
97
- entry: 'src/pages/nest/[b].tsx',
92
+ lazy: () => {
93
+ const Component = await import('./pages/nest/[b]')
94
+ return { Component }
95
+ },
98
96
  // To determine which paths will be pre-rendered
99
97
  getStaticPaths: () => ['nest/b1', 'nest/b2'],
100
98
  },
@@ -136,13 +134,6 @@ export const createRoot = ViteReactSSG(<App />)
136
134
 
137
135
  The RouteObject of vite-react-ssg is based on react-router, and vite-react-ssg receives some additional properties.
138
136
 
139
- #### `entry`
140
-
141
- Used to obtain static resources.If you introduce static resources (such as css files) in that route and use lazy loading (such as React.lazy or route.lazy), you should set the entry field.
142
- It should be the path from root to the target file.
143
-
144
- eg: `src/pages/page1.tsx`
145
-
146
137
  #### `getStaticPaths`
147
138
 
148
139
  The `getStaticPaths()` function should return an array of path
@@ -157,9 +148,16 @@ const route = {
157
148
  entry: 'src/pages/nest/[b].tsx',
158
149
  // To determine which paths will be pre-rendered
159
150
  getStaticPaths: () => ['nest/b1', 'nest/b2'],
160
- },
151
+ }
161
152
  ```
162
153
 
154
+ #### `entry`
155
+
156
+ **You are not required to use this field. It is only necessary when "prehydration style loss" occurs.**
157
+ It should be the path from root to the target file.
158
+
159
+ eg: `src/pages/page1.tsx`
160
+
163
161
  ## lazy
164
162
 
165
163
  These options work well with the `lazy` field.
@@ -175,8 +173,6 @@ export function Component() {
175
173
  export function getStaticPaths() {
176
174
  return ['page1', 'page2']
177
175
  }
178
-
179
- export const entry = 'src/pages/[page].tsx'
180
176
  ```
181
177
 
182
178
  ```ts
@@ -195,6 +191,46 @@ See [example](./examples/lazy-pages/src/App.tsx).
195
191
 
196
192
  You can use react-router-dom's `loader` to fetch data at build time and use `useLoaderData` to get the data in the component.
197
193
 
194
+ In production, the `loader` will only be executed at build time, and the data will be fetched by the manifest generated at build time during the browser navigations .
195
+
196
+ In the development environment, the `loader` also runs only on the server.It provides data to the HTML during initial server rendering, and during browser route navigations , it makes calls to the server by initiating a fetch on the service.
197
+
198
+ ```tsx
199
+ import { useLoaderData } from 'react-router-dom'
200
+
201
+ export default function Docs() {
202
+ const data = useLoaderData() as Awaited<ReturnType<typeof loader>>
203
+
204
+ return (
205
+ <>
206
+ <div>{data.key}</div>
207
+ {/* eslint-disable-next-line react-dom/no-dangerously-set-innerhtml */}
208
+ <div dangerouslySetInnerHTML={{ __html: data.packageCodeHtml }} style={{ textAlign: 'start' }}></div>
209
+ </>
210
+ )
211
+ }
212
+
213
+ export const Component = Docs
214
+
215
+ export const entry = 'src/pages/json.tsx'
216
+
217
+ export async function loader() {
218
+ // The code here will not be executed on the client side, and the modules imported will not be sent to the client.
219
+ const fs = (await import('node:fs'))
220
+ const cwd = process.cwd()
221
+ const json = (await import('../docs/test.json')).default
222
+
223
+ const packageJson = await fs.promises.readFile(`${cwd}/package.json`, 'utf-8')
224
+ const { codeToHtml } = await import('shiki')
225
+ const packageJsonHtml = await codeToHtml(packageJson, { lang: 'json', theme: 'vitesse-light' })
226
+
227
+ return {
228
+ ...json,
229
+ packageCodeHtml: packageJsonHtml,
230
+ }
231
+ }
232
+ ```
233
+
198
234
  See [example | with-loader](./examples/with-loader/src/pages/[docs].tsx).
199
235
 
200
236
  ## `<ClientOnly/>`
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.870c683b.cjs';
3
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.870c683b.cjs';
2
+ import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.4d155759.cjs';
3
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.4d155759.cjs';
4
4
  export { C as ClientOnly, H as Head } from '../shared/vite-react-ssg.faf3855a.cjs';
5
5
  import 'critters';
6
6
  import 'react-router-dom';
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.870c683b.mjs';
3
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.870c683b.mjs';
2
+ import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.4d155759.mjs';
3
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.4d155759.mjs';
4
4
  export { C as ClientOnly, H as Head } from '../shared/vite-react-ssg.faf3855a.mjs';
5
5
  import 'critters';
6
6
  import 'react-router-dom';
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.870c683b.js';
3
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.870c683b.js';
2
+ import { V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from '../shared/vite-react-ssg.4d155759.js';
3
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, c as RouterOptions, S as StyleCollector, b as ViteReactSSGOptions } from '../shared/vite-react-ssg.4d155759.js';
4
4
  export { C as ClientOnly, H as Head } from '../shared/vite-react-ssg.faf3855a.js';
5
5
  import 'critters';
6
6
  import 'react-router-dom';
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.870c683b.cjs';
2
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.cjs';
1
+ import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.4d155759.cjs';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.cjs';
3
3
  export { C as ClientOnly, H as Head } from './shared/vite-react-ssg.faf3855a.cjs';
4
4
  import React from 'react';
5
5
  import { LinkProps, NavLinkProps } from 'react-router-dom';
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.870c683b.mjs';
2
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.mjs';
1
+ import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.4d155759.mjs';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.mjs';
3
3
  export { C as ClientOnly, H as Head } from './shared/vite-react-ssg.faf3855a.mjs';
4
4
  import React from 'react';
5
5
  import { LinkProps, NavLinkProps } from 'react-router-dom';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.870c683b.js';
2
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.js';
1
+ import { c as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './shared/vite-react-ssg.4d155759.js';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, R as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.js';
3
3
  export { C as ClientOnly, H as Head } from './shared/vite-react-ssg.faf3855a.js';
4
4
  import React from 'react';
5
5
  import { LinkProps, NavLinkProps } from 'react-router-dom';
package/dist/node.cjs CHANGED
@@ -850,19 +850,8 @@ function getSize(str) {
850
850
  return `${(str.length / 1024).toFixed(2)} KiB`;
851
851
  }
852
852
  async function routesToPaths(routes) {
853
- const pathToEntry = {};
854
- function addEntry(path, entry) {
855
- if (!entry)
856
- return;
857
- if (entry[0] === "/")
858
- entry = entry.slice(1);
859
- if (pathToEntry[path])
860
- pathToEntry[path].add(entry);
861
- else
862
- pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
863
- }
864
853
  if (!routes || routes.length === 0)
865
- return { paths: ["/"], pathToEntry };
854
+ return { paths: ["/"] };
866
855
  const paths = /* @__PURE__ */ new Set();
867
856
  const getPaths = async (routes2, prefix = "") => {
868
857
  prefix = prefix.replace(/\/$/g, "");
@@ -877,19 +866,16 @@ async function routesToPaths(routes) {
877
866
  }
878
867
  }
879
868
  let path = route.path;
880
- path = handlePath(path, prefix, route.entry);
869
+ path = handlePath(path, prefix);
881
870
  if (route.getStaticPaths && isDynamicSegmentsRoute(path)) {
882
871
  const staticPaths = await route.getStaticPaths();
883
872
  for (let staticPath of staticPaths) {
884
- staticPath = handlePath(staticPath, prefix, route.entry);
873
+ staticPath = handlePath(staticPath, prefix);
885
874
  if (Array.isArray(route.children))
886
875
  await getPaths(route.children, staticPath);
887
876
  }
888
877
  }
889
- if (route.index)
890
- addEntry(prefix, route.entry);
891
878
  if (route.index && !path) {
892
- addEntry("/", route.entry);
893
879
  paths.add("/");
894
880
  }
895
881
  if (Array.isArray(route.children))
@@ -897,16 +883,11 @@ async function routesToPaths(routes) {
897
883
  }
898
884
  };
899
885
  await getPaths(routes);
900
- return { paths: Array.from(paths), pathToEntry };
901
- function handlePath(path, prefix, entry) {
886
+ return { paths: Array.from(paths) };
887
+ function handlePath(path, prefix) {
902
888
  if (path != null) {
903
889
  path = prefix && !path.startsWith("/") ? `${prefix}${path ? `/${path}` : ""}` : path;
904
890
  paths.add(path);
905
- addEntry(path, entry);
906
- if (pathToEntry[prefix]) {
907
- const pathCopy = path;
908
- pathToEntry[prefix].forEach((entry2) => addEntry(pathCopy, entry2));
909
- }
910
891
  }
911
892
  return path;
912
893
  }
@@ -1100,18 +1081,10 @@ function createLink$1(href) {
1100
1081
  return `<link rel="stylesheet" href="${href}">`;
1101
1082
  }
1102
1083
 
1103
- function renderPreloadLinks(document, modules, ssrManifest) {
1084
+ function renderPreloadLinks(document, assets) {
1104
1085
  const seen = /* @__PURE__ */ new Set();
1105
- const preloadLinks = [];
1106
- Array.from(modules).forEach((id) => {
1107
- const files = ssrManifest[id] || [];
1108
- files.forEach((file) => {
1109
- if (!preloadLinks.includes(file))
1110
- preloadLinks.push(file);
1111
- });
1112
- });
1113
- if (preloadLinks) {
1114
- preloadLinks.forEach((file) => {
1086
+ if (assets) {
1087
+ assets.forEach((file) => {
1115
1088
  if (!seen.has(file)) {
1116
1089
  seen.add(file);
1117
1090
  renderPreloadLink(document, file);
@@ -1132,6 +1105,21 @@ function renderPreloadLink(document, file) {
1132
1105
  href: file,
1133
1106
  crossOrigin: ""
1134
1107
  });
1108
+ } else if (file.endsWith(".woff") || file.endsWith(".woff2") || file.endsWith(".ttf")) {
1109
+ appendLink(document, {
1110
+ rel: "preload",
1111
+ as: "font",
1112
+ type: "font/woff2",
1113
+ href: file,
1114
+ crossOrigin: ""
1115
+ });
1116
+ } else if (file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg") || file.endsWith(".webp") || file.endsWith(".gif") || file.endsWith(".ico") || file.endsWith(".svg")) {
1117
+ appendLink(document, {
1118
+ rel: "preload",
1119
+ as: "image",
1120
+ href: file,
1121
+ crossOrigin: ""
1122
+ });
1135
1123
  }
1136
1124
  }
1137
1125
  function createLink(document) {
@@ -1151,6 +1139,69 @@ function appendLink(document, attrs) {
1151
1139
  document.head.appendChild(link);
1152
1140
  }
1153
1141
 
1142
+ const DYNAMIC_IMPORT_REGEX = /import\("([^)]+)"\)/g;
1143
+ function collectAssets({
1144
+ routes,
1145
+ locationArg,
1146
+ base,
1147
+ serverManifest,
1148
+ manifest,
1149
+ ssrManifest
1150
+ }) {
1151
+ const matches = reactRouterDom.matchRoutes([...routes], locationArg, base);
1152
+ const routeEntries = matches?.map((item) => item.route.entry).filter(Boolean) ?? [];
1153
+ const dynamicImports = /* @__PURE__ */ new Set();
1154
+ matches?.forEach((item) => {
1155
+ let lazyStr = "";
1156
+ if (item.route.lazy) {
1157
+ lazyStr += item.route.lazy.toString();
1158
+ }
1159
+ if (item.route.Component?._payload?._result) {
1160
+ lazyStr += item.route.Component._payload._result.toString();
1161
+ }
1162
+ const match = lazyStr.matchAll(DYNAMIC_IMPORT_REGEX);
1163
+ for (const m of match) {
1164
+ dynamicImports.add(m[1].split("/").at(-1) ?? "");
1165
+ }
1166
+ });
1167
+ const entries = /* @__PURE__ */ new Set();
1168
+ routeEntries.forEach((e) => entries.add(e));
1169
+ const manifestEntries = [...Object.entries(serverManifest)];
1170
+ dynamicImports.forEach((name) => {
1171
+ const result = manifestEntries.find(([_, value]) => value.file.endsWith(name));
1172
+ if (result) {
1173
+ entries.add(result[0]);
1174
+ }
1175
+ });
1176
+ const modules = collectModulesForEntries(manifest, entries);
1177
+ const assets = /* @__PURE__ */ new Set();
1178
+ Array.from(modules).forEach((id) => {
1179
+ const files = ssrManifest[id] || [];
1180
+ files.forEach((file) => {
1181
+ assets.add(file);
1182
+ });
1183
+ });
1184
+ return assets;
1185
+ }
1186
+ function collectModulesForEntries(manifest, entries) {
1187
+ const mods = /* @__PURE__ */ new Set();
1188
+ if (!entries)
1189
+ return mods;
1190
+ for (const entry of entries)
1191
+ collectModules(manifest, entry, mods);
1192
+ return mods;
1193
+ }
1194
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1195
+ if (!entry)
1196
+ return mods;
1197
+ mods.add(entry);
1198
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1199
+ collectModules(manifest, item, mods);
1200
+ });
1201
+ return mods;
1202
+ }
1203
+
1204
+ const dotVitedir = Number.parseInt(vite.version) >= 5 ? [".vite"] : [];
1154
1205
  function DefaultIncludedRoutes(paths, _routes) {
1155
1206
  return paths.filter((i) => !i.includes(":") && !i.includes("*"));
1156
1207
  }
@@ -1220,6 +1271,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1220
1271
  await vite.build(vite.mergeConfig(viteConfig, {
1221
1272
  build: {
1222
1273
  ssr: ssrEntry,
1274
+ manifest: true,
1223
1275
  outDir: ssgOut,
1224
1276
  minify: false,
1225
1277
  cssCodeSplit: false,
@@ -1239,11 +1291,12 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1239
1291
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1240
1292
  const ext = format === "esm" ? ".mjs" : ".cjs";
1241
1293
  const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
1294
+ const serverManifest = JSON.parse(await fs__default.readFile(node_path.join(ssgOut, ...dotVitedir, "manifest.json"), "utf-8"));
1242
1295
  const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('node.cjs', document.baseURI).href)));
1243
1296
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1244
1297
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1245
1298
  const { routes } = await createRoot(false);
1246
- const { paths, pathToEntry } = await routesToPaths(routes);
1299
+ const { paths } = await routesToPaths(routes);
1247
1300
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1248
1301
  routesPaths = DefaultIncludedRoutes(routesPaths);
1249
1302
  routesPaths = Array.from(new Set(routesPaths));
@@ -1251,7 +1304,6 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1251
1304
  const critters = crittersOptions !== false ? await getCritters(outDir, { publicPath: configBase, ...crittersOptions }) : void 0;
1252
1305
  if (critters)
1253
1306
  console.log(`${kolorist.gray("[vite-react-ssg]")} ${kolorist.blue("Critical CSS generation enabled via `critters`")}`);
1254
- const dotVitedir = Number.parseInt(vite.version) >= 5 ? [".vite"] : [];
1255
1307
  const ssrManifest = JSON.parse(await fs__default.readFile(node_path.join(out, ...dotVitedir, "ssr-manifest.json"), "utf-8"));
1256
1308
  const manifest = JSON.parse(await fs__default.readFile(node_path.join(out, ...dotVitedir, "manifest.json"), "utf-8"));
1257
1309
  let indexHTML = await fs__default.readFile(node_path.join(out, "index.html"), "utf-8");
@@ -1268,6 +1320,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1268
1320
  const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1269
1321
  const fetchUrl = `${remixRouter.withTrailingSlash(base)}${remixRouter.removeLeadingSlash(path)}`;
1270
1322
  const request = createRequest(fetchUrl);
1323
+ const assets = !app ? collectAssets({ routes: [...routes2], locationArg: fetchUrl, base, serverManifest, manifest, ssrManifest }) : /* @__PURE__ */ new Set();
1271
1324
  const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
1272
1325
  staticLoaderDataManifest[path] = routerContext?.loaderData;
1273
1326
  await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
@@ -1281,8 +1334,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1281
1334
  initialState: null
1282
1335
  });
1283
1336
  const jsdom = new JSDOM.JSDOM(renderedHTML);
1284
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1285
- renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1337
+ renderPreloadLinks(jsdom.window.document, assets);
1286
1338
  const html = jsdom.serialize();
1287
1339
  let transformed = await onPageRendered?.(path, html, appCtx) || html;
1288
1340
  transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
@@ -1356,23 +1408,6 @@ async function formatHtml(html, formatting) {
1356
1408
  }
1357
1409
  return html;
1358
1410
  }
1359
- function collectModulesForEntrys(manifest, entrys) {
1360
- const mods = /* @__PURE__ */ new Set();
1361
- if (!entrys)
1362
- return mods;
1363
- for (const entry of entrys)
1364
- collectModules(manifest, entry, mods);
1365
- return mods;
1366
- }
1367
- function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1368
- if (!entry)
1369
- return mods;
1370
- mods.add(entry);
1371
- manifest[entry]?.dynamicImports?.forEach((item) => {
1372
- collectModules(manifest, item, mods);
1373
- });
1374
- return mods;
1375
- }
1376
1411
 
1377
1412
  function invariant(value, message) {
1378
1413
  if (value === false || value === null || typeof value === "undefined") {
@@ -1488,11 +1523,10 @@ function ssrServerPlugin({
1488
1523
  const indexHTML = await server.transformIndexHtml(url, template);
1489
1524
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1490
1525
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1491
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1526
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1492
1527
  metaAttributes.push(styleTag);
1493
- const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
1494
1528
  const mods = await Promise.all(
1495
- [ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1529
+ [ssrEntry, entry].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1496
1530
  );
1497
1531
  const assetsUrls = /* @__PURE__ */ new Set();
1498
1532
  const collectAssets = async (mod) => {
@@ -1501,7 +1535,7 @@ function ssrServerPlugin({
1501
1535
  const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1502
1536
  const allDeps = [...deps, ...dynamicDeps];
1503
1537
  for (const dep of allDeps) {
1504
- if (dep.endsWith(".css")) {
1538
+ if (dep.endsWith(".css") || dep.endsWith(".scss") || dep.endsWith(".sass") || dep.endsWith(".less")) {
1505
1539
  assetsUrls.add(dep);
1506
1540
  } else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
1507
1541
  const depModule = await server.moduleGraph.getModuleByUrl(dep);
package/dist/node.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.cjs';
2
+ import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.cjs';
3
3
  import 'critters';
4
4
  import 'react';
5
5
  import 'react-router-dom';
package/dist/node.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.mjs';
2
+ import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.mjs';
3
3
  import 'critters';
4
4
  import 'react';
5
5
  import 'react-router-dom';
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.870c683b.js';
2
+ import { b as ViteReactSSGOptions } from './shared/vite-react-ssg.4d155759.js';
3
3
  import 'critters';
4
4
  import 'react';
5
5
  import 'react-router-dom';
package/dist/node.mjs CHANGED
@@ -843,19 +843,8 @@ function getSize(str) {
843
843
  return `${(str.length / 1024).toFixed(2)} KiB`;
844
844
  }
845
845
  async function routesToPaths(routes) {
846
- const pathToEntry = {};
847
- function addEntry(path, entry) {
848
- if (!entry)
849
- return;
850
- if (entry[0] === "/")
851
- entry = entry.slice(1);
852
- if (pathToEntry[path])
853
- pathToEntry[path].add(entry);
854
- else
855
- pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
856
- }
857
846
  if (!routes || routes.length === 0)
858
- return { paths: ["/"], pathToEntry };
847
+ return { paths: ["/"] };
859
848
  const paths = /* @__PURE__ */ new Set();
860
849
  const getPaths = async (routes2, prefix = "") => {
861
850
  prefix = prefix.replace(/\/$/g, "");
@@ -870,19 +859,16 @@ async function routesToPaths(routes) {
870
859
  }
871
860
  }
872
861
  let path = route.path;
873
- path = handlePath(path, prefix, route.entry);
862
+ path = handlePath(path, prefix);
874
863
  if (route.getStaticPaths && isDynamicSegmentsRoute(path)) {
875
864
  const staticPaths = await route.getStaticPaths();
876
865
  for (let staticPath of staticPaths) {
877
- staticPath = handlePath(staticPath, prefix, route.entry);
866
+ staticPath = handlePath(staticPath, prefix);
878
867
  if (Array.isArray(route.children))
879
868
  await getPaths(route.children, staticPath);
880
869
  }
881
870
  }
882
- if (route.index)
883
- addEntry(prefix, route.entry);
884
871
  if (route.index && !path) {
885
- addEntry("/", route.entry);
886
872
  paths.add("/");
887
873
  }
888
874
  if (Array.isArray(route.children))
@@ -890,16 +876,11 @@ async function routesToPaths(routes) {
890
876
  }
891
877
  };
892
878
  await getPaths(routes);
893
- return { paths: Array.from(paths), pathToEntry };
894
- function handlePath(path, prefix, entry) {
879
+ return { paths: Array.from(paths) };
880
+ function handlePath(path, prefix) {
895
881
  if (path != null) {
896
882
  path = prefix && !path.startsWith("/") ? `${prefix}${path ? `/${path}` : ""}` : path;
897
883
  paths.add(path);
898
- addEntry(path, entry);
899
- if (pathToEntry[prefix]) {
900
- const pathCopy = path;
901
- pathToEntry[prefix].forEach((entry2) => addEntry(pathCopy, entry2));
902
- }
903
884
  }
904
885
  return path;
905
886
  }
@@ -1093,18 +1074,10 @@ function createLink$1(href) {
1093
1074
  return `<link rel="stylesheet" href="${href}">`;
1094
1075
  }
1095
1076
 
1096
- function renderPreloadLinks(document, modules, ssrManifest) {
1077
+ function renderPreloadLinks(document, assets) {
1097
1078
  const seen = /* @__PURE__ */ new Set();
1098
- const preloadLinks = [];
1099
- Array.from(modules).forEach((id) => {
1100
- const files = ssrManifest[id] || [];
1101
- files.forEach((file) => {
1102
- if (!preloadLinks.includes(file))
1103
- preloadLinks.push(file);
1104
- });
1105
- });
1106
- if (preloadLinks) {
1107
- preloadLinks.forEach((file) => {
1079
+ if (assets) {
1080
+ assets.forEach((file) => {
1108
1081
  if (!seen.has(file)) {
1109
1082
  seen.add(file);
1110
1083
  renderPreloadLink(document, file);
@@ -1125,6 +1098,21 @@ function renderPreloadLink(document, file) {
1125
1098
  href: file,
1126
1099
  crossOrigin: ""
1127
1100
  });
1101
+ } else if (file.endsWith(".woff") || file.endsWith(".woff2") || file.endsWith(".ttf")) {
1102
+ appendLink(document, {
1103
+ rel: "preload",
1104
+ as: "font",
1105
+ type: "font/woff2",
1106
+ href: file,
1107
+ crossOrigin: ""
1108
+ });
1109
+ } else if (file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg") || file.endsWith(".webp") || file.endsWith(".gif") || file.endsWith(".ico") || file.endsWith(".svg")) {
1110
+ appendLink(document, {
1111
+ rel: "preload",
1112
+ as: "image",
1113
+ href: file,
1114
+ crossOrigin: ""
1115
+ });
1128
1116
  }
1129
1117
  }
1130
1118
  function createLink(document) {
@@ -1144,6 +1132,69 @@ function appendLink(document, attrs) {
1144
1132
  document.head.appendChild(link);
1145
1133
  }
1146
1134
 
1135
+ const DYNAMIC_IMPORT_REGEX = /import\("([^)]+)"\)/g;
1136
+ function collectAssets({
1137
+ routes,
1138
+ locationArg,
1139
+ base,
1140
+ serverManifest,
1141
+ manifest,
1142
+ ssrManifest
1143
+ }) {
1144
+ const matches = matchRoutes([...routes], locationArg, base);
1145
+ const routeEntries = matches?.map((item) => item.route.entry).filter(Boolean) ?? [];
1146
+ const dynamicImports = /* @__PURE__ */ new Set();
1147
+ matches?.forEach((item) => {
1148
+ let lazyStr = "";
1149
+ if (item.route.lazy) {
1150
+ lazyStr += item.route.lazy.toString();
1151
+ }
1152
+ if (item.route.Component?._payload?._result) {
1153
+ lazyStr += item.route.Component._payload._result.toString();
1154
+ }
1155
+ const match = lazyStr.matchAll(DYNAMIC_IMPORT_REGEX);
1156
+ for (const m of match) {
1157
+ dynamicImports.add(m[1].split("/").at(-1) ?? "");
1158
+ }
1159
+ });
1160
+ const entries = /* @__PURE__ */ new Set();
1161
+ routeEntries.forEach((e) => entries.add(e));
1162
+ const manifestEntries = [...Object.entries(serverManifest)];
1163
+ dynamicImports.forEach((name) => {
1164
+ const result = manifestEntries.find(([_, value]) => value.file.endsWith(name));
1165
+ if (result) {
1166
+ entries.add(result[0]);
1167
+ }
1168
+ });
1169
+ const modules = collectModulesForEntries(manifest, entries);
1170
+ const assets = /* @__PURE__ */ new Set();
1171
+ Array.from(modules).forEach((id) => {
1172
+ const files = ssrManifest[id] || [];
1173
+ files.forEach((file) => {
1174
+ assets.add(file);
1175
+ });
1176
+ });
1177
+ return assets;
1178
+ }
1179
+ function collectModulesForEntries(manifest, entries) {
1180
+ const mods = /* @__PURE__ */ new Set();
1181
+ if (!entries)
1182
+ return mods;
1183
+ for (const entry of entries)
1184
+ collectModules(manifest, entry, mods);
1185
+ return mods;
1186
+ }
1187
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1188
+ if (!entry)
1189
+ return mods;
1190
+ mods.add(entry);
1191
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1192
+ collectModules(manifest, item, mods);
1193
+ });
1194
+ return mods;
1195
+ }
1196
+
1197
+ const dotVitedir = Number.parseInt(version$1) >= 5 ? [".vite"] : [];
1147
1198
  function DefaultIncludedRoutes(paths, _routes) {
1148
1199
  return paths.filter((i) => !i.includes(":") && !i.includes("*"));
1149
1200
  }
@@ -1213,6 +1264,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1213
1264
  await build$1(mergeConfig(viteConfig, {
1214
1265
  build: {
1215
1266
  ssr: ssrEntry,
1267
+ manifest: true,
1216
1268
  outDir: ssgOut,
1217
1269
  minify: false,
1218
1270
  cssCodeSplit: false,
@@ -1232,11 +1284,12 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1232
1284
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1233
1285
  const ext = format === "esm" ? ".mjs" : ".cjs";
1234
1286
  const serverEntry = join(prefix, ssgOut, parse(ssrEntry).name + ext);
1287
+ const serverManifest = JSON.parse(await fs.readFile(join(ssgOut, ...dotVitedir, "manifest.json"), "utf-8"));
1235
1288
  const _require = createRequire(import.meta.url);
1236
1289
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1237
1290
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1238
1291
  const { routes } = await createRoot(false);
1239
- const { paths, pathToEntry } = await routesToPaths(routes);
1292
+ const { paths } = await routesToPaths(routes);
1240
1293
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1241
1294
  routesPaths = DefaultIncludedRoutes(routesPaths);
1242
1295
  routesPaths = Array.from(new Set(routesPaths));
@@ -1244,7 +1297,6 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1244
1297
  const critters = crittersOptions !== false ? await getCritters(outDir, { publicPath: configBase, ...crittersOptions }) : void 0;
1245
1298
  if (critters)
1246
1299
  console.log(`${gray("[vite-react-ssg]")} ${blue("Critical CSS generation enabled via `critters`")}`);
1247
- const dotVitedir = Number.parseInt(version$1) >= 5 ? [".vite"] : [];
1248
1300
  const ssrManifest = JSON.parse(await fs.readFile(join(out, ...dotVitedir, "ssr-manifest.json"), "utf-8"));
1249
1301
  const manifest = JSON.parse(await fs.readFile(join(out, ...dotVitedir, "manifest.json"), "utf-8"));
1250
1302
  let indexHTML = await fs.readFile(join(out, "index.html"), "utf-8");
@@ -1261,6 +1313,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1261
1313
  const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1262
1314
  const fetchUrl = `${withTrailingSlash(base)}${removeLeadingSlash(path)}`;
1263
1315
  const request = createRequest(fetchUrl);
1316
+ const assets = !app ? collectAssets({ routes: [...routes2], locationArg: fetchUrl, base, serverManifest, manifest, ssrManifest }) : /* @__PURE__ */ new Set();
1264
1317
  const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
1265
1318
  staticLoaderDataManifest[path] = routerContext?.loaderData;
1266
1319
  await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
@@ -1274,8 +1327,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1274
1327
  initialState: null
1275
1328
  });
1276
1329
  const jsdom = new JSDOM(renderedHTML);
1277
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1278
- renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1330
+ renderPreloadLinks(jsdom.window.document, assets);
1279
1331
  const html = jsdom.serialize();
1280
1332
  let transformed = await onPageRendered?.(path, html, appCtx) || html;
1281
1333
  transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
@@ -1349,23 +1401,6 @@ async function formatHtml(html, formatting) {
1349
1401
  }
1350
1402
  return html;
1351
1403
  }
1352
- function collectModulesForEntrys(manifest, entrys) {
1353
- const mods = /* @__PURE__ */ new Set();
1354
- if (!entrys)
1355
- return mods;
1356
- for (const entry of entrys)
1357
- collectModules(manifest, entry, mods);
1358
- return mods;
1359
- }
1360
- function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1361
- if (!entry)
1362
- return mods;
1363
- mods.add(entry);
1364
- manifest[entry]?.dynamicImports?.forEach((item) => {
1365
- collectModules(manifest, item, mods);
1366
- });
1367
- return mods;
1368
- }
1369
1404
 
1370
1405
  function invariant(value, message) {
1371
1406
  if (value === false || value === null || typeof value === "undefined") {
@@ -1481,11 +1516,10 @@ function ssrServerPlugin({
1481
1516
  const indexHTML = await server.transformIndexHtml(url, template);
1482
1517
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1483
1518
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1484
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1519
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1485
1520
  metaAttributes.push(styleTag);
1486
- const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
1487
1521
  const mods = await Promise.all(
1488
- [ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1522
+ [ssrEntry, entry].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1489
1523
  );
1490
1524
  const assetsUrls = /* @__PURE__ */ new Set();
1491
1525
  const collectAssets = async (mod) => {
@@ -1494,7 +1528,7 @@ function ssrServerPlugin({
1494
1528
  const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1495
1529
  const allDeps = [...deps, ...dynamicDeps];
1496
1530
  for (const dep of allDeps) {
1497
- if (dep.endsWith(".css")) {
1531
+ if (dep.endsWith(".css") || dep.endsWith(".scss") || dep.endsWith(".sass") || dep.endsWith(".less")) {
1498
1532
  assetsUrls.add(dep);
1499
1533
  } else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
1500
1534
  const depModule = await server.moduleGraph.getModuleByUrl(dep);
@@ -140,6 +140,8 @@ interface CommonRouteOptions {
140
140
  /**
141
141
  * Used to obtain static resources through manifest
142
142
  *
143
+ * **You are not required to use this field. It is only necessary when "prehydration style loss" occurs.**
144
+ *
143
145
  * @example `src/pages/home.tsx`
144
146
  */
145
147
  entry?: string;
@@ -140,6 +140,8 @@ interface CommonRouteOptions {
140
140
  /**
141
141
  * Used to obtain static resources through manifest
142
142
  *
143
+ * **You are not required to use this field. It is only necessary when "prehydration style loss" occurs.**
144
+ *
143
145
  * @example `src/pages/home.tsx`
144
146
  */
145
147
  entry?: string;
@@ -140,6 +140,8 @@ interface CommonRouteOptions {
140
140
  /**
141
141
  * Used to obtain static resources through manifest
142
142
  *
143
+ * **You are not required to use this field. It is only necessary when "prehydration style loss" occurs.**
144
+ *
143
145
  * @example `src/pages/home.tsx`
144
146
  */
145
147
  entry?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-react-ssg",
3
3
  "type": "module",
4
- "version": "0.7.0-beta.2",
4
+ "version": "0.7.0",
5
5
  "packageManager": "pnpm@9.4.0",
6
6
  "description": "",
7
7
  "author": "Riri <Daydreamerriri@outlook.com>",