Skip to content

Commit 6638226

Browse files
authored
Generate JavaScript entries at build time (#15373)
Assisted-By: devx/09e6ec2a-279b-4e17-8759-2626d088d051
1 parent fc873b0 commit 6638226

7 files changed

Lines changed: 76 additions & 30 deletions

File tree

integration/cli-test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ const helpText = dedent`
213213
--json Print the routes as JSON
214214
\`reveal\` Options:
215215
--config, -c Use specified Vite config file (string)
216-
--no-typescript Generate plain JavaScript files
216+
--no-typescript Generate plain JavaScript files (deprecated; will be removed in v9)
217217
\`typegen\` Options:
218218
--watch Automatically regenerate types whenever route config (\`routes.ts\`) or route modules change
219219
@@ -461,10 +461,46 @@ test.describe("cli", () => {
461461
expect(existsSync(entryServerFile)).toBeFalsy();
462462
expect(existsSync(entryClientFile)).toBeFalsy();
463463

464-
run(["reveal", "--no-typescript"], { cwd });
464+
let { stderr, status } = run(["reveal", "--no-typescript"], {
465+
cwd,
466+
env: {
467+
...process.env,
468+
FORCE_COLOR: undefined,
469+
NO_COLOR: "1",
470+
},
471+
});
465472

466473
expect(existsSync(entryServerFile)).toBeTruthy();
467474
expect(existsSync(entryClientFile)).toBeTruthy();
475+
expect(readFileSync(entryServerFile, "utf-8")).toContain(
476+
"renderToPipeableStream",
477+
);
478+
expect(readFileSync(entryServerFile, "utf-8")).not.toContain(
479+
"import type",
480+
);
481+
expect(stderr.toString().trim()).toBe(
482+
"The --no-typescript flag is deprecated and will be removed in React Router v9.",
483+
);
484+
expect(status).toBe(0);
485+
expect(build({ cwd }).status).toBe(0);
486+
});
487+
488+
test("generates a web JavaScript server entry for non-Node projects", async () => {
489+
const cwd = await createProject();
490+
let packageJsonPath = path.join(cwd, "package.json");
491+
let pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
492+
delete pkg.dependencies["@react-router/express"];
493+
delete pkg.dependencies["@react-router/node"];
494+
delete pkg.dependencies["@react-router/serve"];
495+
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
496+
497+
let entryServerFile = path.join(cwd, "app", "entry.server.jsx");
498+
499+
run(["reveal", "entry.server", "--no-typescript"], { cwd });
500+
501+
expect(readFileSync(entryServerFile, "utf-8")).toContain(
502+
"renderToReadableStream",
503+
);
468504
});
469505
});
470506

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Generate JavaScript entry files at package build time so `react-router reveal --no-typescript` does not require Prettier at runtime
2+
3+
- Deprecate the `--no-typescript` flag ahead of its removal in React Router v9

packages/react-router-dev/cli/commands.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type { ViteBuildOptions } from "../vite/build";
1414
import { hasNodeDependency, loadConfig } from "../config/config";
1515
import { formatRoutes } from "../config/format";
1616
import type { RoutesFormat } from "../config/format";
17-
import { transpile as convertFileToJS } from "./useJavascript";
1817
import * as profiler from "../vite/profiler";
1918
import * as Typegen from "../typegen";
2019
import { preloadVite, getVite } from "../vite/vite";
@@ -177,18 +176,20 @@ export async function generateEntry(
177176
await copyFile(defaultEntry, outputFile);
178177
} else {
179178
let pkgJson = await readPackageJSON(rootDirectory);
179+
let useTypeScript = flags.typescript ?? true;
180+
let outputExtension = useTypeScript ? "tsx" : "jsx";
180181

181182
let defaultEntryClient = path.resolve(
182183
defaultsDirectory,
183-
"entry.client.tsx",
184+
`entry.client.${outputExtension}`,
184185
);
185186

186187
let defaultEntryServer = path.resolve(
187188
defaultsDirectory,
188189
hasNodeDependency(pkgJson.dependencies) &&
189190
!configResult.value.future.unstable_enableNodeReadableStream
190-
? `entry.server.node.tsx`
191-
: `entry.server.web.tsx`,
191+
? `entry.server.node.${outputExtension}`
192+
: `entry.server.web.${outputExtension}`,
192193
);
193194

194195
let isServerEntry = entry === "entry.server";
@@ -201,20 +202,10 @@ export async function generateEntry(
201202
defaultEntryClient,
202203
);
203204

204-
let useTypeScript = flags.typescript ?? true;
205-
let outputExtension = useTypeScript ? "tsx" : "jsx";
206205
let outputEntry = `${entry}.${outputExtension}`;
207206
outputFile = path.resolve(appDirectory, outputEntry);
208207

209-
if (!useTypeScript) {
210-
let javascript = await convertFileToJS(contents, {
211-
cwd: rootDirectory,
212-
filename: isServerEntry ? defaultEntryServer : defaultEntryClient,
213-
});
214-
await writeFile(outputFile, javascript, "utf-8");
215-
} else {
216-
await writeFile(outputFile, contents, "utf-8");
217-
}
208+
await writeFile(outputFile, contents, "utf-8");
218209
}
219210

220211
console.log(

packages/react-router-dev/cli/run.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ ${colors.blueBright("react-router")}
4545
--json Print the routes as JSON
4646
\`reveal\` Options:
4747
--config, -c Use specified Vite config file (string)
48-
--no-typescript Generate plain JavaScript files
48+
--no-typescript Generate plain JavaScript files (deprecated; will be removed in v9)
4949
\`typegen\` Options:
5050
--watch Automatically regenerate types whenever route config (\`routes.ts\`) or route modules change
5151
@@ -198,6 +198,11 @@ export async function run(
198198

199199
flags.interactive = flags.interactive ?? isMain;
200200
if (values["no-typescript"]) {
201+
console.warn(
202+
colors.yellow(
203+
"The --no-typescript flag is deprecated and will be removed in React Router v9.",
204+
),
205+
);
201206
flags.typescript = false;
202207
}
203208

packages/react-router-dev/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@
9292
"@babel/core": "^7.29.7",
9393
"@babel/generator": "^7.29.7",
9494
"@babel/parser": "^7.29.7",
95-
"@babel/plugin-syntax-jsx": "^7.29.7",
96-
"@babel/preset-typescript": "^7.29.7",
9795
"@babel/traverse": "^7.29.7",
9896
"@babel/types": "^7.29.7",
9997
"@react-router/node": "workspace:*",
@@ -110,14 +108,15 @@
110108
"pathe": "^2.0.3",
111109
"picocolors": "^1.1.1",
112110
"pkg-types": "^2.3.1",
113-
"prettier": "^3.8.3",
114111
"react-refresh": "^0.18.0",
115112
"semver": "^7.8.1",
116113
"tinyglobby": "^0.2.16",
117114
"valibot": "^1.4.1"
118115
},
119116
"devDependencies": {
120117
"@react-router/serve": "workspace:*",
118+
"@babel/plugin-syntax-jsx": "^7.29.7",
119+
"@babel/preset-typescript": "^7.29.7",
121120
"@types/babel__core": "^7.20.5",
122121
"@types/babel__generator": "^7.27.0",
123122
"@types/babel__traverse": "^7.28.0",
@@ -132,6 +131,7 @@
132131
"esbuild-register": "^3.6.0",
133132
"execa": "9.6.1",
134133
"fast-glob": "3.3.3",
134+
"prettier": "^3.8.3",
135135
"react-router": "workspace:^",
136136
"tsdown": "catalog:",
137137
"typescript": "catalog:",

packages/react-router-dev/tsdown.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { defineConfig } from "tsdown";
44

55
// @ts-ignore - out of scope
66
import { createBanner } from "../../build.utils.ts";
7+
import { transpile as convertFileToJS } from "./cli/useJavascript.ts";
78

89
import pkg from "./package.json" with { type: "json" };
910

@@ -34,6 +35,16 @@ async function copyBuildAssets() {
3435
`config/defaults/${file}`,
3536
`dist/config/defaults/${file}`,
3637
);
38+
if (file.endsWith(".tsx")) {
39+
let inputFile = `config/defaults/${file}`;
40+
let tsx = await fsp.readFile(inputFile, "utf-8");
41+
let jsx = await convertFileToJS(tsx, { filename: inputFile });
42+
await fsp.writeFile(
43+
`dist/config/defaults/${file.replace(/\.tsx$/, ".jsx")}`,
44+
jsx,
45+
"utf-8",
46+
);
47+
}
3748
}
3849

3950
await fsp.mkdir("dist/config/default-rsc-entries", {

pnpm-lock.yaml

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)