core/parsers/invocation.parser.ts
core/parsers/invocation.parser.ts is a file in GovLab Quality. 113 lines of code and 17 definitions.
import {
CONCERNS,
CONFIG_FLAG,
FLAG_AND_VALUE,
FLAG_PREFIX,
INSTALL_CONCERN,
REPORTERS,
VALUE_FLAGS,
} from "#configuration/constants/concern.constants";
import type { CliArgs, Concern, Reporter } from "#types/quality.types";
import { missingFlagValue, unknownConcern, unknownFlag, unknownReporter } from "#configuration/strings/tool.strings";
const CONCERN_SET: ReadonlySet<string> = new Set(Object.keys(CONCERNS));
const LIST_SEPARATOR = ", ";
const BOOLEAN_FLAGS: ReadonlyMap<string, (args: CliArgs) => void> = new Map([
[
"--auto",
(args: CliArgs): void => {
args.auto = true;
},
],
[
"--backup",
(args: CliArgs): void => {
args.backup = true;
},
],
[
"--dry-run",
(args: CliArgs): void => {
args.dryRun = true;
},
],
[
"--no-fix",
(args: CliArgs): void => {
args.fix = false;
},
],
]);
const isConcern = function isConcern(value: string): value is Concern {
return CONCERN_SET.has(value);
};
const isReporter = function isReporter(value: string): value is Reporter {
return [...REPORTERS].some((reporter) => reporter === value);
};
const applyValueFlag = function applyValueFlag(args: CliArgs, flag: string, value: string | undefined): void {
if (value === undefined || value.length === 0 || value.startsWith(FLAG_PREFIX)) {
throw new Error(missingFlagValue(flag));
}
if (flag === CONFIG_FLAG) {
args.config = value;
return;
}
if (!isReporter(value)) {
throw new Error(unknownReporter(value, [...REPORTERS].join(LIST_SEPARATOR)));
}
args.reporter = value;
};
const defaultArgs = function defaultArgs(concern: Concern): CliArgs {
return {
auto: false,
backup: false,
concern,
config: null,
dryRun: false,
ecosystems: [],
fix: true,
paths: [],
reporter: "human",
};
};
const consumeToken = function consumeToken(
args: CliArgs,
rest: readonly string[],
cursor: number,
): { next: number; positional: string | null } {
const arg = rest[cursor] ?? "";
if (VALUE_FLAGS.has(arg)) {
applyValueFlag(args, arg, rest[cursor + 1]);
return { next: cursor + FLAG_AND_VALUE, positional: null };
}
const setter = BOOLEAN_FLAGS.get(arg);
if (setter) {
setter(args);
return { next: cursor + 1, positional: null };
}
if (arg.startsWith(FLAG_PREFIX)) {
throw new Error(unknownFlag(arg));
}
return { next: cursor + 1, positional: arg };
};
export const parseCliArgs = function parseCliArgs(argv: readonly string[]): CliArgs {
const [concern] = argv;
if (typeof concern !== "string" || !isConcern(concern)) {
throw new Error(unknownConcern(String(concern), [...CONCERN_SET].join(LIST_SEPARATOR)));
}
const args = defaultArgs(concern);
const rest = argv.slice(1);
const positional: string[] = [];
for (let cursor = 0; cursor < rest.length;) {
const step = consumeToken(args, rest, cursor);
if (step.positional !== null) {
positional.push(step.positional);
}
cursor = step.next;
}
if (concern === INSTALL_CONCERN) {
args.ecosystems = positional;
} else {
args.paths = positional;
}
return args;
};