Skip to content

BladeRunnerJS Plugin Development Guide

Dominic Chambers edited this page Jan 14, 2014 · 12 revisions

BladeRunnerJS supports the following plug-in interfaces:

  • ModelObserverPlugin (observe events on the model)
  • CommandPlugin (run commands through 'brjs')
  • ContentPlugin & BundlerContentPlugin (serve browser requests)
  • TagHandlerPlugin & BundlerTagHandlerPlugin (replace logical page tags)
  • AssetPlugin (support new asset file types, e.g. Node.js classes)
  • AssetLocationPlugin (support new asset directory structures, e.g. Node.js directory structure)
  • MinifierPlugin (source-map friendly minifier support)
  • TestPlugin (support new test runners -- not yet available)

Accessing Plug-ins Via The Model

Plug-ins can be accessed directly via BRJS.plugins(), or indirectly using one of the many methods that makes use of plug-ins under the convers, for example:

  • BRJS.runCommand(String... args)
  • BundlableNode.getSourceModule(String requirePath)
  • BundlableNode.getBundleSet()

Target Plug-ins for 1.0

Here are some of the more interesting plug-ins that will be available with the 1.0 release:

  • In-built Jetty web server (CommandPlugin)
  • Flat-file export support (CommandPlugin)
  • WAR export support (CommandPlugin)
  • Templated project and blade creation (CommandPlugin)
  • BladeRunnerJS Thirdparty Library Support (AssetLocationPlugin, AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Node.js Thirdparty Library Support (AssetLocationPlugin, AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • BladeRunnerJS Conformant Library Support (AssetLocationPlugin -- class & asset support is dependent on other plug-ins)
  • Node.js style class support (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Namespaced style class support (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • CSS bundling (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • HTML template bundling (AssetPlugin & BundlerContentPlugin)
  • XML config bundling (AssetPlugin & BundlerContentPlugin)
  • I18N internatiionalization support (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Composite Javascript Bundling (BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Composite CSS Bundling (BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Fast concatenating minification (MinifierPlugin)
  • Closure Compiler minification with single-level source map support (MinifierPlugin)
  • Aliasing IoC mechanism (ContentPlugin & AssetPlugin)
  • Js-test-driver test runner for fast browser-based testing (TestPlugin)
  • Selenium test runner for pointy-clicky browser-based tests (TestPlugin)

Bundler Plug-in Relationships

You may have noticed that some of the plug-in types are often grouped together. Often you will write plug-ins that implement only a single plug-in interface, but bundler plug-ins often involve a number of plug-ins, due to the following inter-dependencies that they have:

  • The AssetLocationPlugin recognizes JavaScript locations by virtue of there being either a manifest-file or a recognizer-file.
  • The AssetPlugin will only 'recognize' js source files within supported asset locations (using getJsStyle()), so is dependent on an AssetLocationPlugin.
  • The BundlerContentPlugin will only serve assets it recognizes (unless it's a composite), so we need a corresponding AssetPlugin.
  • The BundlerTagHandlerPlugin plug-in will only generate requests for BundlerContentPlugin instances it's associated with (unless it's a composite).

Most Wanted List

These are plug-ins we're not planning to write for 1.0, but which we believe our plug-in interfaces are capable of supporting, and for which we'd love to see contributions:

  • EcmaScript6 support so we can write standards based code now (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin -- depends on the BRJS conformant library plug-in)
  • TypeScript for strongly typed JavaScript, including sourcemap support (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin -- depends on the BRJS conformant library plug-in)
  • Bower style library support (AssetLocationPlugin, AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • Bower package management support (CommandPlugin)
  • JsHint to provide JavaScript linting support (TagHandlerPlugin & ContentPlugin)
  • LessCSS to allow leaner style-sheets (AssetPlugin, BundlerContentPlugin & BundlerTagHandlerPlugin)
  • UglifyJS version 2 minification, with multi-level source map support (MinifierPlugin)
  • Karma test runner for reliable browser based testing (TestPlugin)
  • Node.js test runner for reliable non-browser based testing (TestPlugin)
  • App-Cache support as an alternative to HTTP caching (TagHandlerPlugin & ContentPlugin)
  • Heroku deployment support (CommandPlugin)

We'll provide quick responsive support for anybody attempting to write any of these plug-ins that runs into problems in the core code base.

Plug-in Interfaces

The plug-in interfaces are defined as follows:

Plug-in Registration

Plug-ins are registered using the SPI Mechanism introduced in Java 6. All plug-ins available on the class-path will be automatically discovered, and be usable from within BladeRunnerJS. At present this can only be done by dropping plug-ins into the 'conf/java' directory, but by 1.0 we'll support plug-ins that get pulled in much the same way as libraries can, with the potential to do this using a package management tool like Bower.

Circular Dependencies

Because plug-ins form part of the model, yet use the model to initialize themselves, an untenable circular dependency exists. To overcome this problem, each concrete plugin instance is wrapped inside a Virtual Proxy that delays plug-in initialization until somebody attempts to actually use the plug-in.

Since code interested in interacting with a subset of the plug-ins will often need to query all of them to locate the ones it needs, certain identifier methods are proxied through before the object's setBRJS() method has been invoked, which plug-in authors must be aware of. Another consequence of wrapping all plug-ins in a virtual proxy is that the instanceof operator and the getClass() method does not work as expected. The Plugin.instanceOf() and Plugin.getPluginClass() methods are provided to overcome these deficiencies (see BRJS Initialization Problem for more details).

Further Reading

Clone this wiki locally