Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-doors-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/vite-plugin": patch
---

Add a check to vite-plugin that ensures that the version of Wrangler being used internally is correct
2 changes: 1 addition & 1 deletion packages/vite-plugin-cloudflare/e2e/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const commands = ["dev", "buildAndPreview"] as const;

describe("basic e2e tests", () => {
describe.each(packageManagers)('with "%s" package manager', async (pm) => {
const projectPath = seed("basic", pm);
const projectPath = seed("basic", { pm });

describe.each(commands)('with "%s" command', (command) => {
test.skipIf(isBuildAndPreviewOnWindows(command))(
Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin-cloudflare/e2e/dynamic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const packageManagers = ["pnpm", "npm", "yarn"] as const;

describe("prebundling Node.js compatibility", () => {
describe.each(packageManagers)('with "%s" package manager', (pm) => {
const projectPath = seed("dynamic", pm);
const projectPath = seed("dynamic", { pm });

test("will not cause a reload on a dynamic import of a Node.js module", async ({
expect,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@cloudflare/vite-plugin-e2e-invalid-wrangler-version",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "vite build",
"buildAndPreview": "vite build && vite preview",
"dev": "vite",
"lint": "eslint .",
"preview": "vite preview"
},
"devDependencies": {
"@cloudflare/vite-plugin": "*",
"@cloudflare/workers-types": "^4.20250204.0",
"@eslint/js": "^9.19.0",
"eslint": "^9.19.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.18",
"globals": "^15.14.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.22.0",
"vite": "^6.1.0",
"wrangler": "4.20.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
async fetch() {
return new Response("OK");
},
} satisfies ExportedHandler;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.worker.json" }
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.node.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.worker.tsbuildinfo",
"types": ["@cloudflare/workers-types/2023-07-01", "vite/client"]
},
"include": ["src"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
plugins: [cloudflare({ inspectorPort: false, persistState: false })],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "cloudflare-vite-e2e-invalid-wrangler-version",
"main": "./src/index.ts",
"compatibility_date": "2024-12-30",
"compatibility_flags": ["nodejs_compat"],
}
21 changes: 16 additions & 5 deletions packages/vite-plugin-cloudflare/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,15 @@ const strictPeerDeps = {
/** Seed a test project from a fixture. */
export function seed(
fixture: string,
pm: "pnpm" | "yarn" | "npm",
replacements: Record<string, string> = {}
{
pm,
replacements = {},
useStrictPeerDeps = true,
}: {
pm: "pnpm" | "yarn" | "npm";
replacements?: Record<string, string>;
useStrictPeerDeps?: boolean;
}
) {
const root = inject("root");
const projectPath = path.resolve(root, fixture, pm);
Expand All @@ -56,9 +63,13 @@ export function seed(
debuglog("Fixing up replacements in seeded files");
await fixupReplacements(projectPath, replacements);
debuglog("Updated vite-plugin version in package.json");
runCommand(`${pm} install ${strictPeerDeps[pm]}`, projectPath, {
attempts: 2,
});
runCommand(
`${pm} install ${useStrictPeerDeps ? strictPeerDeps[pm] : ""}`,
projectPath,
{
attempts: 2,
}
);
debuglog("Installed node modules");
}, 200_000);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { runLongLived, seed } from "./helpers";
// The test here just makes sure that the validation takes place.
// Unit tests for the validation are in `src/__tests__/validate-worker-environment-options.spec.ts`
describe("validate Worker environment options", () => {
const projectPath = seed("invalid-worker-environment-options", "pnpm");
const projectPath = seed("invalid-worker-environment-options", {
pm: "pnpm",
});

test("throws an error for invalid environment options", async ({
expect,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, test } from "vitest";
import { runLongLived, seed } from "./helpers.js";

describe("invalid Wrangler version e2e tests", () => {
// Only test with pnpm;
// npm and yarn don't hoist peer dependencies in the same way as pnpm;
// so having a different peer Wrangler doesn't mess up the internal dependency
const projectPath = seed("invalid-wrangler-version", {
pm: "pnpm",
useStrictPeerDeps: false,
});

test("`vite dev` will error when peer installed wrangler version overrides the expected internal dependency", async ({
expect,
}) => {
const proc = await runLongLived("pnpm", "dev", projectPath);
expect(await proc.exitCode).not.toBe(0);
expect(proc.stderr).toMatch(
/The installed version of Wrangler \(4\.20\.0\) is older than the version required by @cloudflare\/vite-plugin \(\d+\.\d+\.\d+\)/
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if (!process.env.CLOUDFLARE_ACCOUNT_ID || !process.env.CLOUDFLARE_API_TOKEN) {
"<<REMOTE_WORKER_PLACEHOLDER_ALT>>": `preserve-e2e-vite-remote-alt`,
};

const projectPath = seed("remote-bindings", "pnpm", replacements);
const projectPath = seed("remote-bindings", { pm: "pnpm", replacements });

beforeAll(async () => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { runLongLived, seed } from "./helpers";
// testing regarding the validation there are unit tests in src/__tests__/get-validated-wrangler-config-path.spec.ts

describe("during development wrangler config files are validated", () => {
const noWranglerConfigProjectPath = seed("no-wrangler-config", "pnpm");
const noWranglerConfigProjectPath = seed("no-wrangler-config", {
pm: "pnpm",
});
test("for the entry worker", async ({ expect }) => {
const proc = await runLongLived("pnpm", "dev", noWranglerConfigProjectPath);
expect(await proc.exitCode).not.toBe(0);
Expand All @@ -16,7 +18,7 @@ describe("during development wrangler config files are validated", () => {

const noWranglerConfigAuxProjectPath = seed(
"no-wrangler-config-for-auxiliary-worker",
"pnpm"
{ pm: "pnpm" }
);
test("for auxiliary workers", async ({ expect }) => {
const proc = await runLongLived(
Expand Down
5 changes: 4 additions & 1 deletion packages/vite-plugin-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -59,9 +60,11 @@
"@cloudflare/workers-tsconfig": "workspace:*",
"@cloudflare/workers-types": "catalog:default",
"@types/node": "catalog:vite-plugin",
"@types/semver": "^7.5.1",
"@types/ws": "^8.5.13",
"magic-string": "^0.30.12",
"mlly": "^1.7.4",
"semver": "^7.7.1",
"tsdown": "^0.15.4",
"typescript": "catalog:default",
"vite": "catalog:vite-plugin",
Expand Down
40 changes: 40 additions & 0 deletions packages/vite-plugin-cloudflare/src/assert-wrangler-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createRequire } from "node:module";
import { compare, Range, satisfies, SemVer } from "semver";

/**
* Asserts that the installed version of Wrangler that gets pulled in at runtime by the `@cloudflare/vite-plugin`
* matches the version that `@cloudflare/vite-plugin` actually depends upon.
*
* This can sometime be broken by package managers that deduplicate dependencies, such as `pnpm`.
*/
export function assertWranglerVersion() {
const require = createRequire(import.meta.url);
const installedVersion = new SemVer(require("wrangler/package.json").version);
const vitePackage = require("@cloudflare/vite-plugin/package.json");

if (vitePackage.dependencies.wrangler.startsWith("workspace:")) {
// We are running in the monorepo, so these deps are not yet computed to specific semver strings.
// We don't need to worry in this case and can skip the check.
return;
}

const wranglerDependency = new SemVer(vitePackage.dependencies.wrangler);
const wranglerPeerDependency = new Range(
vitePackage.peerDependencies.wrangler
);

if (compare(installedVersion, wranglerDependency) < 0) {
throw new Error(
`The installed version of Wrangler (${installedVersion.format()}) is older than the version required by @cloudflare/vite-plugin (${wranglerDependency.format()}).\n` +
`Please install Wrangler version ${wranglerDependency} in your project.`
);
}

if (!satisfies(installedVersion, wranglerPeerDependency)) {
console.warn(
`The installed version of Wrangler (${installedVersion.format()}) does not satisfy the peer dependency required by @cloudflare/vite-plugin (${wranglerDependency.format()}).\n` +
`This may lead to unexpected issues when you come to deploy the application.\n` +
`Please install a version of Wrangler that satisfies the peer dependency: ${wranglerPeerDependency}.`
);
}
}
4 changes: 2 additions & 2 deletions packages/vite-plugin-cloudflare/src/deploy-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from "node:assert";
import * as fs from "node:fs";
import * as path from "node:path";
import * as vite from "vite";
import { unstable_readConfig } from "wrangler";
import * as wrangler from "wrangler";
import type {
AssetsOnlyResolvedConfig,
WorkersResolvedConfig,
Expand Down Expand Up @@ -31,7 +31,7 @@ export function getWorkerConfigs(root: string) {
path.dirname(deployConfigPath),
configPath
);
return unstable_readConfig({ config: resolvedConfigPath });
return wrangler.unstable_readConfig({ config: resolvedConfigPath });
});
}

Expand Down
4 changes: 2 additions & 2 deletions packages/vite-plugin-cloudflare/src/dev-vars.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "node:path";
import { unstable_getVarsForDev } from "wrangler";
import * as wrangler from "wrangler";
import type {
AssetsOnlyResolvedConfig,
WorkersResolvedConfig,
Expand All @@ -14,7 +14,7 @@ export function getLocalDevVarsForPreview(
configPath: string | undefined,
cloudflareEnv: string | undefined
): string | undefined {
const dotDevDotVars = unstable_getVarsForDev(
const dotDevDotVars = wrangler.unstable_getVarsForDev(
configPath,
undefined, // We don't currently support setting a list of custom `.env` files.
{}, // Don't pass actual vars since these will be loaded from the wrangler.json.
Expand Down
3 changes: 3 additions & 0 deletions packages/vite-plugin-cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { generateStaticRoutingRuleMatcher } from "@cloudflare/workers-shared/ass
import { CoreHeaders, Miniflare } from "miniflare";
import colors from "picocolors";
import * as vite from "vite";
import { assertWranglerVersion } from "./assert-wrangler-version";
import { hasAssetsConfigChanged } from "./asset-config";
import { createBuildApp } from "./build";
import {
Expand Down Expand Up @@ -68,6 +69,8 @@ let workersConfigsWarningShown = false;
let restartingServer = false;
let miniflare: Miniflare | undefined;

await assertWranglerVersion();

/**
* Vite plugin that enables a full-featured integration between Vite and the Cloudflare Workers runtime.
*
Expand Down
Loading
Loading