Skip to content

Commit

Permalink
Read Vite config in reveal/routes commands (#8916)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Brophy <[email protected]>
  • Loading branch information
markdalgleish and brophdawg11 authored Feb 28, 2024
1 parent 97cc8ec commit 6d32ca5
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-apples-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Support reading from Vite config when running `remix reveal` and `remix routes` CLI commands
72 changes: 46 additions & 26 deletions packages/remix-dev/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import type { RemixConfig } from "../config";
import type { ViteDevOptions } from "../vite/dev";
import type { ViteBuildOptions } from "../vite/build";
import { readConfig } from "../config";
import { formatRoutes, RoutesFormat, isRoutesFormat } from "../config/format";
import { formatRoutes, type RoutesFormat } from "../config/format";
import { loadVitePluginContext } from "../vite/plugin";
import { detectPackageManager } from "./detectPackageManager";
import { transpile as convertFileToJS } from "./useJavascript";
import type { Options } from "../compiler/options";
Expand Down Expand Up @@ -82,13 +83,23 @@ export function setup() {

export async function routes(
remixRoot?: string,
formatArg?: string
flags: {
config?: string;
json?: boolean;
} = {}
): Promise<void> {
let config = await readConfig(remixRoot);

let format = isRoutesFormat(formatArg) ? formatArg : RoutesFormat.jsx;

console.log(formatRoutes(config.routes, format));
let ctx = await loadVitePluginContext({
root: remixRoot,
configFile: flags.config,
});

let routes =
ctx?.remixConfig.routes ||
// v3 TODO: Remove this and require the presence of a Vite config
(await readConfig(remixRoot)).routes;

let format: RoutesFormat = flags.json ? "json" : "jsx";
console.log(formatRoutes(routes, format));
}

export async function build(
Expand Down Expand Up @@ -227,14 +238,28 @@ let disjunctionListFormat = new Intl.ListFormat("en", {
export async function generateEntry(
entry: string,
remixRoot: string,
useTypeScript: boolean = true
flags: {
typescript?: boolean;
config?: string;
} = {}
) {
let config = await readConfig(remixRoot);
let ctx = await loadVitePluginContext({
root: remixRoot,
configFile: flags.config,
});

let { rootDirectory, appDirectory } = ctx
? {
rootDirectory: ctx.rootDirectory,
appDirectory: ctx.remixConfig.appDirectory,
}
: // v3 TODO: Remove this and require the presence of a Vite config
await readConfig(remixRoot);

// if no entry passed, attempt to create both
if (!entry) {
await generateEntry("entry.client", remixRoot, useTypeScript);
await generateEntry("entry.server", remixRoot, useTypeScript);
await generateEntry("entry.client", remixRoot, flags);
await generateEntry("entry.server", remixRoot, flags);
return;
}

Expand All @@ -248,7 +273,7 @@ export async function generateEntry(
return;
}

let pkgJson = await PackageJson.load(config.rootDirectory);
let pkgJson = await PackageJson.load(rootDirectory);
let deps = pkgJson.content.dependencies ?? {};

let serverRuntime = deps["@remix-run/deno"]
Expand Down Expand Up @@ -278,30 +303,25 @@ export async function generateEntry(
let defaultEntryClient = path.resolve(defaultsDirectory, "entry.client.tsx");
let defaultEntryServer = path.resolve(
defaultsDirectory,
`entry.server.${serverRuntime}.tsx`
ctx?.remixConfig.ssr === false
? `entry.server.spa.tsx`
: `entry.server.${serverRuntime}.tsx`
);

let isServerEntry = entry === "entry.server";

let contents = isServerEntry
? await createServerEntry(
config.rootDirectory,
config.appDirectory,
defaultEntryServer
)
: await createClientEntry(
config.rootDirectory,
config.appDirectory,
defaultEntryClient
);
? 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(config.appDirectory, outputEntry);
let outputFile = path.resolve(appDirectory, outputEntry);

if (!useTypeScript) {
let javascript = convertFileToJS(contents, {
cwd: config.rootDirectory,
cwd: rootDirectory,
filename: isServerEntry ? defaultEntryServer : defaultEntryClient,
});
await fse.writeFile(outputFile, javascript, "utf-8");
Expand All @@ -312,7 +332,7 @@ export async function generateEntry(
console.log(
colors.blue(
`Entry file ${entry} created at ${path.relative(
config.rootDirectory,
rootDirectory,
outputFile
)}.`
)
Expand Down
22 changes: 16 additions & 6 deletions packages/remix-dev/cli/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,25 @@ export async function run(argv: string[] = process.argv.slice(2)) {
"--tls-key": String,
"--tls-cert": String,

...(argv[0].startsWith("vite:") ||
argv[0] === "reveal" ||
argv[0] === "routes"
? // Handle commands that support Vite's --config flag
{
"--config": String,
"-c": "--config",
}
: {
// Handle non Vite config commands
"-c": "--command",
}),

...(argv[0].startsWith("vite:")
? {
// Vite commands
// --force and --port are already defined above
// --config, --force and --port are already defined above
"--assetsInlineLimit": Number,
"--clearScreen": Boolean,
"--config": String,
"-c": "--config",
"--cors": Boolean,
"--emptyOutDir": Boolean,
"--host": isBooleanFlag("--host") ? Boolean : String,
Expand All @@ -148,7 +159,6 @@ export async function run(argv: string[] = process.argv.slice(2)) {
}
: {
// Non Vite commands
"-c": "--command",
"--sourcemap": Boolean,
}),
},
Expand Down Expand Up @@ -202,7 +212,7 @@ export async function run(argv: string[] = process.argv.slice(2)) {
});
break;
case "routes":
await commands.routes(input[1], flags.json ? "json" : "jsx");
await commands.routes(input[1], flags);
break;
case "build":
if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
Expand All @@ -220,7 +230,7 @@ export async function run(argv: string[] = process.argv.slice(2)) {
break;
case "reveal": {
// TODO: simplify getting started guide
await commands.generateEntry(input[1], input[2], flags.typescript);
await commands.generateEntry(input[1], input[2], flags);
break;
}
case "dev":
Expand Down
13 changes: 3 additions & 10 deletions packages/remix-dev/config/format.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import type { RouteManifest } from "./routes";

export enum RoutesFormat {
json = "json",
jsx = "jsx",
}

export function isRoutesFormat(format: any): format is RoutesFormat {
return format === RoutesFormat.json || format === RoutesFormat.jsx;
}
export type RoutesFormat = "json" | "jsx";

export function formatRoutes(
routeManifest: RouteManifest,
format: RoutesFormat
) {
switch (format) {
case RoutesFormat.json:
case "json":
return formatRoutesAsJson(routeManifest);
case RoutesFormat.jsx:
case "jsx":
return formatRoutesAsJsx(routeManifest);
}
}
Expand Down
52 changes: 11 additions & 41 deletions packages/remix-dev/vite/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,15 @@ import {
type BuildManifest,
type ServerBundleBuildConfig,
type ServerBundlesBuildManifest,
resolveViteConfig,
extractRemixPluginContext,
configRouteToBranchRoute,
getServerBuildDirectory,
} from "./plugin";
import type { ConfigRoute, RouteManifest } from "../config/routes";
import invariant from "../invariant";
import { preloadViteEsm } from "./import-vite-esm-sync";

async function resolveViteConfig({
configFile,
mode,
root,
}: {
configFile?: string;
mode?: string;
root: string;
}) {
let vite = await import("vite");

// Leverage the Vite config as a way to configure the entire multi-step build
// process so we don't need to have a separate Remix config
let viteConfig = await vite.resolveConfig(
{ mode, configFile, root },
"build", // command
"production", // default mode
"production" // default NODE_ENV
);

if (typeof viteConfig.build.manifest === "string") {
throw new Error("Custom Vite manifest paths are not supported");
}

return viteConfig;
}

async function extractRemixPluginContext(viteConfig: Vite.ResolvedConfig) {
let ctx = viteConfig["__remixPluginContext" as keyof typeof viteConfig] as
| RemixPluginContext
| undefined;

if (!ctx) {
console.error(colors.red("Remix Vite plugin not found in Vite config"));
process.exit(1);
}

return ctx;
}

function getAddressableRoutes(routes: RouteManifest): ConfigRoute[] {
let nonAddressableIds = new Set<string>();

Expand Down Expand Up @@ -284,7 +246,15 @@ export async function build(
await preloadViteEsm();

let viteConfig = await resolveViteConfig({ configFile, mode, root });
let ctx = await extractRemixPluginContext(viteConfig);

// eslint-disable-next-line prefer-let/prefer-let -- Improve type narrowing
const ctx = await extractRemixPluginContext(viteConfig);

if (!ctx) {
console.error(colors.red("Remix Vite plugin not found in Vite config"));
process.exit(1);
}

let { remixConfig } = ctx;

let vite = await import("vite");
Expand Down
65 changes: 65 additions & 0 deletions packages/remix-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
type AppConfig as RemixEsbuildUserConfig,
type RemixConfig as ResolvedRemixEsbuildConfig,
resolveConfig as resolveRemixEsbuildConfig,
findConfig,
} from "../config";
import { type Manifest as RemixManifest } from "../manifest";
import invariant from "../invariant";
Expand All @@ -39,6 +40,70 @@ import { resolveFileUrl } from "./resolve-file-url";
import { removeExports } from "./remove-exports";
import { importViteEsmSync, preloadViteEsm } from "./import-vite-esm-sync";

export async function resolveViteConfig({
configFile,
mode,
root,
}: {
configFile?: string;
mode?: string;
root: string;
}) {
let vite = await import("vite");

let viteConfig = await vite.resolveConfig(
{ mode, configFile, root },
"build", // command
"production", // default mode
"production" // default NODE_ENV
);

if (typeof viteConfig.build.manifest === "string") {
throw new Error("Custom Vite manifest paths are not supported");
}

return viteConfig;
}

export async function extractRemixPluginContext(
viteConfig: Vite.ResolvedConfig
) {
return viteConfig["__remixPluginContext" as keyof typeof viteConfig] as
| RemixPluginContext
| undefined;
}

export async function loadVitePluginContext({
configFile,
root,
}: {
configFile?: string;
root?: string;
}) {
if (!root) {
root = process.env.REMIX_ROOT || process.cwd();
}

configFile =
configFile ??
findConfig(root, "vite.config", [
".ts",
".cts",
".mts",
".js",
".cjs",
".mjs",
]);

// V3 TODO: Vite config should not be optional
if (!configFile) {
return;
}

let viteConfig = await resolveViteConfig({ configFile, root });
return await extractRemixPluginContext(viteConfig);
}

const supportedRemixEsbuildConfigKeys = [
"appDirectory",
"future",
Expand Down

0 comments on commit 6d32ca5

Please sign in to comment.