Skip to content

Commit 17d18f4

Browse files
committed
--watch and --serve flags
These new flags replace the functionality of dev-documentation
1 parent 4416acb commit 17d18f4

19 files changed

+572
-123
lines changed

Diff for: bin/documentation.js

+57-22
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,76 @@
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'),
11-
lint = require('../lib/lint'),
12-
args = require('../lib/args.js');
13+
errorPage = require('../lib/error_page'),
14+
Server = require('../lib/server'),
15+
args = require('../lib/args');
1316

14-
var parsedArgs = args(process.argv.slice(2)),
15-
formatterOptions = parsedArgs.formatterOptions,
16-
outputLocation = parsedArgs.output,
17-
formatter = documentation.formats[parsedArgs.formatter];
17+
var parsedArgs = args(process.argv.slice(2));
1818

19-
documentation(parsedArgs.inputs, parsedArgs.options, function (err, comments) {
19+
var generator = documentation.bind(null,
20+
parsedArgs.inputs, parsedArgs.options, onDocumented.bind(null, parsedArgs));
21+
22+
var server = new Server();
23+
server.on('listening', function () {
24+
process.stdout.write('documentation.js serving on port 4001\n');
25+
});
26+
27+
function onDocumented(parsedArgs, err, comments) {
2028
if (err) {
29+
if (parsedArgs.command === 'serve') {
30+
return server.setFiles([errorPage(err)]).start();
31+
}
2132
throw err;
2233
}
2334

24-
if (parsedArgs.options.lint) {
25-
var lintOutput = lint.format(comments);
35+
documentation.formats[parsedArgs.formatter](
36+
comments, parsedArgs.formatterOptions,
37+
onFormatted.bind(null, parsedArgs));
38+
}
39+
40+
function onFormatted(parsedArgs, err, output) {
41+
if (parsedArgs.watch) {
42+
updateWatcher();
43+
}
44+
45+
if (parsedArgs.command === 'serve') {
46+
server.setFiles(output).start();
47+
} else if (parsedArgs.output === 'stdout') {
48+
process.stdout.write(output);
49+
} else if (Array.isArray(output)) {
50+
streamArray(output).pipe(vfs.dest(parsedArgs.output));
51+
} else {
52+
fs.writeFileSync(parsedArgs.output, output);
53+
}
54+
}
55+
56+
if (parsedArgs.watch) {
57+
var watcher = chokidar.watch(parsedArgs.inputs);
58+
watcher.on('all', debounce(generator, 300));
59+
} else if (parsedArgs.command === 'lint') {
60+
documentation.lint(parsedArgs.inputs, parsedArgs.options, function (err, lintOutput) {
2661
if (lintOutput) {
2762
console.log(lintOutput);
2863
process.exit(1);
2964
} else {
3065
process.exit(0);
3166
}
32-
}
33-
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-
}
41-
} else {
42-
process.stdout.write(output);
43-
}
4467
});
45-
});
68+
} else {
69+
generator();
70+
}
71+
72+
function updateWatcher() {
73+
documentation.expandInputs(parsedArgs.inputs, parsedArgs.options, addNewFiles);
74+
}
75+
76+
function addNewFiles(err, files) {
77+
watcher.add(files.map(function (data) {
78+
return data.file;
79+
}));
80+
}

Diff for: index.js

+77-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ var sort = require('./lib/sort'),
1616
inferProperties = require('./lib/infer/properties'),
1717
inferMembership = require('./lib/infer/membership'),
1818
inferReturn = require('./lib/infer/return'),
19-
lint = require('./lib/lint');
19+
formatLint = require('./lib/lint').formatLint,
20+
lintComments = require('./lib/lint').lintComments;
2021

2122
/**
2223
* Build a pipeline of comment handlers.
@@ -45,6 +46,20 @@ function noop(comment) {
4546
return comment;
4647
}
4748

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

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

81-
return inputFn(indexes, options, function (error, inputs) {
95+
return expandInputs(indexes, options, function (error, inputs) {
8296
if (error) {
8397
return callback(error);
8498
}
8599
try {
86100
callback(null,
87101
filterAccess(
88-
(options.private || options.lint) ? [] : undefined,
102+
options.private ? [] : undefined,
89103
hierarchy(
90104
inputs
91105
.filter(filterJS)
92106
.reduce(function (memo, file) {
93107
return memo.concat(parseFn(file));
94108
}, [])
95109
.map(pipeline(
96-
lint.lint,
97110
inferName(),
98111
inferKind(),
99112
inferParams(),
@@ -111,6 +124,65 @@ module.exports = function (indexes, options, callback) {
111124
});
112125
};
113126

127+
/**
128+
* Generate JavaScript documentation as a list of parsed JSDoc
129+
* comments, given a root file as a path.
130+
*
131+
* @param {Array<string>|string} indexes files to process
132+
* @param {Object} options options
133+
* @param {Array<string>} options.external a string regex / glob match pattern
134+
* that defines what external modules will be whitelisted and included in the
135+
* generated documentation.
136+
* @param {Array<string>} options.transform source transforms given as strings
137+
* passed to [the module-deps transform option](https://github.com/substack/module-deps)
138+
* @param {boolean} [options.polyglot=false] parse comments with a regex rather than
139+
* a proper parser. This enables support of non-JavaScript languages but
140+
* reduces documentation's ability to infer structure of code.
141+
* @param {boolean} [options.shallow=false] whether to avoid dependency parsing
142+
* even in JavaScript code. With the polyglot option set, this has no effect.
143+
* @param {Function} callback to be called when the documentation generation
144+
* is complete, with (err, result) argumentsj
145+
* @returns {undefined} calls callback
146+
*/
147+
module.exports.lint = function lint(indexes, options, callback) {
148+
options = options || {};
149+
150+
if (typeof indexes === 'string') {
151+
indexes = [indexes];
152+
}
153+
154+
var parseFn = (options.polyglot) ? polyglot : parseJavaScript;
155+
156+
return expandInputs(indexes, options, function (error, inputs) {
157+
if (error) {
158+
return callback(error);
159+
}
160+
try {
161+
callback(null,
162+
formatLint(hierarchy(
163+
inputs
164+
.filter(filterJS)
165+
.reduce(function (memo, file) {
166+
return memo.concat(parseFn(file));
167+
}, [])
168+
.map(pipeline(
169+
lintComments,
170+
inferName(),
171+
inferKind(),
172+
inferParams(),
173+
inferProperties(),
174+
inferReturn(),
175+
inferMembership(),
176+
nest))
177+
.filter(Boolean))));
178+
} catch (e) {
179+
callback(e);
180+
}
181+
});
182+
};
183+
184+
module.exports.expandInputs = expandInputs;
185+
114186
module.exports.formats = {
115187
html: require('./lib/output/html'),
116188
md: require('./lib/output/markdown'),

Diff for: lib/args.js

+66-45
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,80 @@ function parse(args) {
88
return yargs.reset()
99
.usage('Usage: $0 <command> [options]')
1010

11-
.option('f', {
12-
alias: 'format',
13-
default: 'json',
14-
choices: ['json', 'md', 'html']
15-
})
16-
17-
.option('lint', {
18-
describe: 'check output for common style and uniformity mistakes',
19-
type: 'boolean'
20-
})
11+
.demand(1)
2112

22-
.describe('t', 'specify a theme: this must be a valid theme module')
23-
.alias('t', 'theme')
24-
25-
.boolean('p')
26-
.describe('p', 'generate documentation tagged as private')
27-
.alias('p', 'private')
13+
.command('build', 'build documentation')
14+
.command('lint', 'check for common style and uniformity mistakes')
15+
.command('serve', 'generate, update, and display HTML documentation')
2816

2917
.version(function () {
3018
return require('../package').version;
3119
})
3220

33-
.describe('name', 'project name. by default, inferred from package.json')
34-
.describe('project-version', 'project version. by default, inferred from package.json')
21+
.help('help')
22+
.alias('help', 'h')
3523

24+
.option('format', {
25+
alias: 'f',
26+
default: 'json',
27+
choices: ['json', 'md', 'html']
28+
})
29+
.option('watch', {
30+
describe: 'watch input files and rebuild documentation when they change',
31+
alias: 'w',
32+
type: 'boolean'
33+
})
34+
.option('theme', {
35+
describe: 'specify a theme: this must be a valid theme module',
36+
alias: 't'
37+
})
38+
.option('private', {
39+
describe: 'generate documentation tagged as private',
40+
type: 'boolean',
41+
default: false,
42+
alias: 'p'
43+
})
44+
.option('name', {
45+
describe: 'project name. by default, inferred from package.json'
46+
})
47+
.option('project-version', {
48+
describe: 'project version. by default, inferred from package.json'
49+
})
3650
.option('shallow', {
3751
describe: 'shallow mode turns off dependency resolution, ' +
3852
'only processing the specified files (or the main script specified in package.json)',
3953
default: false,
4054
type: 'boolean'
4155
})
42-
43-
.boolean('polyglot')
44-
.describe('polyglot', 'polyglot mode turns off dependency resolution and ' +
45-
'enables multi-language support. use this to document c++')
46-
47-
.boolean('g')
48-
.describe('g', 'infer links to github in documentation')
49-
.alias('g', 'github')
50-
51-
.option('o', {
56+
.option('polyglot', {
57+
type: 'boolean',
58+
describe: 'polyglot mode turns off dependency resolution and ' +
59+
'enables multi-language support. use this to document c++'
60+
})
61+
.option('github', {
62+
type: 'boolean',
63+
describe: 'infer links to github in documentation',
64+
alias: 'g'
65+
})
66+
.option('config', {
67+
describe: 'configuration file. an array defining explicit sort order',
68+
alias: 'c'
69+
})
70+
.option('output', {
5271
describe: 'output location. omit for stdout, otherwise is a filename ' +
5372
'for single-file outputs and a directory name for multi-file outputs like html',
54-
alias: 'output',
55-
default: 'stdout'
73+
default: 'stdout',
74+
alias: 'o'
5675
})
5776

58-
.describe('c', 'configuration file. an array defining explicit sort order')
59-
.alias('c', 'config')
60-
61-
.help('h')
62-
.alias('h', 'help')
77+
.example('$0 build foo.js -f md > API.md', 'parse documentation in a ' +
78+
'file and generate API documentation as Markdown')
6379

64-
.example('$0 foo.js', 'parse documentation in a given file')
80+
.example('$0 lint project.js', 'check documentation style')
6581

6682
.parse(args)
67-
6883
}
84+
6985
/**
7086
* Parse and validate command-line options for documentation.
7187
* @param {Array} args The array of arguments to parse; e.g. process.argv.slice(2).
@@ -75,14 +91,14 @@ function parse(args) {
7591
module.exports = function (args) {
7692
var argv = parse(args);
7793

78-
var inputs,
79-
name = argv.name,
94+
var command = argv._[0],
95+
inputs = argv._.slice(1);
96+
97+
var name = argv.name,
8098
version = argv['project-version'],
8199
transform;
82100

83-
if (argv._.length > 0) {
84-
inputs = argv._;
85-
} else {
101+
if (inputs.length == 0) {
86102
try {
87103
var p = require(path.resolve('package.json'));
88104
inputs = [p.main || 'index.js'];
@@ -97,11 +113,15 @@ module.exports = function (args) {
97113
}
98114
}
99115

100-
if (argv.f === 'html' && argv.o === 'stdout') {
116+
if (argv.f === 'html' && argv.o === 'stdout' && !argv.serve) {
101117
yargs.showHelp();
102118
throw new Error('The HTML output mode requires a destination directory set with -o');
103119
}
104120

121+
if (command === 'serve') {
122+
argv.format = 'html';
123+
}
124+
105125
var config = {};
106126

107127
if (argv.config) {
@@ -110,16 +130,17 @@ module.exports = function (args) {
110130

111131
return {
112132
inputs: inputs,
133+
command: command,
113134
options: {
114135
private: argv.private,
115136
transform: transform,
116-
lint: argv.lint,
117137
github: argv.github,
118138
polyglot: argv.polyglot,
119139
order: config.order || [],
120140
shallow: argv.shallow
121141
},
122-
formatter: argv.f,
142+
formatter: argv.format,
143+
watch: argv.w,
123144
formatterOptions: {
124145
name: name,
125146
version: version,

0 commit comments

Comments
 (0)