#!/usr/bin/env node /** * Module dependencies. */ var fs = require('fs') , program = require('commander') , path = require('path') , basename = path.basename , dirname = path.dirname , resolve = path.resolve , exists = fs.existsSync || path.existsSync , join = path.join , monocle = require('monocle')() , mkdirp = require('mkdirp') , jade = require('../'); // jade options var options = {}; // options program .version(require('../package.json').version) .usage('[options] [dir|file ...]') .option('-O, --obj ', 'javascript options object') .option('-o, --out ', 'output the compiled html to ') .option('-p, --path ', 'filename used to resolve includes') .option('-P, --pretty', 'compile pretty html output') .option('-c, --client', 'compile function for client-side runtime.js') .option('-n, --name ', 'The name of the compiled template (requires --client)') .option('-D, --no-debug', 'compile without debugging (smaller functions)') .option('-w, --watch', 'watch files for changes and automatically re-render') .option('--name-after-file', 'Name the template after the last section of the file path (requires --client and overriden by --name)') program.on('--help', function(){ console.log(' Examples:'); console.log(''); console.log(' # translate jade the templates dir'); console.log(' $ jade templates'); console.log(''); console.log(' # create {foo,bar}.html'); console.log(' $ jade {foo,bar}.jade'); console.log(''); console.log(' # jade over stdio'); console.log(' $ jade < my.jade > my.html'); console.log(''); console.log(' # jade over stdio'); console.log(' $ echo \'h1 Jade!\' | jade'); console.log(''); console.log(' # foo, bar dirs rendering to /tmp'); console.log(' $ jade foo bar --out /tmp '); console.log(''); }); program.parse(process.argv); // options given, parse them if (program.obj) { if (exists(program.obj)) { options = JSON.parse(fs.readFileSync(program.obj)); } else { options = eval('(' + program.obj + ')'); } } // --filename if (program.path) options.filename = program.path; // --no-debug options.compileDebug = program.debug; // --client options.client = program.client; // --pretty options.pretty = program.pretty; // --watch options.watch = program.watch; // --name options.name = program.name; // left-over args are file paths var files = program.args; // compile files if (files.length) { console.log(); if (options.watch) { // keep watching when error occured. process.on('uncaughtException', function(err) { console.error(err); }); files.forEach(renderFile); monocle.watchFiles({ files: files, listener: function(file) { renderFile(file.absolutePath); } }); } else { files.forEach(renderFile); } process.on('exit', function () { console.log(); }); // stdio } else { stdin(); } /** * Compile from stdin. */ function stdin() { var buf = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', function(chunk){ buf += chunk; }); process.stdin.on('end', function(){ var output; if (options.client) { output = jade.compileClient(buf, options); } else { var fn = jade.compile(buf, options); var output = fn(options); } process.stdout.write(output); }).resume(); process.on('SIGINT', function() { process.stdout.write('\n'); process.stdin.emit('end'); process.stdout.write('\n'); process.exit(); }) } /** * Process the given path, compiling the jade files found. * Always walk the subdirectories. */ function renderFile(path) { var re = /\.jade$/; fs.lstat(path, function(err, stat) { if (err) throw err; // Found jade file if (stat.isFile() && re.test(path)) { fs.readFile(path, 'utf8', function(err, str){ if (err) throw err; options.filename = path; if (program.nameAfterFile) { options.name = getNameFromFileName(path); } var fn = options.client ? jade.compileClient(str, options) : jade.compile(str, options); var extname = options.client ? '.js' : '.html'; path = path.replace(re, extname); if (program.out) path = join(program.out, basename(path)); var dir = resolve(dirname(path)); mkdirp(dir, 0755, function(err){ if (err) throw err; try { var output = options.client ? fn : fn(options); fs.writeFile(path, output, function(err){ if (err) throw err; console.log(' \033[90mrendered \033[36m%s\033[0m', path); }); } catch (e) { if (options.watch) { console.error(e.stack || e.message || e); } else { throw e } } }); }); // Found directory } else if (stat.isDirectory()) { fs.readdir(path, function(err, files) { if (err) throw err; files.map(function(filename) { return path + '/' + filename; }).forEach(renderFile); }); } }); } /** * Get a sensible name for a template function from a file path * * @param {String} filename * @returns {String} */ function getNameFromFileName(filename) { var file = path.basename(filename, '.jade'); return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) { return character.toUpperCase(); }) + 'Template'; }