Skip to content

Commit 458a54d

Browse files
committed
Hook up config loader to plugin
1 parent e8af3a3 commit 458a54d

File tree

3 files changed

+135
-105
lines changed

3 files changed

+135
-105
lines changed

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

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,11 @@ async function resolveConfig({
473473
}
474474

475475
export async function createConfigLoader({
476-
root: userRoot,
476+
rootDirectory: userRoot,
477477
command,
478-
onChange,
479478
}: {
480-
root?: string;
479+
rootDirectory?: string;
481480
command?: "dev" | "build";
482-
onChange?: (result: Result<ResolvedReactRouterConfig>) => void;
483481
}) {
484482
let root = userRoot ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
485483

@@ -510,61 +508,81 @@ export async function createConfigLoader({
510508

511509
let fsWatcher: FSWatcher | undefined;
512510

513-
if (onChange) {
514-
fsWatcher = chokidar.watch(
515-
[
516-
...(reactRouterConfigFile ? [reactRouterConfigFile] : []),
517-
...(appDirectory ? [appDirectory] : []),
518-
],
519-
{ ignoreInitial: true }
520-
);
511+
type ChangeHandler = (args: {
512+
result: Result<ResolvedReactRouterConfig>;
513+
routeConfigChanged: boolean;
514+
}) => void;
515+
let changeHandlers: ChangeHandler[] = [];
521516

522-
await new Promise((resolve) => fsWatcher!.on("ready", resolve));
517+
return {
518+
getConfig,
519+
onChange: (handler: ChangeHandler) => {
520+
changeHandlers.push(handler);
521+
522+
if (!fsWatcher) {
523+
fsWatcher = chokidar.watch(
524+
[
525+
...(reactRouterConfigFile ? [reactRouterConfigFile] : []),
526+
...(appDirectory ? [appDirectory] : []),
527+
],
528+
{ ignoreInitial: true }
529+
);
523530

524-
fsWatcher.on("all", async (eventName, rawFilepath) => {
525-
let filepath = path.normalize(rawFilepath);
531+
fsWatcher.on("all", async (eventName, rawFilepath) => {
532+
let filepath = path.normalize(rawFilepath);
526533

527-
let appFileAddedOrRemoved =
528-
appDirectory &&
529-
(eventName === "add" || eventName === "unlink") &&
530-
filepath.startsWith(path.normalize(appDirectory));
534+
let appFileAddedOrRemoved =
535+
appDirectory &&
536+
(eventName === "add" || eventName === "unlink") &&
537+
filepath.startsWith(path.normalize(appDirectory));
531538

532-
let reactRouterConfigFileChanged =
533-
reactRouterConfigFile &&
534-
eventName === "change" &&
535-
filepath === path.normalize(reactRouterConfigFile);
539+
let reactRouterConfigFileChanged =
540+
reactRouterConfigFile &&
541+
eventName === "change" &&
542+
filepath === path.normalize(reactRouterConfigFile);
536543

537-
let configModuleGraphChanged = Boolean(
538-
viteNodeContext.devServer?.moduleGraph.getModuleById(filepath)
539-
);
544+
let configModuleGraphChanged = Boolean(
545+
viteNodeContext.devServer?.moduleGraph.getModuleById(filepath)
546+
);
540547

541-
if (configModuleGraphChanged || appFileAddedOrRemoved) {
542-
viteNodeContext.devServer?.moduleGraph.invalidateAll();
543-
viteNodeContext.runner?.moduleCache.clear();
544-
}
548+
if (configModuleGraphChanged || appFileAddedOrRemoved) {
549+
viteNodeContext.devServer?.moduleGraph.invalidateAll();
550+
viteNodeContext.runner?.moduleCache.clear();
551+
}
545552

546-
if (
547-
appFileAddedOrRemoved ||
548-
reactRouterConfigFileChanged ||
549-
configModuleGraphChanged
550-
) {
551-
onChange(await getConfig());
553+
// todo: isolate from rest of config
554+
let routeConfigChanged = configModuleGraphChanged;
555+
556+
if (
557+
appFileAddedOrRemoved ||
558+
reactRouterConfigFileChanged ||
559+
configModuleGraphChanged
560+
) {
561+
let result = await getConfig();
562+
for (let handler of changeHandlers) {
563+
handler({ result, routeConfigChanged });
564+
}
565+
}
566+
});
552567
}
553-
});
554-
}
555568

556-
return {
557-
get: getConfig,
569+
return () => {
570+
changeHandlers = changeHandlers.filter(
571+
(changeHandler) => changeHandler !== handler
572+
);
573+
};
574+
},
558575
close: async () => {
576+
changeHandlers = [];
559577
await viteNodeContext.devServer.close();
560578
await fsWatcher?.close();
561579
},
562580
};
563581
}
564582

565-
export async function loadConfig({ root }: { root: string }) {
566-
let configLoader = await createConfigLoader({ root });
567-
let config = await configLoader.get();
583+
export async function loadConfig({ rootDirectory }: { rootDirectory: string }) {
584+
let configLoader = await createConfigLoader({ rootDirectory });
585+
let config = await configLoader.getConfig();
568586
await configLoader.close();
569587
return config;
570588
}

packages/react-router-dev/vite/plugin.ts

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ import { combineURLs } from "./combine-urls";
3636
import { removeExports } from "./remove-exports";
3737
import { importViteEsmSync, preloadViteEsm } from "./import-vite-esm-sync";
3838
import {
39-
type ReactRouterConfig,
4039
type ResolvedReactRouterConfig,
41-
resolveReactRouterConfig,
40+
createConfigLoader,
41+
} from "../config/config";
42+
import {
43+
// todo: clean these up
44+
// type ReactRouterConfig,
45+
// type ResolvedReactRouterConfig,
46+
// resolveReactRouterConfig,
4247
resolveEntryFiles,
4348
resolvePublicPath,
4449
} from "./config";
@@ -418,16 +423,12 @@ let deepFreeze = (o: any) => {
418423
return o;
419424
};
420425

421-
type ReactRouterVitePlugin = (config?: ReactRouterConfig) => Vite.Plugin[];
426+
type ReactRouterVitePlugin = () => Vite.Plugin[];
422427
/**
423428
* React Router [Vite plugin.](https://vitejs.dev/guide/using-plugins.html)
424429
*/
425-
export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
426-
let reactRouterUserConfig = _config ?? {};
427-
428-
// Prevent mutations to the user config
429-
reactRouterUserConfig = deepFreeze(reactRouterUserConfig);
430-
430+
export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
431+
let rootDirectory: string;
431432
let viteCommand: Vite.ResolvedConfig["command"];
432433
let viteUserConfig: Vite.UserConfig;
433434
let viteConfigEnv: Vite.ConfigEnv;
@@ -442,24 +443,32 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
442443
// change or route file addition/removal.
443444
let ctx: ReactRouterPluginContext;
444445

446+
let reactRouterConfigLoader: Awaited<ReturnType<typeof createConfigLoader>>;
447+
448+
let logger: Vite.Logger;
449+
450+
let firstLoad = true;
451+
445452
/** Mutates `ctx` as a side-effect */
446453
let updatePluginContext = async ({
447454
routeConfigChanged = false,
448455
}: {
449456
routeConfigChanged?: boolean;
450457
} = {}): Promise<void> => {
451-
let rootDirectory =
452-
viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
453-
454458
invariant(routesViteNodeContext);
455-
let reactRouterConfig = await resolveReactRouterConfig({
456-
rootDirectory,
457-
reactRouterUserConfig,
458-
routeConfigChanged,
459-
viteUserConfig,
460-
viteCommand,
461-
routesViteNodeContext,
462-
});
459+
460+
let reactRouterConfig: ResolvedReactRouterConfig;
461+
let reactRouterConfigResult = await reactRouterConfigLoader.getConfig();
462+
463+
if (reactRouterConfigResult.ok) {
464+
reactRouterConfig = reactRouterConfigResult.value;
465+
} else {
466+
logger.error(reactRouterConfigResult.error);
467+
if (firstLoad) {
468+
process.exit(1);
469+
}
470+
return;
471+
}
463472

464473
let { entryClientFilePath, entryServerFilePath } = await resolveEntryFiles({
465474
rootDirectory,
@@ -480,6 +489,8 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
480489
}
481490
: { isSsrBuild: false };
482491

492+
firstLoad = false;
493+
483494
ctx = {
484495
reactRouterConfig,
485496
rootDirectory,
@@ -749,6 +760,18 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
749760
viteConfigEnv = _viteConfigEnv;
750761
viteCommand = viteConfigEnv.command;
751762

763+
logger = vite.createLogger(viteUserConfig.logLevel, {
764+
prefix: "[react-router]",
765+
});
766+
767+
rootDirectory =
768+
viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
769+
770+
reactRouterConfigLoader = await createConfigLoader({
771+
rootDirectory,
772+
command: viteCommand === "serve" ? "dev" : "build",
773+
});
774+
752775
routesViteNodeContext = await ViteNode.createContext({
753776
root: viteUserConfig.root,
754777
mode: viteConfigEnv.mode,
@@ -1062,47 +1085,32 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
10621085
},
10631086
});
10641087

1065-
// Invalidate virtual modules and update cached plugin config via file watcher
1066-
viteDevServer.watcher.on("all", async (eventName, rawFilepath) => {
1067-
let { normalizePath } = importViteEsmSync();
1068-
let filepath = normalizePath(rawFilepath);
1088+
reactRouterConfigLoader.onChange(
1089+
async ({ result, routeConfigChanged }) => {
1090+
if (result.ok) {
1091+
let lastReactRouterConfig = ctx.reactRouterConfig;
10691092

1070-
let appFileAddedOrRemoved =
1071-
(eventName === "add" || eventName === "unlink") &&
1072-
filepath.startsWith(
1073-
normalizePath(ctx.reactRouterConfig.appDirectory)
1074-
);
1075-
1076-
invariant(viteConfig?.configFile);
1077-
let viteConfigChanged =
1078-
eventName === "change" &&
1079-
filepath === normalizePath(viteConfig.configFile);
1080-
1081-
let routeConfigChanged = Boolean(
1082-
routesViteNodeContext?.devServer?.moduleGraph.getModuleById(
1083-
filepath
1084-
)
1085-
);
1086-
1087-
if (routeConfigChanged || appFileAddedOrRemoved) {
1088-
routesViteNodeContext?.devServer?.moduleGraph.invalidateAll();
1089-
routesViteNodeContext?.runner?.moduleCache.clear();
1090-
}
1091-
1092-
if (
1093-
appFileAddedOrRemoved ||
1094-
viteConfigChanged ||
1095-
routeConfigChanged
1096-
) {
1097-
let lastReactRouterConfig = ctx.reactRouterConfig;
1093+
if (routeConfigChanged) {
1094+
logger.info(colors.green("Route config changed."), {
1095+
clear: true,
1096+
timestamp: true,
1097+
});
1098+
}
10981099

1099-
await updatePluginContext({ routeConfigChanged });
1100+
await updatePluginContext({ routeConfigChanged });
11001101

1101-
if (!isEqualJson(lastReactRouterConfig, ctx.reactRouterConfig)) {
1102+
if (!isEqualJson(lastReactRouterConfig, ctx.reactRouterConfig)) {
1103+
invalidateVirtualModules(viteDevServer);
1104+
}
1105+
} else {
11021106
invalidateVirtualModules(viteDevServer);
1107+
logger.error(result.error, {
1108+
clear: true,
1109+
timestamp: true,
1110+
});
11031111
}
11041112
}
1105-
});
1113+
);
11061114

11071115
return () => {
11081116
// Let user servers handle SSR requests in middleware mode,
@@ -1236,6 +1244,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = (_config) => {
12361244
async buildEnd() {
12371245
await viteChildCompiler?.close();
12381246
await routesViteNodeContext?.devServer?.close();
1247+
await reactRouterConfigLoader.close();
12391248
},
12401249
},
12411250
{
@@ -1739,7 +1748,7 @@ async function getRouteMetadata(
17391748

17401749
async function getPrerenderBuildAndHandler(
17411750
viteConfig: Vite.ResolvedConfig,
1742-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1751+
reactRouterConfig: ResolvedReactRouterConfig,
17431752
serverBuildDirectory: string
17441753
) {
17451754
let serverBuildPath = path.join(
@@ -1756,7 +1765,7 @@ async function getPrerenderBuildAndHandler(
17561765

17571766
async function handleSpaMode(
17581767
viteConfig: Vite.ResolvedConfig,
1759-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1768+
reactRouterConfig: ResolvedReactRouterConfig,
17601769
serverBuildDirectory: string,
17611770
clientBuildDirectory: string
17621771
) {
@@ -1784,7 +1793,7 @@ async function handleSpaMode(
17841793

17851794
async function handlePrerender(
17861795
viteConfig: Vite.ResolvedConfig,
1787-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1796+
reactRouterConfig: ResolvedReactRouterConfig,
17881797
serverBuildDirectory: string,
17891798
clientBuildDirectory: string
17901799
) {
@@ -1921,7 +1930,7 @@ async function prerenderData(
19211930
handler: RequestHandler,
19221931
prerenderPath: string,
19231932
clientBuildDirectory: string,
1924-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1933+
reactRouterConfig: ResolvedReactRouterConfig,
19251934
viteConfig: Vite.ResolvedConfig,
19261935
requestInit: RequestInit
19271936
) {
@@ -1949,7 +1958,7 @@ async function prerenderRoute(
19491958
handler: RequestHandler,
19501959
prerenderPath: string,
19511960
clientBuildDirectory: string,
1952-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1961+
reactRouterConfig: ResolvedReactRouterConfig,
19531962
viteConfig: Vite.ResolvedConfig,
19541963
requestInit: RequestInit
19551964
) {
@@ -1979,7 +1988,7 @@ async function prerenderResourceRoute(
19791988
handler: RequestHandler,
19801989
prerenderPath: string,
19811990
clientBuildDirectory: string,
1982-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
1991+
reactRouterConfig: ResolvedReactRouterConfig,
19831992
viteConfig: Vite.ResolvedConfig,
19841993
requestInit: RequestInit
19851994
) {
@@ -2003,7 +2012,7 @@ async function prerenderResourceRoute(
20032012
async function prerenderManifest(
20042013
build: ServerBuild,
20052014
clientBuildDirectory: string,
2006-
reactRouterConfig: Awaited<ReturnType<typeof resolveReactRouterConfig>>,
2015+
reactRouterConfig: ResolvedReactRouterConfig,
20072016
viteConfig: Vite.ResolvedConfig
20082017
) {
20092018
let normalizedPath = `${reactRouterConfig.basename}/__manifest`.replace(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
appDirectory: "app",
3+
};

0 commit comments

Comments
 (0)