Skip to content

Commit

Permalink
feat(remix-dev/vite): add Vite plugin adapter API (#8514)
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish committed Jan 18, 2024
1 parent cf2a649 commit 9f672c3
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 164 deletions.
115 changes: 115 additions & 0 deletions integration/vite-adapter-test.ts
@@ -0,0 +1,115 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { test, expect } from "@playwright/test";
import { normalizePath } from "vite";
import getPort from "get-port";

import {
createProject,
viteDev,
viteBuild,
VITE_CONFIG,
} from "./helpers/vite.js";

test.describe(async () => {
let port: number;
let cwd: string;
let stop: () => void;

function pathStartsWithCwd(pathname: string) {
return normalizePath(pathname).startsWith(normalizePath(cwd));
}

function pathRelativeToCwd(pathname: string) {
return normalizePath(path.relative(cwd, pathname));
}

test.beforeAll(async () => {
port = await getPort();
cwd = await createProject({
"vite.config.ts": await VITE_CONFIG({
port,
pluginOptions: `
{
adapter: async ({ remixConfig }) => ({
unstable_serverBundles(...args) {
// This lets us assert that user options are passed to adapter options hook
return remixConfig.unstable_serverBundles?.(...args) + "--adapter-options";
},
async buildEnd(args) {
let fs = await import("node:fs/promises");
await fs.writeFile("BUILD_END_ARGS.json", JSON.stringify(args, null, 2), "utf-8");
}
}),
unstable_serverBundles() {
return "user-options";
}
},
`,
}),
});
stop = await viteDev({ cwd, port });
});
test.afterAll(() => stop());

test("Vite / adapter / unstable_serverBundles and buildEnd hooks", async () => {
let { status } = viteBuild({ cwd });
expect(status).toBe(0);

expect(
Object.keys(
JSON.parse(
fs.readFileSync(path.join(cwd, "build/server/bundles.json"), "utf8")
).serverBundles
)
).toEqual(["user-options--adapter-options"]);

let buildEndArgs: any = JSON.parse(
fs.readFileSync(path.join(cwd, "BUILD_END_ARGS.json"), "utf8")
);

// Before rewriting to relative paths, assert that paths are absolute within cwd
expect(pathStartsWithCwd(buildEndArgs.serverBuildDirectory)).toBe(true);
expect(pathStartsWithCwd(buildEndArgs.assetsBuildDirectory)).toBe(true);

// Rewrite path args to be relative and normalized for snapshot test
buildEndArgs.serverBuildDirectory = pathRelativeToCwd(
buildEndArgs.serverBuildDirectory
);
buildEndArgs.assetsBuildDirectory = pathRelativeToCwd(
buildEndArgs.assetsBuildDirectory
);

expect(buildEndArgs).toEqual({
assetsBuildDirectory: "build/client",
serverBuildDirectory: "build/server",
serverBuildFile: "index.js",
unstable_serverBundlesManifest: {
routeIdToServerBundleId: {
"routes/_index": "user-options--adapter-options",
},
routes: {
root: {
file: "app/root.tsx",
id: "root",
path: "",
},
"routes/_index": {
file: "app/routes/_index.tsx",
id: "routes/_index",
index: true,
parentId: "root",
},
},
serverBundles: {
"user-options--adapter-options": {
file: "build/server/user-options--adapter-options/index.js",
id: "user-options--adapter-options",
},
},
},
unstable_ssr: true,
});
});
});
5 changes: 4 additions & 1 deletion packages/remix-dev/index.ts
Expand Up @@ -6,5 +6,8 @@ export * as cli from "./cli/index";

export type { Manifest as AssetsManifest } from "./manifest";
export { getDependenciesToBundle } from "./dependencies";
export type { Unstable_ServerBundlesManifest } from "./vite";
export type {
Unstable_ServerBundlesManifest,
Unstable_VitePluginAdapter,
} from "./vite";
export { unstable_vitePlugin } from "./vite";
57 changes: 35 additions & 22 deletions packages/remix-dev/vite/build.ts
Expand Up @@ -4,8 +4,9 @@ import fse from "fs-extra";
import colors from "picocolors";

import {
type ResolvedRemixVitePluginConfig,
type ResolvedVitePluginConfig,
type ServerBuildConfig,
type ServerBundlesManifest,
configRouteToBranchRoute,
} from "./plugin";
import type { ConfigRoute, RouteManifest } from "../config/routes";
Expand All @@ -32,15 +33,15 @@ async function extractConfig({
"production" // default NODE_ENV
);

let pluginConfig = viteConfig[
let remixConfig = viteConfig[
"__remixPluginResolvedConfig" as keyof typeof viteConfig
] as ResolvedRemixVitePluginConfig | undefined;
if (!pluginConfig) {
] as ResolvedVitePluginConfig | undefined;
if (!remixConfig) {
console.error(colors.red("Remix Vite plugin not found in Vite config"));
process.exit(1);
}

return { pluginConfig, viteConfig };
return { remixConfig, viteConfig };
}

function getAddressableRoutes(routes: RouteManifest): ConfigRoute[] {
Expand Down Expand Up @@ -83,25 +84,14 @@ function getRouteBranch(routes: RouteManifest, routeId: string) {
return branch.reverse();
}

export type ServerBundlesManifest = {
serverBundles: {
[serverBundleId: string]: {
id: string;
file: string;
};
};
routeIdToServerBundleId: Record<string, string>;
routes: RouteManifest;
};

async function getServerBuilds({
routes,
serverBuildDirectory,
serverBuildFile,
serverBundles,
rootDirectory,
appDirectory,
}: ResolvedRemixVitePluginConfig): Promise<{
}: ResolvedVitePluginConfig): Promise<{
serverBuilds: ServerBuildConfig[];
serverBundlesManifest?: ServerBundlesManifest;
}> {
Expand Down Expand Up @@ -178,7 +168,7 @@ async function getServerBuilds({

async function cleanServerBuildDirectory(
viteConfig: Vite.ResolvedConfig,
{ rootDirectory, serverBuildDirectory }: ResolvedRemixVitePluginConfig
{ rootDirectory, serverBuildDirectory }: ResolvedVitePluginConfig
) {
let isWithinRoot = () => {
let relativePath = path.relative(rootDirectory, serverBuildDirectory);
Expand Down Expand Up @@ -219,7 +209,7 @@ export async function build(
// so it can be accessed synchronously via `importViteEsmSync`
await preloadViteEsm();

let { pluginConfig, viteConfig } = await extractConfig({
let { remixConfig, viteConfig } = await extractConfig({
configFile,
mode,
root,
Expand Down Expand Up @@ -247,23 +237,46 @@ 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, pluginConfig);
await cleanServerBuildDirectory(viteConfig, remixConfig);

// Run the Vite client build first
await viteBuild();

// Then run Vite SSR builds in parallel
let { serverBuilds, serverBundlesManifest } = await getServerBuilds(
pluginConfig
remixConfig
);

await Promise.all(serverBuilds.map(viteBuild));

if (serverBundlesManifest) {
await fse.writeFile(
path.join(pluginConfig.serverBuildDirectory, "bundles.json"),
path.join(remixConfig.serverBuildDirectory, "bundles.json"),
JSON.stringify(serverBundlesManifest, null, 2),
"utf-8"
);
}

let {
isSpaMode,
assetsBuildDirectory,
serverBuildDirectory,
serverBuildFile,
} = remixConfig;

// Should this already be absolute on the resolved config object?
// In the meantime, we make it absolute before passing to adapter hooks
serverBuildDirectory = path.resolve(root, serverBuildDirectory);

await remixConfig.adapter?.buildEnd?.({
// Since this is public API, these properties need to mirror the options
// passed to the Remix plugin. This means we need to translate our internal
// names back to their original public counterparts. It's probably worth
// aligning these internally so we don't need this translation layer.
assetsBuildDirectory,
serverBuildDirectory,
serverBuildFile,
unstable_serverBundlesManifest: serverBundlesManifest,
unstable_ssr: !isSpaMode,
});
}
5 changes: 4 additions & 1 deletion packages/remix-dev/vite/index.ts
Expand Up @@ -2,7 +2,10 @@
// don't need to have Vite installed as a peer dependency. Only types should
// be imported at the top level.
import type { RemixVitePlugin } from "./plugin";
export type { ServerBundlesManifest as Unstable_ServerBundlesManifest } from "./build";
export type {
ServerBundlesManifest as Unstable_ServerBundlesManifest,
VitePluginAdapter as Unstable_VitePluginAdapter,
} from "./plugin";

export const unstable_vitePlugin: RemixVitePlugin = (...args) => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
Expand Down

0 comments on commit 9f672c3

Please sign in to comment.