Custom Commands
Whether it's developing plugins for Semo
or building applications and utility scripts based on Semo
, one thing that cannot be avoided is adding Semo
command lines before encapsulating plugins. In order to achieve a smooth development experience, Semo
has been continuously optimizing the entire process, which is still ongoing. This article will discuss how to define commands in Semo
.
Preparation Stage
As mentioned later, we don't need to create command code templates ourselves, as it would be repetitive work. Therefore, a command line code generator is provided. But where should this code be placed? First, it needs to be declared. The configuration file recognized by Semo
here is .semorc.yml
, and the effective configuration is commandDir
. Sometimes, if the project is based on TypeScript, you also need to configure a TypeScript command line directory commandMakeDir
. If you are defining commands for other plugins, you need to define the corresponding extandDir
and extandMakeDir
.
Taking plugin development as an example, the configuration file is roughly as follows:
typescript: true
commandDir: lib/commands
commandMakeDir: src/commands
extendDir: lib/extends
extendMakeDir: src/extends
Creating a Command
Semo
has a built-in code generation mechanism, including a command to generate code for adding new commands:
semo generate command COMMAND_NAME COMMAND_DESCRIPTION
Example Command Code Template
Here's an example of a TypeScript-based command:
semo generate command test 'test description'
export const disabled = false // Set to true to disable this command temporarily
// export const plugin = '' // Set this for importing plugin config
export const command = 'test'
export const desc = 'test description'
// export const aliases = ''
// export const middleware = (argv) => {}
export const builder = function (yargs: any) {
// yargs.option('option', { default, describe, alias })
// yargs.commandDir('test')
}
export const handler = async function (argv: any) {
console.log('Start to draw your dream code!')
}
You can see that there are some differences compared to commands in the yargs framework because Semo
is based on yargs
, so these differences are customized based on the extensibility of yargs
.
Explanation of Command Properties
disabled
This indicates whether the command is disabled. When disabled, it is not visible or functional. It's mainly used in scenarios where you want to disable a command without deleting the code.
command
, desc
, aliases
, builder
, handler
These properties are all part of the yargs
command specification and are straightforward. They can be referred to in the yargs documentation.
middleware
Yes, middleware is supported here. The advantage is that you can extract similar processing logic into middleware and reuse the code in multiple commands. It's generally needed in complex business scenarios.
plugin
This is an attribute exclusive to Semo
plugins. If you define commands within a plugin, the benefit of declaring this attribute is that you can retrieve global configuration from the configuration file using Utils.pluginConfig
.
~/.semo/.semorc.yml
$plugin:
test:
a: 1
In the code, you can use:
const a = Utils.pluginConfig('a', 1)
Internally, it calculates from which plugin configuration to retrieve the value.
About the Return Value of handler
Since Semo
has unified entry points, if you really need to recycle resources through onFinishCommand
, it cannot be directly called by the command line. However, you can achieve this by enabling the after_command
hook. Additionally, the return value of the command also participates in the logic.
- Returning
true
or nothing executesonFinishCommand
. - Returning
false
doesn't executeonFinishCommand
. Even if theafter_command
hook is enabled, it won't execute.
Resource recycling for command lines should be implemented using the after_command
hook.
About Subcommands
Note the line in the code template:
yargs.commandDir('test')
The effect of this line is to look for subcommands in the test
directory. There is a flaw here: when a plugin wants other plugins to extend this subcommand, other plugins cannot do so. How to do it then? Semo
encapsulates this method based on this method.
Utils.extendSubCommand('test', 'test-plugin', yargs, __dirname)
The key here is to fill in the first two parameters correctly. Then, how do other plugins extend subcommands? When creating a command, write it like this:
semo generate command test/subcommand --extend=test-plugin