Skip to content
Merged
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
9 changes: 9 additions & 0 deletions .changeset/vite-plugin-vitest4-resolve-external.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cloudflare/vite-plugin": patch
---

Allow `resolve.external` containing only Node.js built-ins in Worker environments

Vitest 4 automatically sets `resolve.external` to the full list of Node.js built-in modules for non-standard Vite environments via its internal `runnerTransform` plugin. Previously, the Cloudflare Vite plugin rejected any non-empty `resolve.external` array, throwing an incompatibility error on startup when used alongside Vitest 4.

The validation check now allows `resolve.external` arrays that contain only Node.js built-in module names (both bare `fs` and `node:fs` forms). The error is only thrown when `resolve.external` is `true` or contains non-built-in package names that would prevent user code from being bundled into the Worker.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,29 @@ describe("validateWorkerEnvironmentOptions", () => {
);
});

test("doesn't throw when resolve.external contains only Node.js built-ins (set by Vitest 4)", ({
expect,
}) => {
const resolvedPluginConfig = {
environmentNameToWorkerMap: new Map([["worker", { config: {} }]]),
} as unknown as AssetsOnlyResolvedConfig | WorkersResolvedConfig;
const resolvedViteConfig = {
environments: {
worker: {
resolve: {
// Vitest 4 sets resolve.external to all Node.js built-ins for
// non-standard environments via its runnerTransform plugin
external: ["assert", "buffer", "fs", "node:path", "node:fs"],
},
},
},
} as unknown as ResolvedConfig;

expect(() =>
validateWorkerEnvironmentOptions(resolvedPluginConfig, resolvedViteConfig)
).not.toThrow();
});

test("throws with an appropriate error message if multiple worker environments contain config violations", ({
expect,
}) => {
Expand Down
33 changes: 32 additions & 1 deletion packages/vite-plugin-cloudflare/src/vite-config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
import assert from "node:assert";
import { builtinModules } from "node:module";
import type {
AssetsOnlyResolvedConfig,
WorkersResolvedConfig,
} from "./plugin-config";
import type * as vite from "vite";

// Node.js built-in module names in both bare and `node:` prefixed forms.
// Vitest 4 automatically adds these to `resolve.external` for non-standard
// environments (via its `runnerTransform` plugin). They are harmless for
// Worker environments — Workers either handle them via the node-compat layer
// or don't use them — so validation is skipped when the array contains only
// these entries.
Comment thread
matingathani marked this conversation as resolved.
const NODE_BUILTIN_SET = new Set([
...builtinModules,
// Only add the `node:` prefix for modules that don't already have it,
// avoiding hypothetical `node:node:*` entries on future Node versions.
...builtinModules
.filter((m) => !m.startsWith("node:"))
.map((m) => `node:${m}`),
]);
Comment thread
petebacondarwin marked this conversation as resolved.

function isOnlyNodeBuiltins(
external: vite.ResolveOptions["external"]
): boolean {
if (!Array.isArray(external)) {
return false;
}
return external.every(
(entry) => typeof entry === "string" && NODE_BUILTIN_SET.has(entry)
);
}

interface DisallowedEnvironmentOptions {
resolveExternal?: vite.ResolveOptions["external"];
}
Expand All @@ -30,7 +57,11 @@ export function validateWorkerEnvironmentOptions(
const { resolve } = environmentOptions;
const disallowedEnvironmentOptions: DisallowedEnvironmentOptions = {};

if (resolve.external === true || resolve.external.length) {
if (
(resolve.external === true ||
(Array.isArray(resolve.external) && resolve.external.length > 0)) &&
!isOnlyNodeBuiltins(resolve.external)
) {
Comment thread
petebacondarwin marked this conversation as resolved.
disallowedEnvironmentOptions.resolveExternal = resolve.external;
}

Expand Down
Loading