vercel
Version:
The command-line interface for Vercel
402 lines (398 loc) • 12.9 kB
JavaScript
import { createRequire as __createRequire } from 'node:module';
import { fileURLToPath as __fileURLToPath } from 'node:url';
import { dirname as __dirname_ } from 'node:path';
const require = __createRequire(import.meta.url);
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __dirname_(__filename);
import {
getRouteVersions
} from "./chunk-AHU7WNL2.js";
import {
ensureProjectLink,
getRoutes,
offerAutoPromote,
parsePosition,
parseSubcommandArgs,
resolveRoute,
shellQuoteRouteIdentifierForSuggestion,
withGlobalFlags
} from "./chunk-XNUHSM7I.js";
import {
reorderSubcommand
} from "./chunk-JFVGRFME.js";
import "./chunk-E3NE4SKN.js";
import "./chunk-X775BOSL.js";
import "./chunk-4OEA5ILS.js";
import {
outputAgentError
} from "./chunk-ULXHXZCZ.js";
import {
stamp_default
} from "./chunk-CO5D46AG.js";
import "./chunk-N2T234LO.js";
import "./chunk-4GQQJY5Y.js";
import {
getCommandName
} from "./chunk-UGXBNJMO.js";
import "./chunk-P4QNYOFB.js";
import {
output_manager_default
} from "./chunk-ZQKJVHXY.js";
import {
require_source
} from "./chunk-S7KYDPEM.js";
import {
__toESM
} from "./chunk-TZ2YI2VH.js";
// src/commands/routes/reorder.ts
var import_chalk = __toESM(require_source(), 1);
// src/util/routes/stage-routes.ts
async function stageRoutes(client, projectId, routes, overwrite, options = {}) {
const { teamId } = options;
const query = new URLSearchParams();
if (teamId)
query.set("teamId", teamId);
const queryString = query.toString();
const url = `/v1/projects/${projectId}/routes${queryString ? `?${queryString}` : ""}`;
return await client.fetch(url, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ routes, overwrite })
});
}
// src/commands/routes/reorder.ts
async function reorder(client, argv) {
const parsed = await parseSubcommandArgs(argv, reorderSubcommand, client);
if (typeof parsed === "number")
return parsed;
const link = await ensureProjectLink(client);
if (typeof link === "number")
return link;
const { project, org } = link;
const teamId = org.type === "team" ? org.id : void 0;
const { args, flags } = parsed;
const skipConfirmation = flags["--yes"];
const identifier = args[0];
if (!identifier) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "missing_arguments",
message: "Route name or ID and position are required.",
next: [
{
command: withGlobalFlags(
client,
"routes reorder <name-or-id> --position <pos> --yes"
),
when: "replace placeholders; --first/--last or numeric position"
}
]
},
1
);
}
output_manager_default.error(
`Route name or ID is required. Usage: ${getCommandName("routes reorder <name-or-id> --position <pos>")}`
);
return 1;
}
const { versions } = await getRouteVersions(client, project.id, { teamId });
const existingStagingVersion = versions.find((v) => v.isStaging);
output_manager_default.spinner("Fetching routes");
const { routes } = await getRoutes(client, project.id, { teamId });
output_manager_default.stopSpinner();
if (routes.length === 0) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "not_found",
message: "No routes found in this project."
},
1
);
}
output_manager_default.error("No routes found in this project.");
return 1;
}
if (routes.length === 1) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "invalid_arguments",
message: "Cannot reorder when there is only one route."
},
1
);
}
output_manager_default.error("Cannot reorder when there is only one route.");
return 1;
}
const route = await resolveRoute(client, routes, identifier);
if (!route) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "not_found",
message: `No route found matching "${identifier}".`,
next: [
{
command: withGlobalFlags(client, "routes list"),
when: "list routes"
}
]
},
1
);
}
output_manager_default.error(
`No route found matching "${identifier}". Run ${import_chalk.default.cyan(
getCommandName("routes list")
)} to see all routes.`
);
return 1;
}
const currentIndex = routes.findIndex((r) => r.id === route.id);
const currentPosition = currentIndex + 1;
let targetIndex;
const positionFlag = flags["--position"];
const firstFlag = flags["--first"];
const lastFlag = flags["--last"];
if (firstFlag) {
targetIndex = 0;
} else if (lastFlag) {
targetIndex = routes.length;
} else if (positionFlag) {
if (positionFlag === "first") {
targetIndex = 0;
} else if (positionFlag === "last") {
targetIndex = routes.length;
} else {
const num = parseInt(positionFlag, 10);
if (!isNaN(num) && String(num) === positionFlag) {
if (num < 1) {
const msg = "Position must be 1 or greater. Positions are 1-based (e.g. --position 1 for first). Use --first or --last instead of 0.";
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "invalid_arguments",
message: msg,
next: [
{
command: withGlobalFlags(
client,
`routes reorder ${shellQuoteRouteIdentifierForSuggestion(identifier)} --first --yes`
),
when: "move to first position"
},
{
command: withGlobalFlags(
client,
`routes reorder ${shellQuoteRouteIdentifierForSuggestion(identifier)} --position 1 --yes`
),
when: "same as first (1-based index)"
},
{
command: withGlobalFlags(
client,
`routes reorder ${shellQuoteRouteIdentifierForSuggestion(identifier)} --last --yes`
),
when: "move to last position"
}
],
hint: "Numeric --position uses 1-based indices; there is no position 0."
},
1
);
return 1;
}
output_manager_default.error(msg);
return 1;
}
targetIndex = Math.min(num - 1, routes.length);
} else {
try {
const pos = parsePosition(positionFlag);
if (pos.placement === "start") {
targetIndex = 0;
} else if (pos.placement === "end") {
targetIndex = routes.length;
} else if (pos.placement === "after" && pos.referenceId) {
const refIndex = routes.findIndex((r) => r.id === pos.referenceId);
if (refIndex === -1) {
output_manager_default.error(
`Reference route "${pos.referenceId}" not found. Run ${import_chalk.default.cyan(
getCommandName("routes list")
)} to see route IDs.`
);
return 1;
}
targetIndex = refIndex + 1;
} else if (pos.placement === "before" && pos.referenceId) {
const refIndex = routes.findIndex((r) => r.id === pos.referenceId);
if (refIndex === -1) {
output_manager_default.error(
`Reference route "${pos.referenceId}" not found. Run ${import_chalk.default.cyan(
getCommandName("routes list")
)} to see route IDs.`
);
return 1;
}
targetIndex = refIndex;
} else {
output_manager_default.error("Invalid position specification.");
return 1;
}
} catch (e) {
output_manager_default.error(
`${e instanceof Error ? e.message : "Invalid position"}. Usage: ${getCommandName("routes reorder <name-or-id> --position <pos>")}`
);
return 1;
}
}
}
} else {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "missing_arguments",
message: "Target position is required. Use --position <n|first|last|after:id|before:id> or --first / --last.",
next: [
{
command: withGlobalFlags(
client,
`routes reorder ${shellQuoteRouteIdentifierForSuggestion(identifier)} --position <pos> --yes`
),
when: "replace <pos> with numeric position or first/last"
}
]
},
1
);
}
output_manager_default.print("\n");
output_manager_default.log("Current route order:");
for (let i = 0; i < routes.length; i++) {
const r = routes[i];
const isCurrent = r.id === route.id;
const prefix = isCurrent ? import_chalk.default.cyan("\u2192") : " ";
const num = import_chalk.default.gray(`${i + 1}.`);
const name = isCurrent ? import_chalk.default.bold(r.name) : r.name;
const src = import_chalk.default.gray(`(${r.route.src})`);
const marker = isCurrent ? import_chalk.default.cyan(" \u2190 current") : "";
output_manager_default.print(` ${prefix} ${num} ${name} ${src}${marker}
`);
}
output_manager_default.print("\n");
const input = await client.input.text({
message: `Move "${route.name}" to position (1-${routes.length}, "first", or "last"):`,
validate: (val) => {
if (!val)
return "Position is required";
if (val === "first" || val === "last")
return true;
const num = parseInt(val, 10);
if (isNaN(num) || num < 1 || num > routes.length) {
return `Enter a number between 1 and ${routes.length}, "first", or "last"`;
}
return true;
}
});
if (input === "first") {
targetIndex = 0;
} else if (input === "last") {
targetIndex = routes.length;
} else {
targetIndex = parseInt(input, 10) - 1;
}
}
const adjustedTarget = currentIndex < targetIndex ? targetIndex - 1 : targetIndex;
const clampedTarget = Math.max(
0,
Math.min(adjustedTarget, routes.length - 1)
);
if (currentIndex === clampedTarget) {
output_manager_default.log(
`Route "${route.name}" is already at position ${currentPosition}.`
);
return 0;
}
const finalPosition = clampedTarget + 1;
if (!skipConfirmation) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "confirmation_required",
message: "Reorder requires confirmation. Pass --yes in non-interactive mode.",
next: [
{
command: withGlobalFlags(
client,
`routes reorder ${shellQuoteRouteIdentifierForSuggestion(identifier)} --position ${finalPosition} --yes`
),
when: "re-run with --yes"
}
]
},
1
);
}
const confirmed = await client.input.confirm(
`Move "${route.name}" from position ${currentPosition} to position ${finalPosition}?`,
true
);
if (!confirmed) {
output_manager_default.log("Aborted.");
return 0;
}
}
const reordered = [...routes];
reordered.splice(currentIndex, 1);
reordered.splice(clampedTarget, 0, route);
const reorderStamp = stamp_default();
output_manager_default.spinner(`Moving route "${route.name}"`);
try {
const { version } = await stageRoutes(
client,
project.id,
reordered,
true,
// overwrite
{ teamId }
);
output_manager_default.log(
`${import_chalk.default.cyan("Moved")} "${route.name}" from position ${currentPosition} to ${finalPosition} ${import_chalk.default.gray(reorderStamp())}`
);
await offerAutoPromote(
client,
project.id,
version,
!!existingStagingVersion,
{ teamId, skipPrompts: skipConfirmation }
);
return 0;
} catch (e) {
const error = e;
output_manager_default.error(error.message || "Failed to reorder route");
return 1;
}
}
export {
reorder as default
};