-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Override yargs help output with custom help renderer #229
Changes from all commits
bc4ef3e
8d68859
883fec0
8f4cf83
c8e2232
7886698
cc1dac4
5dfce09
d54219f
d91215b
91035f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,23 @@ | ||
import { loadCommands, enumerateInstalledCommands, enumerateBuiltInCommands } from './loadCommands'; | ||
import { LoadedCommands } from './interfaces'; | ||
import { GroupMap } from './interfaces'; | ||
import { initCommandLoader, createBuiltInCommandLoader } from './command'; | ||
import config from './config'; | ||
|
||
const commands: LoadedCommands = { | ||
commandsMap: new Map(), | ||
yargsCommandNames: new Map() | ||
}; | ||
|
||
let loaded = false; | ||
|
||
export function reset(): void { | ||
commands.commandsMap = new Map(); | ||
commands.yargsCommandNames = new Map(); | ||
loaded = false; | ||
} | ||
|
||
export async function loadExternalCommands(): Promise<LoadedCommands> { | ||
export async function loadExternalCommands(): Promise<GroupMap> { | ||
const installedCommandLoader = initCommandLoader(config.searchPrefixes); | ||
const installedCommandsPaths = await enumerateInstalledCommands(config); | ||
return await loadCommands(installedCommandsPaths, installedCommandLoader); | ||
} | ||
|
||
export async function loadBuiltInCommands(): Promise<LoadedCommands> { | ||
export async function loadBuiltInCommands(): Promise<GroupMap> { | ||
const builtInCommandLoader = createBuiltInCommandLoader(); | ||
const builtInCommandsPaths = await enumerateBuiltInCommands(config); | ||
return await loadCommands(builtInCommandsPaths, builtInCommandLoader); | ||
} | ||
|
||
export default async function loadAllCommands(): Promise<LoadedCommands> { | ||
if (loaded) { | ||
return Promise.resolve(commands); | ||
} | ||
|
||
export default async function loadAllCommands(): Promise<GroupMap> { | ||
const builtInCommands = await loadBuiltInCommands(); | ||
const installedCommands = await loadExternalCommands(); | ||
|
||
commands.commandsMap = new Map([...installedCommands.commandsMap, ...builtInCommands.commandsMap]); | ||
commands.yargsCommandNames = new Map([ | ||
...installedCommands.yargsCommandNames, | ||
...builtInCommands.yargsCommandNames | ||
]); | ||
loaded = true; | ||
return Promise.resolve(commands); | ||
return Promise.resolve(new Map([...installedCommands, ...builtInCommands])); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
import { Command, CommandWrapper, CommandsMap } from './interfaces'; | ||
const cliui = require('cliui'); | ||
import { Command, CommandWrapper, GroupMap } from './interfaces'; | ||
|
||
/** | ||
* Function to create a loader instance, this allows the config to be injected | ||
|
@@ -16,7 +15,7 @@ export function initCommandLoader(searchPrefixes: string[]): (path: string) => C | |
|
||
try { | ||
const command = convertModuleToCommand(module); | ||
const { description, register, run, alias, eject } = command; | ||
const { description, register, run, alias, eject, global = false } = command; | ||
// derive the group and name from the module directory name, e.g. dojo-cli-group-name | ||
const [, group, name] = <string[]>commandRegExp.exec(path); | ||
|
||
|
@@ -26,6 +25,8 @@ export function initCommandLoader(searchPrefixes: string[]): (path: string) => C | |
alias, | ||
description, | ||
register, | ||
installed: true, | ||
global: group === 'create' && name === 'app' ? true : global, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should move this into a utility or something so that the next time we need to force a global like this we don't have to modify the logic in here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a totally temporary thing, this PR introduces an optional global property on the API that commands can mark whether that are global and then we can remove this logic completely |
||
run, | ||
path, | ||
eject | ||
|
@@ -46,12 +47,14 @@ export function createBuiltInCommandLoader(): (path: string) => CommandWrapper { | |
try { | ||
const command = convertModuleToCommand(module); | ||
// derive the name and group of the built in commands from the command itself (these are optional props) | ||
const { name = '', group = '', alias, description, register, run } = command; | ||
const { name = '', group = '', alias, description, register, run, global = false } = command; | ||
|
||
return { | ||
name, | ||
group, | ||
alias, | ||
global, | ||
installed: true, | ||
description, | ||
register, | ||
run, | ||
|
@@ -75,24 +78,19 @@ export function convertModuleToCommand(module: any): Command { | |
} | ||
} | ||
|
||
export function getGroupDescription(commandNames: Set<string>, commands: CommandsMap): string { | ||
const numCommands = commandNames.size; | ||
if (numCommands > 1) { | ||
return getMultiCommandDescription(commandNames, commands); | ||
} else { | ||
const { description } = <CommandWrapper>commands.get(Array.from(commandNames.keys())[0]); | ||
return description; | ||
export function getCommand(groupMap: GroupMap, groupName: string, commandName?: string) { | ||
const commandMap = groupMap.get(groupName); | ||
if (!commandMap) { | ||
throw new Error(`Unable to find command group: ${groupName}`); | ||
} | ||
} | ||
|
||
function getMultiCommandDescription(commandNames: Set<string>, commands: CommandsMap): string { | ||
const descriptions = Array.from(commandNames.keys(), (commandName) => { | ||
const { name, description } = <CommandWrapper>commands.get(commandName); | ||
return `${name} \t${description}`; | ||
}); | ||
const ui = cliui({ | ||
width: 80 | ||
}); | ||
ui.div(descriptions.join('\n')); | ||
return ui.toString(); | ||
if (commandName) { | ||
const command = commandMap.get(commandName); | ||
if (!command) { | ||
throw new Error(`Unable to find command: ${commandName} for group: ${groupName}`); | ||
} | ||
return command; | ||
} | ||
return [...commandMap.values()].find((wrapper) => { | ||
return !!wrapper.default; | ||
})!; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funny that we're renaming these to "commands" in the README and changing them FROM commands in the code 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it’s because we already have a commands map that is the value of the group map. Traditionally it’s easier to understand these things as different (as they really are) when it comes to code. Open to other name suggestions though.