Skip to content

Commit 55a507e

Browse files
committed
fix(yok): keep hierarchical dispatchers on the owning injector
createHierarchicalCommand registered the synthesized parent - and its execute path resolved commandsService and errors - through the module-level global injector instead of the instance it was called on, so a parent dispatcher leaked onto the global injector whenever a hierarchical command was registered on any other instance. The require-ordering guard accidentally depended on that leak: with the parent now landing on the same instance, it is exempted for synthesized parents (they exist because a child registered, not because requires ran out of order), and the else branch mirrors the existing default-command skip. Net effect: register-then-require orderings that used to throw 'Default commands should be required before child commands' now work; nothing that worked before changes.
1 parent a23a901 commit 55a507e

1 file changed

Lines changed: 15 additions & 5 deletions

File tree

lib/common/yok.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export class Yok implements IInjector {
6767
}
6868

6969
private COMMANDS_NAMESPACE: string = "commands";
70+
/**
71+
* Parents whose dispatcher THIS instance synthesized. The require-ordering
72+
* guard below must not fire for them: they exist because a child was
73+
* registered, not because child requires ran out of order.
74+
*/
75+
private synthesizedParents = new Set<string>();
7076
private KEY_COMMANDS_NAMESPACE: string = "keyCommands";
7177
private hierarchicalCommands: IDictionary<string[]> = {};
7278

@@ -88,7 +94,8 @@ export class Yok implements IInjector {
8894
if (commands.length > 1) {
8995
if (
9096
_.startsWith(commands[1], "*") &&
91-
this.container.has(this.createCommandName(commands[0]))
97+
this.container.has(this.createCommandName(commands[0])) &&
98+
!this.synthesizedParents.has(commands[0])
9299
) {
93100
throw new Error(
94101
"Default commands should be required before child commands",
@@ -114,7 +121,9 @@ export class Yok implements IInjector {
114121
if (commands[1] && !commandName.match(/\|\*/)) {
115122
this.require(this.createCommandName(commandName), file);
116123
}
117-
} else {
124+
} else if (!commandName.match(/\|\*/)) {
125+
// Mirrors the default-command skip of the branch above: a default's
126+
// own record comes from registerCommand, never from a require path.
118127
this.require(this.createCommandName(commandName), file);
119128
}
120129
});
@@ -305,12 +314,13 @@ export class Yok implements IInjector {
305314
}
306315

307316
private createHierarchicalCommand(name: string) {
317+
this.synthesizedParents.add(name);
308318
const factory = () => {
309319
return {
310320
disableAnalytics: true,
311321
isHierarchicalCommand: true,
312322
execute: async (args: string[]): Promise<void> => {
313-
const commandsService = injector.resolve("commandsService");
323+
const commandsService = this.resolve("commandsService");
314324
let commandName: string = null;
315325
const defaultCommand = this.getDefaultCommand(name, args);
316326
let commandArguments: ICommandArgument[] = [];
@@ -364,7 +374,7 @@ export class Yok implements IInjector {
364374
};
365375
};
366376

367-
injector.registerCommand(name, factory);
377+
this.registerCommand(name, factory);
368378
}
369379

370380
private getHierarchicalCommandName(
@@ -395,7 +405,7 @@ export class Yok implements IInjector {
395405
// In case buildHierarchicalCommand doesn't find a valid command
396406
// there isn't a valid command or default with those arguments
397407

398-
const errors = injector.resolve("errors");
408+
const errors = this.resolve("errors");
399409
errors.failWithHelp(ERROR_NO_VALID_SUBCOMMAND_FORMAT, commandName);
400410
}
401411

0 commit comments

Comments
 (0)