Jazz-Plugins
This section aims to explain how to get started developing jazz-plugins.
Watch this tutorial by video:
Note: This is in portuguese - Work in Progress to have this in english.
Click here to access the playlist on youtube.
Getting Started
Prerequisites
Note: visit the section Preparing the Environment for more details
- NodeJs v8.11.3 or greather installed
- NPM v5.6.0 or greather installed ( comes with NodeJs )
Clone the project boilerplate:
git clone https://gitlab.com/bruno-bert/jazz-plugin-boilerplate jazz-plugin-{your_plugin_name}
cd into your project directory:
jazz-plugin-{your_plugin_name}
Install the npm dependencies:
npm install
Note: this boilerplate is prepared to create a plugin to send mails, so has the nodemailer lib in dependencies. You can remove this before install the dependencies if your plugin may not have the purpose to send emails.
Done! you are ready to start developing your jazz-plugin. Check the details below to understand how to run tests to your jazz-plugin.
Explaining the Project Boilerplate
package.json
dependencies
Contains the dependencies of the plugin, used at runtime. In the case of this plugin, it contains:
- @babel/runtime: used to run transpiled code from babel 7
- nodemailer: used to send mail
"dependencies": {
"@babel/runtime": "^7.4.5",
"nodemailer": "^6.2.1"
},
devDependencies
Contains the dependencies of development of the plugin. These dependencies are used only during development. They are not distributed with the plugin.
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/node": "^7.4.5",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"babel-plugin-root-import": "^6.2.0",
"del": "^4.1.1",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-import-resolver-babel-plugin-root-import": "^1.1.1",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-promise": "^4.1.1",
"gulp": "^4.0.2",
"gulp-jsonminify": "^1.1.0",
"gulp-uglify": "^3.0.2",
"jazz-core": "^1.0.5",
"jazz-plugin-textloader": "^1.0.5",
"prettier-eslint-cli": "^4.7.1"
}
This plugin contains basically 4 groups of dependencies:
Babel core and plugins: used in the process of build, to transpile es6+ code into "legacy javascript code"
Gulp core and plugins: task runner used in the process of build and distribution of the package
Eslint: used to format code by using best practices. In this case, it is using the air-bnb guides.
Jazz Dependencies:
jazz-core: it is the main engine of jazz. During development it is referenced in node-modules directory. However, once the plugin is distributed, it will reference the jazz-core lib from the client. This means, it will not be distributed with the plugin.
jazz-plugin-text-loader: in this boilerplate, it is used only for testing the plugin end-to-end, by receiving data from another step in the pipeline, that is executed by another plugin. Also, used only for testing purposes during development flow. For this reason, it is referenced in the devDependencies, not in main dependencies. This means, it will not be distributed with the plugin.
npm scripts
"scripts": {
"build": "babel src -d build && gulp minifyJson",
"prebuild": "gulp cleanBuild",
"lint": "eslint 'src/**/*.js' --fix",
"git": "git add . && git commit -m %RANDOM% && git push",
"dist": "npm run build && gulp dist",
"start": "node ./e2e.js jazz --configFile ./src/__tests__/pack-config --mailTo bruno.bert.jj@gmail.com --outputFile ./src/__tests__/target.txt",
"dev": "nodemon --exec babel-node ./src/__tests__/e2e.js jazz --configFile ./src/__tests__/pack-config --mailTo bruno.bert.jj@gmail.com --outputFile ./src/__tests__/target.txt --debug",
"task-test": "nodemon --exec babel-node ./src/__tests__/task-test.js"
},
- build: used to transpile the code into legacy javascript code using babel 7. Saves the code into build folder
- prebuild: called automatically by npm before each build. in this case it excludes all the files inside the build directory
- lint: used to run the eslint checks
- git: used to commit and push changes into a git repository
- start: runs the e2e.js file in "production mode"
- dev: runs the e2e.js file by using nodemon. This improves the development flow, since on each change, it will re-run automatically. (check the usage of e2e file on this documentation). This command calls the command line
jazz
with some input parameters:- configFile: references to the jazzpack file (in this case ./src/__tests__/pack-config)
- mailTo: input parameter specific for the plugin jazz-plugin-sendmail. Represents the mail address that will receive the email after processing th task
- outputFile: input parameter specific for the plugin jazz-plugin-textloader. Represents the file path that text file will be saved after the task is precessed
- dist: calls the build and then the task gulp dist. The task dist will perform the following tasks in the sequence: (see gulpfile.js file):
- cleanDist: excludes all files from dist folder
- minifyJson: minifies the json files that may exist in the .src folder
- uglifyJs: uglifies the js files that are in the build folder and saves into dist folder
- replaceDist: some files may have a different version on development time than on productin. This task replaces some files that need to be different on distribution folder.
config.js
This file exports basically 3 properties:
- name: Always use the template name jazz-plugin-pluginname
export const name = "jazz-plugin-sendmail";
pipeline: It is the internal pipeline of the plugin. A plugin can have as many tasks as it needs to perform it's work. Externally, when a pipeline is configured in the pack-config.js file, it can request to execute all tasks in the plugin (by using the name convention pluginpath:pluginname:*) or specify the task(s) that it requires to be executed (by using the name convention pluginpath:pluginname:taskid).
This is an array in which each item is an object that represents a task of the plugin.
Each item in the pipeline can have:
- id: name of the task (name convention: use always lowercase)
- description: describes the main purpose of the task
- class: the name of the class that is responsible for executing the task
- params: it is an array of input parameters that the task can receive
- ovewritables: it is an array of string. Each string is a method name in the task class. By adding these methods in this array will allow them to be ovewritten in jazz-pack level.
Note: the methods below can be ovewritten by default, even if not informed in the ovewritables array:
- getRawData
- execute
- preExecute
- postExecute
In the case of this plugin, it contains a single task that is responsible for sending emails.
export const pipeline = [
{
id: "sendmail",
description: "Send information from pipeline to email",
class: "SendMailTask",
params: ["mailTo"]
}
];
- inputParameters : This is a javascript object in which each property represents a input parameters that can be received by the plugin.
In the case of jazz-plugin-sendmail, it is configured to receive a single parameter that is mail address that plugin will send the generated emails.
Each parameter can have each of the properties below:
- alias: represents the id of the parameter
- describe: when using the command line "jazz", this is the description that will be displayed when user type:
jazz --help
demandOption: this indicates if the parameter is required (true) or optional (false)
default: indicates the default value of the onput parameter if not informed by the user
- name: it is the name of the parameter. It will reference to the parameter indicated in the pipeline, in the poperty params. For example, in this case, the name is mailTo, that references to the params mailTo indicated in the pipeline
export const inputParameters = {
mailTo: {
alias: "mail",
describe: "Mail To",
demandOption: true,
default: "testmail",
name: "mailTo"
}
};
Test Files
pack-config.js
This file will be explained in details in the documentation of jazz-pack-boilerplate repository.
This is the jazz-pack file. This is the main file of any process in Jazz engine. It is on this file that the user will configure the pipeline of it's process and all the configuration of each plugin in the pipeline. Jazz cannot process anything if this file is not informed as an input parameter.
It does NOT need necessarily to have the name pack-config. It can have the name the user wants, and the path will be informed in the input parameter configFile when calling the command line. Example below:
jazz --configFile ./src/__tests__/pack-config
It basically is a js file that will export a javascript object. This object will usually contain the following properties:
- pipeline: it is an array that contains all the steps to be executed. It can have as many steps as you want. The step name must follow this template:
pluginPath:pluginName:taskId
pluginPath: This is the source of the task code. It can reference basically to 3 different sources:
Same codebase: since you are developing a plugin, you probably will use this option. You can reference task in same codebase by using the reserved word this. For example,since we are testing the task sendmail, you can reference it by using:
this:jazz-plugin-sendmail:sendmail
External plugin: let's say you want to test your plugin, by receiving as raw data the result of step executed by a third-party plugin. That is possible by referencing the plugin path from node-modules. Also, the plugin used here needs to be installed in the project as a dev dependency. For example:
const defaultRelativePluginPath = "../.."; const textLoaderPluginPath = `${defaultRelativePluginPath}/jazz-plugin-textloader/dist`; // and in the pipeline step: `${textLoaderPluginPath}:jazz-plugin-textloader:load`;
- native plugin: In Jazz there is a specific plugin that is native, which means is part of the jazz-core lib. This is the Transform plugin, which is responsible for receiving a javascript object and convert into another object by following a bunch of conversion rules. This plugin will have a specifi documentation (work in progress). It can be used in the pipeline by using the reserved word native. For example:
native:Transformer:transform;
Below a complete pipeline used on this boilerplate:
- load task inside Text Loader plugin
- sendmail task on this codebase
pipeline: [
`${textLoaderPluginPath}:jazz-plugin-textloader:load`,
`${source}:jazz-plugin-sendmail:sendmail`
],
e2e.js
This file contains only 2 lines of code. It calls the run method inside the CliLoader class. The CliLoader class is available in the jazz-core lib, and is responsible for creating a "jazz" command line tool. The run method is responsible for starting the pipeline. It means it will read the jazzpack config file, mount the pipeline with all the tasks and call each task in the sequence.
import CliLoader from "jazz-core/dist/CliLoader";
CliLoader.run();
By running the bash npm run dev
, it will make possible testing the plugin inside a pipeline.
task-test.js
By running bash npm run task-test
, it will allow to unit test each task. It means, not inside a full pipeline.
Basically, it will import the SendMailTask class and some helpers:
import { createConfigInfo } from "jazz-core/dist/helpers";
import SendMailTask from "../SendMailTask";
Then, it will create a configuration file, by referencing the pack-config file.
const configFile = "./src/__tests__/pack-config";
const config = createConfigInfo(configFile);
Then, it will instantiate the SendMailTask class, sending as parameters in the constructor: the task id, the parameters as an js object, and the configuration object.
const task = new SendMailTask(
"sendmail",
{ mailTo: "mailto@gmail.com" },
config
);
Then, it will setup some raw data to be processed by the task. This step is required because, when executing the task individually, it will not have any raw data being received as a result from a previous task in the pipeline. Simply because it is not being executed in a pipeline in this situation.
const data = {
data: ["row-test1", "row-test2"]
};
task.setRawData(data);
Finally, it will execute the task:
task.execute(data);
Explaining the __dist-replace__ folder
This folder contains all the *.js files that for some reason need to be different on distributed version when compared to development version. This will be well explained on the topic How to distribute the jazz plugin.
Basically, in this boilerplate, the only file in this situation is the Task.js. When comparing the files:
/* Task.js: references the Task class from the jazz-core in node-modules */
const Task = require("jazz-core/dist/Task");
export default Task;
/*__dist-replace__/Task.js: references the Task class from the jazz-core from an external path */
const Task = require("../../jazz-core/dist/Task");
export default Task;
The reason: when jazz application is installed on final user computer, there will need to be an structure of directories like this:
Because of this, on each distribution folder, the references to the modules that are inside jazz-core library will need to skip 2 levels up on the tree and then look for the module inside jazz-core/dist, which will look like this:
require("../../jazz-core/dist/Task");
So, basically, as a rule, if your plugin has a code that references to some class or function that is inside the jazz-core lib, there will need to be a version referencing to the module inside node-modules and another version (the one that will be distributed) that will need be referencing to the jazz-core lib from an external path.
How to distribute the plugin
First thing to do is to pack your plugin. This boilerplate already has a npm script that will do the work for you. All you need to do is to open the terminal and on the project folder, type:
npm run dist
ON package.json file you can see that this command will run the commands below, in sequence:
npm run build && gulp dist
npm run build: the build command will run the babel transpiler (using version 7) that will convert your code from new javascript (+ ES6) to "old" javascript (< ES6).
gulp dist: when opening the gulfile.js, the dist task does the following:
- cleanDist: excludes all files from dist folder
- minifyJson: minifies the json files that may exist in the .src folder
- uglifyJs: uglifies the js files that are in the build folder and saves into dist folder
- replaceDist: some files may have a different version on development time than on production. This task replaces some files that need to be different on distribution folder.
Ok, so now you tested your plugin, created the distribution package and you are good to go for production. In soon future, the idea is to have an specific marketplace for jazz-plugins and jazz-packs in which the users will be able to download and buy the plugins (almost like the VSCode market place of extensions). At this moment, this feature is not developed, so that, the only way to publish the jazz plugins is through npm.
So, as a pre-requisite, you need to have an account created on https://www.npmjs.com.
Then, all you need to do is to run the command in the terminal:
npm publish
The .npmignore file will guarantee that only the dist folder will be published. This way, your original code is protected. Of course, if your intention is to charge for the plugin, by publishing it on npm is still not the best solution. As mentioned, the Jazz MarketPlace is a work in progress.
Perfect, your jazz plugin is developed and available for others to use.