Skip to content

Commit

Permalink
feat(remix-dev/vite): add buildDirectory and manifest options (remix-…
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish authored Jan 22, 2024
1 parent bce34c1 commit 10fcf5b
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 137 deletions.
9 changes: 9 additions & 0 deletions .changeset/bright-days-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@remix-run/dev": patch
---

Vite: Add `manifest` option to Vite plugin to enable writing a `manifest.json` file to the build directory

**This is a breaking change for consumers of the Vite plugin's "server bundles" feature.**

The `build/server/bundles.json` file has been superseded by the more general `build/manifest.json`. While the old server bundles manifest was always written to disk when generating server bundles, the build manifest file must be explicitly enabled via the `manifest` option.
24 changes: 24 additions & 0 deletions .changeset/thirty-coins-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
"@remix-run/dev": patch
---

Vite: Add new `buildDirectory` option with a default value of `"build"`. This replaces the old `assetsBuildDirectory` and `serverBuildDirectory` options which defaulted to `"build/client"` and `"build/server"` respectively.

**This is a breaking change for consumers of the Vite plugin that were using the `assetsBuildDirectory` and `serverBuildDirectory` options.**

The Remix Vite plugin now builds into a single directory containing `client` and `server` directories. If you've customized your build output directories, you'll need to migrate to the new `buildDirectory` option, e.g.

```diff
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";

export default defineConfig({
plugins: [
remix({
- serverBuildDirectory: "dist/server",
- assetsBuildDirectory: "dist/client",
+ buildDirectory: "dist",
})
],
});
```
23 changes: 12 additions & 11 deletions docs/future/vite.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ All other bundling-related options are now [configured with Vite][vite-config].
The following subset of Remix config options are supported:

- [appDirectory][app-directory]
- [assetsBuildDirectory][assets-build-directory]
- [ignoredRouteFiles][ignored-route-files]
- [publicPath][public-path]
- [routes][routes]
Expand All @@ -63,9 +62,13 @@ The Vite plugin also accepts the following additional options:

A function for adapting the build output and/or development environment for different hosting providers.

#### serverBuildDirectory
#### buildDirectory

The path to the server build directory, relative to the project root. Defaults to `"build/server"`.
The path to the build directory, relative to the project root. Defaults to `"build"`.

#### manifest

Whether to write a `manifest.json` file to the build directory. Defaults to `false`.

#### serverBuildFile

Expand All @@ -75,6 +78,8 @@ The name of the server file generated in the server build directory. Defaults to

A function for assigning addressable routes to [server bundles][server-bundles].

You may also want to enable the `manifest` option since, when server bundles are enabled, it contains mappings between routes and server bundles.

## Splitting up client and server code

Remix lets you write code that [runs on both the client and the server][server-vs-client].
Expand Down Expand Up @@ -130,18 +135,14 @@ export const PostPreview = ({ title, description }) => {

## New build output paths

There is a notable difference with the way Vite manages the `public` directory compared to the existing Remix compiler. During the build, Vite copies files from the `public` directory into `build/client`, whereas the Remix compiler left the `public` directory untouched and used a subdirectory (`public/build`) as the client build directory.
There is a notable difference with the way Vite manages the `public` directory compared to the existing Remix compiler. Vite copies files from the `public` directory into the client build directory, whereas the Remix compiler left the `public` directory untouched and used a subdirectory (`public/build`) as the client build directory.

In order to align the default Remix project structure with the way Vite works, the build output paths have been changed.

- The server is now compiled into `build/server` by default.
- The client is now compiled into `build/client` by default.
In order to align the default Remix project structure with the way Vite works, the build output paths have been changed. There is now a single `buildDirectory` option that defaults to `"build"`, replacing the separate `assetsBuildDirectory` and `serverBuildDirectory` options. This means that, by default, the server is now compiled into `build/server` and the client is now compiled into `build/client`.

This means that the following configuration defaults have been changed:
This also means that the following configuration defaults have been changed:

- [assetsBuildDirectory][assets-build-directory] defaults to `"build/client"` rather than `"public/build"`
- [publicPath][public-path] defaults to `"/"` rather than `"/build/"`
- [serverBuildPath][server-build-path] has been split into `serverBuildDirectory` and `serverBuildFile`, with the equivalent default for `serverBuildDirectory` being `"build/server"` rather than `"build"`
- [serverBuildPath][server-build-path] has been replaced by `serverBuildFile` which defaults to `"index.js"`. This file will be written into the server directory within your configured `buildDirectory`.

## Additional features & plugins

Expand Down
32 changes: 10 additions & 22 deletions integration/vite-adapter-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test.describe(async () => {
return normalizePath(pathname).startsWith(normalizePath(cwd));
}

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

Expand Down Expand Up @@ -57,35 +57,24 @@ test.describe(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")
);
let { remixConfig } = buildEndArgs;

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

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

expect(buildEndArgs).toEqual({
assetsBuildDirectory: "build/client",
serverBuildDirectory: "build/server",
serverBuildFile: "index.js",
unstable_serverBundlesManifest: {
remixConfig: {
buildDirectory: "build",
serverBuildFile: "index.js",
unstable_ssr: true,
},
buildManifest: {
routeIdToServerBundleId: {
"routes/_index": "user-options--adapter-options",
},
Expand All @@ -109,7 +98,6 @@ test.describe(async () => {
},
},
},
unstable_ssr: true,
});
});
});
95 changes: 95 additions & 0 deletions integration/vite-build-manifest-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import fs from "node:fs";
import path from "node:path";
import { test, expect } from "@playwright/test";
import getPort from "get-port";

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

function createRoute(path: string) {
return {
[`app/routes/${path}`]: `
export default function Route() {
return <p>Path: ${path}</p>;
}
`,
};
}

const TEST_ROUTES = [
"_index.tsx",
"parent-route.tsx",
"parent-route.child-route.tsx",
];

const files = {
"app/root.tsx": `
import { Links, Meta, Outlet, Scripts, LiveReload } from "@remix-run/react";
export default function Root() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
<LiveReload />
</body>
</html>
);
}
`,
...Object.assign({}, ...TEST_ROUTES.map(createRoute)),
};

test.describe(() => {
let cwd: string;
let devPort: number;

test.beforeAll(async () => {
devPort = await getPort();
cwd = await createProject({
"vite.config.ts": await VITE_CONFIG({
port: devPort,
pluginOptions: "{ manifest: true }",
}),
...files,
});

await viteBuild({ cwd });
});

test("Vite / build manifest", async () => {
expect(
JSON.parse(fs.readFileSync(path.join(cwd, "build/manifest.json"), "utf8"))
).toEqual({
routes: {
root: {
file: "root.tsx",
id: "root",
path: "",
},
"routes/_index": {
file: "routes/_index.tsx",
id: "routes/_index",
index: true,
parentId: "root",
},
"routes/parent-route": {
file: "routes/parent-route.tsx",
id: "routes/parent-route",
parentId: "root",
path: "parent-route",
},
"routes/parent-route.child-route": {
file: "routes/parent-route.child-route.tsx",
id: "routes/parent-route.child-route",
parentId: "routes/parent-route",
path: "child-route",
},
},
});
});
});
5 changes: 3 additions & 2 deletions integration/vite-server-bundles-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function createRoute(path: string) {
return {
[`app/routes/${path}`]: `
${ROUTE_FILE_COMMENT}
import { Links, Meta, Outlet, Scripts, LiveReload } from "@remix-run/react";
import { Outlet } from "@remix-run/react";
import { useState, useEffect } from "react";
export default function Route() {
Expand Down Expand Up @@ -121,6 +121,7 @@ test.describe(() => {
"vite.config.ts": await VITE_CONFIG({
port: devPort,
pluginOptions: `{
manifest: true,
unstable_serverBundles: async ({ branch }) => {
// Smoke test to ensure we can read the route files via 'route.file'
await Promise.all(branch.map(async (route) => {
Expand Down Expand Up @@ -291,7 +292,7 @@ test.describe(() => {
test("Vite / server bundles / build / manifest", async () => {
expect(
JSON.parse(
fs.readFileSync(path.join(cwd, "build/server/bundles.json"), "utf8")
fs.readFileSync(path.join(cwd, "build/manifest.json"), "utf8")
)
).toEqual({
serverBundles: {
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export * as cli from "./cli/index";
export type { Manifest as AssetsManifest } from "./manifest";
export { getDependenciesToBundle } from "./dependencies";
export type {
Unstable_ServerBundlesManifest,
Unstable_BuildManifest,
Unstable_VitePluginAdapter,
} from "./vite";
export { unstable_vitePlugin } from "./vite";
Loading

0 comments on commit 10fcf5b

Please sign in to comment.