diff --git a/packages/arg-parser/src/arg-parser.spec.ts b/packages/arg-parser/src/arg-parser.spec.ts index c962b8568..292c3e0e4 100644 --- a/packages/arg-parser/src/arg-parser.spec.ts +++ b/packages/arg-parser/src/arg-parser.spec.ts @@ -5,15 +5,24 @@ import { CliOptionsSchema, generateYargsOptionsFromSchema, getLocale, - parseArgs, - parseArgsWithCliOptions, + createParseArgs, + createParseArgsWithCliOptions, UnknownArgumentError, UnsupportedArgumentError, + type ParserCreationOptions, + type ArgsListOptions, } from './arg-parser'; import { z } from 'zod/v4'; import { coerceIfBoolean, coerceIfFalse } from './utils'; import { InvalidArgumentError } from './arg-metadata'; +const parseArgs = ( + opts: ParserCreationOptions & ArgsListOptions +) => createParseArgs(opts)(opts); +const parseArgsWithCliOptions = ( + opts: Partial> & ArgsListOptions +) => createParseArgsWithCliOptions(opts)(opts); + describe('arg-parser', function () { describe('.getLocale', function () { context('when --locale is provided', function () { diff --git a/packages/arg-parser/src/arg-parser.ts b/packages/arg-parser/src/arg-parser.ts index 41d958148..a3a8a8e58 100644 --- a/packages/arg-parser/src/arg-parser.ts +++ b/packages/arg-parser/src/arg-parser.ts @@ -33,89 +33,95 @@ export const defaultParserOptions: Partial = { }, }; -export type ParserOptions = Partial; - -export function parseArgs({ - args, - schema, - parserOptions, -}: { - args: string[]; - schema: T; - parserOptions?: YargsOptions; -}): { +export type ParsedArgs = { /** Parsed options from the schema, including replaced deprecated arguments. */ parsed: z.infer & Omit; /** Record of used deprecated arguments which have been replaced. */ deprecated: Record>; /** Positional arguments which were not parsed as options. */ positional: parser.Arguments['_']; -} { +}; + +export type ArgsListOptions = { + /** List of arguments to parse. */ + args: string[]; +}; + +export type ParserCreationOptions = { + schema: T; + parserOptions?: YargsOptions; +}; + +export function createParseArgs({ + schema, + parserOptions, +}: ParserCreationOptions): ({ args }: ArgsListOptions) => ParsedArgs { const options = generateYargsOptionsFromSchema({ schema, parserOptions, }); - const { argv, error } = parser.detailed(args, { - ...options, - }); - const { _: positional, ...parsedArgs } = argv; + return ({ args }: { args: string[] }) => { + const { argv, error } = parser.detailed(args, { + ...options, + }); + const { _: positional, ...parsedArgs } = argv; - if (error) { - if (error instanceof ZodError) { - throw new InvalidArgumentError(error.message); + if (error) { + if (error instanceof ZodError) { + throw new InvalidArgumentError(error.message); + } + throw error; } - throw error; - } - const allDeprecatedArgs = getDeprecatedArgsWithReplacement(schema); - const usedDeprecatedArgs = {} as Record>; + const allDeprecatedArgs = getDeprecatedArgsWithReplacement(schema); + const usedDeprecatedArgs = {} as Record< + string, + keyof z.infer + >; - for (const deprecated of Object.keys(allDeprecatedArgs)) { - if (deprecated in parsedArgs) { - const replacement = allDeprecatedArgs[deprecated]; + for (const deprecated of Object.keys(allDeprecatedArgs)) { + if (deprecated in parsedArgs) { + const replacement = allDeprecatedArgs[deprecated]; - // This is a complicated type scenario. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (parsedArgs as any)[replacement] = - parsedArgs[deprecated as keyof typeof parsedArgs]; - usedDeprecatedArgs[deprecated] = replacement; + // This is a complicated type scenario. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (parsedArgs as any)[replacement] = + parsedArgs[deprecated as keyof typeof parsedArgs]; + usedDeprecatedArgs[deprecated] = replacement; - delete parsedArgs[deprecated as keyof typeof parsedArgs]; + delete parsedArgs[deprecated as keyof typeof parsedArgs]; + } } - } - for (const arg of positional) { - if (typeof arg === 'string' && arg.startsWith('-')) { - throw new UnknownArgumentError(arg); + for (const arg of positional) { + if (typeof arg === 'string' && arg.startsWith('-')) { + throw new UnknownArgumentError(arg); + } } - } - const unsupportedArgs = getUnsupportedArgs(schema); - for (const unsupported of unsupportedArgs) { - if (unsupported in parsedArgs) { - throw new UnsupportedArgumentError(unsupported); + const unsupportedArgs = getUnsupportedArgs(schema); + for (const unsupported of unsupportedArgs) { + if (unsupported in parsedArgs) { + throw new UnsupportedArgumentError(unsupported); + } } - } - return { - parsed: parsedArgs as z.infer & Omit, - deprecated: usedDeprecatedArgs, - positional, + return { + parsed: parsedArgs as z.infer & Omit, + deprecated: usedDeprecatedArgs, + positional, + }; }; } /** Parses the arguments with special handling of mongosh CLI options fields. */ -export function parseArgsWithCliOptions({ - args, +export function createParseArgsWithCliOptions({ schema: schemaToExtend, parserOptions, -}: { - args: string[]; - /** Schema to extend the CLI options schema with. */ - schema?: T; - parserOptions?: Partial; -}): ReturnType> { +}: Partial> = {}): ({ + args, +}: ArgsListOptions) => ParsedArgs { const schema = schemaToExtend !== undefined ? z.object({ @@ -123,23 +129,26 @@ export function parseArgsWithCliOptions({ ...schemaToExtend.shape, }) : CliOptionsSchema; - const { parsed, positional, deprecated } = parseArgs({ - args, + const parser = createParseArgs({ schema, parserOptions, }); - const processed = processPositionalCliOptions({ - parsed, - positional, - }); + return ({ args }: { args: string[] }) => { + const { parsed, positional, deprecated } = parser({ args }); + + const processed = processPositionalCliOptions({ + parsed, + positional, + }); - validateCliOptions(processed); + validateCliOptions(processed); - return { - parsed: processed as z.infer & Omit, - positional, - deprecated, + return { + parsed: processed as z.infer & Omit, + positional, + deprecated, + }; }; } diff --git a/packages/cli-repl/src/arg-parser.ts b/packages/cli-repl/src/arg-parser.ts index 7ac6f4dea..cb2ea3c6f 100644 --- a/packages/cli-repl/src/arg-parser.ts +++ b/packages/cli-repl/src/arg-parser.ts @@ -1,7 +1,7 @@ import i18n from '@mongosh/i18n'; import { + createParseArgsWithCliOptions, getLocale, - parseArgsWithCliOptions, UnknownArgumentError, UnsupportedArgumentError, } from '@mongosh/arg-parser/arg-parser'; @@ -10,6 +10,9 @@ import { USAGE } from './constants'; import type { CliOptions } from '@mongosh/arg-parser'; import { CommonErrors, MongoshUnimplementedError } from '@mongosh/errors'; +// Make sure this is part of the startup snapshot +const parseArgsWithCliOptions = createParseArgsWithCliOptions(); + /** * Unknown translation key. */