@react-router/dev 0.0.0-experimental-3a25d7f8a → 0.0.0-experimental-ecd35cd60
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/bin.js +2 -0
- package/dist/cli/commands.js +4 -11
- package/dist/cli/detectPackageManager.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/run.js +1 -1
- package/dist/cli/useJavascript.js +1 -1
- package/dist/cli.js +1 -2
- package/dist/colors.js +1 -1
- package/dist/config/defaults/entry.server.node.tsx +15 -100
- package/dist/config/findConfig.js +1 -1
- package/dist/config/flatRoutes.js +1 -1
- package/dist/config/format.js +1 -1
- package/dist/config/routes.js +1 -1
- package/dist/config.js +6 -24
- package/dist/index.js +1 -1
- package/dist/invariant.js +1 -1
- package/dist/vite/babel.js +1 -1
- package/dist/vite/build.js +1 -1
- package/dist/vite/define-route.d.ts +5 -0
- package/dist/vite/define-route.js +207 -0
- package/dist/vite/dev.js +1 -1
- package/dist/vite/import-vite-esm-sync.js +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/node-adapter.js +2 -2
- package/dist/vite/plugin.js +82 -29
- package/dist/vite/profiler.js +1 -1
- package/dist/vite/remove-exports.js +1 -1
- package/dist/vite/resolve-file-url.js +1 -1
- package/dist/vite/styles.d.ts +1 -1
- package/dist/vite/styles.js +1 -1
- package/dist/vite/vmod.js +1 -1
- package/package.json +12 -18
- package/dist/config/defaults/entry.server.cloudflare.tsx +0 -55
- package/dist/config/defaults/entry.server.deno.tsx +0 -55
- package/dist/config/defaults/entry.server.spa.tsx +0 -73
package/bin.js
ADDED
package/dist/cli/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-ecd35cd60
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -94,10 +94,6 @@ let conjunctionListFormat = new Intl.ListFormat("en", {
|
|
|
94
94
|
style: "long",
|
|
95
95
|
type: "conjunction"
|
|
96
96
|
});
|
|
97
|
-
let disjunctionListFormat = new Intl.ListFormat("en", {
|
|
98
|
-
style: "long",
|
|
99
|
-
type: "disjunction"
|
|
100
|
-
});
|
|
101
97
|
async function generateEntry(entry, reactRouterRoot, flags = {}) {
|
|
102
98
|
let ctx = await plugin.loadPluginContext({
|
|
103
99
|
root: reactRouterRoot,
|
|
@@ -119,16 +115,13 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
|
|
|
119
115
|
}
|
|
120
116
|
let pkgJson = await PackageJson__default["default"].load(rootDirectory);
|
|
121
117
|
let deps = pkgJson.content.dependencies ?? {};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let serverRuntimes = ["@react-router/deno", "@react-router/cloudflare", "@react-router/node"];
|
|
125
|
-
let formattedList = disjunctionListFormat.format(serverRuntimes);
|
|
126
|
-
console.error(colors.error(`Could not determine server runtime. Please install one of the following: ${formattedList}`));
|
|
118
|
+
if (!deps["@react-router/node"]) {
|
|
119
|
+
console.error(colors.error(`No default server entry detected.`));
|
|
127
120
|
return;
|
|
128
121
|
}
|
|
129
122
|
let defaultsDirectory = path__namespace.resolve(__dirname, "..", "config", "defaults");
|
|
130
123
|
let defaultEntryClient = path__namespace.resolve(defaultsDirectory, "entry.client.tsx");
|
|
131
|
-
let defaultEntryServer = path__namespace.resolve(defaultsDirectory,
|
|
124
|
+
let defaultEntryServer = path__namespace.resolve(defaultsDirectory, `entry.server.node.tsx`);
|
|
132
125
|
let isServerEntry = entry === "entry.server";
|
|
133
126
|
let contents = isServerEntry ? await createServerEntry(rootDirectory, appDirectory, defaultEntryServer) : await createClientEntry(rootDirectory, appDirectory, defaultEntryClient);
|
|
134
127
|
let useTypeScript = flags.typescript ?? true;
|
package/dist/cli/index.js
CHANGED
package/dist/cli/run.js
CHANGED
package/dist/cli.js
CHANGED
package/dist/colors.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { PassThrough } from "node:stream";
|
|
2
2
|
|
|
3
|
-
import type { AppLoadContext, EntryContext } from "
|
|
3
|
+
import type { AppLoadContext, EntryContext } from "react-router";
|
|
4
4
|
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { ServerRouter } from "react-router";
|
|
6
|
+
import { isbot } from "isbot";
|
|
7
|
+
import type { RenderToPipeableStreamOptions } from "react-dom/server";
|
|
7
8
|
import { renderToPipeableStream } from "react-dom/server";
|
|
8
9
|
|
|
9
10
|
const ABORT_DELAY = 5_000;
|
|
@@ -12,114 +13,28 @@ export default function handleRequest(
|
|
|
12
13
|
request: Request,
|
|
13
14
|
responseStatusCode: number,
|
|
14
15
|
responseHeaders: Headers,
|
|
15
|
-
|
|
16
|
+
routerContext: EntryContext,
|
|
16
17
|
loadContext: AppLoadContext
|
|
17
|
-
) {
|
|
18
|
-
let prohibitOutOfOrderStreaming =
|
|
19
|
-
isBotRequest(request.headers.get("user-agent")) || remixContext.isSpaMode;
|
|
20
|
-
|
|
21
|
-
return prohibitOutOfOrderStreaming
|
|
22
|
-
? handleBotRequest(
|
|
23
|
-
request,
|
|
24
|
-
responseStatusCode,
|
|
25
|
-
responseHeaders,
|
|
26
|
-
remixContext
|
|
27
|
-
)
|
|
28
|
-
: handleBrowserRequest(
|
|
29
|
-
request,
|
|
30
|
-
responseStatusCode,
|
|
31
|
-
responseHeaders,
|
|
32
|
-
remixContext
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// We have some Remix apps in the wild already running with isbot@3 so we need
|
|
37
|
-
// to maintain backwards compatibility even though we want new apps to use
|
|
38
|
-
// isbot@4. That way, we can ship this as a minor Semver update to @react-router/dev.
|
|
39
|
-
function isBotRequest(userAgent: string | null) {
|
|
40
|
-
if (!userAgent) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// isbot >= 3.8.0, >4
|
|
45
|
-
if ("isbot" in isbotModule && typeof isbotModule.isbot === "function") {
|
|
46
|
-
return isbotModule.isbot(userAgent);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// isbot < 3.8.0
|
|
50
|
-
if ("default" in isbotModule && typeof isbotModule.default === "function") {
|
|
51
|
-
return isbotModule.default(userAgent);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function handleBotRequest(
|
|
58
|
-
request: Request,
|
|
59
|
-
responseStatusCode: number,
|
|
60
|
-
responseHeaders: Headers,
|
|
61
|
-
remixContext: EntryContext
|
|
62
18
|
) {
|
|
63
19
|
return new Promise((resolve, reject) => {
|
|
64
20
|
let shellRendered = false;
|
|
65
|
-
|
|
66
|
-
<RemixServer
|
|
67
|
-
context={remixContext}
|
|
68
|
-
url={request.url}
|
|
69
|
-
abortDelay={ABORT_DELAY}
|
|
70
|
-
/>,
|
|
71
|
-
{
|
|
72
|
-
onAllReady() {
|
|
73
|
-
shellRendered = true;
|
|
74
|
-
const body = new PassThrough();
|
|
75
|
-
const stream = createReadableStreamFromReadable(body);
|
|
76
|
-
|
|
77
|
-
responseHeaders.set("Content-Type", "text/html");
|
|
21
|
+
let userAgent = request.headers.get("user-agent");
|
|
78
22
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
23
|
+
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
|
|
24
|
+
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
|
|
25
|
+
let readyOption: keyof RenderToPipeableStreamOptions =
|
|
26
|
+
(userAgent && isbot(userAgent)) || routerContext.isSpaMode
|
|
27
|
+
? "onAllReady"
|
|
28
|
+
: "onShellReady";
|
|
85
29
|
|
|
86
|
-
pipe(body);
|
|
87
|
-
},
|
|
88
|
-
onShellError(error: unknown) {
|
|
89
|
-
reject(error);
|
|
90
|
-
},
|
|
91
|
-
onError(error: unknown) {
|
|
92
|
-
responseStatusCode = 500;
|
|
93
|
-
// Log streaming rendering errors from inside the shell. Don't log
|
|
94
|
-
// errors encountered during initial shell rendering since they'll
|
|
95
|
-
// reject and get logged in handleDocumentRequest.
|
|
96
|
-
if (shellRendered) {
|
|
97
|
-
console.error(error);
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
setTimeout(abort, ABORT_DELAY);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function handleBrowserRequest(
|
|
108
|
-
request: Request,
|
|
109
|
-
responseStatusCode: number,
|
|
110
|
-
responseHeaders: Headers,
|
|
111
|
-
remixContext: EntryContext
|
|
112
|
-
) {
|
|
113
|
-
return new Promise((resolve, reject) => {
|
|
114
|
-
let shellRendered = false;
|
|
115
30
|
const { pipe, abort } = renderToPipeableStream(
|
|
116
|
-
<
|
|
117
|
-
context={
|
|
31
|
+
<ServerRouter
|
|
32
|
+
context={routerContext}
|
|
118
33
|
url={request.url}
|
|
119
34
|
abortDelay={ABORT_DELAY}
|
|
120
35
|
/>,
|
|
121
36
|
{
|
|
122
|
-
|
|
37
|
+
[readyOption]() {
|
|
123
38
|
shellRendered = true;
|
|
124
39
|
const body = new PassThrough();
|
|
125
40
|
const stream = createReadableStreamFromReadable(body);
|
package/dist/config/format.js
CHANGED
package/dist/config/routes.js
CHANGED
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-ecd35cd60
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -216,8 +216,7 @@ async function resolveEntryFiles({
|
|
|
216
216
|
reactRouterConfig
|
|
217
217
|
}) {
|
|
218
218
|
let {
|
|
219
|
-
appDirectory
|
|
220
|
-
future
|
|
219
|
+
appDirectory
|
|
221
220
|
} = reactRouterConfig;
|
|
222
221
|
let defaultsDirectory = path__default["default"].resolve(__dirname, "config", "defaults");
|
|
223
222
|
let userEntryClientFile = findEntry(appDirectory, "entry.client");
|
|
@@ -226,24 +225,11 @@ async function resolveEntryFiles({
|
|
|
226
225
|
let entryClientFile = userEntryClientFile || "entry.client.tsx";
|
|
227
226
|
let pkgJson = await PackageJson__default["default"].load(rootDirectory);
|
|
228
227
|
let deps = pkgJson.content.dependencies ?? {};
|
|
229
|
-
if (
|
|
230
|
-
// This is a super-simple default since we don't need streaming in SPA Mode.
|
|
231
|
-
// We can include this in a remix-spa template, but right now `npx remix reveal`
|
|
232
|
-
// will still expose the streaming template since that command doesn't have
|
|
233
|
-
// access to the `ssr:false` flag in the vite config (the streaming template
|
|
234
|
-
// works just fine so maybe instead of having this we _only have this version
|
|
235
|
-
// in the template...). We let users manage an entry.server file in SPA Mode
|
|
236
|
-
// so they can de ide if they want to hydrate the full document or just an
|
|
237
|
-
// embedded `<div id="app">` or whatever.
|
|
238
|
-
entryServerFile = "entry.server.spa.tsx";
|
|
239
|
-
} else if (userEntryServerFile) {
|
|
228
|
+
if (userEntryServerFile) {
|
|
240
229
|
entryServerFile = userEntryServerFile;
|
|
241
230
|
} else {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
let serverRuntimes = ["@react-router/deno", "@react-router/cloudflare", "@react-router/node"];
|
|
245
|
-
let formattedList = disjunctionListFormat.format(serverRuntimes);
|
|
246
|
-
throw new Error(`Could not determine server runtime. Please install one of the following: ${formattedList}`);
|
|
231
|
+
if (!deps["@react-router/node"]) {
|
|
232
|
+
throw new Error(`Could not determine server runtime. Please install @react-router/node, or provide a custom entry.server.tsx/jsx file in your app directory.`);
|
|
247
233
|
}
|
|
248
234
|
if (!deps["isbot"]) {
|
|
249
235
|
console.log("adding `isbot` to your package.json, you should commit this change");
|
|
@@ -260,7 +246,7 @@ async function resolveEntryFiles({
|
|
|
260
246
|
stdio: "inherit"
|
|
261
247
|
});
|
|
262
248
|
}
|
|
263
|
-
entryServerFile = `entry.server
|
|
249
|
+
entryServerFile = `entry.server.node.tsx`;
|
|
264
250
|
}
|
|
265
251
|
let entryClientFilePath = userEntryClientFile ? path__default["default"].resolve(reactRouterConfig.appDirectory, userEntryClientFile) : path__default["default"].resolve(defaultsDirectory, entryClientFile);
|
|
266
252
|
let entryServerFilePath = userEntryServerFile ? path__default["default"].resolve(reactRouterConfig.appDirectory, userEntryServerFile) : path__default["default"].resolve(defaultsDirectory, entryServerFile);
|
|
@@ -277,10 +263,6 @@ function findEntry(dir, basename) {
|
|
|
277
263
|
}
|
|
278
264
|
return undefined;
|
|
279
265
|
}
|
|
280
|
-
let disjunctionListFormat = new Intl.ListFormat("en", {
|
|
281
|
-
style: "long",
|
|
282
|
-
type: "disjunction"
|
|
283
|
-
});
|
|
284
266
|
|
|
285
267
|
exports.configRouteToBranchRoute = configRouteToBranchRoute;
|
|
286
268
|
exports.resolveEntryFiles = resolveEntryFiles;
|
package/dist/index.js
CHANGED
package/dist/invariant.js
CHANGED
package/dist/vite/babel.js
CHANGED
package/dist/vite/build.js
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function transform(code: string, id: string, options?: {
|
|
2
|
+
ssr?: boolean;
|
|
3
|
+
}): string | import("@babel/generator").GeneratorResult;
|
|
4
|
+
export declare function parseFields(code: string): string[];
|
|
5
|
+
export declare function assertNotImported(code: string): void;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-ecd35cd60
|
|
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 babel$1 = require('@babel/core');
|
|
16
|
+
var babelDeadCodeElimination = require('babel-dead-code-elimination');
|
|
17
|
+
var babel = require('./babel.js');
|
|
18
|
+
var t = require('@babel/types');
|
|
19
|
+
var parser = require('@babel/parser');
|
|
20
|
+
|
|
21
|
+
function _interopNamespace(e) {
|
|
22
|
+
if (e && e.__esModule) return e;
|
|
23
|
+
var n = Object.create(null);
|
|
24
|
+
if (e) {
|
|
25
|
+
Object.keys(e).forEach(function (k) {
|
|
26
|
+
if (k !== 'default') {
|
|
27
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
28
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get: function () { return e[k]; }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
n["default"] = e;
|
|
36
|
+
return Object.freeze(n);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var babel__namespace = /*#__PURE__*/_interopNamespace(babel$1);
|
|
40
|
+
var t__namespace = /*#__PURE__*/_interopNamespace(t);
|
|
41
|
+
|
|
42
|
+
function transform(code, id, options = {}) {
|
|
43
|
+
if (options !== null && options !== void 0 && options.ssr) return code;
|
|
44
|
+
let ast = parse(code);
|
|
45
|
+
let refs = babelDeadCodeElimination.findReferencedIdentifiers(ast);
|
|
46
|
+
let markedForRemoval = [];
|
|
47
|
+
assertDefineRouteOnlyAfterExportDefault(ast);
|
|
48
|
+
babel.traverse(ast, {
|
|
49
|
+
ExportDefaultDeclaration(path) {
|
|
50
|
+
let analysis = analyzeRouteExport(path);
|
|
51
|
+
for (let [key, fieldPath] of Object.entries(analysis)) {
|
|
52
|
+
if (["headers", "serverLoader", "serverAction"].includes(key)) {
|
|
53
|
+
if (!fieldPath) continue;
|
|
54
|
+
markedForRemoval.push(fieldPath);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
markedForRemoval.forEach(path => path.remove());
|
|
60
|
+
babelDeadCodeElimination.deadCodeElimination(ast, refs);
|
|
61
|
+
return babel.generate(ast, {
|
|
62
|
+
sourceMaps: true,
|
|
63
|
+
sourceFileName: id
|
|
64
|
+
}, code);
|
|
65
|
+
}
|
|
66
|
+
function parseFields(code) {
|
|
67
|
+
let fields = [];
|
|
68
|
+
let ast = parse(code);
|
|
69
|
+
assertDefineRouteOnlyAfterExportDefault(ast);
|
|
70
|
+
babel.traverse(ast, {
|
|
71
|
+
ExportDefaultDeclaration(path) {
|
|
72
|
+
let analysis = analyzeRouteExport(path);
|
|
73
|
+
fields = Object.keys(analysis);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return fields;
|
|
77
|
+
}
|
|
78
|
+
function analyzeRouteExport(path) {
|
|
79
|
+
let route = path.node.declaration;
|
|
80
|
+
// export default {...}
|
|
81
|
+
if (t__namespace.isObjectExpression(route)) {
|
|
82
|
+
let routePath = path.get("declaration");
|
|
83
|
+
return analyzeRoute(routePath);
|
|
84
|
+
}
|
|
85
|
+
// export default defineRoute({...})
|
|
86
|
+
if (t__namespace.isCallExpression(route)) {
|
|
87
|
+
let routePath = path.get("declaration");
|
|
88
|
+
if (!isDefineRoute(routePath.get("callee"))) {
|
|
89
|
+
throw routePath.buildCodeFrameError("Default export of a route module must be either a literal object or a call to `defineRoute`");
|
|
90
|
+
}
|
|
91
|
+
if (routePath.node.arguments.length !== 1) {
|
|
92
|
+
throw routePath.buildCodeFrameError("`defineRoute` must take exactly one argument");
|
|
93
|
+
}
|
|
94
|
+
let arg = routePath.node.arguments[0];
|
|
95
|
+
let argPath = routePath.get("arguments.0");
|
|
96
|
+
if (!t__namespace.isObjectExpression(arg)) {
|
|
97
|
+
throw argPath.buildCodeFrameError("`defineRoute` argument must be a literal object");
|
|
98
|
+
}
|
|
99
|
+
return analyzeRoute(argPath);
|
|
100
|
+
}
|
|
101
|
+
throw path.get("declaration").buildCodeFrameError("Default export of a route module must be either a literal object or a call to `defineRoute`");
|
|
102
|
+
}
|
|
103
|
+
function analyzeRoute(path) {
|
|
104
|
+
let analysis = {};
|
|
105
|
+
for (let [i, property] of path.node.properties.entries()) {
|
|
106
|
+
// spread: defineRoute({ ...dynamic })
|
|
107
|
+
if (!t__namespace.isObjectProperty(property) && !t__namespace.isObjectMethod(property)) {
|
|
108
|
+
let propertyPath = path.get(`properties.${i}`);
|
|
109
|
+
throw propertyPath.buildCodeFrameError("Properties cannot be spread into route");
|
|
110
|
+
}
|
|
111
|
+
// defineRoute({ [dynamic]: ... })
|
|
112
|
+
let propertyPath = path.get(`properties.${i}`);
|
|
113
|
+
if (property.computed || !t__namespace.isIdentifier(property.key)) {
|
|
114
|
+
throw propertyPath.buildCodeFrameError("Route cannot have computed keys");
|
|
115
|
+
}
|
|
116
|
+
// defineRoute({ params: [...] })
|
|
117
|
+
let key = property.key.name;
|
|
118
|
+
if (key === "params") {
|
|
119
|
+
let paramsPath = propertyPath;
|
|
120
|
+
if (t__namespace.isObjectMethod(paramsPath.node)) {
|
|
121
|
+
throw paramsPath.buildCodeFrameError("Route params must be a literal array");
|
|
122
|
+
}
|
|
123
|
+
if (!t__namespace.isArrayExpression(paramsPath.node.value)) {
|
|
124
|
+
throw paramsPath.buildCodeFrameError("Route params must be a literal array");
|
|
125
|
+
}
|
|
126
|
+
for (let [i, element] of paramsPath.node.value.elements.entries()) {
|
|
127
|
+
if (!t__namespace.isStringLiteral(element)) {
|
|
128
|
+
let elementPath = paramsPath.get(`value.elements.${i}`);
|
|
129
|
+
throw elementPath.buildCodeFrameError("Route param must be a literal string");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
analysis[key] = propertyPath;
|
|
134
|
+
}
|
|
135
|
+
return analysis;
|
|
136
|
+
}
|
|
137
|
+
function assertNotImported(code) {
|
|
138
|
+
let ast = parse(code);
|
|
139
|
+
babel.traverse(ast, {
|
|
140
|
+
Identifier(path) {
|
|
141
|
+
if (isDefineRoute(path)) {
|
|
142
|
+
throw path.buildCodeFrameError("`defineRoute` cannot be used outside of route modules");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function parse(source) {
|
|
148
|
+
let ast = parser.parse(source, {
|
|
149
|
+
sourceType: "module",
|
|
150
|
+
plugins: ["jsx", ["typescript", {}]]
|
|
151
|
+
});
|
|
152
|
+
// Workaround for `path.buildCodeFrameError`
|
|
153
|
+
// See:
|
|
154
|
+
// - https://github.com/babel/babel/issues/11889
|
|
155
|
+
// - https://github.com/babel/babel/issues/11350#issuecomment-606169054
|
|
156
|
+
// @ts-expect-error `@types/babel__core` is missing types for `File`
|
|
157
|
+
new babel__namespace.File({
|
|
158
|
+
filename: undefined
|
|
159
|
+
}, {
|
|
160
|
+
code: source,
|
|
161
|
+
ast
|
|
162
|
+
});
|
|
163
|
+
return ast;
|
|
164
|
+
}
|
|
165
|
+
function assertDefineRouteOnlyAfterExportDefault(ast) {
|
|
166
|
+
babel.traverse(ast, {
|
|
167
|
+
Identifier(path) {
|
|
168
|
+
if (!isDefineRoute(path)) return;
|
|
169
|
+
if (t__namespace.isImportSpecifier(path.parent)) return;
|
|
170
|
+
if (!t__namespace.isCallExpression(path.parent)) {
|
|
171
|
+
throw path.buildCodeFrameError("`defineRoute` must be a function call immediately after `export default`");
|
|
172
|
+
}
|
|
173
|
+
if (!t__namespace.isExportDefaultDeclaration(path.parentPath.parent)) {
|
|
174
|
+
throw path.buildCodeFrameError("`defineRoute` must be a function call immediately after `export default`");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function isDefineRoute(path) {
|
|
180
|
+
if (!t__namespace.isIdentifier(path.node)) return false;
|
|
181
|
+
let binding = path.scope.getBinding(path.node.name);
|
|
182
|
+
if (!binding) return false;
|
|
183
|
+
return isCanonicallyImportedAs(binding, {
|
|
184
|
+
imported: "defineRoute",
|
|
185
|
+
source: "react-router"
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function isCanonicallyImportedAs(binding, {
|
|
189
|
+
source: sourceName,
|
|
190
|
+
imported: importedName
|
|
191
|
+
}) {
|
|
192
|
+
// import source
|
|
193
|
+
if (!t__namespace.isImportDeclaration(binding === null || binding === void 0 ? void 0 : binding.path.parent)) return false;
|
|
194
|
+
if (binding.path.parent.source.value !== sourceName) return false;
|
|
195
|
+
// import specifier
|
|
196
|
+
if (!t__namespace.isImportSpecifier(binding === null || binding === void 0 ? void 0 : binding.path.node)) return false;
|
|
197
|
+
let {
|
|
198
|
+
imported
|
|
199
|
+
} = binding.path.node;
|
|
200
|
+
if (!t__namespace.isIdentifier(imported)) return false;
|
|
201
|
+
if (imported.name !== importedName) return false;
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
exports.assertNotImported = assertNotImported;
|
|
206
|
+
exports.parseFields = parseFields;
|
|
207
|
+
exports.transform = transform;
|
package/dist/vite/dev.js
CHANGED
package/dist/vite/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-ecd35cd60
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -33,7 +33,7 @@ function fromNodeHeaders(nodeHeaders) {
|
|
|
33
33
|
}
|
|
34
34
|
return headers;
|
|
35
35
|
}
|
|
36
|
-
// Based on `createRemixRequest` in packages/
|
|
36
|
+
// Based on `createRemixRequest` in packages/react-router-express/server.ts
|
|
37
37
|
function fromNodeRequest(nodeReq) {
|
|
38
38
|
let origin = nodeReq.headers.origin && "null" !== nodeReq.headers.origin ? nodeReq.headers.origin : `http://${nodeReq.headers.host}`;
|
|
39
39
|
// Use `req.originalUrl` so React Router is aware of the full path
|
package/dist/vite/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-ecd35cd60
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -17,7 +17,6 @@ var path = require('node:path');
|
|
|
17
17
|
var url = require('node:url');
|
|
18
18
|
var fse = require('fs-extra');
|
|
19
19
|
var babel = require('@babel/core');
|
|
20
|
-
var serverRuntime = require('@react-router/server-runtime');
|
|
21
20
|
var reactRouter = require('react-router');
|
|
22
21
|
var esModuleLexer = require('es-module-lexer');
|
|
23
22
|
var jsesc = require('jsesc');
|
|
@@ -31,6 +30,7 @@ var resolveFileUrl = require('./resolve-file-url.js');
|
|
|
31
30
|
var removeExports = require('./remove-exports.js');
|
|
32
31
|
var importViteEsmSync = require('./import-vite-esm-sync.js');
|
|
33
32
|
var config = require('../config.js');
|
|
33
|
+
var defineRoute = require('./define-route.js');
|
|
34
34
|
|
|
35
35
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
36
36
|
|
|
@@ -108,11 +108,11 @@ async function loadPluginContext({
|
|
|
108
108
|
}
|
|
109
109
|
const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
|
|
110
110
|
const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
// Each route gets its own virtual module marked with an entry query string
|
|
112
|
+
const ROUTE_ENTRY_QUERY_STRING = "?route-entry=1";
|
|
113
|
+
const isRouteEntry = id => {
|
|
114
|
+
return id.endsWith(ROUTE_ENTRY_QUERY_STRING);
|
|
115
|
+
};
|
|
116
116
|
let serverBuildId = vmod.id("server-build");
|
|
117
117
|
let serverManifestId = vmod.id("server-manifest");
|
|
118
118
|
let browserManifestId = vmod.id("browser-manifest");
|
|
@@ -140,7 +140,7 @@ const getHash = (source, maxLength) => {
|
|
|
140
140
|
const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
|
|
141
141
|
let vite = importViteEsmSync.importViteEsmSync();
|
|
142
142
|
let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
|
|
143
|
-
let entryChunk = viteManifest[rootRelativeFilePath +
|
|
143
|
+
let entryChunk = viteManifest[rootRelativeFilePath + ROUTE_ENTRY_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
|
|
144
144
|
if (!entryChunk) {
|
|
145
145
|
let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
|
|
146
146
|
throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
|
|
@@ -195,6 +195,20 @@ const getRouteManifestModuleExports = async (viteChildCompiler, ctx) => {
|
|
|
195
195
|
return Object.fromEntries(entries);
|
|
196
196
|
};
|
|
197
197
|
const getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
|
|
198
|
+
let routePath = path__namespace.resolve(ctx.reactRouterConfig.appDirectory, routeFile);
|
|
199
|
+
let code = await ((readRouteFile === null || readRouteFile === void 0 ? void 0 : readRouteFile()) ?? fse__namespace.readFile(routePath, "utf-8"));
|
|
200
|
+
if (!code.includes("defineRoute")) {
|
|
201
|
+
return _getRouteModuleExports(viteChildCompiler, ctx, routeFile, readRouteFile);
|
|
202
|
+
}
|
|
203
|
+
let renameFields = ["Component", "serverLoader", "actionLoader"];
|
|
204
|
+
let fields = defineRoute.parseFields(code);
|
|
205
|
+
let exports = fields.filter(exportName => !renameFields.includes(exportName));
|
|
206
|
+
if (fields.includes("Component")) exports.push("default");
|
|
207
|
+
if (fields.includes("serverLoader")) exports.push("loader");
|
|
208
|
+
if (fields.includes("serverAction")) exports.push("action");
|
|
209
|
+
return exports;
|
|
210
|
+
};
|
|
211
|
+
const _getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
|
|
198
212
|
if (!viteChildCompiler) {
|
|
199
213
|
throw new Error("Vite child compiler not found");
|
|
200
214
|
}
|
|
@@ -320,7 +334,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
320
334
|
import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryServerFilePath))};
|
|
321
335
|
${Object.keys(routes).map((key, index) => {
|
|
322
336
|
let route = routes[key];
|
|
323
|
-
return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)))};`;
|
|
337
|
+
return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)) + ROUTE_ENTRY_QUERY_STRING)};`;
|
|
324
338
|
}).join("\n")}
|
|
325
339
|
export { default as assets } from ${JSON.stringify(serverManifestId)};
|
|
326
340
|
export const assetsBuildDirectory = ${JSON.stringify(path__namespace.relative(ctx.rootDirectory, getClientBuildDirectory(ctx.reactRouterConfig)))};
|
|
@@ -434,7 +448,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
434
448
|
path: route.path,
|
|
435
449
|
index: route.index,
|
|
436
450
|
caseSensitive: route.caseSensitive,
|
|
437
|
-
module: path__namespace.posix.join(ctx.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`),
|
|
451
|
+
module: path__namespace.posix.join(ctx.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}${ROUTE_ENTRY_QUERY_STRING}`),
|
|
438
452
|
hasAction: sourceExports.includes("action"),
|
|
439
453
|
hasLoader: sourceExports.includes("loader"),
|
|
440
454
|
hasClientAction: sourceExports.includes("clientAction"),
|
|
@@ -459,7 +473,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
459
473
|
return [{
|
|
460
474
|
name: "react-router",
|
|
461
475
|
config: async (_viteUserConfig, _viteConfigEnv) => {
|
|
462
|
-
var _viteUserConfig$serve, _viteUserConfig$serve2, _viteUserConfig$build4;
|
|
476
|
+
var _viteUserConfig$serve, _viteUserConfig$serve2, _viteUserConfig$build4, _viteUserConfig$build5, _viteUserConfig$build6;
|
|
463
477
|
// Preload Vite's ESM build up-front as soon as we're in an async context
|
|
464
478
|
await importViteEsmSync.preloadViteEsm();
|
|
465
479
|
// Ensure sync import of Vite works after async preload
|
|
@@ -496,7 +510,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
496
510
|
// This is only needed within this repo because these packages
|
|
497
511
|
// are linked to a directory outside of node_modules so Vite
|
|
498
512
|
// treats them as internal code by default.
|
|
499
|
-
"react-router", "react-router-dom", "@react-router/
|
|
513
|
+
"react-router", "react-router-dom", "@react-router/dev", "@react-router/express", "@react-router/node", "@react-router/serve"] : undefined
|
|
500
514
|
},
|
|
501
515
|
optimizeDeps: {
|
|
502
516
|
include: [
|
|
@@ -540,7 +554,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
540
554
|
rollupOptions: {
|
|
541
555
|
...baseRollupOptions,
|
|
542
556
|
preserveEntrySignatures: "exports-only",
|
|
543
|
-
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${
|
|
557
|
+
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${ROUTE_ENTRY_QUERY_STRING}`)]
|
|
544
558
|
}
|
|
545
559
|
} : {
|
|
546
560
|
// We move SSR-only assets to client assets. Note that the
|
|
@@ -557,7 +571,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
557
571
|
rollupOptions: {
|
|
558
572
|
...baseRollupOptions,
|
|
559
573
|
preserveEntrySignatures: "exports-only",
|
|
560
|
-
input: serverBuildId,
|
|
574
|
+
input: ((_viteUserConfig$build5 = viteUserConfig.build) === null || _viteUserConfig$build5 === void 0 ? void 0 : (_viteUserConfig$build6 = _viteUserConfig$build5.rollupOptions) === null || _viteUserConfig$build6 === void 0 ? void 0 : _viteUserConfig$build6.input) ?? serverBuildId,
|
|
561
575
|
output: {
|
|
562
576
|
entryFileNames: ctx.reactRouterConfig.serverBuildFile,
|
|
563
577
|
format: ctx.reactRouterConfig.serverModuleFormat
|
|
@@ -637,13 +651,6 @@ const reactRouterVitePlugin = _config => {
|
|
|
637
651
|
if (styles.isCssModulesFile(id)) {
|
|
638
652
|
cssModulesManifest[id] = code;
|
|
639
653
|
}
|
|
640
|
-
if (id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING)) {
|
|
641
|
-
let routeModuleId = id.replace(BUILD_CLIENT_ROUTE_QUERY_STRING, "");
|
|
642
|
-
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
|
|
643
|
-
let routeFileName = path__namespace.basename(routeModuleId);
|
|
644
|
-
let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
|
|
645
|
-
return `export { ${clientExports} } from "./${routeFileName}";`;
|
|
646
|
-
}
|
|
647
654
|
},
|
|
648
655
|
buildStart() {
|
|
649
656
|
invariant["default"](viteConfig);
|
|
@@ -652,7 +659,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
652
659
|
}
|
|
653
660
|
},
|
|
654
661
|
async configureServer(viteDevServer) {
|
|
655
|
-
|
|
662
|
+
reactRouter.unstable_setDevServerHooks({
|
|
656
663
|
// Give the request handler access to the critical CSS in dev to avoid a
|
|
657
664
|
// flash of unstyled content since Vite injects CSS file contents via JS
|
|
658
665
|
getCriticalCss: async (build, url) => {
|
|
@@ -698,7 +705,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
698
705
|
viteDevServer.middlewares.use(async (req, res, next) => {
|
|
699
706
|
try {
|
|
700
707
|
let build = await viteDevServer.ssrLoadModule(serverBuildId);
|
|
701
|
-
let handler =
|
|
708
|
+
let handler = reactRouter.createRequestHandler(build, "development");
|
|
702
709
|
let nodeHandler = async (nodeReq, nodeRes) => {
|
|
703
710
|
let req = nodeAdapter.fromNodeRequest(nodeReq);
|
|
704
711
|
let res = await handler(req, await reactRouterDevLoadContext(req));
|
|
@@ -770,6 +777,34 @@ const reactRouterVitePlugin = _config => {
|
|
|
770
777
|
var _viteChildCompiler;
|
|
771
778
|
await ((_viteChildCompiler = viteChildCompiler) === null || _viteChildCompiler === void 0 ? void 0 : _viteChildCompiler.close());
|
|
772
779
|
}
|
|
780
|
+
}, {
|
|
781
|
+
name: "react-router-route-entry",
|
|
782
|
+
enforce: "pre",
|
|
783
|
+
async transform(code, id, options) {
|
|
784
|
+
if (!isRouteEntry(id)) return;
|
|
785
|
+
let routeModuleId = id.replace(ROUTE_ENTRY_QUERY_STRING, "");
|
|
786
|
+
let routeFileName = path__namespace.basename(routeModuleId);
|
|
787
|
+
if (!code.includes("defineRoute")) {
|
|
788
|
+
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
|
|
789
|
+
let reexports = sourceExports.filter(exportName => (options === null || options === void 0 ? void 0 : options.ssr) && SERVER_ONLY_ROUTE_EXPORTS.includes(exportName) || CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
|
|
790
|
+
return `export { ${reexports} } from "./${routeFileName}";`;
|
|
791
|
+
}
|
|
792
|
+
let renameFields = ["Component", "serverLoader", "serverAction"];
|
|
793
|
+
let fields = defineRoute.parseFields(code);
|
|
794
|
+
let reexports = fields.filter(exportName => !renameFields.includes(exportName)).filter(exportName => (options === null || options === void 0 ? void 0 : options.ssr) && SERVER_ONLY_ROUTE_EXPORTS.includes(exportName) || CLIENT_ROUTE_EXPORTS.includes(exportName)).map(reexport => `export const ${reexport} = route.${reexport};`);
|
|
795
|
+
let content = `import route from "./${routeFileName}";`;
|
|
796
|
+
if (fields.includes("Component")) {
|
|
797
|
+
content += `\n` + [`import { createElement } from "react";`, `import { useParams, useLoaderData, useActionData } from "react-router";`, `export default function Route() {`, ` let params = useParams();`, ` let loaderData = useLoaderData();`, ` let actionData = useActionData();`, ` return createElement(route.Component, { params, loaderData, actionData });`, `}`].join("\n");
|
|
798
|
+
}
|
|
799
|
+
if (options !== null && options !== void 0 && options.ssr && fields.includes("serverLoader")) {
|
|
800
|
+
content += `\nexport const loader = route.serverLoader;`;
|
|
801
|
+
}
|
|
802
|
+
if (options !== null && options !== void 0 && options.ssr && fields.includes("serverAction")) {
|
|
803
|
+
content += `\nexport const action = route.serverAction;`;
|
|
804
|
+
}
|
|
805
|
+
content += "\n" + reexports.join("\n");
|
|
806
|
+
return content;
|
|
807
|
+
}
|
|
773
808
|
}, {
|
|
774
809
|
name: "react-router-virtual-modules",
|
|
775
810
|
enforce: "pre",
|
|
@@ -802,6 +837,19 @@ const reactRouterVitePlugin = _config => {
|
|
|
802
837
|
}
|
|
803
838
|
}
|
|
804
839
|
}
|
|
840
|
+
}, {
|
|
841
|
+
name: "react-router-define-route",
|
|
842
|
+
enforce: "pre",
|
|
843
|
+
async transform(code, id, options) {
|
|
844
|
+
if (options !== null && options !== void 0 && options.ssr) return;
|
|
845
|
+
if (!code.includes("defineRoute")) return; // temporary back compat, remove once old style routes are unsupported
|
|
846
|
+
if (id.endsWith(ROUTE_ENTRY_QUERY_STRING)) return;
|
|
847
|
+
let route = getRoute(ctx.reactRouterConfig, id);
|
|
848
|
+
if (!route && code.includes("defineRoute")) {
|
|
849
|
+
return defineRoute.assertNotImported(code);
|
|
850
|
+
}
|
|
851
|
+
return defineRoute.transform(code, id, options);
|
|
852
|
+
}
|
|
805
853
|
}, {
|
|
806
854
|
name: "react-router-dot-server",
|
|
807
855
|
enforce: "pre",
|
|
@@ -915,6 +963,11 @@ const reactRouterVitePlugin = _config => {
|
|
|
915
963
|
let isJSX = filepath.endsWith("x");
|
|
916
964
|
let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
|
|
917
965
|
if (!useFastRefresh) return;
|
|
966
|
+
if (isRouteEntry(id)) {
|
|
967
|
+
return {
|
|
968
|
+
code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
|
|
969
|
+
};
|
|
970
|
+
}
|
|
918
971
|
let result = await babel__default["default"].transformAsync(code, {
|
|
919
972
|
babelrc: false,
|
|
920
973
|
configFile: false,
|
|
@@ -972,9 +1025,9 @@ const reactRouterVitePlugin = _config => {
|
|
|
972
1025
|
}];
|
|
973
1026
|
};
|
|
974
1027
|
function isInReactRouterMonorepo() {
|
|
975
|
-
// We use '@react-router/
|
|
1028
|
+
// We use '@react-router/node' for this check since it's a
|
|
976
1029
|
// dependency of this package and guaranteed to be in node_modules
|
|
977
|
-
let serverRuntimePath = path__namespace.dirname(require.resolve("@react-router/
|
|
1030
|
+
let serverRuntimePath = path__namespace.dirname(require.resolve("@react-router/node/package.json"));
|
|
978
1031
|
let serverRuntimeParentDir = path__namespace.basename(path__namespace.resolve(serverRuntimePath, ".."));
|
|
979
1032
|
return serverRuntimeParentDir === "packages";
|
|
980
1033
|
}
|
|
@@ -983,7 +1036,7 @@ function isEqualJson(v1, v2) {
|
|
|
983
1036
|
}
|
|
984
1037
|
function addRefreshWrapper(reactRouterConfig, code, id) {
|
|
985
1038
|
let route = getRoute(reactRouterConfig, id);
|
|
986
|
-
let acceptExports = route ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
|
|
1039
|
+
let acceptExports = route || isRouteEntry(id) ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
|
|
987
1040
|
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));
|
|
988
1041
|
}
|
|
989
1042
|
const REACT_REFRESH_HEADER = `
|
|
@@ -1052,7 +1105,7 @@ async function getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, server
|
|
|
1052
1105
|
let build = await import(url__namespace.pathToFileURL(serverBuildPath).toString());
|
|
1053
1106
|
let {
|
|
1054
1107
|
createRequestHandler: createHandler
|
|
1055
|
-
} = await import('
|
|
1108
|
+
} = await import('react-router');
|
|
1056
1109
|
return {
|
|
1057
1110
|
build,
|
|
1058
1111
|
handler: createHandler(build, viteConfig.mode)
|
|
@@ -1133,7 +1186,7 @@ function validatePrerenderedHtml(html, prefix) {
|
|
|
1133
1186
|
throw new Error(`${prefix}: Did you forget to include <Scripts/> in your root route? ` + "Your pre-rendered HTML files cannot hydrate without `<Scripts />`.");
|
|
1134
1187
|
}
|
|
1135
1188
|
}
|
|
1136
|
-
// Note: Duplicated from
|
|
1189
|
+
// Note: Duplicated from react-router/lib/server-runtime
|
|
1137
1190
|
function groupRoutesByParentId(manifest) {
|
|
1138
1191
|
let routes = {};
|
|
1139
1192
|
Object.values(manifest).forEach(route => {
|
|
@@ -1145,7 +1198,7 @@ function groupRoutesByParentId(manifest) {
|
|
|
1145
1198
|
});
|
|
1146
1199
|
return routes;
|
|
1147
1200
|
}
|
|
1148
|
-
// Note: Duplicated from
|
|
1201
|
+
// Note: Duplicated from react-router/lib/server-runtime
|
|
1149
1202
|
function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
|
|
1150
1203
|
return (routesByParentId[parentId] || []).map(route => {
|
|
1151
1204
|
let commonRoute = {
|
package/dist/vite/profiler.js
CHANGED
package/dist/vite/styles.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ServerBuild } from "
|
|
1
|
+
import type { ServerBuild } from "react-router";
|
|
2
2
|
import type { ViteDevServer } from "vite";
|
|
3
3
|
import type { ResolvedVitePluginConfig } from "../config";
|
|
4
4
|
export declare const isCssModulesFile: (file: string) => boolean;
|
package/dist/vite/styles.js
CHANGED
package/dist/vite/vmod.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-router/dev",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-ecd35cd60",
|
|
4
4
|
"description": "Dev tools and CLI for React Router",
|
|
5
5
|
"homepage": "https://reactrouter.com",
|
|
6
6
|
"bugs": {
|
|
@@ -9,20 +9,13 @@
|
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "https://github.com/remix-run/react-router",
|
|
12
|
-
"directory": "packages/
|
|
12
|
+
"directory": "packages/react-router-dev"
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"main": "dist/index.js",
|
|
16
16
|
"typings": "dist/index.d.ts",
|
|
17
|
-
"exports": {
|
|
18
|
-
".": {
|
|
19
|
-
"types": "./dist/index.d.ts",
|
|
20
|
-
"default": "./dist/index.js"
|
|
21
|
-
},
|
|
22
|
-
"./package.json": "./package.json"
|
|
23
|
-
},
|
|
24
17
|
"bin": {
|
|
25
|
-
"react-router": "
|
|
18
|
+
"react-router": "bin.js"
|
|
26
19
|
},
|
|
27
20
|
"dependencies": {
|
|
28
21
|
"@babel/core": "^7.21.8",
|
|
@@ -35,6 +28,7 @@
|
|
|
35
28
|
"@babel/types": "^7.22.5",
|
|
36
29
|
"@npmcli/package-json": "^4.0.1",
|
|
37
30
|
"arg": "^5.0.1",
|
|
31
|
+
"babel-dead-code-elimination": "^1.0.6",
|
|
38
32
|
"chalk": "^4.1.2",
|
|
39
33
|
"es-module-lexer": "^1.3.1",
|
|
40
34
|
"exit-hook": "2.2.1",
|
|
@@ -49,9 +43,7 @@
|
|
|
49
43
|
"react-refresh": "^0.14.0",
|
|
50
44
|
"semver": "^7.3.7",
|
|
51
45
|
"set-cookie-parser": "^2.6.0",
|
|
52
|
-
"@react-router/node": "0.0.0-experimental-
|
|
53
|
-
"@react-router/server-runtime": "0.0.0-experimental-3a25d7f8a",
|
|
54
|
-
"react-router": "0.0.0-experimental-3a25d7f8a"
|
|
46
|
+
"@react-router/node": "0.0.0-experimental-ecd35cd60"
|
|
55
47
|
},
|
|
56
48
|
"devDependencies": {
|
|
57
49
|
"@types/babel__core": "^7.20.5",
|
|
@@ -68,20 +60,21 @@
|
|
|
68
60
|
"@types/prettier": "^2.7.3",
|
|
69
61
|
"@types/set-cookie-parser": "^2.4.1",
|
|
70
62
|
"dotenv": "^16.0.0",
|
|
71
|
-
"execa": "5.1.1",
|
|
72
|
-
"express": "^4.17.1",
|
|
73
63
|
"esbuild-register": "^3.3.2",
|
|
64
|
+
"execa": "5.1.1",
|
|
65
|
+
"express": "^4.19.2",
|
|
74
66
|
"fast-glob": "3.2.11",
|
|
75
67
|
"strip-ansi": "^6.0.1",
|
|
76
68
|
"tiny-invariant": "^1.2.0",
|
|
77
69
|
"vite": "^5.1.0",
|
|
78
|
-
"
|
|
70
|
+
"react-router": "^0.0.0-experimental-ecd35cd60",
|
|
71
|
+
"@react-router/serve": "0.0.0-experimental-ecd35cd60"
|
|
79
72
|
},
|
|
80
73
|
"peerDependencies": {
|
|
81
74
|
"typescript": "^5.1.0",
|
|
82
75
|
"vite": "^5.1.0",
|
|
83
|
-
"react-router": "^0.0.0-experimental-
|
|
84
|
-
"
|
|
76
|
+
"@react-router/serve": "^0.0.0-experimental-ecd35cd60",
|
|
77
|
+
"react-router": "^0.0.0-experimental-ecd35cd60"
|
|
85
78
|
},
|
|
86
79
|
"peerDependenciesMeta": {
|
|
87
80
|
"@react-router/serve": {
|
|
@@ -96,6 +89,7 @@
|
|
|
96
89
|
},
|
|
97
90
|
"files": [
|
|
98
91
|
"dist/",
|
|
92
|
+
"bin.js",
|
|
99
93
|
"CHANGELOG.md",
|
|
100
94
|
"LICENSE.md",
|
|
101
95
|
"README.md"
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { AppLoadContext, EntryContext } from "@react-router/cloudflare";
|
|
2
|
-
import { RemixServer } from "react-router";
|
|
3
|
-
import * as isbotModule from "isbot";
|
|
4
|
-
import { renderToReadableStream } from "react-dom/server";
|
|
5
|
-
|
|
6
|
-
export default async function handleRequest(
|
|
7
|
-
request: Request,
|
|
8
|
-
responseStatusCode: number,
|
|
9
|
-
responseHeaders: Headers,
|
|
10
|
-
remixContext: EntryContext,
|
|
11
|
-
loadContext: AppLoadContext
|
|
12
|
-
) {
|
|
13
|
-
const body = await renderToReadableStream(
|
|
14
|
-
<RemixServer context={remixContext} url={request.url} />,
|
|
15
|
-
{
|
|
16
|
-
signal: request.signal,
|
|
17
|
-
onError(error: unknown) {
|
|
18
|
-
// Log streaming rendering errors from inside the shell
|
|
19
|
-
console.error(error);
|
|
20
|
-
responseStatusCode = 500;
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
if (isBotRequest(request.headers.get("user-agent"))) {
|
|
26
|
-
await body.allReady;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
responseHeaders.set("Content-Type", "text/html");
|
|
30
|
-
return new Response(body, {
|
|
31
|
-
headers: responseHeaders,
|
|
32
|
-
status: responseStatusCode,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// We have some Remix apps in the wild already running with isbot@3 so we need
|
|
37
|
-
// to maintain backwards compatibility even though we want new apps to use
|
|
38
|
-
// isbot@4. That way, we can ship this as a minor Semver update to @react-router/dev.
|
|
39
|
-
function isBotRequest(userAgent: string | null) {
|
|
40
|
-
if (!userAgent) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// isbot >= 3.8.0, >4
|
|
45
|
-
if ("isbot" in isbotModule && typeof isbotModule.isbot === "function") {
|
|
46
|
-
return isbotModule.isbot(userAgent);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// isbot < 3.8.0
|
|
50
|
-
if ("default" in isbotModule && typeof isbotModule.default === "function") {
|
|
51
|
-
return isbotModule.default(userAgent);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { AppLoadContext, EntryContext } from "@react-router/deno";
|
|
2
|
-
import { RemixServer } from "@remix-run/react";
|
|
3
|
-
import * as isbotModule from "isbot";
|
|
4
|
-
import { renderToReadableStream } from "react-dom/server";
|
|
5
|
-
|
|
6
|
-
export default async function handleRequest(
|
|
7
|
-
request: Request,
|
|
8
|
-
responseStatusCode: number,
|
|
9
|
-
responseHeaders: Headers,
|
|
10
|
-
remixContext: EntryContext,
|
|
11
|
-
loadContext: AppLoadContext
|
|
12
|
-
) {
|
|
13
|
-
const body = await renderToReadableStream(
|
|
14
|
-
<RemixServer context={remixContext} url={request.url} />,
|
|
15
|
-
{
|
|
16
|
-
signal: request.signal,
|
|
17
|
-
onError(error: unknown) {
|
|
18
|
-
// Log streaming rendering errors from inside the shell
|
|
19
|
-
console.error(error);
|
|
20
|
-
responseStatusCode = 500;
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
if (isBotRequest(request.headers.get("user-agent"))) {
|
|
26
|
-
await body.allReady;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
responseHeaders.set("Content-Type", "text/html");
|
|
30
|
-
return new Response(body, {
|
|
31
|
-
headers: responseHeaders,
|
|
32
|
-
status: responseStatusCode,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// We have some Remix apps in the wild already running with isbot@3 so we need
|
|
37
|
-
// to maintain backwards compatibility even though we want new apps to use
|
|
38
|
-
// isbot@4. That way, we can ship this as a minor Semver update to @react-router/dev.
|
|
39
|
-
function isBotRequest(userAgent: string | null) {
|
|
40
|
-
if (!userAgent) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// isbot >= 3.8.0, >4
|
|
45
|
-
if ("isbot" in isbotModule && typeof isbotModule.isbot === "function") {
|
|
46
|
-
return isbotModule.isbot(userAgent);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// isbot < 3.8.0
|
|
50
|
-
if ("default" in isbotModule && typeof isbotModule.default === "function") {
|
|
51
|
-
return isbotModule.default(userAgent);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { PassThrough } from "node:stream";
|
|
2
|
-
|
|
3
|
-
import type { AppLoadContext, EntryContext } from "@react-router/node";
|
|
4
|
-
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
5
|
-
import { RemixServer } from "react-router";
|
|
6
|
-
import { renderToPipeableStream } from "react-dom/server";
|
|
7
|
-
|
|
8
|
-
const ABORT_DELAY = 5_000;
|
|
9
|
-
|
|
10
|
-
export default function handleRequest(
|
|
11
|
-
request: Request,
|
|
12
|
-
responseStatusCode: number,
|
|
13
|
-
responseHeaders: Headers,
|
|
14
|
-
remixContext: EntryContext,
|
|
15
|
-
loadContext: AppLoadContext
|
|
16
|
-
) {
|
|
17
|
-
return handleBotRequest(
|
|
18
|
-
request,
|
|
19
|
-
responseStatusCode,
|
|
20
|
-
responseHeaders,
|
|
21
|
-
remixContext
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function handleBotRequest(
|
|
26
|
-
request: Request,
|
|
27
|
-
responseStatusCode: number,
|
|
28
|
-
responseHeaders: Headers,
|
|
29
|
-
remixContext: EntryContext
|
|
30
|
-
) {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
let shellRendered = false;
|
|
33
|
-
const { pipe, abort } = renderToPipeableStream(
|
|
34
|
-
<RemixServer
|
|
35
|
-
context={remixContext}
|
|
36
|
-
url={request.url}
|
|
37
|
-
abortDelay={ABORT_DELAY}
|
|
38
|
-
/>,
|
|
39
|
-
{
|
|
40
|
-
onAllReady() {
|
|
41
|
-
shellRendered = true;
|
|
42
|
-
const body = new PassThrough();
|
|
43
|
-
const stream = createReadableStreamFromReadable(body);
|
|
44
|
-
|
|
45
|
-
responseHeaders.set("Content-Type", "text/html");
|
|
46
|
-
|
|
47
|
-
resolve(
|
|
48
|
-
new Response(stream, {
|
|
49
|
-
headers: responseHeaders,
|
|
50
|
-
status: responseStatusCode,
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
pipe(body);
|
|
55
|
-
},
|
|
56
|
-
onShellError(error: unknown) {
|
|
57
|
-
reject(error);
|
|
58
|
-
},
|
|
59
|
-
onError(error: unknown) {
|
|
60
|
-
responseStatusCode = 500;
|
|
61
|
-
// Log streaming rendering errors from inside the shell. Don't log
|
|
62
|
-
// errors encountered during initial shell rendering since they'll
|
|
63
|
-
// reject and get logged in handleDocumentRequest.
|
|
64
|
-
if (shellRendered) {
|
|
65
|
-
console.error(error);
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
setTimeout(abort, ABORT_DELAY);
|
|
72
|
-
});
|
|
73
|
-
}
|