Skip to content

Commit 8e87754

Browse files
[vite-plugin] make sure that .dev.vars files trigger a full reload (#8685)
* [vite-plugin] make sure that `.dev.vars` files trigger a full reload * Apply suggestions from code review Co-authored-by: James Opstad <[email protected]> * use `.toContains` instead of `.toMatch` --------- Co-authored-by: James Opstad <[email protected]>
1 parent a34b172 commit 8e87754

File tree

10 files changed

+176
-13
lines changed

10 files changed

+176
-13
lines changed

.changeset/blue-hats-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/vite-plugin": patch
3+
---
4+
5+
make sure that `.dev.vars` files trigger a full reload
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MY_SECRET = "secret A"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!.dev.vars

packages/vite-plugin-cloudflare/playground/config-changes/__tests__/config-changes.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ test.runIf(!isBuild)(
1515
await vi.waitFor(
1616
async () => {
1717
const revertedResponse = await getTextResponse();
18-
expect(revertedResponse).toBe('The value of MY_VAR is "one"');
18+
expect(revertedResponse).toContain('The value of MY_VAR is "one"');
1919
},
2020
{ timeout: 5000 }
2121
);
2222
});
2323

2424
const originalResponse = await getTextResponse();
25-
expect(originalResponse).toBe('The value of MY_VAR is "one"');
25+
expect(originalResponse).toContain('The value of MY_VAR is "one"');
2626

2727
const updatedWorkerConfig = JSON.stringify({
2828
...JSON.parse(originalWorkerConfig),
@@ -34,7 +34,7 @@ test.runIf(!isBuild)(
3434
await vi.waitFor(
3535
async () => {
3636
const updatedResponse = await getTextResponse();
37-
expect(updatedResponse).toBe('The value of MY_VAR is "two"');
37+
expect(updatedResponse).toContain('The value of MY_VAR is "two"');
3838
},
3939
{ timeout: 5000 }
4040
);
@@ -53,14 +53,14 @@ test.runIf(!isBuild)(
5353
await vi.waitFor(
5454
async () => {
5555
const revertedResponse = await getTextResponse();
56-
expect(revertedResponse).toBe('The value of MY_VAR is "one"');
56+
expect(revertedResponse).toContain('The value of MY_VAR is "one"');
5757
},
5858
{ timeout: 5000 }
5959
);
6060
});
6161

6262
const originalResponse = await getTextResponse();
63-
expect(originalResponse).toBe('The value of MY_VAR is "one"');
63+
expect(originalResponse).toContain('The value of MY_VAR is "one"');
6464

6565
const updatedWorkerConfig = JSON.stringify({
6666
...JSON.parse(originalWorkerConfig),
@@ -76,7 +76,7 @@ test.runIf(!isBuild)(
7676
expect(serverLogs.errors.join()).toMatch(
7777
/.*The provided Wrangler config main field .+? doesn't point to an existing file.*/
7878
);
79-
expect(newResponse).toBe('The value of MY_VAR is "one"');
79+
expect(newResponse).toContain('The value of MY_VAR is "one"');
8080
},
8181
{ timeout: 5000 }
8282
);
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
interface Env {
22
MY_VAR: string;
3+
MY_SECRET: string;
34
}
45

56
export default {
67
async fetch(_, env) {
7-
return new Response(`The value of MY_VAR is "${env.MY_VAR}"`);
8+
return new Response(
9+
`The value of MY_VAR is "${env.MY_VAR}" and the value of MY_SECRET is "${env.MY_SECRET}"`
10+
);
811
},
912
} satisfies ExportedHandler<Env>;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { cloudflare } from "@cloudflare/vite-plugin";
2+
import { defineConfig } from "vite";
3+
4+
export default defineConfig({
5+
mode: "with-specified-env",
6+
plugins: [cloudflare({ inspectorPort: false, persistState: false })],
7+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as fs from "node:fs";
2+
import * as path from "node:path";
3+
import { expect, test, vi } from "vitest";
4+
import { getJsonResponse, isBuild } from "../../__test-utils__";
5+
6+
test.runIf(!isBuild)(
7+
"successfully updates when a var is updated in a .dev.vars file",
8+
async ({ onTestFinished }) => {
9+
const dotDevDotVarsFilePath = path.join(__dirname, "../.dev.vars");
10+
const originalDotDevDotVars = fs.readFileSync(
11+
dotDevDotVarsFilePath,
12+
"utf-8"
13+
);
14+
15+
onTestFinished(async () => {
16+
fs.writeFileSync(dotDevDotVarsFilePath, originalDotDevDotVars);
17+
// We need to ensure that the original config is restored before the next test runs
18+
await vi.waitFor(
19+
async () => {
20+
expect(await getJsonResponse()).toEqual({
21+
"variables present in .dev.vars": {
22+
MY_DEV_VAR_A: "my .dev.vars variable A",
23+
MY_DEV_VAR_B: "my .dev.vars variable B",
24+
MY_DEV_VAR_C: "my .dev.vars variable C",
25+
},
26+
});
27+
},
28+
{ timeout: 5000 }
29+
);
30+
});
31+
32+
const originalResponse = await getJsonResponse();
33+
expect(originalResponse).toEqual({
34+
"variables present in .dev.vars": {
35+
MY_DEV_VAR_A: "my .dev.vars variable A",
36+
MY_DEV_VAR_B: "my .dev.vars variable B",
37+
MY_DEV_VAR_C: "my .dev.vars variable C",
38+
},
39+
});
40+
41+
const updatedDotDevDotVars = originalDotDevDotVars.replace(
42+
/my \.dev\.vars variable/g,
43+
"my .dev.vars UPDATED variable"
44+
);
45+
46+
fs.writeFileSync(dotDevDotVarsFilePath, updatedDotDevDotVars);
47+
await vi.waitFor(
48+
async () => {
49+
const updatedResponse = await getJsonResponse();
50+
expect(updatedResponse).toEqual({
51+
"variables present in .dev.vars": {
52+
MY_DEV_VAR_A: "my .dev.vars UPDATED variable A",
53+
MY_DEV_VAR_B: "my .dev.vars UPDATED variable B",
54+
MY_DEV_VAR_C: "my .dev.vars UPDATED variable C",
55+
},
56+
});
57+
},
58+
{ timeout: 5000 }
59+
);
60+
}
61+
);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as fs from "node:fs";
2+
import * as path from "node:path";
3+
import { expect, test, vi } from "vitest";
4+
import { getJsonResponse, isBuild } from "../../../__test-utils__";
5+
6+
test.runIf(!isBuild)(
7+
"successfully updates when a var is updated in a .dev.vars.staging file",
8+
async ({ onTestFinished }) => {
9+
const dotDevDotVarsFilePath = path.join(
10+
__dirname,
11+
"../../.dev.vars.staging"
12+
);
13+
const originalDotDevDotVars = fs.readFileSync(
14+
dotDevDotVarsFilePath,
15+
"utf-8"
16+
);
17+
18+
onTestFinished(async () => {
19+
fs.writeFileSync(dotDevDotVarsFilePath, originalDotDevDotVars);
20+
// We need to ensure that the original config is restored before the next test runs
21+
await vi.waitFor(
22+
async () => {
23+
expect(await getJsonResponse()).toEqual({
24+
"variables present in .dev.vars.staging": {
25+
MY_DEV_VAR_A: "my .dev.vars staging variable A",
26+
MY_DEV_VAR_B: "my .dev.vars staging variable B",
27+
},
28+
});
29+
},
30+
{ timeout: 5000 }
31+
);
32+
});
33+
34+
const originalResponse = await getJsonResponse();
35+
expect(originalResponse).toEqual({
36+
"variables present in .dev.vars.staging": {
37+
MY_DEV_VAR_A: "my .dev.vars staging variable A",
38+
MY_DEV_VAR_B: "my .dev.vars staging variable B",
39+
},
40+
});
41+
42+
const updatedDotDevDotVars = originalDotDevDotVars.replace(
43+
/my \.dev\.vars staging variable/g,
44+
"my .dev.vars UPDATED staging variable"
45+
);
46+
47+
fs.writeFileSync(dotDevDotVarsFilePath, updatedDotDevDotVars);
48+
await vi.waitFor(
49+
async () => {
50+
const updatedResponse = await getJsonResponse();
51+
expect(updatedResponse).toEqual({
52+
"variables present in .dev.vars.staging": {
53+
MY_DEV_VAR_A: "my .dev.vars UPDATED staging variable A",
54+
MY_DEV_VAR_B: "my .dev.vars UPDATED staging variable B",
55+
},
56+
});
57+
},
58+
{ timeout: 5000 }
59+
);
60+
}
61+
);

packages/vite-plugin-cloudflare/src/asset-config.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,15 @@ import type { Unstable_Config } from "wrangler";
2626
export function hasAssetsConfigChanged(
2727
resolvedPluginConfig: ResolvedPluginConfig,
2828
resolvedViteConfig: ResolvedConfig,
29-
changedFile: string
29+
changedFilePath: string
3030
) {
3131
if (!resolvedPluginConfig.experimental?.headersAndRedirectsDevModeSupport) {
3232
return false;
3333
}
34-
// Note that we must "resolve" the changed file since the path from Vite will not match Windows backslashes.
3534
return [
3635
getRedirectsConfigPath(resolvedViteConfig),
3736
getHeadersConfigPath(resolvedViteConfig),
38-
].includes(path.resolve(changedFile));
37+
].includes(changedFilePath);
3938
}
4039

4140
/**

packages/vite-plugin-cloudflare/src/index.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,16 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
309309
}
310310
},
311311
hotUpdate(options) {
312+
// Note that we must "resolve" the changed file since the path from Vite will not match Windows backslashes.
313+
const changedFilePath = path.resolve(options.file);
314+
312315
if (
313-
// Vite normalizes `options.file` so we use `path.resolve` for Windows compatibility
314-
resolvedPluginConfig.configPaths.has(path.resolve(options.file)) ||
316+
resolvedPluginConfig.configPaths.has(changedFilePath) ||
317+
hasDotDevDotVarsFileChanged(resolvedPluginConfig, changedFilePath) ||
315318
hasAssetsConfigChanged(
316319
resolvedPluginConfig,
317320
resolvedViteConfig,
318-
options.file
321+
changedFilePath
319322
)
320323
) {
321324
// It's OK for this to be called multiple times as Vite prevents concurrent execution
@@ -868,3 +871,25 @@ function getDotDevDotVarsContent(
868871

869872
return null;
870873
}
874+
875+
/**
876+
* Returns true if the `changedFile` matches one of a potential .dev.vars file.
877+
*/
878+
function hasDotDevDotVarsFileChanged(
879+
resolvedPluginConfig: ResolvedPluginConfig,
880+
changedFilePath: string
881+
) {
882+
return [...resolvedPluginConfig.configPaths].some((configPath) => {
883+
const dotDevDotVars = path.join(path.dirname(configPath), ".dev.vars");
884+
if (dotDevDotVars === changedFilePath) {
885+
return true;
886+
}
887+
888+
if (resolvedPluginConfig.cloudflareEnv) {
889+
const dotDevDotVarsForEnv = `${dotDevDotVars}.${resolvedPluginConfig.cloudflareEnv}`;
890+
return dotDevDotVarsForEnv === changedFilePath;
891+
}
892+
893+
return false;
894+
});
895+
}

0 commit comments

Comments
 (0)