diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml
index bcfb2e3779..12dcb31894 100644
--- a/.github/workflows/deploy-examples.yml
+++ b/.github/workflows/deploy-examples.yml
@@ -403,7 +403,7 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
PREVIEW_NAME: pr-${{ github.event.pull_request.number }}
- run: vp exec vinext-cloudflare previews deploy "$PREVIEW_NAME"
+ run: vp exec vinext-cloudflare previews deploy --preview-name "$PREVIEW_NAME"
- name: Deploy Named Preview Worker
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && hashFiles(format('{0}/cloudflare.config.ts', matrix.example.working_directory)) == '' && matrix.example.worker_name
diff --git a/docs/deploying/cloudflare.mdx b/docs/deploying/cloudflare.mdx
index 68e038a26b..7200749b53 100644
--- a/docs/deploying/cloudflare.mdx
+++ b/docs/deploying/cloudflare.mdx
@@ -118,15 +118,15 @@ Use `previews deploy` with a project that has `cloudflare.config.ts` and the `cf
-The optional name identifies the Preview, not the Worker or the build mode. If you omit it, CF uses the current CI or Git branch. If no branch is available, provide a name.
+The optional `--preview-name` identifies the Preview, not the Worker or the build mode. If you omit it, CF uses the current CI or Git branch. If no branch is available, pass `--preview-name`.
As with `cf previews deploy`, the Preview name and the Vite mode are independent. The build uses the default `production` mode unless you pass `--mode`, and loads the matching `.env*` files. Functions in `cloudflare.config.ts` receive the selected `mode` and `isPreview: true`. The command deploys the Preview Build Output with `cf previews deploy` and prints the Preview URL. Preview deployments do not use cache warming or version promotion.
@@ -134,15 +134,15 @@ As with `cf previews deploy`, the Preview name and the Vite mode are independent
-The subcommand accepts the optional name, `--mode`, `--skip-build`, `--vite-config`, and `--help`. Configure Preview-specific settings in `cloudflare.config.ts` using `isPreview`.
+The subcommand accepts `--preview-name`, `--mode`, `--skip-build`, `--vite-config`, and `--help`. Configure Preview-specific settings in `cloudflare.config.ts` using `isPreview`.
## Use Workers bindings
diff --git a/packages/cloudflare/src/cli.ts b/packages/cloudflare/src/cli.ts
index a9aab065a5..407b848f42 100644
--- a/packages/cloudflare/src/cli.ts
+++ b/packages/cloudflare/src/cli.ts
@@ -31,7 +31,7 @@ function printHelp(commandName?: string): void {
Commands:
deploy Deploy to Cloudflare Workers
- previews deploy [preview-name] Deploy a Cloudflare Worker Preview
+ previews deploy Deploy a Cloudflare Worker Preview
Options:
-h, --help Show this help
diff --git a/packages/cloudflare/src/deploy-help.ts b/packages/cloudflare/src/deploy-help.ts
index f1dc2d7252..30122199ba 100644
--- a/packages/cloudflare/src/deploy-help.ts
+++ b/packages/cloudflare/src/deploy-help.ts
@@ -132,15 +132,13 @@ export function formatPreviewsDeployHelp(): string {
return `
vinext-cloudflare previews deploy - Deploy a Cloudflare Worker Preview
- Usage: vinext-cloudflare previews deploy [preview-name] [options]
+ Usage: vinext-cloudflare previews deploy [options]
Builds the app with isPreview=true, then deploys its Preview Build Output
using cf previews deploy. Requires cloudflare.config.ts and the cf CLI.
- Arguments:
- preview-name Optional Preview name (cf uses the CI or Git branch if omitted)
-
Options:
+ --preview-name Preview name (default: the current CI or Git branch)
--mode Vite mode to build and deploy in (default: production)
--skip-build Deploy existing Preview Build Output without rebuilding
--vite-config Vite config file to build with (default: vite.config.*)
@@ -148,9 +146,9 @@ export function formatPreviewsDeployHelp(): string {
Examples:
vinext-cloudflare previews deploy
- vinext-cloudflare previews deploy pr-123
- vinext-cloudflare previews deploy pr-123 --mode staging
- vinext-cloudflare previews deploy pr-123 --skip-build
+ vinext-cloudflare previews deploy --preview-name pr-123
+ vinext-cloudflare previews deploy --preview-name pr-123 --mode staging
+ vinext-cloudflare previews deploy --preview-name pr-123 --skip-build
`;
}
diff --git a/packages/cloudflare/src/deploy.ts b/packages/cloudflare/src/deploy.ts
index c48d86d67d..672cf4181b 100644
--- a/packages/cloudflare/src/deploy.ts
+++ b/packages/cloudflare/src/deploy.ts
@@ -723,7 +723,7 @@ export function buildCfDeployArgs(
const mode = options.env || (options.preview ? "preview" : undefined);
const isPreviewDeploy = options.target?.type === "preview";
const args = isPreviewDeploy ? ["previews", "deploy"] : ["deploy"];
- if (isPreviewDeploy && options.target?.name) args.push(options.target.name);
+ if (isPreviewDeploy && options.target?.name) args.push("--preview-name", options.target.name);
args.push("--prebuilt");
if (mode) args.push("--mode", validateWranglerEnvName(mode));
return { args, mode };
diff --git a/packages/cloudflare/src/previews-deploy.ts b/packages/cloudflare/src/previews-deploy.ts
index b8d1d6496f..3851b0129b 100644
--- a/packages/cloudflare/src/previews-deploy.ts
+++ b/packages/cloudflare/src/previews-deploy.ts
@@ -24,18 +24,23 @@ export function parsePreviewsDeployArgs(args: string[]) {
options: {
help: { type: "boolean", short: "h", default: false },
mode: { type: "string" },
+ "preview-name": { type: "string" },
"skip-build": { type: "boolean", default: false },
"vite-config": { type: "string" },
},
});
- if (positionals.length > 1) {
- throw new Error("Expected at most one Preview name: previews deploy [preview-name].");
+ // Like `cf previews deploy`, the Preview name is a flag, not a positional argument.
+ if (positionals.length > 0) {
+ throw new Error(
+ `Unexpected argument: ${positionals[0]}. Pass the Preview name with --preview-name .`,
+ );
}
+ const previewName = values["preview-name"];
return {
help: values.help,
- name: positionals[0] === undefined ? undefined : validatePreviewName(positionals[0]),
+ name: previewName === undefined ? undefined : validatePreviewName(previewName),
mode: values.mode === undefined ? undefined : validateMode(values.mode),
skipBuild: values["skip-build"],
viteConfig: values["vite-config"]?.trim() || undefined,
diff --git a/tests/deploy-prerender-config.test.ts b/tests/deploy-prerender-config.test.ts
index 8dab2b08fe..ab246c4dce 100644
--- a/tests/deploy-prerender-config.test.ts
+++ b/tests/deploy-prerender-config.test.ts
@@ -387,6 +387,7 @@ describe("deploy prerender config wiring", () => {
expect.stringContaining(path.join("cf", "bin", "cf")),
"previews",
"deploy",
+ "--preview-name",
"pr-123",
"--prebuilt",
"--mode",
@@ -451,6 +452,7 @@ describe("deploy prerender config wiring", () => {
expect.any(String),
"previews",
"deploy",
+ "--preview-name",
"pr-123",
"--prebuilt",
"--mode",
diff --git a/tests/deploy.test.ts b/tests/deploy.test.ts
index 80259c0918..763518f671 100644
--- a/tests/deploy.test.ts
+++ b/tests/deploy.test.ts
@@ -626,7 +626,14 @@ describe("cf Build Output deployment", () => {
it.each([undefined, "pr-123"])("builds Preview deploy args (name: %s)", (name) => {
expect(buildCfDeployArgs({ env: "preview", target: { type: "preview", name } })).toEqual({
- args: ["previews", "deploy", ...(name ? [name] : []), "--prebuilt", "--mode", "preview"],
+ args: [
+ "previews",
+ "deploy",
+ ...(name ? ["--preview-name", name] : []),
+ "--prebuilt",
+ "--mode",
+ "preview",
+ ],
mode: "preview",
});
});
@@ -656,7 +663,16 @@ describe("cf Build Output deployment", () => {
expect(invocations).toEqual([
[
process.execPath,
- [resolveCfBin(tmpDir), "previews", "deploy", "main", "--prebuilt", "--mode", "preview"],
+ [
+ resolveCfBin(tmpDir),
+ "previews",
+ "deploy",
+ "--preview-name",
+ "main",
+ "--prebuilt",
+ "--mode",
+ "preview",
+ ],
{ cwd: tmpDir, stdio: ["inherit", "pipe", "pipe"], shell: false },
],
]);
@@ -816,14 +832,16 @@ describe("parseWorkerDeploymentUrl", () => {
// ─── Deploy CLI arg parsing ─────────────────────────────────────────────────
describe("parsePreviewsDeployArgs", () => {
- it("accepts an optional name, mode, skip-build, and help", () => {
+ it("accepts an optional Preview name, mode, skip-build, and help", () => {
expect(parsePreviewsDeployArgs([])).toEqual({
name: undefined,
mode: undefined,
skipBuild: false,
help: false,
});
- expect(parsePreviewsDeployArgs(["pr-123", "--mode", "staging", "--skip-build"])).toEqual({
+ expect(
+ parsePreviewsDeployArgs(["--preview-name", "pr-123", "--mode", "staging", "--skip-build"]),
+ ).toEqual({
name: "pr-123",
mode: "staging",
skipBuild: true,
@@ -847,10 +865,15 @@ describe("parsePreviewsDeployArgs", () => {
expect(() => parsePreviewsDeployArgs([option])).toThrow();
});
- it("rejects multiple or invalid Preview names", () => {
- expect(() => parsePreviewsDeployArgs(["one", "two"])).toThrow("at most one");
- expect(() => parsePreviewsDeployArgs([" "])).toThrow("must not be empty");
- expect(() => parsePreviewsDeployArgs(["pr\0name"])).toThrow("null bytes");
+ it("rejects a positional Preview name", () => {
+ expect(() => parsePreviewsDeployArgs(["pr-123"])).toThrow(
+ "Unexpected argument: pr-123. Pass the Preview name with --preview-name .",
+ );
+ });
+
+ it("rejects invalid Preview names", () => {
+ expect(() => parsePreviewsDeployArgs(["--preview-name", " "])).toThrow("must not be empty");
+ expect(() => parsePreviewsDeployArgs(["--preview-name", "pr\0name"])).toThrow("null bytes");
});
it.each([["--mode="], ["--mode", " "]])("rejects an empty mode: %j", (...args) => {