vite-react-ssg 0.1.2 → 0.3.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
@@ -4,10 +4,25 @@ Static-site generation for React on Vite.
4
4
 
5
5
  [![NPM version](https://img.shields.io/npm/v/vite-react-ssg?color=a1b858&label=)](https://www.npmjs.com/package/vite-react-ssg)
6
6
 
7
- ## Install
7
+ # Table of contents
8
+ * [Usage](#usage)
9
+ * [Extra route options](#extra-route-options)
10
+ * [`entry`](#entry)
11
+ * [`getStaticPaths`](#getstaticpaths)
12
+ * [lazy](#lazy)
13
+ * [`<ClientOnly/>`](#clientonly)
14
+ * [Document head](#document-head)
15
+ * [Reactive head](#reactive-head)
16
+ * [CSS in JS](#css-in-js)
17
+ * [Critical CSS](#critical-css)
18
+ * [Configuration](#configuration)
19
+ * [Custom Routes to Render](#custom-routes-to-render)
20
+ * [Use CSR in development environment](#use-csr-in-development-environment)
21
+ * [Roadmap](#roadmap)
22
+ * [Credits](#credits)
23
+
24
+ ## Usage
8
25
 
9
- > **This library requires Node.js version >= 17**
10
- > or `Request` is available
11
26
  <pre>
12
27
  <b>npm i -D vite-react-ssg</b> <em>react-router-dom</em>
13
28
  </pre>
@@ -48,40 +63,91 @@ import React from 'react'
48
63
  import type { RouteRecord } from 'vite-react-ssg'
49
64
  import './App.css'
50
65
 
51
- const pages = import.meta.glob<any>('./pages/**/*.tsx')
52
-
53
- const children: RouteRecord[] = Object.entries(pages).map(([filepath, component]) => {
54
- let path = filepath.split('/pages')[1]
55
- path = path.split('.')[0].replace('index', '')
56
- const entry = `src${filepath.slice(1)}`
57
-
58
- if (path.endsWith('/')) {
59
- return {
60
- index: true,
61
- Component: React.lazy(component),
62
- entry,
63
- }
64
- }
65
- return {
66
- path,
67
- Component: React.lazy(component),
68
- // Used to obtain static resources through manifest
69
- entry,
70
- }
71
- })
72
-
73
66
  const Layout = React.lazy(() => import('./Layout'))
67
+
74
68
  export const routes: RouteRecord[] = [
75
69
  {
76
70
  path: '/',
77
71
  element: <Layout />,
78
- children,
79
- // Used to obtain static resources through manifest
80
72
  entry: 'src/Layout.tsx',
73
+ children: [
74
+ {
75
+ path: 'a',
76
+ Component: React.lazy(() => import('./pages/a')),
77
+ entry: 'src/pages/a.tsx',
78
+ },
79
+ {
80
+ index: true,
81
+ Component: React.lazy(() => import('./pages/index')),
82
+ // Used to obtain static resources through manifest
83
+ entry: 'src/pages/index.tsx',
84
+ },
85
+ {
86
+ path: 'nest/:b',
87
+ Component: React.lazy(() => import('./pages/nest/[b]')),
88
+ entry: 'src/pages/nest/[b].tsx',
89
+ // To determine which paths will be pre-rendered
90
+ getStaticPaths: () => ['nest/b1', 'nest/b2'],
91
+ },
92
+ ],
81
93
  },
82
94
  ]
83
95
  ```
84
96
 
97
+ ## Extra route options
98
+
99
+ The RouteObject of vite-react-ssg is based on react-router, and vite-react-ssg receives some additional properties.
100
+
101
+ #### `entry`
102
+ 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. It should be the path from root to the target file.
103
+
104
+ eg: `src/pages/page1.tsx`
105
+
106
+ #### `getStaticPaths`
107
+ The `getStaticPaths()` function should return an array of path
108
+ to determine which paths will be pre-rendered by vite-react-ssg.
109
+
110
+ This function is only valid for dynamic route.
111
+
112
+ ```ts
113
+ const route = {
114
+ path: 'nest/:b',
115
+ Component: React.lazy(() => import('./pages/nest/[b]')),
116
+ entry: 'src/pages/nest/[b].tsx',
117
+ // To determine which paths will be pre-rendered
118
+ getStaticPaths: () => ['nest/b1', 'nest/b2'],
119
+ },
120
+ ```
121
+
122
+ ## lazy
123
+ These options work well with the `lazy` field.
124
+
125
+ ```tsx
126
+ // src/pages/[page].tsx
127
+ export function Component() {
128
+ return (
129
+ <div>{/* your component */}</div>
130
+ )
131
+ }
132
+
133
+ export function getStaticPaths() {
134
+ return ['page1', 'page2']
135
+ }
136
+
137
+ export const entry = 'src/pages/[page].tsx'
138
+ ```
139
+ ```ts
140
+ // src/routes.ts
141
+ const routes = [
142
+ {
143
+ path: '/:page',
144
+ lazy: () => import('./pages/[page]')
145
+ }
146
+ ]
147
+ ```
148
+
149
+ See [example](./examples/lazy-pages/src/App.tsx).
150
+
85
151
  ## `<ClientOnly/>`
86
152
 
87
153
  If you need to render some component in browser only, you can wrap your component with `<ClientOnly>`.
@@ -176,11 +242,14 @@ Use the `getStyleCollector` option to specify an SSR/SSG style collector. Curren
176
242
 
177
243
  ```tsx
178
244
  import { ViteReactSSG } from 'vite-react-ssg'
179
- import { getStyledComponentsCollector } from 'vite-react-ssg/style-collectors'
245
+ import getStyledComponentsCollector from 'vite-react-ssg/style-collectors/styled-components'
180
246
  import { routes } from './App.js'
181
247
  import './index.css'
182
248
 
183
- export const createRoot = ViteReactSSG({ routes }, () => {}, { getStyleCollector: getStyledComponentsCollector })
249
+ export const createRoot = ViteReactSSG(
250
+ { routes },
251
+ () => { },
252
+ { getStyleCollector: getStyledComponentsCollector })
184
253
  ```
185
254
 
186
255
  You can provide your own by looking at the [implementation](./src/style-collectors/) of any of the existing collectors.
@@ -226,6 +295,110 @@ export default {
226
295
  }
227
296
  ```
228
297
 
298
+ ```ts
299
+ interface ViteReactSSGOptions {
300
+ /**
301
+ * Set the scripts' loading mode. Only works for `type="module"`.
302
+ *
303
+ * @default 'sync'
304
+ */
305
+ script?: 'sync' | 'async' | 'defer' | 'async defer'
306
+ /**
307
+ * Build format.
308
+ *
309
+ * @default 'esm'
310
+ */
311
+ format?: 'esm' | 'cjs'
312
+ /**
313
+ * The path of the main entry file (relative to the project root).
314
+ *
315
+ * @default 'src/main.ts'
316
+ */
317
+ entry?: string
318
+ /**
319
+ * Mock browser global variables (window, document, etc...) from SSG.
320
+ *
321
+ * @default false
322
+ */
323
+ mock?: boolean
324
+ /**
325
+ * Apply formatter to the generated index file.
326
+ *
327
+ * **It will cause Hydration Failed.**
328
+ *
329
+ * @default 'none'
330
+ */
331
+ formatting?: 'minify' | 'prettify' | 'none'
332
+ /**
333
+ * Vite environmeng mode.
334
+ */
335
+ mode?: string
336
+ /**
337
+ * Directory style of the output directory.
338
+ *
339
+ * flat: `/foo` -> `/foo.html`
340
+ * nested: `/foo` -> `/foo/index.html`
341
+ *
342
+ * @default 'flat'
343
+ */
344
+ dirStyle?: 'flat' | 'nested'
345
+ /**
346
+ * Generate for all routes, including dynamic routes.
347
+ * If enabled, you will need to configGure your serve
348
+ * manually to handle dynamic routes properly.
349
+ *
350
+ * @default false
351
+ */
352
+ includeAllRoutes?: boolean
353
+ /**
354
+ * Options for the critters packages.
355
+ *
356
+ * @see https://github.com/GoogleChromeLabs/critters
357
+ */
358
+ crittersOptions?: CrittersOptions | false
359
+ /**
360
+ * Custom function to modify the routes to do the SSG.
361
+ *
362
+ * Works only when `includeAllRoutes` is set to false.
363
+ *
364
+ * Defaults to a handler that filters out all the dynamic routes.
365
+ * When passing your custom handler, you should also take care of the dynamic routes yourself.
366
+ */
367
+ includedRoutes?: (paths: string[], routes: Readonly<RouteRecord[]>) => Promise<string[]> | string[]
368
+ /**
369
+ * Callback to be called before every page render.
370
+ *
371
+ * It can be used to transform the project's `index.html` file before passing it to the renderer.
372
+ *
373
+ * To do so, you can change the 'index.html' file contents (passed in through the `indexHTML` parameter), and return it.
374
+ * The returned value will then be passed to renderer.
375
+ */
376
+ onBeforePageRender?: (route: string, indexHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
377
+ /**
378
+ * Callback to be called on every rendered page.
379
+ *
380
+ * It can be used to transform the current route's rendered HTML.
381
+ *
382
+ * To do so, you can transform the route's rendered HTML (passed in through the `renderedHTML` parameter), and return it.
383
+ * The returned value will be used as the HTML of the route.
384
+ */
385
+ onPageRendered?: (route: string, renderedHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined
386
+
387
+ onFinished?: () => Promise<void> | void
388
+ /**
389
+ * The application's root container `id`.
390
+ *
391
+ * @default `root`
392
+ */
393
+ rootContainerId?: string
394
+ /**
395
+ * The size of the SSG processing queue.
396
+ *
397
+ * @default 20
398
+ */
399
+ concurrency?: number
400
+ }
401
+ ```
229
402
  See [src/types.ts](./src/types.ts). for more options available.
230
403
 
231
404
  ### Custom Routes to Render
@@ -300,8 +473,12 @@ Then, you can start the application with CSR in the development environment.
300
473
  - [x] Preload assets
301
474
  - [x] Document head
302
475
  - [x] SSR in dev environment
476
+ - [x] More Client components, such as `<ClientOnly />`
477
+ - [x] `getStaticPaths` for dynamic routes
303
478
  - [ ] Initial State
304
- - [ ] More Client components, such as `<ClientOnly />`
479
+
480
+ ## Credits
481
+ This project inspired by [vite-ssg](https://github.com/antfu/vite-ssg), thanks to [@antfu](https://github.com/antfu) for his awesome work.
305
482
 
306
483
  ## License
307
484
 
package/dist/index.cjs CHANGED
@@ -157,6 +157,17 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
157
157
  root.render(app);
158
158
  });
159
159
  } else {
160
+ const lazeMatches = reactRouterDom.matchRoutes(routerOptions.routes, window.location)?.filter(
161
+ (m) => m.route.lazy
162
+ );
163
+ if (lazeMatches && lazeMatches?.length > 0) {
164
+ await Promise.all(
165
+ lazeMatches.map(async (m) => {
166
+ const routeModule = await m.route.lazy();
167
+ Object.assign(m.route, { ...routeModule, lazy: void 0 });
168
+ })
169
+ );
170
+ }
160
171
  React__default.startTransition(() => {
161
172
  client.hydrateRoot(container, app);
162
173
  });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-408e974a.js';
2
- export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './types-408e974a.js';
1
+ import { R as RouterOptions, V as ViteReactSSGContext, a as ViteReactSSGClientOptions } from './types-c03d6a58.js';
2
+ export { I as IndexRouteRecord, N as NonIndexRouteRecord, c as RouteRecord, S as StyleCollector, b as ViteReactSSGOptions } from './types-c03d6a58.js';
3
3
  import React, { ReactNode } from 'react';
4
4
  import { HelmetProps } from 'react-helmet-async';
5
5
  import { LinkProps, NavLinkProps } from 'react-router-dom';
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useEffect, isValidElement, forwardRef } from 'react';
2
2
  import { createRoot, hydrateRoot } from 'react-dom/client';
3
3
  import { HelmetProvider } from 'react-helmet-async';
4
- import { useLinkClickHandler, Link as Link$1, NavLink as NavLink$1, createBrowserRouter, RouterProvider } from 'react-router-dom';
4
+ import { useLinkClickHandler, Link as Link$1, NavLink as NavLink$1, matchRoutes, createBrowserRouter, RouterProvider } from 'react-router-dom';
5
5
  import { d as deserializeState, S as SiteMetadataDefaults } from './shared/vite-react-ssg.9d005d5e.mjs';
6
6
  export { H as Head } from './shared/vite-react-ssg.9d005d5e.mjs';
7
7
 
@@ -152,6 +152,17 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
152
152
  root.render(app);
153
153
  });
154
154
  } else {
155
+ const lazeMatches = matchRoutes(routerOptions.routes, window.location)?.filter(
156
+ (m) => m.route.lazy
157
+ );
158
+ if (lazeMatches && lazeMatches?.length > 0) {
159
+ await Promise.all(
160
+ lazeMatches.map(async (m) => {
161
+ const routeModule = await m.route.lazy();
162
+ Object.assign(m.route, { ...routeModule, lazy: void 0 });
163
+ })
164
+ );
165
+ }
155
166
  React.startTransition(() => {
156
167
  hydrateRoot(container, app);
157
168
  });
package/dist/node/cli.cjs CHANGED
@@ -3,7 +3,8 @@
3
3
  const kolorist = require('kolorist');
4
4
  const yargs = require('yargs');
5
5
  const helpers = require('yargs/helpers');
6
- const dev = require('../shared/vite-react-ssg.b0fd1823.cjs');
6
+ const webFetch = require('@remix-run/web-fetch');
7
+ const dev = require('../shared/vite-react-ssg.677d2862.cjs');
7
8
  require('node:path');
8
9
  require('node:module');
9
10
  require('fs-extra');
@@ -22,6 +23,44 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
22
23
 
23
24
  const yargs__default = /*#__PURE__*/_interopDefaultCompat(yargs);
24
25
 
26
+ class NodeRequest extends webFetch.Request {
27
+ constructor(info, init) {
28
+ super(info, init);
29
+ }
30
+ get headers() {
31
+ return super.headers;
32
+ }
33
+ clone() {
34
+ return new NodeRequest(this);
35
+ }
36
+ }
37
+ class NodeResponse extends webFetch.Response {
38
+ get headers() {
39
+ return super.headers;
40
+ }
41
+ clone() {
42
+ return super.clone();
43
+ }
44
+ }
45
+ const fetch = (info, init) => {
46
+ init = {
47
+ // Disable compression handling so people can return the result of a fetch
48
+ // directly in the loader without messing with the Content-Encoding header.
49
+ compress: false,
50
+ ...init
51
+ };
52
+ return webFetch.fetch(info, init);
53
+ };
54
+
55
+ function installGlobals() {
56
+ global.Headers = webFetch.Headers;
57
+ global.Request = NodeRequest;
58
+ global.Response = NodeResponse;
59
+ global.fetch = fetch;
60
+ global.FormData = webFetch.FormData;
61
+ }
62
+
63
+ installGlobals();
25
64
  yargs__default(helpers.hideBin(process.argv)).scriptName("vite-react-ssg").usage("$0 [args]").command(
26
65
  "build",
27
66
  "Build SSG",
package/dist/node/cli.mjs CHANGED
@@ -1,7 +1,8 @@
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, d as dev } from '../shared/vite-react-ssg.b83f6ed3.mjs';
4
+ import { Request, Response, fetch as fetch$1, Headers, FormData } from '@remix-run/web-fetch';
5
+ import { b as build, d as dev } from '../shared/vite-react-ssg.eb444549.mjs';
5
6
  import 'node:path';
6
7
  import 'node:module';
7
8
  import 'fs-extra';
@@ -16,6 +17,44 @@ import 'react-router-dom/server.js';
16
17
  import 'node:stream';
17
18
  import 'react-dom/server';
18
19
 
20
+ class NodeRequest extends Request {
21
+ constructor(info, init) {
22
+ super(info, init);
23
+ }
24
+ get headers() {
25
+ return super.headers;
26
+ }
27
+ clone() {
28
+ return new NodeRequest(this);
29
+ }
30
+ }
31
+ class NodeResponse extends Response {
32
+ get headers() {
33
+ return super.headers;
34
+ }
35
+ clone() {
36
+ return super.clone();
37
+ }
38
+ }
39
+ const fetch = (info, init) => {
40
+ init = {
41
+ // Disable compression handling so people can return the result of a fetch
42
+ // directly in the loader without messing with the Content-Encoding header.
43
+ compress: false,
44
+ ...init
45
+ };
46
+ return fetch$1(info, init);
47
+ };
48
+
49
+ function installGlobals() {
50
+ global.Headers = Headers;
51
+ global.Request = NodeRequest;
52
+ global.Response = NodeResponse;
53
+ global.fetch = fetch;
54
+ global.FormData = FormData;
55
+ }
56
+
57
+ installGlobals();
19
58
  yargs(hideBin(process.argv)).scriptName("vite-react-ssg").usage("$0 [args]").command(
20
59
  "build",
21
60
  "Build SSG",
package/dist/node.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const dev = require('./shared/vite-react-ssg.b0fd1823.cjs');
3
+ const dev = require('./shared/vite-react-ssg.677d2862.cjs');
4
4
  require('node:path');
5
5
  require('node:module');
6
6
  require('kolorist');
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InlineConfig } from 'vite';
2
- import { b as ViteReactSSGOptions } from './types-408e974a.js';
2
+ import { b as ViteReactSSGOptions } from './types-c03d6a58.js';
3
3
  import 'critters';
4
4
  import 'react';
5
5
  import 'react-router-dom';
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { b as build, d as dev } from './shared/vite-react-ssg.b83f6ed3.mjs';
1
+ export { b as build, d as dev } from './shared/vite-react-ssg.eb444549.mjs';
2
2
  import 'node:path';
3
3
  import 'node:module';
4
4
  import 'kolorist';
@@ -872,6 +872,8 @@ function routesToPaths(routes) {
872
872
  function addEntry(path, entry) {
873
873
  if (!entry)
874
874
  return;
875
+ if (entry[0] === "/")
876
+ entry = entry.slice(1);
875
877
  if (pathToEntry[path])
876
878
  pathToEntry[path].add(entry);
877
879
  else
@@ -880,20 +882,22 @@ function routesToPaths(routes) {
880
882
  if (!routes)
881
883
  return { paths: ["/"], pathToEntry };
882
884
  const paths = /* @__PURE__ */ new Set();
885
+ const lazyPaths = /* @__PURE__ */ new Set();
883
886
  const getPaths = (routes2, prefix = "") => {
884
- const parentPath = prefix;
885
887
  prefix = prefix.replace(/\/$/g, "");
886
888
  for (const route of routes2) {
887
889
  let path = route.path;
888
- if (route.path != null) {
889
- path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
890
- paths.add(path);
891
- addEntry(path, route.entry);
892
- if (pathToEntry[parentPath]) {
893
- const pathCopy = path;
894
- pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
890
+ path = handlePath(path, prefix, route.entry);
891
+ if (route.getStaticPaths && path?.includes(":")) {
892
+ const staticPaths = route.getStaticPaths();
893
+ for (let staticPath of staticPaths) {
894
+ staticPath = handlePath(staticPath, prefix, route.entry);
895
+ if (Array.isArray(route.children))
896
+ getPaths(route.children, staticPath);
895
897
  }
896
898
  }
899
+ if (route.lazy)
900
+ lazyPaths.add(route.index ? prefix : path ?? "");
897
901
  if (route.index)
898
902
  addEntry(prefix, route.entry);
899
903
  if (Array.isArray(route.children))
@@ -901,7 +905,19 @@ function routesToPaths(routes) {
901
905
  }
902
906
  };
903
907
  getPaths(routes);
904
- return { paths: Array.from(paths), pathToEntry };
908
+ return { paths: Array.from(paths), pathToEntry, lazyPaths: Array.from(lazyPaths) };
909
+ function handlePath(path, prefix, entry) {
910
+ if (path != null) {
911
+ path = prefix && !path.startsWith("/") ? `${prefix}${path ? `/${path}` : ""}` : path;
912
+ paths.add(path);
913
+ addEntry(path, entry);
914
+ if (pathToEntry[prefix]) {
915
+ const pathCopy = path;
916
+ pathToEntry[prefix].forEach((entry2) => addEntry(pathCopy, entry2));
917
+ }
918
+ }
919
+ return path;
920
+ }
905
921
  }
906
922
  function createFetchRequest(req) {
907
923
  const origin = `${req.protocol}://${req.get("host")}`;
@@ -934,8 +950,15 @@ async function resolveAlias(config, entry) {
934
950
  return result || node_path.join(config.root, entry);
935
951
  }
936
952
  const { version } = JSON.parse(
937
- node_fs.readFileSync(new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.b0fd1823.cjs', document.baseURI).href)))).toString()
953
+ node_fs.readFileSync(new URL("../../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-react-ssg.677d2862.cjs', document.baseURI).href)))).toString()
938
954
  );
955
+ function createRequest(path) {
956
+ const url = new URL(path, "http://vite-react-ssg.com");
957
+ url.search = "";
958
+ url.hash = "";
959
+ url.pathname = path;
960
+ return new Request(url.href);
961
+ }
939
962
 
940
963
  async function getCritters(outDir, options = {}) {
941
964
  try {
@@ -1020,7 +1043,17 @@ async function render(routes, request, styleCollector) {
1020
1043
  ];
1021
1044
  const styleTag = styleCollector?.toString?.(appHTML) ?? "";
1022
1045
  const metaAttributes = metaStrings.filter(Boolean);
1023
- return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag };
1046
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag, routerContext: context };
1047
+ }
1048
+ async function preLoad(routes, paths) {
1049
+ if (!paths || paths.length === 0)
1050
+ return routes;
1051
+ const { dataRoutes, query } = server_js.createStaticHandler(routes);
1052
+ await Promise.all(paths.map(async (path) => {
1053
+ const request = createRequest(path);
1054
+ return query(request);
1055
+ }));
1056
+ return dataRoutes;
1024
1057
  }
1025
1058
 
1026
1059
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1200,12 +1233,15 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1200
1233
  const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
1201
1234
  const ext = format === "esm" ? ".mjs" : ".cjs";
1202
1235
  const serverEntry = node_path.join(prefix, ssgOut, node_path.parse(ssrEntry).name + ext);
1203
- 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.b0fd1823.cjs', document.baseURI).href)));
1236
+ 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.677d2862.cjs', document.baseURI).href)));
1204
1237
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1205
1238
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1206
1239
  const { routes } = await createRoot(false);
1207
- const { paths, pathToEntry } = routesToPaths(routes);
1240
+ const { lazyPaths } = routesToPaths(routes);
1241
+ const dataRoutes = await preLoad([...routes], lazyPaths);
1242
+ const { paths, pathToEntry } = routesToPaths(dataRoutes);
1208
1243
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1244
+ routesPaths = DefaultIncludedRoutes(routesPaths);
1209
1245
  routesPaths = Array.from(new Set(routesPaths));
1210
1246
  buildLog("Rendering Pages...", routesPaths.length);
1211
1247
  const critters = crittersOptions !== false ? await getCritters(outDir, crittersOptions) : void 0;
@@ -1216,20 +1252,16 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1216
1252
  let indexHTML = await fs__default.readFile(node_path.join(out, "index.html"), "utf-8");
1217
1253
  indexHTML = rewriteScripts(indexHTML, script);
1218
1254
  const queue = new PQueue({ concurrency });
1219
- for (const route of routesPaths) {
1255
+ for (const path of routesPaths) {
1220
1256
  queue.add(async () => {
1221
1257
  try {
1222
- const appCtx = await createRoot(false, route);
1223
- const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState, getStyleCollector } = appCtx;
1258
+ const appCtx = await createRoot(false, path);
1259
+ const { initialState, triggerOnSSRAppRendered, transformState = SiteMetadataDefaults.serializeState, getStyleCollector } = appCtx;
1224
1260
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1225
- const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
1226
- const url = new URL(route, "http://vite-react-ssg.com");
1227
- url.search = "";
1228
- url.hash = "";
1229
- url.pathname = route;
1230
- const request = new Request(url.href);
1231
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes2], request, styleCollector);
1232
- await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1261
+ const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1262
+ const request = createRequest(path);
1263
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(dataRoutes, request, styleCollector);
1264
+ await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
1233
1265
  const renderedHTML = await renderHTML({
1234
1266
  rootContainerId,
1235
1267
  appHTML,
@@ -1240,24 +1272,24 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1240
1272
  initialState: null
1241
1273
  });
1242
1274
  const jsdom = new JSDOM.JSDOM(renderedHTML);
1243
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[route]);
1275
+ const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1244
1276
  renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1245
1277
  const html = jsdom.serialize();
1246
- let transformed = await onPageRendered?.(route, html, appCtx) || html;
1278
+ let transformed = await onPageRendered?.(path, html, appCtx) || html;
1247
1279
  if (critters)
1248
1280
  transformed = await critters.process(transformed);
1249
1281
  if (styleTag)
1250
1282
  transformed = transformed.replace("<head>", `<head>${styleTag}`);
1251
1283
  const formatted = await formatHtml(transformed, formatting);
1252
- const relativeRouteFile = `${(route.endsWith("/") ? `${route}index` : route).replace(/^\//g, "")}.html`;
1253
- const filename = dirStyle === "nested" ? node_path.join(route.replace(/^\//g, ""), "index.html") : relativeRouteFile;
1284
+ const relativeRouteFile = `${(path.endsWith("/") ? `${path}index` : path).replace(/^\//g, "")}.html`;
1285
+ const filename = dirStyle === "nested" ? node_path.join(path.replace(/^\//g, ""), "index.html") : relativeRouteFile;
1254
1286
  await fs__default.ensureDir(node_path.join(out, node_path.dirname(filename)));
1255
1287
  await fs__default.writeFile(node_path.join(out, filename), formatted, "utf-8");
1256
1288
  config.logger.info(
1257
1289
  `${kolorist.dim(`${outDir}/`)}${kolorist.cyan(filename.padEnd(15, " "))} ${kolorist.dim(getSize(formatted))}`
1258
1290
  );
1259
1291
  } catch (err) {
1260
- throw new Error(`${kolorist.gray("[vite-react-ssg]")} ${kolorist.red(`Error on page: ${kolorist.cyan(route)}`)}
1292
+ throw new Error(`${kolorist.gray("[vite-react-ssg]")} ${kolorist.red(`Error on page: ${kolorist.cyan(path)}`)}
1261
1293
  ${err.stack}`);
1262
1294
  }
1263
1295
  });
@@ -1427,14 +1459,17 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
1427
1459
  const { routes, getStyleCollector } = appCtx;
1428
1460
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1429
1461
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1430
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes], createFetchRequest(req), styleCollector);
1462
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render([...routes], createFetchRequest(req), styleCollector);
1431
1463
  metaAttributes.push(styleTag);
1432
- const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
1464
+ const matchesEntries = routerContext.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`);
1465
+ const mods = await Promise.all(
1466
+ [entry, ...matchesEntries].map(async (entry2) => await viteServer.moduleGraph.getModuleByUrl(entry2))
1467
+ );
1433
1468
  const assetsUrls = /* @__PURE__ */ new Set();
1434
- const collectAssets = async (mod2) => {
1435
- if (!mod2?.ssrTransformResult)
1469
+ const collectAssets = async (mod) => {
1470
+ if (!mod || !mod?.ssrTransformResult)
1436
1471
  return;
1437
- const { deps = [], dynamicDeps = [] } = mod2?.ssrTransformResult;
1472
+ const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1438
1473
  const allDeps = [...deps, ...dynamicDeps];
1439
1474
  for (const dep of allDeps) {
1440
1475
  if (dep.endsWith(".css")) {
@@ -1445,7 +1480,7 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
1445
1480
  }
1446
1481
  }
1447
1482
  };
1448
- await collectAssets(mod);
1483
+ await Promise.all(mods.map(async (mod) => collectAssets(mod)));
1449
1484
  const preloadLink = [...assetsUrls].map((item) => createLink(item));
1450
1485
  metaAttributes.push(...preloadLink);
1451
1486
  const renderedHTML = await renderHTML({
@@ -864,6 +864,8 @@ function routesToPaths(routes) {
864
864
  function addEntry(path, entry) {
865
865
  if (!entry)
866
866
  return;
867
+ if (entry[0] === "/")
868
+ entry = entry.slice(1);
867
869
  if (pathToEntry[path])
868
870
  pathToEntry[path].add(entry);
869
871
  else
@@ -872,20 +874,22 @@ function routesToPaths(routes) {
872
874
  if (!routes)
873
875
  return { paths: ["/"], pathToEntry };
874
876
  const paths = /* @__PURE__ */ new Set();
877
+ const lazyPaths = /* @__PURE__ */ new Set();
875
878
  const getPaths = (routes2, prefix = "") => {
876
- const parentPath = prefix;
877
879
  prefix = prefix.replace(/\/$/g, "");
878
880
  for (const route of routes2) {
879
881
  let path = route.path;
880
- if (route.path != null) {
881
- path = prefix && !route.path.startsWith("/") ? `${prefix}${route.path ? `/${route.path}` : ""}` : route.path;
882
- paths.add(path);
883
- addEntry(path, route.entry);
884
- if (pathToEntry[parentPath]) {
885
- const pathCopy = path;
886
- pathToEntry[parentPath].forEach((entry) => addEntry(pathCopy, entry));
882
+ path = handlePath(path, prefix, route.entry);
883
+ if (route.getStaticPaths && path?.includes(":")) {
884
+ const staticPaths = route.getStaticPaths();
885
+ for (let staticPath of staticPaths) {
886
+ staticPath = handlePath(staticPath, prefix, route.entry);
887
+ if (Array.isArray(route.children))
888
+ getPaths(route.children, staticPath);
887
889
  }
888
890
  }
891
+ if (route.lazy)
892
+ lazyPaths.add(route.index ? prefix : path ?? "");
889
893
  if (route.index)
890
894
  addEntry(prefix, route.entry);
891
895
  if (Array.isArray(route.children))
@@ -893,7 +897,19 @@ function routesToPaths(routes) {
893
897
  }
894
898
  };
895
899
  getPaths(routes);
896
- return { paths: Array.from(paths), pathToEntry };
900
+ return { paths: Array.from(paths), pathToEntry, lazyPaths: Array.from(lazyPaths) };
901
+ function handlePath(path, prefix, entry) {
902
+ if (path != null) {
903
+ path = prefix && !path.startsWith("/") ? `${prefix}${path ? `/${path}` : ""}` : path;
904
+ 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
+ }
911
+ return path;
912
+ }
897
913
  }
898
914
  function createFetchRequest(req) {
899
915
  const origin = `${req.protocol}://${req.get("host")}`;
@@ -928,6 +944,13 @@ async function resolveAlias(config, entry) {
928
944
  const { version } = JSON.parse(
929
945
  readFileSync(new URL("../../package.json", import.meta.url)).toString()
930
946
  );
947
+ function createRequest(path) {
948
+ const url = new URL(path, "http://vite-react-ssg.com");
949
+ url.search = "";
950
+ url.hash = "";
951
+ url.pathname = path;
952
+ return new Request(url.href);
953
+ }
931
954
 
932
955
  async function getCritters(outDir, options = {}) {
933
956
  try {
@@ -1012,7 +1035,17 @@ async function render(routes, request, styleCollector) {
1012
1035
  ];
1013
1036
  const styleTag = styleCollector?.toString?.(appHTML) ?? "";
1014
1037
  const metaAttributes = metaStrings.filter(Boolean);
1015
- return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag };
1038
+ return { appHTML, htmlAttributes, bodyAttributes, metaAttributes, styleTag, routerContext: context };
1039
+ }
1040
+ async function preLoad(routes, paths) {
1041
+ if (!paths || paths.length === 0)
1042
+ return routes;
1043
+ const { dataRoutes, query } = createStaticHandler(routes);
1044
+ await Promise.all(paths.map(async (path) => {
1045
+ const request = createRequest(path);
1046
+ return query(request);
1047
+ }));
1048
+ return dataRoutes;
1016
1049
  }
1017
1050
 
1018
1051
  function renderPreloadLinks(document, modules, ssrManifest) {
@@ -1196,8 +1229,11 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1196
1229
  const { createRoot, includedRoutes: serverEntryIncludedRoutes } = format === "esm" ? await import(serverEntry) : _require(serverEntry);
1197
1230
  const includedRoutes = serverEntryIncludedRoutes || configIncludedRoutes;
1198
1231
  const { routes } = await createRoot(false);
1199
- const { paths, pathToEntry } = routesToPaths(routes);
1232
+ const { lazyPaths } = routesToPaths(routes);
1233
+ const dataRoutes = await preLoad([...routes], lazyPaths);
1234
+ const { paths, pathToEntry } = routesToPaths(dataRoutes);
1200
1235
  let routesPaths = includeAllRoutes ? paths : await includedRoutes(paths, routes || []);
1236
+ routesPaths = DefaultIncludedRoutes(routesPaths);
1201
1237
  routesPaths = Array.from(new Set(routesPaths));
1202
1238
  buildLog("Rendering Pages...", routesPaths.length);
1203
1239
  const critters = crittersOptions !== false ? await getCritters(outDir, crittersOptions) : void 0;
@@ -1208,20 +1244,16 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1208
1244
  let indexHTML = await fs.readFile(join(out, "index.html"), "utf-8");
1209
1245
  indexHTML = rewriteScripts(indexHTML, script);
1210
1246
  const queue = new PQueue({ concurrency });
1211
- for (const route of routesPaths) {
1247
+ for (const path of routesPaths) {
1212
1248
  queue.add(async () => {
1213
1249
  try {
1214
- const appCtx = await createRoot(false, route);
1215
- const { routes: routes2, initialState, triggerOnSSRAppRendered, transformState = serializeState, getStyleCollector } = appCtx;
1250
+ const appCtx = await createRoot(false, path);
1251
+ const { initialState, triggerOnSSRAppRendered, transformState = serializeState, getStyleCollector } = appCtx;
1216
1252
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1217
- const transformedIndexHTML = await onBeforePageRender?.(route, indexHTML, appCtx) || indexHTML;
1218
- const url = new URL(route, "http://vite-react-ssg.com");
1219
- url.search = "";
1220
- url.hash = "";
1221
- url.pathname = route;
1222
- const request = new Request(url.href);
1223
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes2], request, styleCollector);
1224
- await triggerOnSSRAppRendered?.(route, appHTML, appCtx);
1253
+ const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
1254
+ const request = createRequest(path);
1255
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(dataRoutes, request, styleCollector);
1256
+ await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
1225
1257
  const renderedHTML = await renderHTML({
1226
1258
  rootContainerId,
1227
1259
  appHTML,
@@ -1232,24 +1264,24 @@ async function build(ssgOptions = {}, viteConfig = {}) {
1232
1264
  initialState: null
1233
1265
  });
1234
1266
  const jsdom = new JSDOM(renderedHTML);
1235
- const modules = collectModulesForEntrys(manifest, pathToEntry?.[route]);
1267
+ const modules = collectModulesForEntrys(manifest, pathToEntry?.[path]);
1236
1268
  renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
1237
1269
  const html = jsdom.serialize();
1238
- let transformed = await onPageRendered?.(route, html, appCtx) || html;
1270
+ let transformed = await onPageRendered?.(path, html, appCtx) || html;
1239
1271
  if (critters)
1240
1272
  transformed = await critters.process(transformed);
1241
1273
  if (styleTag)
1242
1274
  transformed = transformed.replace("<head>", `<head>${styleTag}`);
1243
1275
  const formatted = await formatHtml(transformed, formatting);
1244
- const relativeRouteFile = `${(route.endsWith("/") ? `${route}index` : route).replace(/^\//g, "")}.html`;
1245
- const filename = dirStyle === "nested" ? join(route.replace(/^\//g, ""), "index.html") : relativeRouteFile;
1276
+ const relativeRouteFile = `${(path.endsWith("/") ? `${path}index` : path).replace(/^\//g, "")}.html`;
1277
+ const filename = dirStyle === "nested" ? join(path.replace(/^\//g, ""), "index.html") : relativeRouteFile;
1246
1278
  await fs.ensureDir(join(out, dirname(filename)));
1247
1279
  await fs.writeFile(join(out, filename), formatted, "utf-8");
1248
1280
  config.logger.info(
1249
1281
  `${dim(`${outDir}/`)}${cyan(filename.padEnd(15, " "))} ${dim(getSize(formatted))}`
1250
1282
  );
1251
1283
  } catch (err) {
1252
- throw new Error(`${gray("[vite-react-ssg]")} ${red(`Error on page: ${cyan(route)}`)}
1284
+ throw new Error(`${gray("[vite-react-ssg]")} ${red(`Error on page: ${cyan(path)}`)}
1253
1285
  ${err.stack}`);
1254
1286
  }
1255
1287
  });
@@ -1419,14 +1451,17 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
1419
1451
  const { routes, getStyleCollector } = appCtx;
1420
1452
  const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
1421
1453
  const styleCollector = getStyleCollector ? await getStyleCollector() : null;
1422
- const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render([...routes], createFetchRequest(req), styleCollector);
1454
+ const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render([...routes], createFetchRequest(req), styleCollector);
1423
1455
  metaAttributes.push(styleTag);
1424
- const mod = await viteServer.moduleGraph.getModuleByUrl(entry);
1456
+ const matchesEntries = routerContext.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`);
1457
+ const mods = await Promise.all(
1458
+ [entry, ...matchesEntries].map(async (entry2) => await viteServer.moduleGraph.getModuleByUrl(entry2))
1459
+ );
1425
1460
  const assetsUrls = /* @__PURE__ */ new Set();
1426
- const collectAssets = async (mod2) => {
1427
- if (!mod2?.ssrTransformResult)
1461
+ const collectAssets = async (mod) => {
1462
+ if (!mod || !mod?.ssrTransformResult)
1428
1463
  return;
1429
- const { deps = [], dynamicDeps = [] } = mod2?.ssrTransformResult;
1464
+ const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
1430
1465
  const allDeps = [...deps, ...dynamicDeps];
1431
1466
  for (const dep of allDeps) {
1432
1467
  if (dep.endsWith(".css")) {
@@ -1437,7 +1472,7 @@ async function dev(ssgOptions = {}, viteConfig = {}) {
1437
1472
  }
1438
1473
  }
1439
1474
  };
1440
- await collectAssets(mod);
1475
+ await Promise.all(mods.map(async (mod) => collectAssets(mod)));
1441
1476
  const preloadLink = [...assetsUrls].map((item) => createLink(item));
1442
1477
  metaAttributes.push(...preloadLink);
1443
1478
  const renderedHTML = await renderHTML({
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ const styledComponents = require('styled-components');
4
+
3
5
  async function ssrCollector() {
4
- const { ServerStyleSheet } = await import('styled-components');
5
- const sheet = new ServerStyleSheet();
6
+ const sheet = new styledComponents.ServerStyleSheet();
6
7
  return {
7
8
  collect(app) {
8
9
  return sheet.collectStyles(app);
@@ -15,6 +16,5 @@ async function ssrCollector() {
15
16
  }
16
17
  };
17
18
  }
18
- const styledComponents = undefined.SSR ? ssrCollector : null;
19
19
 
20
- exports.getStyledComponentsCollector = styledComponents;
20
+ module.exports = ssrCollector;
@@ -5,6 +5,5 @@ declare function ssrCollector(): Promise<{
5
5
  toString(): string;
6
6
  cleanup(): void;
7
7
  }>;
8
- declare const _default: typeof ssrCollector | null;
9
8
 
10
- export { _default as getStyledComponentsCollector };
9
+ export { ssrCollector as default };
@@ -1,5 +1,6 @@
1
+ import { ServerStyleSheet } from 'styled-components';
2
+
1
3
  async function ssrCollector() {
2
- const { ServerStyleSheet } = await import('styled-components');
3
4
  const sheet = new ServerStyleSheet();
4
5
  return {
5
6
  collect(app) {
@@ -13,6 +14,5 @@ async function ssrCollector() {
13
14
  }
14
15
  };
15
16
  }
16
- const styledComponents = import.meta.env.SSR ? ssrCollector : null;
17
17
 
18
- export { styledComponents as getStyledComponentsCollector };
18
+ export { ssrCollector as default };
@@ -92,7 +92,7 @@ interface ViteReactSSGOptions {
92
92
  onPageRendered?: (route: string, renderedHTML: string, appCtx: ViteReactSSGContext<true>) => Promise<string | null | undefined> | string | null | undefined;
93
93
  onFinished?: () => Promise<void> | void;
94
94
  /**
95
- * The application's root container `class`.
95
+ * The application's root container `id`.
96
96
  *
97
97
  * @default `root`
98
98
  */
@@ -142,9 +142,18 @@ interface CommonRouteOptions {
142
142
  /**
143
143
  * Used to obtain static resources through manifest
144
144
  *
145
- * @example `src/pages/home.tsx
145
+ * @example `src/pages/home.tsx`
146
146
  */
147
147
  entry?: string;
148
+ /**
149
+ * The getStaticPaths() function should return an array of path
150
+ * to determine which paths will be pre-rendered by vite-react-ssg.
151
+ *
152
+ * This function is only valid for dynamic route.
153
+ *
154
+ * @example () => ['path1', 'path2']
155
+ */
156
+ getStaticPaths?: () => string[];
148
157
  }
149
158
  type NonIndexRouteRecord = Omit<NonIndexRouteObject, 'children'> & {
150
159
  children?: RouteRecord[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-react-ssg",
3
3
  "type": "module",
4
- "version": "0.1.2",
4
+ "version": "0.3.0",
5
5
  "packageManager": "pnpm@8.6.6",
6
6
  "description": "",
7
7
  "author": "Riri <Daydreamerriri@outlook.com>",
@@ -31,10 +31,10 @@
31
31
  "require": "./dist/node.cjs",
32
32
  "import": "./dist/node.mjs"
33
33
  },
34
- "./style-collectors": {
35
- "types": "./dist/style-collectors.d.ts",
36
- "require": "./dist/style-collectors.cjs",
37
- "import": "./dist/style-collectors.mjs"
34
+ "./style-collectors/styled-components": {
35
+ "types": "./dist/style-collectors/styled-components.d.ts",
36
+ "require": "./dist/style-collectors/styled-components.cjs",
37
+ "import": "./dist/style-collectors/styled-components.mjs"
38
38
  }
39
39
  },
40
40
  "main": "./dist/index.mjs",
@@ -45,8 +45,8 @@
45
45
  "node": [
46
46
  "./dist/node.d.ts"
47
47
  ],
48
- "style-collectors": [
49
- "./dist/style-collectors.d.ts"
48
+ "style-collectors/styled-components": [
49
+ "./dist/style-collectors/styled-components.d.ts"
50
50
  ]
51
51
  }
52
52
  },
@@ -87,6 +87,7 @@
87
87
  }
88
88
  },
89
89
  "dependencies": {
90
+ "@remix-run/web-fetch": "^4.3.5",
90
91
  "express": "^4.18.2",
91
92
  "fs-extra": "^11.1.1",
92
93
  "html-minifier": "^4.0.0",
@@ -117,7 +118,6 @@
117
118
  "react-dom": "^18.2.0",
118
119
  "react-router-dom": "^6.14.1",
119
120
  "rimraf": "5.0.1",
120
- "simple-git-hooks": "^2.8.1",
121
121
  "styled-components": "6.0.5",
122
122
  "typescript": "5.1.6",
123
123
  "unbuild": "^1.2.1",