UNPKG

5.69 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3/**
4 * Module dependencies.
5 */
6
7var fs = require('fs')
8 , program = require('commander')
9 , path = require('path')
10 , basename = path.basename
11 , dirname = path.dirname
12 , resolve = path.resolve
13 , exists = fs.existsSync || path.existsSync
14 , join = path.join
15 , monocle = require('monocle')()
16 , mkdirp = require('mkdirp')
17 , jade = require('../');
18
19// jade options
20
21var options = {};
22
23// options
24
25program
26 .version(require('../package.json').version)
27 .usage('[options] [dir|file ...]')
28 .option('-O, --obj <str>', 'javascript options object')
29 .option('-o, --out <dir>', 'output the compiled html to <dir>')
30 .option('-p, --path <path>', 'filename used to resolve includes')
31 .option('-P, --pretty', 'compile pretty html output')
32 .option('-c, --client', 'compile function for client-side runtime.js')
33 .option('-n, --name <str>', 'The name of the compiled template (requires --client)')
34 .option('-D, --no-debug', 'compile without debugging (smaller functions)')
35 .option('-w, --watch', 'watch files for changes and automatically re-render')
36 .option('--name-after-file', 'Name the template after the last section of the file path (requires --client and overriden by --name)')
37 .option('--doctype <str>', 'Specify the doctype on the command line (useful if it is not specified by the template)')
38
39
40program.on('--help', function(){
41 console.log(' Examples:');
42 console.log('');
43 console.log(' # translate jade the templates dir');
44 console.log(' $ jade templates');
45 console.log('');
46 console.log(' # create {foo,bar}.html');
47 console.log(' $ jade {foo,bar}.jade');
48 console.log('');
49 console.log(' # jade over stdio');
50 console.log(' $ jade < my.jade > my.html');
51 console.log('');
52 console.log(' # jade over stdio');
53 console.log(' $ echo \'h1 Jade!\' | jade');
54 console.log('');
55 console.log(' # foo, bar dirs rendering to /tmp');
56 console.log(' $ jade foo bar --out /tmp ');
57 console.log('');
58});
59
60program.parse(process.argv);
61
62// options given, parse them
63
64if (program.obj) {
65 if (exists(program.obj)) {
66 options = JSON.parse(fs.readFileSync(program.obj));
67 } else {
68 options = eval('(' + program.obj + ')');
69 }
70}
71
72// --filename
73
74if (program.path) options.filename = program.path;
75
76// --no-debug
77
78options.compileDebug = program.debug;
79
80// --client
81
82options.client = program.client;
83
84// --pretty
85
86options.pretty = program.pretty;
87
88// --watch
89
90options.watch = program.watch;
91
92// --name
93
94options.name = program.name;
95
96// --doctype
97
98options.doctype = program.doctype;
99
100// left-over args are file paths
101
102var files = program.args;
103
104// compile files
105
106if (files.length) {
107 console.log();
108 if (options.watch) {
109 // keep watching when error occured.
110 process.on('uncaughtException', function(err) {
111 console.error(err);
112 });
113 files.forEach(renderFile);
114 monocle.watchFiles({
115 files: files,
116 listener: function(file) {
117 renderFile(file.absolutePath);
118 }
119 });
120 } else {
121 files.forEach(renderFile);
122 }
123 process.on('exit', function () {
124 console.log();
125 });
126// stdio
127} else {
128 stdin();
129}
130
131/**
132 * Compile from stdin.
133 */
134
135function stdin() {
136 var buf = '';
137 process.stdin.setEncoding('utf8');
138 process.stdin.on('data', function(chunk){ buf += chunk; });
139 process.stdin.on('end', function(){
140 var output;
141 if (options.client) {
142 output = jade.compileClient(buf, options);
143 } else {
144 var fn = jade.compile(buf, options);
145 var output = fn(options);
146 }
147 process.stdout.write(output);
148 }).resume();
149
150 process.on('SIGINT', function() {
151 process.stdout.write('\n');
152 process.stdin.emit('end');
153 process.stdout.write('\n');
154 process.exit();
155 })
156}
157
158/**
159 * Process the given path, compiling the jade files found.
160 * Always walk the subdirectories.
161 */
162
163function renderFile(path) {
164 var re = /\.jade$/;
165 fs.lstat(path, function(err, stat) {
166 if (err) throw err;
167 // Found jade file
168 if (stat.isFile() && re.test(path)) {
169 fs.readFile(path, 'utf8', function(err, str){
170 if (err) throw err;
171 options.filename = path;
172 if (program.nameAfterFile) {
173 options.name = getNameFromFileName(path);
174 }
175 var fn = options.client ? jade.compileClient(str, options) : jade.compile(str, options);
176 var extname = options.client ? '.js' : '.html';
177 path = path.replace(re, extname);
178 if (program.out) path = join(program.out, basename(path));
179 var dir = resolve(dirname(path));
180 mkdirp(dir, 0755, function(err){
181 if (err) throw err;
182 try {
183 var output = options.client ? fn : fn(options);
184 fs.writeFile(path, output, function(err){
185 if (err) throw err;
186 console.log(' \033[90mrendered \033[36m%s\033[0m', path);
187 });
188 } catch (e) {
189 if (options.watch) {
190 console.error(e.stack || e.message || e);
191 } else {
192 throw e
193 }
194 }
195 });
196 });
197 // Found directory
198 } else if (stat.isDirectory()) {
199 fs.readdir(path, function(err, files) {
200 if (err) throw err;
201 files.map(function(filename) {
202 return path + '/' + filename;
203 }).forEach(renderFile);
204 });
205 }
206 });
207}
208
209/**
210 * Get a sensible name for a template function from a file path
211 *
212 * @param {String} filename
213 * @returns {String}
214 */
215function getNameFromFileName(filename) {
216 var file = path.basename(filename, '.jade');
217 return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
218 return character.toUpperCase();
219 }) + 'Template';
220}