Skip to content

Instantly share code, notes, and snippets.

@jonschlinkert
Last active February 8, 2024 08:26
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jonschlinkert/e2da295ec7ca5d159914 to your computer and use it in GitHub Desktop.
Save jonschlinkert/e2da295ec7ca5d159914 to your computer and use it in GitHub Desktop.
How to create a project with Assemble v0.6.0

assemblefile.js

This is a fairly complete assemblefile

Shows how to:

  • load templates
  • use tasks with plugins
  • use middleware
  • register helpers
var assemble = require('assemble');
var htmlmin = require('gulp-htmlmin');
var minifycss = require('gulp-minify-css');
var less = require('gulp-less');

// load templates to be used in any or all of the tasks defined below
assemble.partials('templates/partials/*.hbs');
assemble.layouts('templates/layouts/*.hbs');

assemble.task('html', function() {
  assemble.src('templates/*.hbs')
    // `htmlmin` is a plugin that will minify html after templates are rendered
    .pipe(htmlmin())
    .pipe(assemble.dest('_gh_pages'));
});

assemble.task('css', function() {
  assemble.src('styles/*.less')
    // pre-process less
    .pipe(less())
    // minify css
    .pipe(minifycss())
    .pipe(assemble.dest('_gh_pages/assets/css'));
});

// running `assemble` in the command line will execute both the html and css tasks
assemble.task('default', ['html', 'css']);

Helper

Example of creating, registering and using a custom helper:

Create a helper

// in `helpers/uppercase.js`
module.exports = function uppercase(str) {
  return str.toUpperCase();
};

Register the helper

In assemblefile.js

assemble.helper('uppercase', require('./helpers/uppercase');

Usage

{{uppercase name}}
//=> ASSEMBLE

More complex helpers

This is an example of a synchronous helper that uses various assemble objects and methods.

function index(collection, options, context) {
  // this is the instance of assemble
  var app = this.app;
  // context is created from front-matter, locals, and global context.
  // if necessary you can merge the helper's context in as well
  var ctx = this.context;
  // assemble options, defined using `assemble.option('foo', 'bar')`
  var opts = this.options;

  // `assemble.views` is used to store all template collections
  // so this might be `assemble.views.pages`
  var collection = assemble.views[collection];
  for (var item in collection) {
    if (collection.hasOwnProperty(item)) {
      var template = collection[item];
      // do stuff to template
    }
  }
  return something;
}

Async helpers

Here are a couple of async helper examples, both completely different use cases:

Middleware

Assemble middleware example

  • middleware functions are similar to plugins, except that file.content is used (as a string) instead of file.contents (as a buffer or stream).
  • Any number of middleware functions can be run during the lifecycle of a single plugin.
  • middleware VERBS are used to determine when middleware is run. These verbs are:
    • .onLoad(): middleware runs immediately when a template is loaded. Can be used on templates of any type, including partials and layouts.
    • .preRender(): middleware runs directly before render. Only renderable templates will be looped over, since layouts have already been applied and partials are on the context to pass to the engine.
    • .postRender(): middleware runs directly after render.
    • .use() runs everywhere middleware is called

(There are other methods related to middleware but they go beyond the scope of this)

Example middleware

// `app` is used here since middlewares can be used
// by other libs besides assemble, such as Template and verb
module.exports = function(app) {
  return function(file, next) {
    // do stuff
    next();
  }
};

// used with whatever VERB makes sense, like this
app.preRender(/foo/, require('your-middleware')(app));

Detailed example

// in 'middleware/indexPages.js'
module.exports = function indexPages(file, next) {
  // 'file' is a template object
  // 'file.path' is the template's source filepath
  // 'file.content' is the template's content
  // 'file.data' is from front-matter
  // 'file.locals' are passed on template methods, e.g assemble.page(..., {locals: {}})
  next();
});

assemble.preRender(/foo/, require('./middleware/indexPages'));

Plugin

This is an example of a basic plugin.

  • plugins are used in a task pipeline
  • plugins follow the some format and conventions as gulp plugins.
  • you can use any gulp plugin with assemble, and you can use their documentation as a resource.
var through = require('through2');
var foo = require('some-module');

module.exports = function(options) {
  return through.obj(function (file, enc, cb) {
    var str = file.contents.toString();
    // we only process one file at a time in the function body
    
    // do stuff to string, re-buffer content, and push 
    // the file back into the stream
    file.contents = new Buffer(foo(str));
    this.push(file);
    cb();
  }, function(cb) {
    // this is the "flush" function. if you need to loop over a collection
    // that gets built up at runtime, that would be done here.
    // Example:
    for (var key in collection) {
      var file = collection[key];
      // do stuff to file
      file.contents = new Buffer(file.content);
      this.push(file);
      cb();
    }
  });
};

Templates

How custom template collections work in Assemble v0.6.0

You might already be familiar with pages, layouts and partials. These are Assemble's default template collections, created using the .create() method, the same way a user would create their own custom template collections.

Template types

What's a template type?

Template collections may be associated with one or more "types". Types, among other things, determine which convenience methods are decorated onto a collection, and how templates are handled at render time.

Here are the currently supported types (these will make more sense when you read the template collections section):

  • partial: templates that belong to this type may be used as partials.
  • layout: templates that belong to this type may be used as layouts
  • renderable: templates that belong to this type may be rendered

One more type is under consideration:

  • index: templates of this type would be automatically generated to be used as index pages for a given view collection

Template collections

Let's create a collection and associate it with one or more types, then we'll explain it afterwards:

assemble.create('include', {isPartial: true});

We just created a new template collection, include that:

  • has the isPartial designation, which means it belongs to the partial template type
  • all template collections are stored on assemble.views using the plural form of the collection. so these would be stored on assemble.views.includes
  • two special methods have been created for loading includes: assemble.include() and assemble.includes()
  • a special get method has been decorated: assemble.getInclude()
  • a special helper was created to allow any include to be used as a partial. example: {{include "foo" context}}

Any of the above methods may be overridden by the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment