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 Oct 30, 2018 · 25 revisions

The Imperative CLI Framework contains features that let you develop plug-ins for use with an Imperative-based CLI application. 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 Plugin Module Loader

When enabling plugins in an Imperative Based CLI (IBC), the Plugin Management Facility (PMF) will initialize the Module Loader (ML). The ML's purpose is to provide the correct version of Imperative and the IBC to a Plugin Installed in the IBC (PIIBC).

The ML does this by overriding Module.prototype.require, which also overrides require. In short the ML does the following:

  1. Stores the original mapping of Module.protoype.require for use later.
    • Stored in a variable called originalRequire for the purpose of explanation
  2. Overrides Module.prototype.require with a custom method.

After the ML has been initialized, all further require calls will undergo the following logic:

  1. require is intercepted by the ML custom function.
  2. The custom function will check if the require is for the IBC module or the Imperative module.
    • If this is the case, the ML will remap the require to either the IBC or the imperative present in the node_modules of the IBC. (I will referr to both these modules as Injected Modules here forward)
  3. The originalRequire method is called and returned. This ensures that require returns the proper module information to the caller.

What this allows is for the PIIBC to get the Injected Modules from the runtime location. This means that the PIIBC does not have to specify Imperative or the IBC as a dependency. If these are specified as a dependency, they will never be used as the ML will still provide them as Injected Modules. It is recommended to specify the Injected Modules as a devDependency if you plan to use them.

Example

This is an example of a possible IBC and PIIBC combination.

IBC Setup

Suppose you have this IBC structure:

  • /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"
  }
}

PIIBC Setup

The PIIBC could have this structure:

  • /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 installing the PIIBC to the IBC, say you issue the following command to invoke /plugin-root/index.js:

cli-command some-plugin-name

You would see the following output. Notice the difference in output with the ML disabled vs. enabled.

ML 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
ML 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 you can see above, when the ML is disabled, all imports will go to the /plugin-root/node_modules folder. This means that the PIIBC must supply the Injected Modules (Imperative and some-package-name) that are already in the runtime.

With the ML enabled, those existing modules are loaded from the existing ones in the runtime. This allows the PIIBC to install without needing to install the Injected Modules as well, saving some download time and disk space. This is done by specifying the Injected Modules as devDependencies in the PIIBC's package.json as seen in the setup above.