From 1d1d1967577e9c0dea4d576dade5960fe81b2494 Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Tue, 23 Jan 2024 16:02:40 +1100 Subject: [PATCH] refactor(remix-dev/vite): move server bundle ID to build context (#8582) --- packages/remix-dev/vite/build.ts | 31 ++++++------- packages/remix-dev/vite/plugin.ts | 75 ++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/packages/remix-dev/vite/build.ts b/packages/remix-dev/vite/build.ts index 0b24bc5bc1..9368e9815c 100644 --- a/packages/remix-dev/vite/build.ts +++ b/packages/remix-dev/vite/build.ts @@ -9,13 +9,13 @@ import { type BuildManifest, type ServerBundlesBuildManifest, configRouteToBranchRoute, - getServerBuildDirectory, + getServerBuildRootDirectory, } from "./plugin"; import type { ConfigRoute, RouteManifest } from "../config/routes"; import invariant from "../invariant"; import { preloadViteEsm } from "./import-vite-esm-sync"; -async function extractConfig({ +async function resolveViteConfig({ configFile, mode, root, @@ -35,6 +35,10 @@ async function extractConfig({ "production" // default NODE_ENV ); + return viteConfig; +} + +async function extractRemixConfig(viteConfig: Vite.ResolvedConfig) { let remixConfig = viteConfig[ "__remixPluginResolvedConfig" as keyof typeof viteConfig ] as ResolvedVitePluginConfig | undefined; @@ -43,7 +47,7 @@ async function extractConfig({ process.exit(1); } - return { remixConfig, viteConfig }; + return remixConfig; } function getAddressableRoutes(routes: RouteManifest): ConfigRoute[] { @@ -110,7 +114,7 @@ async function getServerBuilds(remixConfig: ResolvedVitePluginConfig): Promise<{ rootDirectory, appDirectory, } = remixConfig; - let serverBuildDirectory = getServerBuildDirectory(remixConfig); + let serverBuildRootDirectory = getServerBuildRootDirectory(remixConfig); if (!serverBundles) { return { serverBuilds: [{ ssr: true }], @@ -160,7 +164,7 @@ async function getServerBuilds(remixConfig: ResolvedVitePluginConfig): Promise<{ let relativeServerBundleDirectory = path.relative( rootDirectory, - path.join(serverBuildDirectory, serverBundleId) + path.join(serverBuildRootDirectory, serverBundleId) ); let serverBuildConfig = serverBundleBuildConfigById.get(serverBundleId); if (!serverBuildConfig) { @@ -198,21 +202,21 @@ async function getServerBuilds(remixConfig: ResolvedVitePluginConfig): Promise<{ }; } -async function cleanServerBuildDirectory( +async function cleanServerBuildRootDirectory( viteConfig: Vite.ResolvedConfig, remixConfig: ResolvedVitePluginConfig ) { - let serverBuildDirectory = getServerBuildDirectory(remixConfig); + let serverBuildRootDirectory = getServerBuildRootDirectory(remixConfig); let isWithinRoot = () => { let relativePath = path.relative( remixConfig.rootDirectory, - serverBuildDirectory + serverBuildRootDirectory ); return !relativePath.startsWith("..") && !path.isAbsolute(relativePath); }; if (viteConfig.build.emptyOutDir ?? isWithinRoot()) { - await fse.remove(serverBuildDirectory); + await fse.remove(serverBuildRootDirectory); } } @@ -245,11 +249,8 @@ export async function build( // so it can be accessed synchronously via `importViteEsmSync` await preloadViteEsm(); - let { remixConfig, viteConfig } = await extractConfig({ - configFile, - mode, - root, - }); + let viteConfig = await resolveViteConfig({ configFile, mode, root }); + let remixConfig = await extractRemixConfig(viteConfig); let vite = await import("vite"); @@ -275,7 +276,7 @@ export async function build( // output directories, we need to clean the root server build directory // ourselves rather than relying on Vite to do it, otherwise you can end up // with stale server bundle directories in your build output - await cleanServerBuildDirectory(viteConfig, remixConfig); + await cleanServerBuildRootDirectory(viteConfig, remixConfig); // Run the Vite client build first await viteBuild({ ssr: false }); diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index fdd4ffa45b..2fe611d2ef 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -197,7 +197,6 @@ export type ResolvedVitePluginConfig = Pick< buildDirectory: string; manifest: boolean; serverBuildFile: string; - serverBundleId?: string; serverBundles?: ServerBundlesFunction; ssr: boolean; }; @@ -207,6 +206,18 @@ export type ServerBundleBuildConfig = { serverBundleId: string; }; +type BuildContext = + | { + isSsrBuild: false; + getBrowserManifest?: never; + serverBundleId?: never; + } + | { + isSsrBuild: true; + getBrowserManifest: () => Promise; + serverBundleId: string | undefined; + }; + let serverBuildId = VirtualModule.id("server-build"); let serverManifestId = VirtualModule.id("server-manifest"); let browserManifestId = VirtualModule.id("browser-manifest"); @@ -405,15 +416,20 @@ const getServerBundleBuildConfig = ( return viteUserConfig.__remixServerBundleBuildConfig as ServerBundleBuildConfig; }; -export let getServerBuildDirectory = (remixConfig: ResolvedVitePluginConfig) => +let getServerBuildDirectory = ( + remixConfig: ResolvedVitePluginConfig, + { serverBundleId }: Pick +) => path.join( remixConfig.buildDirectory, "server", - ...(typeof remixConfig.serverBundleId === "string" - ? [remixConfig.serverBundleId] - : []) + ...(typeof serverBundleId === "string" ? [serverBundleId] : []) ); +export let getServerBuildRootDirectory = ( + remixConfig: ResolvedVitePluginConfig +) => getServerBuildDirectory(remixConfig, { serverBundleId: undefined }); + let getClientBuildDirectory = (remixConfig: ResolvedVitePluginConfig) => path.join(remixConfig.buildDirectory, "client"); @@ -424,9 +440,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { let viteConfig: Vite.ResolvedConfig | undefined; let cssModulesManifest: Record = {}; - let ssrBuildContext: - | { isSsrBuild: false } - | { isSsrBuild: true; getBrowserManifest: () => Promise }; + let buildContext: BuildContext; let viteChildCompiler: Vite.ViteDevServer | null = null; @@ -507,10 +521,8 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { // For server bundle builds, override the relevant config. This lets us run // multiple server builds with each one targeting a subset of routes. - let serverBundleId: string | undefined = undefined; if (serverBundleBuildConfig) { routes = serverBundleBuildConfig.routes; - serverBundleId = serverBundleBuildConfig.serverBundleId; } let resolvedRemixConfig: ResolvedVitePluginConfig = { @@ -525,7 +537,6 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { rootDirectory, routes, serverBuildFile, - serverBundleId, serverBundles, serverModuleFormat, ssr, @@ -534,6 +545,22 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { return resolvedRemixConfig; }; + let resolveBuildContext = (viteConfigEnv: Vite.ConfigEnv): BuildContext => { + let buildContext: BuildContext = + viteConfigEnv.isSsrBuild && viteCommand === "build" + ? { + isSsrBuild: true, + getBrowserManifest: createBrowserManifestForBuild, + serverBundleId: + getServerBundleBuildConfig(viteUserConfig)?.serverBundleId, + } + : { + isSsrBuild: false, + }; + + return buildContext; + }; + let getServerEntry = async () => { return ` import * as entryServer from ${JSON.stringify( @@ -708,6 +735,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { viteCommand = viteConfigEnv.command; remixConfig = await resolvePluginConfig(); + buildContext = resolveBuildContext(viteConfigEnv); Object.assign( process.env, @@ -796,7 +824,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { ssrEmitAssets: true, copyPublicDir: false, // Assets in the public directory are only used by the client manifest: true, // We need the manifest to detect SSR-only assets - outDir: getServerBuildDirectory(remixConfig), + outDir: getServerBuildDirectory(remixConfig, buildContext), rollupOptions: { ...viteUserConfig.build?.rollupOptions, preserveEntrySignatures: "exports-only", @@ -816,16 +844,6 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { viteConfig = resolvedViteConfig; - ssrBuildContext = - viteConfig.build.ssr && viteCommand === "build" - ? { - isSsrBuild: true, - getBrowserManifest: createBrowserManifestForBuild, - } - : { - isSsrBuild: false, - }; - // We load the same Vite config file again for the child compiler so // that both parent and child compiler's plugins have independent state. // If we re-used the `viteUserConfig.plugins` array for the child @@ -846,7 +864,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { { command: viteConfig.command, mode: viteConfig.mode, - isSsrBuild: ssrBuildContext.isSsrBuild, + isSsrBuild: buildContext.isSsrBuild, }, viteConfig.configFile ); @@ -1010,15 +1028,18 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { // After the SSR build is finished, we inspect the Vite manifest for // the SSR build and move server-only assets to client assets directory async handler() { - if (!ssrBuildContext.isSsrBuild) { + if (!buildContext.isSsrBuild) { return; } invariant(viteConfig); let { serverBuildFile, rootDirectory } = remixConfig; - let serverBuildDirectory = getServerBuildDirectory(remixConfig); let clientBuildDirectory = getClientBuildDirectory(remixConfig); + let serverBuildDirectory = getServerBuildDirectory( + remixConfig, + buildContext + ); let ssrViteManifest = await loadViteManifest(serverBuildDirectory); let clientViteManifest = await loadViteManifest(clientBuildDirectory); @@ -1105,8 +1126,8 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { return await getServerEntry(); } case VirtualModule.resolve(serverManifestId): { - let browserManifest = ssrBuildContext.isSsrBuild - ? await ssrBuildContext.getBrowserManifest() + let browserManifest = buildContext.isSsrBuild + ? await buildContext.getBrowserManifest() : await getBrowserManifestForDev(); return `export default ${jsesc(browserManifest, { es6: true })};`;