Skip to content

Commit 3ea2944

Browse files
author
Anand Thakker
committed
Add readme command and tests
1 parent ed14745 commit 3ea2944

File tree

8 files changed

+234
-3
lines changed

8 files changed

+234
-3
lines changed

lib/commands/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
module.exports = {
1313
'build': require('./build'),
1414
'serve': require('./serve'),
15-
'lint': require('./lint')
15+
'lint': require('./lint'),
16+
'readme': require('./readme')
1617
};

lib/commands/readme.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var mdast = require('mdast');
5+
var inject = require('mdast-util-inject');
6+
var chalk = require('chalk');
7+
var disparity = require('disparity');
8+
var build = require('./build');
9+
10+
module.exports = readme;
11+
module.exports.description = 'inject documentation into your README.md';
12+
module.exports.parseArgs = function (yargs) {
13+
yargs.usage('Usage: $0 readme [--readme-file=README.md] --section "API"' +
14+
' [--compare-only] [other documentationjs options]')
15+
.option('readme-file', {
16+
describe: 'The markdown file into which to inject documentation',
17+
default: 'README.md'
18+
})
19+
.option('section', {
20+
alias: 's',
21+
describe: 'The section heading after which to inject generated documentation',
22+
required: true
23+
})
24+
.option('diff-only', {
25+
alias: 'd',
26+
describe: 'Instead of updating the given README with the generated documentation,' +
27+
' just check if its contents match, exiting nonzero if not.',
28+
default: false
29+
})
30+
.option('quiet', {
31+
alias: 'q',
32+
describe: 'Quiet mode: do not print messages or README diff to stdout.',
33+
default: false
34+
})
35+
.help('help')
36+
.example('$0 readme index.js -s "API Docs" --github');
37+
};
38+
39+
function readme(documentation, parsedArgs) {
40+
var readmeOptions = parsedArgs.commandOptions;
41+
readmeOptions.format = 'mdast';
42+
/* eslint no-console: 0 */
43+
var log = readmeOptions.q ? function () {}
44+
: console.log.bind(console, '[documentation-readme] ');
45+
var readmeFile = readmeOptions['readme-file'];
46+
47+
build(documentation, parsedArgs, onAst);
48+
49+
function onAst(err, docsAst) {
50+
if (err) {
51+
throw err;
52+
}
53+
var readmeContent = fs.readFileSync(readmeFile, 'utf8');
54+
mdast.use(plugin, { toInject: docsAst }).process(readmeContent, onInjected.bind(null, readmeContent));
55+
}
56+
57+
function onInjected(readmeContent, err, file, content) {
58+
if (err) {
59+
throw err;
60+
}
61+
62+
var diffOutput = disparity.unified(readmeContent, content, {
63+
paths: [readmeFile, readmeFile]
64+
});
65+
if (!diffOutput.length) {
66+
log(readmeFile + ' is up to date.');
67+
process.exit(0);
68+
}
69+
70+
if (readmeOptions.d) {
71+
log(chalk.bold(readmeFile + ' needs the following updates:'), '\n' + diffOutput);
72+
process.exit(1);
73+
} else {
74+
log(chalk.bold('Updating ' + readmeFile), '\n' + diffOutput);
75+
}
76+
77+
fs.writeFileSync(readmeFile, content);
78+
}
79+
}
80+
81+
// wrap the inject utility as an mdast plugin
82+
function plugin(mdast, options) {
83+
return function transform(targetAst, file, next) {
84+
inject('API', targetAst, options.toInject);
85+
next();
86+
};
87+
}

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
"ast-types": "^0.8.12",
1212
"babel-core": "^5.0.0",
1313
"babelify": "^6.3.0",
14+
"chalk": "^1.1.1",
1415
"chokidar": "^1.2.0",
1516
"concat-stream": "^1.5.0",
1617
"debounce": "^1.0.0",
18+
"disparity": "^2.0.0",
1719
"doctrine": "^0.7.1",
1820
"documentation-theme-default": "2.1.1",
1921
"documentation-theme-utils": "^1.0.1",
@@ -26,6 +28,7 @@
2628
"jsdoc-inline-lex": "^1.0.1",
2729
"mdast": "^2.0.0",
2830
"mdast-toc": "^1.1.0",
31+
"mdast-util-inject": "^1.0.0",
2932
"micromatch": "^2.1.6",
3033
"mime": "^1.3.4",
3134
"module-deps": "^4.0.2",
@@ -46,10 +49,12 @@
4649
"devDependencies": {
4750
"chdir": "0.0.0",
4851
"eslint": "^1.5.1",
52+
"fs-extra": "^0.26.2",
4953
"glob": "^6.0.1",
5054
"lodash": "^3.10.1",
5155
"mock-fs": "^3.5.0",
52-
"tap": "^2.2.0"
56+
"tap": "^2.2.0",
57+
"tmp": "0.0.28"
5358
},
5459
"keywords": [
5560
"documentation",

test/bin-readme.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
var test = require('tap').test,
2+
path = require('path'),
3+
os = require('os'),
4+
exec = require('child_process').exec,
5+
tmp = require('tmp'),
6+
fs = require('fs-extra');
7+
8+
function documentation(args, options, callback, parseJSON) {
9+
if (!callback) {
10+
callback = options;
11+
options = {};
12+
}
13+
14+
if (!options.cwd) {
15+
options.cwd = __dirname;
16+
}
17+
18+
options.maxBuffer = 1024 * 1024;
19+
20+
args.unshift(path.join(__dirname, '../bin/documentation.js'));
21+
22+
exec(args.join(' '), options, callback);
23+
}
24+
25+
test('readme command', function (group) {
26+
var fixtures = path.join(__dirname, 'fixture/readme');
27+
var sourceFile = path.join(fixtures, 'index.js');
28+
29+
tmp.dir({unsafeCleanup: true}, function (err, d) {
30+
group.error(err);
31+
fs.copySync(path.join(fixtures, 'README.input.md'), path.join(d, 'README.md'));
32+
fs.copySync(path.join(fixtures, 'index.js'), path.join(d, 'index.js'));
33+
34+
// run tests after setting up temp dir
35+
36+
group.test('--diff-only: changes needed', function (t) {
37+
t.error(err);
38+
var before = fs.readFileSync(path.join(d, 'README.md'), 'utf-8');
39+
documentation(['readme index.js --diff-only -s API'], {cwd: d}, function (err, stdout, stderr) {
40+
var after = fs.readFileSync(path.join(d, 'README.md'), 'utf-8');
41+
t.ok(err);
42+
t.notEqual(err.code, 0, 'exit nonzero');
43+
t.same(after, before, 'readme unchanged');
44+
t.end();
45+
});
46+
});
47+
48+
var expectedFile = path.join(fixtures, 'README.output.md');
49+
var expected = fs.readFileSync(expectedFile, 'utf-8');
50+
51+
group.test('updates README.md', function (t) {
52+
documentation(['readme index.js -s API'], {cwd: d}, function (err, stdout) {
53+
t.error(err);
54+
var actual = fs.readFileSync(path.join(d, 'README.md'), 'utf-8');
55+
t.same(actual, expected, 'generated readme output');
56+
t.end();
57+
});
58+
});
59+
60+
group.test('--readme-file', function (t) {
61+
fs.copySync(path.join(fixtures, 'README.input.md'), path.join(d, 'other.md'));
62+
documentation(['readme index.js -s API --readme-file other.md'], {cwd: d}, function (err, stdout) {
63+
t.error(err);
64+
var actual = fs.readFileSync(path.join(d, 'other.md'), 'utf-8');
65+
t.same(actual, expected, 'generated readme output');
66+
t.end();
67+
});
68+
});
69+
70+
group.test('--diff-only: changes NOT needed', function (t) {
71+
t.error(err);
72+
fs.copySync(path.join(fixtures, 'README.output.md'), path.join(d, 'uptodate.md'));
73+
documentation(['readme index.js --diff-only -s API --readme-file uptodate.md'],
74+
{cwd: d}, function (err, stdout, stderr) {
75+
t.error(err);
76+
t.match(stdout, 'is up to date.');
77+
t.end();
78+
});
79+
});
80+
81+
group.test('requires -s option', function (t) {
82+
documentation(['readme index.js'], {cwd: d}, function (err, stdout, stderr) {
83+
t.ok(err);
84+
t.ok(err.code !== 0, 'exit nonzero');
85+
t.match(stderr, 'Missing required argument: s');
86+
t.end();
87+
});
88+
});
89+
90+
group.end();
91+
});
92+
});
93+

test/bin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ var test = require('tap').test,
44
path = require('path'),
55
os = require('os'),
66
exec = require('child_process').exec,
7-
fs = require('fs');
7+
tmp = require('tmp'),
8+
fs = require('fs-extra');
89

910
function documentation(args, options, callback, parseJSON) {
1011
if (!callback) {

test/fixture/readme/README.input.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# A title
2+
3+
# API
4+
5+
# Another section

test/fixture/readme/README.output.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# A title
2+
3+
# API
4+
5+
## bar
6+
7+
A second function with docs
8+
9+
**Parameters**
10+
11+
- `b`
12+
13+
## foo
14+
15+
A function with documentation.
16+
17+
**Parameters**
18+
19+
- `a` {string} blah
20+
21+
Returns **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** answer
22+
23+
# Another section

test/fixture/readme/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
/**
3+
* A function with documentation.
4+
* @param a {string} blah
5+
* @return {number} answer
6+
*/
7+
function foo(a) {
8+
9+
}
10+
11+
/**
12+
* A second function with docs
13+
*/
14+
function bar(b) {
15+
16+
}

0 commit comments

Comments
 (0)