Skip to content

seanami/node-static-asset

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 

Repository files navigation

#node-static-asset

node-static-asset is the best static asset manager for Node.JS, designed for use with Express, Jade, Stylus, and Browserify. This project aims to solve all application deployment problems. No thinking required.

Background

Google has a nice article about strong and weak caching. It's worth a quick read if you don't know what that means.

http://code.google.com/speed/page-speed/docs/caching.html

The Basics

node-static-asset allows you to compile, bundle, minify/compress, and deploy your static assets. The basic idea works like this:

  1. Define your static asset deployment strategy for 'development' and 'production' environments.
    • Tell node-static-asset where your static assets are.
    • Tell node-static-asset how to process certain file extensions.
    • Tell node-static-asset where to put the static assets after processing
  2. Deploy!
  3. Serve up using Amazon S3 or locally with built-in middleware. Point your views to the URL fingerprints of the generated assets.

See! Only 3 steps! :)

Install

npm install static-asset

Default Configuration

static-asset can be fully customized, but it has some basic, reasonably sane default behavior. By default, static-asset does the following:

  • .css and .styl files are rendered using Stylus
  • .jade files are compiled and written to *.js files
  • .coffee files are compiled to *.js files (not working yet...)
  • /client/index.js file is bundled using Browserify
  • Development Environment
    • When applicable, all files are compiled with debugging information, uncompressed
    • Source files are watched for changes and URL fingerprints are updated automatically.
  • Production Environment
    • When applicable, all files are minified and compiled without debugging information
    • URL fingerprints are cached and may change only if the server is restarted
  • Assumes the following directory structure for your project:
	project_root/
		/server
			The location of app.js
		/client
			All client-side .js and .coffee files
			/lib
				Client-side libraries
		/views
			.jade and .html files are in here
		/css
			.styl and .css source files are in here
		/public
			All of the content publicly available to the WWW
			/images
				Contains all of your images (png, jpeg, gif, bmp, etc.)
			/assets
				This dir may be generated by static-asset and can be wiped at any time!
			/abcdef
				This directory will never be touched. It's safe to put files in /static

Basic Usage

Usually, this should be good enough to get started. Stylus, CSS, Jade templates, and client-side JavaScript are automatically handled for you.

WARNING: the code below may wipe the contents of /public/assets. Please read the documentation carefully.

//Assume the full path of this file is /home/../project/server/app.js
var express = require('express');
var app = express.createServer();
var asset = require('static-asset');
asset.setPath('root', __dirname + "/../");
asset.useDefaultConfiguration();
asset.deploy();
app.use(asset.middleware);
app.use(express.static(__dirname + '/../public) );

//... application code follows (routes, etc.)

What just happened? Well, all of your static assets were just compiled and stored in /public/assets. In addition, the static assets can now be accessed using their URL fingerprint. For example, if you want to include your client-side JavaScript code and your /css/main.styl stylesheet, simply do this in your Jade view:

script(type="text/javascript", src=getURLFingerprint("/assets/client.js") )
link(type="text/css", href=getURLFingerprint("/css/main.styl") )

This will render to something like this:

<script type="text/javascript" src="/assets/client-1318365481.js"></script>
<link type="text/css" href="/assets/css/main-1318365481.css"/>

Notice that static-asset added a URL fingerprint (the UNIX timestamp 1318365481) to the filename. By default, when the source file is updated in development environments, the URL fingerprint will be updated, forcing the browser to reload the file. In production environments, by default, URL fingerprints are cached and cannot change until the server is restarted. All of this behavior can be customized, however.

Advanced Usage

Example:

var app = require('express').createServer();
var asset = require('static-asset');
var stylus = require('stylus');
asset.useDefaultConfiguration();

//Start overriding some configuration
asset.configure(function() {
	//Environment-specific configuration goes here... 'this' refers to 'asset'
	
	//CSS
	this.register(['styl', 'css'], function(body, filename, cb) {
		//'this' still refers to 'asset'
		stylus(body, this.get('stylus'))
			.set('filename', filename)
			.set('compress',  || true)
			.include(__dirname + '/../css/')
			.import(__dirname + '/../css/mixins.styl')
			.import(__dirname + '/../css/colors.styl')
			.render(cb);
	});
	//Error-handling
	this.on('error', function(err) {
		//Do something about some error
		console.log('This asset is being an ' + "asset".substring(0, 3) + ':', err.filename);
	});
});

asset.configure('development', function() {
	//Put your development-specific config here...
});
asset.deploy();
app.use(asset.middleware() );

asset.on('deployed', function() {
	console.log("Yay! It worked!");
});

API

asset.configure([environment,] fn)

TJ-style configure. Define a callback function for the given environment (or all environments) with callback fn.

asset.useDefaultConfiguration()

Configures static-asset with the default built-in configuration. Usually, you will want to call this first, and then override settings manually, if needed.

asset.setPath(type, path)

Allows you to customize the directory structure of your project. You should always set the root path of the project explicitly.

By default, paths looks like this...

{
	'root': process.cwd(),
	'client-js': '/client',
	'client-lib': '/client/lib',
	'views': '/views',
	'css': '/css',
	'output': '/public',
	'images': '/public/images'
}

asset.setPath('root', '/home/blake/foobar/'); would change the root path of the project. You get the idea.

asset.getPath(type)

This does what you would expect. asset.getPath('root') would return '/home/blake/foobar/'.

asset.register(extension, fn)

Register a handler for the given file extension. fn can manipulate the body of the file and output the contents back to static-asset. fn should be of the form: function (body, filename, cb) with cb of the form function (err, new_body). You can also pass an array of extensions as in asset.register(['css', 'styl'], function() {...});

If you call asset.register again with the same file extension, the previous handler will be replaced.

asset.output(fn)

Register a handler to deploy the static assets. fn is of the form: function (body, filename, cb) with cb of the form function (err)

static-asset comes with many built-in output functions for your convenience: * Deploy the assets to a local directory? Use asset.output.local * Assumes directory structure above. * Caution: May overwrite all contents of /public/assets * Deploy the assets to S3? Use asset.output.s3(opts)

By default, static-asset will use asset.output.local in production and development environments. If you call asset.output again, the original output function will be replaced by fn.

Example:

asset.configure('production', function() {
	asset.output(asset.output.s3({
		'key': 'my_key',
		'secret': 'my_secret_key',
		'bucket': 'my_bucket',
		'path': ... //optional path prepended to filename
		'headers': ... //optional headers
	})
	.use(function(req) {
		//`use()` is optional, but allows you to do something with the HTTP request Object...
		req.end(); //Be sure to call end() if you introduce middleware
	}) );
}
asset.configure('development', function() {
	//Deploy to /public/assets
	asset.output(asset.output.local);
	app.use(asset.middleware() );
});

asset.deploy(cb)

Compiles and deploys all static assets according to the options specified. Calls cb of the form function (err) when complete. Also, the 'deployed' signal is emitted after the callback is called.

asset.middleware()

Returns an Express middleware function of the form: function (req, res, next). The middleware is only useful when asset.output.local is set as the output function. In this case, if a compiled static asset is requested, strong caching headers will be added to the response. In addition, if the client sends "If-Modified-Since" header, then the middleware may respond with 304 Not Modified, if applicable. Otherwise, the request is passed to the next middleware to handle the request; presumably, a static file server middleware (i.e. connect.static) will handle the request.

Also, the middleware exposes the getURLFingerprint function by adding it to each HTTP request object. If Express is used, it is also exposed directly to views via res.local(...).

Otherwise, if asset.output.local is not used, the default middleware does nothing.

asset.use(fn)

Overrides the default middleware with your own function.

asset.version(fn)

fn allows static-asset to determine the version number of an asset. fn must be of the form: function(body, filename, cb) with cb of the form function(err, version_number).

This allows static-asset to implement strong caching of assets using URL fingerprinting. By default, the version number of an asset is the UNIX timestamp (in seconds, not milliseconds) of the file's last modification date.

asset.getURLFingerprint(filename, cb)

Generates the URL fingerprint for the specified filename, using the function provided in asset.version(fn). cb is of the form function(err, urlFingerprint).

URL fingerprints are of the form: base_path + base_filename '-' version_number '.' file_extension For example: /css/main.css might have a URL fingerprint of /css/main-1318365481.css

asset.cacheFingerprints(boolean)

Tells static-asset whether or not it should cache URL fingerprints. By default, caching is enabled in production, but disabled in development. This means that, in a production environment, a URL fingerprint for a given file is only generated once and only the first time that fingerprint is requested. In development environments where static assets may be changed, the URL fingerprints may change; and therefore, the web browser will be forced to reload the file.

asset.set(key, value)

Convenience function to store a key-value pair for later use.

asset.get(key, value)

Convenience function to retrieve a key-value pair that was stored using set().

Event: error

'error' is emitted whenever an error occurs. Usually, the error object will contain a filename and perhaps a lineno properly, if applicable.

Event: deployed

'deployed' is emitted when deployment is complete.

About

Static asset manager for Node.JS and Express

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%