@qui-cli plugin structure and registrar
npm install @qui-cli/plugin@qui-cli plugin structure and registrar


``sh`
npm install @qui-cli/plugin
Refer to @qui-cli/env for a complete example defining a plugin. The model is to define an ESM module and then to register that module with the Plugin.Registrar. The plugin module can optionally define the following hooks:
- configure(config?: Plugin.Configuration) => void may be invoked either manually before processing user-provided command line arguments (e.g. to update default values) and also (if defined) when options() is invoked. See Configuration below.options() => Plugin.Options
- is invoked (if defined) when preparing to process user-provided command-line arguments and must return a Plugin.Options object describing any user-configurable command line arguments and other user-readable documentation. See Options below.init(args: Plugin.Arguments
- is invoked to process any user-provided command line arguments. See Initialization below.
Refer to @qui-cli/shell for a relatively straight-forward example.
``
š @example/plugin
ā³ package.json
ā³ š src
ā³ index.ts
ā³ Example.ts
#### package.json
Define both a name and version. Include @qui-cli/plugin and _any other plugins that your Plugin depends on_ as peer dependencies:
`json`
{
"peerDependencies": {
"@qui-cli/plugin": "^2.4"
}
}
While npm will install all dependencies at the same level of the ./node_modules/ folder hierarchy, tools like pnpm will not install non-peer dependencies at the same level as the plugin. To ensure that the plugin's peers are both installed and shared properly with other plugins that rely on them, plugin dependencies _must_ be listed as peerDependencies.
#### src/index.ts
Register your plugin with the Plugin.Registrar and export the module for the convenience of other plugins that may depend on it.
`ts
import { register } from '@qui-cli/plugin';
import * as Example from './Example.js';
await register(Example);
export { Example };
`
#### src/Example.ts
Define your plugin module, including exported name and any of the optional Plugin Hooks.
`ts
export const name = 'example';
export function configure(config?) {
// ...
}
export function myMethod() {
// ...
}
`
See dev-plugin-lifecycle for an illustration of the order in which hooks are called.
The configure() hook may prepare the plugin module for use.
While the provided Plugin.Configuration type can be used as the parameter type for the configure() hook, it is recommended to extend that configuration to support IntelliSense in your IDE:
`ts`
type Configuration = Plugin.Configuration & {
myOption?: string;
};
All plugin-defined configuration parameters _must_ be optional, as the configure() hook may be invoked with no parameters to ensure that a plugin is ready to to provide options() or receive init().
In general, it is recommended that configure() be used to set/update/instantiate any module variables that are used elsewhere, and can be understood as the constructor for the singleton object represented by the plugin module.
The options() hook may provide command-line options for jackspeak initialization and user-facing usage documentation.
`ts
type ConfigSet
string,
{
description?: string; // user-readable usage information
short?: string; // single-character short-hand for this option
default?: Value;
hint?: string
validate?: (value: any) => v is Value | (v: any) => boolean;
type?: 'number' | 'string' | 'boolean';
multiple?: boolean;
delim?: string // default ' '
}
>;
type Paragraph = {
text: string;
level?: 1 | 2 | 3 | 4 | 5 | 6;
pre?: boolean;
};
type Options = {
// an option that expects a single number
num?: ConfigSet
// an option that expects a delim-separated list of numbers
numList?: ConfigSet
// an option that expects a string value (optionally quoted)
opt?: ConfigSet
// an option that expects a delim-separated list of strings
optList?: ConfigSet
// a flag that is set (true) or unset (false), --example can be explicitly unset as --no-example
flag?: ConfigSet
// a flag that can be set multiple times (-d vs -ddd)
flagList?: ConfigSet
// must explicitly define type and multiple
fields?: ConfigSet
// additional user-readable usage information
man?: Paragraph[];
};
`
For example, the options() hook could be defined thus:
`tsBar value
export function options() {
return {
flag: {
foo: {
description: 'Engage foo-ness',
short: 'f'
}
},
opt: {
bar: {
description: ,`
short: 'b',
default: 'argle-bargle'
}
}
};
}
The init() hook may receive user-provided command line arguments to update the plugin configuration. The arguments are provided as the result of the jackspeak parse() method.
The arguments may be assumed to reflect that Plugin.Options object returned from the options() hook (if no options() hook is defined, it is recommended to examine only the positional arguments).
`ts`
export function init({
values: { foo, bar }
}: Plugin.ExpectedArguments
// ...
}
`ts`
import * as Plugin from '@qui-cli/plugin';
Register a plugin for use.
`ts
import * as MyPlugin from './MyPlugin.js';
Plugin.register(MyPlugin);
`
Typing for configure() hook. Intended to be extended to provide IntelliSense support, discussed in detail in Configuration.
`ts
type Configuration = Plugin.Configuration & {
// ...
};
export function configure(config?: Configuration) {
// ...
}
`
Typing for options() hook. Maps 1:1 to the jackspeak options API, discussed in detail inOptions.
`ts`
export function options(): Plugin.Options {
// ...
}
Typing for init() hook. Intended to provide IntelliSense support for parameters defined by options() hook.
`ts`
export function init(args: Plugin.ExpectedArguments
// ...
}
The Plugin Registrar, responsible for tracking plugin registrations and triggering plugin hooks. Intended primarily for use by @qui-cli/core
Merge two Plugin.Options objects. b overwrites a` in the event of duplicate keys.