giles
Version:
a next-gen language watcher/compiler for pre-processed languages
434 lines (399 loc) • 12.4 kB
JavaScript
// Generated by CoffeeScript 1.6.1
var Giles, coffee, connect, fs, giles, iced, jade, log, markdown, path, stylus, _ref,
_this = this;
fs = require('fs');
path = require('path');
log = require('./log');
connect = require('connect');
Giles = (function() {
function Giles() {
var _this = this;
this.connect = function(dir) {
return Giles.prototype.connect.apply(_this, arguments);
};
this.compilerMap = {};
this.reverseCompilerMap = {};
this.ignored = [];
this.locals = {};
this.routes = {};
}
Giles.prototype.extendLocals = function(dynLocals) {
var key, locals, value, _ref;
locals = {};
_ref = this.locals;
for (key in _ref) {
value = _ref[key];
locals[key] = value;
}
for (key in dynLocals) {
value = dynLocals[key];
locals[key] = value;
}
return locals;
};
Giles.prototype.connect = function(dir) {
var _this = this;
return function(req, res, next) {
var args, dynLocals, file, fullFilePath, locals, route, _ref, _ref1;
_ref = req.url.split('?'), route = _ref[0], args = _ref[1];
locals = _this.locals;
if (route === '/') {
route = '/index.html';
}
if (_this.routes[route]) {
file = _this.routes[route].source;
dynLocals = _this.routes[route].locals;
if (dynLocals) {
locals = _this.extendLocals(dynLocals);
}
} else {
_ref1 = (dir + route).split("?"), fullFilePath = _ref1[0], args = _ref1[1];
file = _this.reverseLookup(fullFilePath);
}
if (file) {
return _this.compileFile(file, locals, {}, function(result) {
var extname, relInput, relOutput;
relInput = path.relative(process.cwd(), file);
relOutput = path.relative(process.cwd(), result.outputFile);
if (result.exists) {
log.notice("up to date " + relOutput + " from " + relInput);
} else {
log.notice("compiled " + relInput + " into " + relOutput);
log.encourage();
}
extname = path.extname(relOutput);
if (extname === '.css') {
res.setHeader("Content-Type", "text/css");
} else if (extname === '.html') {
res.setHeader("Content-Type", "text/html");
} else if (extname === '.js') {
res.setHeader("Content-Type", "application/javascript");
}
return res.end(result.content);
});
} else {
return next();
}
};
};
Giles.prototype.quiet = function() {
return log.quiet(true);
};
Giles.prototype.get = function(endpoint, source, locals) {
return this.routes[endpoint] = {
source: source,
locals: locals
};
};
Giles.prototype.server = function(dir, opts) {
var port;
port = opts['port'] || 2255;
this.app = connect().use(this.connect(dir)).use(connect["static"](dir)).listen(port);
return log.notice("Giles is watching on port " + port);
};
Giles.prototype.reverseLookup = function(file) {
var ext, extension, foundFile, name, numberFound, pwd, relativeName, _i, _len, _ref, _ref1;
_ref = this.parseFileName(file), name = _ref[0], ext = _ref[1];
pwd = process.cwd();
relativeName = path.relative(pwd, name);
numberFound = 0;
foundFile = null;
if (this.reverseCompilerMap[ext]) {
_ref1 = this.reverseCompilerMap[ext];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
extension = _ref1[_i];
if (fs.existsSync(name + extension)) {
foundFile = name + extension;
numberFound += 1;
}
}
}
if (numberFound > 1) {
throw "You can only have one file that can compile into " + file + " - you have " + numberFound + " - " + this.reverseCompilerMap[ext];
}
return foundFile;
};
Giles.prototype.crawl = function(dir, onFile) {
var handlePath,
_this = this;
handlePath = function(resource) {
return function(err, stats) {
if (err) {
return log.error(err);
} else if (stats.isFile()) {
return onFile(resource);
} else if (stats.isDirectory()) {
return _this.crawl(resource, onFile);
} else {
log.error("Could not determine file " + filename);
return log.error(stats);
}
};
};
return fs.readdir(dir, function(err, files) {
var file, resource, _i, _len, _results;
if (err) {
log.error("cannot read dir");
return log.error(err);
} else {
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
resource = dir + '/' + file;
_results.push(fs.stat(resource, handlePath(resource)));
}
return _results;
}
});
};
Giles.prototype.addCompiler = function(extensions, target, callback) {
var compiler, ext, _i, _j, _len, _len1, _results;
compiler = {
callback: callback,
extension: target
};
if (typeof extensions === 'object') {
for (_i = 0, _len = extensions.length; _i < _len; _i++) {
ext = extensions[_i];
this.compilerMap[ext] = compiler;
}
if (!this.reverseCompilerMap[target]) {
this.reverseCompilerMap[target] = [];
}
_results = [];
for (_j = 0, _len1 = extensions.length; _j < _len1; _j++) {
ext = extensions[_j];
_results.push(this.reverseCompilerMap[target].push(ext));
}
return _results;
} else {
this.compilerMap[extensions] = compiler;
if (!this.reverseCompilerMap[target]) {
this.reverseCompilerMap[target] = [];
}
return this.reverseCompilerMap[target].push(extensions);
}
};
Giles.prototype.process = function(dir, onFile) {
var stats;
stats = fs.statSync(dir);
if (stats.isDirectory()) {
return this.crawl(dir, onFile);
} else if (stats.isFile()) {
return onFile(dir);
} else {
return log.error(dir + " is not a directory or file");
}
};
Giles.prototype.rmFile = function(file) {
return fs.unlink(file, function(err) {
if (err) {
return log.error("Failed to remove: " + file, err);
}
});
};
Giles.prototype.clean = function(dir, opts) {
var fullPath, route, source, _ref,
_this = this;
_ref = this.routes;
for (route in _ref) {
opts = _ref[route];
source = opts.source;
fullPath = path.resolve(process.cwd() + route);
log.notice("cleaning " + fullPath);
this.rmFile(fullPath);
}
return this.process(dir, function(f) {
var sourceFile;
sourceFile = _this.reverseLookup(f);
if (fs.existsSync(sourceFile)) {
log.notice("Cleaning: " + f);
return _this.rmFile(f);
}
});
};
Giles.prototype.build = function(dir, opts) {
var fullPath, locals, route, source, _ref,
_this = this;
_ref = this.routes;
for (route in _ref) {
opts = _ref[route];
source = opts.source;
locals = this.locals;
if (opts.locals) {
locals = this.extendLocals(opts.locals);
}
log.notice("building user-defined route " + route);
fullPath = path.resolve(process.cwd() + route);
this.compile(source, locals, {
outputFile: fullPath
});
}
return this.process(dir, function(f) {
return _this.compile(f);
});
};
Giles.prototype.ignore = function(types) {
return this.ignored = types;
};
Giles.prototype.compile = function(file, locals, options) {
var result,
_this = this;
if (this.isIgnored(file)) {
return;
}
locals = locals || this.locals;
return result = this.compileFile(file, locals, options, function(result) {
var relInput, relOutput;
relInput = path.relative(process.cwd(), file);
relOutput = path.relative(process.cwd(), result.outputFile);
if (result.exists) {
log.notice("up to date " + relOutput + " from " + relInput);
} else {
log.notice("compiled " + relInput + " into " + relOutput);
log.encourage();
}
if (!result) {
return;
}
return fs.writeFileSync(result.outputFile, result.content, 'utf8');
});
};
Giles.prototype.compileFile = function(file, locals, options, cb) {
var compiler, content, cwd, ext, outputContent, outputFile, prefix, _ref;
_ref = this.parseFileName(file), prefix = _ref[0], ext = _ref[1];
compiler = this.compilerMap[ext];
if (!compiler) {
return;
}
if (!fs.existsSync(file)) {
console.error("Could not find source file " + file);
return;
}
outputFile = prefix + compiler.extension;
if (options != null ? options.outputFile : void 0) {
outputFile = options.outputFile;
}
content = fs.readFileSync(file, 'utf8');
outputContent = null;
if (fs.existsSync(outputFile)) {
outputContent = fs.readFileSync(outputFile, 'utf8');
}
cwd = process.cwd();
try {
return compiler.callback(content, file, locals, function(output) {
if (output === outputContent) {
return cb({
content: outputContent,
outputFile: outputFile,
inputFile: file,
originalContent: content,
exists: true
});
} else {
return cb({
outputFile: outputFile,
content: output,
inputFile: file,
originalContent: content
});
}
});
} catch (error) {
log.error(error);
log.error("stack trace:");
return log.error(error.stack.replace(cwd, "."));
}
};
Giles.prototype.parseFileName = function(file) {
var base, ext;
ext = path.extname(file);
base = file.substr(0, file.length - ext.length);
return [base, ext];
};
Giles.prototype.isIgnored = function(name) {
var filename, ignore, _i, _len, _ref;
filename = name.split('/');
filename = filename[filename.length - 1];
if (/^_/.test(filename)) {
return true;
}
_ref = this.ignored;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
ignore = _ref[_i];
if (ignore.test(name)) {
return true;
}
}
return false;
};
return Giles;
})();
_ref = [], stylus = _ref[0], coffee = _ref[1], iced = _ref[2], jade = _ref[3], markdown = _ref[4];
giles = new Giles();
giles.addCompiler([".styl", ".stylus"], '.css', function(contents, filename, options, output) {
var key, styl, val, _i, _len;
if (!stylus) {
stylus = require('stylus');
}
styl = stylus(contents);
styl.set('filename', filename);
styl.include(options.cwd);
for (val = _i = 0, _len = options.length; _i < _len; val = ++_i) {
key = options[val];
styl.define(key, val);
}
return stylus.render(contents, {
filename: filename
}, function(err, css) {
if (err) {
log.error("Could not render stylus file: " + filename);
return log.error(err);
} else {
return output(css);
}
});
});
giles.addCompiler(['.coffee', '.cs'], '.js', function(contents, filename, options, output) {
if (!coffee) {
coffee = require('coffee-script');
}
options.header = true;
options.bare = false;
if (options.scope) {
delete options.scope;
}
return output(coffee.compile(contents, options));
});
giles.addCompiler('.iced', '.js', function(contents, filename, options, output) {
var iced_output;
if (!iced) {
iced = require('iced-coffee-script');
}
iced_output = iced.compile(contents, options);
return output(iced_output);
});
giles.addCompiler('.jade', '.html', function(contents, filename, options, output) {
var compileOpts, compiled;
if (!jade) {
jade = require('jade');
}
compileOpts = {};
compileOpts.filename = filename;
compileOpts.pretty = true;
try {
compiled = jade.compile(contents, compileOpts)(options);
return output(compiled);
} catch (e) {
output("<h1>Error compiling " + filename + "</h1><code><pre>" + e.message + e.stack + "</pre></code>");
throw e;
}
});
giles.addCompiler('.md', '.html', function(contents, filename, options, output) {
var html;
markdown = require("markdown-js");
html = markdown.encode(contents);
return output(html);
});
giles.ignore([/node_modules/, /.git/]);
module.exports = giles;