Document public API, comment framework code.

This commit is contained in:
David Anson 2015-03-15 23:39:17 -07:00
parent ec7684b95f
commit 63a52e9dea
4 changed files with 94 additions and 5 deletions

View file

@ -60,6 +60,71 @@ cases come directly from that project.
See [Rules.md](doc/Rules.md) for more details. See [Rules.md](doc/Rules.md) for more details.
## API
```js
/**
* Lint specified Markdown files according to configurable rules.
*
* @param {Object} options Configuration options.
* @param {Function} callback Callback (err, result) function.
* @returns {void}
*/
function markdownlint(options, callback) { ... }
```
### options
Type: `Object`
Configures the function.
#### options.files
Type: `Array` of `String`
List of files to lint.
Each array element should be a single file (via relative or absolute path);
[globbing](http://en.wikipedia.org/wiki/Glob_%28programming%29) is the caller's
responsibility.
Example: `[ "one.md", "dir/two.md" ]`
#### options.config
Type: `Object` mapping `String` to `Object | Boolean`
Configures the rules to use.
Object keys are rule names and values are the rule's configuration.
The value `false` disables a rule, `true` enables its default configuration,
and passing an object customizes its settings. Setting the special `default`
rule to `true` or `false` includes/excludes all rules by default.
Example:
```json
{
"default": true,
"MD007": {
"indent": 4
},
"MD013": {
"line_length": 100
},
"MD029": false
}
```
### callback
Type: `Function` taking (`Error`, `Object`)
Function to call upon completion.
See below for an example of the structure of the `result` object.
## Usage ## Usage
Invoke `markdownlint` and use the `result` object's `toString` method: Invoke `markdownlint` and use the `result` object's `toString` method:

View file

@ -5,6 +5,7 @@ var md = require("markdown-it")();
var rules = require("./rules"); var rules = require("./rules");
var shared = require("./shared"); var shared = require("./shared");
// Mapping from rule name to description
var ruleToDescription = {}; var ruleToDescription = {};
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
ruleToDescription[rule.name] = rule.desc; ruleToDescription[rule.name] = rule.desc;
@ -12,6 +13,7 @@ rules.forEach(function forRule(rule) {
// console.log("* " + rule.name + " - " + rule.desc); // console.log("* " + rule.name + " - " + rule.desc);
}); });
// Class for results with toString for pretty display
function Results() { } function Results() { }
Results.prototype.toString = function resultsToString() { Results.prototype.toString = function resultsToString() {
var self = this; var self = this;
@ -31,40 +33,48 @@ Results.prototype.toString = function resultsToString() {
return results.join("\n"); return results.join("\n");
}; };
// Array.sort comparison for number objects
function numberComparison(a, b) { function numberComparison(a, b) {
return a - b; return a - b;
} }
// Function to return unique values from a sorted array
function uniqueFilterForSorted(value, index, array) { function uniqueFilterForSorted(value, index, array) {
return (index === 0) || (value > array[index - 1]); return (index === 0) || (value > array[index - 1]);
} }
// Lints a single file
function lintFile(file, config, callback) { function lintFile(file, config, callback) {
fs.readFile(file, { "encoding": "utf8" }, function readFile(err, contents) { fs.readFile(file, shared.utf8Encoding, function readFile(err, contents) {
if (err) { if (err) {
callback(err); callback(err);
} else { } else {
// Parse file into tokens and lines
var tokens = md.parse(contents, {}); var tokens = md.parse(contents, {});
var lines = contents.split(shared.newLineRe); var lines = contents.split(shared.newLineRe);
// Annotate tokens with line/lineNumber
tokens.forEach(function forToken(token) { tokens.forEach(function forToken(token) {
if (token.lines) { if (token.lines) {
token.line = lines[token.lines[0]]; token.line = lines[token.lines[0]];
token.lineNumber = token.lines[0] + 1; token.lineNumber = token.lines[0] + 1;
} }
}); });
// Create parameters for rules
var params = { var params = {
"tokens": tokens, "tokens": tokens,
"lines": lines "lines": lines
}; };
var result = {}; var result = {};
var configDefault = config.default; var defaultRule = (config.default !== undefined) && !!config.default;
var defaultRule = (configDefault !== undefined) && !!configDefault; // Run each rule
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
var ruleConfig = config[rule.name]; var ruleConfig = config[rule.name];
if (ruleConfig || (defaultRule && (ruleConfig === undefined))) { if (ruleConfig || (defaultRule && (ruleConfig === undefined))) {
// Pass rule-specific options
params.options = (ruleConfig instanceof Object) ? ruleConfig : {}; params.options = (ruleConfig instanceof Object) ? ruleConfig : {};
var errors = []; var errors = [];
rule.func(params, errors); rule.func(params, errors);
// Record any errors
if (errors.length) { if (errors.length) {
errors.sort(numberComparison); errors.sort(numberComparison);
result[rule.name] = errors.filter(uniqueFilterForSorted); result[rule.name] = errors.filter(uniqueFilterForSorted);
@ -76,12 +86,21 @@ function lintFile(file, config, callback) {
}); });
} }
/**
* Lint specified Markdown files according to configurable rules.
*
* @param {Object} options Configuration options.
* @param {Function} callback Callback (err, results) function.
* @returns {void}
*/
module.exports = function markdownlint(options, callback) { module.exports = function markdownlint(options, callback) {
// Normalize inputs
options = options || {}; options = options || {};
callback = callback || function noop() {}; callback = callback || function noop() {};
var files = (options.files || []).slice(); var files = (options.files || []).slice();
var config = options.config || { "default": true }; var config = options.config || { "default": true };
var results = new Results(); var results = new Results();
// Lint each input file
function lintFiles() { function lintFiles() {
var file = files.shift(); var file = files.shift();
if (file) { if (file) {
@ -89,6 +108,7 @@ module.exports = function markdownlint(options, callback) {
if (err) { if (err) {
callback(err); callback(err);
} else { } else {
// Record errors and lint next file
results[file] = result; results[file] = result;
lintFiles(); lintFiles();
} }

View file

@ -1,3 +1,7 @@
"use strict"; "use strict";
// Regular expression for matching common newline characters
module.exports.newLineRe = /\r\n|\r|\n/; module.exports.newLineRe = /\r\n|\r|\n/;
// readFile options for reading with the UTF-8 encoding
module.exports.utf8Encoding = { "encoding": "utf8" };

View file

@ -13,7 +13,7 @@ function createTestForFile(file) {
var actualPromise = Q.nfcall(fs.stat, configFile) var actualPromise = Q.nfcall(fs.stat, configFile)
.then( .then(
function configFileExists() { function configFileExists() {
return Q.nfcall(fs.readFile, configFile, { "encoding": "utf8" }) return Q.nfcall(fs.readFile, configFile, shared.utf8Encoding)
.then( .then(
function configFileContents(contents) { function configFileContents(contents) {
return JSON.parse(contents); return JSON.parse(contents);
@ -29,7 +29,7 @@ function createTestForFile(file) {
"config": config "config": config
}); });
}); });
var expectedPromise = Q.nfcall(fs.readFile, file, { "encoding": "utf8" }) var expectedPromise = Q.nfcall(fs.readFile, file, shared.utf8Encoding)
.then( .then(
function fileContents(contents) { function fileContents(contents) {
var lines = contents.split(shared.newLineRe); var lines = contents.split(shared.newLineRe);