Skip to content

Commit afc28fb

Browse files
committed
--watch and --serve flags
These new flags replace the functionality of dev-documentation
1 parent e4cb2ff commit afc28fb

File tree

10 files changed

+442
-18
lines changed

10 files changed

+442
-18
lines changed

Diff for: bin/documentation.js

+54-14
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,32 @@
55
'use strict';
66

77
var documentation = require('../'),
8+
chokidar = require('chokidar'),
9+
debounce = require('debounce'),
810
streamArray = require('stream-array'),
911
fs = require('fs'),
1012
vfs = require('vinyl-fs'),
13+
errorPage = require('../lib/error_page'),
1114
lint = require('../lib/lint'),
12-
args = require('../lib/args.js');
15+
Server = require('../lib/server'),
16+
args = require('../lib/args');
1317

1418
var parsedArgs = args(process.argv.slice(2)),
15-
formatterOptions = parsedArgs.formatterOptions,
16-
outputLocation = parsedArgs.output,
17-
formatter = documentation.formats[parsedArgs.formatter];
19+
servingHTML = parsedArgs.serve && parsedArgs.formatter === 'html';
1820

19-
documentation(parsedArgs.inputs, parsedArgs.options, function (err, comments) {
21+
var generator = documentation.bind(null,
22+
parsedArgs.inputs, parsedArgs.options, onDocumented.bind(null, parsedArgs));
23+
24+
var server = new Server();
25+
server.on('listening', function () {
26+
process.stdout.write('documentation.js serving on port 4001\n');
27+
});
28+
29+
function onDocumented(parsedArgs, err, comments) {
2030
if (err) {
31+
if (servingHTML) {
32+
return server.setFiles([errorPage(err)]).start();
33+
}
2134
throw err;
2235
}
2336

@@ -31,15 +44,42 @@ documentation(parsedArgs.inputs, parsedArgs.options, function (err, comments) {
3144
}
3245
}
3346

34-
formatter(comments, formatterOptions, function (err, output) {
35-
if (outputLocation !== 'stdout') {
36-
if (parsedArgs.formatter === 'html') {
37-
streamArray(output).pipe(vfs.dest(outputLocation));
38-
} else {
39-
fs.writeFileSync(outputLocation, output);
40-
}
47+
documentation.formats[parsedArgs.formatter](
48+
comments, parsedArgs.formatterOptions,
49+
onFormatted.bind(null, parsedArgs));
50+
}
51+
52+
function onFormatted(parsedArgs, err, output) {
53+
var destination = parsedArgs.output;
54+
if (destination !== 'stdout') {
55+
if (parsedArgs.formatter === 'html') {
56+
streamArray(output)
57+
.pipe(vfs.dest(destination));
4158
} else {
42-
process.stdout.write(output);
59+
fs.writeFileSync(destination, output);
4360
}
61+
} else if (parsedArgs.formatter !== 'html') {
62+
process.stdout.write(output);
63+
}
64+
if (parsedArgs.formatter === 'html' && parsedArgs.serve) {
65+
server.setFiles(output).start();
66+
}
67+
if (parsedArgs.watch) {
68+
updateWatcher();
69+
}
70+
}
71+
72+
if (parsedArgs.watch) {
73+
var watcher = chokidar.watch(parsedArgs.inputs);
74+
watcher.on('all', debounce(generator, 300));
75+
} else {
76+
generator();
77+
}
78+
79+
function updateWatcher() {
80+
documentation.expandInputs(parsedArgs.inputs, parsedArgs.options, function (err, files) {
81+
watcher.add(files.map(function (data) {
82+
return data.file;
83+
}));
4484
});
45-
});
85+
}

Diff for: index.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ function noop(comment) {
4545
return comment;
4646
}
4747

48+
/**
49+
* Given an array of indexes and options for whether to resolve shallow
50+
* or deep dependencies, resolve dependencies.
51+
*
52+
* @param {Array<string>|string} indexes files to process
53+
* @param {Object} options options
54+
* @param {Function} callback called with results
55+
* @returns {undefined}
56+
*/
57+
function expandInputs(indexes, options, callback) {
58+
var inputFn = (options.polyglot || options.shallow) ? shallow : dependency;
59+
inputFn(indexes, options, callback);
60+
}
61+
4862
/**
4963
* Generate JavaScript documentation as a list of parsed JSDoc
5064
* comments, given a root file as a path.
@@ -75,10 +89,9 @@ module.exports = function (indexes, options, callback) {
7589
indexes = [indexes];
7690
}
7791

78-
var inputFn = (options.polyglot || options.shallow) ? shallow : dependency;
7992
var parseFn = (options.polyglot) ? polyglot : parseJavaScript;
8093

81-
return inputFn(indexes, options, function (error, inputs) {
94+
return expandInputs(indexes, options, function (error, inputs) {
8295
if (error) {
8396
return callback(error);
8497
}
@@ -111,6 +124,8 @@ module.exports = function (indexes, options, callback) {
111124
});
112125
};
113126

127+
module.exports.expandInputs = expandInputs;
128+
114129
module.exports.formats = {
115130
html: require('./lib/output/html'),
116131
md: require('./lib/output/markdown'),

Diff for: lib/args.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ function parse(args) {
1919
type: 'boolean'
2020
})
2121

22+
.option('w', {
23+
describe: 'watch input files and rebuild documentation when they change',
24+
alias: 'watch',
25+
type: 'boolean'
26+
})
27+
28+
.option('s', {
29+
describe: 'serve html files under a webserver. must be used alongide -w',
30+
alias: 'serve',
31+
type: 'boolean'
32+
})
33+
2234
.describe('t', 'specify a theme: this must be a valid theme module')
2335
.alias('t', 'theme')
2436

@@ -97,7 +109,12 @@ module.exports = function (args) {
97109
}
98110
}
99111

100-
if (argv.f === 'html' && argv.o === 'stdout') {
112+
if (argv.w && argv.lint) {
113+
yargs.showHelp();
114+
throw new Error('--lint cannot be specified along with --watch');
115+
}
116+
117+
if (argv.f === 'html' && argv.o === 'stdout' && !argv.serve) {
101118
yargs.showHelp();
102119
throw new Error('The HTML output mode requires a destination directory set with -o');
103120
}
@@ -120,6 +137,8 @@ module.exports = function (args) {
120137
shallow: argv.shallow
121138
},
122139
formatter: argv.f,
140+
watch: argv.w,
141+
serve: argv.s,
123142
formatterOptions: {
124143
name: name,
125144
version: version,

Diff for: lib/error_page.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
var File = require('vinyl');
2+
var ansiHTML = require('ansi-html');
3+
4+
var template = '<head><style>' +
5+
'body{padding:20px;font:16px monospace;background:#CC0000;color:#fff;}' +
6+
'pre{background:#fff}' +
7+
'</style></head>';
8+
9+
/**
10+
* Given an error, generate an HTML page that represents the error.
11+
* @param {Error} error parse or generation error
12+
* @returns {Object} vinyl file object
13+
*/
14+
function errorPage(error) {
15+
var errorText = error.toString();
16+
if (error.codeFrame) {
17+
errorText += '<pre>' + ansiHTML(error.codeFrame) + '</pre>';
18+
}
19+
return new File({
20+
path: 'index.html',
21+
contents: new Buffer(template + errorText)
22+
});
23+
}
24+
25+
module.exports = errorPage;

Diff for: lib/server.js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
var http = require('http'),
2+
mime = require('mime'),
3+
util = require('util'),
4+
EventEmitter = require('events').EventEmitter,
5+
liveReload = require('tiny-lr');
6+
7+
/**
8+
* A static file server designed to support documentation.js's --serve
9+
* option. It serves from an array of Vinyl File objects (virtual files in
10+
* memory) and exposes a `setFiles` method that both replaces the set
11+
* of files and notifies any browsers using LiveReload to reload
12+
* and display the new content.
13+
* @class
14+
*/
15+
function Server() {
16+
this._files = [];
17+
}
18+
19+
util.inherits(Server, EventEmitter);
20+
21+
/**
22+
* Update the set of files exposed by this server and notify LiveReload
23+
* clients
24+
*
25+
* @param {Array<File>} files new content. replaces any previously-set content.
26+
* @returns {Server} self
27+
*/
28+
Server.prototype.setFiles = function (files) {
29+
this._files = files;
30+
if (this._lr) {
31+
this._lr.changed({ body: { files: '*' } });
32+
}
33+
return this;
34+
};
35+
36+
Server.prototype.handler = function (request, response) {
37+
var path = request.url.substring(1);
38+
if (path === '') {
39+
path = 'index.html';
40+
}
41+
for (var i = 0; i < this._files.length; i++) {
42+
if (this._files[i].relative === path) {
43+
response.writeHead(200, { 'Content-Type': mime.lookup(path) });
44+
this._files[i].pipe(response);
45+
return;
46+
}
47+
}
48+
response.writeHead(404, { 'Content-Type': 'text/plain' });
49+
response.end('Not found');
50+
};
51+
52+
/**
53+
* Boot up the server's HTTP & LiveReload endpoints. This method
54+
* can be called multiple times.
55+
*
56+
* @param {Function} [callback=] called when server is started
57+
* @returns {undefined}
58+
*/
59+
Server.prototype.start = function (callback) {
60+
61+
callback = callback || noop;
62+
63+
// idempotent
64+
if (this._lr) {
65+
return callback();
66+
}
67+
68+
this._lr = liveReload();
69+
this._http = http.createServer(this.handler.bind(this));
70+
71+
this._lr.listen(35729, function () {
72+
this._http.listen(4001, function () {
73+
this.emit('listening');
74+
callback();
75+
}.bind(this));
76+
}.bind(this));
77+
};
78+
79+
/**
80+
* Shut down the server's HTTP & LiveReload endpoints. This method
81+
* can be called multiple times.
82+
*
83+
* @param {Function} [callback=] called when server is closed
84+
* @returns {undefined}
85+
*/
86+
Server.prototype.stop = function (callback) {
87+
88+
callback = callback || noop;
89+
90+
// idempotent
91+
if (!this._lr) {
92+
return callback();
93+
}
94+
95+
this._http.close(function () {
96+
this._lr.close();
97+
this._http = null;
98+
this._lr = null;
99+
callback();
100+
}.bind(this));
101+
};
102+
103+
function noop() {}
104+
105+
module.exports = Server;

Diff for: package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
"vinyl-fs": false
1616
},
1717
"dependencies": {
18+
"ansi-html": "0.0.4",
1819
"ast-types": "^0.8.12",
19-
"babelify": "^6.3.0",
2020
"babel-core": "^5.0.0",
21+
"babelify": "^6.3.0",
2122
"brfs": "^1.4.0",
23+
"chokidar": "^1.2.0",
2224
"concat-stream": "^1.5.0",
25+
"debounce": "^1.0.0",
2326
"doctrine": "^0.7.1",
2427
"documentation-theme-default": "^1.0.0",
28+
"events": "^1.1.0",
2529
"extend": "^3.0.0",
2630
"get-comments": "^1.0.1",
2731
"github-url-from-git": "^1.4.0",
@@ -34,13 +38,15 @@
3438
"mdast-html": "^1.2.1",
3539
"mdast-toc": "^1.1.0",
3640
"micromatch": "^2.1.6",
41+
"mime": "^1.3.4",
3742
"module-deps": "^3.7.3",
3843
"parse-filepath": "^0.6.3",
3944
"remote-origin-url": "^0.4.0",
4045
"resolve": "^1.1.6",
4146
"slugg": "^0.1.2",
4247
"stream-array": "^1.1.0",
4348
"strip-json-comments": "^1.0.2",
49+
"tiny-lr": "^0.2.1",
4450
"traverse": "^0.6.6",
4551
"unist-builder": "^1.0.0",
4652
"vfile": "^1.1.2",

0 commit comments

Comments
 (0)