@vinext/cloudflare 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/cdn-adapter.runtime.js +10 -4
- package/dist/cache/cdn-adapter.worker.js +2 -1
- package/dist/cli.js +38 -2
- package/dist/deploy-help.d.ts +2 -0
- package/dist/deploy-help.js +32 -2
- package/dist/deploy.d.ts +9 -2
- package/dist/deploy.js +79 -12
- package/dist/previews-deploy.d.ts +9 -0
- package/dist/previews-deploy.js +43 -0
- package/package.json +4 -4
|
@@ -132,6 +132,7 @@ function toEdgeCacheControl(cacheControl) {
|
|
|
132
132
|
*/
|
|
133
133
|
const MAX_CACHE_TAG_BYTES = 16384;
|
|
134
134
|
const CACHE_TAG_PREFIX = "vinext-";
|
|
135
|
+
const MAX_TAGS_PER_PURGE = 100;
|
|
135
136
|
/** Encode a case-sensitive Next.js tag into a fixed Workers Cache tag. */
|
|
136
137
|
function encodeCloudflareCacheTag(tag) {
|
|
137
138
|
return `${CACHE_TAG_PREFIX}${fnv1a64(`0:${tag}`)}${fnv1a64(`1:${tag}`)}`;
|
|
@@ -222,11 +223,16 @@ var CloudflareCdnCacheAdapter = class {
|
|
|
222
223
|
if (!cache) return;
|
|
223
224
|
const tagList = (Array.isArray(tags) ? tags : [tags]).filter((t) => typeof t === "string");
|
|
224
225
|
if (tagList.length === 0) return;
|
|
225
|
-
const
|
|
226
|
+
const encodedTags = [...new Set(tagList.map(encodeCloudflareCacheTag))];
|
|
226
227
|
const operation = isStaleTagInvalidation(durations) ? "invalidate" : "purge";
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
const chunks = [];
|
|
229
|
+
for (let i = 0; i < encodedTags.length; i += MAX_TAGS_PER_PURGE) chunks.push(encodedTags.slice(i, i + MAX_TAGS_PER_PURGE));
|
|
230
|
+
const settled = await Promise.allSettled(chunks.map(async (chunk) => await (operation === "invalidate" ? invalidateOrPurge(cache, { tags: chunk }) : cache.purge({ tags: chunk }))));
|
|
231
|
+
const rejection = settled.find((result) => result.status === "rejected");
|
|
232
|
+
if (rejection) throw rejection.reason;
|
|
233
|
+
const failures = settled.flatMap((result) => result.status === "fulfilled" && result.value?.success === false ? [result.value] : []);
|
|
234
|
+
if (failures.length > 0) {
|
|
235
|
+
const errors = failures.flatMap((result) => result.errors).map(({ code, message }) => `${code}: ${message}`).join(", ");
|
|
230
236
|
throw new Error(`[vinext] Workers Cache ${operation} failed${errors ? `: ${errors}` : ""}`);
|
|
231
237
|
}
|
|
232
238
|
}
|
|
@@ -132,7 +132,8 @@ async function createCacheFacingRequest(request, serializedInvocation) {
|
|
|
132
132
|
return new Request(url, {
|
|
133
133
|
cf: { vary: { default: { action: "passthrough" } } },
|
|
134
134
|
headers,
|
|
135
|
-
method: request.method
|
|
135
|
+
method: request.method,
|
|
136
|
+
redirect: "manual"
|
|
136
137
|
});
|
|
137
138
|
}
|
|
138
139
|
function restoreResponseStageRequest(request, requestUrl, requestMethod) {
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { deploy, parseDeployArgs } from "./deploy.js";
|
|
3
|
-
import { printDeployHelp } from "./deploy-help.js";
|
|
3
|
+
import { printDeployHelp, printPreviewsDeployHelp } from "./deploy-help.js";
|
|
4
|
+
import { parsePreviewsDeployArgs } from "./previews-deploy.js";
|
|
4
5
|
import fs from "node:fs";
|
|
5
6
|
//#region src/cli.ts
|
|
6
7
|
const VERSION = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version;
|
|
@@ -11,6 +12,10 @@ function printHelp(commandName) {
|
|
|
11
12
|
printDeployHelp();
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
15
|
+
if (commandName === "previews") {
|
|
16
|
+
printPreviewsDeployHelp();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
14
19
|
console.log(`
|
|
15
20
|
vinext-cloudflare v${VERSION}
|
|
16
21
|
|
|
@@ -21,7 +26,8 @@ function printHelp(commandName) {
|
|
|
21
26
|
vp exec vinext-cloudflare <command> Run the locally installed bin
|
|
22
27
|
|
|
23
28
|
Commands:
|
|
24
|
-
deploy
|
|
29
|
+
deploy Deploy to Cloudflare Workers
|
|
30
|
+
previews deploy [preview-name] Deploy a Cloudflare Worker Preview
|
|
25
31
|
|
|
26
32
|
Options:
|
|
27
33
|
-h, --help Show this help
|
|
@@ -68,6 +74,30 @@ async function deployCommand() {
|
|
|
68
74
|
tprWindow: parsed.tprWindow
|
|
69
75
|
});
|
|
70
76
|
}
|
|
77
|
+
async function previewsCommand() {
|
|
78
|
+
const subcommand = rawArgs[0];
|
|
79
|
+
if (subcommand !== "deploy") {
|
|
80
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
81
|
+
printHelp("previews");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Unknown previews command: ${subcommand}`);
|
|
85
|
+
}
|
|
86
|
+
const options = parsePreviewsDeployArgs(rawArgs.slice(1));
|
|
87
|
+
if (options.help) {
|
|
88
|
+
printHelp("previews");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await deploy({
|
|
92
|
+
root: process.cwd(),
|
|
93
|
+
env: options.mode,
|
|
94
|
+
skipBuild: options.skipBuild,
|
|
95
|
+
target: {
|
|
96
|
+
type: "preview",
|
|
97
|
+
name: options.name
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
71
101
|
if (command === "--version" || command === "-v") {
|
|
72
102
|
console.log(`vinext-cloudflare v${VERSION}`);
|
|
73
103
|
process.exit(0);
|
|
@@ -83,6 +113,12 @@ switch (command) {
|
|
|
83
113
|
process.exit(1);
|
|
84
114
|
});
|
|
85
115
|
break;
|
|
116
|
+
case "previews":
|
|
117
|
+
previewsCommand().catch((error) => {
|
|
118
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
86
122
|
default:
|
|
87
123
|
console.error(`Unknown command: ${command}`);
|
|
88
124
|
printHelp();
|
package/dist/deploy-help.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//#region src/deploy-help.d.ts
|
|
2
2
|
export declare function formatDeployHelp(): string;
|
|
3
3
|
export declare function printDeployHelp(): void;
|
|
4
|
+
export declare function formatPreviewsDeployHelp(): string;
|
|
5
|
+
export declare function printPreviewsDeployHelp(): void;
|
|
4
6
|
//#endregion
|
package/dist/deploy-help.js
CHANGED
|
@@ -87,11 +87,14 @@ function formatDeployHelp() {
|
|
|
87
87
|
be reused outside the data center reached by the warmup request after cache
|
|
88
88
|
propagation, but the deploy does not wait for every edge location to fill.
|
|
89
89
|
|
|
90
|
+
--preview is an alias for --env preview. It selects a mode/environment,
|
|
91
|
+
not a Preview deployment. Use previews deploy to deploy a Worker Preview.
|
|
92
|
+
|
|
90
93
|
Examples:
|
|
91
94
|
npx @vinext/cloudflare deploy Build and deploy to production
|
|
92
95
|
vpx @vinext/cloudflare deploy Build and deploy with Vite+
|
|
93
96
|
vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
|
|
94
|
-
vinext-cloudflare deploy --preview Deploy
|
|
97
|
+
vinext-cloudflare deploy --preview Deploy using mode/environment preview
|
|
95
98
|
vinext-cloudflare deploy --env staging Deploy using Cloudflare mode/environment staging
|
|
96
99
|
vinext-cloudflare deploy --config dist/server/wrangler.json Deploy using a generated Wrangler config
|
|
97
100
|
vinext-cloudflare deploy --dry-run Validate setup without building or deploying
|
|
@@ -113,5 +116,32 @@ function formatDeployHelp() {
|
|
|
113
116
|
function printDeployHelp() {
|
|
114
117
|
console.log(formatDeployHelp());
|
|
115
118
|
}
|
|
119
|
+
function formatPreviewsDeployHelp() {
|
|
120
|
+
return `
|
|
121
|
+
vinext-cloudflare previews deploy - Deploy a Cloudflare Worker Preview
|
|
122
|
+
|
|
123
|
+
Usage: vinext-cloudflare previews deploy [preview-name] [options]
|
|
124
|
+
|
|
125
|
+
Builds the app with isPreview=true, then deploys its Preview Build Output
|
|
126
|
+
using cf previews deploy. Requires cloudflare.config.ts and the cf CLI.
|
|
127
|
+
|
|
128
|
+
Arguments:
|
|
129
|
+
preview-name Optional Preview name (cf uses the CI or Git branch if omitted)
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
--mode <mode> Vite mode to build and deploy in (default: production)
|
|
133
|
+
--skip-build Deploy existing Preview Build Output without rebuilding
|
|
134
|
+
-h, --help Show this help
|
|
135
|
+
|
|
136
|
+
Examples:
|
|
137
|
+
vinext-cloudflare previews deploy
|
|
138
|
+
vinext-cloudflare previews deploy pr-123
|
|
139
|
+
vinext-cloudflare previews deploy pr-123 --mode staging
|
|
140
|
+
vinext-cloudflare previews deploy pr-123 --skip-build
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
function printPreviewsDeployHelp() {
|
|
144
|
+
console.log(formatPreviewsDeployHelp());
|
|
145
|
+
}
|
|
116
146
|
//#endregion
|
|
117
|
-
export { formatDeployHelp, printDeployHelp };
|
|
147
|
+
export { formatDeployHelp, formatPreviewsDeployHelp, printDeployHelp, printPreviewsDeployHelp };
|
package/dist/deploy.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ export type DeployOptions = {
|
|
|
12
12
|
root: string;
|
|
13
13
|
/** Deploy to preview environment (default: production) */
|
|
14
14
|
preview?: boolean;
|
|
15
|
+
/** Deploy to Preview deployment, supported only by cf. */
|
|
16
|
+
target?: {
|
|
17
|
+
type: "preview";
|
|
18
|
+
name?: string;
|
|
19
|
+
};
|
|
15
20
|
/** Wrangler environment name from wrangler.jsonc env.<name> */
|
|
16
21
|
env?: string;
|
|
17
22
|
/** Custom project name for the Worker */
|
|
@@ -120,6 +125,8 @@ export declare function parseDeployArgs(args: string[]): {
|
|
|
120
125
|
* Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
|
|
121
126
|
*/
|
|
122
127
|
export declare function withCloudflareEnv<T>(env: string | undefined, fn: () => Promise<T>): Promise<T>;
|
|
128
|
+
/** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
|
|
129
|
+
export declare function withCloudflarePreviewBuild<T>(fn: () => Promise<T>): Promise<T>;
|
|
123
130
|
export declare function resolveViteBuildMode(deploymentTool: DeploymentTool, env: string | undefined): string;
|
|
124
131
|
type WranglerDeployArgs = {
|
|
125
132
|
args: string[];
|
|
@@ -133,7 +140,7 @@ export declare function validateWranglerEnvName(env: string): string;
|
|
|
133
140
|
export declare function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerDeployArgs;
|
|
134
141
|
export declare function resolveDeploymentTool(root: string): DeploymentTool;
|
|
135
142
|
export declare function isCfCliInstalled(root: string, resolvePackageJson?: (root: string) => string | null): boolean;
|
|
136
|
-
export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env">): CfDeployArgs;
|
|
143
|
+
export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "target">): CfDeployArgs;
|
|
137
144
|
/**
|
|
138
145
|
* Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
|
|
139
146
|
*
|
|
@@ -154,7 +161,7 @@ export declare function buildWranglerInvocation(root: string, options: Pick<Depl
|
|
|
154
161
|
export declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
155
162
|
promote?: boolean;
|
|
156
163
|
}, execute?: typeof spawn): Promise<string>;
|
|
157
|
-
export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">, execute?: typeof spawn): Promise<string>;
|
|
164
|
+
export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "target">, execute?: typeof spawn): Promise<string>;
|
|
158
165
|
export declare function hasCdnWarmRequests(plan: Omit<CdnWarmRequestPlan, "pagesDataPaths"> & {
|
|
159
166
|
pagesDataPaths?: readonly string[];
|
|
160
167
|
}): boolean;
|
package/dist/deploy.js
CHANGED
|
@@ -16,7 +16,7 @@ import { setTimeout } from "node:timers/promises";
|
|
|
16
16
|
import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, discoverPrerenderPathManifest, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
|
|
17
17
|
import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
|
|
18
18
|
import { spawn } from "node:child_process";
|
|
19
|
-
import { parseArgs } from "node:util";
|
|
19
|
+
import { parseArgs, stripVTControlCharacters } from "node:util";
|
|
20
20
|
import { pathToFileURL } from "node:url";
|
|
21
21
|
import { VINEXT_BUILD_LIFECYCLE_CONFIG } from "vinext/internal/build/lifecycle";
|
|
22
22
|
import { runPrerender } from "vinext/internal/build/run-prerender";
|
|
@@ -301,6 +301,18 @@ async function withCloudflareEnv(env, fn) {
|
|
|
301
301
|
else delete process.env.CLOUDFLARE_ENV;
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
+
/** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
|
|
305
|
+
async function withCloudflarePreviewBuild(fn) {
|
|
306
|
+
const hadPrevious = "CLOUDFLARE_PREVIEW_BUILD" in process.env;
|
|
307
|
+
const previous = process.env.CLOUDFLARE_PREVIEW_BUILD;
|
|
308
|
+
process.env.CLOUDFLARE_PREVIEW_BUILD = "true";
|
|
309
|
+
try {
|
|
310
|
+
return await fn();
|
|
311
|
+
} finally {
|
|
312
|
+
if (hadPrevious) process.env.CLOUDFLARE_PREVIEW_BUILD = previous;
|
|
313
|
+
else delete process.env.CLOUDFLARE_PREVIEW_BUILD;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
304
316
|
async function loadProjectViteApi(root) {
|
|
305
317
|
let vitePath;
|
|
306
318
|
try {
|
|
@@ -384,7 +396,10 @@ function isCfCliInstalled(root, resolvePackageJson = (projectRoot) => {
|
|
|
384
396
|
}
|
|
385
397
|
function buildCfDeployArgs(options) {
|
|
386
398
|
const mode = options.env || (options.preview ? "preview" : void 0);
|
|
387
|
-
const
|
|
399
|
+
const isPreviewDeploy = options.target?.type === "preview";
|
|
400
|
+
const args = isPreviewDeploy ? ["previews", "deploy"] : ["deploy"];
|
|
401
|
+
if (isPreviewDeploy && options.target?.name) args.push(options.target.name);
|
|
402
|
+
args.push("--prebuilt");
|
|
388
403
|
if (mode) args.push("--mode", validateWranglerEnvName(mode));
|
|
389
404
|
return {
|
|
390
405
|
args,
|
|
@@ -482,7 +497,8 @@ async function runWranglerDeploy(root, options, execute = spawn) {
|
|
|
482
497
|
}
|
|
483
498
|
async function runCfDeploy(root, options, execute = spawn) {
|
|
484
499
|
const { args, mode } = buildCfDeployArgs(options);
|
|
485
|
-
|
|
500
|
+
const isPreview = options.target?.type === "preview";
|
|
501
|
+
console.log(mode ? `\n Deploying ${isPreview ? "Preview " : ""}Build Output in mode: ${mode}...` : "\n Deploying Build Output...");
|
|
486
502
|
const child = execute(process.execPath, [resolveCfBin(root), ...args], {
|
|
487
503
|
cwd: root,
|
|
488
504
|
stdio: [
|
|
@@ -493,9 +509,11 @@ async function runCfDeploy(root, options, execute = spawn) {
|
|
|
493
509
|
shell: false
|
|
494
510
|
});
|
|
495
511
|
let output = "";
|
|
512
|
+
let stdout = "";
|
|
496
513
|
child.stdout?.on("data", (chunk) => {
|
|
497
514
|
const text = chunk.toString();
|
|
498
515
|
output += text;
|
|
516
|
+
stdout += text;
|
|
499
517
|
process.stdout.write(text);
|
|
500
518
|
});
|
|
501
519
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -507,11 +525,27 @@ async function runCfDeploy(root, options, execute = spawn) {
|
|
|
507
525
|
child.once("error", reject);
|
|
508
526
|
child.once("close", (code, signal) => {
|
|
509
527
|
if (code === 0) resolve();
|
|
510
|
-
else reject(/* @__PURE__ */ new Error(`cf deploy failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
|
|
528
|
+
else reject(/* @__PURE__ */ new Error(`cf ${isPreview ? "previews deploy" : "deploy"} failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
|
|
511
529
|
});
|
|
512
530
|
});
|
|
531
|
+
if (isPreview) return parseCfPreviewUrl(stdout) ?? parseWorkerDeploymentUrl(output) ?? "(Preview URL not detected in cf output)";
|
|
513
532
|
return parseWorkerDeploymentUrl(output) ?? "(URL not detected in cf output)";
|
|
514
533
|
}
|
|
534
|
+
/** `cf previews deploy` prints a JSON result; prefer the named Preview URL over the deployment URL. */
|
|
535
|
+
function parseCfPreviewUrl(stdout) {
|
|
536
|
+
const text = stripVTControlCharacters(stdout);
|
|
537
|
+
const start = text.indexOf("{");
|
|
538
|
+
const end = text.lastIndexOf("}");
|
|
539
|
+
if (start === -1 || end < start) return null;
|
|
540
|
+
let result;
|
|
541
|
+
try {
|
|
542
|
+
result = JSON.parse(text.slice(start, end + 1));
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
for (const urls of [result.preview_urls, result.deployment_urls]) if (Array.isArray(urls) && typeof urls[0] === "string") return urls[0];
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
515
549
|
function hasCdnWarmRequests(plan) {
|
|
516
550
|
return plan.paths.length + (plan.pagesDataPaths?.length ?? 0) + (plan.routeHandlerPaths?.length ?? 0) + plan.rscPaths.length + plan.loadingShellPaths.length > 0;
|
|
517
551
|
}
|
|
@@ -1130,13 +1164,30 @@ function withPromotedVersionWarmupNote(error) {
|
|
|
1130
1164
|
const message = error instanceof Error ? error.message : String(error);
|
|
1131
1165
|
return new Error(`${message} The uploaded version is already promoted to 100% and its Worker triggers/routes were updated; rerun deploy to retry cache warming or create a deployment for the previous version.`, { cause: error });
|
|
1132
1166
|
}
|
|
1167
|
+
/** Worker Previews skip production-only steps, so reject options that would otherwise be ignored. */
|
|
1168
|
+
function assertPreviewDeployOptions(options) {
|
|
1169
|
+
const unsupported = Object.entries({
|
|
1170
|
+
preview: options.preview,
|
|
1171
|
+
name: options.name !== void 0,
|
|
1172
|
+
config: options.config !== void 0,
|
|
1173
|
+
warmCdnCache: options.warmCdnCache,
|
|
1174
|
+
warmCdnTarget: options.warmCdnTarget !== void 0,
|
|
1175
|
+
warmCdnCertify: options.warmCdnCertify,
|
|
1176
|
+
warmCdnPromote: options.warmCdnPromote === false,
|
|
1177
|
+
experimentalTPR: options.experimentalTPR
|
|
1178
|
+
}).filter(([, isSet]) => isSet).map(([option]) => option);
|
|
1179
|
+
if (unsupported.length > 0) throw new Error(`Worker Preview deployments do not support: ${unsupported.join(", ")}.`);
|
|
1180
|
+
}
|
|
1133
1181
|
async function deploy(options) {
|
|
1182
|
+
const isPreview = options.target?.type === "preview";
|
|
1183
|
+
if (isPreview) assertPreviewDeployOptions(options);
|
|
1134
1184
|
if (options.warmCdnTarget !== void 0 && !options.warmCdnCache && !options.experimentalTPR) throw new Error("--warm-cache-target requires --warm-cache or --traffic-aware-warm-cache.");
|
|
1135
1185
|
const warmCdnTarget = options.warmCdnTarget === void 0 ? void 0 : validateCdnWarmTarget(options.warmCdnTarget);
|
|
1136
1186
|
const deployEnv = options.env || (options.preview ? "preview" : void 0);
|
|
1137
1187
|
if (deployEnv) validateWranglerEnvName(deployEnv);
|
|
1138
1188
|
const root = path.resolve(options.root);
|
|
1139
1189
|
const deploymentTool = resolveDeploymentTool(root);
|
|
1190
|
+
if (isPreview && deploymentTool !== "cf") throw new Error("Worker Previews require a cloudflare.config.ts project and the cf CLI.");
|
|
1140
1191
|
if (deploymentTool === "cf" && options.name) throw new Error("--name is not supported for cloudflare.config.ts projects. Set `name` in cloudflare.config.ts instead.");
|
|
1141
1192
|
const viteMode = resolveViteBuildMode(deploymentTool, deployEnv);
|
|
1142
1193
|
const wranglerFallbackEnv = deploymentTool === "cf" ? void 0 : deployEnv;
|
|
@@ -1144,7 +1195,9 @@ async function deploy(options) {
|
|
|
1144
1195
|
root,
|
|
1145
1196
|
mode: viteMode
|
|
1146
1197
|
});
|
|
1147
|
-
|
|
1198
|
+
const cloudflareEnv = isPreview ? void 0 : deployEnv;
|
|
1199
|
+
const withBuildContext = (fn) => withCloudflareEnv(cloudflareEnv, () => isPreview ? withCloudflarePreviewBuild(fn) : fn());
|
|
1200
|
+
console.log(isPreview ? "\n vinext-cloudflare previews deploy\n" : "\n vinext-cloudflare deploy\n");
|
|
1148
1201
|
const info = detectProject(root);
|
|
1149
1202
|
if (!info.isAppRouter && !info.isPagesRouter) {
|
|
1150
1203
|
console.error(" Error: No app/ or pages/ directory found.");
|
|
@@ -1171,9 +1224,17 @@ async function deploy(options) {
|
|
|
1171
1224
|
console.log("\n Dry run complete. No build or deploy performed.\n");
|
|
1172
1225
|
return;
|
|
1173
1226
|
}
|
|
1174
|
-
|
|
1227
|
+
if (isPreview && options.skipBuild) {
|
|
1228
|
+
console.log("\n Skipping build (--skip-build)");
|
|
1229
|
+
printDeployUrl("Preview URL", await runCfDeploy(root, {
|
|
1230
|
+
env: viteMode,
|
|
1231
|
+
target: options.target
|
|
1232
|
+
}));
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const viteConfigMetadata = await withBuildContext(() => loadDeployViteConfigMetadata(info.root, viteMode));
|
|
1175
1236
|
const cdnAdapterConfig = resolveCdnAdapterConfig(viteConfigMetadata.cacheConfig);
|
|
1176
|
-
const nextConfig = await
|
|
1237
|
+
const nextConfig = await withBuildContext(async () => {
|
|
1177
1238
|
const inlineNextConfig = viteConfigMetadata.nextConfig;
|
|
1178
1239
|
const rawNextConfig = inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD);
|
|
1179
1240
|
return resolveNextConfig(rawNextConfig, info.root);
|
|
@@ -1194,10 +1255,10 @@ async function deploy(options) {
|
|
|
1194
1255
|
const shouldEmitPrerenderPathManifest = !options.skipBuild && shouldPrerenderLocally;
|
|
1195
1256
|
if (prerenderDecision && !shouldPrerenderLocally) {
|
|
1196
1257
|
const trigger = prerenderDecision.reason === "flag" ? "--prerender-all" : "vinext prerender config";
|
|
1197
|
-
const replacement = options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
|
|
1258
|
+
const replacement = isPreview ? "Preview deployments render these routes on request." : options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
|
|
1198
1259
|
console.warn(`\n Warning: ${trigger} is ignored by Cloudflare deploy. ${replacement}`);
|
|
1199
1260
|
}
|
|
1200
|
-
if (!options.skipBuild) await runBuild(info,
|
|
1261
|
+
if (!options.skipBuild) await withBuildContext(() => runBuild(info, cloudflareEnv, viteMode));
|
|
1201
1262
|
else console.log("\n Skipping build (--skip-build)");
|
|
1202
1263
|
const prerenderOutputDirs = resolvePrerenderOutputDirs(info.root, deploymentTool, viteConfigMetadata.routeRootConfig);
|
|
1203
1264
|
const canWarmTpr = options.experimentalTPR && !shouldPrerenderLocally && hasBuildIdentityHeader;
|
|
@@ -1355,15 +1416,21 @@ async function deploy(options) {
|
|
|
1355
1416
|
}
|
|
1356
1417
|
if (url === void 0) {
|
|
1357
1418
|
if (deploymentTool === "cf" && options.warmCdnPromote === false) url = runCfVersionUpload(root, wranglerOptions).previewUrl ?? "(Preview URL not detected in cf output)";
|
|
1358
|
-
else if (deploymentTool === "cf") url = await runCfDeploy(root,
|
|
1419
|
+
else if (deploymentTool === "cf") url = await runCfDeploy(root, {
|
|
1420
|
+
...wranglerOptions,
|
|
1421
|
+
target: options.target
|
|
1422
|
+
});
|
|
1359
1423
|
else url = await runWranglerDeploy(root, {
|
|
1360
1424
|
...wranglerOptions,
|
|
1361
1425
|
promote: options.warmCdnPromote
|
|
1362
1426
|
});
|
|
1363
1427
|
}
|
|
1428
|
+
printDeployUrl(isPreview ? "Preview URL" : options.warmCdnPromote === false ? "Version URL" : "Deployed to", url);
|
|
1429
|
+
}
|
|
1430
|
+
function printDeployUrl(label, url) {
|
|
1364
1431
|
console.log("\n ─────────────────────────────────────────");
|
|
1365
|
-
console.log(` ${
|
|
1432
|
+
console.log(` ${label}: ${url}`);
|
|
1366
1433
|
console.log(" ─────────────────────────────────────────\n");
|
|
1367
1434
|
}
|
|
1368
1435
|
//#endregion
|
|
1369
|
-
export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv };
|
|
1436
|
+
export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv, withCloudflarePreviewBuild };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/previews-deploy.d.ts
|
|
2
|
+
/** CLI options for `vinext-cloudflare previews deploy`. */
|
|
3
|
+
export declare function parsePreviewsDeployArgs(args: string[]): {
|
|
4
|
+
help: boolean;
|
|
5
|
+
name: string | undefined;
|
|
6
|
+
mode: string | undefined;
|
|
7
|
+
skipBuild: boolean;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
//#region src/previews-deploy.ts
|
|
3
|
+
function validatePreviewName(name) {
|
|
4
|
+
const trimmed = name.trim();
|
|
5
|
+
if (!trimmed) throw new Error("Preview name must not be empty.");
|
|
6
|
+
if (trimmed.includes("\0")) throw new Error("Preview names cannot contain null bytes.");
|
|
7
|
+
if (trimmed.startsWith("-")) throw new Error("Preview name must not start with '-'.");
|
|
8
|
+
return trimmed;
|
|
9
|
+
}
|
|
10
|
+
function validateMode(mode) {
|
|
11
|
+
const trimmed = mode.trim();
|
|
12
|
+
if (!trimmed) throw new Error("--mode requires a non-empty value.");
|
|
13
|
+
return trimmed;
|
|
14
|
+
}
|
|
15
|
+
/** CLI options for `vinext-cloudflare previews deploy`. */
|
|
16
|
+
function parsePreviewsDeployArgs(args) {
|
|
17
|
+
const { values, positionals } = parseArgs({
|
|
18
|
+
args,
|
|
19
|
+
allowPositionals: true,
|
|
20
|
+
strict: true,
|
|
21
|
+
options: {
|
|
22
|
+
help: {
|
|
23
|
+
type: "boolean",
|
|
24
|
+
short: "h",
|
|
25
|
+
default: false
|
|
26
|
+
},
|
|
27
|
+
mode: { type: "string" },
|
|
28
|
+
"skip-build": {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
default: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (positionals.length > 1) throw new Error("Expected at most one Preview name: previews deploy [preview-name].");
|
|
35
|
+
return {
|
|
36
|
+
help: values.help,
|
|
37
|
+
name: positionals[0] === void 0 ? void 0 : validatePreviewName(positionals[0]),
|
|
38
|
+
mode: values.mode === void 0 ? void 0 : validateMode(values.mode),
|
|
39
|
+
skipBuild: values["skip-build"]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { parsePreviewsDeployArgs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vinext/cloudflare",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Cloudflare cache and image adapters for vinext.",
|
|
5
5
|
"homepage": "https://vinext.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cloudflare/workers-response-store": "1.1.
|
|
51
|
+
"@cloudflare/workers-response-store": "1.1.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.9.2",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
58
58
|
"vite-plus": "1.1.0",
|
|
59
59
|
"vitest": "5.0.3",
|
|
60
|
-
"vinext": "1.
|
|
60
|
+
"vinext": "1.3.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"@cloudflare/vite-plugin": "*",
|
|
64
64
|
"cf": "*",
|
|
65
|
-
"vinext": "^1.
|
|
65
|
+
"vinext": "^1.3.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"@cloudflare/vite-plugin": {
|