markdownlint
Advanced tools
Comparing version 0.0.4 to 0.0.5
@@ -6,3 +6,7 @@ "use strict"; | ||
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." | ||
} | ||
}; | ||
@@ -9,0 +13,0 @@ |
@@ -56,82 +56,86 @@ "use strict"; | ||
// Lints a single file | ||
function lintFile(file, config, synchronous, callback) { | ||
// Callback for read file API | ||
function readFile(err, contents) { | ||
if (err) { | ||
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 | ||
tokens.forEach(function forToken(token) { | ||
if (token.map) { | ||
token.line = lines[token.map[0]]; | ||
token.lineNumber = token.map[0] + 1; | ||
// Trim bottom of token to exclude whitespace lines | ||
while (!(lines[token.map[1] - 1].trim())) { | ||
token.map[1]--; | ||
} | ||
// Annotate children with lineNumber | ||
var lineNumber = token.lineNumber; | ||
(token.children || []).forEach(function forChild(child) { | ||
child.lineNumber = lineNumber; | ||
if ((child.type === "softbreak") || (child.type === "hardbreak")) { | ||
lineNumber++; | ||
} | ||
}); | ||
// Lints a single string | ||
function lintContent(content, config) { | ||
// Parse content into tokens and lines | ||
var tokens = md.parse(content, {}); | ||
var lines = content.split(shared.newLineRe); | ||
// Annotate tokens with line/lineNumber | ||
tokens.forEach(function forToken(token) { | ||
if (token.map) { | ||
token.line = lines[token.map[0]]; | ||
token.lineNumber = token.map[0] + 1; | ||
// Trim bottom of token to exclude whitespace lines | ||
while (!(lines[token.map[1] - 1].trim())) { | ||
token.map[1]--; | ||
} | ||
// Annotate children with lineNumber | ||
var lineNumber = token.lineNumber; | ||
(token.children || []).forEach(function forChild(child) { | ||
child.lineNumber = lineNumber; | ||
if ((child.type === "softbreak") || (child.type === "hardbreak")) { | ||
lineNumber++; | ||
} | ||
}); | ||
// Create parameters for rules | ||
var params = { | ||
"tokens": tokens, | ||
"lines": lines | ||
}; | ||
// Merge rules/tags and sanitize config | ||
var mergedRules = {}; | ||
var ruleDefault = (config.default === undefined) || !!config.default; | ||
rules.forEach(function forRule(rule) { | ||
mergedRules[rule.name] = ruleDefault; | ||
} | ||
}); | ||
// Create parameters for rules | ||
var params = { | ||
"tokens": tokens, | ||
"lines": lines | ||
}; | ||
// Merge rules/tags and sanitize config | ||
var mergedRules = {}; | ||
var ruleDefault = (config.default === undefined) || !!config.default; | ||
rules.forEach(function forRule(rule) { | ||
mergedRules[rule.name] = ruleDefault; | ||
}); | ||
Object.keys(config).forEach(function forKey(key) { | ||
var value = config[key]; | ||
if (value) { | ||
if (!(value instanceof Object)) { | ||
value = {}; | ||
} | ||
} else { | ||
value = false; | ||
} | ||
if (ruleToDescription[key]) { | ||
mergedRules[key] = value; | ||
} else if (tagToRules[key]) { | ||
tagToRules[key].forEach(function forRule(rule) { | ||
mergedRules[rule] = value; | ||
}); | ||
Object.keys(config).forEach(function forKey(key) { | ||
var value = config[key]; | ||
if (value) { | ||
if (!(value instanceof Object)) { | ||
value = {}; | ||
} | ||
} else { | ||
value = false; | ||
} | ||
if (ruleToDescription[key]) { | ||
mergedRules[key] = value; | ||
} else if (tagToRules[key]) { | ||
tagToRules[key].forEach(function forRule(rule) { | ||
mergedRules[rule] = value; | ||
}); | ||
} | ||
}); | ||
// Run each enabled rule | ||
var result = {}; | ||
rules.forEach(function forRule(rule) { | ||
if (mergedRules[rule.name]) { | ||
// Configure rule | ||
params.options = mergedRules[rule.name]; | ||
var errors = []; | ||
rule.func(params, errors); | ||
// Record any errors | ||
if (errors.length) { | ||
errors.sort(numberComparison); | ||
result[rule.name] = errors.filter(uniqueFilterForSorted); | ||
} | ||
} | ||
}); | ||
callback(null, result); | ||
} | ||
}); | ||
// Run each enabled rule | ||
var result = {}; | ||
rules.forEach(function forRule(rule) { | ||
if (mergedRules[rule.name]) { | ||
// Configure rule | ||
params.options = mergedRules[rule.name]; | ||
var errors = []; | ||
rule.func(params, errors); | ||
// Record any errors | ||
if (errors.length) { | ||
errors.sort(numberComparison); | ||
result[rule.name] = errors.filter(uniqueFilterForSorted); | ||
} | ||
} | ||
}); | ||
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 | ||
if (synchronous) { | ||
readFile(null, fs.readFileSync(file, shared.utf8Encoding)); | ||
lintContentWrapper(null, fs.readFileSync(file, shared.utf8Encoding)); | ||
} else { | ||
fs.readFile(file, shared.utf8Encoding, readFile); | ||
fs.readFile(file, shared.utf8Encoding, lintContentWrapper); | ||
} | ||
@@ -160,7 +164,8 @@ } | ||
var files = (options.files || []).slice(); | ||
var strings = options.strings || {}; | ||
var config = options.config || { "default": true }; | ||
var synchronous = (callback === markdownlintSynchronousCallback); | ||
var results = new Results(); | ||
// Lint each input file | ||
function lintFiles() { | ||
// Helper to lint the next file in the array | ||
function lintFilesArray() { | ||
var file = files.shift(); | ||
@@ -170,8 +175,7 @@ if (file) { | ||
if (err) { | ||
callback(err); | ||
} else { | ||
// Record errors and lint next file | ||
results[file] = result; | ||
lintFiles(); | ||
return callback(err); | ||
} | ||
// Record errors and lint next file | ||
results[file] = result; | ||
lintFilesArray(); | ||
}); | ||
@@ -182,3 +186,10 @@ } else { | ||
} | ||
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) { | ||
@@ -185,0 +196,0 @@ return results; |
@@ -93,3 +93,3 @@ "use strict"; | ||
var current = null; | ||
var lastWithMap = null; | ||
var lastWithMap = { "map": [ 0, 1 ] }; | ||
tokens.forEach(function forToken(token) { | ||
@@ -546,3 +546,3 @@ if ((token.type === "bullet_list_open") || | ||
list.items.forEach(function forItem(item) { | ||
var re = new RegExp("^\\s*" + String(number) + "\\. "); | ||
var re = new RegExp("^\\s*" + String(number) + "\\."); | ||
if (!re.test(item.line)) { | ||
@@ -576,3 +576,3 @@ errors.push(item.lineNumber); | ||
var match = /^\s*\S+(\s+)/.exec(item.line); | ||
if (match[1].length !== expectedSpaces) { | ||
if (!match || (match[1].length !== expectedSpaces)) { | ||
errors.push(item.lineNumber); | ||
@@ -579,0 +579,0 @@ } |
{ | ||
"name": "markdownlint", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "A Node.js style checker and lint tool for Markdown files.", | ||
@@ -18,13 +18,15 @@ "main": "lib/markdownlint.js", | ||
"debug": "node debug node_modules/nodeunit/bin/nodeunit", | ||
"lint": "eslint lib test & eslint --rule \"no-console: 0, no-shadow: 0\" example", | ||
"example": "cd example & node standalone.js & grunt markdownlint & gulp markdownlint" | ||
"lint": "eslint lib test & eslint --env browser --global markdownit --global markdownlint --rule \"no-unused-vars: 0, no-extend-native: 0, max-statements: 0\" demo & eslint --rule \"no-console: 0, no-shadow: 0\" example", | ||
"build-demo": "copy node_modules\\markdown-it\\dist\\markdown-it.min.js demo & browserify lib/markdownlint.js --standalone markdownlint --exclude markdown-it --outfile demo/markdownlint-browser.js", | ||
"example": "npm install through2 & cd example & node standalone.js & grunt markdownlint & gulp markdownlint" | ||
}, | ||
"dependencies": { | ||
"markdown-it": "^4.1.1" | ||
"markdown-it": "^4.2.1" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^0.19.0", | ||
"istanbul": "^0.3.8", | ||
"browserify": "^10.1.3", | ||
"eslint": "^0.21.0", | ||
"istanbul": "^0.3.13", | ||
"nodeunit": "^0.9.1", | ||
"q": "^1.2.0" | ||
"q": "^1.4.0" | ||
}, | ||
@@ -31,0 +33,0 @@ "keywords": [ |
@@ -36,2 +36,10 @@ # markdownlint | ||
## Demonstration | ||
[`markdownlint` demo](http://dlaa.me/markdownlint/), an interactive, in-browser | ||
playground for learning and exploring. | ||
> *Note*: `markdownlint` is not intended for use in the browser; the demo has | ||
> polyfills and avoids referencing the `fs` module. | ||
## Rules | ||
@@ -147,2 +155,21 @@ | ||
#### 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 | ||
@@ -210,3 +237,7 @@ | ||
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." | ||
} | ||
}; | ||
@@ -231,2 +262,5 @@ | ||
```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 | ||
@@ -251,2 +285,7 @@ bad.md: 1: MD018 No space after hash on atx style header | ||
{ | ||
"good.string": {}, | ||
"bad.string": { | ||
"MD010": [ 3 ], | ||
"MD018": [ 1, 3 ] | ||
}, | ||
"good.md": {}, | ||
@@ -339,2 +378,3 @@ "bad.md": { | ||
* 0.0.4 - Add tests MD033-MD040, update dependencies. | ||
* 0.0.5 - Add `strings` option to enable file-less scenarios, add in-browser demo. | ||
@@ -341,0 +381,0 @@ [npm-image]: https://img.shields.io/npm/v/markdownlint.svg |
@@ -10,2 +10,3 @@ "use strict"; | ||
var rules = require("../lib/rules"); | ||
var polyfills = require("../demo/browser-polyfills"); | ||
@@ -162,2 +163,25 @@ function createTestForFile(file) { | ||
module.exports.stringInputLineEndings = function stringInputLineEndings(test) { | ||
test.expect(2); | ||
var options = { | ||
"strings": { | ||
"cr": "One\rTwo\r#Three", | ||
"lf": "One\nTwo\n#Three", | ||
"crlf": "One\r\nTwo\r\n#Three", | ||
"mixed": "One\rTwo\n#Three" | ||
} | ||
}; | ||
markdownlint(options, function callback(err, actualResult) { | ||
test.ifError(err); | ||
var expectedResult = { | ||
"cr": { "MD018": [ 3 ] }, | ||
"lf": { "MD018": [ 3 ] }, | ||
"crlf": { "MD018": [ 3 ] }, | ||
"mixed": { "MD018": [ 3 ] } | ||
}; | ||
test.deepEqual(actualResult, expectedResult, "Undetected issues."); | ||
test.done(); | ||
}); | ||
}; | ||
module.exports.defaultTrue = function defaultTrue(test) { | ||
@@ -447,3 +471,3 @@ test.expect(2); | ||
module.exports.filesNotModified = function filesNotModified(test) { | ||
module.exports.filesArrayNotModified = function filesArrayNotModified(test) { | ||
test.expect(2); | ||
@@ -466,3 +490,3 @@ var files = [ | ||
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(); | ||
@@ -472,7 +496,7 @@ }); | ||
module.exports.missingFiles = function missingFiles(test) { | ||
module.exports.missingFilesAndStrings = function missingFilesAndStrings(test) { | ||
test.expect(2); | ||
markdownlint({}, function callback(err, result) { | ||
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(); | ||
@@ -502,3 +526,3 @@ }); | ||
module.exports.badFileSync = function badFileSync(test) { | ||
test.expect(3); | ||
test.expect(4); | ||
test.throws(function badFileCall() { | ||
@@ -509,2 +533,3 @@ markdownlint.sync({ | ||
}, function testError(err) { | ||
test.ok(err, "Did not get an error for bad file."); | ||
test.ok(err instanceof Error, "Error not instance of Error."); | ||
@@ -517,2 +542,22 @@ test.equal(err.code, "ENOENT", "Error code for bad file not ENOENT."); | ||
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) { | ||
@@ -614,1 +659,49 @@ test.expect(95); | ||
}; | ||
module.exports.typeAllFiles = function typeAllFiles(test) { | ||
// Simulates typing each test file to validate handling of partial input | ||
var files = fs.readdirSync("./test"); | ||
files.forEach(function forFile(file) { | ||
if (/\.md$/.test(file)) { | ||
var content = fs.readFileSync( | ||
path.join("./test", file), shared.utf8Encoding); | ||
while (content) { | ||
markdownlint.sync({ | ||
"strings": { | ||
"content": content | ||
} | ||
}); | ||
content = content.slice(0, -1); | ||
} | ||
} | ||
}); | ||
test.done(); | ||
}; | ||
module.exports.trimPolyfills = function trimPolyfills(test) { | ||
var inputs = [ | ||
"text text", | ||
" text text ", | ||
" text text ", | ||
// ECMAScript Whitespace | ||
"\u0009 text text \u0009", | ||
"\u000b text text \u000b", | ||
"\u000c text text \u000c", | ||
"\u0020 text text \u0020", | ||
"\u00a0 text text \u00a0", | ||
"\ufeff text text \ufeff", | ||
// ECMAScript LineTerminator | ||
"\u000a text text \u000a", | ||
"\u000d text text \u000d", | ||
"\u2028 text text \u2028", | ||
"\u2029 text text \u2029" | ||
]; | ||
test.expect(inputs.length * 2); | ||
inputs.forEach(function forInput(input) { | ||
test.equal(polyfills.trimLeftPolyfill.call(input), input.trimLeft(), | ||
"trimLeft incorrect for '" + input + "'"); | ||
test.equal(polyfills.trimRightPolyfill.call(input), input.trimRight(), | ||
"trimRight incorrect for '" + input + "'"); | ||
}); | ||
test.done(); | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
123985
114
2081
385
5
Updatedmarkdown-it@^4.2.1