From e941c68cc9277bc9b61899b16a26c0db0cdea413 Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Mon, 4 Mar 2024 14:20:54 +1100 Subject: [PATCH] Vite: Fix `server.fs.allow` with default entry --- .changeset/tall-frogs-visit.md | 5 +++ integration/helpers/vite.ts | 12 ++++-- integration/vite-server-fs-allow-test.ts | 51 ++++++++++++++++++++++++ packages/remix-dev/vite/plugin.ts | 15 +++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 .changeset/tall-frogs-visit.md create mode 100644 integration/vite-server-fs-allow-test.ts diff --git a/.changeset/tall-frogs-visit.md b/.changeset/tall-frogs-visit.md new file mode 100644 index 00000000000..d727a4f15e8 --- /dev/null +++ b/.changeset/tall-frogs-visit.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +Vite: Fix error when using Vite's `server.fs.allow` option without a client entry file diff --git a/integration/helpers/vite.ts b/integration/helpers/vite.ts index 72ef162fde5..0f66984a707 100644 --- a/integration/helpers/vite.ts +++ b/integration/helpers/vite.ts @@ -17,14 +17,20 @@ const remixBin = "node_modules/@remix-run/dev/dist/cli.js"; const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); export const viteConfig = { - server: async (args: { port: number }) => { + server: async (args: { port: number; fsAllow?: string[] }) => { + let { port, fsAllow } = args; let hmrPort = await getPort(); let text = dedent` - server: { port: ${args.port}, strictPort: true, hmr: { port: ${hmrPort} } }, + server: { + port: ${port}, + strictPort: true, + hmr: { port: ${hmrPort} }, + fs: { allow: ${fsAllow ? JSON.stringify(fsAllow) : "undefined"} } + }, `; return text; }, - basic: async (args: { port: number }) => { + basic: async (args: { port: number; fsAllow?: string[] }) => { return dedent` import { vitePlugin as remix } from "@remix-run/dev"; diff --git a/integration/vite-server-fs-allow-test.ts b/integration/vite-server-fs-allow-test.ts new file mode 100644 index 00000000000..e38bc2cc0dd --- /dev/null +++ b/integration/vite-server-fs-allow-test.ts @@ -0,0 +1,51 @@ +import { test, expect } from "@playwright/test"; +import getPort from "get-port"; + +import { + createProject, + customDev, + EXPRESS_SERVER, + viteConfig, +} from "./helpers/vite.js"; + +let files = { + "app/routes/test-route.tsx": String.raw` + export default function IndexRoute() { + return
Hello world
+ } + `, +}; + +test.describe(async () => { + let port: number; + let cwd: string; + let stop: () => void; + + test.beforeAll(async () => { + port = await getPort(); + cwd = await createProject({ + "vite.config.ts": await viteConfig.basic({ port, fsAllow: ["app"] }), + "server.mjs": EXPRESS_SERVER({ port }), + ...files, + }); + stop = await customDev({ cwd, port }); + }); + test.afterAll(() => stop()); + + test("Vite / server.fs.allow / works with basic allow list", async ({ + page, + }) => { + let pageErrors: unknown[] = []; + page.on("pageerror", (error) => pageErrors.push(error)); + + await page.goto(`http://localhost:${port}/test-route`, { + waitUntil: "networkidle", + }); + expect(pageErrors).toEqual([]); + + let testContent = page.locator("#test"); + await expect(testContent).toBeAttached(); + + expect(pageErrors).toEqual([]); + }); +}); diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index 51030788e87..7dcce6a9f6d 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -497,6 +497,12 @@ export let getServerBuildDirectory = (ctx: RemixPluginContext) => let getClientBuildDirectory = (remixConfig: ResolvedVitePluginConfig) => path.join(remixConfig.buildDirectory, "client"); +let defaultEntriesDir = path.resolve(__dirname, "..", "config", "defaults"); +let defaultEntries = fse + .readdirSync(defaultEntriesDir) + .map((filename) => path.join(defaultEntriesDir, filename)); +invariant(defaultEntries.length > 0, "No default entries found"); + let mergeRemixConfig = (...configs: VitePluginConfig[]): VitePluginConfig => { let reducer = ( configA: VitePluginConfig, @@ -1087,6 +1093,15 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { }, base: viteUserConfig.base, + // When consumer provides an allow list for files that can be read by + // the server, ensure that Remix's default entry files are included. + // If we don't do this and a default entry file is used, the server + // will throw an error that the file is not allowed to be read. + // https://vitejs.dev/config/server-options#server-fs-allow + server: viteUserConfig.server?.fs?.allow + ? { fs: { allow: defaultEntries } } + : undefined, + // Vite config options for building ...(viteCommand === "build" ? {