Add support for valueless --changed
MODIFY
packages/@romejs/cli-flags/Parser.ts
packages/@romejs/cli-flags/Parser.ts
@@ -17,7 +17,6 @@ import {
ConsumeSourceLocationRequestTarget,
} from '@romejs/consume';
import {toKebabCase, toCamelCase} from '@romejs/string-utils';
-import {DiagnosticsError} from '@romejs/diagnostics';
import {createUnknownFilePath} from '@romejs/path';
import {Dict} from '@romejs/typescript-helpers';
import {markup} from '@romejs/string-markup';
@@ -74,6 +73,10 @@ export default class Parser<T> {
this.flags = new Map();
this.args = [];
+ // These are used to track where we should insert an argument for a boolean flag value
+ this.flagToArgIndex = new Map();
+ this.flagToArgOffset = 0;
+
this.consumeRawArgs(rawArgs);
this.commands = new Map();
@@ -120,6 +123,8 @@ export default class Parser<T> {
flags: Map<string, string | boolean>;
defaultFlags: Map<string, unknown>;
declaredFlags: Map<string, ArgDeclaration>;
+ flagToArgIndex: Map<string, number>;
+ flagToArgOffset: number;
ranCommand: undefined | string;
commands: Map<string, AnyCommandOptions>;
@@ -158,6 +163,8 @@ export default class Parser<T> {
this.flags.set(name, String(rawArgs.shift()));
}
+ this.flagToArgIndex.set(name, this.args.length);
+
if (arg[0] === '-' && arg[1] !== '-') {
this.shorthandFlags.add(name);
}
@@ -174,6 +181,12 @@ export default class Parser<T> {
this.flags.delete(key);
this.flags.set(alias, value);
}
+
+ const argIndex = this.flagToArgIndex.get(key);
+ if (argIndex !== undefined) {
+ this.flagToArgIndex.set(alias, argIndex);
+ this.flagToArgIndex.delete(key);
+ }
}
getFlagsConsumer(): Consumer {
@@ -188,7 +201,7 @@ export default class Parser<T> {
filePath: createUnknownFilePath('argv'),
value: flags,
- onDefinition: (def) => {
+ onDefinition: (def, valueConsumer) => {
const key = def.objectPath.join('.');
// Detect root object
@@ -196,6 +209,34 @@ export default class Parser<T> {
return;
}
+ const value = flags[key];
+
+ // Allow omitting a string flag value
+ if (def.type === 'string' && value === true) {
+ valueConsumer.setValue('');
+ }
+
+ // We've parsed arguments like `--foo bar` as `{foo: 'bar}`
+
+ // However, --foo may be a boolean flag, so `bar` needs to be correctly added to args
+ if (def.type === 'boolean' && value !== true && value !== false &&
+ value !== undefined) {
+ // This isn't necessarily the correct position... Probably doesn't matter?
+ const argIndex = this.flagToArgIndex.get(key);
+ if (argIndex === undefined) {
+ throw new Error('No arg index. Should always exist.');
+ }
+
+ // Insert the argument at the correct place
+ this.args.splice(argIndex + this.flagToArgOffset, 0, String(value));
+
+ // Increase offset to correct subsequent insertions
+ this.flagToArgOffset++;
+
+ //
+ valueConsumer.setValue(true);
+ }
+
this.declareArgument({
name: key,
command: this.currentCommand,
@@ -259,7 +300,7 @@ export default class Parser<T> {
return new ParserInterface(this);
}
- async shouldRunCommand(
+ async maybeDefineCommand(
commandName: string,
consumer: Consumer,
): Promise<undefined | DefinedCommand> {
@@ -283,37 +324,21 @@ export default class Parser<T> {
process.exit(0);
}
- // We've parsed arguments like `--foo bar` as `{foo: 'bar}`
-
- // However, --foo may be a boolean flag, so `bar` needs to be correctly added to args
- for (const [key, value] of this.flags) {
- const declared = this.declaredFlags.get(key);
-
- if (declared !== undefined && declared.definition.type === 'boolean' &&
- value !== true && value !== false) {
- // This isn't necessarily the correct position... Probably doesn't matter?
- this.args.push(value);
-
- //
- this.flags.set(key, true);
- }
- }
-
const consumer = this.getFlagsConsumer();
let definedCommand: undefined | DefinedCommand;
- const {diagnostics, result} = await consumer.capture(async (consumer) => {
+ const rootFlags = await consumer.captureDiagnostics(async (consumer) => {
for (const shorthandName of this.shorthandFlags) {
consumer.get(shorthandName).unexpected(
`Shorthand flags are not supported`,
);
}
- const result = this.opts.defineFlags(consumer);
+ const rootFlags = this.opts.defineFlags(consumer);
for (const key of this.commands.keys()) {
- const defined = await this.shouldRunCommand(key, consumer);
+ const defined = await this.maybeDefineCommand(key, consumer);
if (defined) {
this.currentCommand = key;
definedCommand = defined;
@@ -324,13 +349,9 @@ export default class Parser<T> {
consumer.enforceUsedProperties('flag', false);
this.currentCommand = undefined;
- return result;
+ return rootFlags;
});
- if (result === undefined) {
- throw new DiagnosticsError('CLI flag parsing diagnostics', diagnostics);
- }
-
// Show help for --help
if (this.helpMode) {
await this.showHelp(definedCommand === undefined
@@ -344,7 +365,7 @@ export default class Parser<T> {
await definedCommand.command.callback(definedCommand.flags);
}
- return result;
+ return rootFlags;
}
buildOptionsHelp(keys: Array<string>): Array<string> {
@@ -484,6 +505,9 @@ export default class Parser<T> {
const {description, usage, examples, programName} = this.opts;
const consumer = this.getFlagsConsumer();
+
+ // Supress diagnostics
+ await consumer.capture(async (consumer) => {
await this.opts.defineFlags(consumer);
for (const key of this.commands.keys()) {
@@ -491,6 +515,7 @@ export default class Parser<T> {
}
this.showUsageHelp(description, usage);
+ });
const {reporter} = this;
reporter.section('Global Flags', () => {

MODIFY
packages/@romejs/consume/Consumer.ts
packages/@romejs/consume/Consumer.ts
@@ -145,7 +145,11 @@ export default class Consumer {
const definitions: Array<ConsumePropertyDefinition> = [];
const consumer = this.clone({
- onDefinition(def) {
+ onDefinition: (def, consumer) => {
+ if (this.onDefinition !== undefined) {
+ this.onDefinition(def, consumer);
+ }
+
definitions.push(def);
},
@@ -158,6 +162,16 @@ export default class Consumer {
return {result, definitions, diagnostics};
}
+ async captureDiagnostics<T>(
+ callback: (consumer: Consumer) => Promise<T> | T,
+ ): Promise<T> {
+ const {result, diagnostics} = await this.capture(callback);
+ if (result === undefined || diagnostics.length > 0) {
+ throw new DiagnosticsError('Captured diagnostics', diagnostics);
+ }
+ return result;
+ }
+
handleThrownDiagnostics(callback: () => void) {
if (this.handleUnexpected === undefined) {
callback();
@@ -185,7 +199,7 @@ export default class Consumer {
...def,
objectPath: this.keyPath,
metadata: this.propertyMetadata,
- } as ConsumePropertyDefinition));
+ } as ConsumePropertyDefinition), this);
}
}
@@ -782,7 +796,7 @@ export default class Consumer {
});
if (this.exists()) {
- return this.asString(def);
+ return this._asString(def);
} else {
return undefined;
}

MODIFY
packages/@romejs/consume/types.ts
packages/@romejs/consume/types.ts
@@ -64,7 +64,7 @@ export type ConsumePropertyDefinition =
| ConsumePropertyPrimitiveDefinition
| ConsumePropertyNumberRangeDefinition;
-export type ConsumerOnDefinition = (definition: ConsumePropertyDefinition) => void;
+export type ConsumerOnDefinition = (definition: ConsumePropertyDefinition, consumer: Consumer) => void;
export type ConsumerHandleUnexpected = (diagnostic: Diagnostic) => void;

MODIFY
packages/@romejs/core/master/commands/lint.ts
packages/@romejs/core/master/commands/lint.ts
@@ -13,7 +13,7 @@ import {Consumer} from '@romejs/consume';
type Flags = {
fix: boolean;
- changes: undefined | string;
+ changed: undefined | string;
};
export default createMasterCommand<Flags>({
@@ -23,7 +23,7 @@ export default createMasterCommand<Flags>({
defineFlags(consumer: Consumer): Flags {
return {
fix: consumer.get('fix').asBoolean(false),
- changes: consumer.get('changes').asStringOrVoid(),
+ changed: consumer.get('changed').asStringOrVoid(),
};
},
@@ -38,21 +38,20 @@ export default createMasterCommand<Flags>({
// Look up arguments manually in vsc if we were passed a changes branch
let args;
- if (flags.changes !== undefined) {
+ if (flags.changed !== undefined) {
// No arguments expected when using this flag
req.expectArgumentLength(0);
const client = await req.master.projectManager.getVCSClient(
await req.assertClientCwdProject(),
);
- args = await client.getModifiedFiles(flags.changes);
+ const target = flags.changed === '' ? client.trunkBranch : flags.changed;
+ args = await client.getModifiedFiles(target);
if (args.length === 0) {
- reporter.warn(
- `No files changed from <emphasis>${flags.changes}</emphasis>`,
- );
+ reporter.warn(`No files changed from <emphasis>${target}</emphasis>`);
} else {
- reporter.info(`Files changed from <emphasis>${flags.changes}</emphasis>`);
+ reporter.info(`Files changed from <emphasis>${target}</emphasis>`);
reporter.list(args.map((arg) => `<filelink target="${arg}" />`));
reporter.hr();
}

Add support for valueless --changed