@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 ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("./dist/cli");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
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
- let serverRuntime = deps["@react-router/deno"] ? "deno" : deps["@react-router/cloudflare"] ? "cloudflare" : deps["@react-router/node"] ? "node" : undefined;
123
- if (!serverRuntime) {
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, (ctx === null || ctx === void 0 ? void 0 : ctx.reactRouterConfig.ssr) === false ? `entry.server.spa.tsx` : `entry.server.${serverRuntime}.tsx`);
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;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/run.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli.js CHANGED
@@ -1,6 +1,5 @@
1
- #!/usr/bin/env node
2
1
  /**
3
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
4
3
  *
5
4
  * Copyright (c) Remix Software Inc.
6
5
  *
package/dist/colors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,9 +1,10 @@
1
1
  import { PassThrough } from "node:stream";
2
2
 
3
- import type { AppLoadContext, EntryContext } from "@react-router/node";
3
+ import type { AppLoadContext, EntryContext } from "react-router";
4
4
  import { createReadableStreamFromReadable } from "@react-router/node";
5
- import { RemixServer } from "react-router";
6
- import * as isbotModule from "isbot";
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
- remixContext: EntryContext,
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
- const { pipe, abort } = renderToPipeableStream(
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
- resolve(
80
- new Response(stream, {
81
- headers: responseHeaders,
82
- status: responseStatusCode,
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
- <RemixServer
117
- context={remixContext}
31
+ <ServerRouter
32
+ context={routerContext}
118
33
  url={request.url}
119
34
  abortDelay={ABORT_DELAY}
120
35
  />,
121
36
  {
122
- onShellReady() {
37
+ [readyOption]() {
123
38
  shellRendered = true;
124
39
  const body = new PassThrough();
125
40
  const stream = createReadableStreamFromReadable(body);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
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 (!reactRouterConfig.ssr) {
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
- let serverRuntime = deps["@react-router/deno"] ? "deno" : deps["@react-router/cloudflare"] ? "cloudflare" : deps["@react-router/node"] ? "node" : undefined;
243
- if (!serverRuntime) {
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.${serverRuntime}.tsx`;
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/invariant.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
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/remix-express/server.ts
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
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
- /** This is used to manage a build optimization to remove unused route exports
112
- from the client build output. This is important in cases where custom route
113
- exports are only ever used on the server. Without this optimization we can't
114
- tree-shake any unused custom exports because routes are entry points. */
115
- const BUILD_CLIENT_ROUTE_QUERY_STRING = "?__remix-build-client-route";
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 + BUILD_CLIENT_ROUTE_QUERY_STRING] ?? 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/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"] : undefined
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)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`)]
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
- serverRuntime.unstable_setDevServerHooks({
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 = serverRuntime.createRequestHandler(build, "development");
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/server-runtime' for this check since it's a
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/server-runtime/package.json"));
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('@react-router/node');
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 remix-server-runtime
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 remix-server-runtime
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 = {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,4 +1,4 @@
1
- import type { ServerBuild } from "@react-router/server-runtime";
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;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite/vmod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-3a25d7f8a
2
+ * @react-router/dev v0.0.0-experimental-ecd35cd60
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-3a25d7f8a",
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/remix-dev"
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": "dist/cli.js"
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-3a25d7f8a",
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
- "@react-router/serve": "0.0.0-experimental-3a25d7f8a"
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-3a25d7f8a",
84
- "@react-router/serve": "^0.0.0-experimental-3a25d7f8a"
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
- }