Skip to content

Commit 33a4e59

Browse files
committed
refactor: remove all process.exit() calls
Replace `process.exit()` with `process.exitCode` throughout `parseCore()`: - `--help` now sets `process.exitCode = 0` and returns `{ helpShown: true }` - `--version` now sets `process.exitCode = 0` and returns `{ helpShown: true }` - `--completion-script` now sets appropriate exit code and returns - `--get-bargs-completions` now sets exit code and returns - `showNestedCommandHelp()` now returns a result instead of calling exit This allows the process to terminate naturally, enabling proper cleanup handlers and making the code more testable and composable.
1 parent 3547913 commit 33a4e59

1 file changed

Lines changed: 48 additions & 28 deletions

File tree

src/bargs.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,22 @@ const parseCore = (
819819
> => {
820820
const { aliasMap, commands, options, theme } = state;
821821

822-
/* c8 ignore start -- help/version output calls process.exit() */
822+
/**
823+
* Helper to create an early-exit result (for help, version, completions).
824+
* Sets process.exitCode and returns a result with helpShown: true.
825+
*
826+
* @function
827+
*/
828+
const earlyExit = (
829+
exitCode: number,
830+
): ParseResult<unknown, readonly unknown[]> & {
831+
command?: string;
832+
helpShown: true;
833+
} => {
834+
process.exitCode = exitCode;
835+
return { command: undefined, helpShown: true, positionals: [], values: {} };
836+
};
837+
823838
// Handle --help
824839
if (args.includes('--help') || args.includes('-h')) {
825840
// Check for command-specific help
@@ -852,27 +867,25 @@ const parseCore = (
852867
values: {},
853868
};
854869
// This will trigger the nested builder's help handling
855-
// and call process.exit(0) if --help is handled
856-
void internalNestedBuilder.__parseWithParentGlobals(
870+
return internalNestedBuilder.__parseWithParentGlobals(
857871
nestedArgs,
858872
emptyGlobals,
859873
true,
860874
);
861875
}
862876

863877
// If no more args, show help for this nested command group
864-
showNestedCommandHelp(state, commandName);
865-
// showNestedCommandHelp calls process.exit(0)
878+
return showNestedCommandHelp(state, commandName);
866879
}
867880

868881
// Regular command help
869882
console.log(generateCommandHelpNew(state, commandName, theme));
870-
process.exit(0);
883+
return earlyExit(0);
871884
}
872885
}
873886

874887
console.log(generateHelpNew(state, theme));
875-
process.exit(0);
888+
return earlyExit(0);
876889
}
877890

878891
// Handle --version
@@ -883,7 +896,7 @@ const parseCore = (
883896
} else {
884897
console.log('Version information not available');
885898
}
886-
process.exit(0);
899+
return earlyExit(0);
887900
}
888901

889902
// Handle shell completion (when enabled)
@@ -896,15 +909,15 @@ const parseCore = (
896909
console.error(
897910
'Error: --completion-script requires a shell argument (bash, zsh, or fish)',
898911
);
899-
process.exit(1);
912+
return earlyExit(1);
900913
}
901914
try {
902915
const shell = validateShell(shellArg);
903916
console.log(generateCompletionScript(state.name, shell));
904-
process.exit(0);
917+
return earlyExit(0);
905918
} catch (err) {
906919
console.error(`Error: ${(err as Error).message}`);
907-
process.exit(1);
920+
return earlyExit(1);
908921
}
909922
}
910923

@@ -914,7 +927,7 @@ const parseCore = (
914927
const shellArg = args[getCompletionsIndex + 1];
915928
if (!shellArg) {
916929
// No shell specified, output nothing
917-
process.exit(0);
930+
return earlyExit(0);
918931
}
919932
try {
920933
const shell = validateShell(shellArg);
@@ -924,14 +937,13 @@ const parseCore = (
924937
if (candidates.length > 0) {
925938
console.log(candidates.join('\n'));
926939
}
927-
process.exit(0);
940+
return earlyExit(0);
928941
} catch {
929942
// Invalid shell, output nothing
930-
process.exit(0);
943+
return earlyExit(0);
931944
}
932945
}
933946
}
934-
/* c8 ignore stop */
935947

936948
// If we have commands, dispatch to the appropriate one
937949
if (commands.size > 0) {
@@ -947,15 +959,25 @@ const parseCore = (
947959
*
948960
* @function
949961
*/
950-
/* c8 ignore start -- only called from help paths that call process.exit() */
951962
const showNestedCommandHelp = (
952963
state: InternalCliState,
953964
commandName: string,
954-
): void => {
965+
):
966+
| (ParseResult<unknown, readonly unknown[]> & {
967+
command?: string;
968+
helpShown?: boolean;
969+
})
970+
| Promise<
971+
ParseResult<unknown, readonly unknown[]> & {
972+
command?: string;
973+
helpShown?: boolean;
974+
}
975+
> => {
955976
const commandEntry = state.commands.get(commandName);
956977
if (!commandEntry || commandEntry.type !== 'nested') {
957-
console.log(`Unknown command group: ${commandName}`);
958-
process.exit(1);
978+
console.error(`Unknown command group: ${commandName}`);
979+
process.exitCode = 1;
980+
return { command: undefined, helpShown: true, positionals: [], values: {} };
959981
}
960982

961983
// Delegate to nested builder with --help
@@ -968,21 +990,20 @@ const showNestedCommandHelp = (
968990
values: {},
969991
};
970992

971-
// This will show the nested builder's help and call process.exit(0)
972-
void internalNestedBuilder.__parseWithParentGlobals(
993+
// This will show the nested builder's help
994+
return internalNestedBuilder.__parseWithParentGlobals(
973995
['--help'],
974996
emptyGlobals,
975997
true,
976998
);
977999
};
978-
/* c8 ignore stop */
9791000

9801001
/**
9811002
* Generate command-specific help.
9821003
*
9831004
* @function
9841005
*/
985-
/* c8 ignore start -- only called from help paths that call process.exit() */
1006+
/* c8 ignore start -- only called from help paths */
9861007
const generateCommandHelpNew = (
9871008
state: InternalCliState,
9881009
commandName: string,
@@ -993,11 +1014,10 @@ const generateCommandHelpNew = (
9931014
return `Unknown command: ${commandName}`;
9941015
}
9951016

996-
// Handle nested commands - this shouldn't be reached as nested commands
997-
// delegate to showNestedCommandHelp in parseCore, but handle it gracefully
1017+
// Nested commands are handled by showNestedCommandHelp in parseCore,
1018+
// so this function should never be called for nested commands
9981019
if (commandEntry.type === 'nested') {
999-
showNestedCommandHelp(state, commandName);
1000-
return ''; // Never reached, showNestedCommandHelp calls process.exit
1020+
return `${commandName} is a command group. Use --help after a subcommand.`;
10011021
}
10021022

10031023
// Regular command help
@@ -1024,7 +1044,7 @@ const generateCommandHelpNew = (
10241044
*
10251045
* @function
10261046
*/
1027-
/* c8 ignore start -- only called from help paths that call process.exit() */
1047+
/* c8 ignore start -- only called from help paths */
10281048
const generateHelpNew = (state: InternalCliState, theme: Theme): string => {
10291049
// Build options schema, adding built-in options
10301050
let options = state.globalParser?.__optionsSchema;

0 commit comments

Comments
 (0)