Skip to content

Commit

Permalink
Add a flag to document all exported bindings
Browse files Browse the repository at this point in the history
This adds a boolean flag called `document-exported` (defaults to false)
that effectively adds an empty comment to all exported bindings that do
not already have a JSDoc comment. It also does the same for the
members of exported classes and objects.

```js
export class C {
  method() {}
}
```

Both `C` and `C#method` are now part of the generated documentation.

Related to #424
  • Loading branch information
arv committed Aug 18, 2016
1 parent b506893 commit 0f0ae8f
Show file tree
Hide file tree
Showing 14 changed files with 853 additions and 62 deletions.
62 changes: 33 additions & 29 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,41 @@ Commands:
readme inject documentation into your README.md

Options:
--help Show help [boolean]
--version Show version number [boolean]
--shallow shallow mode turns off dependency resolution, only
processing the specified files (or the main script
specified in package.json) [boolean] [default: false]
--config, -c configuration file. an array defining explicit sort order
--external a string / glob match pattern that defines which external
modules will be whitelisted and included in the generated
documentation. [default: null]
--extension, -e only input source files matching this extension will be
parsed, this option can be used multiple times.
--polyglot polyglot mode turns off dependency resolution and enables
multi-language support. use this to document c++ [boolean]
--private, -p generate documentation tagged as private
--help Show help [boolean]
--version Show version number [boolean]
--shallow shallow mode turns off dependency resolution, only
processing the specified files (or the main script
specified in package.json) [boolean] [default: false]
--config, -c configuration file. an array defining explicit sort order
--external a string / glob match pattern that defines which external
modules will be whitelisted and included in the generated
documentation. [default: null]
--extension, -e only input source files matching this extension will be
parsed, this option can be used multiple times.
--polyglot polyglot mode turns off dependency resolution and enables
multi-language support. use this to document c++[boolean]
--private, -p generate documentation tagged as private
[boolean] [default: false]
--access, -a Include only comments with a given access level, out of
private, protected, public, undefined. By default, public,
protected, and undefined access levels are included
--access, -a Include only comments with a given access level, out of
private, protected, public, undefined. By default,
public, protected, and undefined access levels are
included
[choices: "public", "private", "protected", "undefined"]
--github, -g infer links to github in documentation [boolean]
--infer-private Infer private access based on the name. This is a regular
expression that is used to match the name [string]
--theme, -t specify a theme: this must be a valid theme module
--name project name. by default, inferred from package.json
--watch, -w watch input files and rebuild documentation when they
change [boolean]
--project-version project version. by default, inferred from package.json
--output, -o output location. omit for stdout, otherwise is a filename
for single-file outputs and a directory name for multi-file
outputs like html [default: "stdout"]
--format, -f [choices: "json", "md", "html"] [default: "json"]
--github, -g infer links to github in documentation [boolean]
--infer-private Infer private access based on the name. This is a regular
expression that is used to match the name [string]
--document-exported Generate documentation for all exported bindings and
members even if there is no JSDoc for them
[boolean] [default: false]
--theme, -t specify a theme: this must be a valid theme module
--name project name. by default, inferred from package.json
--watch, -w watch input files and rebuild documentation when they
change [boolean]
--project-version project version. by default, inferred from package.json
--output, -o output location. omit for stdout, otherwise is a filename
for single-file outputs and a directory name for
multi-file outputs like html [default: "stdout"]
--format, -f [choices: "json", "md", "remark", "html"] [default: "json"]

Examples:
documentation build foo.js -f md > parse documentation in a file and
Expand Down
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function buildSync(indexes, options) {
return index;
}).filter(filterJS(options.extension, options.polyglot))
.reduce(function (memo, file) {
return memo.concat(parseFn(file));
return memo.concat(parseFn(file, options));
}, [])
.map(pipeline(
inferName(),
Expand Down Expand Up @@ -226,7 +226,7 @@ function lint(indexes, options, callback) {
inputs
.filter(filterJS(options.extension, options.polyglot))
.reduce(function (memo, file) {
return memo.concat(parseFn(file));
return memo.concat(parseFn(file, options));
}, [])
.map(pipeline(
lintComments,
Expand Down
6 changes: 6 additions & 0 deletions lib/commands/shared_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ function sharedInputOptions(parser) {
type: 'string',
describe: 'Infer private access based on the name. This is a regular expression that ' +
'is used to match the name'
})
.option('document-exported', {
type: 'boolean',
describe: 'Generate documentation for all exported bindings and members ' +
'even if there is no JSDoc for them',
default: false
});
}

Expand Down
106 changes: 80 additions & 26 deletions lib/parsers/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ function leftPad(str, width) {
* reads the file, parses the JavaScript, and parses the JSDoc.
*
* @param {Object} data a chunk of data provided by module-deps
* @param {Object} options options
* @return {Array<Object>} an array of parsed comments
*/
function parseJavaScript(data) {
function parseJavaScript(data, options) {
options = options || {};
var results = [];
var ast = babylon.parse(data.source, {
allowImportExportEverywhere: true,
Expand Down Expand Up @@ -80,31 +82,7 @@ function parseJavaScript(data) {
* @return {undefined} this emits data
*/
function parseComment(comment) {
var context = {
loc: extend({}, JSON.parse(JSON.stringify(path.node.loc))),
file: data.file,
sortKey: data.sortKey + ' ' + leftPad(path.node.loc.start.line, 8)
};
// Avoid visiting the same comment twice as a leading
// and trailing node
var key = JSON.stringify(comment.loc);
if (!visited[key]) {
visited[key] = true;
if (includeContext) {
// This is non-enumerable so that it doesn't get stringified in
// output; e.g. by the documentation binary.
Object.defineProperty(context, 'ast', {
enumerable: false,
value: path
});

if (path.parentPath && path.parentPath.node) {
context.code = data.source.substring
.apply(data.source, path.parentPath.node.range);
}
}
results.push(parse(comment.value, comment.loc, context));
}
addComment(comment.value, comment.loc, path, path.node.loc, includeContext);
}

(path.node[type] || [])
Expand All @@ -114,10 +92,86 @@ function parseJavaScript(data) {
});
}

function addComment(commentValue, commentLoc, path, nodeLoc, includeContext) {
var context = {
loc: extend({}, JSON.parse(JSON.stringify(nodeLoc))),
file: data.file,
sortKey: data.sortKey + ' ' + leftPad(nodeLoc.start.line, 8)
};
// Avoid visiting the same comment twice as a leading
// and trailing node
var key = JSON.stringify(commentLoc);
if (!visited[key]) {
visited[key] = true;
if (includeContext) {
// This is non-enumerable so that it doesn't get stringified in
// output; e.g. by the documentation binary.
Object.defineProperty(context, 'ast', {
enumerable: false,
value: path
});

if (path.parentPath && path.parentPath.node) {
context.code = data.source.substring
.apply(data.source, path.parentPath.node.range);
}
}
results.push(parse(commentValue, commentLoc, context));
}
}

function addBlankComment(path, node) {
addComment('', node.loc, path, node.loc, true);
}

function walkExported(ast) {
traverse(ast, {
enter: function (path) {
if (path.isExportDeclaration()) {
if (!hasJSDocComment(path)) {
if (!path.node.declaration) {
return;
}
const node = path.node.declaration;
addBlankComment(path, node);
}
} else if ((path.isClassProperty() || path.isClassMethod()) &&
!hasJSDocComment(path) && inExportedClass(path)) {
addBlankComment(path, path.node);
} else if ((path.isObjectProperty() || path.isObjectMethod()) &&
!hasJSDocComment(path) && inExportedObject(path)) {
addBlankComment(path, path.node);
}
}
});
}

function hasJSDocComment(path) {
return path.node.leadingComments && path.node.leadingComments.some(isJSDocComment);
}

function inExportedClass(path) {
var c = path.parentPath.parentPath;
return c.isClass() && c.parentPath.isExportDeclaration();
}

function inExportedObject(path) {
// ObjectExpression -> VariableDeclarator -> VariableDeclaration -> ExportNamedDeclaration
var p = path.parentPath.parentPath;
if (!p.isVariableDeclarator()) {
return false;
}
return p.parentPath.parentPath.isExportDeclaration();
}

walkComments(ast, 'leadingComments', true);
walkComments(ast, 'innerComments', false);
walkComments(ast, 'trailingComments', false);

if (options.documentExported) {
walkExported(ast);
}

return results;
}

Expand Down
15 changes: 15 additions & 0 deletions test/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,18 @@ test('fatal error', function (t) {
t.end();
}, false);
}, options);

test('build --document-exported', function (t) {
documentation(['build fixture/document-exported.input.js --document-exported -f md'], {}, function (err, data) {
t.error(err);

var outputfile = path.join(__dirname, 'fixture', 'document-exported.output.enabled.md');
if (process.env.UPDATE) {
fs.writeFileSync(outputfile, data, 'utf8');
}

var expect = fs.readFileSync(outputfile, 'utf-8');
t.equal(data, expect);
t.end();
}, false);
});
33 changes: 33 additions & 0 deletions test/fixture/document-exported.input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export class Class {
classMethod() {}
get classGetter() {}
set classSetter(v) {}
static staticMethod() {}
static get staticGetter() {}
static set staticSetter(v) {}
}

export var object = {
method() {},
get getter() {},
set setter(v) {},
prop: 42,
func: function() {},
};

class NotExportedClass {
classMethod() {}
get classGetter() {}
set classSetter(v) {}
static staticMethod() {}
static get staticGetter() {}
static set staticSetter(v) {}
}

var notExportedObject = {
method() {},
get getter() {},
set setter(v) {},
prop: 42,
func: function() {},
};
Loading

0 comments on commit 0f0ae8f

Please sign in to comment.