This is an Express-compatible, Connect middleware for Mincer.
Mincer is an excellent port of Sprockets, which means it is a robust and comprehensive asset manager for your Node app. However, Mincer makes no assumptions about your application so by default it requires some work to get going with a typical Express app.
Using connect-mincer, you can skip that work and simply:
If you're used to the Rails asset pipeline, using this will give your Connect/Express application almost all the same capability - the only thing missing is a built-in precompile, but that's easily added (see the example app for an example precompile script).
npm install connect-mincer
Now, in your connect app:
var ConnectMincer = require'connect-mincer';var connectMincer =root: __dirnameproduction: processenvNODE_ENV === 'production'mountPoint: '/assets'manifestFile: __dirname + '/public/assets/manifest.json'paths:'assets/css''assets/js''vendor/js';appuseconnectMincerassets;if processenvNODE_ENV !== 'production'appuse'/assets' connectMincercreateServer;
The connectMincer.assets() middleware will:
Now, in your views, you can do this:
<%- css('main.css') %><%- js('application.js') %>
These helpers will output something like:
The second piece of middleware,
connectMincer.createServer(), sets up a Mincer server which will send the compiled version of the asset. This is great for development, though in production you'll probably want to have these files served by nginx or from a cloud host (see more about 'in production' below).
Mincer and this middleware are unopinionated about where your keep your assets. When you initialise connect-mincer you pass in several options:
Manifest.compile(see 'Precompiling' section).
A typical app folder structure might be this:
app/ assets/ js/ application.js css/ main.less blog/ more.css images/ logo.png lib/ public/ vendor/ js/ jquery.js app.js
With this, a suitable path list would be:
['assets/js', 'assets/css', 'assets/images', 'vendor/js']
Now anything within these paths can be referenced in your views with the helpers like so:
css('main.css') css('blog/more.css') js('jquery.js')
Which would become:
<link href='/assets/main.css' /> <link href='/assets/blog/more.css' /> <script src='/assets/jquery.js' />
connect-mincer provides several helpers which are available in all views. All of them take an asset filename as their first argument, which is used to search for a matching asset across all asset directories (i.e. the path option provided to connect-mincer). If the asset is actually a bundle (it includes or requires other assets), all of these will be returned in development mode. In production, only the bundle itself will be returned (which will include all its required dependencies concatenated within).
In all cases, the urls returned here will contain their MD5 digest if the app is running in production.
Looks for a matching JS asset and returns
<script> tag with the
src attribute set. Additional attributes can be provided as the second argument.
Looks for a matching CSS asset and returns a
<link> tag with the
href attribute set. Additional attributes can be provided as the second argument - by default it will have
Looks for any matching asset and returns the url to access it, e.g.
/assets/logo.png. If the filename is a bundle, and the app is in production, an Array of urls will be returned instead.
For production use, connect-mincer lets you pass in a Mincer manifest file and will use this to generate correct asset paths (with MD5 digests). Precompiling is easy, using Mincer.Manifest, and there's an example of this below. For now, let's assume you have precompiled your assets to the
/public/assets directory. This directory will contain a
manifest.json file which links asset filenames to their digest format.
If you pass the manifest file to connect-mincer, e.g:
manifestFile: __dirname + '/public/assets/manifest.json',
When the helpers (js, css, asset_path) are called in your views, connect-mincer will look up the compiled name in the manifest and use that, leading to an output like this:
which will correspond to the file
/public/assets/application-4b02e3a0746a47886505c9acf5f8c655.js. Now you can set nginx up to intercept requests to
/assets and serve the static file in
/public/assets instead. Thanks to the MD5 digest, you can set the cache headers to maximum. The next time you deploy and precompile the digests will change, and your app will adjust its
<link> tags accordingly.
OK, cool, you can do that. After precompiling all your assets are ordinary static files in
public/assets, so you can use the
connect.static middleware to serve them like any other static file. You can also use the
connect.staticCache middleware to speed it up for production.
appuseconnectstatic__dirname + '/public';
Because this is a middleware, it doesn't provide anything special to handle precompiling. But that's OK, because it's easy to do with Mincer so you can create your own custom precompile routine (e.g. a grunt task).
A simple precompile script:
var Mincer = require'mincer';var env = './';envappendPath'assets/js';envappendPath'assets/css';envappendPath'vendor/js';var manifest = env './public/assets';manifestcompile'*' '*/**'console.info'Finished precompile:';consoledirdata;;
This will precompile everything in the
vendor/js directories. You can pass in more specific paths to
manifest.compile() if you only want certain things to be included.
If you were to run this from your root app directory, it would create the folder
/public/assets, populate it with the compiled versions of all your assets, and create a manifest file suitable for passing to connect-mincer.
A more substantial example of a precompile script is part of the express example app, here.
Mincer is, like Sprockets, really powerful. The Mincer documentation has you covered.
connect-mincer supports anything Mincer does, so bundles and supported engines will all work out of the box. If you want to do more custom things, like adding helpers for your assets to use, you can add them directly to the Mincer Environment:
var connectMincer = ;connectMincerenvironmentregisterHelper'version'return require__dirname + '/package.json'version;;appuseconnectMincerassets;
This will add a
version helper which will be available in any asset, to be used like so:
appversion: '<%= version() %>';
Mincer supports helpers in EJS and Stylus. Fortunately, even if your asset is something else (e.g. CoffeeScript, LESS), you can attach the EJS processor to it and have variables too by appending
.ejs to the file. Mincer processes a filename from right to left, so the file:
will first be processed by EJS (resolving things like
<%= version() %>) and then LESS itself.
Note: any modifications to the environment must be done before the connectMincer.assets() middleware is called. When the app runs in production mode, the environment is set to a read-only index (for speed), so any modifications must be done before this happens.
All feedback or contributions are welcome!