Skip to content
This repository has been archived by the owner on Nov 13, 2023. It is now read-only.

Plugins

Wright, Christopher R edited this page Nov 6, 2018 · 25 revisions

The Imperative CLI Framework contains features and components that let you develop plug-ins that you can use with Imperative-based CLI applications. Use the documentation in this section to learn about developing and managing CLI plug-ins.


Brandon/Jim not sure where this goes but this should be from the developer of an imperative cli based application. (and possibly someone who takes an imperative based cli application and calls the apis directly. So like the vscode extension would be an example.)

It also might be useful information to a developer of a plugin for an imperative cli so have fun figuring out where this goes.

Imperative Plug-in Module Loader

When you enable plug-ins in Imperative Based CLIs, the Plugin Management Facility (PMF) initializes the Module Loader. The Module Loader provides the correct versions of the Imperative Framework and the Imperative-based CLI to the plug-in that you installed in your Imperative-based CLI.

To provide the correct version, the Module Loader overrides Module.prototype.require, which also overrides require. In short, the Module Loader performs the following actions:

  1. Stores the original mapping of Module.prototype.require for use at a later time. The Module Loader stores the mapping information in a variable named originalRequire.
  2. Overrides Module.prototype.require with a custom method.

After the Module Loader initializes, all further require calls undergo the following logic:

  1. require is intercepted by the Module Loader custom function.
  2. The custom function checks if the require is for the Imperative-based CLI module or the Imperative module.
    • Note: When the require is for the Imperative-based CLI module or the Imperative module, the Module Loader remaps the require to the Imperative-based CLI module or the imperative module that is present in the node_modules of the Imperative-based CLI (injected modules).
  3. The originalRequire method is called and returned. Calling (and returning) the method helps to ensure that require returns the proper module information to the caller.

The above logic lets your plug-in get the injected modules from the runtime location rather than specifying them as a dependency. When you specify the modules as a dependency, the Module Loader never uses them, however, the Module Loader provides them as injected modules. As a best practice, we recommend that you specify the injected modules as a devDependency only when you plan to use them.

Example

The following example illustrates the structures and properties of an Imperative-based CLI and your installed plug-in.

Imperative-based CLI Setup

Consider the following structure of an Imperative-based CLI:

  • /root

    • /node_modules
      • /@brightside/imperative
      • /test-pkg
    • package.json
    • /lib
      • main.js
      • index.js
  • Where plugins are enabled.

  • Where /root/lib/index.js is the application code.

  • Where /root/lib/main.js is the cli entrypoint.

  • Where /root/package.json contains:

{
  "name": "some-package-name",
  "bin": {
    "cli-command": "./lib/main.js"
  },
  "main": "lib/index.js",
  "dependencies": {
    "test-pkg": "1.0.0",
    "@brightside/imperative": "2.0.0"
  }
}

Your plug-in Setup

Consider the following structure for your plug-in:

  • /plugin-root

    • /node_modules
      • /test-pkg
      • /@brightside/imperative
      • /some-package-name
    • package.json
    • index.js
  • Where /plugin-root/package.json contains:

{
  "name": "some-plugin-name",
  "main": "index.js",
  "dependencies": {
    "test-pkg": "2.0.0"
  },
  "devDependencies": {
    "@brightside/imperative": "2.0.0",
    "some-package-name": "1.0.0"
  }
}
  • Where /plugin-root/index.js contains:
console.log(require.resolve("@brightside/imperative"));
console.log(require.resolve("@brightside/imperative/lib/error"));
console.log(require.resolve("some-package-name"));
console.log(require.resolve("some-package-name/lib/index"));
console.log(require.resolve("test-pkg"));

Issuing the Test

After you install the plug-in that you developed to the Imperative-based CLI, you can issue the following command to invoke /plugin-root/index.js:

cli-command some-plugin-name

The following output displays.

Observe the difference between the output that displays when the Module Loader is disabled and when the Module Loader is enabled.

Module Loader Disabled
/plugin-root/node_modules/@brightside/imperative/lib/index.js
/plugin-root/node_modules/@brightside/imperative/lib/error/index.js
/plugin-root/node_modules/some-package-name/lib/index.js
/plugin-root/node_modules/some-package-name/lib/index.js
/plugin-root/node_modules/test-pkg/index.js
Module Loader Enabled
/root/node_modules/@brightside/imperative/lib/index.js
/root/node_modules/@brightside/imperative/lib/error/index.js
/root/lib/index.js
/root/lib/index.js
/plugin-root/node_modules/test-pkg/index.js

Comparison of Output

As illustrated by the above example, when the Module Loadter is disabled, all imports go to the /plugin-root/node_modules folder. In other words, your plug-in must supply the injected modules (Imperative and some-package-name) that are already in the runtime.

When the Module Loader is enabled, the modules are loaded from the existing modules in the runtime. This lets you install your plug-in without having to install the injected modules, which can help reduce download time and consume less disk space. This is done by specifying the injected modules as devDependencies in the package.json for your plug-in, as illustrated in the setup above.