UNPKG

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