1 |
|
2 | /*!
|
3 | * Stylus - middleware
|
4 | * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
|
5 | * MIT Licensed
|
6 | */
|
7 |
|
8 | /**
|
9 | * Module dependencies.
|
10 | */
|
11 |
|
12 | var stylus = require('./stylus')
|
13 | , fs = require('fs')
|
14 | , url = require('url')
|
15 | , basename = require('path').basename
|
16 | , join = require('path').join
|
17 | , ENOENT;
|
18 |
|
19 | // COMPAT:
|
20 |
|
21 | try {
|
22 | ENOENT = require('constants').ENOENT;
|
23 | } catch (err) {
|
24 | ENOENT = process.ENOENT;
|
25 | }
|
26 |
|
27 | /**
|
28 | * Return Connect middleware with the given `options`.
|
29 | *
|
30 | * Options:
|
31 | *
|
32 | * `force` Always re-compile
|
33 | * `src` Source directory used to find .styl files
|
34 | * `dest` Destination directory used to output .css files
|
35 | * when undefined defaults to `src`.
|
36 | * `compile` Custom compile function, accepting the arguments
|
37 | * `(str, path, callback)`.
|
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 |
|
74 | module.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 | .render(fn);
|
99 | };
|
100 |
|
101 | // Middleware
|
102 | return function(req, res, next){
|
103 | if ('GET' != req.method && 'HEAD' != req.method) return next();
|
104 | var path = url.parse(req.url).pathname;
|
105 | if (/\.css$/.test(path)) {
|
106 | var cssPath = join(dest, path)
|
107 | , stylusPath = join(src, path.replace('.css', '.styl'));
|
108 |
|
109 | // Ignore ENOENT to fall through as 404
|
110 | function error(err) {
|
111 | next(ENOENT == err.errno
|
112 | ? null
|
113 | : err);
|
114 | }
|
115 |
|
116 | // Force
|
117 | if (force) return compile();
|
118 |
|
119 | // Compile to cssPath
|
120 | function compile() {
|
121 | fs.readFile(stylusPath, 'utf8', function(err, str){
|
122 | if (err) return error(err);
|
123 | options.compile(str, stylusPath, function(err, css){
|
124 | if (err) return next(err);
|
125 | fs.writeFile(cssPath, css, 'utf8', function(err){
|
126 | next(err);
|
127 | });
|
128 | });
|
129 | });
|
130 | }
|
131 |
|
132 | // Compare mtimes
|
133 | fs.stat(stylusPath, function(err, stylusStats){
|
134 | if (err) return error(err);
|
135 | fs.stat(cssPath, function(err, cssStats){
|
136 | // CSS has not been compiled, compile it!
|
137 | if (err) {
|
138 | if (ENOENT == err.errno) {
|
139 | compile();
|
140 | } else {
|
141 | next(err);
|
142 | }
|
143 | } else {
|
144 | // Source has changed, compile it
|
145 | if (stylusStats.mtime > cssStats.mtime) {
|
146 | compile();
|
147 | // Already compiled, defer serving
|
148 | } else {
|
149 | next();
|
150 | }
|
151 | }
|
152 | });
|
153 | });
|
154 | } else {
|
155 | next();
|
156 | }
|
157 | }
|
158 | }; |
\ | No newline at end of file |