UNPKG

3.57 kBJavaScriptView Raw
1const fs = require('fs');
2const remark = require('remark');
3const path = require('path');
4const documentation = require('../');
5const sharedOptions = require('./shared_options');
6const inject = require('mdast-util-inject');
7const chalk = require('chalk');
8const disparity = require('disparity');
9const getReadmeFile = require('../get-readme-file');
10
11module.exports.command = 'readme [input..]';
12module.exports.description = 'inject documentation into your README.md';
13
14const defaultReadmeFile = getReadmeFile('.');
15
16/**
17 * Add yargs parsing for the readme command
18 * @param {Object} yargs module instance
19 * @returns {Object} yargs with options
20 * @private
21 */
22module.exports.builder = Object.assign(
23 {},
24 sharedOptions.sharedOutputOptions,
25 sharedOptions.sharedInputOptions,
26 {
27 'readme-file': {
28 describe: 'The markdown file into which to inject documentation',
29 default: defaultReadmeFile
30 },
31 section: {
32 alias: 's',
33 describe:
34 'The section heading after which to inject generated documentation',
35 required: true
36 },
37 'diff-only': {
38 alias: 'd',
39 describe:
40 'Instead of updating the given README with the generated documentation,' +
41 ' just check if its contents match, exiting nonzero if not.',
42 default: false
43 },
44 quiet: {
45 alias: 'q',
46 describe: 'Quiet mode: do not print messages or README diff to stdout.',
47 default: false
48 }
49 }
50);
51
52/**
53 * Insert API documentation into a Markdown readme
54 * @private
55 * @param {Object} argv args from the CLI option parser
56 * @returns {undefined} has the side-effect of writing a file or printing to stdout
57 */
58module.exports.handler = function readme(argv) {
59 argv._handled = true;
60
61 if (!argv.input.length) {
62 try {
63 argv.input = [
64 JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf8'))
65 .main || 'index.js'
66 ];
67 } catch (e) {
68 throw new Error(
69 'documentation was given no files and was not run in a module directory'
70 );
71 }
72 }
73
74 argv.noReferenceLinks = true;
75 argv.format = 'remark';
76 /* eslint no-console: 0 */
77 const log = (...data) => {
78 if (!argv.q) {
79 console.log.apply(console, data);
80 }
81 };
82
83 const readmeContent = fs.readFileSync(argv.readmeFile, 'utf8');
84
85 documentation
86 .build(argv.input, argv)
87 .then(comments => documentation.formats.remark(comments, argv))
88 .then(docsAst =>
89 remark()
90 .use(plugin, {
91 section: argv.section,
92 toInject: JSON.parse(docsAst)
93 })
94 .process(readmeContent)
95 )
96 .then(file => {
97 const diffOutput = disparity.unified(readmeContent, file.contents, {
98 paths: [argv.readmeFile, argv.readmeFile]
99 });
100 if (!diffOutput.length) {
101 log(`${argv.readmeFile} is up to date.`);
102 process.exit(0);
103 }
104
105 if (argv.d) {
106 log(
107 chalk.bold(`${argv.readmeFile} needs the following updates:`),
108 `\n${diffOutput}`
109 );
110 process.exit(1);
111 } else {
112 log(chalk.bold(`Updating ${argv.readmeFile}`), `\n${diffOutput}`);
113 }
114
115 fs.writeFileSync(argv.readmeFile, file.contents);
116 })
117 .catch(err => {
118 console.error(err);
119 process.exit(1);
120 });
121};
122
123// wrap the inject utility as an remark plugin
124function plugin(options) {
125 return function transform(targetAst, file, next) {
126 if (!inject(options.section, targetAst, options.toInject)) {
127 return next(new Error(`Heading ${options.section} not found.`));
128 }
129 next();
130 };
131}