UNPKG

4.11 kBJavaScriptView Raw
1/*!
2 * Stylus - middleware
3 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
4 * MIT Licensed
5 */
6
7/**
8 * Module dependencies.
9 */
10
11var stylus = require('./stylus')
12 , fs = require('fs')
13 , url = require('url')
14 , basename = require('path').basename
15 , join = require('path').join
16 , ENOENT;
17
18// COMPAT:
19
20try {
21 ENOENT = require('constants').ENOENT;
22} catch (err) {
23 ENOENT = process.ENOENT;
24}
25
26/**
27 * Return Connect middleware with the given `options`.
28 *
29 * Options:
30 *
31 * `force` Always re-compile
32 * `src` Source directory used to find .styl files
33 * `dest` Destination directory used to output .css files
34 * when undefined defaults to `src`.
35 * `compile` Custom compile function, accepting the arguments
36 * `(str, path, callback)`.
37 * `compress` Whether the output .css files should be compressed
38 *
39 * Examples:
40 *
41 * Here we set up the custom compile function so that we may
42 * set the `compress` option, or define additional functions.
43 *
44 * By default the compile function simply sets the `filename`
45 * and renders the CSS.
46 *
47 * function compile(str, path, fn) {
48 * stylus(str)
49 * .set('filename', path)
50 * .set('compress', true)
51 * .render(fn);
52 * }
53 *
54 * Pass the middleware to Connect, grabbing .styl files from this directory
55 * and saving .css files to _./public_. Also supplying our custom `compile` function.
56 *
57 * Following that we have a `staticProvider` layer setup to serve the .css
58 * files generated by Stylus.
59 *
60 * var server = connect.createServer(
61 * stylus.middleware({
62 * src: __dirname
63 * , dest: __dirname + '/public'
64 * , compile: compile
65 * })
66 * , connect.staticProvider(__dirname + '/public')
67 * );
68 *
69 * @param {Object} options
70 * @return {Function}
71 * @api public
72 */
73
74module.exports = function(options){
75 options = options || {};
76
77 // Accept src/dest dir
78 if ('string' == typeof options) {
79 options = { src: options };
80 }
81
82 // Force compilation
83 var force = options.force;
84
85 // Source dir required
86 var src = options.src;
87 if (!src) throw new Error('stylus.middleware() requires "src" directory');
88
89 // Default dest dir to source
90 var dest = options.dest
91 ? options.dest
92 : src;
93
94 // Default compile callback
95 options.compile = options.compile || function(str, path, fn){
96 stylus(str)
97 .set('filename', path)
98 .set('compress', options.compress)
99 .render(fn);
100 };
101
102 // Middleware
103 return function(req, res, next){
104 if ('GET' != req.method && 'HEAD' != req.method) return next();
105 var path = url.parse(req.url).pathname;
106 if (/\.css$/.test(path)) {
107 var cssPath = join(dest, path)
108 , stylusPath = join(src, path.replace('.css', '.styl'));
109
110 // Ignore ENOENT to fall through as 404
111 function error(err) {
112 next(ENOENT == err.errno
113 ? null
114 : err);
115 }
116
117 // Force
118 if (force) return compile();
119
120 // Compile to cssPath
121 function compile() {
122 fs.readFile(stylusPath, 'utf8', function(err, str){
123 if (err) return error(err);
124 options.compile(str, stylusPath, function(err, css){
125 if (err) return next(err);
126 fs.writeFile(cssPath, css, 'utf8', function(err){
127 next(err);
128 });
129 });
130 });
131 }
132
133 // Compare mtimes
134 fs.stat(stylusPath, function(err, stylusStats){
135 if (err) return error(err);
136 fs.stat(cssPath, function(err, cssStats){
137 // CSS has not been compiled, compile it!
138 if (err) {
139 if (ENOENT == err.errno) {
140 compile();
141 } else {
142 next(err);
143 }
144 } else {
145 // Source has changed, compile it
146 if (stylusStats.mtime > cssStats.mtime) {
147 compile();
148 // Already compiled, defer serving
149 } else {
150 next();
151 }
152 }
153 });
154 });
155 } else {
156 next();
157 }
158 }
159};
\No newline at end of file