UNPKG

5.33 kBJavaScriptView Raw
1var fs = require('fs');
2var coffee = require('coffee-script');
3var stylus = require('stylus');
4var jade = require('jade');
5var colors = require('colors');
6var walk = require('./walk.js').walk;
7var BuildError = require('./errors.js').BuildError;
8var CoffeeScriptError = require('./errors.js').CoffeeScriptError;
9var StylusError = require('./errors.js').StylusError;
10var JadeError = require('./errors.js').JadeError;
11
12
13/**
14 * Watch a file, passing its contents through a specified function and writing
15 * the result to disk.
16 */
17function Builder(source, destination, compileFunction) {
18 this.source = source;
19 this.destination = destination;
20 this.compileFunction = compileFunction;
21 this.build();
22 this.start();
23}
24
25Builder.prototype = {
26
27 /**
28 * Compile our target file.
29 */
30 build: function() {
31 console.log('Reading:'.grey, this.source);
32 var self = this;
33
34 fs.readFile(this.source, 'utf8', function(err, data) {
35
36 try {
37 var compiled = self.compileFunction(data);
38 } catch(err) {
39 err.printError();
40 }
41
42 if(compiled) {
43 fs.writeFile(self.destination, compiled, function(err) {
44 console.log('Writing:'.grey, self.destination);
45 });
46 }
47
48 });
49 },
50
51 /**
52 * Start watching file and build when it changes.
53 */
54 start: function() {
55 var self = this;
56 fs.watch(this.source, function(event, filename) {
57 if(event == 'change') {
58 self.build();
59 }
60 });
61 },
62
63 /**
64 * Stop watching (and building) file.
65 */
66 stop: function() {
67 fs.unwatchFile(this.source);
68 }
69
70};
71
72
73/**
74 * Compiles CoffeeScript into JavaScript.
75 */
76function CoffeeBuilder(source, destination) {
77
78 var destination = destination || replaceExtension(source, 'js');
79
80 var builder = new Builder(source, destination, function(input) {
81 try {
82 return coffee.compile(input);
83 } catch(err) {
84 throw new CoffeeScriptError(err.message, source);
85 }
86 });
87
88 this.start = builder.start;
89 this.stop = builder.stop;
90}
91
92
93/**
94 * Compiles Stylus into CSS.
95 */
96function StylusBuilder(source, destination) {
97
98 var destination = destination || replaceExtension(source, 'css');
99
100 var builder = new Builder(source, destination, function(input) {
101 var output;
102 stylus.render(input, {}, function(err, css) {
103 if(err) {
104 throw new StylusError(err.message, source);
105 }
106 output = css;
107 });
108 return output;
109 });
110
111 this.start = builder.start;
112 this.stop = builder.stop;
113}
114
115
116/**
117 * Compiles Jade into HTML.
118 */
119function JadeBuilder(source, destination) {
120
121 var destination = destination || replaceExtension(source, 'html');
122
123 var builder = new Builder(source, destination, function(input) {
124 try {
125 return jade.compile(input, { pretty: true })();
126 } catch(err) {
127 throw new JadeError(err.message, source);
128 }
129 });
130
131 this.start = builder.start;
132 this.stop = builder.stop;
133}
134
135
136/*
137 * Replace the extension on a filename.
138 */
139function replaceExtension(filename, newExtension) {
140 return filename.replace(/(?:\.\w+)$/i, '.' + newExtension);
141};
142
143
144/**
145 * Cakewalker
146 *
147 * Object that initiates the walking procedure.
148 *
149 * Usage:
150 * new Cakewalk('path/to/directory/to/watch');
151 */
152function Cakewalker(directory) {
153
154 this.builders = [];
155 this.directory = directory || '.';
156
157 this.start();
158
159}
160
161// Note: the last rule in the below pattern should ignore all files prefixed
162// with an underscore. Those files should be treated as includes and mixins and
163// thus need not be compiled separately.
164Cakewalker.IGNORE_PATTERN = new RegExp(/(?:\.git|\.svn|node_modules|\/_)/);
165
166Cakewalker.COFFEE_PATTERN = new RegExp(/\.coffee$/);
167Cakewalker.STYLUS_PATTERN = new RegExp(/\.styl$/);
168Cakewalker.JADE_PATTERN = new RegExp(/\.jade$/);
169
170Cakewalker.prototype = {
171
172 /**
173 * Walk a directory, find files to compile, and watch them for changes.
174 */
175 start: function() {
176
177 var self = this;
178
179 walk(this.directory, function(err, files) {
180
181 if(err) throw new Error(err);
182
183 for(var i=0; i<files.length; i++) {
184 var file = files[i];
185
186 if(!Cakewalker.IGNORE_PATTERN.test(file)) {
187
188 if(Cakewalker.COFFEE_PATTERN.test(file)) {
189 self.builders.push(new CoffeeBuilder(file));
190 } else if(Cakewalker.STYLUS_PATTERN.test(file)) {
191 self.builders.push(new StylusBuilder(file));
192 } else if(Cakewalker.JADE_PATTERN.test(file)) {
193 self.builders.push(new JadeBuilder(file));
194 }
195
196 }
197 }
198
199 if(self.builders.length === 0) {
200 console.error('No CoffeeScript, Stylus, or Jade files found. Exiting.');
201 process.exit();
202 }
203
204 });
205
206 },
207
208 /**
209 * Stop watching and building.
210 */
211 stop: function() {
212 for(var i = 0; i < builders.length; i++) {
213 builders[i].stop();
214 }
215 }
216
217}
218
219
220exports.Cakewalker = Cakewalker;