-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathcommands.ts
214 lines (183 loc) · 5.79 KB
/
commands.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import * as path from "node:path";
import fse from "fs-extra";
import PackageJson from "@npmcli/package-json";
import exitHook from "exit-hook";
import colors from "picocolors";
// Workaround for "ERR_REQUIRE_CYCLE_MODULE" in Node 22.10.0+
import "react-router";
import type { ViteDevOptions } from "../vite/dev";
import type { ViteBuildOptions } from "../vite/build";
import { loadConfig } from "../config/config";
import { formatRoutes } from "../config/format";
import type { RoutesFormat } from "../config/format";
import { transpile as convertFileToJS } from "./useJavascript";
import * as profiler from "../vite/profiler";
import * as Typegen from "../typegen";
import { preloadVite, getVite } from "../vite/vite";
export async function routes(
reactRouterRoot?: string,
flags: {
config?: string;
json?: boolean;
} = {}
): Promise<void> {
let rootDirectory = reactRouterRoot ?? process.cwd();
let configResult = await loadConfig({ rootDirectory });
if (!configResult.ok) {
console.error(colors.red(configResult.error));
process.exit(1);
}
let format: RoutesFormat = flags.json ? "json" : "jsx";
console.log(formatRoutes(configResult.value.routes, format));
}
export async function build(
root?: string,
options: ViteBuildOptions = {}
): Promise<void> {
if (!root) {
root = process.env.REACT_ROUTER_ROOT || process.cwd();
}
let { build } = await import("../vite/build");
if (options.profile) {
await profiler.start();
}
try {
await build(root, options);
} finally {
await profiler.stop(console.info);
}
}
export async function dev(root: string, options: ViteDevOptions = {}) {
let { dev } = await import("../vite/dev");
if (options.profile) {
await profiler.start();
}
exitHook(() => profiler.stop(console.info));
await dev(root, options);
// keep `react-router dev` alive by waiting indefinitely
await new Promise(() => {});
}
let clientEntries = ["entry.client.tsx", "entry.client.js", "entry.client.jsx"];
let serverEntries = ["entry.server.tsx", "entry.server.js", "entry.server.jsx"];
let entries = ["entry.client", "entry.server"];
let conjunctionListFormat = new Intl.ListFormat("en", {
style: "long",
type: "conjunction",
});
export async function generateEntry(
entry?: string,
reactRouterRoot?: string,
flags: {
typescript?: boolean;
config?: string;
} = {}
) {
// if no entry passed, attempt to create both
if (!entry) {
await generateEntry("entry.client", reactRouterRoot, flags);
await generateEntry("entry.server", reactRouterRoot, flags);
return;
}
let rootDirectory = reactRouterRoot ?? process.cwd();
let configResult = await loadConfig({ rootDirectory });
if (!configResult.ok) {
console.error(colors.red(configResult.error));
return;
}
let appDirectory = configResult.value.appDirectory;
if (!entries.includes(entry)) {
let entriesArray = Array.from(entries);
let list = conjunctionListFormat.format(entriesArray);
console.error(
colors.red(`Invalid entry file. Valid entry files are ${list}`)
);
return;
}
let pkgJson = await PackageJson.load(rootDirectory);
let deps = pkgJson.content.dependencies ?? {};
if (!deps["@react-router/node"]) {
console.error(colors.red(`No default server entry detected.`));
return;
}
let defaultsDirectory = path.resolve(
path.dirname(require.resolve("@react-router/dev/package.json")),
"dist",
"config",
"defaults"
);
let defaultEntryClient = path.resolve(defaultsDirectory, "entry.client.tsx");
let defaultEntryServer = path.resolve(
defaultsDirectory,
`entry.server.node.tsx`
);
let isServerEntry = entry === "entry.server";
let contents = isServerEntry
? await createServerEntry(rootDirectory, appDirectory, defaultEntryServer)
: await createClientEntry(rootDirectory, appDirectory, defaultEntryClient);
let useTypeScript = flags.typescript ?? true;
let outputExtension = useTypeScript ? "tsx" : "jsx";
let outputEntry = `${entry}.${outputExtension}`;
let outputFile = path.resolve(appDirectory, outputEntry);
if (!useTypeScript) {
let javascript = convertFileToJS(contents, {
cwd: rootDirectory,
filename: isServerEntry ? defaultEntryServer : defaultEntryClient,
});
await fse.writeFile(outputFile, javascript, "utf-8");
} else {
await fse.writeFile(outputFile, contents, "utf-8");
}
console.log(
colors.blue(
`Entry file ${entry} created at ${path.relative(
rootDirectory,
outputFile
)}.`
)
);
}
async function checkForEntry(
rootDirectory: string,
appDirectory: string,
entries: string[]
) {
for (let entry of entries) {
let entryPath = path.resolve(appDirectory, entry);
let exists = await fse.pathExists(entryPath);
if (exists) {
let relative = path.relative(rootDirectory, entryPath);
console.error(colors.red(`Entry file ${relative} already exists.`));
return process.exit(1);
}
}
}
async function createServerEntry(
rootDirectory: string,
appDirectory: string,
inputFile: string
) {
await checkForEntry(rootDirectory, appDirectory, serverEntries);
let contents = await fse.readFile(inputFile, "utf-8");
return contents;
}
async function createClientEntry(
rootDirectory: string,
appDirectory: string,
inputFile: string
) {
await checkForEntry(rootDirectory, appDirectory, clientEntries);
let contents = await fse.readFile(inputFile, "utf-8");
return contents;
}
export async function typegen(root: string, flags: { watch: boolean }) {
root ??= process.cwd();
if (flags.watch) {
await preloadVite();
const vite = getVite();
const logger = vite.createLogger("info", { prefix: "[react-router]" });
await Typegen.watch(root, { logger });
await new Promise(() => {}); // keep alive
return;
}
await Typegen.run(root);
}