vite-react-ssg 0.0.1 → 0.0.3

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.
@@ -1,84 +1,84 @@
1
1
  import JSDOM from 'jsdom';
2
2
 
3
- /*
4
- MIT License
5
-
6
- Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
7
- jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
8
-
9
- Permission is hereby granted, free of charge, to any person obtaining a copy
10
- of this software and associated documentation files (the "Software"), to deal
11
- in the Software without restriction, including without limitation the rights
12
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- copies of the Software, and to permit persons to whom the Software is
14
- furnished to do so, subject to the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be included in all
17
- copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
- SOFTWARE.
26
- */
27
-
28
-
29
- const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
30
-
31
- // define this here so that we only ever dynamically populate KEYS once.
32
-
33
- const KEYS = [];
34
-
35
- function jsdomGlobal(html = defaultHtml, options = {}) {
36
- // Idempotency
37
- if (global.navigator
38
- && global.navigator.userAgent
39
- && global.navigator.userAgent.includes('Node.js')
40
- && global.document
41
- && typeof global.document.destroy === 'function')
42
- return global.document.destroy
43
-
44
- // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
45
- if (!('url' in options))
46
- Object.assign(options, { url: 'http://localhost:3000' });
47
-
48
- // enable pretendToBeVisual by default since react needs
49
- // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
50
- if (!('pretendToBeVisual' in options))
51
- Object.assign(options, { pretendToBeVisual: true });
52
-
53
- const jsdom = new JSDOM.JSDOM(html, options);
54
- const { window } = jsdom;
55
- const { document } = window;
56
-
57
- // generate our list of keys by enumerating document.window - this list may vary
58
- // based on the jsdom version. filter out internal methods as well as anything
59
- // that node already defines
60
-
61
- if (KEYS.length === 0) {
62
- KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
63
- // going to add our jsdom instance, see below
64
- KEYS.push('$jsdom');
65
- }
66
-
67
- KEYS.forEach(key => global[key] = window[key]);
68
-
69
- // setup document / window / window.console
70
- global.document = document;
71
- global.window = window;
72
- window.console = global.console;
73
-
74
- // add access to our jsdom instance
75
- global.$jsdom = jsdom;
76
-
77
- const cleanup = () => KEYS.forEach(key => delete global[key]);
78
-
79
- document.destroy = cleanup;
80
-
81
- return cleanup
3
+ /*
4
+ MIT License
5
+
6
+ Copyright for portions of global-jsdom are held by Rico Sta. Cruz, 2016 as part of
7
+ jsdom-global. All other copyright for global-jsdom are held by jonathan schatz, 2017.
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+ */
27
+
28
+
29
+ const defaultHtml = '<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>';
30
+
31
+ // define this here so that we only ever dynamically populate KEYS once.
32
+
33
+ const KEYS = [];
34
+
35
+ function jsdomGlobal(html = defaultHtml, options = {}) {
36
+ // Idempotency
37
+ if (global.navigator
38
+ && global.navigator.userAgent
39
+ && global.navigator.userAgent.includes('Node.js')
40
+ && global.document
41
+ && typeof global.document.destroy === 'function')
42
+ return global.document.destroy
43
+
44
+ // set a default url if we don't get one - otherwise things explode when we copy localstorage keys
45
+ if (!('url' in options))
46
+ Object.assign(options, { url: 'http://localhost:3000' });
47
+
48
+ // enable pretendToBeVisual by default since react needs
49
+ // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser
50
+ if (!('pretendToBeVisual' in options))
51
+ Object.assign(options, { pretendToBeVisual: true });
52
+
53
+ const jsdom = new JSDOM.JSDOM(html, options);
54
+ const { window } = jsdom;
55
+ const { document } = window;
56
+
57
+ // generate our list of keys by enumerating document.window - this list may vary
58
+ // based on the jsdom version. filter out internal methods as well as anything
59
+ // that node already defines
60
+
61
+ if (KEYS.length === 0) {
62
+ KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_')).filter(k => !(k in global)));
63
+ // going to add our jsdom instance, see below
64
+ KEYS.push('$jsdom');
65
+ }
66
+
67
+ KEYS.forEach(key => global[key] = window[key]);
68
+
69
+ // setup document / window / window.console
70
+ global.document = document;
71
+ global.window = window;
72
+ window.console = global.console;
73
+
74
+ // add access to our jsdom instance
75
+ global.$jsdom = jsdom;
76
+
77
+ const cleanup = () => KEYS.forEach(key => delete global[key]);
78
+
79
+ document.destroy = cleanup;
80
+
81
+ return cleanup
82
82
  }
83
83
 
84
84
  export { jsdomGlobal };
package/dist/index.cjs CHANGED
@@ -25,7 +25,7 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
25
25
  rootContainer = "#root"
26
26
  } = options;
27
27
  const isClient = typeof window !== "undefined";
28
- async function createApp(client = false, routePath) {
28
+ async function createRoot(client = false, routePath) {
29
29
  const browserRouter = client ? reactRouterDom.createBrowserRouter(routerOptions.routes) : void 0;
30
30
  const appRenderCallbacks = [];
31
31
  const onSSRAppRendered = client ? () => {
@@ -63,11 +63,11 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
63
63
  if (isClient) {
64
64
  (async () => {
65
65
  const container = typeof rootContainer === "string" ? document.querySelector(rootContainer) : rootContainer;
66
- const { router } = await createApp(true);
66
+ const { router } = await createRoot(true);
67
67
  client.hydrateRoot(container, /* @__PURE__ */ React__default.createElement(reactHelmetAsync.HelmetProvider, null, /* @__PURE__ */ React__default.createElement(SiteMetadataDefaults.SiteMetadataDefaults, null), /* @__PURE__ */ React__default.createElement(reactRouterDom.RouterProvider, { router })));
68
68
  })();
69
69
  }
70
- return createApp;
70
+ return createRoot;
71
71
  }
72
72
 
73
73
  exports.Head = SiteMetadataDefaults.Head;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-fa5fe9c2.js';
1
+ import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-82f34756.js';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, b as ViteReactSSGOptions } from './types-82f34756.js';
2
3
  import { ReactNode } from 'react';
3
4
  import { HelmetProps } from 'react-helmet-async';
4
5
  import 'critters';
@@ -11,4 +12,4 @@ declare function Head(props: Props): JSX.Element;
11
12
 
12
13
  declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
13
14
 
14
- export { Head, ViteReactSSG };
15
+ export { Head, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
package/dist/index.mjs CHANGED
@@ -20,7 +20,7 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
20
20
  rootContainer = "#root"
21
21
  } = options;
22
22
  const isClient = typeof window !== "undefined";
23
- async function createApp(client = false, routePath) {
23
+ async function createRoot(client = false, routePath) {
24
24
  const browserRouter = client ? createBrowserRouter(routerOptions.routes) : void 0;
25
25
  const appRenderCallbacks = [];
26
26
  const onSSRAppRendered = client ? () => {
@@ -58,11 +58,11 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
58
58
  if (isClient) {
59
59
  (async () => {
60
60
  const container = typeof rootContainer === "string" ? document.querySelector(rootContainer) : rootContainer;
61
- const { router } = await createApp(true);
61
+ const { router } = await createRoot(true);
62
62
  hydrateRoot(container, /* @__PURE__ */ React.createElement(HelmetProvider, null, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(RouterProvider, { router })));
63
63
  })();
64
64
  }
65
- return createApp;
65
+ return createRoot;
66
66
  }
67
67
 
68
68
  export { ViteReactSSG };
package/dist/node/cli.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const kolorist = require('kolorist');
4
4
  const yargs = require('yargs');
5
5
  const helpers = require('yargs/helpers');
6
- const build = require('../shared/vite-react-ssg.1128b41a.cjs');
6
+ const build = require('../shared/vite-react-ssg.a5dfecac.cjs');
7
7
  require('node:path');
8
8
  require('node:module');
9
9
  require('fs-extra');
package/dist/node/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { gray, bold, red, reset, underline } from 'kolorist';
2
2
  import yargs from 'yargs';
3
3
  import { hideBin } from 'yargs/helpers';
4
- import { b as build } from '../shared/vite-react-ssg.c2c344c3.mjs';
4
+ import { b as build } from '../shared/vite-react-ssg.524ba00d.mjs';
5
5
  import 'node:path';
6
6
  import 'node:module';
7
7
  import 'fs-extra';
package/dist/node.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const build = require('./shared/vite-react-ssg.1128b41a.cjs');
3
+ const build = require('./shared/vite-react-ssg.a5dfecac.cjs');
4
4
  require('node:path');
5
5
  require('node:module');
6
6
  require('kolorist');
package/dist/node.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './types-fa5fe9c2.js';
2
+ import { b as ViteReactSSGOptions } from './types-82f34756.js';
3
3
  import 'critters';
4
- import 'react';
5
4
  import 'react-router-dom';
6
5
 
7
6
  declare function build(ssgOptions?: Partial<ViteReactSSGOptions>, viteConfig?: InlineConfig): Promise<void>;
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { b as build } from './shared/vite-react-ssg.c2c344c3.mjs';
1
+ export { b as build } from './shared/vite-react-ssg.524ba00d.mjs';
2
2
  import 'node:path';
3
3
  import 'node:module';
4
4
  import 'kolorist';
@@ -858,23 +858,40 @@ function getSize(str) {
858
858
  return `${(str.length / 1024).toFixed(2)} KiB`;
859
859
  }
860
860
  function routesToPaths(routes) {
861
+ const pathToEntry = {};
862
+ function addEntry(path, entry) {
863
+ if (!entry)
864
+ return;
865
+ if (pathToEntry[path])
866
+ pathToEntry[path].add(entry);
867
+ else
868
+ pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
869
+ }
861
870
  if (!routes)
862
- return ["/"];
871
+ return { paths: ["/"], pathToEntry };
863
872
  const paths = /* @__PURE__ */ new Set();
864
873
  const getPaths = (routes2, prefix = "") => {
874
+ const parentPath = prefix;
865
875
  prefix = prefix.replace(/\/$/g, "");
866
876
  for (const route of routes2) {
867
877
  let path = route.path;
868
878
  if (route.path != null) {
869
879
  path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
870
880
  paths.add(path);
881
+ addEntry(path, route.entry);
882
+ if (pathToEntry[parentPath]) {
883
+ const pathCopy = path;
884
+ pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
885
+ }
871
886
  }
887
+ if (route.index)
888
+ addEntry(prefix, route.entry);
872
889
  if (Array.isArray(route.children))
873
890
  getPaths(route.children, path);
874
891
  }
875
892
  };
876
893
  getPaths(routes);
877
- return Array.from(paths);
894
+ return { paths: Array.from(paths), pathToEntry };
878
895
  }
879
896
 
880
897
  async function getCritters(outDir, options = {}) {
@@ -946,17 +963,18 @@ async function render(routes, request) {
946
963
  throw context;
947
964
  const router = createStaticRouter(dataRoutes, context);
948
965
  const app = /* @__PURE__ */ React.createElement(HelmetProvider, { context: helmetContext }, /* @__PURE__ */ React.createElement(SiteMetadataDefaults, null), /* @__PURE__ */ React.createElement(StaticRouterProvider, { router, context }));
949
- const appHtml = await renderStaticApp(app);
966
+ const appHTML = await renderStaticApp(app);
950
967
  const { helmet } = helmetContext;
951
- helmet.htmlAttributes.toString();
952
- helmet.bodyAttributes.toString();
953
- [
968
+ const htmlAttributes = helmet.htmlAttributes.toString();
969
+ const bodyAttributes = helmet.bodyAttributes.toString();
970
+ const metaStrings = [
954
971
  helmet.title.toString(),
955
972
  helmet.meta.toString(),
956
973
  helmet.link.toString(),
957
974
  helmet.script.toString()
958
975
  ];
959
- return appHtml;
976
+ const metaAttributes = metaStrings.filter(Boolean);
977
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
960
978
  }
961
979
 
962
980
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1041,6 +1059,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1041
1059
  buildLog("Build for client...");
1042
1060
  await build$1(mergeConfig(viteConfig, {
1043
1061
  build: {
1062
+ manifest: true,
1044
1063
  ssrManifest: true,
1045
1064
  rollupOptions: {
1046
1065
  input: {
@@ -1079,41 +1098,46 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1079
1098
  const ext = format === "esm" ? ".mjs" : ".cjs";
1080
1099
  const serverEntry = join(prefix, ssgOut, parse(ssrEntry).name + ext);
1081
1100
  const _require = createRequire(import.meta.url);
1082
- const { createApp, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1101
+ const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1083
1102
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1084
- const { routes } = await createApp(false);
1085
- let routesPaths = includeAllRoutes ? routesToPaths(routes) : await includedRoutes(routesToPaths(routes), routes || []);
1103
+ const { routes } = await createRoot(false);
1104
+ const { paths, pathToEntry } = routesToPaths(routes);
1105
+ let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1086
1106
  routesPaths = Array.from(new Set(routesPaths));
1087
1107
  buildLog("Rendering Pages...", routesPaths.length);
1088
1108
  const critters = crittersOptions !== false ? await getCritters(outDir, crittersOptions) : void 0;
1089
1109
  if (critters)
1090
1110
  console.log(`${gray("[vite-react-ssg]")} ${blue("Critical CSS generation enabled via `critters`")}`);
1091
1111
  const ssrManifest = JSON.parse(await fs.readFile(join(out, "ssr-manifest.json"), "utf-8"));
1112
+ const manifest = JSON.parse(await fs.readFile(join(out, "manifest.json"), "utf-8"));
1092
1113
  let indexHTML = await fs.readFile(join(out, "index.html"), "utf-8");
1093
1114
  indexHTML = rewriteScripts(indexHTML, script);
1094
1115
  const queue = new PQueue({ concurrency });
1095
1116
  for (const route of routesPaths) {
1096
- console.log("\u{1F680} ~ file: build.ts:132 ~ build ~ routesPaths:", routesPaths);
1097
1117
  queue.add(async () => {
1098
1118
  try {
1099
- const appCtx = await createApp(false, route);
1100
- const { app: innerApp, routes: routes2, initialState, triggerOnSSRAppRendered, transformState = serializeState } = appCtx;
1119
+ const appCtx = await createRoot(false, route);
1120
+ const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = serializeState } = appCtx;
1101
1121
  const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx);
1102
1122
  const url = new URL(route, "http://vite-react-ssg.com");
1103
1123
  url.search = "";
1104
1124
  url.hash = "";
1105
1125
  url.pathname = route;
1106
1126
  const request = new Request(url.href);
1107
- const appHTML = await render([...routes2], request);
1127
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
1108
1128
  await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1109
1129
  const renderedHTML = await renderHTML({
1110
1130
  rootContainerId,
1111
1131
  appHTML,
1112
1132
  indexHTML,
1133
+ metaAttributes,
1134
+ bodyAttributes,
1135
+ htmlAttributes,
1113
1136
  initialState: null
1114
1137
  });
1115
1138
  const jsdom = new JSDOM(renderedHTML);
1116
- renderPreloadLinks(jsdom.window.document, /* @__PURE__ */ new Set(), ssrManifest);
1139
+ const modules = collectModulesForEntrys(manifest, pathToEntry?.[route]);
1140
+ renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1117
1141
  const html = jsdom.serialize();
1118
1142
  let transformed = await onPageRendered?.(route, html, appCtx) || html;
1119
1143
  if (critters)
@@ -1174,10 +1198,20 @@ async function renderHTML({
1174
1198
  rootContainerId,
1175
1199
  indexHTML,
1176
1200
  appHTML,
1201
+ metaAttributes,
1202
+ bodyAttributes,
1203
+ htmlAttributes,
1177
1204
  initialState
1178
1205
  }) {
1179
1206
  const stateScript = initialState ? `
1180
1207
  <script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
1208
+ const headStartTag = "<head>";
1209
+ const metaTags = metaAttributes.join("");
1210
+ indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
1211
+ const bodyStartTag = "<body";
1212
+ indexHTML = indexHTML.replace(bodyStartTag, `${bodyStartTag} ${bodyAttributes}`);
1213
+ const htmlStartTag = "<html";
1214
+ indexHTML = indexHTML.replace(htmlStartTag, `${htmlStartTag} ${htmlAttributes}`);
1181
1215
  const container = `<div id="${rootContainerId}"></div>`;
1182
1216
  if (indexHTML.includes(container)) {
1183
1217
  return indexHTML.replace(
@@ -1212,8 +1246,29 @@ async function formatHtml(html, formatting) {
1212
1246
  minifyJS: true,
1213
1247
  minifyCSS: true
1214
1248
  });
1249
+ } else if (formatting === "prettify") {
1250
+ const prettier = (await import('prettier/esm/standalone.mjs')).default;
1251
+ const parserHTML = (await import('prettier/esm/parser-html.mjs')).default;
1252
+ return prettier.format(html, { semi: false, parser: "html", plugins: [parserHTML] });
1215
1253
  }
1216
1254
  return html;
1217
1255
  }
1256
+ function collectModulesForEntrys(manifest, entrys) {
1257
+ const mods = /* @__PURE__ */ new Set();
1258
+ if (!entrys)
1259
+ return mods;
1260
+ for (const entry of entrys)
1261
+ collectModules(manifest, entry, mods);
1262
+ return mods;
1263
+ }
1264
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1265
+ if (!entry)
1266
+ return mods;
1267
+ mods.add(entry);
1268
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1269
+ collectModules(manifest, item, mods);
1270
+ });
1271
+ return mods;
1272
+ }
1218
1273
 
1219
1274
  export { build as b };
@@ -865,23 +865,40 @@ function getSize(str) {
865
865
  return `${(str.length / 1024).toFixed(2)} KiB`;
866
866
  }
867
867
  function routesToPaths(routes) {
868
+ const pathToEntry = {};
869
+ function addEntry(path, entry) {
870
+ if (!entry)
871
+ return;
872
+ if (pathToEntry[path])
873
+ pathToEntry[path].add(entry);
874
+ else
875
+ pathToEntry[path] = /* @__PURE__ */ new Set([entry]);
876
+ }
868
877
  if (!routes)
869
- return ["/"];
878
+ return { paths: ["/"], pathToEntry };
870
879
  const paths = /* @__PURE__ */ new Set();
871
880
  const getPaths = (routes2, prefix = "") => {
881
+ const parentPath = prefix;
872
882
  prefix = prefix.replace(/\/$/g, "");
873
883
  for (const route of routes2) {
874
884
  let path = route.path;
875
885
  if (route.path != null) {
876
886
  path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
877
887
  paths.add(path);
888
+ addEntry(path, route.entry);
889
+ if (pathToEntry[parentPath]) {
890
+ const pathCopy = path;
891
+ pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
892
+ }
878
893
  }
894
+ if (route.index)
895
+ addEntry(prefix, route.entry);
879
896
  if (Array.isArray(route.children))
880
897
  getPaths(route.children, path);
881
898
  }
882
899
  };
883
900
  getPaths(routes);
884
- return Array.from(paths);
901
+ return { paths: Array.from(paths), pathToEntry };
885
902
  }
886
903
 
887
904
  async function getCritters(outDir, options = {}) {
@@ -953,17 +970,18 @@ async function render(routes, request) {
953
970
  throw context;
954
971
  const router = server_js.createStaticRouter(dataRoutes, context);
955
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 }));
956
- const appHtml = await renderStaticApp(app);
973
+ const appHTML = await renderStaticApp(app);
957
974
  const { helmet } = helmetContext;
958
- helmet.htmlAttributes.toString();
959
- helmet.bodyAttributes.toString();
960
- [
975
+ const htmlAttributes = helmet.htmlAttributes.toString();
976
+ const bodyAttributes = helmet.bodyAttributes.toString();
977
+ const metaStrings = [
961
978
  helmet.title.toString(),
962
979
  helmet.meta.toString(),
963
980
  helmet.link.toString(),
964
981
  helmet.script.toString()
965
982
  ];
966
- return appHtml;
983
+ const metaAttributes = metaStrings.filter(Boolean);
984
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes };
967
985
  }
968
986
 
969
987
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1048,6 +1066,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1048
1066
  buildLog("Build for client...");
1049
1067
  await vite.build(vite.mergeConfig(viteConfig, {
1050
1068
  build: {
1069
+ manifest: true,
1051
1070
  ssrManifest: true,
1052
1071
  rollupOptions: {
1053
1072
  input: {
@@ -1085,42 +1104,47 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1085
1104
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1086
1105
  const ext = format === "esm" ? ".mjs" : ".cjs";
1087
1106
  const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
1088
- 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.1128b41a.cjs', document.baseURI).href)));
1089
- const { createApp, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
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)));
1108
+ const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1090
1109
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1091
- const { routes } = await createApp(false);
1092
- let routesPaths = includeAllRoutes ? routesToPaths(routes) : await includedRoutes(routesToPaths(routes), routes || []);
1110
+ const { routes } = await createRoot(false);
1111
+ const { paths, pathToEntry } = routesToPaths(routes);
1112
+ let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1093
1113
  routesPaths = Array.from(new Set(routesPaths));
1094
1114
  buildLog("Rendering Pages...", routesPaths.length);
1095
1115
  const critters = crittersOptions !== false ? await getCritters(outDir, crittersOptions) : void 0;
1096
1116
  if (critters)
1097
1117
  console.log(`${kolorist.gray("[vite-react-ssg]")} ${kolorist.blue("Critical CSS generation enabled via `critters`")}`);
1098
1118
  const ssrManifest = JSON.parse(await fs__default.readFile(node_path.join(out, "ssr-manifest.json"), "utf-8"));
1119
+ const manifest = JSON.parse(await fs__default.readFile(node_path.join(out, "manifest.json"), "utf-8"));
1099
1120
  let indexHTML = await fs__default.readFile(node_path.join(out, "index.html"), "utf-8");
1100
1121
  indexHTML = rewriteScripts(indexHTML, script);
1101
1122
  const queue = new PQueue({ concurrency });
1102
1123
  for (const route of routesPaths) {
1103
- console.log("\u{1F680} ~ file: build.ts:132 ~ build ~ routesPaths:", routesPaths);
1104
1124
  queue.add(async () => {
1105
1125
  try {
1106
- const appCtx = await createApp(false, route);
1107
- const { app: innerApp, routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState } = appCtx;
1126
+ const appCtx = await createRoot(false, route);
1127
+ const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState } = appCtx;
1108
1128
  const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx);
1109
1129
  const url = new URL(route, "http://vite-react-ssg.com");
1110
1130
  url.search = "";
1111
1131
  url.hash = "";
1112
1132
  url.pathname = route;
1113
1133
  const request = new Request(url.href);
1114
- const appHTML = await render([...routes2], request);
1134
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes } = await render([...routes2], request);
1115
1135
  await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1116
1136
  const renderedHTML = await renderHTML({
1117
1137
  rootContainerId,
1118
1138
  appHTML,
1119
1139
  indexHTML,
1140
+ metaAttributes,
1141
+ bodyAttributes,
1142
+ htmlAttributes,
1120
1143
  initialState: null
1121
1144
  });
1122
1145
  const jsdom = new JSDOM.JSDOM(renderedHTML);
1123
- renderPreloadLinks(jsdom.window.document, /* @__PURE__ */ new Set(), ssrManifest);
1146
+ const modules = collectModulesForEntrys(manifest, pathToEntry?.[route]);
1147
+ renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1124
1148
  const html = jsdom.serialize();
1125
1149
  let transformed = await onPageRendered?.(route, html, appCtx) || html;
1126
1150
  if (critters)
@@ -1181,10 +1205,20 @@ async function renderHTML({
1181
1205
  rootContainerId,
1182
1206
  indexHTML,
1183
1207
  appHTML,
1208
+ metaAttributes,
1209
+ bodyAttributes,
1210
+ htmlAttributes,
1184
1211
  initialState
1185
1212
  }) {
1186
1213
  const stateScript = initialState ? `
1187
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}`);
1188
1222
  const container = `<div id="${rootContainerId}"></div>`;
1189
1223
  if (indexHTML.includes(container)) {
1190
1224
  return indexHTML.replace(
@@ -1219,8 +1253,29 @@ async function formatHtml(html, formatting) {
1219
1253
  minifyJS: true,
1220
1254
  minifyCSS: true
1221
1255
  });
1256
+ } else if (formatting === "prettify") {
1257
+ const prettier = (await import('prettier/esm/standalone.mjs')).default;
1258
+ const parserHTML = (await import('prettier/esm/parser-html.mjs')).default;
1259
+ return prettier.format(html, { semi: false, parser: "html", plugins: [parserHTML] });
1222
1260
  }
1223
1261
  return html;
1224
1262
  }
1263
+ function collectModulesForEntrys(manifest, entrys) {
1264
+ const mods = /* @__PURE__ */ new Set();
1265
+ if (!entrys)
1266
+ return mods;
1267
+ for (const entry of entrys)
1268
+ collectModules(manifest, entry, mods);
1269
+ return mods;
1270
+ }
1271
+ function collectModules(manifest, entry, mods = /* @__PURE__ */ new Set()) {
1272
+ if (!entry)
1273
+ return mods;
1274
+ mods.add(entry);
1275
+ manifest[entry]?.dynamicImports?.forEach((item) => {
1276
+ collectModules(manifest, item, mods);
1277
+ });
1278
+ return mods;
1279
+ }
1225
1280
 
1226
1281
  exports.build = build;