As the language of the internet, Javascript can be used to write Flow plugins.
Plugins written in TypeScript/JavaScript use the JSON-RPC protocol to communicate with Flow via JSON structured calls.
Although not a hard requirement, this guide will use Node.js to run the TypeScript/JavaScript. We will refer to TypeScript/JavaScript plugin as Node.js plugin from here on.
When building a Node.js plugin, there are several things to be mindful of:
-
The most important thing is we do not expect users to have to manually install the dependencies via npm because we aim to provide a seamless experience for them. This can be achieved by adding the following three things to your project:
- Add a GitHub workflow — use a GitHub workflow that will install all your plugin's dependencies including the modules inside a folder called
node_modules. - Publish all as a zip — zip up your project including the node_modules directory that contains the modules and publish it to GitHub Releases page.
- Point your module path to the node_modules directory — reference all the modules to that directory.
- Add a GitHub workflow — use a GitHub workflow that will install all your plugin's dependencies including the modules inside a folder called
-
Users can use their system-installed Node.js with Flow Launcher, but in most circumstances, they will most likely be using Flow Launcher's download of Node.js. This download of portable Node.js version is isolated from the user's system and can be simply removed.
Have a look at this simple example plugin, notice it has a folder called .github/workflows and a file called Publish Release.yml. This is the workflow file that GitHub Workflow uses to run the CI/CD for the project. Moving out of that folder, you can go into the main.js file; this is the entry file for your plugin.
The workflow file will help build and deploy your project, it does the following things:
-
workflow_dispatch:gives you the option to manually run your workflow from the Actions section of your project -
On pushes to main, it will kick off the workflow but ignore the push if it's only changes made to the workflow file.
push:
branches: [ main ]
paths-ignore:
- .github/workflows/*- It specifies the Node.js version that will be used for building your project:
- name: Set up Node.Js
uses: actions/setup-node@v2
with:
node-version: '17.3.0'- The project's release version is obtained from your plugin.json automatically by the CI, so when built, it will be used to tag the release:
- name: get version
id: version
uses: notiz-dev/github-action-json-property@release
with:
path: 'plugin.json'
prop_path: 'Version'- The Install dependencies section is where you will do most of your CI work. It will run
npm install, which will output all the dependencies specified in package.json into the 'node_modules' directory. The workflow will then zip them up along with your project usingzip -r Flow.Launcher.Plugin.HelloWorldNodeJS.zip . -x '*.git*', where you replace thisFlow.Launcher.Plugin.HelloWorldNodeJSwith the name of your plugin.
- name: Install dependencies
run: |
npm install
zip -r Flow.Launcher.Plugin.HelloWorldNodeJS.zip . -x '*.git*'The final step to the workflow file is this Publish section, which will publish the zip file you generated, upload to GitHub Releases page and tag with the version generated from the previous step from your plugin.json file. Remember again to replace Flow.Launcher.Plugin.HelloWorldNodeJS with the name of your plugin.
- name: Publish
uses: softprops/action-gh-release@v1
with:
files: 'Flow.Launcher.Plugin.HelloWorldNodeJS.zip'
tag_name: "v${{steps.version.outputs.prop}}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Feel free to also have a read of this blog post which does a simple explanation of how to use GitHub Actions Workflow.
Once the node_modules folder is included in your zip release, it can then be used without needing the user to manually npm install the plugin's dependencies. You just have to tell the plugin during runtime to find those modules in your local node_modules directory. Do this by using this exact copy of the following code block in your main.js:
const open = require('./node_modules/open');Since we have created a CI for your plugin in the previous step, which includes creating a release when you push/merge to the 'main' branch, it is then necessary to create another git branch separate to your 'main' branch so you can continue to work on your plugin with git commits and pushes without creating a new release each time.
It is a good practice that you create a branch for each of the new feature/fixes you are releasing for your plugin, if you are not sure how to do so then follow this video tutorial. Once you have fully finished developing your plugin with your new branch, then you can merge it into the 'main' branch, which will consequently create a new release for your plugin with a version from your plugin.json.
your main.js should look something like below:
const open = require('./node_modules/open');
const { method, parameters, settings } = JSON.parse(process.argv[2]);
if (method === "query") {
console.log(JSON.stringify(
{
"result": [{
"Title": "Hello World Typescript",
"SubTitle": "Showing your query parameters: " + parameters + ". Click to open Flow's website",
"JsonRPCAction": {
"method": "do_something_for_query",
"parameters": ["https://github.com/Flow-Launcher/Flow.Launcher"]
},
"IcoPath": "Images\\app.png",
"Score" : 0
}]
}
));
}
if (method === "do_something_for_query") {
url = parameters[0];
do_something_for_query(url);
}
function do_something_for_query(url) {
open(url);
}if (method === "query")
This if statement captures the args passed via JSON-RPC defined as const { method, parameters } = JSON.parse(process.argv[2]), so if method is 'query' then the console.log's code block will be run. As the result property is an array, you can also specify a single or multiple results.
JsonRPCAction
This is where you specify the method that will be executed when the user selects on the result.
In this example, if the user selects the result, the do_something_for_query method will be called with the url parameter which opens the Flow Launcher GitHub repo.
The score field provides the ability to assign a weight to your score; the higher the score is, the higher the result from the plugin would show in flow's result list. Flow's own fuzzy search scores range from 0 to 100, so assign scores in that range. You can keep it as 0 if your plugin generally uses an action keyword to trigger, but if you are using a global action keyword - * - then the average weight for a plugin would be 50, which blends in with other results. Flow also raises the score of results the user has selected before, matching them by Title and SubTitle, so keep those consistent between queries. Additionally, users can tweak the score via Flow's plugin setting as well.
You will also need to, if not yet already, create a plugin.json file that will instruct Flow on how to load your plugin.
This file should be placed in the top level folder.
To revisit what to include in your plugin.json, visit the plugin.json reference
When you are ready to share your plugin, head to Flow's plugin repo and follow the instructions in the readme.
Here are some plugins to use as a reference:
- Plugin Template https://github.com/Joehoel/flow-launcher-plugin-template-node
- Discord Timestamps https://github.com/Jessuhh/discord-timestamps-flowlauncher-plugin
- NPM Search https://github.com/gabrielcarloto/flow-search-npm