Add strings option to enable file-less scenarios.

This commit is contained in:
David Anson 2015-04-29 18:46:52 -07:00
parent fde1947031
commit 548e3d35cb
4 changed files with 176 additions and 87 deletions

View file

@ -143,6 +143,25 @@ responsibility.
Example: `[ "one.md", "dir/two.md" ]` Example: `[ "one.md", "dir/two.md" ]`
#### options.strings
Type: `Object` mapping `String` to `String`
Map of identifiers to strings for linting.
When Markdown content is not available as files, it can be passed as strings.
The keys of the `strings` object are used to identify each input value in the
`result` summary.
Example:
```json
{
"readme": "# README\n...",
"changelog": "# CHANGELOG\n..."
}
```
#### options.config #### options.config
Type: `Object` mapping `String` to `Boolean | Object` Type: `Object` mapping `String` to `Boolean | Object`
@ -205,7 +224,11 @@ Invoke `markdownlint` and use the `result` object's `toString` method:
var markdownlint = require("markdownlint"); var markdownlint = require("markdownlint");
var options = { var options = {
"files": [ "good.md", "bad.md" ] "files": [ "good.md", "bad.md" ],
"strings": {
"good.string": "# good.string\n\nThis string passes all rules.",
"bad.string": "#bad.string\n\n#This string fails\tsome rules."
}
}; };
markdownlint(options, function callback(err, result) { markdownlint(options, function callback(err, result) {
@ -225,6 +248,9 @@ console.log(result.toString());
Output of both calls: Output of both calls:
```text ```text
bad.string: 3: MD010 Hard tabs
bad.string: 1: MD018 No space after hash on atx style header
bad.string: 3: MD018 No space after hash on atx style header
bad.md: 3: MD010 Hard tabs bad.md: 3: MD010 Hard tabs
bad.md: 1: MD018 No space after hash on atx style header bad.md: 1: MD018 No space after hash on atx style header
bad.md: 3: MD018 No space after hash on atx style header bad.md: 3: MD018 No space after hash on atx style header
@ -244,6 +270,11 @@ Output:
```json ```json
{ {
"good.string": {},
"bad.string": {
"MD010": [ 3 ],
"MD018": [ 1, 3 ]
},
"good.md": {}, "good.md": {},
"bad.md": { "bad.md": {
"MD010": [ 3 ], "MD010": [ 3 ],
@ -331,6 +362,7 @@ bad.md: 3: MD018 No space after hash on atx style header
* 0.0.2 - Improve documentation, tests, and code. * 0.0.2 - Improve documentation, tests, and code.
* 0.0.3 - Add synchronous API, improve documentation and code. * 0.0.3 - Add synchronous API, improve documentation and code.
* 0.0.4 - Add tests MD033-MD040, update dependencies. * 0.0.4 - Add tests MD033-MD040, update dependencies.
* *PENDING* - Add `strings` option to enable file-less scenarios.
[npm-image]: https://img.shields.io/npm/v/markdownlint.svg [npm-image]: https://img.shields.io/npm/v/markdownlint.svg
[npm-url]: https://www.npmjs.com/package/markdownlint [npm-url]: https://www.npmjs.com/package/markdownlint

View file

@ -3,7 +3,11 @@
var markdownlint = require("../lib/markdownlint"); var markdownlint = require("../lib/markdownlint");
var options = { var options = {
"files": [ "good.md", "bad.md" ] "files": [ "good.md", "bad.md" ],
"strings": {
"good.string": "# good.string\n\nThis string passes all rules.",
"bad.string": "#bad.string\n\n#This string fails\tsome rules."
}
}; };
// Uses result.toString for pretty formatting // Uses result.toString for pretty formatting

View file

@ -53,16 +53,11 @@ function uniqueFilterForSorted(value, index, array) {
return (index === 0) || (value > array[index - 1]); return (index === 0) || (value > array[index - 1]);
} }
// Lints a single file // Lints a single string
function lintFile(file, config, synchronous, callback) { function lintContent(content, config) {
// Callback for read file API // Parse content into tokens and lines
function readFile(err, contents) { var tokens = md.parse(content, {});
if (err) { var lines = content.split(shared.newLineRe);
callback(err);
} else {
// Parse file into tokens and lines
var tokens = md.parse(contents, {});
var lines = contents.split(shared.newLineRe);
// Annotate tokens with line/lineNumber // Annotate tokens with line/lineNumber
tokens.forEach(function forToken(token) { tokens.forEach(function forToken(token) {
if (token.map) { if (token.map) {
@ -125,14 +120,23 @@ function lintFile(file, config, synchronous, callback) {
} }
} }
}); });
callback(null, result); return result;
}
// Lints a single file
function lintFile(file, config, synchronous, callback) {
function lintContentWrapper(err, content) {
if (err) {
return callback(err);
} }
var result = lintContent(content, config);
callback(null, result);
} }
// Make a/synchronous call to read file // Make a/synchronous call to read file
if (synchronous) { if (synchronous) {
readFile(null, fs.readFileSync(file, shared.utf8Encoding)); lintContentWrapper(null, fs.readFileSync(file, shared.utf8Encoding));
} else { } else {
fs.readFile(file, shared.utf8Encoding, readFile); fs.readFile(file, shared.utf8Encoding, lintContentWrapper);
} }
} }
@ -156,27 +160,34 @@ function markdownlint(options, callback) {
options = options || {}; options = options || {};
callback = callback || function noop() {}; callback = callback || function noop() {};
var files = (options.files || []).slice(); var files = (options.files || []).slice();
var strings = options.strings || {};
var config = options.config || { "default": true }; var config = options.config || { "default": true };
var synchronous = (callback === markdownlintSynchronousCallback); var synchronous = (callback === markdownlintSynchronousCallback);
var results = new Results(); var results = new Results();
// Lint each input file // Helper to lint the next file in the array
function lintFiles() { function lintFilesArray() {
var file = files.shift(); var file = files.shift();
if (file) { if (file) {
lintFile(file, config, synchronous, function lintedFile(err, result) { lintFile(file, config, synchronous, function lintedFile(err, result) {
if (err) { if (err) {
callback(err); return callback(err);
} else { }
// Record errors and lint next file // Record errors and lint next file
results[file] = result; results[file] = result;
lintFiles(); lintFilesArray();
}
}); });
} else { } else {
callback(null, results); callback(null, results);
} }
} }
lintFiles(); // Lint strings
Object.keys(strings).forEach(function forKey(key) {
var result = lintContent(strings[key] || "", config);
results[key] = result;
});
// Lint files
lintFilesArray();
// Return results
if (synchronous) { if (synchronous) {
return results; return results;
} }

View file

@ -158,6 +158,27 @@ module.exports.resultFormattingSync = function resultFormattingSync(test) {
test.done(); test.done();
}; };
module.exports.stringInputLineEndings = function stringInputLineEndings(test) {
test.expect(2);
var options = {
"strings": {
"lf": "One\nTwo\n#Three",
"crlf": "One\r\nTwo\r\n#Three",
"mixed": "One\r\nTwo\n#Three"
}
};
markdownlint(options, function callback(err, actualResult) {
test.ifError(err);
var expectedResult = {
"lf": { "MD018": [ 3 ] },
"crlf": { "MD018": [ 3 ] },
"mixed": { "MD018": [ 3 ] }
};
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
test.done();
});
};
module.exports.defaultTrue = function defaultTrue(test) { module.exports.defaultTrue = function defaultTrue(test) {
test.expect(2); test.expect(2);
var options = { var options = {
@ -442,7 +463,7 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
}); });
}; };
module.exports.filesNotModified = function filesNotModified(test) { module.exports.filesArrayNotModified = function filesArrayNotModified(test) {
test.expect(2); test.expect(2);
var files = [ var files = [
"./test/atx_header_spacing.md", "./test/atx_header_spacing.md",
@ -460,16 +481,16 @@ module.exports.missingOptions = function missingOptions(test) {
test.expect(2); test.expect(2);
markdownlint(null, function callback(err, result) { markdownlint(null, function callback(err, result) {
test.ifError(err); test.ifError(err);
test.ok(result, "Did not get result for missing options."); test.deepEqual(result, {}, "Did not get empty result for missing options.");
test.done(); test.done();
}); });
}; };
module.exports.missingFiles = function missingFiles(test) { module.exports.missingFilesAndStrings = function missingFilesAndStrings(test) {
test.expect(2); test.expect(2);
markdownlint({}, function callback(err, result) { markdownlint({}, function callback(err, result) {
test.ifError(err); test.ifError(err);
test.ok(result, "Did not get result for missing files."); test.ok(result, "Did not get result for missing files/strings.");
test.done(); test.done();
}); });
}; };
@ -494,12 +515,13 @@ module.exports.badFile = function badFile(test) {
}; };
module.exports.badFileSync = function badFileSync(test) { module.exports.badFileSync = function badFileSync(test) {
test.expect(3); test.expect(4);
test.throws(function badFileCall() { test.throws(function badFileCall() {
markdownlint.sync({ markdownlint.sync({
"files": [ "./badFile" ] "files": [ "./badFile" ]
}); });
}, function testError(err) { }, function testError(err) {
test.ok(err, "Did not get an error for bad file.");
test.ok(err instanceof Error, "Error not instance of Error."); test.ok(err instanceof Error, "Error not instance of Error.");
test.equal(err.code, "ENOENT", "Error code for bad file not ENOENT."); test.equal(err.code, "ENOENT", "Error code for bad file not ENOENT.");
return true; return true;
@ -507,6 +529,26 @@ module.exports.badFileSync = function badFileSync(test) {
test.done(); test.done();
}; };
module.exports.missingStringValue = function missingStringValue(test) {
test.expect(2);
markdownlint({
"strings": {
"undefined": undefined,
"null": null,
"empty": ""
}
}, function callback(err, result) {
test.ifError(err);
var expectedResult = {
"undefined": {},
"null": {},
"empty": {}
};
test.deepEqual(result, expectedResult, "Did not get empty results.");
test.done();
});
};
module.exports.readme = function readme(test) { module.exports.readme = function readme(test) {
test.expect(95); test.expect(95);
var tagToRules = {}; var tagToRules = {};