Skip to content
Merged
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: 5 additions & 0 deletions .changeset/flat-zoos-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/cli": minor
---

Error on unsupported flags for individual CLI commands and print the matching command usage to make mistakes easier to spot.
10 changes: 10 additions & 0 deletions packages/cli/src/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const COMMAND_HELP: Record<string, string> = {
init: "init",
add: "add [--empty] [--open] [--since <branch>] [--message <text>]",
version:
"version [--ignore] [--snapshot <?name>] [--snapshot-prerelease-template <template>]",
publish: "publish [--tag <name>] [--otp <code>] [--no-git-tag]",
status: "status [--since <branch>] [--verbose] [--output JSON_FILE.json]",
pre: "pre <enter|exit> <tag>",
tag: "tag",
};
51 changes: 22 additions & 29 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@ import mri from "mri";
import { ExitError, InternalError } from "@changesets/errors";
import { error } from "@changesets/logger";
import { format } from "util";
import { COMMAND_HELP } from "./help";
import { run } from "./run";

const args = process.argv.slice(2);
const aliases = {
// Short flags
v: "verbose",
o: "output",
m: "message",
// Support kebab-case flags
"since-master": "sinceMaster",
"git-tag": "gitTag",
"snapshot-prerelease-template": "snapshotPrereleaseTemplate",
// Deprecated flags
"update-changelog": "updateChangelog",
"is-public": "isPublic",
"skip-c-i": "skipCI",
};

const parsed = mri(args, {
boolean: ["sinceMaster", "verbose", "empty", "open", "gitTag", "snapshot"],
Expand All @@ -18,23 +33,8 @@ const parsed = mri(args, {
"snapshot",
"snapshotPrereleaseTemplate",
],
alias: {
// Short flags
v: "verbose",
o: "output",
m: "message",
// Support kebab-case flags
"since-master": "sinceMaster",
"git-tag": "gitTag",
"snapshot-prerelease-template": "snapshotPrereleaseTemplate",
// Deprecated flags
"update-changelog": "updateChangelog",
"is-public": "isPublic",
"skip-c-i": "skipCI",
},
default: {
gitTag: true,
},
// mri mutates the alias object passed to it, so we need to copy it here to maintain the original object
alias: { ...aliases },
});

// `mri` doesn't handle mixed boolean and strings well. It'll always try to coerce it as
Expand All @@ -44,17 +44,6 @@ if (parsed.snapshot === "" && args[args.indexOf("--snapshot") + 1] !== "") {
parsed.snapshot = true;
}

const COMMAND_HELP: Record<string, string> = {
init: "init",
add: "add [--empty] [--open] [--since <branch>] [--message <text>]",
version:
"version [--ignore] [--snapshot <?name>] [--snapshot-prerelease-template <template>]",
publish: "publish [--tag <name>] [--otp <code>] [--no-git-tag]",
status: "status [--since <branch>] [--verbose] [--output JSON_FILE.json]",
pre: "pre <enter|exit> <tag>",
tag: "tag",
};

if (parsed.help) {
const command = parsed._[0];
if (command && COMMAND_HELP[command]) {
Expand Down Expand Up @@ -89,8 +78,12 @@ if (parsed.version && args.length === 1) {
}

const cwd = process.cwd();
const flags = { ...parsed };
for (const flag of ["_", ...Object.keys(aliases)]) {
delete flags[flag];
}

run(parsed._, parsed, cwd).catch((err) => {
run(parsed._, flags, cwd).catch((err) => {
if (err instanceof InternalError) {
error(
"The following error is an internal unexpected error, these should never happen."
Expand Down
61 changes: 43 additions & 18 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,26 @@ import publish from "./commands/publish";
import status from "./commands/status";
import tagCommand from "./commands/tag";
import version from "./commands/version";
import { COMMAND_HELP } from "./help";
import { CliOptions } from "./types";

function validateCommandFlags(
command: keyof typeof COMMAND_HELP,
flags: Record<string, unknown>
) {
const unknownFlags = Object.keys(flags);

if (unknownFlags.length > 0) {
error(
`Unknown ${
unknownFlags.length === 1 ? "flag" : "flags"
} for ${command}: ${unknownFlags.map((flag) => `--${flag}`).join(", ")}`
);
error(`Usage: changeset ${COMMAND_HELP[command]}`);
throw new ExitError(1);
}
}

export async function run(
input: string[],
flags: { [name: string]: any },
Expand All @@ -25,6 +43,7 @@ export async function run(
const rootDir = packages.root.dir;

if (input[0] === "init") {
validateCommandFlags("init", flags);
await init(rootDir);
return;
}
Expand Down Expand Up @@ -65,30 +84,20 @@ export async function run(
}

if (input.length < 1) {
const { empty, open, since, message }: CliOptions = flags;
// @ts-ignore if this is undefined, we have already exited
const { empty, open, since, message, ...rest }: CliOptions = flags;
validateCommandFlags("add", rest);
await add(rootDir, { empty, open, since, message }, config);
} else if (input[0] !== "pre" && input.length > 1) {
error(
"Too many arguments passed to changesets - we only accept the command name as an argument"
);
} else {
const {
sinceMaster,
since,
verbose,
output,
otp,
empty,
ignore,
snapshot,
snapshotPrereleaseTemplate,
tag,
open,
gitTag,
message,
}: CliOptions = flags;
const deadFlags = ["updateChangelog", "isPublic", "skipCI", "commit"];
const deadFlags = [
"updateChangelog",
"isPublic",
"skipCI",
"commit",
] as const;

deadFlags.forEach((flag) => {
if (flags[flag]) {
Expand All @@ -108,10 +117,19 @@ export async function run(

switch (input[0]) {
case "add": {
const { empty, open, since, message, ...rest }: CliOptions = flags;
validateCommandFlags("add", rest);
await add(rootDir, { empty, open, since, message }, config);
return;
}
case "version": {
const {
ignore,
snapshot,
snapshotPrereleaseTemplate,
...rest
}: CliOptions = flags;
validateCommandFlags("version", rest);
let ignoreArrayFromCmd: undefined | string[];
if (typeof ignore === "string") {
ignoreArrayFromCmd = [ignore];
Expand Down Expand Up @@ -204,18 +222,25 @@ export async function run(
return;
}
case "publish": {
const { otp, tag, gitTag, ...rest }: CliOptions = flags;
validateCommandFlags("publish", rest);
await publish(rootDir, { otp, tag, gitTag }, config);
return;
}
case "status": {
const { sinceMaster, since, verbose, output, ...rest }: CliOptions =
flags;
validateCommandFlags("status", rest);
await status(rootDir, { sinceMaster, since, verbose, output }, config);
return;
}
case "tag": {
validateCommandFlags("tag", flags);
await tagCommand(rootDir, config);
return;
}
case "pre": {
validateCommandFlags("pre", flags);
let command = input[1];
if (command !== "enter" && command !== "exit") {
error(
Expand Down
Loading