Skip to content
Closed
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: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Your existing `pages/`, `app/`, `next.config.js`, and `public/` directories work

Options: `-p / --port <port>`, `-H / --hostname <host>`, `--turbopack` (accepted, no-op).

`vinext deploy` options: `--preview`, `--name <name>`, `--skip-build`, `--dry-run`, `--experimental-tpr`.
`vinext deploy` options: `--preview`, `--env <name>`, `--name <name>`, `--skip-build`, `--dry-run`, `--experimental-tpr`.

`vinext init` options: `--port <port>` (default: 3001), `--skip-check`, `--force`.

Expand Down Expand Up @@ -157,8 +157,11 @@ We track the public Next.js API surface and add support for new stable features.

```bash
vinext deploy
vinext deploy --env staging
```

Use `--env <name>` to target `wrangler.jsonc` `env.<name>`. `--preview` is shorthand for `--env preview`.

The deploy command also auto-detects and fixes common migration issues:
- Adds `"type": "module"` to package.json if missing
- Resolves tsconfig.json path aliases automatically (via `vite-tsconfig-paths`)
Expand Down
34 changes: 24 additions & 10 deletions packages/vinext/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ function parseNumericFlag(args: string[], flag: string): number | undefined {
return undefined;
}

/** Parse a string flag like --name my-app or --name=my-app. */
function parseStringFlag(args: string[], flag: string): string | undefined {
const idx = args.indexOf(flag);
const next = idx !== -1 ? args[idx + 1] : undefined;
if (next && !next.startsWith("-")) {
return next;
}

const eq = args.find((a) => a.startsWith(`${flag}=`));
if (eq) {
const value = eq.slice(flag.length + 1);
if (value) return value;
}

return undefined;
}

// ─── Auto-configuration ───────────────────────────────────────────────────────

/**
Expand Down Expand Up @@ -344,15 +361,9 @@ async function deployCommand() {
const dryRun = rawArgs.includes("--dry-run");
const experimentalTPR = rawArgs.includes("--experimental-tpr");

// Parse --name flag
let name: string | undefined;
const nameIdx = rawArgs.indexOf("--name");
if (nameIdx !== -1 && rawArgs[nameIdx + 1]) {
name = rawArgs[nameIdx + 1];
} else {
const nameEq = rawArgs.find((a) => a.startsWith("--name="));
if (nameEq) name = nameEq.split("=")[1];
}
// Parse string flags
const name = parseStringFlag(rawArgs, "--name");
const env = parseStringFlag(rawArgs, "--env");

// Parse TPR flags
const tprCoverage = parseNumericFlag(rawArgs, "--tpr-coverage");
Expand All @@ -362,6 +373,7 @@ async function deployCommand() {
await runDeploy({
root: process.cwd(),
preview,
env,
skipBuild,
dryRun,
name,
Expand Down Expand Up @@ -467,7 +479,8 @@ function printHelp(cmd?: string) {
- Deploys via wrangler

Options:
--preview Deploy to a preview environment
--preview Deploy to preview environment (same as --env preview)
--env <name> Deploy using wrangler env.<name>
--name <name> Custom Worker name (default: from package.json)
--skip-build Skip the build step (use existing dist/)
--dry-run Generate config files without building or deploying
Expand All @@ -488,6 +501,7 @@ function printHelp(cmd?: string) {
Examples:
vinext deploy Build and deploy to production
vinext deploy --preview Deploy to a preview URL
vinext deploy --env staging Deploy using wrangler env.staging
vinext deploy --dry-run See what files would be generated
vinext deploy --name my-app Deploy with a custom Worker name
vinext deploy --experimental-tpr Enable TPR during deploy
Expand Down
28 changes: 24 additions & 4 deletions packages/vinext/src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export interface DeployOptions {
root: string;
/** Deploy to preview environment (default: production) */
preview?: boolean;
/** Wrangler environment name from wrangler.jsonc env.<name> */
env?: string;
/** Custom project name for the Worker */
name?: string;
/** Skip the build step (assume already built) */
Expand Down Expand Up @@ -661,7 +663,16 @@ async function runBuild(info: ProjectInfo): Promise<void> {

// ─── Deploy ──────────────────────────────────────────────────────────────────

function runWranglerDeploy(root: string, preview: boolean): string {
export function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env">): string[] {
const args = ["deploy"];
const env = options.env?.trim() || (options.preview ? "preview" : undefined);
if (env) {
args.push("--env", env);
}
return args;
}

function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">): string {
const wranglerBin = path.join(root, "node_modules", ".bin", "wrangler");

const execOpts: ExecSyncOptions = {
Expand All @@ -671,10 +682,16 @@ function runWranglerDeploy(root: string, preview: boolean): string {
};

// wrangler deploy outputs the URL to stdout
const args = preview ? ["deploy", "--env", "preview"] : ["deploy"];
const args = buildWranglerDeployArgs(options);
const cmd = `"${wranglerBin}" ${args.join(" ")}`;
const envFlagIndex = args.indexOf("--env");
const deployEnv = envFlagIndex === -1 ? null : args[envFlagIndex + 1];

console.log(preview ? "\n Deploying to preview..." : "\n Deploying to production...");
if (deployEnv) {
console.log(`\n Deploying to env: ${deployEnv}...`);
} else {
console.log("\n Deploying to production...");
}

const output = execSync(cmd, execOpts) as string;

Expand Down Expand Up @@ -776,7 +793,10 @@ export async function deploy(options: DeployOptions): Promise<void> {
}

// Step 7: Deploy via wrangler
const url = runWranglerDeploy(root, options.preview ?? false);
const url = runWranglerDeploy(root, {
preview: options.preview ?? false,
env: options.env,
});

console.log("\n ─────────────────────────────────────────");
console.log(` Deployed to: ${url}`);
Expand Down
25 changes: 25 additions & 0 deletions tests/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getFilesToGenerate,
ensureESModule,
renameCJSConfigs,
buildWranglerDeployArgs,
} from "../packages/vinext/src/deploy.js";
import { computeLazyChunks } from "../packages/vinext/src/index.js";

Expand Down Expand Up @@ -43,6 +44,30 @@ afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});

// ─── Wrangler deploy args ───────────────────────────────────────────────────

describe("buildWranglerDeployArgs", () => {
it("uses plain deploy for production by default", () => {
expect(buildWranglerDeployArgs({})).toEqual(["deploy"]);
});

it("maps --preview to wrangler --env preview", () => {
expect(buildWranglerDeployArgs({ preview: true })).toEqual(["deploy", "--env", "preview"]);
});

it("passes through explicit env names", () => {
expect(buildWranglerDeployArgs({ env: "staging" })).toEqual(["deploy", "--env", "staging"]);
});

it("prefers explicit env over --preview shorthand", () => {
expect(buildWranglerDeployArgs({ preview: true, env: "qa" })).toEqual([
"deploy",
"--env",
"qa",
]);
});
});

// ─── detectProject ──────────────────────────────────────────────────────────

describe("detectProject", () => {
Expand Down