UNPKG

5.18 kBJavaScriptView Raw
1var path = require('path');
2var fs = require('fs');
3var _ = require('underscore')._;
4var EventEmitter = require('events').EventEmitter;
5var util = require('util');
6
7var build = require('./build');
8
9function FileTree(data, file) {
10 this.onChange = _.bind(this.onChange, this);
11 this.onChange = _.debounce(this.onChange, 3000, true);
12 this.onChildChange = _.bind(this.onChildChange, this);
13 EventEmitter.call(this);
14 this.data = data;
15 this.file = file;
16 this.children = [];
17 this.watch();
18 this.branch();
19}
20
21util.inherits(FileTree, EventEmitter);
22
23FileTree.prototype.watch = function() {
24 var self = this;
25 if (this.file) {
26 this.watcher = fs.watch(this.file, this.onChange);
27 }
28};
29FileTree.prototype.onChange = function(event) {
30 // update everything below this level of the tree
31 console.log('Mason: changed', this.file);
32 this.emit('change', this);
33 this.branch();
34};
35FileTree.prototype.onChildChange = function(tree) {
36 this.emit('change', this);
37},
38FileTree.prototype.stop = function() {
39 if (this.watcher) this.watcher.close();
40 this.removeAllListeners();
41};
42FileTree.prototype.addChild = function(child) {
43 child.on('change', this.onChildChange);
44 this.children.push(child);
45};
46FileTree.prototype.removeAllChildren = function() {
47 this.children.forEach(function(child) {
48 child.stop();
49 });
50 this.children = [];
51};
52FileTree.prototype.branch = function() {
53 var self = this,
54 newChild;
55 this.removeAllChildren();
56 if (this.file) {
57 // File asset
58 this[this.data.type]();
59 }
60 else {
61 // Non-file asset
62 if (typeof this.data.source === 'string') {
63 // Simple source
64 var fileName = path.join(this.data.dir, this.data.source);
65 newChild = new FileTree(this.data, fileName);
66 self.addChild(newChild);
67 }
68 else if (typeof this.data.source === 'object') {
69 // Complex source
70 this.data.source.src.forEach(function(src) {
71 var fileName = path.join(self.data.dir, self.data.source.base, src);
72 newChild = new FileTree(self.data, fileName);
73 self.addChild(newChild);
74 });
75 }
76 }
77};
78FileTree.prototype.javascript = function() {
79 // JS doesn't branch
80};
81FileTree.prototype.stylus = function() {
82 var self = this;
83 var dir = path.dirname(this.file);
84 var file = fs.readFileSync(this.file, 'utf-8');
85 var imports = file.match(/@import '(.*)'/g);
86 imports && imports.forEach(function(imported) {
87 var first = imported.indexOf("'") + 1;
88 var last = imported.lastIndexOf("'");
89 var fileName = imported.slice(first, last);
90 fileName += (path.extname(fileName) === '') ? '.styl' : '';
91 fileName = path.join(dir, fileName);
92 self.addChild(new FileTree(self.data, fileName));
93 });
94};
95FileTree.prototype.jade = function() {
96 var self = this;
97 var dir = path.dirname(this.file);
98 var file = fs.readFileSync(this.file, 'utf-8');
99 var extended = file.match(/extends (.*)/g);
100 extended && extended.forEach(function(extend) {
101 var fileName = extend.slice(8);
102 fileName += (path.extname(fileName) === '') ? '.jade' : '';
103 fileName = path.join(dir, fileName);
104 self.addChild(new FileTree(self.data, fileName));
105 });
106 var included = file.match(/include (.*)/g);
107 included && included.forEach(function(include) {
108 var fileName = include.slice(8);
109 fileName += (path.extname(fileName) === '') ? '.jade' : '';
110 fileName = path.join(dir, fileName);
111 self.addChild(new FileTree(self.data, fileName));
112 });
113};
114
115
116function Asset(name, data) {
117 var self = this;
118 this.rebuild = _.debounce(this.rebuild, 2000, true);
119 EventEmitter.call(this);
120 this.name = name;
121 this.data = data;
122 this.fileTree = new FileTree(this.data);
123 this.fileTree.on('change', function() {
124 self.emit('change', self.name);
125 });
126}
127
128util.inherits(Asset, EventEmitter);
129
130
131function Root(file) {
132 this.onChange = _.bind(this.onChange, this);
133 this.onJsonChange = _.bind(this.onJsonChange, this);
134 EventEmitter.call(this);
135 this.file = file;
136 this.children = [];
137 this.watch();
138 this.branch();
139}
140
141util.inherits(Root, EventEmitter);
142
143Root.prototype.watch = function() {
144 fs.watch(this.file, this.onJsonChange);
145};
146
147Root.prototype.onChange = function(name) {
148 names = name ? [name] : undefined;
149 this.emit('change', names);
150};
151
152Root.prototype.onJsonChange = function() {
153 this.emit('change');
154};
155
156Root.prototype.addChild = function(child) {
157 child.on('change', this.onChange);
158 this.children.push(child);
159};
160
161Root.prototype.removeAllChildren = function() {
162 this.children.forEach(function(child) {
163 child.removeAllListeners();
164 });
165 this.children = [];
166};
167
168Root.prototype.branch = function() {
169 var self = this;
170 this.removeAllListeners();
171 var dir = path.dirname(this.file);
172 var assets = require(this.file);
173 Object.keys(assets).forEach(function(name) {
174 var asset = assets[name];
175 asset.dir = dir;
176 self.addChild(new Asset(name, asset));
177 });
178};
179
180
181module.exports = function watch(root, config) {
182 config = config || {};
183 var file = path.join(root, 'mason.json');
184
185 var rootWatcher = new Root(file);
186 rootWatcher.on('change', rebuild);
187 rebuild();
188
189 function rebuild(names) {
190 build(root, config, names);
191 }
192};
193