@react-router/dev 0.0.0-experimental-c0856287f
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/CHANGELOG.md +1773 -0
- package/LICENSE.md +23 -0
- package/README.md +13 -0
- package/dist/cli/commands.d.ts +12 -0
- package/dist/cli/commands.js +174 -0
- package/dist/cli/detectPackageManager.d.ts +10 -0
- package/dist/cli/detectPackageManager.js +39 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +180 -0
- package/dist/cli/useJavascript.d.ts +4 -0
- package/dist/cli/useJavascript.js +66 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -0
- package/dist/colors.d.ts +17 -0
- package/dist/colors.js +49 -0
- package/dist/config/defaults/entry.client.tsx +12 -0
- package/dist/config/defaults/entry.dev.d.ts +2 -0
- package/dist/config/defaults/entry.dev.ts +13 -0
- package/dist/config/defaults/entry.server.cloudflare.tsx +55 -0
- package/dist/config/defaults/entry.server.deno.tsx +55 -0
- package/dist/config/defaults/entry.server.node.tsx +155 -0
- package/dist/config/defaults/entry.server.spa.tsx +20 -0
- package/dist/config/flat-routes.d.ts +14 -0
- package/dist/config/flat-routes.js +418 -0
- package/dist/config/format.d.ts +5 -0
- package/dist/config/format.js +68 -0
- package/dist/config/routes.d.ts +98 -0
- package/dist/config/routes.js +93 -0
- package/dist/config/serverModes.d.ts +9 -0
- package/dist/config/serverModes.js +28 -0
- package/dist/config.d.ts +75 -0
- package/dist/config.js +152 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +23 -0
- package/dist/invariant.d.ts +2 -0
- package/dist/invariant.js +22 -0
- package/dist/manifest.d.ts +28 -0
- package/dist/vite/babel.d.ts +20 -0
- package/dist/vite/babel.js +49 -0
- package/dist/vite/build.d.ts +15 -0
- package/dist/vite/build.js +271 -0
- package/dist/vite/cloudflare-proxy-plugin.d.ts +15 -0
- package/dist/vite/cloudflare-proxy-plugin.js +82 -0
- package/dist/vite/dev.d.ts +15 -0
- package/dist/vite/dev.js +81 -0
- package/dist/vite/import-vite-esm-sync.d.ts +4 -0
- package/dist/vite/import-vite-esm-sync.js +28 -0
- package/dist/vite/index.d.ts +4 -0
- package/dist/vite/index.js +30 -0
- package/dist/vite/node-adapter.d.ts +6 -0
- package/dist/vite/node-adapter.js +78 -0
- package/dist/vite/plugin.d.ts +165 -0
- package/dist/vite/plugin.js +1178 -0
- package/dist/vite/profiler.d.ts +5 -0
- package/dist/vite/profiler.js +55 -0
- package/dist/vite/remove-exports-test.d.ts +1 -0
- package/dist/vite/remove-exports.d.ts +2 -0
- package/dist/vite/remove-exports.js +278 -0
- package/dist/vite/resolve-file-url.d.ts +3 -0
- package/dist/vite/resolve-file-url.js +53 -0
- package/dist/vite/static/refresh-utils.cjs +185 -0
- package/dist/vite/styles.d.ts +13 -0
- package/dist/vite/styles.js +176 -0
- package/dist/vite/vmod.d.ts +3 -0
- package/dist/vite/vmod.js +21 -0
- package/package.json +107 -0
|
@@ -0,0 +1,1178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-c0856287f
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Remix Software Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE.md file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
14
|
+
|
|
15
|
+
var node_crypto = require('node:crypto');
|
|
16
|
+
var path = require('node:path');
|
|
17
|
+
var url = require('node:url');
|
|
18
|
+
var fse = require('fs-extra');
|
|
19
|
+
var babel = require('@babel/core');
|
|
20
|
+
var serverRuntime = require('@react-router/server-runtime');
|
|
21
|
+
var esModuleLexer = require('es-module-lexer');
|
|
22
|
+
var jsesc = require('jsesc');
|
|
23
|
+
var pick = require('lodash/pick');
|
|
24
|
+
var omit = require('lodash/omit');
|
|
25
|
+
var colors = require('picocolors');
|
|
26
|
+
var config = require('../config.js');
|
|
27
|
+
var invariant = require('../invariant.js');
|
|
28
|
+
var nodeAdapter = require('./node-adapter.js');
|
|
29
|
+
var styles = require('./styles.js');
|
|
30
|
+
var vmod = require('./vmod.js');
|
|
31
|
+
var resolveFileUrl = require('./resolve-file-url.js');
|
|
32
|
+
var removeExports = require('./remove-exports.js');
|
|
33
|
+
var importViteEsmSync = require('./import-vite-esm-sync.js');
|
|
34
|
+
|
|
35
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
36
|
+
|
|
37
|
+
function _interopNamespace(e) {
|
|
38
|
+
if (e && e.__esModule) return e;
|
|
39
|
+
var n = Object.create(null);
|
|
40
|
+
if (e) {
|
|
41
|
+
Object.keys(e).forEach(function (k) {
|
|
42
|
+
if (k !== 'default') {
|
|
43
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
44
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: function () { return e[k]; }
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
n["default"] = e;
|
|
52
|
+
return Object.freeze(n);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
56
|
+
var url__namespace = /*#__PURE__*/_interopNamespace(url);
|
|
57
|
+
var fse__namespace = /*#__PURE__*/_interopNamespace(fse);
|
|
58
|
+
var babel__default = /*#__PURE__*/_interopDefaultLegacy(babel);
|
|
59
|
+
var jsesc__default = /*#__PURE__*/_interopDefaultLegacy(jsesc);
|
|
60
|
+
var pick__default = /*#__PURE__*/_interopDefaultLegacy(pick);
|
|
61
|
+
var omit__default = /*#__PURE__*/_interopDefaultLegacy(omit);
|
|
62
|
+
var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
|
|
63
|
+
|
|
64
|
+
async function resolveViteConfig({
|
|
65
|
+
configFile,
|
|
66
|
+
mode,
|
|
67
|
+
root
|
|
68
|
+
}) {
|
|
69
|
+
let vite = await import('vite');
|
|
70
|
+
let viteConfig = await vite.resolveConfig({
|
|
71
|
+
mode,
|
|
72
|
+
configFile,
|
|
73
|
+
root
|
|
74
|
+
}, "build",
|
|
75
|
+
// command
|
|
76
|
+
"production",
|
|
77
|
+
// default mode
|
|
78
|
+
"production" // default NODE_ENV
|
|
79
|
+
);
|
|
80
|
+
if (typeof viteConfig.build.manifest === "string") {
|
|
81
|
+
throw new Error("Custom Vite manifest paths are not supported");
|
|
82
|
+
}
|
|
83
|
+
return viteConfig;
|
|
84
|
+
}
|
|
85
|
+
async function extractPluginContext(viteConfig) {
|
|
86
|
+
return viteConfig["__reactRouterPluginContext"];
|
|
87
|
+
}
|
|
88
|
+
async function loadPluginContext({
|
|
89
|
+
configFile,
|
|
90
|
+
root
|
|
91
|
+
}) {
|
|
92
|
+
if (!root) {
|
|
93
|
+
root = process.env.REACT_ROUTER_ROOT || process.cwd();
|
|
94
|
+
}
|
|
95
|
+
configFile = configFile ?? config.findConfig(root, "vite.config", [".ts", ".cts", ".mts", ".js", ".cjs", ".mjs"]);
|
|
96
|
+
if (!configFile) {
|
|
97
|
+
console.error(colors__default["default"].red("Vite config file not found"));
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
let viteConfig = await resolveViteConfig({
|
|
101
|
+
configFile,
|
|
102
|
+
root
|
|
103
|
+
});
|
|
104
|
+
let ctx = await extractPluginContext(viteConfig);
|
|
105
|
+
if (!ctx) {
|
|
106
|
+
console.error(colors__default["default"].red("React Router Vite plugin not found in Vite config"));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
return ctx;
|
|
110
|
+
}
|
|
111
|
+
const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
|
|
112
|
+
const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
|
|
113
|
+
// The "=1" suffix ensures client route requests can be processed before hitting
|
|
114
|
+
// the Vite plugin since "?client-route" can be serialized as "?client-route="
|
|
115
|
+
const CLIENT_ROUTE_QUERY_STRING = "?client-route=1";
|
|
116
|
+
// Only expose a subset of route properties to the "serverBundles" function
|
|
117
|
+
const branchRouteProperties = ["id", "path", "file", "index"];
|
|
118
|
+
const configRouteToBranchRoute = configRoute => pick__default["default"](configRoute, branchRouteProperties);
|
|
119
|
+
const excludedConfigPresetKeys = ["presets"];
|
|
120
|
+
let serverBuildId = vmod.id("server-build");
|
|
121
|
+
let serverManifestId = vmod.id("server-manifest");
|
|
122
|
+
let browserManifestId = vmod.id("browser-manifest");
|
|
123
|
+
let hmrRuntimeId = vmod.id("hmr-runtime");
|
|
124
|
+
let injectHmrRuntimeId = vmod.id("inject-hmr-runtime");
|
|
125
|
+
const resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
|
|
126
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
127
|
+
let file = route.file;
|
|
128
|
+
let fullPath = path__namespace.resolve(reactRouterConfig.appDirectory, file);
|
|
129
|
+
return vite.normalizePath(fullPath);
|
|
130
|
+
};
|
|
131
|
+
let vmods = [serverBuildId, serverManifestId, browserManifestId];
|
|
132
|
+
const invalidateVirtualModules = viteDevServer => {
|
|
133
|
+
vmods.forEach(vmod$1 => {
|
|
134
|
+
let mod = viteDevServer.moduleGraph.getModuleById(vmod.resolve(vmod$1));
|
|
135
|
+
if (mod) {
|
|
136
|
+
viteDevServer.moduleGraph.invalidateModule(mod);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
const getHash = (source, maxLength) => {
|
|
141
|
+
let hash = node_crypto.createHash("sha256").update(source).digest("hex");
|
|
142
|
+
return typeof maxLength === "number" ? hash.slice(0, maxLength) : hash;
|
|
143
|
+
};
|
|
144
|
+
const isClientRoute = id => {
|
|
145
|
+
return id.endsWith(CLIENT_ROUTE_QUERY_STRING);
|
|
146
|
+
};
|
|
147
|
+
const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
|
|
148
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
149
|
+
let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
|
|
150
|
+
let entryChunk = viteManifest[rootRelativeFilePath + CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
|
|
151
|
+
if (!entryChunk) {
|
|
152
|
+
let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
|
|
153
|
+
throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
|
|
154
|
+
}
|
|
155
|
+
return entryChunk;
|
|
156
|
+
};
|
|
157
|
+
const getReactRouterManifestBuildAssets = (ctx, viteManifest, entryFilePath, prependedAssetFilePaths = []) => {
|
|
158
|
+
let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
|
|
159
|
+
// This is here to support prepending client entry assets to the root route
|
|
160
|
+
let prependedAssetChunks = prependedAssetFilePaths.map(filePath => resolveChunk(ctx, viteManifest, filePath));
|
|
161
|
+
let chunks = resolveDependantChunks(viteManifest, [...prependedAssetChunks, entryChunk]);
|
|
162
|
+
return {
|
|
163
|
+
module: `${ctx.reactRouterConfig.publicPath}${entryChunk.file}`,
|
|
164
|
+
imports: dedupe(chunks.flatMap(e => e.imports ?? [])).map(imported => {
|
|
165
|
+
return `${ctx.reactRouterConfig.publicPath}${viteManifest[imported].file}`;
|
|
166
|
+
}) ?? [],
|
|
167
|
+
css: dedupe(chunks.flatMap(e => e.css ?? [])).map(href => {
|
|
168
|
+
return `${ctx.reactRouterConfig.publicPath}${href}`;
|
|
169
|
+
}) ?? []
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
function resolveDependantChunks(viteManifest, entryChunks) {
|
|
173
|
+
let chunks = new Set();
|
|
174
|
+
function walk(chunk) {
|
|
175
|
+
if (chunks.has(chunk)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (chunk.imports) {
|
|
179
|
+
for (let importKey of chunk.imports) {
|
|
180
|
+
walk(viteManifest[importKey]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
chunks.add(chunk);
|
|
184
|
+
}
|
|
185
|
+
for (let entryChunk of entryChunks) {
|
|
186
|
+
walk(entryChunk);
|
|
187
|
+
}
|
|
188
|
+
return Array.from(chunks);
|
|
189
|
+
}
|
|
190
|
+
function dedupe(array) {
|
|
191
|
+
return [...new Set(array)];
|
|
192
|
+
}
|
|
193
|
+
const writeFileSafe = async (file, contents) => {
|
|
194
|
+
await fse__namespace.ensureDir(path__namespace.dirname(file));
|
|
195
|
+
await fse__namespace.writeFile(file, contents);
|
|
196
|
+
};
|
|
197
|
+
const getRouteManifestModuleExports = async (viteChildCompiler, ctx) => {
|
|
198
|
+
let entries = await Promise.all(Object.entries(ctx.reactRouterConfig.routes).map(async ([key, route]) => {
|
|
199
|
+
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, route.file);
|
|
200
|
+
return [key, sourceExports];
|
|
201
|
+
}));
|
|
202
|
+
return Object.fromEntries(entries);
|
|
203
|
+
};
|
|
204
|
+
const getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
|
|
205
|
+
if (!viteChildCompiler) {
|
|
206
|
+
throw new Error("Vite child compiler not found");
|
|
207
|
+
}
|
|
208
|
+
// We transform the route module code with the Vite child compiler so that we
|
|
209
|
+
// can parse the exports from non-JS files like MDX. This ensures that we can
|
|
210
|
+
// understand the exports from anything that Vite can compile to JS, not just
|
|
211
|
+
// the route file formats that the Remix compiler historically supported.
|
|
212
|
+
let ssr = true;
|
|
213
|
+
let {
|
|
214
|
+
pluginContainer,
|
|
215
|
+
moduleGraph
|
|
216
|
+
} = viteChildCompiler;
|
|
217
|
+
let routePath = path__namespace.resolve(ctx.reactRouterConfig.appDirectory, routeFile);
|
|
218
|
+
let url = resolveFileUrl.resolveFileUrl(ctx, routePath);
|
|
219
|
+
let resolveId = async () => {
|
|
220
|
+
let result = await pluginContainer.resolveId(url, undefined, {
|
|
221
|
+
ssr
|
|
222
|
+
});
|
|
223
|
+
if (!result) throw new Error(`Could not resolve module ID for ${url}`);
|
|
224
|
+
return result.id;
|
|
225
|
+
};
|
|
226
|
+
let [id, code] = await Promise.all([resolveId(), (readRouteFile === null || readRouteFile === void 0 ? void 0 : readRouteFile()) ?? fse__namespace.readFile(routePath, "utf-8"),
|
|
227
|
+
// pluginContainer.transform(...) fails if we don't do this first:
|
|
228
|
+
moduleGraph.ensureEntryFromUrl(url, ssr)]);
|
|
229
|
+
let transformed = await pluginContainer.transform(code, id, {
|
|
230
|
+
ssr
|
|
231
|
+
});
|
|
232
|
+
let [, exports] = esModuleLexer.parse(transformed.code);
|
|
233
|
+
let exportNames = exports.map(e => e.n);
|
|
234
|
+
return exportNames;
|
|
235
|
+
};
|
|
236
|
+
const getServerBundleBuildConfig = viteUserConfig => {
|
|
237
|
+
if (!("__reactRouterServerBundleBuildConfig" in viteUserConfig) || !viteUserConfig.__reactRouterServerBundleBuildConfig) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return viteUserConfig.__reactRouterServerBundleBuildConfig;
|
|
241
|
+
};
|
|
242
|
+
let getServerBuildDirectory = ctx => path__namespace.join(ctx.reactRouterConfig.buildDirectory, "server", ...(ctx.serverBundleBuildConfig ? [ctx.serverBundleBuildConfig.serverBundleId] : []));
|
|
243
|
+
let getClientBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "client");
|
|
244
|
+
let defaultEntriesDir = path__namespace.resolve(__dirname, "..", "config", "defaults");
|
|
245
|
+
let defaultEntries = fse__namespace.readdirSync(defaultEntriesDir).map(filename => path__namespace.join(defaultEntriesDir, filename));
|
|
246
|
+
invariant["default"](defaultEntries.length > 0, "No default entries found");
|
|
247
|
+
let mergeReactRouterConfig = (...configs) => {
|
|
248
|
+
let reducer = (configA, configB) => {
|
|
249
|
+
let mergeRequired = key => configA[key] !== undefined && configB[key] !== undefined;
|
|
250
|
+
return {
|
|
251
|
+
...configA,
|
|
252
|
+
...configB,
|
|
253
|
+
...(mergeRequired("buildEnd") ? {
|
|
254
|
+
buildEnd: async (...args) => {
|
|
255
|
+
var _configA$buildEnd, _configB$buildEnd;
|
|
256
|
+
await Promise.all([(_configA$buildEnd = configA.buildEnd) === null || _configA$buildEnd === void 0 ? void 0 : _configA$buildEnd.call(configA, ...args), (_configB$buildEnd = configB.buildEnd) === null || _configB$buildEnd === void 0 ? void 0 : _configB$buildEnd.call(configB, ...args)]);
|
|
257
|
+
}
|
|
258
|
+
} : {}),
|
|
259
|
+
...(mergeRequired("future") ? {
|
|
260
|
+
future: {
|
|
261
|
+
...configA.future,
|
|
262
|
+
...configB.future
|
|
263
|
+
}
|
|
264
|
+
} : {}),
|
|
265
|
+
...(mergeRequired("ignoredRouteFiles") ? {
|
|
266
|
+
ignoredRouteFiles: Array.from(new Set([...(configA.ignoredRouteFiles ?? []), ...(configB.ignoredRouteFiles ?? [])]))
|
|
267
|
+
} : {}),
|
|
268
|
+
...(mergeRequired("presets") ? {
|
|
269
|
+
presets: [...(configA.presets ?? []), ...(configB.presets ?? [])]
|
|
270
|
+
} : {}),
|
|
271
|
+
...(mergeRequired("routes") ? {
|
|
272
|
+
routes: async (...args) => {
|
|
273
|
+
var _configA$routes, _configB$routes;
|
|
274
|
+
let [routesA, routesB] = await Promise.all([(_configA$routes = configA.routes) === null || _configA$routes === void 0 ? void 0 : _configA$routes.call(configA, ...args), (_configB$routes = configB.routes) === null || _configB$routes === void 0 ? void 0 : _configB$routes.call(configB, ...args)]);
|
|
275
|
+
return {
|
|
276
|
+
...routesA,
|
|
277
|
+
...routesB
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
} : {})
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
return configs.reduce(reducer, {});
|
|
284
|
+
};
|
|
285
|
+
let reactRouterDevLoadContext = () => ({});
|
|
286
|
+
let setReactRouterDevLoadContext = loadContext => {
|
|
287
|
+
reactRouterDevLoadContext = loadContext;
|
|
288
|
+
};
|
|
289
|
+
// Inlined from https://github.com/jsdf/deep-freeze
|
|
290
|
+
let deepFreeze = o => {
|
|
291
|
+
Object.freeze(o);
|
|
292
|
+
let oIsFunction = typeof o === "function";
|
|
293
|
+
let hasOwnProp = Object.prototype.hasOwnProperty;
|
|
294
|
+
Object.getOwnPropertyNames(o).forEach(function (prop) {
|
|
295
|
+
if (hasOwnProp.call(o, prop) && (oIsFunction ? prop !== "caller" && prop !== "callee" && prop !== "arguments" : true) && o[prop] !== null && (typeof o[prop] === "object" || typeof o[prop] === "function") && !Object.isFrozen(o[prop])) {
|
|
296
|
+
deepFreeze(o[prop]);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return o;
|
|
300
|
+
};
|
|
301
|
+
const reactRouterVitePlugin = _config => {
|
|
302
|
+
let reactRouterUserConfig = _config ?? {};
|
|
303
|
+
// Prevent mutations to the user config
|
|
304
|
+
reactRouterUserConfig = deepFreeze(reactRouterUserConfig);
|
|
305
|
+
let viteCommand;
|
|
306
|
+
let viteUserConfig;
|
|
307
|
+
let viteConfigEnv;
|
|
308
|
+
let viteConfig;
|
|
309
|
+
let cssModulesManifest = {};
|
|
310
|
+
let viteChildCompiler = null;
|
|
311
|
+
// This is initialized by `updatePluginContext` during Vite's `config`
|
|
312
|
+
// hook, so most of the code can assume this defined without null check.
|
|
313
|
+
// During dev, `updatePluginContext` is called again on every config file
|
|
314
|
+
// change or route file addition/removal.
|
|
315
|
+
let ctx;
|
|
316
|
+
/** Mutates `ctx` as a side-effect */
|
|
317
|
+
let updatePluginContext = async () => {
|
|
318
|
+
var _viteUserConfig$serve, _viteUserConfig$build;
|
|
319
|
+
let presets = (await Promise.all((reactRouterUserConfig.presets ?? []).map(async preset => {
|
|
320
|
+
if (!preset.name) {
|
|
321
|
+
throw new Error("React Router presets must have a `name` property defined.");
|
|
322
|
+
}
|
|
323
|
+
if (!preset.reactRouterConfig) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
let configPreset = omit__default["default"](await preset.reactRouterConfig({
|
|
327
|
+
reactRouterUserConfig
|
|
328
|
+
}), excludedConfigPresetKeys);
|
|
329
|
+
return configPreset;
|
|
330
|
+
}))).filter(function isNotNull(value) {
|
|
331
|
+
return value !== null;
|
|
332
|
+
});
|
|
333
|
+
let defaults = {
|
|
334
|
+
basename: "/",
|
|
335
|
+
buildDirectory: "build",
|
|
336
|
+
manifest: false,
|
|
337
|
+
serverBuildFile: "index.js",
|
|
338
|
+
ssr: true
|
|
339
|
+
};
|
|
340
|
+
let resolvedReactRouterUserConfig = {
|
|
341
|
+
...defaults,
|
|
342
|
+
// Default values should be completely overridden by user/preset config, not merged
|
|
343
|
+
...mergeReactRouterConfig(...presets, reactRouterUserConfig)
|
|
344
|
+
};
|
|
345
|
+
let rootDirectory = viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
|
|
346
|
+
let {
|
|
347
|
+
basename,
|
|
348
|
+
buildEnd,
|
|
349
|
+
manifest,
|
|
350
|
+
ssr
|
|
351
|
+
} = resolvedReactRouterUserConfig;
|
|
352
|
+
let isSpaMode = !ssr;
|
|
353
|
+
let {
|
|
354
|
+
appDirectory,
|
|
355
|
+
entryClientFilePath,
|
|
356
|
+
entryServerFilePath,
|
|
357
|
+
future,
|
|
358
|
+
routes,
|
|
359
|
+
serverModuleFormat
|
|
360
|
+
} = await config.resolveConfig(resolvedReactRouterUserConfig, {
|
|
361
|
+
rootDirectory,
|
|
362
|
+
isSpaMode
|
|
363
|
+
});
|
|
364
|
+
let buildDirectory = path__namespace.resolve(rootDirectory, resolvedReactRouterUserConfig.buildDirectory);
|
|
365
|
+
let {
|
|
366
|
+
serverBuildFile,
|
|
367
|
+
serverBundles
|
|
368
|
+
} = resolvedReactRouterUserConfig;
|
|
369
|
+
let publicPath = viteUserConfig.base ?? "/";
|
|
370
|
+
if (basename !== "/" && viteCommand === "serve" && !((_viteUserConfig$serve = viteUserConfig.server) !== null && _viteUserConfig$serve !== void 0 && _viteUserConfig$serve.middlewareMode) && !basename.startsWith(publicPath)) {
|
|
371
|
+
throw new Error("When using the React Router `basename` and the Vite `base` config, " + "the `basename` config must begin with `base` for the default " + "Vite dev server.");
|
|
372
|
+
}
|
|
373
|
+
// Log warning for incompatible vite config flags
|
|
374
|
+
if (isSpaMode && serverBundles) {
|
|
375
|
+
console.warn(colors__default["default"].yellow(colors__default["default"].bold("⚠️ SPA Mode: ") + "the `serverBundles` config is invalid with " + "`ssr:false` and will be ignored`"));
|
|
376
|
+
serverBundles = undefined;
|
|
377
|
+
}
|
|
378
|
+
let reactRouterConfig = deepFreeze({
|
|
379
|
+
appDirectory,
|
|
380
|
+
basename,
|
|
381
|
+
buildDirectory,
|
|
382
|
+
buildEnd,
|
|
383
|
+
future,
|
|
384
|
+
manifest,
|
|
385
|
+
publicPath,
|
|
386
|
+
routes,
|
|
387
|
+
serverBuildFile,
|
|
388
|
+
serverBundles,
|
|
389
|
+
serverModuleFormat,
|
|
390
|
+
ssr
|
|
391
|
+
});
|
|
392
|
+
for (let preset of reactRouterUserConfig.presets ?? []) {
|
|
393
|
+
var _preset$reactRouterCo;
|
|
394
|
+
await ((_preset$reactRouterCo = preset.reactRouterConfigResolved) === null || _preset$reactRouterCo === void 0 ? void 0 : _preset$reactRouterCo.call(preset, {
|
|
395
|
+
reactRouterConfig
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
let viteManifestEnabled = ((_viteUserConfig$build = viteUserConfig.build) === null || _viteUserConfig$build === void 0 ? void 0 : _viteUserConfig$build.manifest) === true;
|
|
399
|
+
let ssrBuildCtx = viteConfigEnv.isSsrBuild && viteCommand === "build" ? {
|
|
400
|
+
isSsrBuild: true,
|
|
401
|
+
getReactRouterServerManifest: async () => (await generateReactRouterManifestsForBuild()).reactRouterServerManifest,
|
|
402
|
+
serverBundleBuildConfig: getServerBundleBuildConfig(viteUserConfig)
|
|
403
|
+
} : {
|
|
404
|
+
isSsrBuild: false
|
|
405
|
+
};
|
|
406
|
+
ctx = {
|
|
407
|
+
reactRouterConfig,
|
|
408
|
+
rootDirectory,
|
|
409
|
+
entryClientFilePath,
|
|
410
|
+
entryServerFilePath,
|
|
411
|
+
viteManifestEnabled,
|
|
412
|
+
...ssrBuildCtx
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
let pluginIndex = pluginName => {
|
|
416
|
+
invariant["default"](viteConfig);
|
|
417
|
+
return viteConfig.plugins.findIndex(plugin => plugin.name === pluginName);
|
|
418
|
+
};
|
|
419
|
+
let getServerEntry = async () => {
|
|
420
|
+
invariant["default"](viteConfig, "viteconfig required to generate the server entry");
|
|
421
|
+
let routes = ctx.serverBundleBuildConfig ?
|
|
422
|
+
// For server bundle builds, the server build should only import the
|
|
423
|
+
// routes for this bundle rather than importing all routes
|
|
424
|
+
ctx.serverBundleBuildConfig.routes :
|
|
425
|
+
// Otherwise, all routes are imported as usual
|
|
426
|
+
ctx.reactRouterConfig.routes;
|
|
427
|
+
return `
|
|
428
|
+
import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryServerFilePath))};
|
|
429
|
+
${Object.keys(routes).map((key, index) => {
|
|
430
|
+
let route = routes[key];
|
|
431
|
+
return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)))};`;
|
|
432
|
+
}).join("\n")}
|
|
433
|
+
export { default as assets } from ${JSON.stringify(serverManifestId)};
|
|
434
|
+
export const assetsBuildDirectory = ${JSON.stringify(path__namespace.relative(ctx.rootDirectory, getClientBuildDirectory(ctx.reactRouterConfig)))};
|
|
435
|
+
export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
|
|
436
|
+
export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
|
|
437
|
+
export const isSpaMode = ${!ctx.reactRouterConfig.ssr};
|
|
438
|
+
export const publicPath = ${JSON.stringify(ctx.reactRouterConfig.publicPath)};
|
|
439
|
+
export const entry = { module: entryServer };
|
|
440
|
+
export const routes = {
|
|
441
|
+
${Object.keys(routes).map((key, index) => {
|
|
442
|
+
let route = routes[key];
|
|
443
|
+
return `${JSON.stringify(key)}: {
|
|
444
|
+
id: ${JSON.stringify(route.id)},
|
|
445
|
+
parentId: ${JSON.stringify(route.parentId)},
|
|
446
|
+
path: ${JSON.stringify(route.path)},
|
|
447
|
+
index: ${JSON.stringify(route.index)},
|
|
448
|
+
caseSensitive: ${JSON.stringify(route.caseSensitive)},
|
|
449
|
+
module: route${index}
|
|
450
|
+
}`;
|
|
451
|
+
}).join(",\n ")}
|
|
452
|
+
};`;
|
|
453
|
+
};
|
|
454
|
+
let loadViteManifest = async directory => {
|
|
455
|
+
let manifestContents = await fse__namespace.readFile(path__namespace.resolve(directory, ".vite", "manifest.json"), "utf-8");
|
|
456
|
+
return JSON.parse(manifestContents);
|
|
457
|
+
};
|
|
458
|
+
let getViteManifestAssetPaths = viteManifest => {
|
|
459
|
+
// Get .css?url imports and CSS entry points
|
|
460
|
+
let cssUrlPaths = Object.values(viteManifest).filter(chunk => chunk.file.endsWith(".css")).map(chunk => chunk.file);
|
|
461
|
+
// Get bundled CSS files and generic asset types
|
|
462
|
+
let chunkAssetPaths = Object.values(viteManifest).flatMap(chunk => chunk.assets ?? []);
|
|
463
|
+
return new Set([...cssUrlPaths, ...chunkAssetPaths]);
|
|
464
|
+
};
|
|
465
|
+
let generateReactRouterManifestsForBuild = async () => {
|
|
466
|
+
invariant["default"](viteConfig);
|
|
467
|
+
let viteManifest = await loadViteManifest(getClientBuildDirectory(ctx.reactRouterConfig));
|
|
468
|
+
let entry = getReactRouterManifestBuildAssets(ctx, viteManifest, ctx.entryClientFilePath);
|
|
469
|
+
let browserRoutes = {};
|
|
470
|
+
let serverRoutes = {};
|
|
471
|
+
let routeManifestExports = await getRouteManifestModuleExports(viteChildCompiler, ctx);
|
|
472
|
+
for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
|
|
473
|
+
var _ctx$serverBundleBuil;
|
|
474
|
+
let routeFilePath = path__namespace.join(ctx.reactRouterConfig.appDirectory, route.file);
|
|
475
|
+
let sourceExports = routeManifestExports[key];
|
|
476
|
+
let isRootRoute = route.parentId === undefined;
|
|
477
|
+
let routeManifestEntry = {
|
|
478
|
+
id: route.id,
|
|
479
|
+
parentId: route.parentId,
|
|
480
|
+
path: route.path,
|
|
481
|
+
index: route.index,
|
|
482
|
+
caseSensitive: route.caseSensitive,
|
|
483
|
+
hasAction: sourceExports.includes("action"),
|
|
484
|
+
hasLoader: sourceExports.includes("loader"),
|
|
485
|
+
hasClientAction: sourceExports.includes("clientAction"),
|
|
486
|
+
hasClientLoader: sourceExports.includes("clientLoader"),
|
|
487
|
+
hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
|
|
488
|
+
...getReactRouterManifestBuildAssets(ctx, viteManifest, routeFilePath,
|
|
489
|
+
// If this is the root route, we also need to include assets from the
|
|
490
|
+
// client entry file as this is a common way for consumers to import
|
|
491
|
+
// global reset styles, etc.
|
|
492
|
+
isRootRoute ? [ctx.entryClientFilePath] : [])
|
|
493
|
+
};
|
|
494
|
+
browserRoutes[key] = routeManifestEntry;
|
|
495
|
+
let serverBundleRoutes = (_ctx$serverBundleBuil = ctx.serverBundleBuildConfig) === null || _ctx$serverBundleBuil === void 0 ? void 0 : _ctx$serverBundleBuil.routes;
|
|
496
|
+
if (!serverBundleRoutes || serverBundleRoutes[key]) {
|
|
497
|
+
serverRoutes[key] = routeManifestEntry;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
let fingerprintedValues = {
|
|
501
|
+
entry,
|
|
502
|
+
routes: browserRoutes
|
|
503
|
+
};
|
|
504
|
+
let version = getHash(JSON.stringify(fingerprintedValues), 8);
|
|
505
|
+
let manifestPath = path__namespace.posix.join(viteConfig.build.assetsDir, `manifest-${version}.js`);
|
|
506
|
+
let url = `${ctx.reactRouterConfig.publicPath}${manifestPath}`;
|
|
507
|
+
let nonFingerprintedValues = {
|
|
508
|
+
url,
|
|
509
|
+
version
|
|
510
|
+
};
|
|
511
|
+
let reactRouterBrowserManifest = {
|
|
512
|
+
...fingerprintedValues,
|
|
513
|
+
...nonFingerprintedValues
|
|
514
|
+
};
|
|
515
|
+
// Write the browser manifest to disk as part of the build process
|
|
516
|
+
await writeFileSafe(path__namespace.join(getClientBuildDirectory(ctx.reactRouterConfig), manifestPath), `window.__remixManifest=${JSON.stringify(reactRouterBrowserManifest)};`);
|
|
517
|
+
// The server manifest is the same as the browser manifest, except for
|
|
518
|
+
// server bundle builds which only includes routes for the current bundle,
|
|
519
|
+
// otherwise the server and client have the same routes
|
|
520
|
+
let reactRouterServerManifest = {
|
|
521
|
+
...reactRouterBrowserManifest,
|
|
522
|
+
routes: serverRoutes
|
|
523
|
+
};
|
|
524
|
+
return {
|
|
525
|
+
reactRouterBrowserManifest,
|
|
526
|
+
reactRouterServerManifest
|
|
527
|
+
};
|
|
528
|
+
};
|
|
529
|
+
// In dev, the server and browser manifests are the same
|
|
530
|
+
let getReactRouterManifestForDev = async () => {
|
|
531
|
+
let routes = {};
|
|
532
|
+
let routeManifestExports = await getRouteManifestModuleExports(viteChildCompiler, ctx);
|
|
533
|
+
for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
|
|
534
|
+
let sourceExports = routeManifestExports[key];
|
|
535
|
+
routes[key] = {
|
|
536
|
+
id: route.id,
|
|
537
|
+
parentId: route.parentId,
|
|
538
|
+
path: route.path,
|
|
539
|
+
index: route.index,
|
|
540
|
+
caseSensitive: route.caseSensitive,
|
|
541
|
+
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}${CLIENT_ROUTE_QUERY_STRING}`),
|
|
542
|
+
hasAction: sourceExports.includes("action"),
|
|
543
|
+
hasLoader: sourceExports.includes("loader"),
|
|
544
|
+
hasClientAction: sourceExports.includes("clientAction"),
|
|
545
|
+
hasClientLoader: sourceExports.includes("clientLoader"),
|
|
546
|
+
hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
|
|
547
|
+
imports: []
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
version: String(Math.random()),
|
|
552
|
+
url: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, vmod.url(browserManifestId)),
|
|
553
|
+
hmr: {
|
|
554
|
+
runtime: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, vmod.url(injectHmrRuntimeId))
|
|
555
|
+
},
|
|
556
|
+
entry: {
|
|
557
|
+
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, resolveFileUrl.resolveFileUrl(ctx, ctx.entryClientFilePath)),
|
|
558
|
+
imports: []
|
|
559
|
+
},
|
|
560
|
+
routes
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
return [{
|
|
564
|
+
name: "react-router",
|
|
565
|
+
config: async (_viteUserConfig, _viteConfigEnv) => {
|
|
566
|
+
var _viteUserConfig$serve2, _viteUserConfig$serve3, _viteUserConfig$build4;
|
|
567
|
+
// Preload Vite's ESM build up-front as soon as we're in an async context
|
|
568
|
+
await importViteEsmSync.preloadViteEsm();
|
|
569
|
+
// Ensure sync import of Vite works after async preload
|
|
570
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
571
|
+
viteUserConfig = _viteUserConfig;
|
|
572
|
+
viteConfigEnv = _viteConfigEnv;
|
|
573
|
+
viteCommand = viteConfigEnv.command;
|
|
574
|
+
await updatePluginContext();
|
|
575
|
+
Object.assign(process.env, vite.loadEnv(viteConfigEnv.mode, ctx.rootDirectory,
|
|
576
|
+
// We override default prefix of "VITE_" with a blank string since
|
|
577
|
+
// we're targeting the server, so we want to load all environment
|
|
578
|
+
// variables, not just those explicitly marked for the client
|
|
579
|
+
""));
|
|
580
|
+
let baseRollupOptions = {
|
|
581
|
+
// Silence Rollup "use client" warnings
|
|
582
|
+
// Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
|
|
583
|
+
onwarn(warning, defaultHandler) {
|
|
584
|
+
var _viteUserConfig$build2, _viteUserConfig$build3;
|
|
585
|
+
if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if ((_viteUserConfig$build2 = viteUserConfig.build) !== null && _viteUserConfig$build2 !== void 0 && (_viteUserConfig$build3 = _viteUserConfig$build2.rollupOptions) !== null && _viteUserConfig$build3 !== void 0 && _viteUserConfig$build3.onwarn) {
|
|
589
|
+
viteUserConfig.build.rollupOptions.onwarn(warning, defaultHandler);
|
|
590
|
+
} else {
|
|
591
|
+
defaultHandler(warning);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
return {
|
|
596
|
+
__reactRouterPluginContext: ctx,
|
|
597
|
+
appType: viteCommand === "serve" && viteConfigEnv.mode === "production" && ctx.reactRouterConfig.ssr === false ? "spa" : "custom",
|
|
598
|
+
ssr: {
|
|
599
|
+
external: [
|
|
600
|
+
// This is only necessary for development within this repo
|
|
601
|
+
// because these packages are symlinked and Vite treats them as
|
|
602
|
+
// internal source code. For consumers this is a no-op.
|
|
603
|
+
"react-router", "react-router-dom", "@react-router/architect", "@react-router/cloudflare-pages", "@react-router/cloudflare-workers", "@react-router/cloudflare", "@react-router/deno", "@react-router/dev", "@react-router/express", "@react-router/netlify", "@react-router/node", "@react-router/serve", "@react-router/server-runtime"]
|
|
604
|
+
},
|
|
605
|
+
optimizeDeps: {
|
|
606
|
+
include: [
|
|
607
|
+
// Pre-bundle React dependencies to avoid React duplicates,
|
|
608
|
+
// even if React dependencies are not direct dependencies.
|
|
609
|
+
// https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
|
|
610
|
+
"react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/client",
|
|
611
|
+
// Pre-bundle router dependencies to avoid router duplicates.
|
|
612
|
+
// Mismatching routers cause `Error: You must render this element inside a <Remix> element`.
|
|
613
|
+
"react-router", "react-router-dom",
|
|
614
|
+
// For some reason, the `vite-dotenv` integration test consistently fails on webkit
|
|
615
|
+
// with `504 (Outdated Optimize Dep)` from Vite unless `@react-router/node` is included
|
|
616
|
+
// in `optimizeDeps.include`. 🤷
|
|
617
|
+
// This could be caused by how we copy `node_modules/` into integration test fixtures,
|
|
618
|
+
// so maybe this will be unnecessary once we switch to pnpm
|
|
619
|
+
"@react-router/node"]
|
|
620
|
+
},
|
|
621
|
+
esbuild: {
|
|
622
|
+
jsx: "automatic",
|
|
623
|
+
jsxDev: viteCommand !== "build"
|
|
624
|
+
},
|
|
625
|
+
resolve: {
|
|
626
|
+
dedupe: [
|
|
627
|
+
// https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
|
|
628
|
+
"react", "react-dom",
|
|
629
|
+
// see description for `optimizeDeps.include`
|
|
630
|
+
"react-router", "react-router-dom"]
|
|
631
|
+
},
|
|
632
|
+
base: viteUserConfig.base,
|
|
633
|
+
// When consumer provides an allow list for files that can be read by
|
|
634
|
+
// the server, ensure that the default entry files are included.
|
|
635
|
+
// If we don't do this and a default entry file is used, the server
|
|
636
|
+
// will throw an error that the file is not allowed to be read.
|
|
637
|
+
// https://vitejs.dev/config/server-options#server-fs-allow
|
|
638
|
+
server: (_viteUserConfig$serve2 = viteUserConfig.server) !== null && _viteUserConfig$serve2 !== void 0 && (_viteUserConfig$serve3 = _viteUserConfig$serve2.fs) !== null && _viteUserConfig$serve3 !== void 0 && _viteUserConfig$serve3.allow ? {
|
|
639
|
+
fs: {
|
|
640
|
+
allow: defaultEntries
|
|
641
|
+
}
|
|
642
|
+
} : undefined,
|
|
643
|
+
// Vite config options for building
|
|
644
|
+
...(viteCommand === "build" ? {
|
|
645
|
+
build: {
|
|
646
|
+
cssMinify: ((_viteUserConfig$build4 = viteUserConfig.build) === null || _viteUserConfig$build4 === void 0 ? void 0 : _viteUserConfig$build4.cssMinify) ?? true,
|
|
647
|
+
...(!viteConfigEnv.isSsrBuild ? {
|
|
648
|
+
manifest: true,
|
|
649
|
+
outDir: getClientBuildDirectory(ctx.reactRouterConfig),
|
|
650
|
+
rollupOptions: {
|
|
651
|
+
...baseRollupOptions,
|
|
652
|
+
preserveEntrySignatures: "exports-only",
|
|
653
|
+
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${CLIENT_ROUTE_QUERY_STRING}`)]
|
|
654
|
+
}
|
|
655
|
+
} : {
|
|
656
|
+
// We move SSR-only assets to client assets. Note that the
|
|
657
|
+
// SSR build can also emit code-split JS files (e.g. by
|
|
658
|
+
// dynamic import) under the same assets directory
|
|
659
|
+
// regardless of "ssrEmitAssets" option, so we also need to
|
|
660
|
+
// keep these JS files have to be kept as-is.
|
|
661
|
+
ssrEmitAssets: true,
|
|
662
|
+
copyPublicDir: false,
|
|
663
|
+
// Assets in the public directory are only used by the client
|
|
664
|
+
manifest: true,
|
|
665
|
+
// We need the manifest to detect SSR-only assets
|
|
666
|
+
outDir: getServerBuildDirectory(ctx),
|
|
667
|
+
rollupOptions: {
|
|
668
|
+
...baseRollupOptions,
|
|
669
|
+
preserveEntrySignatures: "exports-only",
|
|
670
|
+
input: serverBuildId,
|
|
671
|
+
output: {
|
|
672
|
+
entryFileNames: ctx.reactRouterConfig.serverBuildFile,
|
|
673
|
+
format: ctx.reactRouterConfig.serverModuleFormat
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
})
|
|
677
|
+
}
|
|
678
|
+
} : undefined),
|
|
679
|
+
// Vite config options for SPA preview mode
|
|
680
|
+
...(viteCommand === "serve" && ctx.reactRouterConfig.ssr === false ? {
|
|
681
|
+
build: {
|
|
682
|
+
manifest: true,
|
|
683
|
+
outDir: getClientBuildDirectory(ctx.reactRouterConfig)
|
|
684
|
+
}
|
|
685
|
+
} : undefined)
|
|
686
|
+
};
|
|
687
|
+
},
|
|
688
|
+
async configResolved(resolvedViteConfig) {
|
|
689
|
+
await esModuleLexer.init;
|
|
690
|
+
viteConfig = resolvedViteConfig;
|
|
691
|
+
invariant["default"](viteConfig);
|
|
692
|
+
// We load the same Vite config file again for the child compiler so
|
|
693
|
+
// that both parent and child compiler's plugins have independent state.
|
|
694
|
+
// If we re-used the `viteUserConfig.plugins` array for the child
|
|
695
|
+
// compiler, it could lead to mutating shared state between plugin
|
|
696
|
+
// instances in unexpected ways, e.g. during `vite build` the
|
|
697
|
+
// `configResolved` plugin hook would be called with `command = "build"`
|
|
698
|
+
// by parent and then `command = "serve"` by child, which some plugins
|
|
699
|
+
// may respond to by updating state referenced by the parent.
|
|
700
|
+
if (!viteConfig.configFile) {
|
|
701
|
+
throw new Error("The React Router Vite plugin requires the use of a Vite config file");
|
|
702
|
+
}
|
|
703
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
704
|
+
let childCompilerConfigFile = await vite.loadConfigFromFile({
|
|
705
|
+
command: viteConfig.command,
|
|
706
|
+
mode: viteConfig.mode,
|
|
707
|
+
isSsrBuild: ctx.isSsrBuild
|
|
708
|
+
}, viteConfig.configFile);
|
|
709
|
+
invariant["default"](childCompilerConfigFile, "Vite config file was unable to be resolved for React Router child compiler");
|
|
710
|
+
// Validate that commonly used Rollup plugins that need to run before
|
|
711
|
+
// ours are in the correct order. This is because Rollup plugins can't
|
|
712
|
+
// set `enforce: "pre"` like Vite plugins can. Explicitly validating
|
|
713
|
+
// this provides a much nicer developer experience.
|
|
714
|
+
let rollupPrePlugins = [{
|
|
715
|
+
pluginName: "@mdx-js/rollup",
|
|
716
|
+
displayName: "@mdx-js/rollup"
|
|
717
|
+
}];
|
|
718
|
+
for (let prePlugin of rollupPrePlugins) {
|
|
719
|
+
let prePluginIndex = pluginIndex(prePlugin.pluginName);
|
|
720
|
+
if (prePluginIndex >= 0 && prePluginIndex > pluginIndex("react-router")) {
|
|
721
|
+
throw new Error(`The "${prePlugin.displayName}" plugin should be placed before the React Router plugin in your Vite config file`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
viteChildCompiler = await vite.createServer({
|
|
725
|
+
...viteUserConfig,
|
|
726
|
+
mode: viteConfig.mode,
|
|
727
|
+
server: {
|
|
728
|
+
watch: viteConfig.command === "build" ? null : undefined,
|
|
729
|
+
preTransformRequests: false,
|
|
730
|
+
hmr: false
|
|
731
|
+
},
|
|
732
|
+
configFile: false,
|
|
733
|
+
envFile: false,
|
|
734
|
+
plugins: [...(childCompilerConfigFile.config.plugins ?? []).flat()
|
|
735
|
+
// Exclude this plugin from the child compiler to prevent an
|
|
736
|
+
// infinite loop (plugin creates a child compiler with the same
|
|
737
|
+
// plugin that creates another child compiler, repeat ad
|
|
738
|
+
// infinitum), and to prevent the manifest from being written to
|
|
739
|
+
// disk from the child compiler. This is important in the
|
|
740
|
+
// production build because the child compiler is a Vite dev
|
|
741
|
+
// server and will generate incorrect manifests.
|
|
742
|
+
.filter(plugin => typeof plugin === "object" && plugin !== null && "name" in plugin && plugin.name !== "react-router" && plugin.name !== "react-router-hmr-updates")]
|
|
743
|
+
});
|
|
744
|
+
await viteChildCompiler.pluginContainer.buildStart({});
|
|
745
|
+
},
|
|
746
|
+
async transform(code, id) {
|
|
747
|
+
if (styles.isCssModulesFile(id)) {
|
|
748
|
+
cssModulesManifest[id] = code;
|
|
749
|
+
}
|
|
750
|
+
if (isClientRoute(id)) {
|
|
751
|
+
let routeModuleId = id.replace(CLIENT_ROUTE_QUERY_STRING, "");
|
|
752
|
+
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
|
|
753
|
+
let routeFileName = path__namespace.basename(routeModuleId);
|
|
754
|
+
let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
|
|
755
|
+
return `export { ${clientExports} } from "./${routeFileName}";`;
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
buildStart() {
|
|
759
|
+
invariant["default"](viteConfig);
|
|
760
|
+
if (viteCommand === "build" && viteConfig.mode === "production" && !viteConfig.build.ssr && viteConfig.build.sourcemap) {
|
|
761
|
+
viteConfig.logger.warn(colors__default["default"].yellow("\n" + colors__default["default"].bold(" ⚠️ Source maps are enabled in production\n") + ["This makes your server code publicly", "visible in the browser. This is highly", "discouraged! If you insist, ensure that", "you are using environment variables for", "secrets and not hard-coding them in", "your source code."].map(line => " " + line).join("\n") + "\n"));
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
async configureServer(viteDevServer) {
|
|
765
|
+
serverRuntime.unstable_setDevServerHooks({
|
|
766
|
+
// Give the request handler access to the critical CSS in dev to avoid a
|
|
767
|
+
// flash of unstyled content since Vite injects CSS file contents via JS
|
|
768
|
+
getCriticalCss: async (build, url) => {
|
|
769
|
+
return styles.getStylesForUrl({
|
|
770
|
+
rootDirectory: ctx.rootDirectory,
|
|
771
|
+
entryClientFilePath: ctx.entryClientFilePath,
|
|
772
|
+
reactRouterConfig: ctx.reactRouterConfig,
|
|
773
|
+
viteDevServer,
|
|
774
|
+
cssModulesManifest,
|
|
775
|
+
build,
|
|
776
|
+
url
|
|
777
|
+
});
|
|
778
|
+
},
|
|
779
|
+
// If an error is caught within the request handler, let Vite fix the
|
|
780
|
+
// stack trace so it maps back to the actual source code
|
|
781
|
+
processRequestError: error => {
|
|
782
|
+
if (error instanceof Error) {
|
|
783
|
+
viteDevServer.ssrFixStacktrace(error);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
// Invalidate virtual modules and update cached plugin config via file watcher
|
|
788
|
+
viteDevServer.watcher.on("all", async (eventName, filepath) => {
|
|
789
|
+
var _viteConfig;
|
|
790
|
+
let {
|
|
791
|
+
normalizePath
|
|
792
|
+
} = importViteEsmSync.importViteEsmSync();
|
|
793
|
+
let appFileAddedOrRemoved = (eventName === "add" || eventName === "unlink") && normalizePath(filepath).startsWith(normalizePath(ctx.reactRouterConfig.appDirectory));
|
|
794
|
+
invariant["default"]((_viteConfig = viteConfig) === null || _viteConfig === void 0 ? void 0 : _viteConfig.configFile);
|
|
795
|
+
let viteConfigChanged = eventName === "change" && normalizePath(filepath) === normalizePath(viteConfig.configFile);
|
|
796
|
+
if (appFileAddedOrRemoved || viteConfigChanged) {
|
|
797
|
+
let lastReactRouterConfig = ctx.reactRouterConfig;
|
|
798
|
+
await updatePluginContext();
|
|
799
|
+
if (!isEqualJson(lastReactRouterConfig, ctx.reactRouterConfig)) {
|
|
800
|
+
invalidateVirtualModules(viteDevServer);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
return () => {
|
|
805
|
+
// Let user servers handle SSR requests in middleware mode,
|
|
806
|
+
// otherwise the Vite plugin will handle the request
|
|
807
|
+
if (!viteDevServer.config.server.middlewareMode) {
|
|
808
|
+
viteDevServer.middlewares.use(async (req, res, next) => {
|
|
809
|
+
try {
|
|
810
|
+
let build = await viteDevServer.ssrLoadModule(serverBuildId);
|
|
811
|
+
let handler = serverRuntime.createRequestHandler(build, "development");
|
|
812
|
+
let nodeHandler = async (nodeReq, nodeRes) => {
|
|
813
|
+
let req = nodeAdapter.fromNodeRequest(nodeReq);
|
|
814
|
+
let res = await handler(req, await reactRouterDevLoadContext(req));
|
|
815
|
+
await nodeAdapter.toNodeRequest(res, nodeRes);
|
|
816
|
+
};
|
|
817
|
+
await nodeHandler(req, res);
|
|
818
|
+
} catch (error) {
|
|
819
|
+
next(error);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
},
|
|
825
|
+
writeBundle: {
|
|
826
|
+
// After the SSR build is finished, we inspect the Vite manifest for
|
|
827
|
+
// the SSR build and move server-only assets to client assets directory
|
|
828
|
+
async handler() {
|
|
829
|
+
if (!ctx.isSsrBuild) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
invariant["default"](viteConfig);
|
|
833
|
+
let clientBuildDirectory = getClientBuildDirectory(ctx.reactRouterConfig);
|
|
834
|
+
let serverBuildDirectory = getServerBuildDirectory(ctx);
|
|
835
|
+
let ssrViteManifest = await loadViteManifest(serverBuildDirectory);
|
|
836
|
+
let clientViteManifest = await loadViteManifest(clientBuildDirectory);
|
|
837
|
+
let clientAssetPaths = getViteManifestAssetPaths(clientViteManifest);
|
|
838
|
+
let ssrAssetPaths = getViteManifestAssetPaths(ssrViteManifest);
|
|
839
|
+
// We only move assets that aren't in the client build, otherwise we
|
|
840
|
+
// remove them. These assets only exist because we explicitly set
|
|
841
|
+
// `ssrEmitAssets: true` in the SSR Vite config. These assets
|
|
842
|
+
// typically wouldn't exist by default, which is why we assume it's
|
|
843
|
+
// safe to remove them. We're aiming for a clean build output so that
|
|
844
|
+
// unnecessary assets don't get deployed alongside the server code.
|
|
845
|
+
let movedAssetPaths = [];
|
|
846
|
+
for (let ssrAssetPath of ssrAssetPaths) {
|
|
847
|
+
let src = path__namespace.join(serverBuildDirectory, ssrAssetPath);
|
|
848
|
+
if (!clientAssetPaths.has(ssrAssetPath)) {
|
|
849
|
+
let dest = path__namespace.join(clientBuildDirectory, ssrAssetPath);
|
|
850
|
+
await fse__namespace.move(src, dest);
|
|
851
|
+
movedAssetPaths.push(dest);
|
|
852
|
+
} else {
|
|
853
|
+
await fse__namespace.remove(src);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
// We assume CSS assets from the SSR build are unnecessary and remove
|
|
857
|
+
// them for the same reasons as above.
|
|
858
|
+
let ssrCssPaths = Object.values(ssrViteManifest).flatMap(chunk => chunk.css ?? []);
|
|
859
|
+
await Promise.all(ssrCssPaths.map(cssPath => fse__namespace.remove(path__namespace.join(serverBuildDirectory, cssPath))));
|
|
860
|
+
if (movedAssetPaths.length) {
|
|
861
|
+
viteConfig.logger.info(["", `${colors__default["default"].green("✓")} ${movedAssetPaths.length} asset${movedAssetPaths.length > 1 ? "s" : ""} moved from React Router server build to client assets.`, ...movedAssetPaths.map(movedAssetPath => colors__default["default"].dim(path__namespace.relative(ctx.rootDirectory, movedAssetPath))), ""].join("\n"));
|
|
862
|
+
}
|
|
863
|
+
if (!ctx.reactRouterConfig.ssr) {
|
|
864
|
+
await handleSpaMode(serverBuildDirectory, ctx.reactRouterConfig.serverBuildFile, clientBuildDirectory, viteConfig, ctx.reactRouterConfig.basename);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
async buildEnd() {
|
|
869
|
+
var _viteChildCompiler;
|
|
870
|
+
await ((_viteChildCompiler = viteChildCompiler) === null || _viteChildCompiler === void 0 ? void 0 : _viteChildCompiler.close());
|
|
871
|
+
}
|
|
872
|
+
}, {
|
|
873
|
+
name: "react-router-virtual-modules",
|
|
874
|
+
enforce: "pre",
|
|
875
|
+
resolveId(id) {
|
|
876
|
+
if (vmods.includes(id)) return vmod.resolve(id);
|
|
877
|
+
},
|
|
878
|
+
async load(id) {
|
|
879
|
+
switch (id) {
|
|
880
|
+
case vmod.resolve(serverBuildId):
|
|
881
|
+
{
|
|
882
|
+
return await getServerEntry();
|
|
883
|
+
}
|
|
884
|
+
case vmod.resolve(serverManifestId):
|
|
885
|
+
{
|
|
886
|
+
let reactRouterManifest = ctx.isSsrBuild ? await ctx.getReactRouterServerManifest() : await getReactRouterManifestForDev();
|
|
887
|
+
return `export default ${jsesc__default["default"](reactRouterManifest, {
|
|
888
|
+
es6: true
|
|
889
|
+
})};`;
|
|
890
|
+
}
|
|
891
|
+
case vmod.resolve(browserManifestId):
|
|
892
|
+
{
|
|
893
|
+
if (viteCommand === "build") {
|
|
894
|
+
throw new Error("This module only exists in development");
|
|
895
|
+
}
|
|
896
|
+
let reactRouterManifest = await getReactRouterManifestForDev();
|
|
897
|
+
let reactRouterManifestString = jsesc__default["default"](reactRouterManifest, {
|
|
898
|
+
es6: true
|
|
899
|
+
});
|
|
900
|
+
return `window.__remixManifest=${reactRouterManifestString};`;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}, {
|
|
905
|
+
name: "react-router-dot-server",
|
|
906
|
+
enforce: "pre",
|
|
907
|
+
async resolveId(id, importer, options) {
|
|
908
|
+
var _options$custom;
|
|
909
|
+
if (options !== null && options !== void 0 && options.ssr) return;
|
|
910
|
+
let isResolving = (options === null || options === void 0 ? void 0 : (_options$custom = options.custom) === null || _options$custom === void 0 ? void 0 : _options$custom["react-router-dot-server"]) ?? false;
|
|
911
|
+
if (isResolving) return;
|
|
912
|
+
options.custom = {
|
|
913
|
+
...options.custom,
|
|
914
|
+
"react-router-dot-server": true
|
|
915
|
+
};
|
|
916
|
+
let resolved = await this.resolve(id, importer, options);
|
|
917
|
+
if (!resolved) return;
|
|
918
|
+
let serverFileRE = /\.server(\.[cm]?[jt]sx?)?$/;
|
|
919
|
+
let serverDirRE = /\/\.server\//;
|
|
920
|
+
let isDotServer = serverFileRE.test(resolved.id) || serverDirRE.test(resolved.id);
|
|
921
|
+
if (!isDotServer) return;
|
|
922
|
+
if (!importer) return;
|
|
923
|
+
if (viteCommand !== "build" && importer.endsWith(".html")) {
|
|
924
|
+
// Vite has a special `index.html` importer for `resolveId` within `transformRequest`
|
|
925
|
+
// https://github.com/vitejs/vite/blob/5684fcd8d27110d098b3e1c19d851f44251588f1/packages/vite/src/node/server/transformRequest.ts#L158
|
|
926
|
+
// https://github.com/vitejs/vite/blob/5684fcd8d27110d098b3e1c19d851f44251588f1/packages/vite/src/node/server/pluginContainer.ts#L668
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
930
|
+
let importerShort = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, importer));
|
|
931
|
+
let isRoute = getRoute(ctx.reactRouterConfig, importer);
|
|
932
|
+
if (isRoute) {
|
|
933
|
+
let serverOnlyExports = SERVER_ONLY_ROUTE_EXPORTS.map(xport => `\`${xport}\``).join(", ");
|
|
934
|
+
throw Error([colors__default["default"].red(`Server-only module referenced by client`), "", ` '${id}' imported by route '${importerShort}'`, "", ` React Router automatically removes server-code from these exports:`, ` ${serverOnlyExports}`, "", ` But other route exports in '${importerShort}' depend on '${id}'.`, "", " See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code", ""].join("\n"));
|
|
935
|
+
}
|
|
936
|
+
throw Error([colors__default["default"].red(`Server-only module referenced by client`), "", ` '${id}' imported by '${importerShort}'`, "", " See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code", ""].join("\n"));
|
|
937
|
+
}
|
|
938
|
+
}, {
|
|
939
|
+
name: "react-router-dot-client",
|
|
940
|
+
async transform(code, id, options) {
|
|
941
|
+
if (!(options !== null && options !== void 0 && options.ssr)) return;
|
|
942
|
+
let clientFileRE = /\.client(\.[cm]?[jt]sx?)?$/;
|
|
943
|
+
let clientDirRE = /\/\.client\//;
|
|
944
|
+
if (clientFileRE.test(id) || clientDirRE.test(id)) {
|
|
945
|
+
let exports = esModuleLexer.parse(code)[1];
|
|
946
|
+
return {
|
|
947
|
+
code: exports.map(({
|
|
948
|
+
n: name
|
|
949
|
+
}) => name === "default" ? "export default undefined;" : `export const ${name} = undefined;`).join("\n"),
|
|
950
|
+
map: null
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}, {
|
|
955
|
+
name: "react-router-route-exports",
|
|
956
|
+
async transform(code, id, options) {
|
|
957
|
+
if (options !== null && options !== void 0 && options.ssr) return;
|
|
958
|
+
let route = getRoute(ctx.reactRouterConfig, id);
|
|
959
|
+
if (!route) return;
|
|
960
|
+
if (!ctx.reactRouterConfig.ssr) {
|
|
961
|
+
let serverOnlyExports = esModuleLexer.parse(code)[1].map(exp => exp.n).filter(exp => SERVER_ONLY_ROUTE_EXPORTS.includes(exp));
|
|
962
|
+
if (serverOnlyExports.length > 0) {
|
|
963
|
+
let str = serverOnlyExports.map(e => `\`${e}\``).join(", ");
|
|
964
|
+
let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in ` + `\`${route.file}\`: ${str}. See https://remix.run/guides/spa-mode ` + `for more information.`;
|
|
965
|
+
throw Error(message);
|
|
966
|
+
}
|
|
967
|
+
if (route.id !== "root") {
|
|
968
|
+
let hasHydrateFallback = esModuleLexer.parse(code)[1].map(exp => exp.n).some(exp => exp === "HydrateFallback");
|
|
969
|
+
if (hasHydrateFallback) {
|
|
970
|
+
let message = `SPA Mode: Invalid \`HydrateFallback\` export found in ` + `\`${route.file}\`. \`HydrateFallback\` is only permitted on ` + `the root route in SPA Mode. See https://remix.run/guides/spa-mode ` + `for more information.`;
|
|
971
|
+
throw Error(message);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
let [filepath] = id.split("?");
|
|
976
|
+
return removeExports.removeExports(code, SERVER_ONLY_ROUTE_EXPORTS, {
|
|
977
|
+
sourceMaps: true,
|
|
978
|
+
filename: id,
|
|
979
|
+
sourceFileName: filepath
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
}, {
|
|
983
|
+
name: "react-router-inject-hmr-runtime",
|
|
984
|
+
enforce: "pre",
|
|
985
|
+
resolveId(id) {
|
|
986
|
+
if (id === injectHmrRuntimeId) return vmod.resolve(injectHmrRuntimeId);
|
|
987
|
+
},
|
|
988
|
+
async load(id) {
|
|
989
|
+
if (id !== vmod.resolve(injectHmrRuntimeId)) return;
|
|
990
|
+
return [`import RefreshRuntime from "${hmrRuntimeId}"`, "RefreshRuntime.injectIntoGlobalHook(window)", "window.$RefreshReg$ = () => {}", "window.$RefreshSig$ = () => (type) => type", "window.__vite_plugin_react_preamble_installed__ = true"].join("\n");
|
|
991
|
+
}
|
|
992
|
+
}, {
|
|
993
|
+
name: "react-router-hmr-runtime",
|
|
994
|
+
enforce: "pre",
|
|
995
|
+
resolveId(id) {
|
|
996
|
+
if (id === hmrRuntimeId) return vmod.resolve(hmrRuntimeId);
|
|
997
|
+
},
|
|
998
|
+
async load(id) {
|
|
999
|
+
if (id !== vmod.resolve(hmrRuntimeId)) return;
|
|
1000
|
+
let reactRefreshDir = path__namespace.dirname(require.resolve("react-refresh/package.json"));
|
|
1001
|
+
let reactRefreshRuntimePath = path__namespace.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
|
|
1002
|
+
return ["const exports = {}", await fse__namespace.readFile(reactRefreshRuntimePath, "utf8"), await fse__namespace.readFile(require.resolve("./static/refresh-utils.cjs"), "utf8"), "export default exports"].join("\n");
|
|
1003
|
+
}
|
|
1004
|
+
}, {
|
|
1005
|
+
name: "react-router-react-refresh-babel",
|
|
1006
|
+
async transform(code, id, options) {
|
|
1007
|
+
if (viteCommand !== "serve") return;
|
|
1008
|
+
if (id.includes("/node_modules/")) return;
|
|
1009
|
+
let [filepath] = id.split("?");
|
|
1010
|
+
let extensionsRE = /\.(jsx?|tsx?|mdx?)$/;
|
|
1011
|
+
if (!extensionsRE.test(filepath)) return;
|
|
1012
|
+
let devRuntime = "react/jsx-dev-runtime";
|
|
1013
|
+
let ssr = (options === null || options === void 0 ? void 0 : options.ssr) === true;
|
|
1014
|
+
let isJSX = filepath.endsWith("x");
|
|
1015
|
+
let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
|
|
1016
|
+
if (!useFastRefresh) return;
|
|
1017
|
+
if (isClientRoute(id)) {
|
|
1018
|
+
return {
|
|
1019
|
+
code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
let result = await babel__default["default"].transformAsync(code, {
|
|
1023
|
+
babelrc: false,
|
|
1024
|
+
configFile: false,
|
|
1025
|
+
filename: id,
|
|
1026
|
+
sourceFileName: filepath,
|
|
1027
|
+
parserOpts: {
|
|
1028
|
+
sourceType: "module",
|
|
1029
|
+
allowAwaitOutsideFunction: true
|
|
1030
|
+
},
|
|
1031
|
+
plugins: [[require("react-refresh/babel"), {
|
|
1032
|
+
skipEnvCheck: true
|
|
1033
|
+
}]],
|
|
1034
|
+
sourceMaps: true
|
|
1035
|
+
});
|
|
1036
|
+
if (result === null) return;
|
|
1037
|
+
code = result.code;
|
|
1038
|
+
let refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
|
|
1039
|
+
if (refreshContentRE.test(code)) {
|
|
1040
|
+
code = addRefreshWrapper(ctx.reactRouterConfig, code, id);
|
|
1041
|
+
}
|
|
1042
|
+
return {
|
|
1043
|
+
code,
|
|
1044
|
+
map: result.map
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
}, {
|
|
1048
|
+
name: "react-router-hmr-updates",
|
|
1049
|
+
async handleHotUpdate({
|
|
1050
|
+
server,
|
|
1051
|
+
file,
|
|
1052
|
+
modules,
|
|
1053
|
+
read
|
|
1054
|
+
}) {
|
|
1055
|
+
let route = getRoute(ctx.reactRouterConfig, file);
|
|
1056
|
+
let hmrEventData = {
|
|
1057
|
+
route: null
|
|
1058
|
+
};
|
|
1059
|
+
if (route) {
|
|
1060
|
+
// invalidate manifest on route exports change
|
|
1061
|
+
let serverManifest = (await server.ssrLoadModule(serverManifestId)).default;
|
|
1062
|
+
let oldRouteMetadata = serverManifest.routes[route.id];
|
|
1063
|
+
let newRouteMetadata = await getRouteMetadata(ctx, viteChildCompiler, route, read);
|
|
1064
|
+
hmrEventData.route = newRouteMetadata;
|
|
1065
|
+
if (!oldRouteMetadata || ["hasLoader", "hasClientLoader", "hasAction", "hasClientAction", "hasErrorBoundary"].some(key => oldRouteMetadata[key] !== newRouteMetadata[key])) {
|
|
1066
|
+
invalidateVirtualModules(server);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
server.ws.send({
|
|
1070
|
+
type: "custom",
|
|
1071
|
+
event: "react-router:hmr",
|
|
1072
|
+
data: hmrEventData
|
|
1073
|
+
});
|
|
1074
|
+
return modules;
|
|
1075
|
+
}
|
|
1076
|
+
}];
|
|
1077
|
+
};
|
|
1078
|
+
function isEqualJson(v1, v2) {
|
|
1079
|
+
return JSON.stringify(v1) === JSON.stringify(v2);
|
|
1080
|
+
}
|
|
1081
|
+
function addRefreshWrapper(reactRouterConfig, code, id) {
|
|
1082
|
+
let route = getRoute(reactRouterConfig, id);
|
|
1083
|
+
let acceptExports = route || isClientRoute(id) ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
|
|
1084
|
+
return REACT_REFRESH_HEADER.replaceAll("__SOURCE__", JSON.stringify(id)) + code + REACT_REFRESH_FOOTER.replaceAll("__SOURCE__", JSON.stringify(id)).replaceAll("__ACCEPT_EXPORTS__", JSON.stringify(acceptExports)).replaceAll("__ROUTE_ID__", JSON.stringify(route === null || route === void 0 ? void 0 : route.id));
|
|
1085
|
+
}
|
|
1086
|
+
const REACT_REFRESH_HEADER = `
|
|
1087
|
+
import RefreshRuntime from "${hmrRuntimeId}";
|
|
1088
|
+
|
|
1089
|
+
const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
|
|
1090
|
+
let prevRefreshReg;
|
|
1091
|
+
let prevRefreshSig;
|
|
1092
|
+
|
|
1093
|
+
if (import.meta.hot && !inWebWorker) {
|
|
1094
|
+
if (!window.__vite_plugin_react_preamble_installed__) {
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
"React Router Vite plugin can't detect preamble. Something is wrong."
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
prevRefreshReg = window.$RefreshReg$;
|
|
1101
|
+
prevRefreshSig = window.$RefreshSig$;
|
|
1102
|
+
window.$RefreshReg$ = (type, id) => {
|
|
1103
|
+
RefreshRuntime.register(type, __SOURCE__ + " " + id)
|
|
1104
|
+
};
|
|
1105
|
+
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
1106
|
+
}`.replace(/\n+/g, "");
|
|
1107
|
+
const REACT_REFRESH_FOOTER = `
|
|
1108
|
+
if (import.meta.hot && !inWebWorker) {
|
|
1109
|
+
window.$RefreshReg$ = prevRefreshReg;
|
|
1110
|
+
window.$RefreshSig$ = prevRefreshSig;
|
|
1111
|
+
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
|
|
1112
|
+
RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
|
|
1113
|
+
import.meta.hot.accept((nextExports) => {
|
|
1114
|
+
if (!nextExports) return;
|
|
1115
|
+
__ROUTE_ID__ && window.__reactRouterRouteModuleUpdates.set(__ROUTE_ID__, nextExports);
|
|
1116
|
+
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports, __ACCEPT_EXPORTS__);
|
|
1117
|
+
if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
|
|
1118
|
+
});
|
|
1119
|
+
});
|
|
1120
|
+
}`;
|
|
1121
|
+
function getRoute(pluginConfig, file) {
|
|
1122
|
+
let vite = importViteEsmSync.importViteEsmSync();
|
|
1123
|
+
let routePath = vite.normalizePath(path__namespace.relative(pluginConfig.appDirectory, file));
|
|
1124
|
+
let route = Object.values(pluginConfig.routes).find(r => vite.normalizePath(r.file) === routePath);
|
|
1125
|
+
return route;
|
|
1126
|
+
}
|
|
1127
|
+
async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
|
|
1128
|
+
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, route.file, readRouteFile);
|
|
1129
|
+
let info = {
|
|
1130
|
+
id: route.id,
|
|
1131
|
+
parentId: route.parentId,
|
|
1132
|
+
path: route.path,
|
|
1133
|
+
index: route.index,
|
|
1134
|
+
caseSensitive: route.caseSensitive,
|
|
1135
|
+
url: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, "/" + path__namespace.relative(ctx.rootDirectory, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))),
|
|
1136
|
+
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}?import`),
|
|
1137
|
+
// Ensure the Vite dev server responds with a JS module
|
|
1138
|
+
hasAction: sourceExports.includes("action"),
|
|
1139
|
+
hasClientAction: sourceExports.includes("clientAction"),
|
|
1140
|
+
hasLoader: sourceExports.includes("loader"),
|
|
1141
|
+
hasClientLoader: sourceExports.includes("clientLoader"),
|
|
1142
|
+
hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
|
|
1143
|
+
imports: []
|
|
1144
|
+
};
|
|
1145
|
+
return info;
|
|
1146
|
+
}
|
|
1147
|
+
async function handleSpaMode(serverBuildDirectoryPath, serverBuildFile, clientBuildDirectory, viteConfig, basename) {
|
|
1148
|
+
// Create a handler and call it for the `/` path - rendering down to the
|
|
1149
|
+
// proper HydrateFallback ... or not! Maybe they have a static landing page
|
|
1150
|
+
// generated from routes/_index.tsx.
|
|
1151
|
+
let serverBuildPath = path__namespace.join(serverBuildDirectoryPath, serverBuildFile);
|
|
1152
|
+
let build = await import(url__namespace.pathToFileURL(serverBuildPath).toString());
|
|
1153
|
+
let {
|
|
1154
|
+
createRequestHandler: createHandler
|
|
1155
|
+
} = await import('@react-router/node');
|
|
1156
|
+
let handler = createHandler(build, viteConfig.mode);
|
|
1157
|
+
let response = await handler(new Request(`http://localhost${basename}`));
|
|
1158
|
+
let html = await response.text();
|
|
1159
|
+
if (response.status !== 200) {
|
|
1160
|
+
throw new Error(`SPA Mode: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while generating the \`index.html\` file.\n${html}`);
|
|
1161
|
+
}
|
|
1162
|
+
if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
|
|
1163
|
+
throw new Error("SPA Mode: Did you forget to include <Scripts/> in your `root.tsx` " + "`HydrateFallback` component? Your `index.html` file cannot hydrate " + "into a SPA without `<Scripts />`.");
|
|
1164
|
+
}
|
|
1165
|
+
// Write out the index.html file for the SPA
|
|
1166
|
+
await fse__namespace.writeFile(path__namespace.join(clientBuildDirectory, "index.html"), html);
|
|
1167
|
+
viteConfig.logger.info("SPA Mode: index.html has been written to your " + colors__default["default"].bold(path__namespace.relative(process.cwd(), clientBuildDirectory)) + " directory");
|
|
1168
|
+
// Cleanup - we no longer need the server build assets
|
|
1169
|
+
fse__namespace.removeSync(serverBuildDirectoryPath);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
exports.configRouteToBranchRoute = configRouteToBranchRoute;
|
|
1173
|
+
exports.extractPluginContext = extractPluginContext;
|
|
1174
|
+
exports.getServerBuildDirectory = getServerBuildDirectory;
|
|
1175
|
+
exports.loadPluginContext = loadPluginContext;
|
|
1176
|
+
exports.reactRouterVitePlugin = reactRouterVitePlugin;
|
|
1177
|
+
exports.resolveViteConfig = resolveViteConfig;
|
|
1178
|
+
exports.setReactRouterDevLoadContext = setReactRouterDevLoadContext;
|