Skip to content

Commit

Permalink
Handle custom envDir in Vite config (#12969)
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish authored Feb 9, 2025
1 parent 033bc34 commit 1923f4b
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 58 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-buses-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-router/dev": patch
---

Handle custom `envDir` in Vite config
11 changes: 9 additions & 2 deletions integration/helpers/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,14 @@ export const reactRouterConfig = ({
`;
};

type ViteConfigArgs = {
port: number;
fsAllow?: string[];
envDir?: string;
};

export const viteConfig = {
server: async (args: { port: number; fsAllow?: string[] }) => {
server: async (args: ViteConfigArgs) => {
let { port, fsAllow } = args;
let hmrPort = await getPort();
let text = dedent`
Expand All @@ -72,14 +78,15 @@ export const viteConfig = {
`;
return text;
},
basic: async (args: { port: number; fsAllow?: string[] }) => {
basic: async (args: ViteConfigArgs) => {
return dedent`
import { reactRouter } from "@react-router/dev/vite";
import { envOnlyMacros } from "vite-env-only";
import tsconfigPaths from "vite-tsconfig-paths";
export default {
${await viteConfig.server(args)}
envDir: ${args.envDir ? `"${args.envDir}"` : "undefined"},
plugins: [
reactRouter(),
envOnlyMacros(),
Expand Down
150 changes: 95 additions & 55 deletions integration/vite-dotenv-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,73 +8,113 @@ import {
viteConfig,
} from "./helpers/vite.js";

let files = {
".env": `
ENV_VAR_FROM_DOTENV_FILE=Content from .env file
`,
"app/routes/dotenv.tsx": String.raw`
import { useState, useEffect } from "react";
import { useLoaderData } from "react-router";
export const loader = () => {
return {
loaderContent: process.env.ENV_VAR_FROM_DOTENV_FILE,
}
}
let getFiles = async ({ envDir, port }: { envDir?: string; port: number }) => {
let envPath = `${envDir ? `${envDir}/` : ""}.env`;

export default function DotenvRoute() {
const { loaderContent } = useLoaderData();
return {
"vite.config.js": await viteConfig.basic({ port, envDir }),
"server.mjs": EXPRESS_SERVER({ port }),
[envPath]: `
ENV_VAR_FROM_DOTENV_FILE=Content from ${envPath} file
`,
"app/routes/dotenv.tsx": String.raw`
import { useState, useEffect } from "react";
import { useLoaderData } from "react-router";
const [clientContent, setClientContent] = useState('');
useEffect(() => {
try {
setClientContent("process.env.ENV_VAR_FROM_DOTENV_FILE shouldn't be available on the client, found: " + process.env.ENV_VAR_FROM_DOTENV_FILE);
} catch (err) {
setClientContent("process.env.ENV_VAR_FROM_DOTENV_FILE not available on the client, which is a good thing");
export const loader = () => {
return {
loaderContent: process.env.ENV_VAR_FROM_DOTENV_FILE,
}
}, []);
return <>
<div data-dotenv-route-loader-content>{loaderContent}</div>
<div data-dotenv-route-client-content>{clientContent}</div>
</>
}
`,
}
export default function DotenvRoute() {
const { loaderContent } = useLoaderData();
const [clientContent, setClientContent] = useState('');
useEffect(() => {
try {
setClientContent("process.env.ENV_VAR_FROM_DOTENV_FILE shouldn't be available on the client, found: " + process.env.ENV_VAR_FROM_DOTENV_FILE);
} catch (err) {
setClientContent("process.env.ENV_VAR_FROM_DOTENV_FILE not available on the client, which is a good thing");
}
}, []);
return <>
<div data-dotenv-route-loader-content>{loaderContent}</div>
<div data-dotenv-route-client-content>{clientContent}</div>
</>
}
`,
};
};

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

test.beforeAll(async () => {
port = await getPort();
cwd = await createProject({
"vite.config.js": await viteConfig.basic({ port }),
"server.mjs": EXPRESS_SERVER({ port }),
...files,
test.describe("Vite .env", () => {
test.describe("defaults", async () => {
let port: number;
let cwd: string;
let stop: () => void;

test.beforeAll(async () => {
port = await getPort();
cwd = await createProject(await getFiles({ port }));
stop = await customDev({ cwd, port });
});
test.afterAll(() => stop());

test("express", async ({ page }) => {
let pageErrors: unknown[] = [];
page.on("pageerror", (error) => pageErrors.push(error));

await page.goto(`http://localhost:${port}/dotenv`, {
waitUntil: "networkidle",
});
expect(pageErrors).toEqual([]);

let loaderContent = page.locator("[data-dotenv-route-loader-content]");
await expect(loaderContent).toHaveText("Content from .env file");

let clientContent = page.locator("[data-dotenv-route-client-content]");
await expect(clientContent).toHaveText(
"process.env.ENV_VAR_FROM_DOTENV_FILE not available on the client, which is a good thing"
);

expect(pageErrors).toEqual([]);
});
stop = await customDev({ cwd, port });
});
test.afterAll(() => stop());

test("Vite / Load context / express", async ({ page }) => {
let pageErrors: unknown[] = [];
page.on("pageerror", (error) => pageErrors.push(error));
test.describe("custom env dir", async () => {
let port: number;
let cwd: string;
let stop: () => void;

await page.goto(`http://localhost:${port}/dotenv`, {
waitUntil: "networkidle",
test.beforeAll(async () => {
const envDir = "custom-env-dir";
port = await getPort();
cwd = await createProject(await getFiles({ envDir, port }));
stop = await customDev({ cwd, port });
});
expect(pageErrors).toEqual([]);
test.afterAll(() => stop());

test("express", async ({ page }) => {
let pageErrors: unknown[] = [];
page.on("pageerror", (error) => pageErrors.push(error));

let loaderContent = page.locator("[data-dotenv-route-loader-content]");
await expect(loaderContent).toHaveText("Content from .env file");
await page.goto(`http://localhost:${port}/dotenv`, {
waitUntil: "networkidle",
});
expect(pageErrors).toEqual([]);

let clientContent = page.locator("[data-dotenv-route-client-content]");
await expect(clientContent).toHaveText(
"process.env.ENV_VAR_FROM_DOTENV_FILE not available on the client, which is a good thing"
);
let loaderContent = page.locator("[data-dotenv-route-loader-content]");
await expect(loaderContent).toHaveText(
"Content from custom-env-dir/.env file"
);

expect(pageErrors).toEqual([]);
let clientContent = page.locator("[data-dotenv-route-client-content]");
await expect(clientContent).toHaveText(
"process.env.ENV_VAR_FROM_DOTENV_FILE not available on the client, which is a good thing"
);

expect(pageErrors).toEqual([]);
});
});
});
2 changes: 1 addition & 1 deletion packages/react-router-dev/vite/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
process.env,
vite.loadEnv(
viteConfigEnv.mode,
ctx.rootDirectory,
viteUserConfig.envDir ?? ctx.rootDirectory,
// We override default prefix of "VITE_" with a blank string since
// we're targeting the server, so we want to load all environment
// variables, not just those explicitly marked for the client
Expand Down

0 comments on commit 1923f4b

Please sign in to comment.