vite-react-ssg 0.6.3 → 0.7.0-beta.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/dist/index.cjs +30 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +30 -1
- package/dist/node/cli.cjs +3 -0
- package/dist/node/cli.mjs +3 -0
- package/dist/node.cjs +222 -85
- package/dist/node.mjs +223 -86
- package/dist/shared/vite-react-ssg.0408f7e1.cjs +72 -0
- package/dist/shared/vite-react-ssg.054e813a.mjs +65 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const reactHelmetAsync = require('react-helmet-async');
|
|
|
6
6
|
const reactRouterDom = require('react-router-dom');
|
|
7
7
|
const ClientOnly = require('./shared/vite-react-ssg.c1d49976.cjs');
|
|
8
8
|
const state = require('./shared/vite-react-ssg.bee8a5a9.cjs');
|
|
9
|
+
const remixRouter = require('./shared/vite-react-ssg.0408f7e1.cjs');
|
|
9
10
|
|
|
10
11
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
11
12
|
|
|
@@ -86,7 +87,7 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
86
87
|
const isClient = typeof window !== "undefined";
|
|
87
88
|
const BASE_URL = routerOptions.basename ?? "/";
|
|
88
89
|
async function createRoot(client = false, routePath) {
|
|
89
|
-
const browserRouter = client ? reactRouterDom.createBrowserRouter(routerOptions.routes, { basename: BASE_URL }) : void 0;
|
|
90
|
+
const browserRouter = client ? reactRouterDom.createBrowserRouter(remixRouter.convertRoutesToDataRoutes(routerOptions.routes, transformStaticLoaderRoute), { basename: BASE_URL }) : void 0;
|
|
90
91
|
const appRenderCallbacks = [];
|
|
91
92
|
const onSSRAppRendered = client ? () => {
|
|
92
93
|
} : (cb) => appRenderCallbacks.push(cb);
|
|
@@ -152,6 +153,34 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
152
153
|
})();
|
|
153
154
|
}
|
|
154
155
|
return createRoot;
|
|
156
|
+
function transformStaticLoaderRoute(route) {
|
|
157
|
+
const loader = async ({ request }) => {
|
|
158
|
+
if (undefined.DEV) {
|
|
159
|
+
const routeId = encodeURIComponent(route.id);
|
|
160
|
+
const dataQuery = `_data=${routeId}`;
|
|
161
|
+
const url = request.url.includes("?") ? `${request.url}&${dataQuery}` : `${request.url}?${dataQuery}`;
|
|
162
|
+
return fetch(url);
|
|
163
|
+
} else {
|
|
164
|
+
let staticLoadData;
|
|
165
|
+
if (window.__VITE_REACT_SSG_STATIC_LOADER_DATA__) {
|
|
166
|
+
staticLoadData = window.__VITE_REACT_SSG_STATIC_LOADER_DATA__;
|
|
167
|
+
} else {
|
|
168
|
+
const manifestUrl = remixRouter.joinUrlSegments(BASE_URL, `static-loader-data-manifest-${window.__VITE_REACT_SSG_HASH__}.json`);
|
|
169
|
+
staticLoadData = await (await fetch(remixRouter.withLeadingSlash(manifestUrl))).json();
|
|
170
|
+
window.__VITE_REACT_SSG_STATIC_LOADER_DATA__ = staticLoadData;
|
|
171
|
+
}
|
|
172
|
+
const { url } = request;
|
|
173
|
+
let { pathname } = new URL(url);
|
|
174
|
+
if (BASE_URL !== "/") {
|
|
175
|
+
pathname = remixRouter.stripBase(pathname, BASE_URL);
|
|
176
|
+
}
|
|
177
|
+
const routeData = staticLoadData?.[pathname]?.[route.id];
|
|
178
|
+
return routeData ?? null;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
route.loader = loader;
|
|
182
|
+
return route;
|
|
183
|
+
}
|
|
155
184
|
}
|
|
156
185
|
|
|
157
186
|
exports.ClientOnly = ClientOnly.ClientOnly;
|
package/dist/index.d.cts
CHANGED
|
@@ -10,5 +10,11 @@ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttribu
|
|
|
10
10
|
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
11
11
|
|
|
12
12
|
declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
|
|
13
|
+
declare global {
|
|
14
|
+
interface Window {
|
|
15
|
+
__VITE_REACT_SSG_STATIC_LOADER_DATA__: any;
|
|
16
|
+
__VITE_REACT_SSG_HASH__: string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
export { Link, NavLink, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
|
package/dist/index.d.mts
CHANGED
|
@@ -10,5 +10,11 @@ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttribu
|
|
|
10
10
|
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
11
11
|
|
|
12
12
|
declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
|
|
13
|
+
declare global {
|
|
14
|
+
interface Window {
|
|
15
|
+
__VITE_REACT_SSG_STATIC_LOADER_DATA__: any;
|
|
16
|
+
__VITE_REACT_SSG_HASH__: string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
export { Link, NavLink, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,11 @@ declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttribu
|
|
|
10
10
|
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
11
11
|
|
|
12
12
|
declare function ViteReactSSG(routerOptions: RouterOptions, fn?: (context: ViteReactSSGContext<true>) => Promise<void> | void, options?: ViteReactSSGClientOptions): (client?: boolean, routePath?: string) => Promise<ViteReactSSGContext<true>>;
|
|
13
|
+
declare global {
|
|
14
|
+
interface Window {
|
|
15
|
+
__VITE_REACT_SSG_STATIC_LOADER_DATA__: any;
|
|
16
|
+
__VITE_REACT_SSG_HASH__: string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
export { Link, NavLink, RouterOptions, ViteReactSSG, ViteReactSSGClientOptions, ViteReactSSGContext };
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { useLinkClickHandler, Link as Link$1, NavLink as NavLink$1, matchRoutes,
|
|
|
5
5
|
import { d as documentReady } from './shared/vite-react-ssg.a292c181.mjs';
|
|
6
6
|
export { C as ClientOnly, H as Head } from './shared/vite-react-ssg.a292c181.mjs';
|
|
7
7
|
import { d as deserializeState } from './shared/vite-react-ssg.579feabb.mjs';
|
|
8
|
+
import { c as convertRoutesToDataRoutes, j as joinUrlSegments, w as withLeadingSlash, s as stripBase } from './shared/vite-react-ssg.054e813a.mjs';
|
|
8
9
|
|
|
9
10
|
const Link = forwardRef((props, ref) => {
|
|
10
11
|
const {
|
|
@@ -81,7 +82,7 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
81
82
|
const isClient = typeof window !== "undefined";
|
|
82
83
|
const BASE_URL = routerOptions.basename ?? "/";
|
|
83
84
|
async function createRoot$1(client = false, routePath) {
|
|
84
|
-
const browserRouter = client ? createBrowserRouter(routerOptions.routes, { basename: BASE_URL }) : void 0;
|
|
85
|
+
const browserRouter = client ? createBrowserRouter(convertRoutesToDataRoutes(routerOptions.routes, transformStaticLoaderRoute), { basename: BASE_URL }) : void 0;
|
|
85
86
|
const appRenderCallbacks = [];
|
|
86
87
|
const onSSRAppRendered = client ? () => {
|
|
87
88
|
} : (cb) => appRenderCallbacks.push(cb);
|
|
@@ -147,6 +148,34 @@ function ViteReactSSG(routerOptions, fn, options = {}) {
|
|
|
147
148
|
})();
|
|
148
149
|
}
|
|
149
150
|
return createRoot$1;
|
|
151
|
+
function transformStaticLoaderRoute(route) {
|
|
152
|
+
const loader = async ({ request }) => {
|
|
153
|
+
if (import.meta.env.DEV) {
|
|
154
|
+
const routeId = encodeURIComponent(route.id);
|
|
155
|
+
const dataQuery = `_data=${routeId}`;
|
|
156
|
+
const url = request.url.includes("?") ? `${request.url}&${dataQuery}` : `${request.url}?${dataQuery}`;
|
|
157
|
+
return fetch(url);
|
|
158
|
+
} else {
|
|
159
|
+
let staticLoadData;
|
|
160
|
+
if (window.__VITE_REACT_SSG_STATIC_LOADER_DATA__) {
|
|
161
|
+
staticLoadData = window.__VITE_REACT_SSG_STATIC_LOADER_DATA__;
|
|
162
|
+
} else {
|
|
163
|
+
const manifestUrl = joinUrlSegments(BASE_URL, `static-loader-data-manifest-${window.__VITE_REACT_SSG_HASH__}.json`);
|
|
164
|
+
staticLoadData = await (await fetch(withLeadingSlash(manifestUrl))).json();
|
|
165
|
+
window.__VITE_REACT_SSG_STATIC_LOADER_DATA__ = staticLoadData;
|
|
166
|
+
}
|
|
167
|
+
const { url } = request;
|
|
168
|
+
let { pathname } = new URL(url);
|
|
169
|
+
if (BASE_URL !== "/") {
|
|
170
|
+
pathname = stripBase(pathname, BASE_URL);
|
|
171
|
+
}
|
|
172
|
+
const routeData = staticLoadData?.[pathname]?.[route.id];
|
|
173
|
+
return routeData ?? null;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
route.loader = loader;
|
|
177
|
+
return route;
|
|
178
|
+
}
|
|
150
179
|
}
|
|
151
180
|
|
|
152
181
|
export { Link, NavLink, ViteReactSSG };
|
package/dist/node/cli.cjs
CHANGED
|
@@ -11,11 +11,14 @@ require('fs-extra');
|
|
|
11
11
|
require('vite');
|
|
12
12
|
require('jsdom');
|
|
13
13
|
require('../shared/vite-react-ssg.bee8a5a9.cjs');
|
|
14
|
+
require('../shared/vite-react-ssg.0408f7e1.cjs');
|
|
14
15
|
require('node:fs');
|
|
15
16
|
require('react');
|
|
16
17
|
require('react-helmet-async');
|
|
17
18
|
require('node:stream');
|
|
18
19
|
require('react-dom/server');
|
|
20
|
+
require('react-router-dom');
|
|
21
|
+
require('node:events');
|
|
19
22
|
|
|
20
23
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
21
24
|
|
package/dist/node/cli.mjs
CHANGED
|
@@ -9,11 +9,14 @@ import 'fs-extra';
|
|
|
9
9
|
import 'vite';
|
|
10
10
|
import 'jsdom';
|
|
11
11
|
import '../shared/vite-react-ssg.579feabb.mjs';
|
|
12
|
+
import '../shared/vite-react-ssg.054e813a.mjs';
|
|
12
13
|
import 'node:fs';
|
|
13
14
|
import 'react';
|
|
14
15
|
import 'react-helmet-async';
|
|
15
16
|
import 'node:stream';
|
|
16
17
|
import 'react-dom/server';
|
|
18
|
+
import 'react-router-dom';
|
|
19
|
+
import 'node:events';
|
|
17
20
|
|
|
18
21
|
function installGlobals() {
|
|
19
22
|
global.Headers = Headers;
|
package/dist/node.cjs
CHANGED
|
@@ -7,11 +7,14 @@ const fs = require('fs-extra');
|
|
|
7
7
|
const vite = require('vite');
|
|
8
8
|
const JSDOM = require('jsdom');
|
|
9
9
|
const state = require('./shared/vite-react-ssg.bee8a5a9.cjs');
|
|
10
|
+
const remixRouter = require('./shared/vite-react-ssg.0408f7e1.cjs');
|
|
10
11
|
const node_fs = require('node:fs');
|
|
11
12
|
const React = require('react');
|
|
12
13
|
const reactHelmetAsync = require('react-helmet-async');
|
|
13
14
|
const node_stream = require('node:stream');
|
|
14
15
|
const server = require('react-dom/server');
|
|
16
|
+
const reactRouterDom = require('react-router-dom');
|
|
17
|
+
const node_events = require('node:events');
|
|
15
18
|
|
|
16
19
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
17
20
|
|
|
@@ -919,23 +922,6 @@ function createRequest(path) {
|
|
|
919
922
|
url.pathname = path;
|
|
920
923
|
return new Request(url.href);
|
|
921
924
|
}
|
|
922
|
-
function joinUrlSegments(a, b) {
|
|
923
|
-
if (!a || !b)
|
|
924
|
-
return a || b || "";
|
|
925
|
-
if (a[a.length - 1] === "/")
|
|
926
|
-
a = a.substring(0, a.length - 1);
|
|
927
|
-
if (b[0] !== "/")
|
|
928
|
-
b = `/${b}`;
|
|
929
|
-
return a + b;
|
|
930
|
-
}
|
|
931
|
-
function removeLeadingSlash(str) {
|
|
932
|
-
return str[0] === "/" ? str.slice(1) : str;
|
|
933
|
-
}
|
|
934
|
-
function withTrailingSlash(path) {
|
|
935
|
-
if (path[path.length - 1] !== "/")
|
|
936
|
-
return `${path}/`;
|
|
937
|
-
return path;
|
|
938
|
-
}
|
|
939
925
|
const dynamicRE = /[:*?]/;
|
|
940
926
|
function isDynamicSegmentsRoute(route) {
|
|
941
927
|
if (!route)
|
|
@@ -1050,6 +1036,7 @@ function extractHelmet(context, styleCollector) {
|
|
|
1050
1036
|
return { htmlAttributes, bodyAttributes, metaAttributes, styleTag };
|
|
1051
1037
|
}
|
|
1052
1038
|
|
|
1039
|
+
const SCRIPT_COMMENT_PLACEHOLDER = "/* SCRIPT_COMMENT_PLACEHOLDER */";
|
|
1053
1040
|
async function renderHTML({
|
|
1054
1041
|
rootContainerId,
|
|
1055
1042
|
indexHTML,
|
|
@@ -1061,6 +1048,8 @@ async function renderHTML({
|
|
|
1061
1048
|
}) {
|
|
1062
1049
|
const stateScript = initialState ? `
|
|
1063
1050
|
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1051
|
+
const scriptPlaceHolder = `
|
|
1052
|
+
<script>${SCRIPT_COMMENT_PLACEHOLDER}<\/script>`;
|
|
1064
1053
|
const headStartTag = "<head>";
|
|
1065
1054
|
const metaTags = metaAttributes.join("");
|
|
1066
1055
|
indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
|
|
@@ -1072,7 +1061,7 @@ async function renderHTML({
|
|
|
1072
1061
|
if (indexHTML.includes(container)) {
|
|
1073
1062
|
return indexHTML.replace(
|
|
1074
1063
|
container,
|
|
1075
|
-
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1064
|
+
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}${scriptPlaceHolder}`
|
|
1076
1065
|
);
|
|
1077
1066
|
}
|
|
1078
1067
|
const html5Parser = await import('html5parser');
|
|
@@ -1166,7 +1155,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1166
1155
|
const config = await vite.resolveConfig(viteConfig, "build", mode, mode);
|
|
1167
1156
|
const cwd = process.cwd();
|
|
1168
1157
|
const root = config.root || cwd;
|
|
1169
|
-
const
|
|
1158
|
+
const hash = Math.random().toString(36).substring(2, 12);
|
|
1159
|
+
const ssgOut = node_path.join(root, ".vite-react-ssg-temp", hash);
|
|
1170
1160
|
const outDir = config.build.outDir || "dist";
|
|
1171
1161
|
const out = node_path.isAbsolute(outDir) ? outDir : node_path.join(root, outDir);
|
|
1172
1162
|
const configBase = config.base;
|
|
@@ -1205,7 +1195,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1205
1195
|
}
|
|
1206
1196
|
}
|
|
1207
1197
|
},
|
|
1208
|
-
mode: config.mode
|
|
1198
|
+
mode: config.mode,
|
|
1199
|
+
ssr: { noExternal: ["vite-react-ssg"] }
|
|
1209
1200
|
}));
|
|
1210
1201
|
if (mock) {
|
|
1211
1202
|
const { jsdomGlobal } = await import('./chunks/jsdomGlobal.cjs');
|
|
@@ -1230,7 +1221,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1230
1221
|
}
|
|
1231
1222
|
}
|
|
1232
1223
|
},
|
|
1233
|
-
mode: config.mode
|
|
1224
|
+
mode: config.mode,
|
|
1225
|
+
ssr: { noExternal: ["vite-react-ssg"] }
|
|
1234
1226
|
}));
|
|
1235
1227
|
const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
|
|
1236
1228
|
const ext = format === "esm" ? ".mjs" : ".cjs";
|
|
@@ -1254,6 +1246,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1254
1246
|
indexHTML = rewriteScripts(indexHTML, script);
|
|
1255
1247
|
const queue = new PQueue({ concurrency });
|
|
1256
1248
|
const crittersQueue = new PQueue({ concurrency: 1 });
|
|
1249
|
+
const staticLoaderDataManifest = {};
|
|
1257
1250
|
for (const path of routesPaths) {
|
|
1258
1251
|
queue.add(async () => {
|
|
1259
1252
|
try {
|
|
@@ -1261,9 +1254,10 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1261
1254
|
const { initialState, base, routes: routes2, triggerOnSSRAppRendered, transformState = state.serializeState, getStyleCollector, app } = appCtx;
|
|
1262
1255
|
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1263
1256
|
const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
|
|
1264
|
-
const fetchUrl = `${withTrailingSlash(base)}${removeLeadingSlash(path)}`;
|
|
1257
|
+
const fetchUrl = `${remixRouter.withTrailingSlash(base)}${remixRouter.removeLeadingSlash(path)}`;
|
|
1265
1258
|
const request = createRequest(fetchUrl);
|
|
1266
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes2], request, styleCollector, base);
|
|
1259
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
|
|
1260
|
+
staticLoaderDataManifest[path] = routerContext?.loaderData;
|
|
1267
1261
|
await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
|
|
1268
1262
|
const renderedHTML = await renderHTML({
|
|
1269
1263
|
rootContainerId,
|
|
@@ -1279,6 +1273,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1279
1273
|
renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
|
|
1280
1274
|
const html = jsdom.serialize();
|
|
1281
1275
|
let transformed = await onPageRendered?.(path, html, appCtx) || html;
|
|
1276
|
+
transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
|
|
1282
1277
|
if (critters) {
|
|
1283
1278
|
transformed = await crittersQueue.add(() => critters.process(transformed));
|
|
1284
1279
|
transformed = transformed.replace(/<link\srel="stylesheet"/g, '<link rel="stylesheet" crossorigin');
|
|
@@ -1300,6 +1295,7 @@ ${err.stack}`);
|
|
|
1300
1295
|
});
|
|
1301
1296
|
}
|
|
1302
1297
|
await queue.start().onIdle();
|
|
1298
|
+
await fs__default.writeFile(node_path.join(out, `static-loader-data-manifest-${hash}.json`), JSON.stringify(staticLoaderDataManifest, null, 2));
|
|
1303
1299
|
await fs__default.remove(node_path.join(root, ".vite-react-ssg-temp"));
|
|
1304
1300
|
const pwaPlugin = config.plugins.find((i) => i.name === "vite-plugin-pwa")?.api;
|
|
1305
1301
|
if (pwaPlugin && !pwaPlugin.disabled && pwaPlugin.generateSW) {
|
|
@@ -1397,6 +1393,201 @@ function fromNodeRequest(nodeReq) {
|
|
|
1397
1393
|
};
|
|
1398
1394
|
return new Request(url.href, init);
|
|
1399
1395
|
}
|
|
1396
|
+
async function toNodeRequest(res, nodeRes) {
|
|
1397
|
+
nodeRes.statusCode = res.status;
|
|
1398
|
+
nodeRes.statusMessage = res.statusText;
|
|
1399
|
+
for (const [name, value] of res.headers) {
|
|
1400
|
+
nodeRes.setHeader(name, value);
|
|
1401
|
+
}
|
|
1402
|
+
if (res.body) {
|
|
1403
|
+
const responseBody = res.body;
|
|
1404
|
+
const readable = node_stream.Readable.from(responseBody);
|
|
1405
|
+
readable.pipe(nodeRes);
|
|
1406
|
+
await node_events.once(readable, "end");
|
|
1407
|
+
} else {
|
|
1408
|
+
nodeRes.end();
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
function ssrServerPlugin({
|
|
1413
|
+
template,
|
|
1414
|
+
ssrEntry,
|
|
1415
|
+
onBeforePageRender,
|
|
1416
|
+
entry,
|
|
1417
|
+
rootContainerId,
|
|
1418
|
+
onPageRendered
|
|
1419
|
+
}) {
|
|
1420
|
+
return {
|
|
1421
|
+
name: "vite-react-ssg:dev-server-remix",
|
|
1422
|
+
configureServer(server) {
|
|
1423
|
+
const renderMiddleware = async (req, res, _next) => {
|
|
1424
|
+
try {
|
|
1425
|
+
const url = req.originalUrl;
|
|
1426
|
+
const createRoot = await server.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1427
|
+
const appCtx = await createRoot(false, url);
|
|
1428
|
+
const { routes, getStyleCollector, base, app } = appCtx;
|
|
1429
|
+
const searchParams = new URLSearchParams(url.split("?")[1]);
|
|
1430
|
+
if (!app && searchParams.has("_data")) {
|
|
1431
|
+
const request = fromNodeRequest(req);
|
|
1432
|
+
const url2 = new URL(request.url);
|
|
1433
|
+
const routeId = decodeURIComponent(searchParams.get("_data"));
|
|
1434
|
+
const matches = reactRouterDom.matchRoutes(
|
|
1435
|
+
remixRouter.convertRoutesToDataRoutes([...routes], (route) => route),
|
|
1436
|
+
{
|
|
1437
|
+
pathname: url2.pathname,
|
|
1438
|
+
search: url2.search,
|
|
1439
|
+
hash: url2.hash,
|
|
1440
|
+
state: null,
|
|
1441
|
+
key: "default"
|
|
1442
|
+
},
|
|
1443
|
+
base
|
|
1444
|
+
);
|
|
1445
|
+
if (!matches) {
|
|
1446
|
+
res.statusCode = 404;
|
|
1447
|
+
res.end(`Route not found: ${routeId}`);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const match = matches.find((m) => m.route.id === routeId);
|
|
1451
|
+
if (!match) {
|
|
1452
|
+
res.statusCode = 404;
|
|
1453
|
+
res.end(`Route not found: ${routeId}`);
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
const loader = match.route.loader ?? await match.route.lazy?.().then((m) => m.loader);
|
|
1457
|
+
if (!loader) {
|
|
1458
|
+
res.statusCode = 200;
|
|
1459
|
+
res.end(`There is no loader for the route: ${routeId}`);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
const response = await callRouteLoader({
|
|
1463
|
+
loader,
|
|
1464
|
+
params: match.params,
|
|
1465
|
+
request,
|
|
1466
|
+
routeId
|
|
1467
|
+
});
|
|
1468
|
+
await toNodeRequest(response, res);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
const indexHTML = await server.transformIndexHtml(url, template);
|
|
1472
|
+
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1473
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1474
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
|
|
1475
|
+
metaAttributes.push(styleTag);
|
|
1476
|
+
const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
|
|
1477
|
+
const mods = await Promise.all(
|
|
1478
|
+
[ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
|
|
1479
|
+
);
|
|
1480
|
+
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1481
|
+
const collectAssets = async (mod) => {
|
|
1482
|
+
if (!mod || !mod?.ssrTransformResult)
|
|
1483
|
+
return;
|
|
1484
|
+
const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
|
|
1485
|
+
const allDeps = [...deps, ...dynamicDeps];
|
|
1486
|
+
for (const dep of allDeps) {
|
|
1487
|
+
if (dep.endsWith(".css")) {
|
|
1488
|
+
assetsUrls.add(dep);
|
|
1489
|
+
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1490
|
+
const depModule = await server.moduleGraph.getModuleByUrl(dep);
|
|
1491
|
+
depModule && await collectAssets(depModule);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
await Promise.all(mods.map(async (mod) => collectAssets(mod)));
|
|
1496
|
+
const preloadLink = [...assetsUrls].map((item) => createLink$1(remixRouter.joinUrlSegments(server.config.base, item)));
|
|
1497
|
+
metaAttributes.push(...preloadLink);
|
|
1498
|
+
const renderedHTML = await renderHTML({
|
|
1499
|
+
rootContainerId,
|
|
1500
|
+
appHTML,
|
|
1501
|
+
indexHTML: transformedIndexHTML,
|
|
1502
|
+
metaAttributes,
|
|
1503
|
+
bodyAttributes,
|
|
1504
|
+
htmlAttributes,
|
|
1505
|
+
initialState: null
|
|
1506
|
+
});
|
|
1507
|
+
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1508
|
+
res.statusCode = 200;
|
|
1509
|
+
res.setHeader("Content-Type", "text/html");
|
|
1510
|
+
const isDev = "pluginContainer" in server;
|
|
1511
|
+
const headers = isDev ? server.config.server.headers : server.config.preview.headers;
|
|
1512
|
+
vite.send(req, res, transformed, "html", { headers });
|
|
1513
|
+
} catch (e) {
|
|
1514
|
+
server.ssrFixStacktrace(e);
|
|
1515
|
+
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1516
|
+
res.statusCode = 500;
|
|
1517
|
+
res.end(e.stack);
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
return () => {
|
|
1521
|
+
server.middlewares.use(renderMiddleware);
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
async function callRouteLoader({
|
|
1527
|
+
// loadContext,
|
|
1528
|
+
loader,
|
|
1529
|
+
params,
|
|
1530
|
+
request,
|
|
1531
|
+
routeId
|
|
1532
|
+
// singleFetch,
|
|
1533
|
+
// response,
|
|
1534
|
+
}) {
|
|
1535
|
+
const result = await loader({
|
|
1536
|
+
request: stripDataParam(stripIndexParam(request)),
|
|
1537
|
+
// context: loadContext,
|
|
1538
|
+
params
|
|
1539
|
+
// Only provided when single fetch is enabled, and made available via
|
|
1540
|
+
// `defineLoader` types, not `LoaderFunctionArgs`
|
|
1541
|
+
// ...(singleFetch ? { response } : null),
|
|
1542
|
+
});
|
|
1543
|
+
if (result === void 0) {
|
|
1544
|
+
throw new Error(
|
|
1545
|
+
`You defined a loader for route "${routeId}" but didn't return anything from your \`loader\` function. Please return a value or \`null\`.`
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
return isResponse(result) ? result : reactRouterDom.json(result);
|
|
1549
|
+
}
|
|
1550
|
+
function isResponse(value) {
|
|
1551
|
+
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
|
|
1552
|
+
}
|
|
1553
|
+
function stripIndexParam(request) {
|
|
1554
|
+
const url = new URL(request.url);
|
|
1555
|
+
const indexValues = url.searchParams.getAll("index");
|
|
1556
|
+
url.searchParams.delete("index");
|
|
1557
|
+
const indexValuesToKeep = [];
|
|
1558
|
+
for (const indexValue of indexValues) {
|
|
1559
|
+
if (indexValue) {
|
|
1560
|
+
indexValuesToKeep.push(indexValue);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
for (const toKeep of indexValuesToKeep) {
|
|
1564
|
+
url.searchParams.append("index", toKeep);
|
|
1565
|
+
}
|
|
1566
|
+
const init = {
|
|
1567
|
+
method: request.method,
|
|
1568
|
+
body: request.body,
|
|
1569
|
+
headers: request.headers,
|
|
1570
|
+
signal: request.signal
|
|
1571
|
+
};
|
|
1572
|
+
if (init.body) {
|
|
1573
|
+
init.duplex = "half";
|
|
1574
|
+
}
|
|
1575
|
+
return new Request(url.href, init);
|
|
1576
|
+
}
|
|
1577
|
+
function stripDataParam(request) {
|
|
1578
|
+
const url = new URL(request.url);
|
|
1579
|
+
url.searchParams.delete("_data");
|
|
1580
|
+
const init = {
|
|
1581
|
+
method: request.method,
|
|
1582
|
+
body: request.body,
|
|
1583
|
+
headers: request.headers,
|
|
1584
|
+
signal: request.signal
|
|
1585
|
+
};
|
|
1586
|
+
if (init.body) {
|
|
1587
|
+
init.duplex = "half";
|
|
1588
|
+
}
|
|
1589
|
+
return new Request(url.href, init);
|
|
1590
|
+
}
|
|
1400
1591
|
|
|
1401
1592
|
async function dev(ssgOptions = {}, viteConfig = {}, customOptions) {
|
|
1402
1593
|
const mode = process.env.MODE || process.env.NODE_ENV || ssgOptions.mode || "development";
|
|
@@ -1431,68 +1622,14 @@ ${err.stack}`
|
|
|
1431
1622
|
...viteConfig,
|
|
1432
1623
|
plugins: [
|
|
1433
1624
|
...viteConfig.plugins ?? [],
|
|
1434
|
-
{
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1443
|
-
const appCtx = await createRoot(false, url);
|
|
1444
|
-
const { routes, getStyleCollector, base, app } = appCtx;
|
|
1445
|
-
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1446
|
-
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1447
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
|
|
1448
|
-
metaAttributes.push(styleTag);
|
|
1449
|
-
const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
|
|
1450
|
-
const mods = await Promise.all(
|
|
1451
|
-
[ssrEntry, entry, ...matchesEntries].map(async (entry2) => await viteServer.moduleGraph.getModuleByUrl(entry2))
|
|
1452
|
-
);
|
|
1453
|
-
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1454
|
-
const collectAssets = async (mod) => {
|
|
1455
|
-
if (!mod || !mod?.ssrTransformResult)
|
|
1456
|
-
return;
|
|
1457
|
-
const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
|
|
1458
|
-
const allDeps = [...deps, ...dynamicDeps];
|
|
1459
|
-
for (const dep of allDeps) {
|
|
1460
|
-
if (dep.endsWith(".css")) {
|
|
1461
|
-
assetsUrls.add(dep);
|
|
1462
|
-
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1463
|
-
const depModule = await viteServer.moduleGraph.getModuleByUrl(dep);
|
|
1464
|
-
depModule && await collectAssets(depModule);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
};
|
|
1468
|
-
await Promise.all(mods.map(async (mod) => collectAssets(mod)));
|
|
1469
|
-
const preloadLink = [...assetsUrls].map((item) => createLink$1(joinUrlSegments(config.base, item)));
|
|
1470
|
-
metaAttributes.push(...preloadLink);
|
|
1471
|
-
const renderedHTML = await renderHTML({
|
|
1472
|
-
rootContainerId,
|
|
1473
|
-
appHTML,
|
|
1474
|
-
indexHTML: transformedIndexHTML,
|
|
1475
|
-
metaAttributes,
|
|
1476
|
-
bodyAttributes,
|
|
1477
|
-
htmlAttributes,
|
|
1478
|
-
initialState: null
|
|
1479
|
-
});
|
|
1480
|
-
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1481
|
-
res.statusCode = 200;
|
|
1482
|
-
res.setHeader("Content-Type", "text/html");
|
|
1483
|
-
const isDev = "pluginContainer" in server;
|
|
1484
|
-
const headers = isDev ? server.config.server.headers : server.config.preview.headers;
|
|
1485
|
-
vite.send(req, res, transformed, "html", { headers });
|
|
1486
|
-
} catch (e) {
|
|
1487
|
-
viteServer.ssrFixStacktrace(e);
|
|
1488
|
-
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1489
|
-
res.statusCode = 500;
|
|
1490
|
-
res.end(e.stack);
|
|
1491
|
-
}
|
|
1492
|
-
});
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1625
|
+
ssrServerPlugin({
|
|
1626
|
+
template,
|
|
1627
|
+
ssrEntry,
|
|
1628
|
+
onBeforePageRender,
|
|
1629
|
+
onPageRendered,
|
|
1630
|
+
entry,
|
|
1631
|
+
rootContainerId
|
|
1632
|
+
})
|
|
1496
1633
|
]
|
|
1497
1634
|
});
|
|
1498
1635
|
await viteServer.listen();
|
package/dist/node.mjs
CHANGED
|
@@ -2,14 +2,17 @@ import { join, isAbsolute, parse, dirname } from 'node:path';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { gray, yellow, blue, dim, cyan, red, green, reset, bold, bgLightCyan } from 'kolorist';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
-
import { resolveConfig, build as build$1, mergeConfig, version as version$1,
|
|
5
|
+
import { resolveConfig, build as build$1, mergeConfig, version as version$1, send, createServer } from 'vite';
|
|
6
6
|
import { JSDOM } from 'jsdom';
|
|
7
7
|
import { s as serializeState } from './shared/vite-react-ssg.579feabb.mjs';
|
|
8
|
+
import { a as withTrailingSlash, r as removeLeadingSlash, c as convertRoutesToDataRoutes, j as joinUrlSegments } from './shared/vite-react-ssg.054e813a.mjs';
|
|
8
9
|
import { readFileSync } from 'node:fs';
|
|
9
10
|
import React from 'react';
|
|
10
11
|
import { HelmetProvider } from 'react-helmet-async';
|
|
11
|
-
import { Writable } from 'node:stream';
|
|
12
|
+
import { Writable, Readable } from 'node:stream';
|
|
12
13
|
import { renderToPipeableStream } from 'react-dom/server';
|
|
14
|
+
import { matchRoutes, json } from 'react-router-dom';
|
|
15
|
+
import { once } from 'node:events';
|
|
13
16
|
|
|
14
17
|
function getDefaultExportFromCjs (x) {
|
|
15
18
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
@@ -912,23 +915,6 @@ function createRequest(path) {
|
|
|
912
915
|
url.pathname = path;
|
|
913
916
|
return new Request(url.href);
|
|
914
917
|
}
|
|
915
|
-
function joinUrlSegments(a, b) {
|
|
916
|
-
if (!a || !b)
|
|
917
|
-
return a || b || "";
|
|
918
|
-
if (a[a.length - 1] === "/")
|
|
919
|
-
a = a.substring(0, a.length - 1);
|
|
920
|
-
if (b[0] !== "/")
|
|
921
|
-
b = `/${b}`;
|
|
922
|
-
return a + b;
|
|
923
|
-
}
|
|
924
|
-
function removeLeadingSlash(str) {
|
|
925
|
-
return str[0] === "/" ? str.slice(1) : str;
|
|
926
|
-
}
|
|
927
|
-
function withTrailingSlash(path) {
|
|
928
|
-
if (path[path.length - 1] !== "/")
|
|
929
|
-
return `${path}/`;
|
|
930
|
-
return path;
|
|
931
|
-
}
|
|
932
918
|
const dynamicRE = /[:*?]/;
|
|
933
919
|
function isDynamicSegmentsRoute(route) {
|
|
934
920
|
if (!route)
|
|
@@ -1043,6 +1029,7 @@ function extractHelmet(context, styleCollector) {
|
|
|
1043
1029
|
return { htmlAttributes, bodyAttributes, metaAttributes, styleTag };
|
|
1044
1030
|
}
|
|
1045
1031
|
|
|
1032
|
+
const SCRIPT_COMMENT_PLACEHOLDER = "/* SCRIPT_COMMENT_PLACEHOLDER */";
|
|
1046
1033
|
async function renderHTML({
|
|
1047
1034
|
rootContainerId,
|
|
1048
1035
|
indexHTML,
|
|
@@ -1054,6 +1041,8 @@ async function renderHTML({
|
|
|
1054
1041
|
}) {
|
|
1055
1042
|
const stateScript = initialState ? `
|
|
1056
1043
|
<script>window.__INITIAL_STATE__=${initialState}<\/script>` : "";
|
|
1044
|
+
const scriptPlaceHolder = `
|
|
1045
|
+
<script>${SCRIPT_COMMENT_PLACEHOLDER}<\/script>`;
|
|
1057
1046
|
const headStartTag = "<head>";
|
|
1058
1047
|
const metaTags = metaAttributes.join("");
|
|
1059
1048
|
indexHTML = indexHTML.replace(headStartTag, headStartTag + metaTags);
|
|
@@ -1065,7 +1054,7 @@ async function renderHTML({
|
|
|
1065
1054
|
if (indexHTML.includes(container)) {
|
|
1066
1055
|
return indexHTML.replace(
|
|
1067
1056
|
container,
|
|
1068
|
-
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}`
|
|
1057
|
+
`<div id="${rootContainerId}" data-server-rendered="true">${appHTML}</div>${stateScript}${scriptPlaceHolder}`
|
|
1069
1058
|
);
|
|
1070
1059
|
}
|
|
1071
1060
|
const html5Parser = await import('html5parser');
|
|
@@ -1159,7 +1148,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1159
1148
|
const config = await resolveConfig(viteConfig, "build", mode, mode);
|
|
1160
1149
|
const cwd = process.cwd();
|
|
1161
1150
|
const root = config.root || cwd;
|
|
1162
|
-
const
|
|
1151
|
+
const hash = Math.random().toString(36).substring(2, 12);
|
|
1152
|
+
const ssgOut = join(root, ".vite-react-ssg-temp", hash);
|
|
1163
1153
|
const outDir = config.build.outDir || "dist";
|
|
1164
1154
|
const out = isAbsolute(outDir) ? outDir : join(root, outDir);
|
|
1165
1155
|
const configBase = config.base;
|
|
@@ -1198,7 +1188,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1198
1188
|
}
|
|
1199
1189
|
}
|
|
1200
1190
|
},
|
|
1201
|
-
mode: config.mode
|
|
1191
|
+
mode: config.mode,
|
|
1192
|
+
ssr: { noExternal: ["vite-react-ssg"] }
|
|
1202
1193
|
}));
|
|
1203
1194
|
if (mock) {
|
|
1204
1195
|
const { jsdomGlobal } = await import('./chunks/jsdomGlobal.mjs');
|
|
@@ -1223,7 +1214,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1223
1214
|
}
|
|
1224
1215
|
}
|
|
1225
1216
|
},
|
|
1226
|
-
mode: config.mode
|
|
1217
|
+
mode: config.mode,
|
|
1218
|
+
ssr: { noExternal: ["vite-react-ssg"] }
|
|
1227
1219
|
}));
|
|
1228
1220
|
const prefix = format === "esm" && process.platform === "win32" ? "file://" : "";
|
|
1229
1221
|
const ext = format === "esm" ? ".mjs" : ".cjs";
|
|
@@ -1247,6 +1239,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1247
1239
|
indexHTML = rewriteScripts(indexHTML, script);
|
|
1248
1240
|
const queue = new PQueue({ concurrency });
|
|
1249
1241
|
const crittersQueue = new PQueue({ concurrency: 1 });
|
|
1242
|
+
const staticLoaderDataManifest = {};
|
|
1250
1243
|
for (const path of routesPaths) {
|
|
1251
1244
|
queue.add(async () => {
|
|
1252
1245
|
try {
|
|
@@ -1256,7 +1249,8 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1256
1249
|
const transformedIndexHTML = await onBeforePageRender?.(path, indexHTML, appCtx) || indexHTML;
|
|
1257
1250
|
const fetchUrl = `${withTrailingSlash(base)}${removeLeadingSlash(path)}`;
|
|
1258
1251
|
const request = createRequest(fetchUrl);
|
|
1259
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag } = await render(app ?? [...routes2], request, styleCollector, base);
|
|
1252
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes2], request, styleCollector, base);
|
|
1253
|
+
staticLoaderDataManifest[path] = routerContext?.loaderData;
|
|
1260
1254
|
await triggerOnSSRAppRendered?.(path, appHTML, appCtx);
|
|
1261
1255
|
const renderedHTML = await renderHTML({
|
|
1262
1256
|
rootContainerId,
|
|
@@ -1272,6 +1266,7 @@ async function build(ssgOptions = {}, viteConfig = {}) {
|
|
|
1272
1266
|
renderPreloadLinks(jsdom.window.document, modules, ssrManifest);
|
|
1273
1267
|
const html = jsdom.serialize();
|
|
1274
1268
|
let transformed = await onPageRendered?.(path, html, appCtx) || html;
|
|
1269
|
+
transformed = transformed.replace(SCRIPT_COMMENT_PLACEHOLDER, `window.__VITE_REACT_SSG_HASH__ = '${hash}'`);
|
|
1275
1270
|
if (critters) {
|
|
1276
1271
|
transformed = await crittersQueue.add(() => critters.process(transformed));
|
|
1277
1272
|
transformed = transformed.replace(/<link\srel="stylesheet"/g, '<link rel="stylesheet" crossorigin');
|
|
@@ -1293,6 +1288,7 @@ ${err.stack}`);
|
|
|
1293
1288
|
});
|
|
1294
1289
|
}
|
|
1295
1290
|
await queue.start().onIdle();
|
|
1291
|
+
await fs.writeFile(join(out, `static-loader-data-manifest-${hash}.json`), JSON.stringify(staticLoaderDataManifest, null, 2));
|
|
1296
1292
|
await fs.remove(join(root, ".vite-react-ssg-temp"));
|
|
1297
1293
|
const pwaPlugin = config.plugins.find((i) => i.name === "vite-plugin-pwa")?.api;
|
|
1298
1294
|
if (pwaPlugin && !pwaPlugin.disabled && pwaPlugin.generateSW) {
|
|
@@ -1390,6 +1386,201 @@ function fromNodeRequest(nodeReq) {
|
|
|
1390
1386
|
};
|
|
1391
1387
|
return new Request(url.href, init);
|
|
1392
1388
|
}
|
|
1389
|
+
async function toNodeRequest(res, nodeRes) {
|
|
1390
|
+
nodeRes.statusCode = res.status;
|
|
1391
|
+
nodeRes.statusMessage = res.statusText;
|
|
1392
|
+
for (const [name, value] of res.headers) {
|
|
1393
|
+
nodeRes.setHeader(name, value);
|
|
1394
|
+
}
|
|
1395
|
+
if (res.body) {
|
|
1396
|
+
const responseBody = res.body;
|
|
1397
|
+
const readable = Readable.from(responseBody);
|
|
1398
|
+
readable.pipe(nodeRes);
|
|
1399
|
+
await once(readable, "end");
|
|
1400
|
+
} else {
|
|
1401
|
+
nodeRes.end();
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
function ssrServerPlugin({
|
|
1406
|
+
template,
|
|
1407
|
+
ssrEntry,
|
|
1408
|
+
onBeforePageRender,
|
|
1409
|
+
entry,
|
|
1410
|
+
rootContainerId,
|
|
1411
|
+
onPageRendered
|
|
1412
|
+
}) {
|
|
1413
|
+
return {
|
|
1414
|
+
name: "vite-react-ssg:dev-server-remix",
|
|
1415
|
+
configureServer(server) {
|
|
1416
|
+
const renderMiddleware = async (req, res, _next) => {
|
|
1417
|
+
try {
|
|
1418
|
+
const url = req.originalUrl;
|
|
1419
|
+
const createRoot = await server.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1420
|
+
const appCtx = await createRoot(false, url);
|
|
1421
|
+
const { routes, getStyleCollector, base, app } = appCtx;
|
|
1422
|
+
const searchParams = new URLSearchParams(url.split("?")[1]);
|
|
1423
|
+
if (!app && searchParams.has("_data")) {
|
|
1424
|
+
const request = fromNodeRequest(req);
|
|
1425
|
+
const url2 = new URL(request.url);
|
|
1426
|
+
const routeId = decodeURIComponent(searchParams.get("_data"));
|
|
1427
|
+
const matches = matchRoutes(
|
|
1428
|
+
convertRoutesToDataRoutes([...routes], (route) => route),
|
|
1429
|
+
{
|
|
1430
|
+
pathname: url2.pathname,
|
|
1431
|
+
search: url2.search,
|
|
1432
|
+
hash: url2.hash,
|
|
1433
|
+
state: null,
|
|
1434
|
+
key: "default"
|
|
1435
|
+
},
|
|
1436
|
+
base
|
|
1437
|
+
);
|
|
1438
|
+
if (!matches) {
|
|
1439
|
+
res.statusCode = 404;
|
|
1440
|
+
res.end(`Route not found: ${routeId}`);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const match = matches.find((m) => m.route.id === routeId);
|
|
1444
|
+
if (!match) {
|
|
1445
|
+
res.statusCode = 404;
|
|
1446
|
+
res.end(`Route not found: ${routeId}`);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
const loader = match.route.loader ?? await match.route.lazy?.().then((m) => m.loader);
|
|
1450
|
+
if (!loader) {
|
|
1451
|
+
res.statusCode = 200;
|
|
1452
|
+
res.end(`There is no loader for the route: ${routeId}`);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const response = await callRouteLoader({
|
|
1456
|
+
loader,
|
|
1457
|
+
params: match.params,
|
|
1458
|
+
request,
|
|
1459
|
+
routeId
|
|
1460
|
+
});
|
|
1461
|
+
await toNodeRequest(response, res);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const indexHTML = await server.transformIndexHtml(url, template);
|
|
1465
|
+
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1466
|
+
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1467
|
+
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
|
|
1468
|
+
metaAttributes.push(styleTag);
|
|
1469
|
+
const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
|
|
1470
|
+
const mods = await Promise.all(
|
|
1471
|
+
[ssrEntry, entry, ...matchesEntries].map(async (entry2) => await server.moduleGraph.getModuleByUrl(entry2))
|
|
1472
|
+
);
|
|
1473
|
+
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1474
|
+
const collectAssets = async (mod) => {
|
|
1475
|
+
if (!mod || !mod?.ssrTransformResult)
|
|
1476
|
+
return;
|
|
1477
|
+
const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
|
|
1478
|
+
const allDeps = [...deps, ...dynamicDeps];
|
|
1479
|
+
for (const dep of allDeps) {
|
|
1480
|
+
if (dep.endsWith(".css")) {
|
|
1481
|
+
assetsUrls.add(dep);
|
|
1482
|
+
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1483
|
+
const depModule = await server.moduleGraph.getModuleByUrl(dep);
|
|
1484
|
+
depModule && await collectAssets(depModule);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
await Promise.all(mods.map(async (mod) => collectAssets(mod)));
|
|
1489
|
+
const preloadLink = [...assetsUrls].map((item) => createLink$1(joinUrlSegments(server.config.base, item)));
|
|
1490
|
+
metaAttributes.push(...preloadLink);
|
|
1491
|
+
const renderedHTML = await renderHTML({
|
|
1492
|
+
rootContainerId,
|
|
1493
|
+
appHTML,
|
|
1494
|
+
indexHTML: transformedIndexHTML,
|
|
1495
|
+
metaAttributes,
|
|
1496
|
+
bodyAttributes,
|
|
1497
|
+
htmlAttributes,
|
|
1498
|
+
initialState: null
|
|
1499
|
+
});
|
|
1500
|
+
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1501
|
+
res.statusCode = 200;
|
|
1502
|
+
res.setHeader("Content-Type", "text/html");
|
|
1503
|
+
const isDev = "pluginContainer" in server;
|
|
1504
|
+
const headers = isDev ? server.config.server.headers : server.config.preview.headers;
|
|
1505
|
+
send(req, res, transformed, "html", { headers });
|
|
1506
|
+
} catch (e) {
|
|
1507
|
+
server.ssrFixStacktrace(e);
|
|
1508
|
+
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1509
|
+
res.statusCode = 500;
|
|
1510
|
+
res.end(e.stack);
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
return () => {
|
|
1514
|
+
server.middlewares.use(renderMiddleware);
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
async function callRouteLoader({
|
|
1520
|
+
// loadContext,
|
|
1521
|
+
loader,
|
|
1522
|
+
params,
|
|
1523
|
+
request,
|
|
1524
|
+
routeId
|
|
1525
|
+
// singleFetch,
|
|
1526
|
+
// response,
|
|
1527
|
+
}) {
|
|
1528
|
+
const result = await loader({
|
|
1529
|
+
request: stripDataParam(stripIndexParam(request)),
|
|
1530
|
+
// context: loadContext,
|
|
1531
|
+
params
|
|
1532
|
+
// Only provided when single fetch is enabled, and made available via
|
|
1533
|
+
// `defineLoader` types, not `LoaderFunctionArgs`
|
|
1534
|
+
// ...(singleFetch ? { response } : null),
|
|
1535
|
+
});
|
|
1536
|
+
if (result === void 0) {
|
|
1537
|
+
throw new Error(
|
|
1538
|
+
`You defined a loader for route "${routeId}" but didn't return anything from your \`loader\` function. Please return a value or \`null\`.`
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
return isResponse(result) ? result : json(result);
|
|
1542
|
+
}
|
|
1543
|
+
function isResponse(value) {
|
|
1544
|
+
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
|
|
1545
|
+
}
|
|
1546
|
+
function stripIndexParam(request) {
|
|
1547
|
+
const url = new URL(request.url);
|
|
1548
|
+
const indexValues = url.searchParams.getAll("index");
|
|
1549
|
+
url.searchParams.delete("index");
|
|
1550
|
+
const indexValuesToKeep = [];
|
|
1551
|
+
for (const indexValue of indexValues) {
|
|
1552
|
+
if (indexValue) {
|
|
1553
|
+
indexValuesToKeep.push(indexValue);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
for (const toKeep of indexValuesToKeep) {
|
|
1557
|
+
url.searchParams.append("index", toKeep);
|
|
1558
|
+
}
|
|
1559
|
+
const init = {
|
|
1560
|
+
method: request.method,
|
|
1561
|
+
body: request.body,
|
|
1562
|
+
headers: request.headers,
|
|
1563
|
+
signal: request.signal
|
|
1564
|
+
};
|
|
1565
|
+
if (init.body) {
|
|
1566
|
+
init.duplex = "half";
|
|
1567
|
+
}
|
|
1568
|
+
return new Request(url.href, init);
|
|
1569
|
+
}
|
|
1570
|
+
function stripDataParam(request) {
|
|
1571
|
+
const url = new URL(request.url);
|
|
1572
|
+
url.searchParams.delete("_data");
|
|
1573
|
+
const init = {
|
|
1574
|
+
method: request.method,
|
|
1575
|
+
body: request.body,
|
|
1576
|
+
headers: request.headers,
|
|
1577
|
+
signal: request.signal
|
|
1578
|
+
};
|
|
1579
|
+
if (init.body) {
|
|
1580
|
+
init.duplex = "half";
|
|
1581
|
+
}
|
|
1582
|
+
return new Request(url.href, init);
|
|
1583
|
+
}
|
|
1393
1584
|
|
|
1394
1585
|
async function dev(ssgOptions = {}, viteConfig = {}, customOptions) {
|
|
1395
1586
|
const mode = process.env.MODE || process.env.NODE_ENV || ssgOptions.mode || "development";
|
|
@@ -1424,68 +1615,14 @@ ${err.stack}`
|
|
|
1424
1615
|
...viteConfig,
|
|
1425
1616
|
plugins: [
|
|
1426
1617
|
...viteConfig.plugins ?? [],
|
|
1427
|
-
{
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
const createRoot = await viteServer.ssrLoadModule(ssrEntry).then((m) => m.createRoot);
|
|
1436
|
-
const appCtx = await createRoot(false, url);
|
|
1437
|
-
const { routes, getStyleCollector, base, app } = appCtx;
|
|
1438
|
-
const transformedIndexHTML = await onBeforePageRender?.(url, indexHTML, appCtx) || indexHTML;
|
|
1439
|
-
const styleCollector = getStyleCollector ? await getStyleCollector() : null;
|
|
1440
|
-
const { appHTML, bodyAttributes, htmlAttributes, metaAttributes, styleTag, routerContext } = await render(app ?? [...routes], fromNodeRequest(req), styleCollector, base);
|
|
1441
|
-
metaAttributes.push(styleTag);
|
|
1442
|
-
const matchesEntries = routerContext?.matches.map((match) => match.route.entry).filter((entry2) => !!entry2).map((entry2) => entry2[0] === "/" ? entry2 : `/${entry2}`) ?? [];
|
|
1443
|
-
const mods = await Promise.all(
|
|
1444
|
-
[ssrEntry, entry, ...matchesEntries].map(async (entry2) => await viteServer.moduleGraph.getModuleByUrl(entry2))
|
|
1445
|
-
);
|
|
1446
|
-
const assetsUrls = /* @__PURE__ */ new Set();
|
|
1447
|
-
const collectAssets = async (mod) => {
|
|
1448
|
-
if (!mod || !mod?.ssrTransformResult)
|
|
1449
|
-
return;
|
|
1450
|
-
const { deps = [], dynamicDeps = [] } = mod?.ssrTransformResult;
|
|
1451
|
-
const allDeps = [...deps, ...dynamicDeps];
|
|
1452
|
-
for (const dep of allDeps) {
|
|
1453
|
-
if (dep.endsWith(".css")) {
|
|
1454
|
-
assetsUrls.add(dep);
|
|
1455
|
-
} else if (dep.endsWith(".ts") || dep.endsWith(".tsx")) {
|
|
1456
|
-
const depModule = await viteServer.moduleGraph.getModuleByUrl(dep);
|
|
1457
|
-
depModule && await collectAssets(depModule);
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
};
|
|
1461
|
-
await Promise.all(mods.map(async (mod) => collectAssets(mod)));
|
|
1462
|
-
const preloadLink = [...assetsUrls].map((item) => createLink$1(joinUrlSegments(config.base, item)));
|
|
1463
|
-
metaAttributes.push(...preloadLink);
|
|
1464
|
-
const renderedHTML = await renderHTML({
|
|
1465
|
-
rootContainerId,
|
|
1466
|
-
appHTML,
|
|
1467
|
-
indexHTML: transformedIndexHTML,
|
|
1468
|
-
metaAttributes,
|
|
1469
|
-
bodyAttributes,
|
|
1470
|
-
htmlAttributes,
|
|
1471
|
-
initialState: null
|
|
1472
|
-
});
|
|
1473
|
-
const transformed = await onPageRendered?.(url, renderedHTML, appCtx) || renderedHTML;
|
|
1474
|
-
res.statusCode = 200;
|
|
1475
|
-
res.setHeader("Content-Type", "text/html");
|
|
1476
|
-
const isDev = "pluginContainer" in server;
|
|
1477
|
-
const headers = isDev ? server.config.server.headers : server.config.preview.headers;
|
|
1478
|
-
send(req, res, transformed, "html", { headers });
|
|
1479
|
-
} catch (e) {
|
|
1480
|
-
viteServer.ssrFixStacktrace(e);
|
|
1481
|
-
console.error(`[vite-react-ssg] error: ${e.stack}`);
|
|
1482
|
-
res.statusCode = 500;
|
|
1483
|
-
res.end(e.stack);
|
|
1484
|
-
}
|
|
1485
|
-
});
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1618
|
+
ssrServerPlugin({
|
|
1619
|
+
template,
|
|
1620
|
+
ssrEntry,
|
|
1621
|
+
onBeforePageRender,
|
|
1622
|
+
onPageRendered,
|
|
1623
|
+
entry,
|
|
1624
|
+
rootContainerId
|
|
1625
|
+
})
|
|
1489
1626
|
]
|
|
1490
1627
|
});
|
|
1491
1628
|
await viteServer.listen();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function joinUrlSegments(a, b) {
|
|
4
|
+
if (!a || !b)
|
|
5
|
+
return a || b || "";
|
|
6
|
+
if (a[a.length - 1] === "/")
|
|
7
|
+
a = a.substring(0, a.length - 1);
|
|
8
|
+
if (b[0] !== "/")
|
|
9
|
+
b = `/${b}`;
|
|
10
|
+
return a + b;
|
|
11
|
+
}
|
|
12
|
+
function removeLeadingSlash(str) {
|
|
13
|
+
return str[0] === "/" ? str.slice(1) : str;
|
|
14
|
+
}
|
|
15
|
+
function stripBase(path, base) {
|
|
16
|
+
if (path === base)
|
|
17
|
+
return "/";
|
|
18
|
+
const devBase = withTrailingSlash(base);
|
|
19
|
+
return path.startsWith(devBase) ? path.slice(devBase.length - 1) : path;
|
|
20
|
+
}
|
|
21
|
+
function withTrailingSlash(path) {
|
|
22
|
+
if (path[path.length - 1] !== "/")
|
|
23
|
+
return `${path}/`;
|
|
24
|
+
return path;
|
|
25
|
+
}
|
|
26
|
+
function withLeadingSlash(path) {
|
|
27
|
+
if (path[0] !== "/")
|
|
28
|
+
return `/${path}`;
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath = []) {
|
|
33
|
+
return routes.map((route, index) => {
|
|
34
|
+
const treePath = [...parentPath, String(index)];
|
|
35
|
+
const id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
36
|
+
route.id = id;
|
|
37
|
+
if (isIndexRoute(route)) {
|
|
38
|
+
const indexRoute = {
|
|
39
|
+
...route,
|
|
40
|
+
...mapRouteProperties(route),
|
|
41
|
+
id
|
|
42
|
+
};
|
|
43
|
+
return indexRoute;
|
|
44
|
+
} else {
|
|
45
|
+
const pathOrLayoutRoute = {
|
|
46
|
+
...route,
|
|
47
|
+
...mapRouteProperties(route),
|
|
48
|
+
id,
|
|
49
|
+
children: void 0
|
|
50
|
+
};
|
|
51
|
+
if (route.children) {
|
|
52
|
+
pathOrLayoutRoute.children = convertRoutesToDataRoutes(
|
|
53
|
+
route.children,
|
|
54
|
+
mapRouteProperties,
|
|
55
|
+
treePath
|
|
56
|
+
// manifest,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return pathOrLayoutRoute;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function isIndexRoute(route) {
|
|
64
|
+
return route.index === true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
exports.convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
68
|
+
exports.joinUrlSegments = joinUrlSegments;
|
|
69
|
+
exports.removeLeadingSlash = removeLeadingSlash;
|
|
70
|
+
exports.stripBase = stripBase;
|
|
71
|
+
exports.withLeadingSlash = withLeadingSlash;
|
|
72
|
+
exports.withTrailingSlash = withTrailingSlash;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function joinUrlSegments(a, b) {
|
|
2
|
+
if (!a || !b)
|
|
3
|
+
return a || b || "";
|
|
4
|
+
if (a[a.length - 1] === "/")
|
|
5
|
+
a = a.substring(0, a.length - 1);
|
|
6
|
+
if (b[0] !== "/")
|
|
7
|
+
b = `/${b}`;
|
|
8
|
+
return a + b;
|
|
9
|
+
}
|
|
10
|
+
function removeLeadingSlash(str) {
|
|
11
|
+
return str[0] === "/" ? str.slice(1) : str;
|
|
12
|
+
}
|
|
13
|
+
function stripBase(path, base) {
|
|
14
|
+
if (path === base)
|
|
15
|
+
return "/";
|
|
16
|
+
const devBase = withTrailingSlash(base);
|
|
17
|
+
return path.startsWith(devBase) ? path.slice(devBase.length - 1) : path;
|
|
18
|
+
}
|
|
19
|
+
function withTrailingSlash(path) {
|
|
20
|
+
if (path[path.length - 1] !== "/")
|
|
21
|
+
return `${path}/`;
|
|
22
|
+
return path;
|
|
23
|
+
}
|
|
24
|
+
function withLeadingSlash(path) {
|
|
25
|
+
if (path[0] !== "/")
|
|
26
|
+
return `/${path}`;
|
|
27
|
+
return path;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath = []) {
|
|
31
|
+
return routes.map((route, index) => {
|
|
32
|
+
const treePath = [...parentPath, String(index)];
|
|
33
|
+
const id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
34
|
+
route.id = id;
|
|
35
|
+
if (isIndexRoute(route)) {
|
|
36
|
+
const indexRoute = {
|
|
37
|
+
...route,
|
|
38
|
+
...mapRouteProperties(route),
|
|
39
|
+
id
|
|
40
|
+
};
|
|
41
|
+
return indexRoute;
|
|
42
|
+
} else {
|
|
43
|
+
const pathOrLayoutRoute = {
|
|
44
|
+
...route,
|
|
45
|
+
...mapRouteProperties(route),
|
|
46
|
+
id,
|
|
47
|
+
children: void 0
|
|
48
|
+
};
|
|
49
|
+
if (route.children) {
|
|
50
|
+
pathOrLayoutRoute.children = convertRoutesToDataRoutes(
|
|
51
|
+
route.children,
|
|
52
|
+
mapRouteProperties,
|
|
53
|
+
treePath
|
|
54
|
+
// manifest,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return pathOrLayoutRoute;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function isIndexRoute(route) {
|
|
62
|
+
return route.index === true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { withTrailingSlash as a, convertRoutesToDataRoutes as c, joinUrlSegments as j, removeLeadingSlash as r, stripBase as s, withLeadingSlash as w };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-react-ssg",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0-beta.1",
|
|
5
5
|
"packageManager": "pnpm@9.4.0",
|
|
6
6
|
"description": "",
|
|
7
7
|
"author": "Riri <Daydreamerriri@outlook.com>",
|
|
@@ -136,4 +136,4 @@
|
|
|
136
136
|
"vite-plugin-pwa": "^0.17.4",
|
|
137
137
|
"vitest": "1.6.0"
|
|
138
138
|
}
|
|
139
|
-
}
|
|
139
|
+
}
|