Permalink
Browse files

stylesheets

  • Loading branch information...
1 parent cc2b3ab commit ed0068bf649b391dc24b844c95d725cc6b54fef5 @lancejpollard committed Nov 16, 2011
View
5 Cakefile
@@ -5,8 +5,9 @@ fs = require 'fs'
task 'coffee', ->
coffee = spawn './node_modules/coffee-script/bin/coffee', ['-o', 'lib', '-w', 'src']
coffee.stdout.on 'data', (data) -> console.log data.toString().trim()
- coffee = spawn './node_modules/coffee-script/bin/coffee', ['-o', 'spec', '-w', 'spec']
- coffee.stdout.on 'data', (data) -> console.log data.toString().trim()
+ #coffee2 = spawn './node_modules/coffee-script/bin/coffee', ['-w', 'spec', '-o', 'spec']
+ #coffee2.stdout.on 'data', (data) -> console.log data.toString().trim()
+ #coffee2.stderr.on 'data', (data) -> console.log data.toString().trim()
task 'spec', 'Run jasmine specs', ->
spec = spawn './node_modules/jasmine-node/bin/jasmine-node', ['--coffee', './spec']
View
0 MIT-LICENSE → MIT-LICENSE.md
File renamed without changes.
View
6 README.md
@@ -74,6 +74,12 @@ app.post '/design.io/:action', (request, response) ->
response.send request.params.action
```
+## Possibilities
+
+- Incrementing values with keyboard and swipe pad in textmate. http://old.nabble.com/Incremental-Sequences-for-Replacement-td27741019.html
+- http://stackoverflow.com/questions/3459476/how-to-append-to-a-file-in-node
+- http://francisshanahan.com/index.php/2011/stream-a-webcam-using-javascript-nodejs-android-opera-mobile-web-sockets-and-html5/
+
## License
(The MIT License)
View
13 Watchfile
@@ -1,8 +1,9 @@
#watcher "stylesheet", extensions: [".styl", ".less", ".sass", ".css"]
-require("./extensions/stylesheets")(compress: true)
-require("./extensions/javascripts")()
-require("./extensions/templates")()
+File = require('pathfinder').File
-watch /\.mustache/
- update: (path) ->
- @logger.info "Mustache Template: #{path}"
+require("../../spec/stylesheetExtension")
+ compress: true
+ #outputPath: (path) ->
+ # "spec/tmp/test.css"
+ write: (path, string) ->
+ console.log File.digestFile(path)
View
2 bin/design.io
@@ -1,3 +1,3 @@
#!/usr/bin/env node
-require('../lib/design.io/process.js.js')
+require('../lib/design.io/process.js')
View
5 lib/design.io.js
@@ -1,11 +1,10 @@
+ require('underscore.logger');
+
module.exports = {
watcher: require('./design.io/watcher'),
command: require('./design.io/command'),
connection: require('./design.io/connection'),
- logger: new (require("common-logger"))({
- colorized: true
- }),
extension: function(name) {
return require("./design.io/extensions/" + name).apply(this, Array.prototype.slice.call(arguments, 1, arguments.length));
}
View
37 lib/design.io/client.js
@@ -4,8 +4,6 @@
function DesignIO(options) {
options || (options = {});
this.callbacks = {};
- this.stylesheets = {};
- this.javascripts = {};
this.watchers = [];
this.port = options.port || 4181;
this.url = options.url || ("" + window.location.protocol + "//" + window.location.hostname + ":" + this.port + "/design.io");
@@ -20,11 +18,10 @@
return socket.on('connect', function() {
socket.emit('userAgent', self.userAgent());
socket.on('watch', function(data) {
- console.log(data);
- return self.watch(JSON.parse(data, self.reviver));
+ return self.watch(data);
});
return socket.on('exec', function(data) {
- return self.exec(JSON.parse(data, self.reviver));
+ return self.exec(data);
});
});
};
@@ -39,18 +36,38 @@
};
DesignIO.prototype.watch = function(data) {
- return this.watchers = data.body;
+ var watcher, watchers, _i, _len, _results;
+ this.watchers = watchers = JSON.parse(data, this.reviver).body;
+ _results = [];
+ for (_i = 0, _len = watchers.length; _i < _len; _i++) {
+ watcher = watchers[_i];
+ watcher.client = this;
+ watcher.log = function(data) {
+ data.path || (data.path = this.path);
+ data.action || (data.action = this.action);
+ data.timestamp || (data.timestamp = new Date);
+ data.id = this.id;
+ return this.client.log(data);
+ };
+ if (watcher.hasOwnProperty("connect")) {
+ _results.push(watcher.connect());
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
};
DesignIO.prototype.exec = function(data) {
var watcher, watchers, _i, _len;
+ data = JSON.parse(data, this.reviver);
watchers = this.watchers;
for (_i = 0, _len = watchers.length; _i < _len; _i++) {
watcher = watchers[_i];
- if (watcher.match(data.path)) {
- if (watcher.hasOwnProperty(data.action)) {
- watcher[data.action].call(this, data);
- }
+ if (watcher.id === data.id) {
+ watcher.path = data.path;
+ watcher.action = data.action;
+ if (watcher.hasOwnProperty(data.action)) watcher[data.action](data);
}
}
return this.runCallback(data.action, data);
View
5 lib/design.io/connection.js
@@ -10,16 +10,15 @@
io.set('log level', 1);
designer = io.of("/design.io");
designer.on("connection", function(socket) {
- console.log('passed it!');
socket.on("userAgent", function(data) {
return socket.set("userAgent", data, function() {
socket.emit("ready");
Watcher.connect();
return true;
});
});
- socket.on("log", function(msg) {
- console.log(msg);
+ socket.on("log", function(data) {
+ Watcher.log(data);
return true;
});
return socket.on("disconnect", function() {
View
51 lib/design.io/extensions/javascripts.js
@@ -1,51 +0,0 @@
-(function() {
- var Shift, fs, _path;
-
- Shift = require('shift');
-
- _path = require('path');
-
- fs = require('fs');
-
- module.exports = function() {
- var args, compressor, options;
- args = Array.prototype.slice.call(arguments, 0, arguments.length);
- options = typeof args[args.length - 1] === "object" ? args.pop() : {};
- if (!(args.length > 0)) args[0] = /\.(coffee|ejs|js)$/;
- if (options.hasOwnProperty("compress") && options.compress === true) {
- compressor = new Shift.UglifyJS;
- }
- return Watcher.create(args, {
- update: function(path) {
- var self;
- self = this;
- return fs.readFile(path, 'utf-8', function(error, result) {
- return Shift.render({
- path: path,
- string: result
- }, function(error, output) {
- if (error) return self.error(error);
- if (compressor) {
- return compressor.render(output, function(error, result) {
- if (error) return self.error(error);
- return self.broadcast({
- body: result
- });
- });
- } else {
- return self.broadcast({
- body: output
- });
- }
- });
- });
- },
- client: {
- update: function(data) {
- return $("<script id='" + data.id + "' type='text/javascript'>" + data.body + "</script>");
- }
- }
- });
- };
-
-}).call(this);
View
43 lib/design.io/extensions/reload.js
@@ -1,43 +0,0 @@
-(function() {
- var exec;
-
- exec = require('child_process').exec;
-
- module.exports = function() {
- var args, command, options;
- args = Array.prototype.slice.call(arguments, 0, arguments.length);
- options = typeof args[args.length - 1] === "object" ? args.pop() : {};
- if (!(args.length > 0)) args[0] = /.*/;
- if (!options.server) {
- throw new Error("You must specify the restart command: require('design.io/reload')(command: 'node app.js')");
- }
- command = options.command;
- return Watcher.create(args, {
- update: function(path) {
- return exec('ps aux | grep node', function(e, stdout, o) {
- var line, lines, match, pid, _i, _len, _results;
- lines = stdout.toString().split("\n");
- _results = [];
- for (_i = 0, _len = lines.length; _i < _len; _i++) {
- line = lines[_i];
- if (line.match(server)) {
- match = line.match(/[^ ]+ +(\d+)/);
- if (match) {
- pid = match[1];
- exec("kill -2 " + pid);
- exec(command);
- _results.push(console.log("Server restarted... " + command));
- } else {
- _results.push(void 0);
- }
- } else {
- _results.push(void 0);
- }
- }
- return _results;
- });
- }
- });
- };
-
-}).call(this);
View
66 lib/design.io/extensions/stylesheets.js
@@ -1,66 +0,0 @@
-(function() {
- var Shift, fs, _path;
-
- Shift = require('shift');
-
- _path = require('path');
-
- fs = require('fs');
-
- module.exports = function() {
- var args, compressor, options;
- args = Array.prototype.slice.call(arguments, 0, arguments.length);
- options = typeof args[args.length - 1] === "object" ? args.pop() : {};
- if (!(args.length > 0)) args[0] = /\.(styl|less|css|sass|scss)$/;
- if (options.hasOwnProperty("compress") && options.compress === true) {
- compressor = new Shift.YuiCompressor;
- }
- return Watcher.create(args, {
- update: function(path) {
- var self;
- self = this;
- fs.readFile(path, 'utf-8', function(error, result) {
- return Shift.render({
- path: path,
- string: result
- }, function(error, output) {
- if (error) return self.error(error);
- if (compressor) {
- return compressor.render(output, function(error, result) {
- if (error) return self.error(error);
- return self.broadcast({
- body: result
- });
- });
- } else {
- return self.broadcast({
- body: output
- });
- }
- });
- });
- return true;
- },
- client: {
- connect: function() {
- return this.stylesheets = {};
- },
- update: function(data) {
- var node;
- if (this.stylesheets[data.id] != null) {
- this.stylesheets[data.id].remove();
- }
- node = $("<style id='" + data.id + "' type='text/css'>" + data.body + "</style>");
- this.stylesheets[data.id] = node;
- return $("body").append(node);
- },
- "delete": function(data) {
- if (this.stylesheets[data.id] != null) {
- return this.stylesheets[data.id].remove();
- }
- }
- }
- });
- };
-
-}).call(this);
View
35 lib/design.io/extensions/templates.js
@@ -1,35 +0,0 @@
-(function() {
- var Shift, fs, _path;
-
- Shift = require('shift');
-
- _path = require('path');
-
- fs = require('fs');
-
- module.exports = function() {
- var args, options;
- args = Array.prototype.slice.call(arguments, 0, arguments.length);
- options = typeof args[args.length - 1] === "object" ? args.pop() : {};
- if (!(args.length > 0)) args[0] = /\.(jade|mustache|haml|erb|coffee)$/;
- return Watcher.create(args, {
- update: function(path) {
- var self;
- self = this;
- fs.readFile(path, 'utf-8', function(error, result) {
- return Shift.render({
- path: path,
- string: result
- }, function(error, output) {
- if (error) return self.error(error);
- return self.broadcast({
- body: output
- });
- });
- });
- return true;
- }
- });
- };
-
-}).call(this);
View
2 lib/design.io/extensions/watchfile.js
@@ -4,7 +4,7 @@
update: function() {
return Watcher.update();
},
- "delete": function() {
+ destroy: function() {
return Watcher.update();
}
});
View
48 lib/design.io/listener.js
@@ -9,41 +9,23 @@
Listener = (function() {
- function Listener(root, options) {
- if (options == null) options = {};
+ function Listener(root, callback) {
+ var directories, files, path, paths, source, stat, _i, _len;
this.root = root;
- this.directories = {};
- this.files = {};
- this.logger = require('../design.io').logger;
- }
-
- Listener.prototype.listen = function(callback) {
- var files, paths, root, source, stat, _i, _len, _results;
- files = this.files;
- root = this.root;
+ this.directories = directories = {};
+ this.files = files = {};
paths = require('findit').sync(root);
- _results = [];
for (_i = 0, _len = paths.length; _i < _len; _i++) {
source = paths[_i];
stat = File.stat(source);
+ path = _path.join(root, source.replace(root, ""));
if (!stat.isDirectory()) {
- _results.push(files[_path.join(root, source.replace(root, ""))] = stat);
+ files[path] = stat;
} else {
- _results.push(void 0);
+ directories[path] = File.entries(path);
}
}
- return _results;
- };
-
- Listener.prototype.log = function(path, options, callback) {
- if (options == null) options = {};
- this.logger.info("" + options.action + "d " + path);
- try {
- return callback.call(this, path, options);
- } catch (error) {
- return this.logger.error(error.message);
- }
- };
+ }
Listener.prototype.changed = function(path, callback) {
var absolutePath, action, base, changed, current, deleted, directories, entries, entry, files, previous, relativePath, timestamp, _i, _len, _results;
@@ -55,7 +37,7 @@
base = this.root;
if (directories[path] && entries.length < directories[path].length) {
directories = this.directories;
- action = "delete";
+ action = "destroy";
deleted = directories[path].filter(function(i) {
return !(entries.indexOf(i) > -1);
});
@@ -95,6 +77,18 @@
return _results;
};
+ Listener.prototype.log = function(path, options, callback) {
+ var name;
+ if (options == null) options = {};
+ name = options.action === "destroy" ? "deleted" : "" + options.action + "d";
+ _console.info("" + name + " " + path);
+ try {
+ return callback.call(this, path, options);
+ } catch (error) {
+ return _console.error(error.message);
+ }
+ };
+
return Listener;
})();
View
21 lib/design.io/listener/mac.js
@@ -1,34 +1,29 @@
(function() {
- var Mac, fs;
+ var Mac, exec, spawn, _ref;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
- fs = require('fs');
+ _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
Mac = (function() {
__extends(Mac, require('../listener'));
- function Mac() {
- Mac.__super__.constructor.apply(this, arguments);
- }
-
- Mac.prototype.listen = function(callback) {
- var command, exec, self, spawn, _ref;
- Mac.__super__.listen.call(this, callback);
+ function Mac(pathfinder, callback) {
+ var command, self;
+ Mac.__super__.constructor.call(this, pathfinder);
self = this;
- _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
command = spawn('ruby', ["" + __dirname + "/mac.rb"]);
command.stdout.setEncoding('utf8');
command.stdout.on('data', function(data) {
return self.changed(data, callback);
});
command.stdout.setEncoding('utf8');
command.stderr.on('data', function(data) {
- return require('../../design.io').logger.error(data.toString().trim());
+ return _console.error(data.toString().trim());
});
command.stdin.write(this.root);
- return command.stdin.end();
- };
+ command.stdin.end();
+ }
return Mac;
View
2 lib/design.io/process.js
@@ -3,6 +3,8 @@
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
+ global._console || (global._console = require('underscore.logger'));
+
command = new (require("" + __dirname + "/command"))(process.argv);
command.run();
View
77 lib/design.io/watcher.js
@@ -1,32 +1,36 @@
(function() {
- var Shift, Watcher, fs, path, request;
+ var Pathfinder, Shift, Watcher, fs, path, request, uuid;
fs = require('fs');
path = require('path');
+ uuid = require('node-uuid');
+
Shift = require('shift');
request = require('request');
+ Pathfinder = require('pathfinder');
+
+ require('underscore.logger');
+
Watcher = (function() {
Watcher.initialize = function(options) {
if (options == null) options = {};
this.directory = options.directory;
+ this.pathfinder = new Pathfinder(this.directory);
this.watchfile = options.watchfile;
this.port = options.port;
this.url = options.url;
- this.logger = require('../design.io').logger;
if (!this.watchfile) throw new Error("You must specify the watchfile");
if (!this.directory) {
throw new Error("You must specify the directory to watch");
}
this.read(function() {
- var listener;
- listener = new (require('./listener/mac'))(options.directory);
- return listener.listen(function(path, options) {
- return Watcher.exec(path, options);
+ return new (require('./listener/mac'))(Watcher.pathfinder.root, function(path, options) {
+ return Watcher.changed(path, options);
});
});
return this;
@@ -98,8 +102,8 @@
}
};
- Watcher.exec = function(path, options) {
- var action, success, timestamp, watcher, watchers, _i, _len, _results;
+ Watcher.changed = function(path, options) {
+ var action, timestamp, watcher, watchers, _i, _len, _results;
if (options == null) options = {};
watchers = this.all();
action = options.action;
@@ -112,9 +116,35 @@
watcher.action = action;
watcher.timestamp = timestamp;
try {
- _results.push(success = !!watcher[action].call(watcher, path, options));
+ _results.push(!!watcher[action](path, options));
} catch (error) {
- _results.push(console.log(error));
+ _results.push(_console.error(error.toString()));
+ }
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Watcher.log = function(data) {
+ var action, timestamp, watcher, watchers, _i, _len, _results;
+ watchers = this.all();
+ path = data.path;
+ action = data.action;
+ timestamp = data.timestamp;
+ _results = [];
+ for (_i = 0, _len = watchers.length; _i < _len; _i++) {
+ watcher = watchers[_i];
+ if (watcher.hasOwnProperty("server") && watcher.server.hasOwnProperty(action) && watcher.id === data.id) {
+ server.watcher = watcher;
+ server.path = path;
+ server.action = action;
+ server.timestamp = timestamp;
+ try {
+ _results.push(!!server[action](data));
+ } catch (error) {
+ _results.push(_console.error(error.toString()));
}
} else {
_results.push(void 0);
@@ -139,17 +169,16 @@
return true;
} else {
if (error) {
- return console.log(error);
+ return _console.error(error.toString());
} else {
- return console.log(response.body);
+ return _console.error(response.body);
}
}
});
};
function Watcher() {
var arg, args, key, methods, value, _i, _len;
- this.logger = this.constructor.logger;
args = Array.prototype.slice.call(arguments, 0, arguments.length);
methods = args.pop();
if (typeof methods === "function") methods = methods.call(this);
@@ -163,6 +192,8 @@
value = methods[key];
this[key] = value;
}
+ this.id || (this.id = uuid());
+ if (this.hasOwnProperty("server")) this.server.watcher = this;
}
Watcher.prototype.create = function(path) {
@@ -180,19 +211,15 @@
});
};
- Watcher.prototype["delete"] = function() {
+ Watcher.prototype.destroy = function() {
return this.broadcast();
};
Watcher.prototype.error = function(error) {
- this.constructor.logger.error(error.hasOwnProperty("message") ? error.message : error.toString());
+ _console.error(error.hasOwnProperty("message") ? error.message : error.toString());
return false;
};
- Watcher.prototype.toId = function(path) {
- return path.replace(process.cwd() + '/', '').replace(/[\/\.]/g, '-');
- };
-
Watcher.prototype.match = function(path) {
var pattern, patterns, _i, _len;
patterns = this.patterns;
@@ -209,7 +236,7 @@
data = args.pop() || {};
data.action || (data.action = this.action);
data.path || (data.path = this.path);
- data.id || (data.id = this.toId(data.path));
+ data.id = this.id;
action = args.shift() || "exec";
return this.constructor.broadcast(action, data);
};
@@ -218,10 +245,11 @@
var action, actions, client, data, _i, _len;
data = {
patterns: this.patterns,
- match: this.match
+ match: this.match,
+ id: this.id
};
if (this.hasOwnProperty("client")) {
- actions = ["create", "update", "delete"];
+ actions = ["create", "update", "destroy", "connect"];
client = this.client;
for (_i = 0, _len = actions.length; _i < _len; _i++) {
action = actions[_i];
@@ -246,11 +274,6 @@
return Watcher.create.apply(Watcher, arguments);
};
- Watchfile.prototype.watcher = function(name, options) {
- if (options == null) options = {};
- return require("design.io-" + name)(options);
- };
-
return Watchfile;
})();
View
20 package.json
@@ -1,6 +1,6 @@
{
"name": "design.io",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "Design and Test Your App in Real-Time from TextMate",
"homepage": "http://github.com/viatropos/design.io",
"main": "lib/design.io.js",
@@ -26,16 +26,16 @@
"engines": { "node": ">= 0.4.0" },
"bin": { "design.io": "./bin/design.io" },
"dependencies": {
- "commander": ">= 0.3.2",
- "socket.io": ">= 0.8.6",
- "request": ">= 2.1.1",
- "pathfinder": ">= 0.1.5",
- "shift": ">= 0.1.8",
- "async": ">= 0.1.15",
- "common-logger": ">= 0.2.0"
+ "commander": ">= 0.3.2",
+ "socket.io": ">= 0.8.6",
+ "request": ">= 2.1.1",
+ "pathfinder": ">= 0.1.6",
+ "shift": ">= 0.1.8",
+ "async": ">= 0.1.15",
+ "node-uuid": ">= 1.2.0",
+ "underscore.logger": ">= 0.3.1"
},
"scripts": {
- "install": "gem install rb-fsevent --no-ri --no-rdoc"
- }
+ "install": "gem install rb-fsevent --no-ri --no-rdoc"
}
}
View
74 spec/app/javascriptExtension.coffee
@@ -0,0 +1,74 @@
+Shift = require 'shift'
+_path = require 'path'
+fs = require 'fs'
+Pathfinder = require 'pathfinder'
+File = Pathfinder.File
+
+# http://darcyclarke.me/development/detect-attribute-changes-with-jquery/
+# https://github.com/jollytoad/jquery.mutation-events
+# http://stackoverflow.com/questions/1029241/javascript-object-watch-for-all-browsers
+# https://github.com/stubbornella/csslint
+#
+# Example
+#
+# require('design.io-stylesheets') /\.(styl|less|sass|scss)$/
+# outputPath: (path) -> "./public/#{path}"
+# lookup: File.directories(process.cwd())
+# compress: false
+# write: (path, string) -> # make your own!
+# File.write(@outputPath(path), string)
+# File.write(File.pathWithDigest(path), string)
+#
+module.exports = ->
+ pathfinder = Watcher.pathfinder
+ args = Array.prototype.slice.call(arguments, 0, arguments.length)
+ options = if typeof args[args.length - 1] == "object" then args.pop() else {}
+ args[0] = /\.(coffee|js)$/ unless args.length > 0
+ args[0] ||= options.patterns if options.hasOwnProperty("patterns")
+
+ outputPath = options.outputPath
+ writeMethod = options.write
+ importPaths = options.paths || []
+
+ if options.hasOwnProperty("compress") && options.compress == true
+ compressor = new Shift.UglifyJS
+
+ write = (path, string) ->
+ if writeMethod
+ writeMethod.call(@, path, string)
+ else if outputPath
+ _outputPath = outputPath.call(@, path)
+ if _outputPath
+ File.write(_outputPath, string)
+
+ touchDependencies = (file) ->
+ dependentPaths = pathfinder.dependsOn(file.absolutePath())
+ if dependentPaths && dependentPaths.length > 0
+ for dependentPath in dependentPaths
+ # touch the file so it loops back through
+ File.touch dependentPath
+
+ Watcher.create args,
+ toSlug: (path) ->
+ path.replace(process.cwd() + '/', '').replace(/[\/\.]/g, '-')
+
+ update: (path) ->
+ self = @
+
+ pathfinder.compile path, (error, string, file) ->
+ return self.error(error) if error
+
+ if compressor
+ compressor.render string, (error, result) ->
+ return self.error(error) if error
+ self.broadcast body: result, slug: self.toSlug(path)
+ write.call(self, path, result)
+ touchDependencies(file)
+ else
+ self.broadcast body: string, slug: self.toSlug(path)
+ write.call(self, path, string)
+ touchDependencies(file)
+
+ client:
+ update: (data) ->
+ eval("(data.body)")
View
2 spec/app/stylesheets/another.styl
@@ -5,4 +5,4 @@
width: 100px
height: 10px
- background: grey
+ background: green
View
4 spec/app/stylesheets/application.styl
@@ -7,4 +7,6 @@
width: 40px
color: red
- background: yellow
+ background: grey
+
+// @import './another.styl'
View
89 spec/stylesheetExtension.coffee
@@ -0,0 +1,89 @@
+Shift = require 'shift'
+_path = require 'path'
+fs = require 'fs'
+Pathfinder = require 'pathfinder'
+File = Pathfinder.File
+
+# http://darcyclarke.me/development/detect-attribute-changes-with-jquery/
+# https://github.com/jollytoad/jquery.mutation-events
+# http://stackoverflow.com/questions/1029241/javascript-object-watch-for-all-browsers
+# https://github.com/stubbornella/csslint
+#
+# Example
+#
+# require('design.io-stylesheets') /\.(styl|less|sass|scss)$/
+# outputPath: (path) -> "./public/#{path}"
+# lookup: File.directories(process.cwd())
+# compress: false
+# write: (path, string) -> # make your own!
+# File.write(@outputPath(path), string)
+# File.write(File.pathWithDigest(path), string)
+#
+module.exports = ->
+ pathfinder = Watcher.pathfinder
+ args = Array.prototype.slice.call(arguments, 0, arguments.length)
+ options = if typeof args[args.length - 1] == "object" then args.pop() else {}
+ args[0] = /\.(styl|less|sass|scss)$/ unless args.length > 0
+ args[0] ||= options.patterns if options.hasOwnProperty("patterns")
+
+ outputPath = options.outputPath
+ writeMethod = options.write
+ importPaths = options.paths || []
+
+ if options.hasOwnProperty("compress") && options.compress == true
+ compressor = new Shift.YuiCompressor
+
+ write = (path, string) ->
+ if writeMethod
+ writeMethod.call(@, path, string)
+ else if outputPath
+ _outputPath = outputPath.call(@, path)
+ if _outputPath
+ File.write(_outputPath, string)
+
+ touchDependencies = (file) ->
+ dependentPaths = pathfinder.dependsOn(file.absolutePath())
+ if dependentPaths && dependentPaths.length > 0
+ for dependentPath in dependentPaths
+ # touch the file so it loops back through
+ File.touch dependentPath
+
+ Watcher.create args,
+ toSlug: (path) ->
+ path.replace(process.cwd() + '/', '').replace(/[\/\.]/g, '-')
+
+ update: (path) ->
+ self = @
+
+ pathfinder.compile path, (error, string, file) ->
+ return self.error(error) if error
+
+ if compressor
+ compressor.render string, (error, result) ->
+ return self.error(error) if error
+ self.broadcast body: result, slug: self.toSlug(path)
+ write.call(self, path, result)
+ touchDependencies(file)
+ else
+ self.broadcast body: string, slug: self.toSlug(path)
+ write.call(self, path, string)
+ touchDependencies(file)
+
+ client:
+ connect: ->
+ @stylesheets = {}
+
+ # this should get better so it knows how to map template files to browser files
+ update: (data) ->
+ @stylesheets[data.slug].remove() if @stylesheets[data.slug]?
+ node = $("<style id='#{data.slug}' type='text/css'>#{data.body}</style>")
+ @stylesheets[data.slug] = node
+ $("body").append(node)
+
+ destroy: (data) ->
+ @stylesheets[data.slug].remove() if @stylesheets[data.slug]?
+
+ server:
+ # so you update the stylesheet from the web inspector or something...
+ # now the file could update
+ update: (data) ->
View
3 src/design.io.coffee
@@ -1,7 +1,8 @@
+require 'underscore.logger'
+
module.exports =
watcher: require './design.io/watcher'
command: require './design.io/command'
connection: require './design.io/connection'
- logger: new (require("common-logger"))(colorized: true)
extension: (name) ->
require("./design.io/extensions/#{name}").apply(@, Array.prototype.slice.call(arguments, 1, arguments.length))
View
27 src/design.io/client.coffee
@@ -2,8 +2,6 @@ class window.DesignIO
constructor: (options) ->
options ||= {}
@callbacks = {}
- @stylesheets = {}
- @javascripts = {}
@watchers = []
@port = options.port || 4181
@url = options.url || "#{window.location.protocol}//#{window.location.hostname}:#{@port}/design.io"
@@ -17,10 +15,9 @@ class window.DesignIO
socket.on 'connect', ->
socket.emit 'userAgent', self.userAgent()
socket.on 'watch', (data) ->
- console.log data
- self.watch JSON.parse(data, self.reviver)
+ self.watch data
socket.on 'exec', (data) ->
- self.exec JSON.parse(data, self.reviver)
+ self.exec data
# on "create"
on: (name, callback) ->
@@ -31,17 +28,31 @@ class window.DesignIO
true
watch: (data) ->
- @watchers = data.body
+ @watchers = watchers = JSON.parse(data, @reviver).body
+ for watcher in watchers
+ watcher.client = @
+ watcher.log = (data) ->
+ data.path ||= @path
+ data.action ||= @action
+ data.timestamp ||= new Date
+ data.id = @id
+ @client.log(data)
+ watcher.connect() if watcher.hasOwnProperty("connect")
exec: (data) ->
+ data = JSON.parse(data, @reviver)
+
watchers = @watchers
for watcher in watchers
- if watcher.match(data.path)
- watcher[data.action].call(@, data) if watcher.hasOwnProperty(data.action)
+ if watcher.id == data.id
+ watcher.path = data.path # tmp set
+ watcher.action = data.action
+ watcher[data.action](data) if watcher.hasOwnProperty(data.action)
@runCallback data.action, data
+ # id, path, then anything else
log: (data) ->
if typeof(data) == "object"
data.userAgent = window.navigator.userAgent
View
5 src/design.io/connection.coffee
@@ -10,15 +10,14 @@ module.exports = (portOrIo) ->
designer = io.of("/design.io")
designer.on "connection", (socket) ->
- console.log 'passed it!'
socket.on "userAgent", (data) ->
socket.set "userAgent", data, ->
socket.emit "ready"
Watcher.connect()
true
- socket.on "log", (msg) ->
- console.log msg
+ socket.on "log", (data) ->
+ Watcher.log(data)
true
socket.on "disconnect", ->
View
29 src/design.io/extensions/javascripts.coffee
@@ -1,29 +0,0 @@
-Shift = require 'shift'
-_path = require 'path'
-fs = require 'fs'
-
-module.exports = ->
- args = Array.prototype.slice.call(arguments, 0, arguments.length)
- options = if typeof args[args.length - 1] == "object" then args.pop() else {}
- args[0] = /\.(coffee|ejs|js)$/ unless args.length > 0
-
- if options.hasOwnProperty("compress") && options.compress == true
- compressor = new Shift.UglifyJS
-
- Watcher.create args,
- update: (path) ->
- self = @
-
- fs.readFile path, 'utf-8', (error, result) ->
- Shift.render path: path, string: result, (error, output) ->
- return self.error(error) if error
- if compressor
- compressor.render output, (error, result) ->
- return self.error(error) if error
- self.broadcast body: result
- else
- self.broadcast body: output
-
- client:
- update: (data) ->
- $("<script id='#{data.id}' type='text/javascript'>#{data.body}</script>")
View
24 src/design.io/extensions/reload.coffee
@@ -1,24 +0,0 @@
-exec = require('child_process').exec
-
-module.exports = ->
- args = Array.prototype.slice.call(arguments, 0, arguments.length)
- options = if typeof args[args.length - 1] == "object" then args.pop() else {}
- args[0] = /.*/ unless args.length > 0
-
- unless options.server
- throw new Error("You must specify the restart command: require('design.io/reload')(command: 'node app.js')")
-
- command = options.command
-
- Watcher.create args,
- update: (path) ->
- exec 'ps aux | grep node', (e, stdout, o) ->
- lines = stdout.toString().split("\n")
- for line in lines
- if line.match(server)
- match = line.match(/[^ ]+ +(\d+)/)
- if match
- pid = match[1]
- exec "kill -2 #{pid}"
- exec command
- console.log "Server restarted... #{command}"
View
40 src/design.io/extensions/stylesheets.coffee
@@ -1,40 +0,0 @@
-Shift = require 'shift'
-_path = require 'path'
-fs = require 'fs'
-
-module.exports = ->
- args = Array.prototype.slice.call(arguments, 0, arguments.length)
- options = if typeof args[args.length - 1] == "object" then args.pop() else {}
- args[0] = /\.(styl|less|css|sass|scss)$/ unless args.length > 0
-
- if options.hasOwnProperty("compress") && options.compress == true
- compressor = new Shift.YuiCompressor
-
- Watcher.create args,
- update: (path) ->
- self = @
-
- fs.readFile path, 'utf-8', (error, result) ->
- Shift.render path: path, string: result, (error, output) ->
- return self.error(error) if error
- if compressor
- compressor.render output, (error, result) ->
- return self.error(error) if error
- self.broadcast body: result
- else
- self.broadcast body: output
- true
-
- client:
- connect: ->
- @stylesheets = {}
-
- # this should get better so it knows how to map template files to browser files
- update: (data) ->
- @stylesheets[data.id].remove() if @stylesheets[data.id]?
- node = $("<style id='#{data.id}' type='text/css'>#{data.body}</style>")
- @stylesheets[data.id] = node
- $("body").append(node)
-
- delete: (data) ->
- @stylesheets[data.id].remove() if @stylesheets[data.id]?
View
19 src/design.io/extensions/templates.coffee
@@ -1,19 +0,0 @@
-Shift = require 'shift'
-_path = require 'path'
-fs = require 'fs'
-
-module.exports = ->
- args = Array.prototype.slice.call(arguments, 0, arguments.length)
- options = if typeof args[args.length - 1] == "object" then args.pop() else {}
- args[0] = /\.(jade|mustache|haml|erb|coffee)$/ unless args.length > 0
-
- Watcher.create args,
- update: (path) ->
- self = @
-
- fs.readFile path, 'utf-8', (error, result) ->
- Shift.render path: path, string: result, (error, output) ->
- return self.error(error) if error
- self.broadcast body: output
-
- true
View
2 src/design.io/extensions/watchfile.coffee
@@ -3,5 +3,5 @@ module.exports = ->
update: ->
Watcher.update()
- delete: ->
+ destroy: ->
Watcher.update()
View
36 src/design.io/listener.coffee
@@ -3,27 +3,19 @@ Pathfinder = require 'pathfinder'
File = Pathfinder.File
class Listener
- constructor: (root, options = {}) ->
+ constructor: (root, callback) ->
@root = root
- @directories = {}
- @files = {}
- @logger = require('../design.io').logger
+ @directories = directories = {}
+ @files = files = {}
+ paths = require('findit').sync(root)
- listen: (callback) ->
- files = @files
- root = @root
- paths = require('findit').sync(root)
for source in paths
- stat = File.stat(source)
+ stat = File.stat(source)
+ path = _path.join(root, source.replace(root, ""))
unless stat.isDirectory()
- files[_path.join(root, source.replace(root, ""))] = stat
-
- log: (path, options = {}, callback) ->
- @logger.info "#{options.action}d #{path}" # #{options.timestamp.toLocaleTimeString()} -
- try
- callback.call(@, path, options)
- catch error
- @logger.error error.message
+ files[path] = stat
+ else
+ directories[path] = File.entries(path)
changed: (path, callback) ->
entries = File.entries(path)
@@ -35,7 +27,7 @@ class Listener
if directories[path] && entries.length < directories[path].length
directories = @directories
- action = "delete"
+ action = "destroy"
deleted = directories[path].filter (i) -> !(entries.indexOf(i) > -1)
directories[path] = entries
relativePath = File.join(path, deleted[0]).replace(base + '/', '')
@@ -70,6 +62,14 @@ class Listener
@log relativePath, action: action, timestamp: timestamp, previous: previous, current: current, callback
+ log: (path, options = {}, callback) ->
+ name = if options.action == "destroy" then "deleted" else "#{options.action}d"
+ _console.info "#{name} #{path}" # #{options.timestamp.toLocaleTimeString()} -
+ try
+ callback.call(@, path, options)
+ catch error
+ _console.error error.message
+
require './listener/mac'
require './listener/polling'
require './listener/windows'
View
25 src/design.io/listener/mac.coffee
@@ -1,34 +1,21 @@
-fs = require 'fs'
+{spawn, exec} = require 'child_process'
# https://github.com/thibaudgg/rb-fsevent
class Mac extends (require('../listener'))
- listen: (callback) ->
- super(callback)
+ constructor: (pathfinder, callback) ->
+ super(pathfinder)
self = @
- {spawn, exec} = require 'child_process'
command = spawn 'ruby', ["#{__dirname}/mac.rb"]
command.stdout.setEncoding('utf8')
command.stdout.on 'data', (data) ->
# console.log(data.toString().trim())
self.changed(data, callback)
command.stdout.setEncoding('utf8')
command.stderr.on 'data', (data) ->
- require('../../design.io').logger.error data.toString().trim()
+ _console.error data.toString().trim()
command.stdin.write @root
command.stdin.end()
- #listen: (callback) ->
- # FSEvents = require("fsevents")
- # self = @
- # global.fse = new FSEvents(@root)
- #
- # fse.on "change", (path, flags, evtid) ->
- # self.changed(path)
- #
- # if fs.statSync("stop")
- # console.log "Stop"
- # fs.unlinkSync "stop"
- # fse.stop()
-
-module.exports = Mac
+
+module.exports = Mac
View
10 src/design.io/listener/mac.rb
@@ -0,0 +1,10 @@
+require 'rb-fsevent'
+
+fsevent = FSEvent.new
+STDOUT.sync = true
+io = STDOUT
+directory = STDIN.read
+fsevent.watch directory do |directories|
+ io.write directories[0][0..-2]
+end
+fsevent.run
View
8 src/design.io/process.coffee
@@ -1,5 +1,7 @@
{spawn, exec} = require 'child_process'
+global._console ||= require('underscore.logger')
+
command = new (require("#{__dirname}/command"))(process.argv)
command.run()
@@ -9,5 +11,7 @@ server = spawn "node", [
"--directory", command.program.directory,
"--port", command.program.port
]
-server.stdout.on 'data', (data) -> console.log data.toString().trim()
-server.stderr.on 'data', (data) -> console.log data.toString().trim()
+server.stdout.on 'data', (data) ->
+ console.log data.toString().trim()
+server.stderr.on 'data', (data) ->
+ console.log data.toString().trim()
View
67 src/design.io/watcher.coffee
@@ -1,23 +1,25 @@
-fs = require 'fs'
-path = require 'path'
-Shift = require 'shift'
-request = require 'request'
+fs = require 'fs'
+path = require 'path'
+uuid = require 'node-uuid'
+Shift = require 'shift'
+request = require 'request'
+Pathfinder = require 'pathfinder'
+require 'underscore.logger'
class Watcher
@initialize: (options = {}) ->
@directory = options.directory
+ @pathfinder = new Pathfinder(@directory)
@watchfile = options.watchfile
@port = options.port
@url = options.url
- @logger = require('../design.io').logger
throw new Error("You must specify the watchfile") unless @watchfile
throw new Error("You must specify the directory to watch") unless @directory
@read ->
- listener = new (require('./listener/mac'))(options.directory)
- listener.listen (path, options) ->
- Watcher.exec(path, options)
+ new (require('./listener/mac')) Watcher.pathfinder.root, (path, options) ->
+ Watcher.changed(path, options)
@
@@ -81,7 +83,7 @@ class Watcher
else
value
- @exec: (path, options = {}) ->
+ @changed: (path, options = {}) ->
watchers = @all()
action = options.action
timestamp = options.timestamp
@@ -93,15 +95,34 @@ class Watcher
watcher.timestamp = timestamp
try
- success = !!watcher[action].call(watcher, path, options)
+ !!watcher[action](path, options)
catch error
- console.log error
+ _console.error error.toString()
# make async
# delete watcher.path
# delete watcher.action
# delete watcher.timestamp
#break unless success
+
+ @log: (data) ->
+ watchers = @all()
+ path = data.path
+ action = data.action
+ timestamp = data.timestamp
+
+ for watcher in watchers
+ if watcher.hasOwnProperty("server") &&
+ watcher.server.hasOwnProperty(action) &&
+ watcher.id == data.id
+ server.watcher = watcher
+ server.path = path
+ server.action = action
+ server.timestamp = timestamp
+ try
+ !!server[action](data)
+ catch error
+ _console.error error.toString()
@broadcast: (action, data) ->
replacer = @replacer
@@ -118,12 +139,11 @@ class Watcher
true
else
if error
- console.log error
+ _console.error error.toString()
else
- console.log response.body
+ _console.error response.body
constructor: ->
- @logger = @constructor.logger
args = Array.prototype.slice.call(arguments, 0, arguments.length)
methods = args.pop()
methods = methods.call(@) if typeof methods == "function"
@@ -132,6 +152,9 @@ class Watcher
for arg in args
@patterns.push if typeof arg == "string" then new RegExp(arg) else arg
@[key] = value for key, value of methods
+
+ @id ||= uuid()
+ @server.watcher = @ if @hasOwnProperty("server")
# Example:
#
@@ -147,16 +170,13 @@ class Watcher
return self.error(error) if error
self.broadcast body: result
- delete: ->
+ destroy: ->
@broadcast()
error: (error) ->
- @constructor.logger.error if error.hasOwnProperty("message") then error.message else error.toString()
+ _console.error if error.hasOwnProperty("message") then error.message else error.toString()
false
- toId: (path) ->
- path.replace(process.cwd() + '/', '').replace(/[\/\.]/g, '-')
-
match: (path) ->
patterns = @patterns
for pattern in patterns
@@ -169,7 +189,7 @@ class Watcher
data = args.pop() || {}
data.action ||= @action
data.path ||= @path
- data.id ||= @toId(data.path)
+ data.id = @id
action = args.shift() || "exec"
@constructor.broadcast action, data
@@ -178,9 +198,10 @@ class Watcher
data =
patterns: @patterns
match: @match
+ id: @id
if @hasOwnProperty("client")
- actions = ["create", "update", "delete"]
+ actions = ["create", "update", "destroy", "connect"]
client = @client
for action in actions
data[action] = client[action] if client.hasOwnProperty(action)
@@ -196,9 +217,5 @@ class Watcher
watch: ->
Watcher.create(arguments...)
-
- # for plugins, like Guard, TODO
- watcher: (name, options = {}) ->
- require("design.io-#{name}")(options)
module.exports = Watcher

0 comments on commit ed0068b

Please sign in to comment.