UNPKG

8.14 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const _ = require('lodash');
4const sort = require('./sort');
5const nest = require('./nest');
6const filterAccess = require('./filter_access');
7const dependency = require('./input/dependency');
8const shallow = require('./input/shallow');
9const parseJavaScript = require('./parsers/javascript');
10const parseVueScript = require('./parsers/vue');
11const github = require('./github');
12const hierarchy = require('./hierarchy');
13const inferName = require('./infer/name');
14const inferKind = require('./infer/kind');
15const inferAugments = require('./infer/augments');
16const inferImplements = require('./infer/implements');
17const inferParams = require('./infer/params');
18const inferProperties = require('./infer/properties');
19const inferMembership = require('./infer/membership');
20const inferReturn = require('./infer/return');
21const inferAccess = require('./infer/access');
22const inferType = require('./infer/type');
23const formatLint = require('./lint').formatLint;
24const garbageCollect = require('./garbage_collect');
25const lintComments = require('./lint').lintComments;
26const markdownAST = require('./output/markdown_ast');
27const mergeConfig = require('./merge_config');
28
29/**
30 * Build a pipeline of comment handlers.
31 * @param {Array<Function>} fns - Pipeline elements. Each is a function that accepts
32 * a comment and can return a comment or undefined (to drop that comment).
33 * @returns {Function} pipeline
34 * @private
35 */
36function pipeline(fns) {
37 return comment => {
38 for (let i = 0; comment && i < fns.length; i++) {
39 if (fns[i]) {
40 comment = fns[i](comment);
41 }
42 }
43 return comment;
44 };
45}
46
47function configure(indexes, args) {
48 const mergedConfig = mergeConfig(args);
49
50 return mergedConfig.then(config => {
51 const expandedInputs = expandInputs(indexes, config);
52
53 return expandedInputs.then(inputs => {
54 return {
55 inputs,
56 config
57 };
58 });
59 });
60}
61
62/**
63 * Given an array of indexes and options for whether to resolve shallow
64 * or deep dependencies, resolve dependencies.
65 *
66 * @param {Array<string>|string} indexes files to process
67 * @param {Object} config options
68 * @returns {Promise<Array<string>>} promise with results
69 */
70function expandInputs(indexes, config) {
71 // Ensure that indexes is an array of strings
72 indexes = [].concat(indexes);
73
74 if (config.shallow || config.documentExported) {
75 return shallow(indexes, config);
76 }
77
78 return dependency(indexes, config);
79}
80
81function buildInternal(inputsAndConfig) {
82 const config = inputsAndConfig.config;
83 const inputs = inputsAndConfig.inputs;
84
85 if (!config.access) {
86 config.access = ['public', 'undefined', 'protected'];
87 }
88
89 const buildPipeline = pipeline([
90 inferName,
91 inferAccess(config.inferPrivate),
92 inferAugments,
93 inferImplements,
94 inferKind,
95 nest,
96 inferParams,
97 inferProperties,
98 inferReturn,
99 inferMembership(),
100 inferType,
101 config.github && github,
102 garbageCollect
103 ]);
104
105 const extractedComments = _.flatMap(inputs, function(sourceFile) {
106 if (!sourceFile.source) {
107 sourceFile.source = fs.readFileSync(sourceFile.file, 'utf8');
108 }
109
110 if (!sourceFile.file) {
111 sourceFile.file = '';
112 }
113
114 if (path.extname(sourceFile.file) === '.vue') {
115 return parseVueScript(sourceFile, config).map(buildPipeline);
116 }
117 return parseJavaScript(sourceFile, config).map(buildPipeline);
118 }).filter(Boolean);
119
120 return filterAccess(
121 config.access,
122 hierarchy(sort(extractedComments, config))
123 );
124}
125
126function lintInternal(inputsAndConfig) {
127 const inputs = inputsAndConfig.inputs;
128 const config = inputsAndConfig.config;
129
130 const lintPipeline = pipeline([
131 lintComments,
132 inferName,
133 inferAccess(config.inferPrivate),
134 inferAugments,
135 inferKind,
136 inferParams,
137 inferProperties,
138 inferReturn,
139 inferMembership(),
140 inferType,
141 nest
142 ]);
143
144 const extractedComments = _.flatMap(inputs, sourceFile => {
145 if (!sourceFile.source) {
146 sourceFile.source = fs.readFileSync(sourceFile.file, 'utf8');
147 }
148
149 return parseJavaScript(sourceFile, config).map(lintPipeline);
150 }).filter(Boolean);
151
152 return formatLint(hierarchy(extractedComments));
153}
154
155/**
156 * Lint files for non-standard or incorrect documentation
157 * information, returning a potentially-empty string
158 * of lint information intended for human-readable output.
159 *
160 * @param {Array<string>|string} indexes files to process
161 * @param {Object} args args
162 * @param {Array<string>} args.external a string regex / glob match pattern
163 * that defines what external modules will be whitelisted and included in the
164 * generated documentation.
165 * @param {boolean} [args.shallow=false] whether to avoid dependency parsing
166 * even in JavaScript code.
167 * @param {string} [args.inferPrivate] a valid regular expression string
168 * to infer whether a code element should be private, given its naming structure.
169 * For instance, you can specify `inferPrivate: '^_'` to automatically treat
170 * methods named like `_myMethod` as private.
171 * @param {string|Array<string>} [args.extension] treat additional file extensions
172 * as JavaScript, extending the default set of `js`, `es6`, and `jsx`.
173 * @returns {Promise} promise with lint results
174 * @public
175 * @example
176 * documentation.lint('file.js').then(lintOutput => {
177 * if (lintOutput) {
178 * console.log(lintOutput);
179 * process.exit(1);
180 * } else {
181 * process.exit(0);
182 * }
183 * });
184 */
185const lint = (indexes, args) => configure(indexes, args).then(lintInternal);
186
187/**
188 * Generate JavaScript documentation as a list of parsed JSDoc
189 * comments, given a root file as a path.
190 *
191 * @param {Array<string>|string} indexes files to process
192 * @param {Object} args args
193 * @param {Array<string>} args.external a string regex / glob match pattern
194 * that defines what external modules will be whitelisted and included in the
195 * generated documentation.
196 * @param {boolean} [args.shallow=false] whether to avoid dependency parsing
197 * even in JavaScript code.
198 * @param {Array<string|Object>} [args.order=[]] optional array that
199 * defines sorting order of documentation
200 * @param {Array<string>} [args.access=[]] an array of access levels
201 * to output in documentation
202 * @param {Object} [args.hljs] hljs optional args
203 * @param {boolean} [args.hljs.highlightAuto=false] hljs automatically detect language
204 * @param {Array} [args.hljs.languages] languages for hljs to choose from
205 * @param {string} [args.inferPrivate] a valid regular expression string
206 * to infer whether a code element should be private, given its naming structure.
207 * For instance, you can specify `inferPrivate: '^_'` to automatically treat
208 * methods named like `_myMethod` as private.
209 * @param {string|Array<string>} [args.extension] treat additional file extensions
210 * as JavaScript, extending the default set of `js`, `es6`, and `jsx`.
211 * @returns {Promise} results
212 * @public
213 * @example
214 * var documentation = require('documentation');
215 *
216 * documentation.build(['index.js'], {
217 * // only output comments with an explicit @public tag
218 * access: ['public']
219 * }).then(res => {
220 * // res is an array of parsed comments with inferred properties
221 * // and more: everything you need to build documentation or
222 * // any other kind of code data.
223 * });
224 */
225const build = (indexes, args) => configure(indexes, args).then(buildInternal);
226
227/**
228 * Documentation's formats are modular methods that take comments
229 * and config as input and return Promises with results,
230 * like stringified JSON, markdown strings, or Vinyl objects for HTML
231 * output.
232 * @public
233 */
234const formats = {
235 html: require('./output/html'),
236 md: require('./output/markdown'),
237 remark: (comments, config) =>
238 markdownAST(comments, config).then(res => JSON.stringify(res, null, 2)),
239 json: require('./output/json')
240};
241
242module.exports.lint = lint;
243module.exports.expandInputs = expandInputs;
244module.exports.build = build;
245module.exports.formats = formats;
246
247module.exports.util = {
248 createFormatters: require('./output/util/formatters'),
249 LinkerStack: require('./output/util/linker_stack')
250};