Skip to main content
  • Middleman Home page
  • Support
  • Community
  • Documentation

Basics

  • Install
  • Upgrading to v4
  • Start a New Site
  • Directory Structure
  • Development Cycle
  • Build & Deploy
  • Frontmatter
  • Templating Language
  • Helper Methods
  • Layouts
  • Partials
  • Redirects
  • Blogging

Advanced

  • Configuring Your Site
  • Project Templates
  • Dynamic Pages
  • Data Files
  • Localization (i18n)
  • Asset Pipeline
  • External Pipeline
  • Rack Middleware
  • The Sitemap
  • Pretty URLs (Directory Indexes)
  • Improving Cacheability
  • File Size Optimization
  • Custom Extensions
  • Template Engine Options

External Pipeline

We are in the middle of an explosion of front-end languages and tooling. Since the beginning, Middleman has defaulted to Rails' Asset Pipeline as our solution for frontend dependency management and compilation.

In the past few years, the community has moved away from Rails and is now focused on task runners (gulp, Grunt), dependency management (Browserify, webpack) via npm, official tooling (EmberCLI, React Native) and transpilers (ClojureScript, Elm).

Middleman cannot accommodate all these different solutions and languages, so we've decided to allow all of them to live inside Middleman. This feature is called external_pipeline and it allows Middleman to run multiple subprocesses which output content to temporary folders which are then merged into the Middleman sitemap.

Ember Example

A simple Ember example looks like this:

activate :external_pipeline,
  name: :ember,
  command: "cd test-app/ && ember #{build? ? :build : :serve} --environment #{config[:environment]}",
  source: "test-app/dist",
  latency: 2

This assumes that you have a test-app folder in your Middleman project which contains the Ember frontend code. When in build mode, we tell Ember to do a static build. When in dev, we get a dev build.

Ember compiles this information to the test-app/dist folder, who's contents we merge into the Middleman sitemap.

webpack Example

activate :external_pipeline,
  name: :webpack,
  command: build? ? './node_modules/webpack/bin/webpack.js --bail' : './node_modules/webpack/bin/webpack.js --watch -d',
  source: ".tmp/dist",
  latency: 1

Broccoli Example

Broccoli is a powerful asset pipeline in the Node ecosystem. With a myriad of plugins it can accommodate most pre-processing needs: CSS-languages (SCSS, Compass), minification (UglifyJS et. al), module loaders, transpilation (Babel, etc.) and much more.

config.rb

activate :external_pipeline,
  :name => 'broccoli',
  :command => (build? ? 'broccoli build pipeline-build' : 'broccoli-timepiece pipeline-build'),
  :source => 'pipeline-build',
  :latency => 2

Brocfile.js (this can be expanded with Babel, SCSS and similar plugins)

/* globals module,require,process */

var Funnel            = require('broccoli-funnel');
var mergeTrees        = require('broccoli-merge-trees');
var concatFiles       = require('broccoli-concat');
var uglifyJavaScript  = require('broccoli-uglify-js');

var env = process.env.BROCCOLI_ENV || 'production';

var SOURCE_DIR = 'assets';
var OUTPUT_DIR = 'res';

var VENDOR_JS = [
  'jquery-1.11.0.js',
];

var VENDOR_CSS = [
  'normalize-2.1.2.css',
];


// JavaScript
var jsVendorTree = concatFiles(SOURCE_DIR + '/vendor/js', {
  outputFile: 'vendor.js',
  headerFiles: VENDOR_JS,
});

var jsOurTree = new Funnel(SOURCE_DIR + '/js');

// merge vendor and our js
var jsTree = mergeTrees([jsVendorTree, jsOurTree]);

// concat vendor and our js
jsTree = concatFiles(jsTree, {
  outputFile: 'js/all.js',
  headerFiles: ['vendor.js'], // headerFiles are ordered
  inputFiles: ['**/*.js'], // inputFiles are un-ordered
  sourceMapConfig: { enabled: (env === 'development') },
});

if (env !== 'development') {
  jsTree = uglifyJavaScript(jsTree);
}


// CSS
var cssVendorTree = concatFiles(SOURCE_DIR + '/vendor/css', {
  outputFile: 'vendor.css',
  headerFiles: VENDOR_CSS,
});

var cssOurTree = new Funnel(SOURCE_DIR + '/css');

// merge vendor and our css
var cssTree = mergeTrees([cssVendorTree, cssOurTree]);

// concat vendor and our css
cssTree = concatFiles(cssTree, {
  outputFile: 'css/all.css',
  headerFiles: ['vendor.css'], // headerFiles are ordered
  inputFiles: ['**/*.css'], // inputFiles are un-ordered
  sourceMapConfig: { enabled: (env === 'development') },
});


// images
var imagesTree = new Funnel(SOURCE_DIR + '/images', {
  destDir: 'images',
});


// merge everything
var everythingTree = mergeTrees([jsTree, cssTree, imagesTree]);

var finalTree = new Funnel(everythingTree, {
  destDir: OUTPUT_DIR,
});

module.exports = finalTree;

package.json

{
  "name": "assets",
  "version": "1.0.0",
  "description": "Website assets",
  "private": true,
  "engines": {
    "node": ">= 0.10.0"
  },
  "author": "Middleman Team",
  "devDependencies": {
    "babel-preset-es2015": "^6.18.0",
    "broccoli": "0.16.9",
    "broccoli-babel-transpiler": "5.5.0",
    "broccoli-concat": "^3.0.1",
    "broccoli-funnel": "1.0.3",
    "broccoli-merge-trees": "1.1.2",
    "broccoli-timepiece": "rwjblue/broccoli-timepiece",
    "broccoli-uglify-js": "0.2.0"
  }
}

© 2011–2022 Thomas Reynolds

Maintained by the core team with help from contributors.

  • Twitter
  • GitHub