Advanced
Plugins API
While Featurevisor CLI is packed with various core functionalities, it also has a plugins API allowing you to extend it with further tooling as per your needs.
CLI
The entire CLI is built on top of the plugins API. This means that all the core functionalities are implemented as plugins internally.
You can create your own plugins either locally at individual project level, or even share them with others in the form of reusable npm packages.
Installing plugins
Additional plugins can be installed from npm directly.
$ cd my-featurevisor-project
$ npm install --save featurevisor-plugin-example
Plugins can also be created locally without needing any additional npm package or publishing to a central registry.
Registering plugins
You can register plugins via configuration file found at featurevisor.config.js
:
// featurevisor.config.js
module.exports = {
environments: ["staging", "production"],
tags: ["web", "mobile"],
// register plugins here
plugins: [
require("featurevisor-plugin-example"),
// require("./plugins/my-local-plugin"),
],
};
Running a plugin
Once registered, you can run the plugin via the CLI:
$ npx featurevisor example
Hello world!
Creating a plugin
A plugin is a simple JavaScript module that exports an object following below structure:
// plugins/example.js
module.exports = {
// this will be made available as "example" command:
//
// $ npx featurevisor example
//
command: "example",
// handle the command
handler: async function ({
rootDirectoryPath,
projectConfig,
parsed,
datasource,
}) {
console.log("Hello world!");
if (somethingFailed) {
return false; // this will exit the CLI with an error
}
},
// self-documenting examples
examples: [
{
command: "example",
description: "run the example command",
},
{
command: "example --foo=bar",
description: "run the example command with additional options",
},
],
};
Using TypeScript
For type-safety, you can make use of the Plugin
type:
// plugins/example.ts
import { Plugin } from "@featurevisor/core";
const examplePlugin: Plugin = {
command: "example",
handler: async function ({
rootDirectoryPath,
projectConfig,
parsed,
datasource,
}) {
// handle the command here...
},
examples: [
// examples here...
],
};
export default examplePlugin;
Advice for reusable plugins
Above example shows how to create a simple plugin. However, if you are creating a plugin that you wish to share with others, it's recommended to make it configurable when registering them.
Instead of exporting the plugin object directly from a module, we can export a function that returns the plugin object:
// npm package: featurevisor-plugin-example
module.exports = function configureExamplePlugin(options) {
// use `options` here as needed
// return the plugin object
return {
command: "example",
handler: async function ({
rootDirectoryPath,
projectConfig,
parsed,
datasource,
}) {
// ...
},
examples: [
// ...
],
};
};
When registering the plugin, the configuration options can be passed based on project specific needs:
// featurevisor.config.js
module.exports = {
environments: ["staging", "production"],
tags: ["web", "mobile"],
plugins: [
require("featurevisor-plugin-example")({
// custom options here...
someProperty: "some value",
}),
],
};
Handler options
rootDirectoryPath
This is the root directory path of the Featurevisor project where the CLI was executed from.
projectConfig
This is the fully processed configuration object as found in featurevisor.config.js
file in the root of your Featurevisor project.
For full details of what this object contains, refer to the configuration documentation.
parsed
This object will contain the parsed command line arguments.
For example, if the command was:
$ npx featurevisor example --foo=bar
Then parsed
object will be:
{
foo: "bar"
}
It uses yargs internally for parsing the command line arguments.
datasource
Datasource allows reading/writing data from/to the Featurevisor project, so that you don't have to deal with the file system directly.
Read further in datasource documentation.
Here's a quick summary of reading and writing various types of entities using the datasource API:
Revision
See state files for more details.
const revision = await datasource.readRevision();
await datasource.writeRevision(revision + 1);
Features
See features for more details.
const features = await datasource.listFeatures();
const fooFeatureExists = await datasource.featureExists("foo");
const fooFeature = await datasource.readFeature("foo");
await datasource.writeFeature("foo", { ...fooFeature, ...newData });
await datasource.deleteFeature("foo");
Segments
See segments for more details.
const segments = await datasource.listSegments();
const fooSegmentExists = await datasource.segmentExists("foo");
const fooSegment = await datasource.readSegment("foo");
await datasource.writeSegment("foo", { ...fooSegment, ...newData });
await datasource.deleteSegment("foo");
Attributes
See attributes for more details.
const attributes = await datasource.listAttributes();
const fooAttributeExists = await datasource.attributeExists("foo");
const fooAttribute = await datasource.readAttribute("foo");
await datasource.writeAttribute("foo", { ...fooAttribute, ...newData });
await datasource.deleteAttribute("foo");
See more in datasource documentation.