Writing a no-build plugin
The following guide will provide a short tutorial on how to create a single page no-build plugin for JBrowse 2.
Prerequisites
- you can run an instance of JBrowse 2 on the web, see any of our quickstart guides for details
- a stable and recent version of node
- basic familiarity with the command line and navigating the file system
What is the difference between a no-build plugin and a regular plugin?
A "regular" JBrowse plugin often uses our plugin template
https://github.com/GMOD/jbrowse-plugin-template which uses rollup
to compile
extra dependencies that your plugin might use. In contrast, "no-build" plugins
have no build step and can be hand edited. This can be useful for adding
extra jexl config callbacks for making extra config callbacks or similar modifications.
Writing a no-build plugin
Adding a callback function which you can use in your config
A common method for a no-build plugin might be making a custom function that you
can use to simplify jexl
callbacks in your config. We will create a file
myplugin.js
, which will contain a "UMD" module
providing a single "Plugin" class [1].
myplugin.js
// the plugin will be a simplified UMD module, and we put the code in a
function to avoid variable name collisions with the global scope
;(function () {
class Plugin {
name = 'MyNoBuildPlugin'
version = '1.0'
install(pluginManager) {
pluginManager.jexl.addFunction('customColor', feature => {
if (feature.get('type') === 'exon') {
return 'red'
} else if (feature.get('type') === 'CDS') {
return 'green'
}
})
}
configure(pluginManager) {}
}
// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginMyNoBuildPlugin =
{
default: Plugin,
}
})()
Put this file myplugin.js
in the same folder as your config file, and then,
you can refer to this plugin and the custom function you added in your config.
{
"plugins": [{ "name": "UMDLocPlugin", "umdLoc": { "uri": "myplugin.js" } }],
"tracks": [
{
"type": "FeatureTrack",
"trackId": "mytrack",
"name": "mytrack",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"gffGzLocation": {
"uri": "file.gff.gz"
},
"index": {
"location": {
"uri": "file.gff.gz.tbi"
}
}
},
"displays": [
{
"type": "LinearBasicDisplay",
"displayId": "mytrack-LinearBasicDisplay",
"renderer": {
"type": "SvgFeatureRenderer",
"color1": "jexl:customColor(feature)"
}
}
]
}
]
}
[1] Note that you can also provide an ESM module that has just
export default class
but this is not supported by all browsers, notably
firefox, which cannot import ESM files in webworkers, so for maximum
compatibility, we show are using the UMD format still. Once firefox gains
support for ESM modules, we will update this!
Adding a global menu item
Another example of a no-build plugin is to add menu items or minor extension
points. Here, we're going to add a menu item using the configure
method in the
plugin class.
myplugin.js
// ...
configure(pluginManager) {
// adding a new menu to the top toolbar
pluginManager.rootModel.insertMenu('Citations', 4)
// appending a menu item to the new menu
pluginManager.rootModel.appendToMenu('Citations', {
label: 'Cite this JBrowse session',
onClick: (session) => { }
})
}
// ...
Importing with jbrequire
Because our plugin is not going to be built with any dependencies, the process for referencing external libraries is a little different.
If a package you need to use is found within the JBrowse core project, a special
function jbrequire
can provide your plugin access to these packages. Click
here
for a full list of packages accessible through jbrequire
. Using jbrequire
might look like this:
const { types } = pluginManager.jbrequire('mobx-state-tree')
which would provide the functionality of mobx-state-tree through that value.
Our final no-build plugin looks as follows:
myplugin.js
export default class MyNoBuildPlugin {
name = 'MyNoBuildPlugin'
version = '1.0'
install(pluginManager) {
// here, we use jbrequire to reference packages exported through JBrowse
const { ConfigurationSchema } = pluginManager.jbrequire(
'@jbrowse/core/configuration',
)
const WidgetType = pluginManager.jbrequire(
'@jbrowse/core/pluggableElementTypes/WidgetType',
)
const { ElementId } = pluginManager.jbrequire(
'@jbrowse/core/util/types/mst',
)
const { types } = pluginManager.jbrequire('mobx-state-tree')
const React = pluginManager.jbrequire('react')
// this is our react component
const CiteWidget = props => {
// React.createElement can be used to add html to our widget component
const header = React.createElement(
'h1',
null,
'Cite this JBrowse session',
)
const content = React.createElement(
'p',
null,
`Diesh, Colin, et al. "JBrowse 2: A modular genome browser with views of synteny and structural variation. bioRxiv. 2022.`,
)
return React.createElement('div', null, [header, content])
}
// we're adding a widget that we can open upon clicking on our menu item
pluginManager.addWidgetType(() => {
// adding a widget to the plugin
return new WidgetType({
name: 'CiteWidget',
heading: 'Cite this JBrowse session',
configSchema: ConfigurationSchema('CiteWidget', {}),
stateModel: types.model('CiteWidget', {
id: ElementId,
type: types.literal('CiteWidget'),
}),
// we're going to provide this component ourselves
ReactComponent: CiteWidget,
})
})
}
configure(pluginManager) {
pluginManager.rootModel.insertMenu('Citations', 4)
pluginManager.rootModel.appendToMenu('Citations', {
label: 'Cite this JBrowse session',
onClick: session => {
// upon clicking on this menu item, we need to add and show our new widget
const widget = session.addWidget('CiteWidget', 'citeWidget', {
view: self,
})
session.showWidget(widget)
},
})
}
}
With JBrowse running and your plugin added to your config, your JBrowse session should look like the following:
Conclusion and next steps
Congratulations! You built and ran a single file no-build plugin in JBrowse.
If you'd like some general development information, checkout the series of developer guides available.
Have some questions? Contact us through our various communication channels.