Skip to content
Rich-Harris edited this page Nov 28, 2014 · 13 revisions

There are two types of Gobble plugin - directory transformers and file transformers. When you're writing a build definition (a.k.a. a [gobblefile](How to write a gobblefile)), you use both types of plugin the same way - with the node.transform() method:

var gobble = require( 'gobble' );

var sourcenode = gobble( 'path/to/files' );
module.exports = sourcenode.transform( myplugin, options );

In both cases, the plugin is simply a function. Gobble distinguishes between directory transformers and file transformers based on how many arguments the function takes.

Directory transformers

Directory transformers are those which take a set of files and produce one or more output files. The gobble-concat plugin is one example - it takes a folder of files and concatenates them into a single output file.

The function's signature is as illustrated here:

function myplugin ( inputdir, outputdir, options[, callback] ) {
  /*
    - `inputdir` is the path to a directory containing the
      output from the input node (which is `sourceNode`
      in this example)

    - `outputdir` is the path to a directory that this plugin
      must write files to (i.e. the `inputdir` for any
      subsequent transformation)

    - `options` is an object. it is shallow-cloned from the
      user-supplied options object (if none is supplied, it
      will be created as `{}`), so you can modify it safely

    - `callback` must be called once the files have been
      written. If something goes wrong, call it with an
      error object. You can, if you prefer, return a Promise
      rather than using a callback
  */
}

The function's name is used by Gobble to name temporary folders, for easier debugging. For that reason it's better to do...

function myplugin (...) {...}

...than...

var myplugin = function (...) {...}

...because the latter is an anonymous (rather than named) function.

File transformers

If your plugin transforms files on a one-to-one basis (e.g. compiling templates), rather than transforming a directory on an N-to-N basis, you can use a simpler alternative syntax:

function myplugin ( input, options ) {
  /*
    - `input` is the contents of an individual file

    - `options` is shallow-cloned from the user-supplied
      options (plus the defaults, if specified. User
      options override defaults)
  */

  return output;
}

As well as being easier to write, this has performance advantages - Gobble is able to cache the result of successful transformations and only re-run them for the individual files that have changed.

Optionally, you can specify default options, which can include accept and ext properties:

myplugin.defaults = {
  accept: [ '.abc', '.def' ], // can be string or array
  ext: '.xyz'
};

In this example, the transformation would be applied to foo.abc and bar.def, which would become foo.xyz and bar.xyz respectively. Other files, such as baz.ghi, would be passed through un-transformed. This is useful for doing this sort of thing:

if ( gobble.env() === 'production' ) {
  node = node
    .transform( 'uglifyjs' )
    .transform( 'htmlmin'  )
    .transform( 'imagemin' );
}

Sourcemaps and file transformers

With directory transformers, sourcemaps are easy - just write the .map file to outputdir and you're good to go.

With file transformers, you're not interacting with the filesystem, so there's no chance to write a .map file. Instead, return an object from the transformer, with code and map properties, rather than a string. The gobble-coffee plugin is an example:

function coffee ( code, options ) {
  var compiled;

  options.sourceMap = true;
  compiled = require( 'coffee-script' ).compile( code, options );

  return {
    code: compiled.js,
    map: compiled.v3SourceMap
  };
}

The map property should be an object that meets the sourcemap spec, or a JSON representation thereof.

The file, sources and sourcesContent properties of the sourcemap will be filled in by Gobble. Don't worry about being able to ingest sourcemaps from previous transformations - the sourcemaps can be combined later (e.g. with gobble-sorcery).

Publishing plugins

If you create a plugin that you think others would benefit from, please publish it to npm!

  • The package name should follow the gobble-myplugin convention. This allows people to invoke it with node.transform('myplugin',{...})
  • Remember to include a README, with installation and basic usage instructions. See gobble-concat for an example README
  • In the keywords property of your package.json, include gobble-plugin