diff --git a/.changeset/v1-container-command-guidance.md b/.changeset/v1-container-command-guidance.md new file mode 100644 index 00000000000..2c1f1563ade --- /dev/null +++ b/.changeset/v1-container-command-guidance.md @@ -0,0 +1,7 @@ +--- +"@cloudflare/vite-plugin": patch +--- + +Fix Container setup guidance in Vite development and preview + +Container registry instructions now show the Wrangler command and the configuration location, including Wrangler files and inline Vite configuration. diff --git a/packages/vite-plugin-cloudflare/package.json b/packages/vite-plugin-cloudflare/package.json index 3f567b9d543..f254f5f066e 100644 --- a/packages/vite-plugin-cloudflare/package.json +++ b/packages/vite-plugin-cloudflare/package.json @@ -52,7 +52,7 @@ "ws": "8.22.0" }, "devDependencies": { - "@cloudflare/containers-shared": "0.22.1", + "@cloudflare/containers-shared": "0.23.0", "@cloudflare/mock-npm-registry": "workspace:*", "@cloudflare/remote-bindings": "workspace:*", "@cloudflare/workers-shared": "workspace:*", diff --git a/packages/vite-plugin-cloudflare/src/__tests__/containers-server.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/containers-server.spec.ts index 9fc75f213a6..dc1353a567d 100644 --- a/packages/vite-plugin-cloudflare/src/__tests__/containers-server.spec.ts +++ b/packages/vite-plugin-cloudflare/src/__tests__/containers-server.spec.ts @@ -402,6 +402,66 @@ export default { plugins: [cloudflare({ inspectorPort: false, persistState: fals ); } + test("uses inline configuration guidance without inventing a config file", async ({ + expect, + onTestFinished, + }) => { + fs.writeFileSync("package.json", JSON.stringify({ type: "module" })); + fs.writeFileSync("Dockerfile", "FROM alpine:3.19\n"); + fs.writeFileSync( + "index.js", + `import { DurableObject } from "cloudflare:workers"; +export class Probe extends DurableObject {} +export default { fetch() { return new Response("ready"); } };` + ); + function config() { + return { + root: process.cwd(), + configFile: false as const, + logLevel: "silent" as const, + server: { port: 0 }, + preview: { port: 0 }, + plugins: [ + cloudflare({ + inspectorPort: false, + persistState: false, + remoteBindings: false, + config: { + name: "inline-container-test", + main: path.resolve("index.js"), + compatibility_date: "2026-10-19", + containers: [ + { + class_name: "Probe", + scheduling_policy: "durable_object", + images: { app: { dockerfile: path.resolve("Dockerfile") } }, + }, + ], + durable_objects: { + bindings: [{ name: "PROBE", class_name: "Probe" }], + }, + migrations: [{ tag: "v1", new_sqlite_classes: ["Probe"] }], + }, + }), + ], + }; + } + if (mode === "preview") { + const builder = await createBuilder(config()); + await builder.buildApp(); + } + const server = await (mode === "dev" + ? createServer(config()) + : preview(config())); + onTestFinished(() => server.close()); + expect(prepareContainerImagesForDev).toHaveBeenCalledExactlyOnceWith( + expect.objectContaining({ + configFileName: + mode === "dev" ? "Vite plugin configuration" : "wrangler.json", + }) + ); + }); + test.for(["image-free", "configured", "disabled", "absent"] as const)( "prepares only enabled Containers: %s", async (scenario, { expect, onTestFinished }) => { @@ -487,6 +547,14 @@ export default { plugins: [cloudflare({ inspectorPort: false, persistState: fals expect(prepareContainerImagesForDev).not.toHaveBeenCalled(); } else { expect(prepareContainerImagesForDev).toHaveBeenCalledTimes(1); + expect(prepareContainerImagesForDev).toHaveBeenCalledWith( + expect.objectContaining({ + configFileName: + mode === "dev" ? "wrangler.jsonc" : "wrangler.json", + registryConfigureCommand: + "wrangler containers registries configure", + }) + ); const options = vi.mocked(prepareContainerImagesForDev).mock .calls[0]?.[0].containerOptions; expect(options).toHaveLength(scenario === "configured" ? 1 : 0); diff --git a/packages/vite-plugin-cloudflare/src/__tests__/containers.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/containers.spec.ts index 7f0ff3ff1c3..0f0b22bf462 100644 --- a/packages/vite-plugin-cloudflare/src/__tests__/containers.spec.ts +++ b/packages/vite-plugin-cloudflare/src/__tests__/containers.spec.ts @@ -330,3 +330,37 @@ describe("Container image planning", () => { expect(new Set(runtimeImageTags)).toEqual(imageTags); }); }); + +test.for([ + { + configPath: "project/wrangler.jsonc", + viteConfigFile: "project/vite.config.mts", + configFileName: "wrangler.jsonc", + }, + { + configPath: undefined, + viteConfigFile: "project/vite.config.mts", + configFileName: "vite.config.mts", + }, + { + configPath: undefined, + viteConfigFile: false as const, + configFileName: "Vite plugin configuration", + }, +])( + "passes $configFileName guidance to the Container helper", + async ({ configPath, viteConfigFile, configFileName }, { expect }) => { + await prepareContainerImagesForVite({ + dockerPath: "test-docker", + containerOptionsByWorker: new Map([[{ configPath }, []]]), + logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, + viteConfigFile, + }); + expect(prepareContainerImagesForDev).toHaveBeenCalledExactlyOnceWith( + expect.objectContaining({ + configFileName, + registryConfigureCommand: "wrangler containers registries configure", + }) + ); + } +); diff --git a/packages/vite-plugin-cloudflare/src/containers.ts b/packages/vite-plugin-cloudflare/src/containers.ts index 01b92c25a50..c914a2a3865 100644 --- a/packages/vite-plugin-cloudflare/src/containers.ts +++ b/packages/vite-plugin-cloudflare/src/containers.ts @@ -1,3 +1,4 @@ +import path from "node:path"; import { configureOpenAPIForContainerPull, isCloudflareRegistryImage, @@ -15,7 +16,10 @@ import type { } from "@cloudflare/containers-shared"; import type { ComplianceConfig } from "@cloudflare/workers-utils"; -type ContainerWorkerConfig = ComplianceConfig & { account_id?: string }; +type ContainerWorkerConfig = ComplianceConfig & { + account_id?: string; + configPath?: string; +}; /** * Configures the Containers API client used to retrieve image pull credentials. @@ -73,13 +77,14 @@ export function normalizeContainerImageUris( * registry account and compliance settings, including Workers whose images * are selected at start time. * - * @param args - Planned images, Docker executable, and Vite logger. + * @param args - Planned images, Docker executable, Vite logger, and configuration source. * @returns No value. */ export async function prepareContainerImagesForVite(args: { dockerPath: string; containerOptionsByWorker: ContainerOptionsByWorker; logger: ViteLogger; + viteConfigFile?: string | false; }): Promise { for (const [workerConfig, plannedOptions] of args.containerOptionsByWorker) { let containerOptions = plannedOptions; @@ -119,6 +124,12 @@ export async function prepareContainerImagesForVite(args: { onContainerImagePreparationEnd: () => {}, logger: args.logger, complianceConfig: workerConfig, + configFileName: workerConfig.configPath + ? path.basename(workerConfig.configPath) + : args.viteConfigFile + ? path.basename(args.viteConfigFile) + : "Vite plugin configuration", + registryConfigureCommand: "wrangler containers registries configure", }); } } diff --git a/packages/vite-plugin-cloudflare/src/plugins/dev.ts b/packages/vite-plugin-cloudflare/src/plugins/dev.ts index 5a8e7ed24d6..ca4bd66d826 100644 --- a/packages/vite-plugin-cloudflare/src/plugins/dev.ts +++ b/packages/vite-plugin-cloudflare/src/plugins/dev.ts @@ -221,6 +221,7 @@ export const devPlugin = createPlugin("dev", (ctx) => { dockerPath: getDockerPath(), containerOptionsByWorker, logger: viteDevServer.config.logger, + viteConfigFile: viteDevServer.config.configFile, }); containerCleanup.track( diff --git a/packages/vite-plugin-cloudflare/src/plugins/preview.ts b/packages/vite-plugin-cloudflare/src/plugins/preview.ts index 3065c459793..1734daae258 100644 --- a/packages/vite-plugin-cloudflare/src/plugins/preview.ts +++ b/packages/vite-plugin-cloudflare/src/plugins/preview.ts @@ -66,6 +66,7 @@ export const previewPlugin = createPlugin("preview", (ctx) => { dockerPath, containerOptionsByWorker, logger: vitePreviewServer.config.logger, + viteConfigFile: vitePreviewServer.config.configFile, }); containerCleanup.track( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1cce96b1cd..c862d691be7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,8 +320,8 @@ importers: version: 8.22.0 devDependencies: '@cloudflare/containers-shared': - specifier: 0.22.1 - version: 0.22.1(vitest@4.1.0) + specifier: 0.23.0 + version: 0.23.0(vitest@4.1.0) '@cloudflare/mock-npm-registry': specifier: workspace:* version: link:../mock-npm-registry @@ -1940,6 +1940,10 @@ packages: resolution: {integrity: sha512-zwvrXDNcLFfxcSRwjDkTH0eR5Moxk50oLsgBlgGblIHZXWblMk6N4xMx3xWD45/GcO8rWJ99aoZGIT/qWTnZZA==} engines: {node: '>=22.0.0'} + '@cloudflare/containers-shared@0.23.0': + resolution: {integrity: sha512-Gkoizqq4G0NzOjPhM8VHEz4xV43doXzoeMc5R7c4m4b611MavjBgCUakynnuoH+d3QfsE2f3iN0hPaVaTuDnnQ==} + engines: {node: '>=22.0.0'} + '@cloudflare/deploy-helpers@0.21.0': resolution: {integrity: sha512-QRBBYnB4kJedvMOdkJgrvSo8KT1wdey+qjQ6G6YouL7lgbYxAX602tpYbn7ayTaCOLVo/f4DKzw4sI05PWIURQ==} @@ -8488,6 +8492,15 @@ snapshots: transitivePeerDependencies: - vitest + '@cloudflare/containers-shared@0.23.0(vitest@4.1.0)': + dependencies: + '@cloudflare/build-output-utils': 0.9.3 + '@cloudflare/cli-shared-helpers': 0.2.6(vitest@4.1.0) + '@cloudflare/config': 0.27.0 + '@cloudflare/workers-utils': 0.47.2(vitest@4.1.0) + transitivePeerDependencies: + - vitest + '@cloudflare/deploy-helpers@0.21.0(@types/node@22.15.17)(vitest@4.1.0)': dependencies: '@cloudflare/cli-shared-helpers': 0.2.6(vitest@4.1.0)