vite-react-ssg 0.7.0-beta.2 → 0.7.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/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
  }
@@ -1029,8 +1010,12 @@ function extractHelmet(context, styleCollector) {
1029
1010
  const { helmet } = context;
1030
1011
  const htmlAttributes = helmet.htmlAttributes.toString();
1031
1012
  const bodyAttributes = helmet.bodyAttributes.toString();
1013
+ let titleString = helmet.title.toString();
1014
+ if (titleString.split(">")[1] === "</title") {
1015
+ titleString = "";
1016
+ }
1032
1017
  const metaStrings = [
1033
- helmet.title.toString(),
1018
+ titleString,
1034
1019
  helmet.meta.toString(),
1035
1020
  helmet.link.toString(),
1036
1021
  helmet.script.toString()
@@ -1100,18 +1085,10 @@ function createLink$1(href) {
1100
1085
  return `<link rel="stylesheet" href="${href}">`;
1101
1086
  }
1102
1087
 
1103
- function renderPreloadLinks(document, modules, ssrManifest) {
1088
+ function renderPreloadLinks(document, assets) {
1104
1089
  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) => {
1090
+ if (assets) {
1091
+ assets.forEach((file) => {
1115
1092
  if (!seen.has(file)) {
1116
1093
  seen.add(file);
1117
1094
  renderPreloadLink(document, file);
@@ -1132,6 +1109,21 @@ function renderPreloadLink(document, file) {
1132
1109
  href: file,
1133
1110
  crossOrigin: ""
1134
1111
  });
1112
+ } else if (file.endsWith(".woff") || file.endsWith(".woff2") || file.endsWith(".ttf")) {
1113
+ appendLink(document, {
1114
+ rel: "preload",
1115
+ as: "font",
1116
+ type: "font/woff2",
1117
+ href: file,
1118
+ crossOrigin: ""
1119
+ });
1120
+ } else if (file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg") || file.endsWith(".webp") || file.endsWith(".gif") || file.endsWith(".ico") || file.endsWith(".svg")) {
1121
+ appendLink(document, {
1122
+ rel: "preload",
1123
+ as: "image",
1124
+ href: file,
1125
+ crossOrigin: ""
1126
+ });
1135
1127
  }
1136
1128
  }
1137
1129
  function createLink(document) {
@@ -1151,6 +1143,69 @@ function appendLink(document, attrs) {
1151
1143
  document.head.appendChild(link);
1152
1144
  }
1153
1145
 
1146
+ const DYNAMIC_IMPORT_REGEX = /import\("([^)]+)"\)/g;
1147
+ function collectAssets({
1148
+ routes,
1149
+ locationArg,
1150
+ base,
1151
+ serverManifest,
1152
+ manifest,
1153
+ ssrManifest
1154
+ }) {
1155
+ const matches = reactRouterDom.matchRoutes([...routes], locationArg, base);
1156
+ const routeEntries = matches?.map((item) => item.route.entry).filter(Boolean) ?? [];
1157
+ const dynamicImports = /* @__PURE__ */ new Set();
1158
+ matches?.forEach((item) => {
1159
+ let lazyStr = "";
1160
+ if (item.route.lazy) {
1161
+ lazyStr += item.route.lazy.toString();
1162
+ }
1163
+ if (item.route.Component?._payload?._result) {
1164
+ lazyStr += item.route.Component._payload._result.toString();
1165
+ }
1166
+ const match = lazyStr.matchAll(DYNAMIC_IMPORT_REGEX);
1167
+ for (const m of match) {
1168
+ dynamicImports.add(m[1].split("/").at(-1) ?? "");
1169
+ }
1170
+ });
1171
+ const entries = /* @__PURE__ */ new Set();
1172
+ routeEntries.forEach((e) => entries.add(e));
1173
+ const manifestEntries = [...Object.entries(serverManifest)];
1174
+ dynamicImports.forEach((name) => {
1175
+ const result = manifestEntries.find(([_, value]) => value.file.endsWith(name));
1176
+ if (result) {
1177
+ entries.add(result[0]);
1178
+ }
1179
+ });
1180
+ const modules = collectModulesForEntries(manifest, entries);
1181
+ const assets = /* @__PURE__ */ new Set();
1182
+ Array.from(modules).forEach((id) => {
1183
+ const files = ssrManifest[id] || [];
1184
+ files.forEach((file) => {
1185
+ assets.add(file);
1186
+ });
1187
+ });
1188
+ return assets;
1189
+ }
1190
+ function collectModulesForEntries(manifest, entries) {
1191
+ const mods = /* @__PURE__ */ new Set();
1192
+ if (!entries)
1193
+ return mods;
1194
+ for (const entry of entries)
1195
+ collectModules(manifest, entry, mods);
1196
+ return mods;
1197
+ }
1198
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1199
+ if (!entry)
1200
+ return mods;
1201
+ mods.add(entry);
1202
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1203
+ collectModules(manifest, item, mods);
1204
+ });
1205
+ return mods;
1206
+ }
1207
+
1208
+ const dotVitedir = Number.parseInt(vite.version) >= 5 ? [".vite"] : [];
1154
1209
  function DefaultIncludedRoutes(paths, _routes) {
1155
1210
  return paths.filter((i) => !i.includes(":") && !i.includes("*"));
1156
1211
  }
@@ -1220,6 +1275,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1220
1275
  await vite.build(vite.mergeConfig(viteConfig, {
1221
1276
  build: {
1222
1277
  ssr: ssrEntry,
1278
+ manifest: true,
1223
1279
  outDir: ssgOut,
1224
1280
  minify: false,
1225
1281
  cssCodeSplit: false,
@@ -1239,11 +1295,12 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1239
1295
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1240
1296
  const ext = format === "esm" ? ".mjs" : ".cjs";
1241
1297
  const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
1298
+ const serverManifest = JSON.parse(await fs__default.readFile(node_path.join(ssgOut, ...dotVitedir, "manifest.json"), "utf-8"));
1242
1299
  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
1300
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1244
1301
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1245
1302
  const { routes } = await createRoot(false);
1246
- const { paths, pathToEntry } = await routesToPaths(routes);
1303
+ const { paths } = await routesToPaths(routes);
1247
1304
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1248
1305
  routesPaths = DefaultIncludedRoutes(routesPaths);
1249
1306
  routesPaths = Array.from(new Set(routesPaths));
@@ -1251,7 +1308,6 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1251
1308
  const critters = crittersOptions !== false ? await getCritters(outDir, { publicPath: configBase, ...crittersOptions }) : void 0;
1252
1309
  if (critters)
1253
1310
  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
1311
  const ssrManifest = JSON.parse(await fs__default.readFile(node_path.join(out, ...dotVitedir, "ssr-manifest.json"), "utf-8"));
1256
1312
  const manifest = JSON.parse(await fs__default.readFile(node_path.join(out, ...dotVitedir, "manifest.json"), "utf-8"));
1257
1313
  let indexHTML = await fs__default.readFile(node_path.join(out, "index.html"), "utf-8");
@@ -1268,6 +1324,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1268
1324
  const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1269
1325
  const fetchUrl = `${remixRouter.withTrailingSlash(base)}${remixRouter.removeLeadingSlash(path)}`;
1270
1326
  const request = createRequest(fetchUrl);
1327
+ const assets = !app ? collectAssets({ routes: [...routes2], locationArg: fetchUrl, base, serverManifest, manifest, ssrManifest }) : /* @__PURE__ */ new Set();
1271
1328
  const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
1272
1329
  staticLoaderDataManifest[path] = routerContext?.loaderData;
1273
1330
  await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
@@ -1281,8 +1338,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1281
1338
  initialState: null
1282
1339
  });
1283
1340
  const jsdom = new JSDOM.JSDOM(renderedHTML);
1284
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1285
- renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1341
+ renderPreloadLinks(jsdom.window.document, assets);
1286
1342
  const html = jsdom.serialize();
1287
1343
  let transformed = await onPageRendered?.(path, html, appCtx) || html;
1288
1344
  transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
@@ -1356,23 +1412,6 @@ async function formatHtml(html, formatting) {
1356
1412
  }
1357
1413
  return html;
1358
1414
  }
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
1415
 
1377
1416
  function invariant(value, message) {
1378
1417
  if (value === false || value === null || typeof value === "undefined") {
@@ -1488,11 +1527,10 @@ function ssrServerPlugin({
1488
1527
  const indexHTML = await server.transformIndexHtml(url, template);
1489
1528
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1490
1529
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1491
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1530
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1492
1531
  metaAttributes.push(styleTag);
1493
- const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
1494
1532
  const mods = await Promise.all(
1495
- [ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1533
+ [ssrEntry, entry].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1496
1534
  );
1497
1535
  const assetsUrls = /* @__PURE__ */ new Set();
1498
1536
  const collectAssets = async (mod) => {
@@ -1501,7 +1539,7 @@ function ssrServerPlugin({
1501
1539
  const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1502
1540
  const allDeps = [...deps, ...dynamicDeps];
1503
1541
  for (const dep of allDeps) {
1504
- if (dep.endsWith(".css")) {
1542
+ if (dep.endsWith(".css") || dep.endsWith(".scss") || dep.endsWith(".sass") || dep.endsWith(".less")) {
1505
1543
  assetsUrls.add(dep);
1506
1544
  } else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
1507
1545
  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
  }
@@ -1022,8 +1003,12 @@ function extractHelmet(context, styleCollector) {
1022
1003
  const { helmet } = context;
1023
1004
  const htmlAttributes = helmet.htmlAttributes.toString();
1024
1005
  const bodyAttributes = helmet.bodyAttributes.toString();
1006
+ let titleString = helmet.title.toString();
1007
+ if (titleString.split(">")[1] === "</title") {
1008
+ titleString = "";
1009
+ }
1025
1010
  const metaStrings = [
1026
- helmet.title.toString(),
1011
+ titleString,
1027
1012
  helmet.meta.toString(),
1028
1013
  helmet.link.toString(),
1029
1014
  helmet.script.toString()
@@ -1093,18 +1078,10 @@ function createLink$1(href) {
1093
1078
  return `<link rel="stylesheet" href="${href}">`;
1094
1079
  }
1095
1080
 
1096
- function renderPreloadLinks(document, modules, ssrManifest) {
1081
+ function renderPreloadLinks(document, assets) {
1097
1082
  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) => {
1083
+ if (assets) {
1084
+ assets.forEach((file) => {
1108
1085
  if (!seen.has(file)) {
1109
1086
  seen.add(file);
1110
1087
  renderPreloadLink(document, file);
@@ -1125,6 +1102,21 @@ function renderPreloadLink(document, file) {
1125
1102
  href: file,
1126
1103
  crossOrigin: ""
1127
1104
  });
1105
+ } else if (file.endsWith(".woff") || file.endsWith(".woff2") || file.endsWith(".ttf")) {
1106
+ appendLink(document, {
1107
+ rel: "preload",
1108
+ as: "font",
1109
+ type: "font/woff2",
1110
+ href: file,
1111
+ crossOrigin: ""
1112
+ });
1113
+ } else if (file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg") || file.endsWith(".webp") || file.endsWith(".gif") || file.endsWith(".ico") || file.endsWith(".svg")) {
1114
+ appendLink(document, {
1115
+ rel: "preload",
1116
+ as: "image",
1117
+ href: file,
1118
+ crossOrigin: ""
1119
+ });
1128
1120
  }
1129
1121
  }
1130
1122
  function createLink(document) {
@@ -1144,6 +1136,69 @@ function appendLink(document, attrs) {
1144
1136
  document.head.appendChild(link);
1145
1137
  }
1146
1138
 
1139
+ const DYNAMIC_IMPORT_REGEX = /import\("([^)]+)"\)/g;
1140
+ function collectAssets({
1141
+ routes,
1142
+ locationArg,
1143
+ base,
1144
+ serverManifest,
1145
+ manifest,
1146
+ ssrManifest
1147
+ }) {
1148
+ const matches = matchRoutes([...routes], locationArg, base);
1149
+ const routeEntries = matches?.map((item) => item.route.entry).filter(Boolean) ?? [];
1150
+ const dynamicImports = /* @__PURE__ */ new Set();
1151
+ matches?.forEach((item) => {
1152
+ let lazyStr = "";
1153
+ if (item.route.lazy) {
1154
+ lazyStr += item.route.lazy.toString();
1155
+ }
1156
+ if (item.route.Component?._payload?._result) {
1157
+ lazyStr += item.route.Component._payload._result.toString();
1158
+ }
1159
+ const match = lazyStr.matchAll(DYNAMIC_IMPORT_REGEX);
1160
+ for (const m of match) {
1161
+ dynamicImports.add(m[1].split("/").at(-1) ?? "");
1162
+ }
1163
+ });
1164
+ const entries = /* @__PURE__ */ new Set();
1165
+ routeEntries.forEach((e) => entries.add(e));
1166
+ const manifestEntries = [...Object.entries(serverManifest)];
1167
+ dynamicImports.forEach((name) => {
1168
+ const result = manifestEntries.find(([_, value]) => value.file.endsWith(name));
1169
+ if (result) {
1170
+ entries.add(result[0]);
1171
+ }
1172
+ });
1173
+ const modules = collectModulesForEntries(manifest, entries);
1174
+ const assets = /* @__PURE__ */ new Set();
1175
+ Array.from(modules).forEach((id) => {
1176
+ const files = ssrManifest[id] || [];
1177
+ files.forEach((file) => {
1178
+ assets.add(file);
1179
+ });
1180
+ });
1181
+ return assets;
1182
+ }
1183
+ function collectModulesForEntries(manifest, entries) {
1184
+ const mods = /* @__PURE__ */ new Set();
1185
+ if (!entries)
1186
+ return mods;
1187
+ for (const entry of entries)
1188
+ collectModules(manifest, entry, mods);
1189
+ return mods;
1190
+ }
1191
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1192
+ if (!entry)
1193
+ return mods;
1194
+ mods.add(entry);
1195
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1196
+ collectModules(manifest, item, mods);
1197
+ });
1198
+ return mods;
1199
+ }
1200
+
1201
+ const dotVitedir = Number.parseInt(version$1) >= 5 ? [".vite"] : [];
1147
1202
  function DefaultIncludedRoutes(paths, _routes) {
1148
1203
  return paths.filter((i) => !i.includes(":") && !i.includes("*"));
1149
1204
  }
@@ -1213,6 +1268,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1213
1268
  await build$1(mergeConfig(viteConfig, {
1214
1269
  build: {
1215
1270
  ssr: ssrEntry,
1271
+ manifest: true,
1216
1272
  outDir: ssgOut,
1217
1273
  minify: false,
1218
1274
  cssCodeSplit: false,
@@ -1232,11 +1288,12 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1232
1288
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1233
1289
  const ext = format === "esm" ? ".mjs" : ".cjs";
1234
1290
  const serverEntry = join(prefix, ssgOut, parse(ssrEntry).name + ext);
1291
+ const serverManifest = JSON.parse(await fs.readFile(join(ssgOut, ...dotVitedir, "manifest.json"), "utf-8"));
1235
1292
  const _require = createRequire(import.meta.url);
1236
1293
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1237
1294
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1238
1295
  const { routes } = await createRoot(false);
1239
- const { paths, pathToEntry } = await routesToPaths(routes);
1296
+ const { paths } = await routesToPaths(routes);
1240
1297
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1241
1298
  routesPaths = DefaultIncludedRoutes(routesPaths);
1242
1299
  routesPaths = Array.from(new Set(routesPaths));
@@ -1244,7 +1301,6 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1244
1301
  const critters = crittersOptions !== false ? await getCritters(outDir, { publicPath: configBase, ...crittersOptions }) : void 0;
1245
1302
  if (critters)
1246
1303
  console.log(`${gray("[vite-react-ssg]")} ${blue("Critical CSS generation enabled via `critters`")}`);
1247
- const dotVitedir = Number.parseInt(version$1) >= 5 ? [".vite"] : [];
1248
1304
  const ssrManifest = JSON.parse(await fs.readFile(join(out, ...dotVitedir, "ssr-manifest.json"), "utf-8"));
1249
1305
  const manifest = JSON.parse(await fs.readFile(join(out, ...dotVitedir, "manifest.json"), "utf-8"));
1250
1306
  let indexHTML = await fs.readFile(join(out, "index.html"), "utf-8");
@@ -1261,6 +1317,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1261
1317
  const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1262
1318
  const fetchUrl = `${withTrailingSlash(base)}${removeLeadingSlash(path)}`;
1263
1319
  const request = createRequest(fetchUrl);
1320
+ const assets = !app ? collectAssets({ routes: [...routes2], locationArg: fetchUrl, base, serverManifest, manifest, ssrManifest }) : /* @__PURE__ */ new Set();
1264
1321
  const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
1265
1322
  staticLoaderDataManifest[path] = routerContext?.loaderData;
1266
1323
  await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
@@ -1274,8 +1331,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1274
1331
  initialState: null
1275
1332
  });
1276
1333
  const jsdom = new JSDOM(renderedHTML);
1277
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1278
- renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1334
+ renderPreloadLinks(jsdom.window.document, assets);
1279
1335
  const html = jsdom.serialize();
1280
1336
  let transformed = await onPageRendered?.(path, html, appCtx) || html;
1281
1337
  transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
@@ -1349,23 +1405,6 @@ async function formatHtml(html, formatting) {
1349
1405
  }
1350
1406
  return html;
1351
1407
  }
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
1408
 
1370
1409
  function invariant(value, message) {
1371
1410
  if (value === false || value === null || typeof value === "undefined") {
@@ -1481,11 +1520,10 @@ function ssrServerPlugin({
1481
1520
  const indexHTML = await server.transformIndexHtml(url, template);
1482
1521
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1483
1522
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1484
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1523
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
1485
1524
  metaAttributes.push(styleTag);
1486
- const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
1487
1525
  const mods = await Promise.all(
1488
- [ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1526
+ [ssrEntry, entry].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
1489
1527
  );
1490
1528
  const assetsUrls = /* @__PURE__ */ new Set();
1491
1529
  const collectAssets = async (mod) => {
@@ -1494,7 +1532,7 @@ function ssrServerPlugin({
1494
1532
  const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1495
1533
  const allDeps = [...deps, ...dynamicDeps];
1496
1534
  for (const dep of allDeps) {
1497
- if (dep.endsWith(".css")) {
1535
+ if (dep.endsWith(".css") || dep.endsWith(".scss") || dep.endsWith(".sass") || dep.endsWith(".less")) {
1498
1536
  assetsUrls.add(dep);
1499
1537
  } else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
1500
1538
  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.1",
5
5
  "packageManager": "pnpm@9.4.0",
6
6
  "description": "",
7
7
  "author": "Riri <Daydreamerriri@outlook.com>",