Plugin Architecture
Code Shaper is designed to be modular and extensible using the concept of
plugins. The shaper
CLI can load plugins dynamically, thus extending its
functionality to your unique needs. This topic describes the plugin architecture
and implementation mechanics to help you write your own plugins.
Concepts
Code Shaper allows you to bundle multiple generators into a plugin. The plugin can be used within its own repository or published to a central registry (e.g. npm or artifactory) for use in other repositories.
Thus creating a generator involves two steps:
- Create a plugin
- Add one or more generators to it
The diagram below shows the plugin architecture visually.
- A plugin consists of one or more generators.
- A generator generates artifacts based on user input.
Here's an example of a plugin that generates React code. It consists of three generators: Component, Page and Context.
- The Component Generator generates components, e.g.
Button
andMovieList
. - The Page Generator generates pages, e.g.
HomePage
. - The Context Generator generates React contexts, e.g.
OrderContext
.
Technical Design
Options
The concept of Options is used throughout Code Shaper. Options
is a hash of
key-value pairs, where keys are strings and values can be anything (usually
strings, arrays or objects). For example, here's an Options object:
{
"itemName": "Button",
"workspace": "packages/ui-lib",
"dirInWorkspace": "src/components/Button"
}
Here's the TypeScript definition of Options
:
type Options = { [option: string]: unknown };
Options can be specified on the shaper
command line where they are parsed and
passed onto the selected plugin. The plugin can add to the options by asking
questions to the user. The plugin then sends the combined options to the
selected generator which can ask further questions and add more options.
Finally, the generator uses these options to make text substitutions in
templates before copying them to their destination.
Code Shaper uses the Inquirer library for asking questions to the user and collecting responses. Inquirer itself has a rich plugin ecosystem that is available for use in your Code Shaper plugins.
Plugins
A plugin must conform to the following interface:
interface Plugin {
/**
* Unique identifier of the plugin
* By convention we use the package name
* Example: "@code-shaper/react"
*/
id: string;
/**
* Human-readable name
* Example: "React"
*/
name: string;
/**
* Short description
* Keep it under 80 characters
* Example: "generates React applications"
*/
description: string;
/**
* Runs the plugin
* @param options specific to the plugin - if a required option is not
* specified, the plugin should prompt for it.
*/
run: (options: Options) => Promise<void>;
}
The shaper
CLI loads the selected plugin and calls its run
method, passing
in all the options.
In order to load a plugin, it should be installed as a dev dependency at the
root of your repository. shaper
reads the dev dependencies to detect which
plugins are available for the user to use.
Generators
A generator must conform to the following interface:
interface Generator {
/**
* Identifier of the generator
* Must be unique within a plugin
* Example: "app"
*/
id: string;
/**
* Human-readable name
* Example: "Application"
*/
name: string;
/**
* Short description
* Keep it under 80 characters
* Example: "generates a React application"
*/
description: string;
/**
* Runs the generator
* @param options specific to the generator - if a required option is not
* specified, the generator should prompt for it.
*/
generate: (options: Options) => Promise<void>;
}
The plugin calls the run
method of the selected generator, passing in all the
options.
Writing your own plugin
Code Shaper provides a plugin to scaffold your own plugins and generators. You can learn how to use this plugin under Create a Custom Generator.