mirror of
https://github.com/remarkjs/remark-lint.git
synced 2024-10-26 19:31:42 +03:00
Refactor code-style
This commit is contained in:
parent
b4f1baec9e
commit
6c1b4cf6b0
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
coverage/
|
||||
remark-lint.js
|
||||
remark-lint.min.js
|
@ -1,4 +1,4 @@
|
||||
exports.settings = {bullet: '*', paddedTable: false};
|
||||
exports.settings = {bullet: '*', paddedTable: false}
|
||||
|
||||
exports.plugins = [
|
||||
require('./packages/remark-preset-lint-recommended'),
|
||||
@ -9,4 +9,4 @@ exports.plugins = [
|
||||
require('remark-validate-links'),
|
||||
require('./script/plugin/list-of-presets'),
|
||||
require('./script/plugin/list-of-rules')
|
||||
];
|
||||
]
|
||||
|
27
package.json
27
package.json
@ -38,6 +38,7 @@
|
||||
"mdast-zone": "^3.0.0",
|
||||
"nyc": "^11.0.0",
|
||||
"parse-author": "^2.0.0",
|
||||
"prettier": "^1.12.1",
|
||||
"remark": "^9.0.0",
|
||||
"remark-cli": "^5.0.0",
|
||||
"remark-comment-config": "^5.0.0",
|
||||
@ -55,16 +56,16 @@
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "lerna bootstrap",
|
||||
"build-presets": "node script/build-presets",
|
||||
"build-rules": "node script/build-rules",
|
||||
"build-md": "remark . -qfo",
|
||||
"build-bundle": "browserify packages/remark-lint/index.js --bare -s remarkLint > remark-lint.js",
|
||||
"build-mangle": "esmangle remark-lint.js > remark-lint.min.js",
|
||||
"build": "npm run build-presets && npm run build-rules && npm run build-md && npm run build-bundle && npm run build-mangle",
|
||||
"lint": "xo",
|
||||
"format": "remark . -qfo && prettier --write '**/*.js' && xo --fix",
|
||||
"generate:presets": "node script/build-presets",
|
||||
"generate:rules": "node script/build-rules",
|
||||
"generate": "npm run generate:presets && npm run generate:rules",
|
||||
"build:bundle": "browserify packages/remark-lint/index.js --bare -s remarkLint > remark-lint.js",
|
||||
"build:mangle": "esmangle remark-lint.js > remark-lint.min.js",
|
||||
"build": "npm run build:bundle && npm run build:mangle",
|
||||
"test-api": "node test",
|
||||
"test-coverage": "nyc --reporter lcov tape test.js",
|
||||
"test": "npm run build && npm run lint && npm run test-coverage"
|
||||
"test": "npm run generate && npm run format && npm run build && npm run test-coverage"
|
||||
},
|
||||
"nyc": {
|
||||
"check-coverage": true,
|
||||
@ -72,8 +73,16 @@
|
||||
"functions": 100,
|
||||
"branches": 100
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": false,
|
||||
"semi": false,
|
||||
"trailingComma": "none"
|
||||
},
|
||||
"xo": {
|
||||
"space": true,
|
||||
"prettier": true,
|
||||
"esnext": false,
|
||||
"rules": {
|
||||
"guard-for-in": "off",
|
||||
|
@ -45,59 +45,64 @@
|
||||
* 9:3: Add 1 space between blockquote and content
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:blockquote-indentation', blockquoteIndentation);
|
||||
module.exports = rule(
|
||||
'remark-lint:blockquote-indentation',
|
||||
blockquoteIndentation
|
||||
)
|
||||
|
||||
function blockquoteIndentation(tree, file, preferred) {
|
||||
preferred = isNaN(preferred) || typeof preferred !== 'number' ? null : preferred;
|
||||
function blockquoteIndentation(tree, file, pref) {
|
||||
pref = typeof pref === 'number' && !isNaN(pref) ? pref : null
|
||||
|
||||
visit(tree, 'blockquote', visitor);
|
||||
visit(tree, 'blockquote', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var indent;
|
||||
var diff;
|
||||
var word;
|
||||
var diff
|
||||
var diffAbs
|
||||
var reason
|
||||
|
||||
if (generated(node) || node.children.length === 0) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (preferred) {
|
||||
indent = check(node);
|
||||
diff = preferred - indent;
|
||||
word = diff > 0 ? 'Add' : 'Remove';
|
||||
|
||||
diff = Math.abs(diff);
|
||||
if (pref) {
|
||||
diff = pref - check(node)
|
||||
|
||||
if (diff !== 0) {
|
||||
file.message(
|
||||
word + ' ' + diff + ' ' + plural('space', diff) +
|
||||
' between blockquote and content',
|
||||
position.start(node.children[0])
|
||||
);
|
||||
diffAbs = Math.abs(diff)
|
||||
|
||||
reason =
|
||||
(diff > 0 ? 'Add' : 'Remove') +
|
||||
' ' +
|
||||
diffAbs +
|
||||
' ' +
|
||||
plural('space', diffAbs) +
|
||||
' between blockquote and content'
|
||||
|
||||
file.message(reason, position.start(node.children[0]))
|
||||
}
|
||||
} else {
|
||||
preferred = check(node);
|
||||
pref = check(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function check(node) {
|
||||
var head = node.children[0];
|
||||
var indentation = position.start(head).column - position.start(node).column;
|
||||
var padding = toString(head).match(/^ +/);
|
||||
var head = node.children[0]
|
||||
var indentation = position.start(head).column - position.start(node).column
|
||||
var padding = toString(head).match(/^ +/)
|
||||
|
||||
if (padding) {
|
||||
indentation += padding[0].length;
|
||||
indentation += padding[0].length
|
||||
}
|
||||
|
||||
return indentation;
|
||||
return indentation
|
||||
}
|
||||
|
@ -68,103 +68,94 @@
|
||||
* 1:1: Invalid checked checkbox marker `!`: use either `'x'`, or `'X'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var vfileLocation = require('vfile-location');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var vfileLocation = require('vfile-location')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:checkbox-character-style', checkboxCharacterStyle);
|
||||
module.exports = rule(
|
||||
'remark-lint:checkbox-character-style',
|
||||
checkboxCharacterStyle
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
var CHECKED = {x: true, X: true};
|
||||
var UNCHECKED = {' ': true, '\t': true};
|
||||
var checked = {x: true, X: true}
|
||||
var unchecked = {' ': true, '\t': true}
|
||||
var types = {true: 'checked', false: 'unchecked'}
|
||||
|
||||
function checkboxCharacterStyle(tree, file, preferred) {
|
||||
var contents = file.toString();
|
||||
var location = vfileLocation(file);
|
||||
function checkboxCharacterStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
var location = vfileLocation(file)
|
||||
|
||||
if (preferred === 'consistent' || typeof preferred !== 'object') {
|
||||
preferred = {};
|
||||
}
|
||||
pref = typeof pref === 'object' ? pref : {}
|
||||
|
||||
if (!preferred.unchecked) {
|
||||
preferred.unchecked = null;
|
||||
}
|
||||
|
||||
if (!preferred.checked) {
|
||||
preferred.checked = null;
|
||||
}
|
||||
|
||||
if (
|
||||
preferred.unchecked !== null &&
|
||||
UNCHECKED[preferred.unchecked] !== true
|
||||
) {
|
||||
if (pref.unchecked && unchecked[pref.unchecked] !== true) {
|
||||
file.fail(
|
||||
'Invalid unchecked checkbox marker `' +
|
||||
preferred.unchecked +
|
||||
'`: use either `\'\\t\'`, or `\' \'`'
|
||||
);
|
||||
pref.unchecked +
|
||||
"`: use either `'\\t'`, or `' '`"
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
preferred.checked !== null &&
|
||||
CHECKED[preferred.checked] !== true
|
||||
) {
|
||||
if (pref.checked && checked[pref.checked] !== true) {
|
||||
file.fail(
|
||||
'Invalid checked checkbox marker `' +
|
||||
preferred.checked +
|
||||
'`: use either `\'x\'`, or `\'X\'`'
|
||||
);
|
||||
pref.checked +
|
||||
"`: use either `'x'`, or `'X'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(tree, 'listItem', visitor);
|
||||
visit(tree, 'listItem', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var type;
|
||||
var initial;
|
||||
var final;
|
||||
var stop;
|
||||
var value;
|
||||
var style;
|
||||
var character;
|
||||
var type
|
||||
var initial
|
||||
var final
|
||||
var value
|
||||
var style
|
||||
var character
|
||||
var reason
|
||||
|
||||
/* Exit early for items without checkbox. */
|
||||
if (node.checked !== Boolean(node.checked) || generated(node)) {
|
||||
return;
|
||||
if (typeof node.checked !== 'boolean' || generated(node)) {
|
||||
return
|
||||
}
|
||||
|
||||
type = node.checked ? 'checked' : 'unchecked';
|
||||
type = types[node.checked]
|
||||
initial = start(node).offset
|
||||
final = (node.children.length ? start(node.children[0]) : end(node)).offset
|
||||
|
||||
initial = start(node).offset;
|
||||
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
|
||||
/* For a checkbox to be parsed, it must be followed by a white space. */
|
||||
value = contents
|
||||
.slice(initial, final)
|
||||
.trimRight()
|
||||
.slice(0, -1)
|
||||
|
||||
/* For a checkbox to be parsed, it must be followed
|
||||
* by a white space. */
|
||||
value = contents.slice(initial, final).trimRight().slice(0, -1);
|
||||
/* The checkbox character is behind a square bracket. */
|
||||
character = value.charAt(value.length - 1)
|
||||
style = pref[type]
|
||||
|
||||
/* The checkbox character is behind a square
|
||||
* bracket. */
|
||||
character = value.charAt(value.length - 1);
|
||||
style = preferred[type];
|
||||
if (style) {
|
||||
if (character !== style) {
|
||||
reason =
|
||||
type.charAt(0).toUpperCase() +
|
||||
type.slice(1) +
|
||||
' checkboxes should use `' +
|
||||
style +
|
||||
'` as a marker'
|
||||
|
||||
if (style === null) {
|
||||
preferred[type] = character;
|
||||
} else if (character !== style) {
|
||||
stop = initial + value.length;
|
||||
|
||||
file.message(
|
||||
type.charAt(0).toUpperCase() + type.slice(1) +
|
||||
' checkboxes should use `' + style + '` as a marker',
|
||||
{
|
||||
start: location.toPosition(stop - 1),
|
||||
end: location.toPosition(stop)
|
||||
}
|
||||
);
|
||||
file.message(reason, {
|
||||
start: location.toPosition(initial + value.length - 1),
|
||||
end: location.toPosition(initial + value.length)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
pref[type] = character
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,56 +27,58 @@
|
||||
* 4:7-4:10: Checkboxes should be followed by a single character
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var vfileLocation = require('vfile-location');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var vfileLocation = require('vfile-location')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:checkbox-content-indent', checkboxContentIndent);
|
||||
module.exports = rule(
|
||||
'remark-lint:checkbox-content-indent',
|
||||
checkboxContentIndent
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
var reason = 'Checkboxes should be followed by a single character'
|
||||
|
||||
function checkboxContentIndent(tree, file) {
|
||||
var contents = file.toString();
|
||||
var location = vfileLocation(file);
|
||||
var contents = String(file)
|
||||
var location = vfileLocation(file)
|
||||
|
||||
visit(tree, 'listItem', visitor);
|
||||
visit(tree, 'listItem', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var initial;
|
||||
var final;
|
||||
var value;
|
||||
var initial
|
||||
var final
|
||||
var value
|
||||
|
||||
/* Exit early for items without checkbox. */
|
||||
if (node.checked !== Boolean(node.checked) || generated(node)) {
|
||||
return;
|
||||
if (typeof node.checked !== 'boolean' || generated(node)) {
|
||||
return
|
||||
}
|
||||
|
||||
initial = start(node).offset;
|
||||
initial = start(node).offset
|
||||
/* istanbul ignore next - hard to test, couldn’t find a case. */
|
||||
final = (node.children.length ? start(node.children[0]) : end(node)).offset;
|
||||
final = (node.children.length ? start(node.children[0]) : end(node)).offset
|
||||
|
||||
while (/[^\S\n]/.test(contents.charAt(final))) {
|
||||
final++;
|
||||
final++
|
||||
}
|
||||
|
||||
/* For a checkbox to be parsed, it must be followed
|
||||
* by a white space. */
|
||||
value = contents.slice(initial, final);
|
||||
value = contents.slice(initial, final)
|
||||
value = value.slice(value.indexOf(']') + 1)
|
||||
|
||||
value = value.slice(value.indexOf(']') + 1);
|
||||
|
||||
if (value.length === 1) {
|
||||
return;
|
||||
if (value.length !== 1) {
|
||||
file.message(reason, {
|
||||
start: location.toPosition(final - value.length + 1),
|
||||
end: location.toPosition(final)
|
||||
})
|
||||
}
|
||||
|
||||
file.message('Checkboxes should be followed by a single character', {
|
||||
start: location.toPosition(final - value.length + 1),
|
||||
end: location.toPosition(final)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -91,65 +91,58 @@
|
||||
* 1:1: Invalid code block style `invalid`: use either `'consistent'`, `'fenced'`, or `'indented'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:code-block-style', codeBlockStyle);
|
||||
module.exports = rule('remark-lint:code-block-style', codeBlockStyle)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
var STYLES = {
|
||||
null: true,
|
||||
fenced: true,
|
||||
indented: true
|
||||
};
|
||||
var styles = {null: true, fenced: true, indented: true}
|
||||
|
||||
function codeBlockStyle(tree, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function codeBlockStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid code block style `' + preferred + '`: use either `\'consistent\'`, `\'fenced\'`, or `\'indented\'`');
|
||||
if (styles[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid code block style `' +
|
||||
pref +
|
||||
"`: use either `'consistent'`, `'fenced'`, or `'indented'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(tree, 'code', visitor);
|
||||
visit(tree, 'code', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var current = check(node);
|
||||
var current = check(node)
|
||||
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preferred) {
|
||||
preferred = current;
|
||||
} else if (preferred !== current) {
|
||||
file.message('Code blocks should be ' + preferred, node);
|
||||
if (current) {
|
||||
if (!pref) {
|
||||
pref = current
|
||||
} else if (pref !== current) {
|
||||
file.message('Code blocks should be ' + pref, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the style of `node`. */
|
||||
function check(node) {
|
||||
var initial = start(node).offset;
|
||||
var final = end(node).offset;
|
||||
var initial = start(node).offset
|
||||
var final = end(node).offset
|
||||
|
||||
if (generated(node)) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
if (
|
||||
node.lang ||
|
||||
/^\s*([~`])\1{2,}/.test(contents.slice(initial, final))
|
||||
) {
|
||||
return 'fenced';
|
||||
}
|
||||
|
||||
return 'indented';
|
||||
return node.lang || /^\s*([~`])\1{2,}/.test(contents.slice(initial, final))
|
||||
? 'fenced'
|
||||
: 'indented'
|
||||
}
|
||||
}
|
||||
|
@ -19,38 +19,36 @@
|
||||
* 1:1-1:47: Do not use upper-case characters in definition labels
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:definition-case', definitionCase);
|
||||
module.exports = rule('remark-lint:definition-case', definitionCase)
|
||||
|
||||
var LABEL = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/;
|
||||
var label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/
|
||||
var reason = 'Do not use upper-case characters in definition labels'
|
||||
|
||||
function definitionCase(tree, file) {
|
||||
var contents = file.toString();
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'definition', validate);
|
||||
visit(tree, 'footnoteDefinition', validate);
|
||||
visit(tree, ['definition', 'footnoteDefinition'], validate)
|
||||
|
||||
/* Validate a node, either a normal definition or
|
||||
* a footnote definition. */
|
||||
function validate(node) {
|
||||
var start = position.start(node).offset;
|
||||
var end = position.end(node).offset;
|
||||
var label;
|
||||
var start = position.start(node).offset
|
||||
var end = position.end(node).offset
|
||||
var value
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
value = contents.slice(start, end).match(label)[1]
|
||||
|
||||
label = contents.slice(start, end).match(LABEL)[1];
|
||||
|
||||
if (label !== label.toLowerCase()) {
|
||||
file.message('Do not use upper-case characters in definition labels', node);
|
||||
if (value !== value.toLowerCase()) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,36 +19,32 @@
|
||||
* 1:1-1:57: Do not use consecutive white-space in definition labels
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:definition-spacing', definitionSpacing);
|
||||
module.exports = rule('remark-lint:definition-spacing', definitionSpacing)
|
||||
|
||||
var LABEL = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/;
|
||||
var label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/
|
||||
var reason = 'Do not use consecutive white-space in definition labels'
|
||||
|
||||
function definitionSpacing(tree, file) {
|
||||
var contents = file.toString();
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'definition', validate);
|
||||
visit(tree, 'footnoteDefinition', validate);
|
||||
visit(tree, ['definition', 'footnoteDefinition'], validate)
|
||||
|
||||
function validate(node) {
|
||||
var start = position.start(node).offset;
|
||||
var end = position.end(node).offset;
|
||||
var label;
|
||||
var start = position.start(node).offset
|
||||
var end = position.end(node).offset
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
label = contents.slice(start, end).match(LABEL)[1];
|
||||
|
||||
if (/[ \t\n]{2,}/.test(label)) {
|
||||
file.message('Do not use consecutive white-space in definition labels', node);
|
||||
if (
|
||||
!generated(node) &&
|
||||
/[ \t\n]{2,}/.test(contents.slice(start, end).match(label)[1])
|
||||
) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,43 +59,45 @@
|
||||
* 1:1: Invalid emphasis marker `invalid`: use either `'consistent'`, `'*'`, or `'_'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:emphasis-marker', emphasisMarker);
|
||||
module.exports = rule('remark-lint:emphasis-marker', emphasisMarker)
|
||||
|
||||
var MARKERS = {
|
||||
'*': true,
|
||||
_: true,
|
||||
null: true
|
||||
};
|
||||
var markers = {null: true, '*': true, _: true}
|
||||
|
||||
function emphasisMarker(tree, file, preferred) {
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
function emphasisMarker(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
if (MARKERS[preferred] !== true) {
|
||||
file.fail('Invalid emphasis marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (markers[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid emphasis marker `' +
|
||||
pref +
|
||||
"`: use either `'consistent'`, `'*'`, or `'_'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(tree, 'emphasis', visitor);
|
||||
visit(tree, 'emphasis', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var marker = file.toString().charAt(position.start(node).offset);
|
||||
var marker
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
marker = contents.charAt(position.start(node).offset)
|
||||
|
||||
if (preferred) {
|
||||
if (marker !== preferred) {
|
||||
file.message('Emphasis should use `' + preferred + '` as a marker', node);
|
||||
if (pref) {
|
||||
if (marker !== pref) {
|
||||
file.message('Emphasis should use `' + pref + '` as a marker', node)
|
||||
}
|
||||
} else {
|
||||
pref = marker
|
||||
}
|
||||
} else {
|
||||
preferred = marker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,48 +64,53 @@
|
||||
* 1:1-3:4: Invalid code-language flag
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:fenced-code-flag', fencedCodeFlag);
|
||||
module.exports = rule('remark-lint:fenced-code-flag', fencedCodeFlag)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function fencedCodeFlag(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
var allowEmpty = false;
|
||||
var flags = [];
|
||||
var fence = /^ {0,3}([~`])\1{2,}/
|
||||
var reasonInvalid = 'Invalid code-language flag'
|
||||
var reasonMissing = 'Missing code-language flag'
|
||||
|
||||
if (typeof preferred === 'object' && !('length' in preferred)) {
|
||||
allowEmpty = Boolean(preferred.allowEmpty);
|
||||
function fencedCodeFlag(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
var allowEmpty = false
|
||||
var flags = []
|
||||
|
||||
preferred = preferred.flags;
|
||||
if (typeof pref === 'object' && !('length' in pref)) {
|
||||
allowEmpty = Boolean(pref.allowEmpty)
|
||||
pref = pref.flags
|
||||
}
|
||||
|
||||
if (typeof preferred === 'object' && 'length' in preferred) {
|
||||
flags = String(preferred).split(',');
|
||||
if (typeof pref === 'object' && 'length' in pref) {
|
||||
flags = String(pref).split(',')
|
||||
}
|
||||
|
||||
visit(ast, 'code', visitor);
|
||||
visit(tree, 'code', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var value = contents.slice(start(node).offset, end(node).offset);
|
||||
var value
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
if (node.lang) {
|
||||
if (flags.length !== 0 && flags.indexOf(node.lang) === -1) {
|
||||
file.message(reasonInvalid, node)
|
||||
}
|
||||
} else {
|
||||
value = contents.slice(start(node).offset, end(node).offset)
|
||||
|
||||
if (node.lang) {
|
||||
if (flags.length !== 0 && flags.indexOf(node.lang) === -1) {
|
||||
file.message('Invalid code-language flag', node);
|
||||
if (!allowEmpty && fence.test(value)) {
|
||||
file.message(reasonMissing, node)
|
||||
}
|
||||
}
|
||||
} else if (/^ {0,3}([~`])\1{2,}/.test(value) && !allowEmpty) {
|
||||
file.message('Missing code-language flag', node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,52 +66,58 @@
|
||||
* 1:1: Invalid fenced code marker `!`: use either `'consistent'`, `` '`' ``, or `'~'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:fenced-code-marker', fencedCodeMarker);
|
||||
module.exports = rule('remark-lint:fenced-code-marker', fencedCodeMarker)
|
||||
|
||||
var MARKERS = {
|
||||
var markers = {
|
||||
'`': true,
|
||||
'~': true,
|
||||
null: true
|
||||
};
|
||||
}
|
||||
|
||||
function fencedCodeMarker(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function fencedCodeMarker(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (MARKERS[preferred] !== true) {
|
||||
file.fail('Invalid fenced code marker `' + preferred + '`: use either `\'consistent\'`, `` \'`\' ``, or `\'~\'`');
|
||||
if (markers[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid fenced code marker `' +
|
||||
pref +
|
||||
"`: use either `'consistent'`, `` '`' ``, or `'~'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'code', visitor);
|
||||
visit(tree, 'code', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var marker = contents.substr(position.start(node).offset, 4);
|
||||
var marker
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
marker = contents
|
||||
.substr(position.start(node).offset, 4)
|
||||
.trimLeft()
|
||||
.charAt(0)
|
||||
|
||||
marker = marker.trimLeft().charAt(0);
|
||||
|
||||
/* Ignore unfenced code blocks. */
|
||||
if (MARKERS[marker] !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preferred) {
|
||||
if (marker !== preferred) {
|
||||
file.message('Fenced code should use ' + preferred + ' as a marker', node);
|
||||
/* Ignore unfenced code blocks. */
|
||||
if (markers[marker] === true) {
|
||||
if (pref) {
|
||||
if (marker !== pref) {
|
||||
file.message(
|
||||
'Fenced code should use ' + pref + ' as a marker',
|
||||
node
|
||||
)
|
||||
}
|
||||
} else {
|
||||
pref = marker
|
||||
}
|
||||
}
|
||||
} else {
|
||||
preferred = marker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,22 +22,18 @@
|
||||
* @example {"name": "readme.mkd", "setting": "mkd"}
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:file-extension', fileExtension);
|
||||
module.exports = rule('remark-lint:file-extension', fileExtension)
|
||||
|
||||
function fileExtension(ast, file, preferred) {
|
||||
var ext = file.extname;
|
||||
function fileExtension(tree, file, pref) {
|
||||
var ext = file.extname
|
||||
|
||||
if (ext) {
|
||||
ext = ext.slice(1);
|
||||
}
|
||||
pref = typeof pref === 'string' ? pref : 'md'
|
||||
|
||||
preferred = typeof preferred === 'string' ? preferred : 'md';
|
||||
|
||||
if (ext && ext !== preferred) {
|
||||
file.message('Invalid extension: use `' + preferred + '`');
|
||||
if (ext && ext.slice(1) !== pref) {
|
||||
file.message('Invalid extension: use `' + pref + '`')
|
||||
}
|
||||
}
|
||||
|
@ -25,36 +25,41 @@
|
||||
* 3:1-3:47: Move definitions to the end of the file (after the node at line `5`)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:final-definition', finalDefinition);
|
||||
module.exports = rule('remark-lint:final-definition', finalDefinition)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
function finalDefinition(ast, file) {
|
||||
var last = null;
|
||||
function finalDefinition(tree, file) {
|
||||
var last = null
|
||||
|
||||
visit(ast, visitor, true);
|
||||
visit(tree, visitor, true)
|
||||
|
||||
function visitor(node) {
|
||||
var line = start(node).line;
|
||||
var line = start(node).line
|
||||
|
||||
/* Ignore generated nodes. */
|
||||
if (node.type === 'root' || generated(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (node.type === 'definition') {
|
||||
if (last !== null && last > line) {
|
||||
file.message('Move definitions to the end of the file (after the node at line `' + last + '`)', node);
|
||||
file.message(
|
||||
'Move definitions to the end of the file (after the node at line `' +
|
||||
last +
|
||||
'`)',
|
||||
node
|
||||
)
|
||||
}
|
||||
} else if (last === null) {
|
||||
last = line;
|
||||
last = line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,17 +49,17 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:final-newline', finalNewline);
|
||||
module.exports = rule('remark-lint:final-newline', finalNewline)
|
||||
|
||||
function finalNewline(ast, file) {
|
||||
var contents = file.toString();
|
||||
var last = contents.length - 1;
|
||||
function finalNewline(tree, file) {
|
||||
var contents = String(file)
|
||||
var last = contents.length - 1
|
||||
|
||||
if (last > -1 && contents.charAt(last) !== '\n') {
|
||||
file.message('Missing newline character at end of file');
|
||||
file.message('Missing newline character at end of file')
|
||||
}
|
||||
}
|
||||
|
@ -77,45 +77,43 @@
|
||||
* 1:1-1:14: First heading level should be `2`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:first-heading-level', firstHeadingLevel);
|
||||
module.exports = rule('remark-lint:first-heading-level', firstHeadingLevel)
|
||||
|
||||
var re = /<h([1-6])/;
|
||||
var re = /<h([1-6])/
|
||||
|
||||
function firstHeadingLevel(tree, file, preferred) {
|
||||
var style = preferred && preferred !== true ? preferred : 1;
|
||||
function firstHeadingLevel(tree, file, pref) {
|
||||
var style = pref && pref !== true ? pref : 1
|
||||
|
||||
visit(tree, visitor);
|
||||
visit(tree, visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var depth;
|
||||
var depth
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type === 'heading') {
|
||||
depth = node.depth;
|
||||
} else if (node.type === 'html') {
|
||||
depth = infer(node);
|
||||
}
|
||||
|
||||
if (depth !== undefined) {
|
||||
if (depth !== style) {
|
||||
file.message('First heading level should be `' + style + '`', node);
|
||||
if (!generated(node)) {
|
||||
if (node.type === 'heading') {
|
||||
depth = node.depth
|
||||
} else if (node.type === 'html') {
|
||||
depth = infer(node)
|
||||
}
|
||||
|
||||
return false;
|
||||
if (depth !== undefined) {
|
||||
if (depth !== style) {
|
||||
file.message('First heading level should be `' + style + '`', node)
|
||||
}
|
||||
|
||||
return visit.EXIT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function infer(node) {
|
||||
var results = node.value.match(re);
|
||||
return results ? Number(results[1]) : undefined;
|
||||
var results = node.value.match(re)
|
||||
return results ? Number(results[1]) : undefined
|
||||
}
|
||||
|
@ -21,33 +21,34 @@
|
||||
* 1:12-2:1: Use two spaces for hard line breaks
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:hard-break-spaces', hardBreakSpaces);
|
||||
module.exports = rule('remark-lint:hard-break-spaces', hardBreakSpaces)
|
||||
|
||||
function hardBreakSpaces(ast, file) {
|
||||
var contents = file.toString();
|
||||
var reason = 'Use two spaces for hard line breaks'
|
||||
|
||||
visit(ast, 'break', visitor);
|
||||
function hardBreakSpaces(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'break', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var start = position.start(node).offset;
|
||||
var end = position.end(node).offset;
|
||||
var value;
|
||||
var value
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
value = contents
|
||||
.slice(position.start(node).offset, position.end(node).offset)
|
||||
.split('\n', 1)[0]
|
||||
.replace(/\r$/, '')
|
||||
|
||||
value = contents.slice(start, end).split('\n', 1)[0].replace(/\r$/, '');
|
||||
|
||||
if (value.length > 2) {
|
||||
file.message('Use two spaces for hard line breaks', node);
|
||||
if (value.length > 2) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,30 +23,32 @@
|
||||
* 3:1-3:10: Heading levels should increment by one level at a time
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:heading-increment', headingIncrement);
|
||||
module.exports = rule('remark-lint:heading-increment', headingIncrement)
|
||||
|
||||
function headingIncrement(ast, file) {
|
||||
var prev = null;
|
||||
var reason = 'Heading levels should increment by one level at a time'
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
function headingIncrement(tree, file) {
|
||||
var prev = null
|
||||
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var depth = node.depth;
|
||||
var depth
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
depth = node.depth
|
||||
|
||||
if (prev && depth > prev + 1) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
|
||||
prev = depth
|
||||
}
|
||||
|
||||
if (prev && depth > prev + 1) {
|
||||
file.message('Heading levels should increment by one level at a time', node);
|
||||
}
|
||||
|
||||
prev = depth;
|
||||
}
|
||||
}
|
||||
|
@ -64,33 +64,31 @@
|
||||
* 6:1-6:13: Headings should use setext
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var style = require('mdast-util-heading-style');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var style = require('mdast-util-heading-style')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:heading-style', headingStyle);
|
||||
module.exports = rule('remark-lint:heading-style', headingStyle)
|
||||
|
||||
var TYPES = ['atx', 'atx-closed', 'setext'];
|
||||
var types = ['atx', 'atx-closed', 'setext']
|
||||
|
||||
function headingStyle(ast, file, preferred) {
|
||||
preferred = TYPES.indexOf(preferred) === -1 ? null : preferred;
|
||||
function headingStyle(tree, file, pref) {
|
||||
pref = types.indexOf(pref) === -1 ? null : pref
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preferred) {
|
||||
if (style(node, preferred) !== preferred) {
|
||||
file.message('Headings should use ' + preferred, node);
|
||||
if (!generated(node)) {
|
||||
if (pref) {
|
||||
if (style(node, pref) !== pref) {
|
||||
file.message('Headings should use ' + pref, node)
|
||||
}
|
||||
} else {
|
||||
pref = style(node, pref)
|
||||
}
|
||||
} else {
|
||||
preferred = style(node, preferred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,46 +45,49 @@
|
||||
* 1:6: Expected linebreaks to be windows (`\r\n`), not unix (`\n`)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var location = require('vfile-location');
|
||||
var rule = require('unified-lint-rule')
|
||||
var location = require('vfile-location')
|
||||
|
||||
module.exports = rule('remark-lint:linebreak-style', linebreakStyle);
|
||||
module.exports = rule('remark-lint:linebreak-style', linebreakStyle)
|
||||
|
||||
var sequences = {
|
||||
unix: '\n',
|
||||
windows: '\r\n'
|
||||
};
|
||||
var sequences = {unix: '\n', windows: '\r\n'}
|
||||
var escaped = {unix: '\\n', windows: '\\r\\n'}
|
||||
var types = {true: 'windows', false: 'unix'}
|
||||
|
||||
var escaped = {
|
||||
unix: '\\n',
|
||||
windows: '\\r\\n'
|
||||
};
|
||||
function linebreakStyle(tree, file, pref) {
|
||||
var content = String(file)
|
||||
var position = location(content).toPosition
|
||||
var index = content.indexOf('\n')
|
||||
var type
|
||||
var reason
|
||||
|
||||
function linebreakStyle(ast, file, preferred) {
|
||||
var content = String(file);
|
||||
var position = location(content).toPosition;
|
||||
var index = content.indexOf('\n');
|
||||
var type;
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
while (index !== -1) {
|
||||
type = content.charAt(index - 1) === '\r' ? 'windows' : 'unix';
|
||||
type = types[content.charAt(index - 1) === '\r']
|
||||
|
||||
if (preferred) {
|
||||
if (sequences[preferred] !== sequences[type]) {
|
||||
file.message(
|
||||
'Expected linebreaks to be ' + preferred + ' (`' + escaped[preferred] + '`), ' +
|
||||
'not ' + type + ' (`' + escaped[type] + '`)',
|
||||
position(index)
|
||||
);
|
||||
if (pref) {
|
||||
if (sequences[pref] !== sequences[type]) {
|
||||
reason =
|
||||
'Expected linebreaks to be ' +
|
||||
pref +
|
||||
' (`' +
|
||||
escaped[pref] +
|
||||
'`), ' +
|
||||
'not ' +
|
||||
type +
|
||||
' (`' +
|
||||
escaped[type] +
|
||||
'`)'
|
||||
|
||||
file.message(reason, position(index))
|
||||
}
|
||||
} else {
|
||||
preferred = type;
|
||||
pref = type
|
||||
}
|
||||
|
||||
index = content.indexOf('\n', index + 1);
|
||||
index = content.indexOf('\n', index + 1)
|
||||
}
|
||||
}
|
||||
|
@ -67,77 +67,75 @@
|
||||
* 1:1: Invalid link title style marker `.`: use either `'consistent'`, `'"'`, `'\''`, or `'()'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var vfileLocation = require('vfile-location');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var vfileLocation = require('vfile-location')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:link-title-style', linkTitleStyle);
|
||||
module.exports = rule('remark-lint:link-title-style', linkTitleStyle)
|
||||
|
||||
var end = position.end;
|
||||
var end = position.end
|
||||
|
||||
var MARKERS = {
|
||||
'"': true,
|
||||
'\'': true,
|
||||
')': true,
|
||||
null: true
|
||||
};
|
||||
var markers = {'"': true, "'": true, ')': true, null: true}
|
||||
|
||||
function linkTitleStyle(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
var location = vfileLocation(file);
|
||||
function linkTitleStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
var location = vfileLocation(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
pref = pref === '()' || pref === '(' ? ')' : pref
|
||||
|
||||
if (preferred === '()' || preferred === '(') {
|
||||
preferred = ')';
|
||||
if (markers[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid link title style marker `' +
|
||||
pref +
|
||||
"`: use either `'consistent'`, `'\"'`, `'\\''`, or `'()'`"
|
||||
)
|
||||
}
|
||||
|
||||
if (MARKERS[preferred] !== true) {
|
||||
file.fail('Invalid link title style marker `' + preferred + '`: use either `\'consistent\'`, `\'"\'`, `\'\\\'\'`, or `\'()\'`');
|
||||
}
|
||||
|
||||
visit(ast, 'link', validate);
|
||||
visit(ast, 'image', validate);
|
||||
visit(ast, 'definition', validate);
|
||||
visit(tree, ['link', 'image', 'definition'], validate)
|
||||
|
||||
function validate(node) {
|
||||
var last = end(node).offset - 1;
|
||||
var character;
|
||||
var pos;
|
||||
var last = end(node).offset - 1
|
||||
var character
|
||||
var reason
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (node.type !== 'definition') {
|
||||
last--;
|
||||
last--
|
||||
}
|
||||
|
||||
while (last) {
|
||||
character = contents.charAt(last);
|
||||
character = contents.charAt(last)
|
||||
|
||||
/* istanbul ignore if - remark before 8.0.0 */
|
||||
if (/\s/.test(character)) {
|
||||
last--;
|
||||
last--
|
||||
} else {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Not a title. */
|
||||
if (!(character in MARKERS)) {
|
||||
return;
|
||||
}
|
||||
/* Skip non-titles. */
|
||||
if (character in markers) {
|
||||
if (pref) {
|
||||
if (pref !== character) {
|
||||
reason =
|
||||
'Titles should use `' +
|
||||
(pref === ')' ? '()' : pref) +
|
||||
'` as a quote'
|
||||
|
||||
if (!preferred) {
|
||||
preferred = character;
|
||||
} else if (preferred !== character) {
|
||||
pos = location.toPosition(last + 1);
|
||||
file.message('Titles should use `' + (preferred === ')' ? '()' : preferred) + '` as a quote', pos);
|
||||
file.message(reason, location.toPosition(last + 1))
|
||||
}
|
||||
} else {
|
||||
pref = character
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,48 +34,52 @@
|
||||
* 4:3: Incorrect indentation before bullet: remove 1 space
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:list-item-bullet-indent', listItemBulletIndent);
|
||||
module.exports = rule(
|
||||
'remark-lint:list-item-bullet-indent',
|
||||
listItemBulletIndent
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
function listItemBulletIndent(ast, file) {
|
||||
var contents = file.toString();
|
||||
function listItemBulletIndent(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
|
||||
items.forEach(visitItems);
|
||||
node.children.forEach(visitItems)
|
||||
}
|
||||
|
||||
function visitItems(item) {
|
||||
var head = item.children[0];
|
||||
var initial = start(item).offset;
|
||||
var final = start(head).offset;
|
||||
var indent;
|
||||
var final
|
||||
var indent
|
||||
var reason
|
||||
|
||||
if (generated(item)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(item)) {
|
||||
final = start(item.children[0])
|
||||
indent = contents.slice(start(item).offset, final.offset).match(/^\s*/)[0]
|
||||
.length
|
||||
|
||||
indent = contents.slice(initial, final).match(/^\s*/)[0].length;
|
||||
if (indent !== 0) {
|
||||
reason =
|
||||
'Incorrect indentation before bullet: remove ' +
|
||||
indent +
|
||||
' ' +
|
||||
plural('space', indent)
|
||||
|
||||
if (indent !== 0) {
|
||||
initial = start(head);
|
||||
|
||||
file.message('Incorrect indentation before bullet: remove ' + indent + ' ' + plural('space', indent), {
|
||||
line: initial.line,
|
||||
column: initial.column - indent
|
||||
});
|
||||
file.message(reason, {
|
||||
line: final.line,
|
||||
column: final.column - indent
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,39 +21,46 @@
|
||||
* 2:5: Don’t use mixed indentation for children, remove 1 space
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:list-item-content-indent', listItemContentIndent);
|
||||
module.exports = rule(
|
||||
'remark-lint:list-item-content-indent',
|
||||
listItemContentIndent
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
function listItemContentIndent(ast, file) {
|
||||
var contents = file.toString();
|
||||
function listItemContentIndent(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(ast, 'listItem', visitor);
|
||||
visit(tree, 'listItem', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var style;
|
||||
var style
|
||||
|
||||
node.children.forEach(visitItem);
|
||||
node.children.forEach(visitItem)
|
||||
|
||||
function visitItem(item, index) {
|
||||
var begin = start(item);
|
||||
var column = begin.column;
|
||||
var char;
|
||||
var diff;
|
||||
var word;
|
||||
var begin
|
||||
var column
|
||||
var char
|
||||
var diff
|
||||
var absDiff
|
||||
var reason
|
||||
|
||||
if (generated(item)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
begin = start(item)
|
||||
column = begin.column
|
||||
|
||||
/* Get indentation for the first child.
|
||||
* Only the first item can have a checkbox,
|
||||
* so here we remove that from the column. */
|
||||
@ -61,37 +68,39 @@ function listItemContentIndent(ast, file) {
|
||||
/* If there’s a checkbox before the content,
|
||||
* look backwards to find the start of that
|
||||
* checkbox. */
|
||||
if (Boolean(node.checked) === node.checked) {
|
||||
char = begin.offset - 1;
|
||||
if (typeof node.checked === 'boolean') {
|
||||
char = begin.offset - 1
|
||||
|
||||
while (contents.charAt(char) !== '[') {
|
||||
char--;
|
||||
char--
|
||||
}
|
||||
|
||||
column -= begin.offset - char;
|
||||
column -= begin.offset - char
|
||||
}
|
||||
|
||||
style = column;
|
||||
style = column
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/* Warn for violating children. */
|
||||
if (column !== style) {
|
||||
diff = style - column;
|
||||
/* istanbul ignore next - hard to test, I couldn’t find it at least. */
|
||||
word = diff > 0 ? 'add' : 'remove';
|
||||
diff = style - column
|
||||
absDiff = Math.abs(diff)
|
||||
|
||||
diff = Math.abs(diff);
|
||||
reason =
|
||||
'Don’t use mixed indentation for children, ' +
|
||||
/* istanbul ignore next - hard to test, I couldn’t find it at least. */
|
||||
(diff > 0 ? 'add' : 'remove') +
|
||||
' ' +
|
||||
absDiff +
|
||||
' ' +
|
||||
plural('space', absDiff)
|
||||
|
||||
file.message(
|
||||
'Don’t use mixed indentation for children, ' + word +
|
||||
' ' + diff + ' ' + plural('space', diff),
|
||||
{
|
||||
line: start(item).line,
|
||||
column: column
|
||||
}
|
||||
);
|
||||
file.message(reason, {
|
||||
line: start(item).line,
|
||||
column: column
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,82 +101,76 @@
|
||||
* 1:1: Invalid list-item indent style `invalid`: use either `'tab-size'`, `'space'`, or `'mixed'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:list-item-indent', listItemIndent);
|
||||
module.exports = rule('remark-lint:list-item-indent', listItemIndent)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
var STYLES = {
|
||||
'tab-size': true,
|
||||
mixed: true,
|
||||
space: true
|
||||
};
|
||||
var styles = {'tab-size': true, mixed: true, space: true}
|
||||
|
||||
function listItemIndent(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function listItemIndent(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred === 'string' ? preferred : 'tab-size';
|
||||
pref = typeof pref === 'string' ? pref : 'tab-size'
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid list-item indent style `' + preferred + '`: use either `\'tab-size\'`, `\'space\'`, or `\'mixed\'`');
|
||||
if (styles[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid list-item indent style `' +
|
||||
pref +
|
||||
"`: use either `'tab-size'`, `'space'`, or `'mixed'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
var loose = node.loose
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
node.children.forEach(visitItem)
|
||||
}
|
||||
|
||||
items.forEach(visitItem);
|
||||
|
||||
function visitItem(item) {
|
||||
var head = item.children[0];
|
||||
var initial = start(item).offset;
|
||||
var final = start(head).offset;
|
||||
var bulletSize;
|
||||
var tab;
|
||||
var marker;
|
||||
var shouldBe;
|
||||
var diff;
|
||||
var word;
|
||||
var head = item.children[0]
|
||||
var final = start(head)
|
||||
var marker
|
||||
var bulletSize
|
||||
var style
|
||||
var diff
|
||||
var absDiff
|
||||
var reason
|
||||
|
||||
marker = contents.slice(initial, final);
|
||||
marker = contents
|
||||
.slice(start(item).offset, final.offset)
|
||||
.replace(/\[[x ]?]\s*$/i, '')
|
||||
|
||||
/* Support checkboxes. */
|
||||
marker = marker.replace(/\[[x ]?]\s*$/i, '');
|
||||
bulletSize = marker.trimRight().length
|
||||
|
||||
bulletSize = marker.trimRight().length;
|
||||
tab = Math.ceil(bulletSize / 4) * 4;
|
||||
style =
|
||||
pref === 'tab-size' || (pref === 'mixed' && loose)
|
||||
? Math.ceil(bulletSize / 4) * 4
|
||||
: bulletSize + 1
|
||||
|
||||
if (preferred === 'tab-size') {
|
||||
shouldBe = tab;
|
||||
} else if (preferred === 'space') {
|
||||
shouldBe = bulletSize + 1;
|
||||
} else {
|
||||
shouldBe = node.loose ? tab : bulletSize + 1;
|
||||
}
|
||||
if (marker.length !== style) {
|
||||
diff = style - marker.length
|
||||
absDiff = Math.abs(diff)
|
||||
|
||||
if (marker.length !== shouldBe) {
|
||||
diff = shouldBe - marker.length;
|
||||
word = diff > 0 ? 'add' : 'remove';
|
||||
reason =
|
||||
'Incorrect list-item indent: ' +
|
||||
(diff > 0 ? 'add' : 'remove') +
|
||||
' ' +
|
||||
absDiff +
|
||||
' ' +
|
||||
plural('space', absDiff)
|
||||
|
||||
diff = Math.abs(diff);
|
||||
|
||||
file.message(
|
||||
'Incorrect list-item indent: ' + word +
|
||||
' ' + diff + ' ' + plural('space', diff),
|
||||
start(head)
|
||||
);
|
||||
file.message(reason, final)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,103 +107,90 @@
|
||||
* 15:1-16:1: Extraneous new line after list item
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:list-item-spacing', listItemSpacing);
|
||||
module.exports = rule('remark-lint:list-item-spacing', listItemSpacing)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function listItemSpacing(ast, file, preferred) {
|
||||
var blanks = Boolean(
|
||||
preferred &&
|
||||
typeof preferred === 'object' &&
|
||||
preferred.checkBlanks
|
||||
);
|
||||
var reasonLoose = 'Missing new line after list item'
|
||||
var reasonTight = 'Extraneous new line after list item'
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
function listItemSpacing(tree, file, pref) {
|
||||
var blanks = pref && typeof pref === 'object' && Boolean(pref.checkBlanks)
|
||||
var fn = blanks ? inferBlankLine : inferMultiline
|
||||
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
var isTightList = true;
|
||||
var indent = start(node).column;
|
||||
var type;
|
||||
var tight = true
|
||||
var indent
|
||||
var children
|
||||
var length
|
||||
var index
|
||||
var child
|
||||
var next
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.forEach(infer);
|
||||
|
||||
type = isTightList ? 'tight' : 'loose';
|
||||
|
||||
items.forEach(warn);
|
||||
|
||||
function infer(item) {
|
||||
var fn = blanks ? inferBlankLine : inferMultiline;
|
||||
|
||||
if (fn(item)) {
|
||||
isTightList = false;
|
||||
}
|
||||
}
|
||||
|
||||
function inferBlankLine(item) {
|
||||
var children = item.children;
|
||||
var length = children.length;
|
||||
var index = 0;
|
||||
var child = children[index];
|
||||
var next;
|
||||
if (!generated(node)) {
|
||||
children = node.children
|
||||
length = children.length
|
||||
index = -1
|
||||
|
||||
while (++index < length) {
|
||||
next = children[index];
|
||||
|
||||
/* All children in `listItem`s are block. */
|
||||
if ((start(next).line - end(child).line) > 1) {
|
||||
return true;
|
||||
if (fn(children[index])) {
|
||||
tight = false
|
||||
break
|
||||
}
|
||||
|
||||
child = next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
indent = start(node).column
|
||||
child = children[0]
|
||||
index = 0
|
||||
|
||||
function inferMultiline(item) {
|
||||
var content = item.children;
|
||||
var head = content[0];
|
||||
var tail = content[content.length - 1];
|
||||
return (end(tail).line - start(head).line) > 0;
|
||||
}
|
||||
while (++index < length) {
|
||||
next = children[index]
|
||||
|
||||
function warn(item, index) {
|
||||
var next = items[index + 1];
|
||||
var isTight = end(item).column > indent;
|
||||
|
||||
/* Ignore last. */
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the list item's state does (not)
|
||||
* match the list's state. */
|
||||
if (isTight !== isTightList) {
|
||||
if (type === 'loose') {
|
||||
file.message('Missing new line after list item', {
|
||||
start: end(item),
|
||||
if (end(child).column > indent !== tight) {
|
||||
file.message(tight ? reasonTight : reasonLoose, {
|
||||
start: end(child),
|
||||
end: start(next)
|
||||
});
|
||||
} else {
|
||||
file.message('Extraneous new line after list item', {
|
||||
start: end(item),
|
||||
end: start(next)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
child = next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inferBlankLine(node) {
|
||||
var children = node.children
|
||||
var child = children[0]
|
||||
var length = children.length
|
||||
var index = 0
|
||||
var next
|
||||
|
||||
while (++index < length) {
|
||||
next = children[index]
|
||||
|
||||
/* All children in `listItem`s are block. */
|
||||
if (start(next).line - end(child).line > 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
child = next
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function inferMultiline(node) {
|
||||
var children = node.children
|
||||
return end(children[children.length - 1]).line - start(children[0]).line > 0
|
||||
}
|
||||
|
@ -25,27 +25,26 @@
|
||||
* 1:1-1:52: Use headings shorter than `40`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:maximum-heading-length', maximumHeadingLength);
|
||||
module.exports = rule(
|
||||
'remark-lint:maximum-heading-length',
|
||||
maximumHeadingLength
|
||||
)
|
||||
|
||||
function maximumHeadingLength(ast, file, preferred) {
|
||||
preferred = isNaN(preferred) || typeof preferred !== 'number' ? 60 : preferred;
|
||||
function maximumHeadingLength(tree, file, pref) {
|
||||
pref = typeof pref === 'number' && !isNaN(pref) ? pref : 60
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toString(node).length > preferred) {
|
||||
file.message('Use headings shorter than `' + preferred + '`', node);
|
||||
if (!generated(node) && toString(node).length > pref) {
|
||||
file.message('Use headings shorter than `' + pref + '`', node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,63 +83,38 @@
|
||||
* 4:12: Line must be at most 10 characters
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:maximum-line-length', maximumLineLength);
|
||||
module.exports = rule('remark-lint:maximum-line-length', maximumLineLength)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function maximumLineLength(ast, file, preferred) {
|
||||
var style = preferred && preferred !== true ? preferred : 80;
|
||||
var content = file.toString();
|
||||
var matrix = content.split(/\r?\n/);
|
||||
var index = -1;
|
||||
var length = matrix.length;
|
||||
var lineLength;
|
||||
function maximumLineLength(tree, file, pref) {
|
||||
var style = typeof pref === 'number' && !isNaN(pref) ? pref : 80
|
||||
var content = String(file)
|
||||
var lines = content.split(/\r?\n/)
|
||||
var length = lines.length
|
||||
var index = -1
|
||||
var lineLength
|
||||
|
||||
/* Next, white list nodes which cannot be wrapped. */
|
||||
visit(ast, ignore);
|
||||
visit(tree, ['heading', 'table', 'code', 'definition'], ignore)
|
||||
visit(tree, ['link', 'image'], validateLink)
|
||||
|
||||
visit(ast, 'link', validateLink);
|
||||
visit(ast, 'image', validateLink);
|
||||
|
||||
/* Iterate over every line, and warn for
|
||||
* violating lines. */
|
||||
/* Iterate over every line, and warn for violating lines. */
|
||||
while (++index < length) {
|
||||
lineLength = matrix[index].length;
|
||||
lineLength = lines[index].length
|
||||
|
||||
if (lineLength > style) {
|
||||
file.message('Line must be at most ' + style + ' characters', {
|
||||
line: index + 1,
|
||||
column: lineLength + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function ignore(node) {
|
||||
var applicable = isIgnored(node);
|
||||
var initial = applicable && start(node).line;
|
||||
var final = applicable && end(node).line;
|
||||
|
||||
if (!applicable || generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
whitelist(initial - 1, final);
|
||||
}
|
||||
|
||||
/* Whitelist from `initial` to `final`, zero-based. */
|
||||
function whitelist(initial, final) {
|
||||
initial--;
|
||||
|
||||
while (++initial < final) {
|
||||
matrix[initial] = '';
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,21 +123,21 @@ function maximumLineLength(ast, file, preferred) {
|
||||
* there’s white-space after it, they are not
|
||||
* whitelisted. */
|
||||
function validateLink(node, pos, parent) {
|
||||
var next = parent.children[pos + 1];
|
||||
var initial = start(node);
|
||||
var final = end(node);
|
||||
var next = parent.children[pos + 1]
|
||||
var initial
|
||||
var final
|
||||
|
||||
/* Nothing to whitelist when generated. */
|
||||
/* istanbul ignore if - Hard to test, as we only run this
|
||||
* case on `position: true` */
|
||||
/* istanbul ignore if - Nothing to whitelist when generated. */
|
||||
if (generated(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/* No whitelisting when starting after the border,
|
||||
* or ending before it. */
|
||||
initial = start(node)
|
||||
final = end(node)
|
||||
|
||||
/* No whitelisting when starting after the border, or ending before it. */
|
||||
if (initial.column > style || final.column < style) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/* No whitelisting when there’s white-space after
|
||||
@ -172,18 +147,23 @@ function maximumLineLength(ast, file, preferred) {
|
||||
start(next).line === initial.line &&
|
||||
(!next.value || /^(.+?[ \t].+?)/.test(next.value))
|
||||
) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
whitelist(initial.line - 1, final.line);
|
||||
whitelist(initial.line - 1, final.line)
|
||||
}
|
||||
|
||||
function ignore(node) {
|
||||
/* istanbul ignore else - Hard to test, as we only run this case on `position: true` */
|
||||
if (!generated(node)) {
|
||||
whitelist(start(node).line - 1, end(node).line)
|
||||
}
|
||||
}
|
||||
|
||||
/* Whitelist from `initial` to `final`, zero-based. */
|
||||
function whitelist(initial, final) {
|
||||
while (initial < final) {
|
||||
lines[initial++] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if `node` is applicable, as in, if it should be
|
||||
* ignored. */
|
||||
function isIgnored(node) {
|
||||
return node.type === 'heading' ||
|
||||
node.type === 'table' ||
|
||||
node.type === 'code' ||
|
||||
node.type === 'definition';
|
||||
}
|
||||
|
@ -31,38 +31,44 @@
|
||||
* 1:1-1:14: All automatic links must start with a protocol
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-auto-link-without-protocol', noAutoLinkWithoutProtocol);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-auto-link-without-protocol',
|
||||
noAutoLinkWithoutProtocol
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
/* Protocol expression. See:
|
||||
* http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax */
|
||||
var PROTOCOL = /^[a-z][a-z+.-]+:\/?/i;
|
||||
var protocol = /^[a-z][a-z+.-]+:\/?/i
|
||||
|
||||
function noAutoLinkWithoutProtocol(ast, file) {
|
||||
visit(ast, 'link', visitor);
|
||||
var reason = 'All automatic links must start with a protocol'
|
||||
|
||||
function noAutoLinkWithoutProtocol(tree, file) {
|
||||
visit(tree, 'link', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var head = start(node.children[0]).column;
|
||||
var tail = end(node.children[node.children.length - 1]).column;
|
||||
var initial = start(node).column;
|
||||
var final = end(node).column;
|
||||
var children
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
children = node.children
|
||||
|
||||
if (initial === head - 1 && final === tail + 1 && !PROTOCOL.test(toString(node))) {
|
||||
file.message('All automatic links must start with a protocol', node);
|
||||
if (
|
||||
start(node).column === start(children[0]).column - 1 &&
|
||||
end(node).column === end(children[children.length - 1]).column + 1 &&
|
||||
!protocol.test(toString(node))
|
||||
) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,58 +31,64 @@
|
||||
* 2:1: Missing marker in blockquote
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var vfileLocation = require('vfile-location');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var vfileLocation = require('vfile-location')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-blockquote-without-marker', noBlockquoteWithoutMarker);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-blockquote-without-marker',
|
||||
noBlockquoteWithoutMarker
|
||||
)
|
||||
|
||||
function noBlockquoteWithoutMarker(ast, file) {
|
||||
var contents = file.toString();
|
||||
var location = vfileLocation(file);
|
||||
var last = contents.length;
|
||||
var reason = 'Missing marker in blockquote'
|
||||
|
||||
visit(ast, 'blockquote', visitor);
|
||||
function noBlockquoteWithoutMarker(tree, file) {
|
||||
var contents = String(file)
|
||||
var location = vfileLocation(file)
|
||||
var last = contents.length
|
||||
|
||||
visit(tree, 'blockquote', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var start = position.start(node).line;
|
||||
var indent = node.position && node.position.indent;
|
||||
var indent = node.position && node.position.indent
|
||||
var start
|
||||
var length
|
||||
var index
|
||||
var line
|
||||
var offset
|
||||
var character
|
||||
var pos
|
||||
|
||||
if (generated(node) || !indent || indent.length === 0) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
indent.forEach(eachLine);
|
||||
start = position.start(node).line
|
||||
length = indent.length
|
||||
index = -1
|
||||
|
||||
function eachLine(column, n) {
|
||||
var character;
|
||||
var line = start + n + 1;
|
||||
var offset = location.toOffset({
|
||||
line: line,
|
||||
column: column
|
||||
}) - 1;
|
||||
while (++index < length) {
|
||||
line = start + index + 1
|
||||
pos = {line: line, column: indent[index]}
|
||||
offset = location.toOffset(pos) - 1
|
||||
|
||||
while (++offset < last) {
|
||||
character = contents.charAt(offset);
|
||||
character = contents.charAt(offset)
|
||||
|
||||
if (character === '>') {
|
||||
return;
|
||||
break
|
||||
}
|
||||
|
||||
/* istanbul ignore else - just for safety */
|
||||
if (character !== ' ' && character !== '\t') {
|
||||
break;
|
||||
file.message(reason, pos)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
file.message('Missing marker in blockquote', {
|
||||
line: line,
|
||||
column: column
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,84 +31,98 @@
|
||||
* ␊
|
||||
* bravo();
|
||||
*
|
||||
* @example {"name": "empty-document.md"}
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "input"}
|
||||
*
|
||||
* Foo...
|
||||
* ␊
|
||||
* ␊
|
||||
* ...Bar.
|
||||
* ...Bar
|
||||
* ␊
|
||||
* ␊
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "output"}
|
||||
*
|
||||
* 4:1: Remove 1 line before node
|
||||
* 4:7: Remove 2 lines after node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-consecutive-blank-lines', noConsecutiveBlankLines);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-consecutive-blank-lines',
|
||||
noConsecutiveBlankLines
|
||||
)
|
||||
|
||||
var MAX = 2;
|
||||
|
||||
function noConsecutiveBlankLines(ast, file) {
|
||||
visit(ast, visitor);
|
||||
function noConsecutiveBlankLines(tree, file) {
|
||||
visit(tree, visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var children = node.children;
|
||||
var head = children && children[0];
|
||||
var tail = children && children[children.length - 1];
|
||||
var children = node.children
|
||||
var head
|
||||
var tail
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node) && children) {
|
||||
head = children[0]
|
||||
|
||||
if (head && !generated(head)) {
|
||||
/* Compare parent and first child. */
|
||||
compare(position.start(node), position.start(head), 0);
|
||||
if (head && !generated(head)) {
|
||||
/* Compare parent and first child. */
|
||||
compare(position.start(node), position.start(head), 0)
|
||||
|
||||
/* Compare between each child. */
|
||||
children.forEach(visitChild);
|
||||
/* Compare between each child. */
|
||||
children.forEach(visitChild)
|
||||
|
||||
/* Compare parent and last child. */
|
||||
if (tail !== head && !generated(tail)) {
|
||||
compare(position.end(node), position.end(tail), 1);
|
||||
tail = children[children.length - 1]
|
||||
|
||||
/* Compare parent and last child. */
|
||||
if (tail !== head && !generated(tail)) {
|
||||
compare(position.end(node), position.end(tail), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function visitChild(child, index) {
|
||||
var prev = children[index - 1];
|
||||
var max = MAX;
|
||||
|
||||
if (!prev || generated(prev) || generated(child)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(prev.type === 'list' && child.type === 'list') ||
|
||||
(child.type === 'code' && prev.type === 'list' && !child.lang)
|
||||
) {
|
||||
max++;
|
||||
}
|
||||
|
||||
compare(position.end(prev), position.start(child), max);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare the difference between `start` and `end`,
|
||||
* and warn when that difference exceeds `max`. */
|
||||
function compare(start, end, max) {
|
||||
var diff = end.line - start.line;
|
||||
var word = diff > 0 ? 'before' : 'after';
|
||||
var diff = end.line - start.line
|
||||
var lines = Math.abs(diff) - max
|
||||
var reason
|
||||
|
||||
diff = Math.abs(diff) - max;
|
||||
if (lines > 0) {
|
||||
reason =
|
||||
'Remove ' +
|
||||
lines +
|
||||
' ' +
|
||||
plural('line', lines) +
|
||||
' ' +
|
||||
(diff > 0 ? 'before' : 'after') +
|
||||
' node'
|
||||
|
||||
if (diff > 0) {
|
||||
file.message('Remove ' + diff + ' ' + plural('line', diff) + ' ' + word + ' node', end);
|
||||
file.message(reason, end)
|
||||
}
|
||||
}
|
||||
|
||||
function visitChild(child, index, all) {
|
||||
var prev = all[index - 1]
|
||||
var max = 2
|
||||
|
||||
if (prev && !generated(prev) && !generated(child)) {
|
||||
if (
|
||||
(prev.type === 'list' && child.type === 'list') ||
|
||||
(child.type === 'code' && prev.type === 'list' && !child.lang)
|
||||
) {
|
||||
max++
|
||||
}
|
||||
|
||||
compare(position.end(prev), position.start(child), max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,12 @@ Paragraph.
|
||||
|
||||
No messages.
|
||||
|
||||
##### `empty-document.md`
|
||||
|
||||
###### Out
|
||||
|
||||
No messages.
|
||||
|
||||
##### `invalid.md`
|
||||
|
||||
###### In
|
||||
@ -69,13 +75,16 @@ Note: `␊` represents a line feed.
|
||||
Foo...
|
||||
␊
|
||||
␊
|
||||
...Bar.
|
||||
...Bar
|
||||
␊
|
||||
␊
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
4:1: Remove 1 line before node
|
||||
4:7: Remove 2 lines after node
|
||||
```
|
||||
|
||||
## Install
|
||||
|
@ -21,39 +21,40 @@
|
||||
* 2:1-2:11: Do not use definitions with the same identifier (1:1)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var rule = require('unified-lint-rule')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
|
||||
module.exports = rule('remark-lint:no-duplicate-definitions', noDuplicateDefinitions);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-duplicate-definitions',
|
||||
noDuplicateDefinitions
|
||||
)
|
||||
|
||||
function noDuplicateDefinitions(ast, file) {
|
||||
var map = {};
|
||||
var reason = 'Do not use definitions with the same identifier'
|
||||
|
||||
visit(ast, 'definition', validate);
|
||||
visit(ast, 'footnoteDefinition', validate);
|
||||
function noDuplicateDefinitions(tree, file) {
|
||||
var map = {}
|
||||
|
||||
visit(tree, ['definition', 'footnoteDefinition'], validate)
|
||||
|
||||
function validate(node) {
|
||||
var duplicate = map[node.identifier];
|
||||
var pos;
|
||||
var identifier
|
||||
var duplicate
|
||||
var pos
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
identifier = node.identifier
|
||||
duplicate = map[identifier]
|
||||
|
||||
if (duplicate && duplicate.type) {
|
||||
pos = position.start(duplicate)
|
||||
file.message(reason + ' (' + pos.line + ':' + pos.column + ')', node)
|
||||
}
|
||||
|
||||
map[identifier] = node
|
||||
}
|
||||
|
||||
if (duplicate && duplicate.type) {
|
||||
pos = position.start(duplicate);
|
||||
|
||||
file.message(
|
||||
'Do not use definitions with the same identifier (' +
|
||||
pos.line + ':' + pos.column + ')',
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
map[node.identifier] = node;
|
||||
}
|
||||
}
|
||||
|
@ -38,39 +38,40 @@
|
||||
* 5:1-5:9: Do not use headings with similar content per section (3:1)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-duplicate-headings-in-section', noDuplicateHeadingsInSection);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-duplicate-headings-in-section',
|
||||
noDuplicateHeadingsInSection
|
||||
)
|
||||
|
||||
var reason = 'Do not use headings with similar content per section'
|
||||
|
||||
function noDuplicateHeadingsInSection(tree, file) {
|
||||
var stack = [{}];
|
||||
var stack = [{}]
|
||||
|
||||
visit(tree, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var depth = node.depth;
|
||||
var siblings = stack[depth - 1] || {};
|
||||
var value = toString(node).toUpperCase();
|
||||
var duplicate = siblings[value];
|
||||
var pos;
|
||||
var depth = node.depth
|
||||
var siblings = stack[depth - 1] || {}
|
||||
var value = toString(node).toUpperCase()
|
||||
var duplicate = siblings[value]
|
||||
var pos
|
||||
|
||||
stack = stack.slice(0, depth);
|
||||
stack[depth] = {};
|
||||
siblings[value] = node;
|
||||
stack = stack.slice(0, depth)
|
||||
stack[depth] = {}
|
||||
siblings[value] = node
|
||||
|
||||
if (!generated(node) && duplicate && duplicate.type === 'heading') {
|
||||
pos = position.start(duplicate);
|
||||
file.message(
|
||||
'Do not use headings with similar content per section (' +
|
||||
pos.line + ':' + pos.column + ')',
|
||||
node
|
||||
);
|
||||
pos = position.start(duplicate)
|
||||
file.message(reason + ' (' + pos.line + ':' + pos.column + ')', node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,40 +26,39 @@
|
||||
* 5:1-5:29: Do not use headings with similar content (3:1)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-duplicate-headings', noDuplicateHeadings);
|
||||
module.exports = rule('remark-lint:no-duplicate-headings', noDuplicateHeadings)
|
||||
|
||||
function noDuplicateHeadings(ast, file) {
|
||||
var map = {};
|
||||
var reason = 'Do not use headings with similar content'
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
function noDuplicateHeadings(tree, file) {
|
||||
var map = {}
|
||||
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var value = toString(node).toUpperCase();
|
||||
var duplicate = map[value];
|
||||
var pos;
|
||||
var value
|
||||
var duplicate
|
||||
var pos
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
value = toString(node).toUpperCase()
|
||||
duplicate = map[value]
|
||||
|
||||
if (duplicate && duplicate.type === 'heading') {
|
||||
pos = position.start(duplicate)
|
||||
|
||||
file.message(reason + ' (' + pos.line + ':' + pos.column + ')', node)
|
||||
}
|
||||
|
||||
map[value] = node
|
||||
}
|
||||
|
||||
if (duplicate && duplicate.type === 'heading') {
|
||||
pos = position.start(duplicate);
|
||||
|
||||
file.message(
|
||||
'Do not use headings with similar content (' +
|
||||
pos.line + ':' + pos.column + ')',
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
map[value] = node;
|
||||
}
|
||||
}
|
||||
|
@ -29,35 +29,33 @@
|
||||
* 5:1-5:8: Don’t use emphasis to introduce a section, use a heading
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-emphasis-as-heading', noEmphasisAsHeading);
|
||||
module.exports = rule('remark-lint:no-emphasis-as-heading', noEmphasisAsHeading)
|
||||
|
||||
function noEmphasisAsHeading(ast, file) {
|
||||
visit(ast, 'paragraph', visitor);
|
||||
var reason = 'Don’t use emphasis to introduce a section, use a heading'
|
||||
|
||||
function noEmphasisAsHeading(tree, file) {
|
||||
visit(tree, 'paragraph', visitor)
|
||||
|
||||
function visitor(node, index, parent) {
|
||||
var children = node.children;
|
||||
var child = children[0];
|
||||
var prev = parent.children[index - 1];
|
||||
var next = parent.children[index + 1];
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
var head = node.children[0]
|
||||
var prev = parent.children[index - 1]
|
||||
var next = parent.children[index + 1]
|
||||
|
||||
if (
|
||||
!generated(node) &&
|
||||
(!prev || prev.type !== 'heading') &&
|
||||
next &&
|
||||
next.type === 'paragraph' &&
|
||||
children.length === 1 &&
|
||||
(child.type === 'emphasis' || child.type === 'strong')
|
||||
node.children.length === 1 &&
|
||||
(head.type === 'emphasis' || head.type === 'strong')
|
||||
) {
|
||||
file.message('Don’t use emphasis to introduce a section, use a heading', node);
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
* [alpha](http://bravo.com).
|
||||
*
|
||||
* ![charlie](http://delta.com/echo.png "foxtrott").
|
||||
*
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "input"}
|
||||
*
|
||||
* [golf]().
|
||||
@ -24,22 +24,20 @@
|
||||
* 3:1-3:11: Don’t use images without URL
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-empty-url', noEmptyURL);
|
||||
|
||||
var types = ['link', 'image'];
|
||||
module.exports = rule('remark-lint:no-empty-url', noEmptyURL)
|
||||
|
||||
function noEmptyURL(tree, file) {
|
||||
visit(tree, visitor);
|
||||
visit(tree, ['link', 'image'], visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (types.indexOf(node.type) !== -1 && !generated(node) && !node.url) {
|
||||
file.message('Don’t use ' + node.type + 's without URL', node);
|
||||
if (!generated(node) && !node.url) {
|
||||
file.message('Don’t use ' + node.type + 's without URL', node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,16 +25,16 @@
|
||||
* 1:1: Do not start file names with `an`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:no-file-name-articles', noFileNameArticles);
|
||||
module.exports = rule('remark-lint:no-file-name-articles', noFileNameArticles)
|
||||
|
||||
function noFileNameArticles(ast, file) {
|
||||
var match = file.stem && file.stem.match(/^(the|teh|an?)\b/i);
|
||||
function noFileNameArticles(tree, file) {
|
||||
var match = file.stem && file.stem.match(/^(the|teh|an?)\b/i)
|
||||
|
||||
if (match) {
|
||||
file.message('Do not start file names with `' + match[0] + '`');
|
||||
file.message('Do not start file names with `' + match[0] + '`')
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,19 @@
|
||||
* 1:1: Do not use consecutive dashes in a file name
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:no-file-name-consecutive-dashes', noFileNameConsecutiveDashes);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-file-name-consecutive-dashes',
|
||||
noFileNameConsecutiveDashes
|
||||
)
|
||||
|
||||
function noFileNameConsecutiveDashes(ast, file) {
|
||||
var reason = 'Do not use consecutive dashes in a file name'
|
||||
|
||||
function noFileNameConsecutiveDashes(tree, file) {
|
||||
if (file.stem && /-{2,}/.test(file.stem)) {
|
||||
file.message('Do not use consecutive dashes in a file name');
|
||||
file.message(reason)
|
||||
}
|
||||
}
|
||||
|
@ -31,23 +31,28 @@
|
||||
* 1:1: Do not use ` ` in a file name
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:no-file-name-irregular-characters', noFileNameIrregularCharacters);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-file-name-irregular-characters',
|
||||
noFileNameIrregularCharacters
|
||||
)
|
||||
|
||||
function noFileNameIrregularCharacters(ast, file, preferred) {
|
||||
var expression = preferred || /[^\\.a-zA-Z0-9-]/;
|
||||
var match;
|
||||
var expression = /[^\\.a-zA-Z0-9-]/
|
||||
|
||||
if (typeof expression === 'string') {
|
||||
expression = new RegExp('[^' + expression + ']');
|
||||
function noFileNameIrregularCharacters(tree, file, pref) {
|
||||
var style = pref || expression
|
||||
var match
|
||||
|
||||
if (typeof style === 'string') {
|
||||
style = new RegExp('[^' + style + ']')
|
||||
}
|
||||
|
||||
match = file.stem && file.stem.match(expression);
|
||||
match = file.stem && file.stem.match(style)
|
||||
|
||||
if (match) {
|
||||
file.message('Do not use `' + match[0] + '` in a file name');
|
||||
file.message('Do not use `' + match[0] + '` in a file name')
|
||||
}
|
||||
}
|
||||
|
@ -16,16 +16,21 @@
|
||||
* 1:1: Do not mix casing in file names
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:no-file-name-mixed-case', noFileNameMixedCase);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-file-name-mixed-case',
|
||||
noFileNameMixedCase
|
||||
)
|
||||
|
||||
function noFileNameMixedCase(ast, file) {
|
||||
var name = file.stem;
|
||||
var reason = 'Do not mix casing in file names'
|
||||
|
||||
function noFileNameMixedCase(tree, file) {
|
||||
var name = file.stem
|
||||
|
||||
if (name && !(name === name.toLowerCase() || name === name.toUpperCase())) {
|
||||
file.message('Do not mix casing in file names');
|
||||
file.message(reason)
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,19 @@
|
||||
* 1:1: Do not use initial or final dashes in a file name
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var rule = require('unified-lint-rule')
|
||||
|
||||
module.exports = rule('remark-lint:no-file-name-outer-dashes', noFileNameOuterDashes);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-file-name-outer-dashes',
|
||||
noFileNameOuterDashes
|
||||
)
|
||||
|
||||
function noFileNameOuterDashes(ast, file) {
|
||||
var reason = 'Do not use initial or final dashes in a file name'
|
||||
|
||||
function noFileNameOuterDashes(tree, file) {
|
||||
if (file.stem && /^-|-$/.test(file.stem)) {
|
||||
file.message('Do not use initial or final dashes in a file name');
|
||||
file.message(reason)
|
||||
}
|
||||
}
|
||||
|
@ -57,75 +57,85 @@
|
||||
* 3:34: Remove 1 space after this heading’s content
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var style = require('mdast-util-heading-style');
|
||||
var plural = require('plur');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var style = require('mdast-util-heading-style')
|
||||
var plural = require('plur')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-heading-content-indent', noHeadingContentIndent);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-heading-content-indent',
|
||||
noHeadingContentIndent
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function noHeadingContentIndent(ast, file) {
|
||||
var contents = file.toString();
|
||||
function noHeadingContentIndent(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var depth = node.depth;
|
||||
var children = node.children;
|
||||
var type = style(node, 'atx');
|
||||
var head;
|
||||
var initial;
|
||||
var final;
|
||||
var diff;
|
||||
var word;
|
||||
var index;
|
||||
var char;
|
||||
var depth
|
||||
var children
|
||||
var type
|
||||
var head
|
||||
var initial
|
||||
var final
|
||||
var diff
|
||||
var absDiff
|
||||
var index
|
||||
var char
|
||||
var reason
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
depth = node.depth
|
||||
children = node.children
|
||||
type = style(node, 'atx')
|
||||
|
||||
if (type === 'atx' || type === 'atx-closed') {
|
||||
initial = start(node);
|
||||
index = initial.offset;
|
||||
char = contents.charAt(index);
|
||||
initial = start(node)
|
||||
index = initial.offset
|
||||
char = contents.charAt(index)
|
||||
|
||||
while (char && char !== '#') {
|
||||
index++;
|
||||
char = contents.charAt(index);
|
||||
char = contents.charAt(++index)
|
||||
}
|
||||
|
||||
/* istanbul ignore if - CR/LF bug: remarkjs/remark#195. */
|
||||
if (!char) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
index = depth + (index - initial.offset);
|
||||
head = start(children[0]).column;
|
||||
index = depth + (index - initial.offset)
|
||||
head = start(children[0]).column
|
||||
|
||||
/* Ignore empty headings. */
|
||||
if (!head) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
diff = head - initial.column - 1 - index;
|
||||
diff = head - initial.column - 1 - index
|
||||
|
||||
if (diff) {
|
||||
word = diff > 0 ? 'Remove' : 'Add';
|
||||
diff = Math.abs(diff);
|
||||
absDiff = Math.abs(diff)
|
||||
|
||||
file.message(
|
||||
word + ' ' + diff + ' ' + plural('space', diff) +
|
||||
' before this heading’s content',
|
||||
start(children[0])
|
||||
);
|
||||
reason =
|
||||
(diff > 0 ? 'Remove' : 'Add') +
|
||||
' ' +
|
||||
absDiff +
|
||||
' ' +
|
||||
plural('space', absDiff) +
|
||||
' before this heading’s content'
|
||||
|
||||
file.message(reason, start(children[0]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,15 +143,18 @@ function noHeadingContentIndent(ast, file) {
|
||||
* between their content and the final hashes,
|
||||
* thus, there is no `add x spaces`. */
|
||||
if (type === 'atx-closed') {
|
||||
final = end(children[children.length - 1]);
|
||||
diff = end(node).column - final.column - 1 - depth;
|
||||
final = end(children[children.length - 1])
|
||||
diff = end(node).column - final.column - 1 - depth
|
||||
|
||||
if (diff) {
|
||||
file.message(
|
||||
'Remove ' + diff + ' ' + plural('space', diff) +
|
||||
' after this heading’s content',
|
||||
final
|
||||
);
|
||||
reason =
|
||||
'Remove ' +
|
||||
diff +
|
||||
' ' +
|
||||
plural('space', diff) +
|
||||
' after this heading’s content'
|
||||
|
||||
file.message(reason, final)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,54 +46,57 @@
|
||||
* 8:4: Remove 3 spaces before this heading
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var plural = require('plur');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var plural = require('plur')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-heading-indent', noHeadingIndent);
|
||||
module.exports = rule('remark-lint:no-heading-indent', noHeadingIndent)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
function noHeadingIndent(ast, file) {
|
||||
var contents = file.toString();
|
||||
var length = contents.length;
|
||||
function noHeadingIndent(tree, file) {
|
||||
var contents = String(file)
|
||||
var length = contents.length
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var initial = start(node);
|
||||
var begin = initial.offset;
|
||||
var index = begin - 1;
|
||||
var character;
|
||||
var diff;
|
||||
var initial
|
||||
var begin
|
||||
var index
|
||||
var character
|
||||
var diff
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
initial = start(node)
|
||||
begin = initial.offset
|
||||
index = begin - 1
|
||||
|
||||
while (++index < length) {
|
||||
character = contents.charAt(index);
|
||||
character = contents.charAt(index)
|
||||
|
||||
if (character !== ' ' && character !== '\t') {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
diff = index - begin;
|
||||
diff = index - begin
|
||||
|
||||
if (diff) {
|
||||
file.message(
|
||||
'Remove ' + diff + ' ' + plural('space', diff) +
|
||||
' before this heading',
|
||||
'Remove ' + diff + ' ' + plural('space', diff) + ' before this heading',
|
||||
{
|
||||
line: initial.line,
|
||||
column: initial.column + diff
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,29 +23,36 @@
|
||||
* 1:1-1:16: This looks like a heading but has too many hashes
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-heading-like-paragraph', noHeadingLikeParagraph);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-heading-like-paragraph',
|
||||
noHeadingLikeParagraph
|
||||
)
|
||||
|
||||
var fence = '#######';
|
||||
var fence = '#######'
|
||||
var reason = 'This looks like a heading but has too many hashes'
|
||||
|
||||
function noHeadingLikeParagraph(tree, file) {
|
||||
visit(tree, 'paragraph', visitor);
|
||||
visit(tree, 'paragraph', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var head = node.children[0];
|
||||
var head
|
||||
|
||||
if (
|
||||
head &&
|
||||
head.type === 'text' &&
|
||||
head.value.slice(0, fence.length) === fence &&
|
||||
!generated(node)
|
||||
) {
|
||||
file.message('This looks like a heading but has too many hashes', node);
|
||||
if (!generated(node)) {
|
||||
head = node.children[0]
|
||||
|
||||
if (
|
||||
head &&
|
||||
head.type === 'text' &&
|
||||
head.value.slice(0, fence.length) === fence
|
||||
) {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,31 +40,37 @@
|
||||
* 9:1-9:9: Don’t add a trailing `;` to headings
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-heading-punctuation', noHeadingPunctuation);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-heading-punctuation',
|
||||
noHeadingPunctuation
|
||||
)
|
||||
|
||||
function noHeadingPunctuation(ast, file, preferred) {
|
||||
preferred = typeof preferred === 'string' ? preferred : '\\.,;:!?';
|
||||
var defaults = '\\.,;:!?'
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
function noHeadingPunctuation(tree, file, pref) {
|
||||
var expression = new RegExp(
|
||||
'[' + (typeof pref === 'string' ? pref : defaults) + ']'
|
||||
)
|
||||
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var value = toString(node);
|
||||
var value
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
value = toString(node)
|
||||
value = value.charAt(value.length - 1)
|
||||
|
||||
value = value.charAt(value.length - 1);
|
||||
|
||||
if (new RegExp('[' + preferred + ']').test(value)) {
|
||||
file.message('Don’t add a trailing `' + value + '` to headings', node);
|
||||
if (expression.test(value)) {
|
||||
file.message('Don’t add a trailing `' + value + '` to headings', node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,20 +24,22 @@
|
||||
* 1:1-1:15: Do not use HTML in markdown
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-html', noHTML);
|
||||
module.exports = rule('remark-lint:no-html', noHTML)
|
||||
|
||||
function noHTML(ast, file) {
|
||||
visit(ast, 'html', visitor);
|
||||
var reason = 'Do not use HTML in markdown'
|
||||
|
||||
function noHTML(tree, file) {
|
||||
visit(tree, 'html', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (!generated(node) && !/^\s*<!--/.test(node.value)) {
|
||||
file.message('Do not use HTML in markdown', node);
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,37 +24,29 @@
|
||||
* 1:32-1:63: Don’t pad `link` with inner spaces
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-inline-padding', noInlinePadding);
|
||||
module.exports = rule('remark-lint:no-inline-padding', noInlinePadding)
|
||||
|
||||
function noInlinePadding(ast, file) {
|
||||
visit(ast, visitor);
|
||||
function noInlinePadding(tree, file) {
|
||||
visit(tree, ['emphasis', 'strong', 'delete', 'image', 'link'], visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var type = node.type;
|
||||
var contents;
|
||||
var contents
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
contents = toString(node)
|
||||
|
||||
if (
|
||||
type === 'emphasis' ||
|
||||
type === 'strong' ||
|
||||
type === 'delete' ||
|
||||
type === 'image' ||
|
||||
type === 'link'
|
||||
) {
|
||||
contents = toString(node);
|
||||
|
||||
if (contents.charAt(0) === ' ' || contents.charAt(contents.length - 1) === ' ') {
|
||||
file.message('Don’t pad `' + type + '` with inner spaces', node);
|
||||
if (
|
||||
contents.charAt(0) === ' ' ||
|
||||
contents.charAt(contents.length - 1) === ' '
|
||||
) {
|
||||
file.message('Don’t pad `' + node.type + '` with inner spaces', node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,40 +27,35 @@
|
||||
* 1:1-1:19: Don’t use literal URLs without angle brackets
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var toString = require('mdast-util-to-string');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
var toString = require('mdast-util-to-string')
|
||||
|
||||
module.exports = rule('remark-lint:no-literal-urls', noLiteralURLs);
|
||||
module.exports = rule('remark-lint:no-literal-urls', noLiteralURLs)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var MAILTO = 'mailto:';
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
var mailto = 'mailto:'
|
||||
var reason = 'Don’t use literal URLs without angle brackets'
|
||||
|
||||
function noLiteralURLs(ast, file) {
|
||||
visit(ast, 'link', visitor);
|
||||
function noLiteralURLs(tree, file) {
|
||||
visit(tree, 'link', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var head = start(node.children[0]).column;
|
||||
var tail = end(node.children[node.children.length - 1]).column;
|
||||
var initial = start(node).column;
|
||||
var final = end(node).column;
|
||||
var value = toString(node);
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
var children = node.children
|
||||
var value = toString(node)
|
||||
|
||||
if (
|
||||
initial === head &&
|
||||
final === tail &&
|
||||
(node.url === MAILTO + value || node.url === value)
|
||||
!generated(node) &&
|
||||
start(node).column === start(children[0]).column &&
|
||||
end(node).column === end(children[children.length - 1]).column &&
|
||||
(node.url === mailto + value || node.url === value)
|
||||
) {
|
||||
file.message('Don’t use literal URLs without angle brackets', node);
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,52 +61,47 @@
|
||||
* 2:1-2:7: Missing blank line before block node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-missing-blank-lines', noMissingBlankLines);
|
||||
module.exports = rule('remark-lint:no-missing-blank-lines', noMissingBlankLines)
|
||||
|
||||
function noMissingBlankLines(ast, file, options) {
|
||||
var allow = (options || {}).exceptTightLists;
|
||||
var reason = 'Missing blank line before block node'
|
||||
|
||||
visit(ast, visitor);
|
||||
var types = [
|
||||
'paragraph',
|
||||
'blockquote',
|
||||
'heading',
|
||||
'code',
|
||||
'yaml',
|
||||
'html',
|
||||
'list',
|
||||
'table',
|
||||
'thematicBreak'
|
||||
]
|
||||
|
||||
function noMissingBlankLines(tree, file, pref) {
|
||||
var allow = (pref || {}).exceptTightLists
|
||||
|
||||
visit(tree, visitor)
|
||||
|
||||
function visitor(node, index, parent) {
|
||||
var next = parent && parent.children[index + 1];
|
||||
var next
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node) && parent && (!allow || parent.type !== 'listItem')) {
|
||||
next = parent.children[index + 1]
|
||||
|
||||
if (allow && parent && parent.type === 'listItem') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
next &&
|
||||
applicable(node) &&
|
||||
applicable(next) &&
|
||||
position.start(next).line === position.end(node).line + 1
|
||||
) {
|
||||
file.message('Missing blank line before block node', next);
|
||||
if (
|
||||
next &&
|
||||
types.indexOf(next.type) !== -1 &&
|
||||
position.start(next).line === position.end(node).line + 1
|
||||
) {
|
||||
file.message(reason, next)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applicable(node) {
|
||||
return [
|
||||
'paragraph',
|
||||
'blockquote',
|
||||
'heading',
|
||||
'code',
|
||||
'yaml',
|
||||
'html',
|
||||
'list',
|
||||
'table',
|
||||
'thematicBreak'
|
||||
].indexOf(node.type) !== -1;
|
||||
}
|
||||
|
@ -25,36 +25,43 @@
|
||||
* 3:1-3:6: Don’t use multiple top level headings (3:1)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-multiple-toplevel-headings', noMultipleToplevelHeadings);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-multiple-toplevel-headings',
|
||||
noMultipleToplevelHeadings
|
||||
)
|
||||
|
||||
function noMultipleToplevelHeadings(ast, file, preferred) {
|
||||
var style = preferred ? preferred : 1;
|
||||
var topLevelheading = false;
|
||||
function noMultipleToplevelHeadings(tree, file, pref) {
|
||||
var style = pref ? pref : 1
|
||||
var topLevelheading = false
|
||||
|
||||
visit(ast, 'heading', visitor);
|
||||
visit(tree, 'heading', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var pos;
|
||||
var pos
|
||||
var reason
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.depth === style) {
|
||||
if (!generated(node) && node.depth === style) {
|
||||
if (topLevelheading) {
|
||||
pos = position.start(node);
|
||||
pos = position.start(node)
|
||||
|
||||
file.message('Don’t use multiple top level headings (' + pos.line + ':' + pos.column + ')', node);
|
||||
reason =
|
||||
'Don’t use multiple top level headings (' +
|
||||
pos.line +
|
||||
':' +
|
||||
pos.column +
|
||||
')'
|
||||
|
||||
file.message(reason, node)
|
||||
}
|
||||
|
||||
topLevelheading = node;
|
||||
topLevelheading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,75 +74,77 @@
|
||||
* 23:2: Expected no indentation in paragraph content
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
|
||||
module.exports = rule('remark-lint:no-paragraph-content-indent', noParagraphContentIndent);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-paragraph-content-indent',
|
||||
noParagraphContentIndent
|
||||
)
|
||||
|
||||
var message = 'Expected no indentation in paragraph content';
|
||||
var reason = 'Expected no indentation in paragraph content'
|
||||
|
||||
function noParagraphContentIndent(tree, file) {
|
||||
visit(tree, 'paragraph', visitor);
|
||||
visit(tree, 'paragraph', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var first = true;
|
||||
var first = true
|
||||
|
||||
visit(node, check);
|
||||
visit(node, check)
|
||||
|
||||
return visit.SKIP
|
||||
|
||||
function check(node, pos, parent) {
|
||||
var start = position.start(node);
|
||||
var value;
|
||||
var index;
|
||||
var line;
|
||||
var cumulative;
|
||||
var indent;
|
||||
var start = position.start(node)
|
||||
var value
|
||||
var index
|
||||
var line
|
||||
var cumulative
|
||||
var indent
|
||||
|
||||
if (!applicable(node)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (!start.line || !node.position.indent) {
|
||||
first = false;
|
||||
return;
|
||||
first = false
|
||||
return
|
||||
}
|
||||
|
||||
if (first && ws(toString(node).charAt(0))) {
|
||||
file.message(message, node.position.start);
|
||||
file.message(reason, node.position.start)
|
||||
}
|
||||
|
||||
first = false;
|
||||
value = toString(node);
|
||||
index = value.indexOf('\n');
|
||||
line = 0;
|
||||
cumulative = 0;
|
||||
indent = node.position.indent;
|
||||
first = false
|
||||
value = toString(node)
|
||||
index = value.indexOf('\n')
|
||||
line = 0
|
||||
cumulative = 0
|
||||
indent = node.position.indent
|
||||
|
||||
while (index !== -1) {
|
||||
cumulative += indent[line];
|
||||
cumulative += indent[line]
|
||||
|
||||
if (ws(value.charAt(index + 1))) {
|
||||
file.message(
|
||||
message,
|
||||
{
|
||||
line: start.line + line + 1,
|
||||
column: indent[line],
|
||||
offset: start.offset + index + cumulative
|
||||
}
|
||||
);
|
||||
file.message(reason, {
|
||||
line: start.line + line + 1,
|
||||
column: indent[line],
|
||||
offset: start.offset + index + cumulative
|
||||
})
|
||||
}
|
||||
|
||||
index = value.indexOf('\n', index + 1);
|
||||
line++;
|
||||
index = value.indexOf('\n', index + 1)
|
||||
line++
|
||||
}
|
||||
|
||||
if (value.charAt(value.length - 1) === '\n') {
|
||||
node = head(parent.children[pos + 1]);
|
||||
node = head(parent.children[pos + 1])
|
||||
|
||||
if (node && ws(toString(node).charAt(0))) {
|
||||
file.message(message, node.position.start);
|
||||
file.message(reason, node.position.start)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,27 +152,27 @@ function noParagraphContentIndent(tree, file) {
|
||||
}
|
||||
|
||||
function ws(character) {
|
||||
return character === ' ' || character === '\t';
|
||||
return character === ' ' || character === '\t'
|
||||
}
|
||||
|
||||
function head(node) {
|
||||
while (node && 'children' in node) {
|
||||
node = node.children[0];
|
||||
node = node.children[0]
|
||||
}
|
||||
|
||||
/* istanbul ignore if - shouldn’t happen by default, could happen for void
|
||||
* nodes though. */
|
||||
if (!node || !applicable(node)) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
|
||||
function applicable(node) {
|
||||
return 'value' in node || node.alt;
|
||||
return 'value' in node || node.alt
|
||||
}
|
||||
|
||||
function toString(node) {
|
||||
return 'value' in node ? node.value : node.alt;
|
||||
return 'value' in node ? node.value : node.alt
|
||||
}
|
||||
|
@ -23,39 +23,42 @@
|
||||
* 1:1-1:17: Did you mean to use `[delta]` instead of `(delta)`, a reference?
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var rule = require('unified-lint-rule')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
|
||||
module.exports = rule('remark-lint:no-reference-like-url', noReferenceLikeURL);
|
||||
module.exports = rule('remark-lint:no-reference-like-url', noReferenceLikeURL)
|
||||
|
||||
function noReferenceLikeURL(tree, file) {
|
||||
var identifiers = [];
|
||||
var identifiers = []
|
||||
|
||||
visit(tree, 'definition', find);
|
||||
|
||||
visit(tree, 'image', check);
|
||||
visit(tree, 'link', check);
|
||||
visit(tree, 'definition', find)
|
||||
visit(tree, ['image', 'link'], check)
|
||||
|
||||
/* Find identifiers. */
|
||||
function find(node) {
|
||||
if (!generated(node)) {
|
||||
identifiers.push(node.identifier.toLowerCase());
|
||||
identifiers.push(node.identifier.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
||||
/* Check `node`. */
|
||||
function check(node) {
|
||||
var url = node.url;
|
||||
var url = node.url
|
||||
var reason
|
||||
|
||||
if (identifiers.indexOf(url.toLowerCase()) !== -1) {
|
||||
file.message(
|
||||
'Did you mean to use `[' + url + ']` instead of ' +
|
||||
'`(' + url + ')`, a reference?',
|
||||
node
|
||||
);
|
||||
reason =
|
||||
'Did you mean to use `[' +
|
||||
url +
|
||||
']` instead of ' +
|
||||
'`(' +
|
||||
url +
|
||||
')`, a reference?'
|
||||
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,11 @@
|
||||
* $ echo a > file
|
||||
* ```
|
||||
*
|
||||
* Some empty code:
|
||||
*
|
||||
* ```command
|
||||
* ```
|
||||
*
|
||||
* It’s fine to use dollars in non-shell code.
|
||||
*
|
||||
* ```js
|
||||
@ -39,13 +44,15 @@
|
||||
* 1:1-4:4: Do not use dollar signs before shell-commands
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-shell-dollars', noShellDollars);
|
||||
module.exports = rule('remark-lint:no-shell-dollars', noShellDollars)
|
||||
|
||||
var reason = 'Do not use dollar signs before shell-commands'
|
||||
|
||||
/* List of shell script file extensions (also used as code
|
||||
* flags for syntax highlighting on GitHub):
|
||||
@ -62,31 +69,36 @@ var flags = [
|
||||
'tmux',
|
||||
'tool',
|
||||
'zsh'
|
||||
];
|
||||
]
|
||||
|
||||
function noShellDollars(ast, file) {
|
||||
visit(ast, 'code', visitor);
|
||||
function noShellDollars(tree, file) {
|
||||
visit(tree, 'code', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var language = node.lang;
|
||||
var value = node.value;
|
||||
var warn;
|
||||
|
||||
if (!language || generated(node)) {
|
||||
return;
|
||||
}
|
||||
var lines
|
||||
var line
|
||||
var length
|
||||
var index
|
||||
|
||||
/* Check both known shell-code and unknown code. */
|
||||
if (flags.indexOf(language) !== -1) {
|
||||
warn = value.length && value.split('\n').every(check);
|
||||
if (!generated(node) && node.lang && flags.indexOf(node.lang) !== -1) {
|
||||
lines = node.value.split('\n')
|
||||
length = lines.length
|
||||
index = -1
|
||||
|
||||
if (warn) {
|
||||
file.message('Do not use dollar signs before shell-commands', node);
|
||||
if (length <= 1) {
|
||||
return
|
||||
}
|
||||
|
||||
while (++index < length) {
|
||||
line = lines[index]
|
||||
|
||||
if (line.trim() && !line.match(/^\s*\$\s*/)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
|
||||
function check(line) {
|
||||
return Boolean(!line.trim() || line.match(/^\s*\$\s*/));
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ a
|
||||
$ echo a > file
|
||||
```
|
||||
|
||||
Some empty code:
|
||||
|
||||
```command
|
||||
```
|
||||
|
||||
It’s fine to use dollars in non-shell code.
|
||||
|
||||
```js
|
||||
|
@ -28,24 +28,25 @@
|
||||
* 1:1-1:7: Use the trailing [] on reference images
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-shortcut-reference-image', noShortcutReferenceImage);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-shortcut-reference-image',
|
||||
noShortcutReferenceImage
|
||||
)
|
||||
|
||||
function noShortcutReferenceImage(ast, file) {
|
||||
visit(ast, 'imageReference', visitor);
|
||||
var reason = 'Use the trailing [] on reference images'
|
||||
|
||||
function noShortcutReferenceImage(tree, file) {
|
||||
visit(tree, 'imageReference', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.referenceType === 'shortcut') {
|
||||
file.message('Use the trailing [] on reference images', node);
|
||||
if (!generated(node) && node.referenceType === 'shortcut') {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,24 +28,25 @@
|
||||
* 1:1-1:6: Use the trailing [] on reference links
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-shortcut-reference-link', noShortcutReferenceLink);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-shortcut-reference-link',
|
||||
noShortcutReferenceLink
|
||||
)
|
||||
|
||||
function noShortcutReferenceLink(ast, file) {
|
||||
visit(ast, 'linkReference', visitor);
|
||||
var reason = 'Use the trailing [] on reference links'
|
||||
|
||||
function noShortcutReferenceLink(tree, file) {
|
||||
visit(tree, 'linkReference', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.referenceType === 'shortcut') {
|
||||
file.message('Use the trailing [] on reference links', node);
|
||||
if (!generated(node) && node.referenceType === 'shortcut') {
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,33 +36,38 @@
|
||||
* 5:1-5:21: Do not indent table rows
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:no-table-indentation', noTableIndentation);
|
||||
module.exports = rule('remark-lint:no-table-indentation', noTableIndentation)
|
||||
|
||||
function noTableIndentation(ast, file) {
|
||||
visit(ast, 'table', visitor);
|
||||
var reason = 'Do not indent table rows'
|
||||
|
||||
function noTableIndentation(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'table', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var contents = file.toString();
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
node.children.forEach(each)
|
||||
}
|
||||
|
||||
node.children.forEach(each);
|
||||
return visit.SKIP
|
||||
}
|
||||
|
||||
function each(row) {
|
||||
var fence = contents.slice(position.start(row).offset, position.start(row.children[0]).offset);
|
||||
function each(row) {
|
||||
var fence = contents.slice(
|
||||
position.start(row).offset,
|
||||
position.start(row.children[0]).offset
|
||||
)
|
||||
|
||||
if (fence.indexOf('|') > 1) {
|
||||
file.message('Do not indent table rows', row);
|
||||
}
|
||||
if (fence.indexOf('|') > 1) {
|
||||
file.message(reason, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,22 +50,22 @@
|
||||
* 13:41: Use spaces instead of hard-tabs
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var location = require('vfile-location');
|
||||
var rule = require('unified-lint-rule')
|
||||
var location = require('vfile-location')
|
||||
|
||||
module.exports = rule('remark-lint:no-tabs', noTabs);
|
||||
module.exports = rule('remark-lint:no-tabs', noTabs)
|
||||
|
||||
function noTabs(ast, file) {
|
||||
var content = file.toString();
|
||||
var position = location(file).toPosition;
|
||||
var index = -1;
|
||||
var length = content.length;
|
||||
var reason = 'Use spaces instead of hard-tabs'
|
||||
|
||||
while (++index < length) {
|
||||
if (content.charAt(index) === '\t') {
|
||||
file.message('Use spaces instead of hard-tabs', position(index));
|
||||
}
|
||||
function noTabs(tree, file) {
|
||||
var content = String(file)
|
||||
var position = location(file).toPosition
|
||||
var index = content.indexOf('\t')
|
||||
|
||||
while (index !== -1) {
|
||||
file.message(reason, position(index))
|
||||
index = content.indexOf('\t', index + 1)
|
||||
}
|
||||
}
|
||||
|
@ -21,33 +21,34 @@
|
||||
* 1:1-1:8: Found reference to undefined definition
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var rule = require('unified-lint-rule')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
|
||||
module.exports = rule('remark-lint:no-undefined-references', noUndefinedReferences);
|
||||
module.exports = rule(
|
||||
'remark-lint:no-undefined-references',
|
||||
noUndefinedReferences
|
||||
)
|
||||
|
||||
function noUndefinedReferences(ast, file) {
|
||||
var map = {};
|
||||
var reason = 'Found reference to undefined definition'
|
||||
|
||||
visit(ast, 'definition', mark);
|
||||
visit(ast, 'footnoteDefinition', mark);
|
||||
function noUndefinedReferences(tree, file) {
|
||||
var map = {}
|
||||
|
||||
visit(ast, 'imageReference', find);
|
||||
visit(ast, 'linkReference', find);
|
||||
visit(ast, 'footnoteReference', find);
|
||||
visit(tree, ['definition', 'footnoteDefinition'], mark)
|
||||
visit(tree, ['imageReference', 'linkReference', 'footnoteReference'], find)
|
||||
|
||||
function mark(node) {
|
||||
if (!generated(node)) {
|
||||
map[node.identifier.toUpperCase()] = true;
|
||||
map[node.identifier.toUpperCase()] = true
|
||||
}
|
||||
}
|
||||
|
||||
function find(node) {
|
||||
if (!generated(node) && !map[node.identifier.toUpperCase()]) {
|
||||
file.message('Found reference to undefined definition', node);
|
||||
file.message(reason, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,49 +21,43 @@
|
||||
* 1:1-1:27: Found unused definition
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var generated = require('unist-util-generated');
|
||||
var visit = require('unist-util-visit');
|
||||
var rule = require('unified-lint-rule')
|
||||
var generated = require('unist-util-generated')
|
||||
var visit = require('unist-util-visit')
|
||||
|
||||
module.exports = rule('remark-lint:no-unused-definitions', noUnusedDefinitions);
|
||||
module.exports = rule('remark-lint:no-unused-definitions', noUnusedDefinitions)
|
||||
|
||||
function noUnusedDefinitions(ast, file) {
|
||||
var map = {};
|
||||
var identifier;
|
||||
var reason = 'Found unused definition'
|
||||
|
||||
visit(ast, 'definition', find);
|
||||
visit(ast, 'footnoteDefinition', find);
|
||||
function noUnusedDefinitions(tree, file) {
|
||||
var map = {}
|
||||
var identifier
|
||||
var entry
|
||||
|
||||
visit(ast, 'imageReference', mark);
|
||||
visit(ast, 'linkReference', mark);
|
||||
visit(ast, 'footnoteReference', mark);
|
||||
visit(tree, ['definition', 'footnoteDefinition'], find)
|
||||
visit(tree, ['imageReference', 'linkReference', 'footnoteReference'], mark)
|
||||
|
||||
for (identifier in map) {
|
||||
if (!map[identifier].used) {
|
||||
file.message('Found unused definition', map[identifier].node);
|
||||
entry = map[identifier]
|
||||
|
||||
if (!entry.used) {
|
||||
file.message(reason, entry.node)
|
||||
}
|
||||
}
|
||||
|
||||
function find(node) {
|
||||
if (generated(node)) {
|
||||
return;
|
||||
if (!generated(node)) {
|
||||
map[node.identifier.toUpperCase()] = {node: node, used: false}
|
||||
}
|
||||
|
||||
map[node.identifier.toUpperCase()] = {
|
||||
node: node,
|
||||
used: false
|
||||
};
|
||||
}
|
||||
|
||||
function mark(node) {
|
||||
var info = map[node.identifier.toUpperCase()];
|
||||
var info = map[node.identifier.toUpperCase()]
|
||||
|
||||
if (generated(node) || !info) {
|
||||
return;
|
||||
if (!generated(node) && info) {
|
||||
info.used = true
|
||||
}
|
||||
|
||||
info.used = true;
|
||||
}
|
||||
}
|
||||
|
@ -54,62 +54,64 @@
|
||||
* 1:1: Invalid ordered list-item marker style `!`: use either `'.'` or `')'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:ordered-list-marker-style', orderedListMarkerStyle);
|
||||
module.exports = rule(
|
||||
'remark-lint:ordered-list-marker-style',
|
||||
orderedListMarkerStyle
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
var STYLES = {
|
||||
var styles = {
|
||||
')': true,
|
||||
'.': true,
|
||||
null: true
|
||||
};
|
||||
}
|
||||
|
||||
function orderedListMarkerStyle(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function orderedListMarkerStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref !== 'string' || pref === 'consistent' ? null : pref
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid ordered list-item marker style `' + preferred + '`: use either `\'.\'` or `\')\'`');
|
||||
if (styles[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid ordered list-item marker style `' +
|
||||
pref +
|
||||
"`: use either `'.'` or `')'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
var children = node.children
|
||||
var length = node.ordered ? children.length : 0
|
||||
var index = -1
|
||||
var marker
|
||||
var child
|
||||
|
||||
if (!node.ordered) {
|
||||
return;
|
||||
}
|
||||
while (++index < length) {
|
||||
child = children[index]
|
||||
|
||||
items.forEach(each);
|
||||
if (!generated(child)) {
|
||||
marker = contents
|
||||
.slice(start(child).offset, start(child.children[0]).offset)
|
||||
.replace(/\s|\d/g, '')
|
||||
.replace(/\[[x ]?]\s*$/i, '')
|
||||
|
||||
function each(item) {
|
||||
var head = item.children[0];
|
||||
var initial = start(item).offset;
|
||||
var final = start(head).offset;
|
||||
var marker;
|
||||
|
||||
if (generated(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
marker = contents.slice(initial, final).replace(/\s|\d/g, '');
|
||||
|
||||
/* Support checkboxes. */
|
||||
marker = marker.replace(/\[[x ]?]\s*$/i, '');
|
||||
|
||||
if (!preferred) {
|
||||
preferred = marker;
|
||||
} else if (marker !== preferred) {
|
||||
file.message('Marker style should be `' + preferred + '`', item);
|
||||
if (pref) {
|
||||
if (marker !== pref) {
|
||||
file.message('Marker style should be `' + pref + '`', child)
|
||||
}
|
||||
} else {
|
||||
pref = marker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +89,15 @@
|
||||
*
|
||||
* 2:1-2:8: Marker should be `1`, was `2`
|
||||
*
|
||||
* @example {"name": "also-invalid.md", "setting": "one", "label": "input"}
|
||||
*
|
||||
* 2. Foo
|
||||
* 1. Bar
|
||||
*
|
||||
* @example {"name": "also-invalid.md", "setting": "one", "label": "output"}
|
||||
*
|
||||
* 1:1-1:8: Marker should be `1`, was `2`
|
||||
*
|
||||
* @example {"name": "invalid.md", "setting": "ordered", "label": "input"}
|
||||
*
|
||||
* 1. Foo
|
||||
@ -103,74 +112,71 @@
|
||||
* 1:1: Invalid ordered list-item marker value `invalid`: use either `'ordered'` or `'one'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:ordered-list-marker-value', orderedListMarkerValue);
|
||||
module.exports = rule(
|
||||
'remark-lint:ordered-list-marker-value',
|
||||
orderedListMarkerValue
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
var STYLES = {
|
||||
ordered: true,
|
||||
single: true,
|
||||
one: true
|
||||
};
|
||||
var styles = {ordered: true, single: true, one: true}
|
||||
|
||||
function orderedListMarkerValue(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function orderedListMarkerValue(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred === 'string' ? preferred : 'ordered';
|
||||
pref = typeof pref === 'string' ? pref : 'ordered'
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid ordered list-item marker value `' + preferred + '`: use either `\'ordered\'` or `\'one\'`');
|
||||
if (styles[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid ordered list-item marker value `' +
|
||||
pref +
|
||||
"`: use either `'ordered'` or `'one'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
var shouldBe = (preferred === 'one' ? 1 : node.start) || 1;
|
||||
var children = node.children
|
||||
var shouldBe = (pref === 'one' ? 1 : node.start) || 1
|
||||
var length = node.ordered ? children.length : 0
|
||||
var index = -1
|
||||
var child
|
||||
var marker
|
||||
|
||||
/* Ignore unordered lists. */
|
||||
if (!node.ordered) {
|
||||
return;
|
||||
}
|
||||
while (++index < length) {
|
||||
child = children[index]
|
||||
|
||||
items.forEach(each);
|
||||
|
||||
function each(item, index) {
|
||||
var head = item.children[0];
|
||||
var initial = start(item).offset;
|
||||
var final = start(head).offset;
|
||||
var marker;
|
||||
|
||||
/* Ignore first list item. */
|
||||
if (index === 0) {
|
||||
return;
|
||||
/* Ignore generated nodes, first items. */
|
||||
if (generated(child) || (index === 0 && pref !== 'one')) {
|
||||
continue
|
||||
}
|
||||
|
||||
/* Increase the expected line number when in
|
||||
* `ordered` mode. */
|
||||
if (preferred === 'ordered') {
|
||||
shouldBe++;
|
||||
if (pref === 'ordered') {
|
||||
shouldBe++
|
||||
}
|
||||
|
||||
/* Ignore generated nodes. */
|
||||
if (generated(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
marker = contents.slice(initial, final).replace(/[\s.)]/g, '');
|
||||
|
||||
/* Support checkboxes. */
|
||||
marker = Number(marker.replace(/\[[x ]?]\s*$/i, ''));
|
||||
marker = Number(
|
||||
contents
|
||||
.slice(start(child).offset, start(child.children[0]).offset)
|
||||
.replace(/[\s.)]/g, '')
|
||||
.replace(/\[[x ]?]\s*$/i, '')
|
||||
)
|
||||
|
||||
if (marker !== shouldBe) {
|
||||
file.message('Marker should be `' + shouldBe + '`, was `' + marker + '`', item);
|
||||
file.message(
|
||||
'Marker should be `' + shouldBe + '`, was `' + marker + '`',
|
||||
child
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,23 @@ When configured with `'one'`.
|
||||
2:1-2:8: Marker should be `1`, was `2`
|
||||
```
|
||||
|
||||
##### `also-invalid.md`
|
||||
|
||||
When configured with `'one'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
2. Foo
|
||||
1. Bar
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
1:1-1:8: Marker should be `1`, was `2`
|
||||
```
|
||||
|
||||
##### `valid.md`
|
||||
|
||||
When configured with `'single'`.
|
||||
|
@ -56,50 +56,46 @@
|
||||
* 1:1: Invalid preferred rule-style: provide a valid markdown rule, or `'consistent'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:rule-style', ruleStyle);
|
||||
module.exports = rule('remark-lint:rule-style', ruleStyle)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function ruleStyle(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function ruleStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (validateRuleStyle(preferred) !== true) {
|
||||
file.fail('Invalid preferred rule-style: provide a valid markdown rule, or `\'consistent\'`');
|
||||
if (pref !== null && /[^-_* ]/.test(pref)) {
|
||||
file.fail(
|
||||
"Invalid preferred rule-style: provide a valid markdown rule, or `'consistent'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'thematicBreak', visitor);
|
||||
visit(tree, 'thematicBreak', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var initial = start(node).offset;
|
||||
var final = end(node).offset;
|
||||
var hr;
|
||||
var initial = start(node).offset
|
||||
var final = end(node).offset
|
||||
var rule
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
if (!generated(node)) {
|
||||
rule = contents.slice(initial, final)
|
||||
|
||||
hr = contents.slice(initial, final);
|
||||
|
||||
if (preferred) {
|
||||
if (hr !== preferred) {
|
||||
file.message('Rules should use `' + preferred + '`', node);
|
||||
if (pref) {
|
||||
if (rule !== pref) {
|
||||
file.message('Rules should use `' + pref + '`', node)
|
||||
}
|
||||
} else {
|
||||
pref = rule
|
||||
}
|
||||
} else {
|
||||
preferred = hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateRuleStyle(style) {
|
||||
return style === null || !/[^-_* ]/.test(style);
|
||||
}
|
||||
|
@ -52,43 +52,43 @@
|
||||
* 1:1: Invalid strong marker `!`: use either `'consistent'`, `'*'`, or `'_'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:strong-marker', strongMarker);
|
||||
module.exports = rule('remark-lint:strong-marker', strongMarker)
|
||||
|
||||
var MARKERS = {
|
||||
'*': true,
|
||||
_: true,
|
||||
null: true
|
||||
};
|
||||
var markers = {'*': true, _: true, null: true}
|
||||
|
||||
function strongMarker(ast, file, preferred) {
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
function strongMarker(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
if (MARKERS[preferred] !== true) {
|
||||
file.fail('Invalid strong marker `' + preferred + '`: use either `\'consistent\'`, `\'*\'`, or `\'_\'`');
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (markers[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid strong marker `' +
|
||||
pref +
|
||||
"`: use either `'consistent'`, `'*'`, or `'_'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'strong', visitor);
|
||||
visit(tree, 'strong', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var marker = file.toString().charAt(position.start(node).offset);
|
||||
var marker = contents.charAt(position.start(node).offset)
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preferred) {
|
||||
if (marker !== preferred) {
|
||||
file.message('Strong should use `' + preferred + '` as a marker', node);
|
||||
if (!generated(node)) {
|
||||
if (pref) {
|
||||
if (marker !== pref) {
|
||||
file.message('Strong should use `' + pref + '` as a marker', node)
|
||||
}
|
||||
} else {
|
||||
pref = marker
|
||||
}
|
||||
} else {
|
||||
preferred = marker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,209 +27,264 @@
|
||||
* | ----- | ----- |
|
||||
* | Alpha | Bravo |
|
||||
*
|
||||
* @example {"name": "valid.md", "setting": "compact"}
|
||||
*
|
||||
* |A |B |
|
||||
* |-----|-----|
|
||||
* |Alpha|Bravo|
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "input"}
|
||||
*
|
||||
* | A | B |
|
||||
* | -----| -----|
|
||||
* | Alpha| Bravo|
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "output"}
|
||||
*
|
||||
* 3:5: Cell should be padded with 1 space, not 3
|
||||
* 3:10: Cell should be padded
|
||||
* 3:17: Cell should be padded
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "input", "setting": "padded"}
|
||||
*
|
||||
* | A | B |
|
||||
* | :----|----: |
|
||||
* | Alpha|Bravo |
|
||||
*
|
||||
* | C | D |
|
||||
* | :----- | ---: |
|
||||
* |Charlie | Delta|
|
||||
*
|
||||
* Too much padding isn’t good either:
|
||||
*
|
||||
* | E | F | G | H |
|
||||
* | :---- | -------- | :----: | -----: |
|
||||
* | Echo | Foxtrot | Golf | Hotel |
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "output", "setting": "padded"}
|
||||
*
|
||||
* 3:8: Cell should be padded
|
||||
* 3:9: Cell should be padded
|
||||
* 7:2: Cell should be padded
|
||||
* 7:17: Cell should be padded
|
||||
* 13:23: Cell should be padded with 1 space, not 2
|
||||
* 13:32: Cell should be padded with 1 space, not 2
|
||||
*
|
||||
* @example {"name": "valid.md", "setting": "compact"}
|
||||
*
|
||||
* |A |B |
|
||||
* |-----|-----|
|
||||
* |Alpha|Bravo|
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "input", "setting": "compact"}
|
||||
*
|
||||
* |A | B|
|
||||
* |:----|-----:|
|
||||
* |Alpha|Bravo |
|
||||
* | A | B |
|
||||
* | -----| -----|
|
||||
* | Alpha| Bravo|
|
||||
*
|
||||
* |C | D|
|
||||
* |:------|-----:|
|
||||
* |Charlie|Delta |
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "output", "setting": "compact"}
|
||||
*
|
||||
* 3:13: Cell should be compact
|
||||
* 3:5: Cell should be compact
|
||||
* 3:12: Cell should be compact
|
||||
* 7:15: Cell should be compact
|
||||
*
|
||||
* @example {"name": "valid-padded.md", "setting": "consistent"}
|
||||
*
|
||||
* | A | B |
|
||||
* | ----- | ----- |
|
||||
* | Alpha | Bravo |
|
||||
*
|
||||
* | C | D |
|
||||
* | ------- | ----- |
|
||||
* | Charlie | Delta |
|
||||
*
|
||||
* @example {"name": "invalid-padded.md", "label": "input", "setting": "consistent"}
|
||||
*
|
||||
* | A | B |
|
||||
* | ----- | ----- |
|
||||
* | Alpha | Bravo |
|
||||
*
|
||||
* | C | D |
|
||||
* | :----- | ----: |
|
||||
* |Charlie | Delta |
|
||||
*
|
||||
* @example {"name": "invalid-padded.md", "label": "output", "setting": "consistent"}
|
||||
*
|
||||
* 7:2: Cell should be padded
|
||||
*
|
||||
* @example {"name": "valid-compact.md", "setting": "consistent"}
|
||||
*
|
||||
* |A |B |
|
||||
* |-----|-----|
|
||||
* |Alpha|Bravo|
|
||||
*
|
||||
* |C |D |
|
||||
* |-------|-----|
|
||||
* |Charlie|Delta|
|
||||
*
|
||||
* @example {"name": "invalid-compact.md", "label": "input", "setting": "consistent"}
|
||||
*
|
||||
* |A |B |
|
||||
* |-----|-----|
|
||||
* |Alpha|Bravo|
|
||||
*
|
||||
* |C | D|
|
||||
* |:------|-----:|
|
||||
* |Charlie|Delta |
|
||||
*
|
||||
* @example {"name": "invalid-compact.md", "label": "output", "setting": "consistent"}
|
||||
*
|
||||
* 7:15: Cell should be compact
|
||||
*
|
||||
* @example {"name": "invalid.md", "label": "output", "setting": "invalid", "config": {"positionless": true}}
|
||||
*
|
||||
* 1:1: Invalid table-cell-padding style `invalid`
|
||||
*
|
||||
* @example {"name": "empty-heading.md"}
|
||||
* @example {"name": "empty.md", "label": "input", "setting": "padded"}
|
||||
*
|
||||
* <!-- Empty heading cells are always OK. -->
|
||||
* <!-- Empty cells are OK, but those surrounding them may not be. -->
|
||||
*
|
||||
* | | Alpha |
|
||||
* | ----- | ------- |
|
||||
* | Bravo | Charlie |
|
||||
* | | Alpha | Bravo|
|
||||
* | ------ | ----- | ---: |
|
||||
* | Charlie| | Echo|
|
||||
*
|
||||
* @example {"name": "empty-body.md"}
|
||||
* @example {"name": "empty.md", "label": "output", "setting": "padded"}
|
||||
*
|
||||
* <!-- Empty body cells are always OK. -->
|
||||
* 3:25: Cell should be padded
|
||||
* 5:10: Cell should be padded
|
||||
* 5:25: Cell should be padded
|
||||
*
|
||||
* | Alpha | Bravo |
|
||||
* | ------- | ------- |
|
||||
* | Charlie | |
|
||||
* @example {"name": "missing-body.md", "setting": "padded"}
|
||||
*
|
||||
* <!-- Missing cells are fine as well. -->
|
||||
*
|
||||
* | Alpha | Bravo | Charlie |
|
||||
* | ----- | -------- | ------- |
|
||||
* | Delta |
|
||||
* | Echo | Foxtrott |
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:table-cell-padding', tableCellPadding);
|
||||
module.exports = rule('remark-lint:table-cell-padding', tableCellPadding)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
var STYLES = {
|
||||
null: true,
|
||||
padded: true,
|
||||
compact: true
|
||||
};
|
||||
var styles = {null: true, padded: true, compact: true}
|
||||
|
||||
function tableCellPadding(tree, file, preferred) {
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
function tableCellPadding(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid table-cell-padding style `' + preferred + '`');
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (styles[pref] !== true) {
|
||||
file.fail('Invalid table-cell-padding style `' + pref + '`')
|
||||
}
|
||||
|
||||
visit(tree, 'table', visitor);
|
||||
visit(tree, 'table', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var rows = node.children;
|
||||
var contents = String(file);
|
||||
var starts = [];
|
||||
var ends = [];
|
||||
var cells = [];
|
||||
var style;
|
||||
var sizes;
|
||||
var rows = node.children
|
||||
var sizes = new Array(node.align.length)
|
||||
var length = generated(node) ? -1 : rows.length
|
||||
var index = -1
|
||||
var entries = []
|
||||
var style
|
||||
var row
|
||||
var cells
|
||||
var column
|
||||
var cellCount
|
||||
var cell
|
||||
var next
|
||||
var fence
|
||||
var pos
|
||||
var entry
|
||||
var final
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
/* Check rows. */
|
||||
while (++index < length) {
|
||||
row = rows[index]
|
||||
cells = row.children
|
||||
cellCount = cells.length
|
||||
column = -2 /* Start without a first cell */
|
||||
next = null
|
||||
final = undefined
|
||||
|
||||
rows.forEach(eachRow);
|
||||
/* Check fences (before, between, and after cells) */
|
||||
while (++column < cellCount) {
|
||||
cell = next
|
||||
next = cells[column + 1]
|
||||
|
||||
sizes = inferSizes(node);
|
||||
fence = contents.slice(
|
||||
cell ? end(cell).offset : start(row).offset,
|
||||
next ? start(next).offset : end(row).offset
|
||||
)
|
||||
|
||||
if (preferred === 'padded') {
|
||||
style = 1;
|
||||
} else if (preferred === 'compact') {
|
||||
style = 0;
|
||||
} else {
|
||||
style = null;
|
||||
starts.concat(ends).some(inferStyle);
|
||||
}
|
||||
pos = fence.indexOf('|')
|
||||
|
||||
cells.forEach(checkCell);
|
||||
if (cell && cell.children.length !== 0 && final !== undefined) {
|
||||
entries.push({node: cell, start: final, end: pos, index: column})
|
||||
|
||||
function eachRow(row) {
|
||||
var children = row.children;
|
||||
/* Detect max space per column. */
|
||||
sizes[column] = Math.max(sizes[column] || 0, size(cell))
|
||||
} else {
|
||||
final = undefined
|
||||
}
|
||||
|
||||
check(start(row).offset, start(children[0]).offset, null, children[0]);
|
||||
ends.pop(); /* Ignore end before row. */
|
||||
|
||||
children.forEach(eachCell);
|
||||
starts.pop(); /* Ignore start after row */
|
||||
|
||||
function eachCell(cell, index) {
|
||||
var next = children[index + 1] || null;
|
||||
check(end(cell).offset, start(next).offset || end(row).offset, cell, next);
|
||||
cells.push(cell);
|
||||
}
|
||||
}
|
||||
|
||||
function inferStyle(pos) {
|
||||
if (pos === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
style = Math.min(pos, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
function check(initial, final, prev, next) {
|
||||
var fence = contents.slice(initial, final);
|
||||
var pos = fence.indexOf('|');
|
||||
|
||||
ends.push(prev && pos !== -1 && prev.children.length !== 0 ? pos : undefined);
|
||||
starts.push(next && next.children.length !== 0 ? fence.length - pos - 1 : undefined);
|
||||
}
|
||||
|
||||
function checkCell(cell, index) {
|
||||
/* Ignore, when compact, every cell except the biggest in the column. */
|
||||
if (style === 0 && size(cell) < sizes[index % sizes.length]) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSide('start', cell, starts[index], index);
|
||||
checkSide('end', cell, ends[index]);
|
||||
}
|
||||
|
||||
function checkSide(side, cell, spacing, index) {
|
||||
var message;
|
||||
|
||||
if (spacing === undefined || spacing === style) {
|
||||
return;
|
||||
}
|
||||
|
||||
message = 'Cell should be ';
|
||||
|
||||
if (style === 0) {
|
||||
message += 'compact';
|
||||
} else {
|
||||
message += 'padded';
|
||||
|
||||
if (spacing > style) {
|
||||
message += ' with 1 space, not ' + spacing;
|
||||
|
||||
/* May be right or center aligned. */
|
||||
if (size(cell) < sizes[index % sizes.length]) {
|
||||
return;
|
||||
}
|
||||
if (next && next.children.length !== 0) {
|
||||
final = fence.length - pos - 1
|
||||
} else {
|
||||
final = undefined
|
||||
}
|
||||
}
|
||||
|
||||
file.message(message, cell.position[side]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inferSizes(tree) {
|
||||
var sizes = new Array(tree.align.length);
|
||||
if (pref) {
|
||||
style = pref === 'padded' ? 1 : 0
|
||||
} else {
|
||||
style = entries[0] && (!entries[0].start || !entries[0].end) ? 0 : 1
|
||||
}
|
||||
|
||||
tree.children.forEach(row);
|
||||
index = -1
|
||||
length = entries.length
|
||||
|
||||
return sizes;
|
||||
while (++index < length) {
|
||||
entry = entries[index]
|
||||
checkSide('start', entry, style, sizes)
|
||||
checkSide('end', entry, style, sizes)
|
||||
}
|
||||
|
||||
function row(node) {
|
||||
node.children.forEach(cell);
|
||||
return visit.SKIP
|
||||
}
|
||||
|
||||
function cell(node, index) {
|
||||
sizes[index] = Math.max(sizes[index] || 0, size(node));
|
||||
function checkSide(side, entry, style, sizes) {
|
||||
var cell = entry.node
|
||||
var spacing = entry[side]
|
||||
var index = entry.index
|
||||
var reason
|
||||
|
||||
if (spacing === undefined || spacing === style) {
|
||||
return
|
||||
}
|
||||
|
||||
reason = 'Cell should be '
|
||||
|
||||
if (style === 0) {
|
||||
reason += 'compact'
|
||||
|
||||
/* Ignore every cell except the biggest in the column. */
|
||||
if (size(cell) < sizes[index]) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
reason += 'padded'
|
||||
|
||||
if (spacing > style) {
|
||||
reason += ' with 1 space, not ' + spacing
|
||||
|
||||
/* May be right or center aligned. */
|
||||
if (size(cell) < sizes[index]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.message(reason, cell.position[side])
|
||||
}
|
||||
}
|
||||
|
||||
function size(node) {
|
||||
return end(node).offset - start(node).offset;
|
||||
return end(node).offset - start(node).offset
|
||||
}
|
||||
|
@ -56,6 +56,16 @@ When configured with `'padded'`.
|
||||
| A | B |
|
||||
| :----|----: |
|
||||
| Alpha|Bravo |
|
||||
|
||||
| C | D |
|
||||
| :----- | ---: |
|
||||
|Charlie | Delta|
|
||||
|
||||
Too much padding isn’t good either:
|
||||
|
||||
| E | F | G | H |
|
||||
| :---- | -------- | :----: | -----: |
|
||||
| Echo | Foxtrot | Golf | Hotel |
|
||||
```
|
||||
|
||||
###### Out
|
||||
@ -63,8 +73,53 @@ When configured with `'padded'`.
|
||||
```text
|
||||
3:8: Cell should be padded
|
||||
3:9: Cell should be padded
|
||||
7:2: Cell should be padded
|
||||
7:17: Cell should be padded
|
||||
13:23: Cell should be padded with 1 space, not 2
|
||||
13:32: Cell should be padded with 1 space, not 2
|
||||
```
|
||||
|
||||
##### `empty.md`
|
||||
|
||||
When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
<!-- Empty cells are OK, but those surrounding them may not be. -->
|
||||
|
||||
| | Alpha | Bravo|
|
||||
| ------ | ----- | ---: |
|
||||
| Charlie| | Echo|
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
3:25: Cell should be padded
|
||||
5:10: Cell should be padded
|
||||
5:25: Cell should be padded
|
||||
```
|
||||
|
||||
##### `missing-body.md`
|
||||
|
||||
When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
<!-- Missing cells are fine as well. -->
|
||||
|
||||
| Alpha | Bravo | Charlie |
|
||||
| ----- | -------- | ------- |
|
||||
| Delta |
|
||||
| Echo | Foxtrott |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
No messages.
|
||||
|
||||
##### `valid.md`
|
||||
|
||||
When configured with `'compact'`.
|
||||
@ -87,68 +142,108 @@ When configured with `'compact'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
|A | B|
|
||||
|:----|-----:|
|
||||
|Alpha|Bravo |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
3:13: Cell should be compact
|
||||
```
|
||||
|
||||
##### `invalid.md`
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
| -----| -----|
|
||||
| Alpha| Bravo|
|
||||
|
||||
|C | D|
|
||||
|:------|-----:|
|
||||
|Charlie|Delta |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
3:5: Cell should be padded with 1 space, not 3
|
||||
3:10: Cell should be padded
|
||||
3:17: Cell should be padded
|
||||
3:5: Cell should be compact
|
||||
3:12: Cell should be compact
|
||||
7:15: Cell should be compact
|
||||
```
|
||||
|
||||
##### `empty-heading.md`
|
||||
##### `valid-padded.md`
|
||||
|
||||
When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
<!-- Empty heading cells are always OK. -->
|
||||
| A | B |
|
||||
| ----- | ----- |
|
||||
| Alpha | Bravo |
|
||||
|
||||
| | Alpha |
|
||||
| ----- | ------- |
|
||||
| Bravo | Charlie |
|
||||
| C | D |
|
||||
| ------- | ----- |
|
||||
| Charlie | Delta |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
No messages.
|
||||
|
||||
##### `empty-body.md`
|
||||
##### `invalid-padded.md`
|
||||
|
||||
When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
<!-- Empty body cells are always OK. -->
|
||||
| A | B |
|
||||
| ----- | ----- |
|
||||
| Alpha | Bravo |
|
||||
|
||||
| Alpha | Bravo |
|
||||
| ------- | ------- |
|
||||
| Charlie | |
|
||||
| C | D |
|
||||
| :----- | ----: |
|
||||
|Charlie | Delta |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
7:2: Cell should be padded
|
||||
```
|
||||
|
||||
##### `valid-compact.md`
|
||||
|
||||
When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
|A |B |
|
||||
|-----|-----|
|
||||
|Alpha|Bravo|
|
||||
|
||||
|C |D |
|
||||
|-------|-----|
|
||||
|Charlie|Delta|
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
No messages.
|
||||
|
||||
##### `invalid-compact.md`
|
||||
|
||||
When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
|A |B |
|
||||
|-----|-----|
|
||||
|Alpha|Bravo|
|
||||
|
||||
|C | D|
|
||||
|:------|-----:|
|
||||
|Charlie|Delta |
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
7:15: Cell should be compact
|
||||
```
|
||||
|
||||
##### `invalid.md`
|
||||
|
||||
When configured with `'invalid'`.
|
||||
|
@ -41,62 +41,69 @@
|
||||
* 3:17-3:18: Misaligned table fence
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:table-pipe-alignment', tablePipeAlignment);
|
||||
module.exports = rule('remark-lint:table-pipe-alignment', tablePipeAlignment)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function tablePipeAlignment(ast, file) {
|
||||
visit(ast, 'table', visitor);
|
||||
var reason = 'Misaligned table fence'
|
||||
|
||||
function tablePipeAlignment(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'table', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var contents = file.toString();
|
||||
var indices = [];
|
||||
var offset;
|
||||
var line;
|
||||
var rows = node.children
|
||||
var length = generated(node) ? 0 : rows.length
|
||||
var index = -1
|
||||
var indices = []
|
||||
var row
|
||||
var cells
|
||||
var begin
|
||||
var column
|
||||
var columns
|
||||
var cell
|
||||
var initial
|
||||
var final
|
||||
var next
|
||||
var nextIndex
|
||||
var fence
|
||||
var pos
|
||||
|
||||
if (generated(node)) {
|
||||
return;
|
||||
}
|
||||
while (++index < length) {
|
||||
row = rows[index]
|
||||
begin = start(row)
|
||||
cells = row.children
|
||||
columns = cells.length
|
||||
column = -2 /* Start without a first cell */
|
||||
next = null
|
||||
|
||||
node.children.forEach(visitRow);
|
||||
while (++column < columns) {
|
||||
cell = next
|
||||
nextIndex = column + 1
|
||||
next = cells[nextIndex]
|
||||
|
||||
function visitRow(row) {
|
||||
var cells = row.children;
|
||||
initial = cell ? end(cell).offset : start(row).offset
|
||||
final = next ? start(next).offset : end(row).offset
|
||||
fence = contents.slice(initial, final)
|
||||
pos = initial + fence.indexOf('|') - begin.offset + 1
|
||||
|
||||
line = start(row).line;
|
||||
offset = start(row).offset;
|
||||
|
||||
check(start(row).offset, start(cells[0]).offset, 0);
|
||||
|
||||
row.children.forEach(visitCell);
|
||||
|
||||
function visitCell(cell, index) {
|
||||
var next = start(cells[index + 1]).offset || end(row).offset;
|
||||
|
||||
check(end(cell).offset, next, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that all pipes after each column are at
|
||||
* aligned. */
|
||||
function check(initial, final, index) {
|
||||
var pos = initial + contents.slice(initial, final).indexOf('|') - offset + 1;
|
||||
|
||||
if (indices[index] === undefined || indices[index] === null) {
|
||||
indices[index] = pos;
|
||||
} else if (pos !== indices[index]) {
|
||||
file.message('Misaligned table fence', {
|
||||
start: {line: line, column: pos},
|
||||
end: {line: line, column: pos + 1}
|
||||
});
|
||||
if (indices[nextIndex] === undefined || indices[nextIndex] === null) {
|
||||
indices[nextIndex] = pos
|
||||
} else if (pos !== indices[nextIndex]) {
|
||||
file.message(reason, {
|
||||
start: {line: begin.line, column: pos},
|
||||
end: {line: begin.line, column: pos + 1}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,43 +36,54 @@
|
||||
* 3:14: Missing final pipe in table fence
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:table-pipes', tablePipes);
|
||||
module.exports = rule('remark-lint:table-pipes', tablePipes)
|
||||
|
||||
var start = position.start;
|
||||
var end = position.end;
|
||||
var start = position.start
|
||||
var end = position.end
|
||||
|
||||
function tablePipes(ast, file) {
|
||||
visit(ast, 'table', visitor);
|
||||
var reasonStart = 'Missing initial pipe in table fence'
|
||||
var reasonEnd = 'Missing final pipe in table fence'
|
||||
|
||||
function tablePipes(tree, file) {
|
||||
var contents = String(file)
|
||||
|
||||
visit(tree, 'table', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var contents = file.toString();
|
||||
var rows = node.children
|
||||
var length = rows.length
|
||||
var index = -1
|
||||
var row
|
||||
var cells
|
||||
var head
|
||||
var tail
|
||||
var initial
|
||||
var final
|
||||
|
||||
node.children.forEach(visitRow);
|
||||
while (++index < length) {
|
||||
row = rows[index]
|
||||
|
||||
function visitRow(row) {
|
||||
var cells = row.children;
|
||||
var head = cells[0];
|
||||
var tail = cells[cells.length - 1];
|
||||
var initial = contents.slice(start(row).offset, start(head).offset);
|
||||
var final = contents.slice(end(tail).offset, end(row).offset);
|
||||
if (!generated(row)) {
|
||||
cells = row.children
|
||||
head = cells[0]
|
||||
tail = cells[cells.length - 1]
|
||||
initial = contents.slice(start(row).offset, start(head).offset)
|
||||
final = contents.slice(end(tail).offset, end(row).offset)
|
||||
|
||||
if (generated(row)) {
|
||||
return;
|
||||
}
|
||||
if (initial.indexOf('|') === -1) {
|
||||
file.message(reasonStart, start(row))
|
||||
}
|
||||
|
||||
if (initial.indexOf('|') === -1) {
|
||||
file.message('Missing initial pipe in table fence', start(row));
|
||||
}
|
||||
|
||||
if (final.indexOf('|') === -1) {
|
||||
file.message('Missing final pipe in table fence', end(row));
|
||||
if (final.indexOf('|') === -1) {
|
||||
file.message(reasonEnd, end(row))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,63 +65,65 @@
|
||||
* 1:1: Invalid unordered list-item marker style `!`: use either `'-'`, `'*'`, or `'+'`
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var rule = require('unified-lint-rule');
|
||||
var visit = require('unist-util-visit');
|
||||
var position = require('unist-util-position');
|
||||
var generated = require('unist-util-generated');
|
||||
var rule = require('unified-lint-rule')
|
||||
var visit = require('unist-util-visit')
|
||||
var position = require('unist-util-position')
|
||||
var generated = require('unist-util-generated')
|
||||
|
||||
module.exports = rule('remark-lint:unordered-list-marker-style', unorderedListMarkerStyle);
|
||||
module.exports = rule(
|
||||
'remark-lint:unordered-list-marker-style',
|
||||
unorderedListMarkerStyle
|
||||
)
|
||||
|
||||
var start = position.start;
|
||||
var start = position.start
|
||||
|
||||
var STYLES = {
|
||||
var styles = {
|
||||
'-': true,
|
||||
'*': true,
|
||||
'+': true,
|
||||
null: true
|
||||
};
|
||||
}
|
||||
|
||||
function unorderedListMarkerStyle(ast, file, preferred) {
|
||||
var contents = file.toString();
|
||||
function unorderedListMarkerStyle(tree, file, pref) {
|
||||
var contents = String(file)
|
||||
|
||||
preferred = typeof preferred !== 'string' || preferred === 'consistent' ? null : preferred;
|
||||
pref = typeof pref === 'string' && pref !== 'consistent' ? pref : null
|
||||
|
||||
if (STYLES[preferred] !== true) {
|
||||
file.fail('Invalid unordered list-item marker style `' + preferred + '`: use either `\'-\'`, `\'*\'`, or `\'+\'`');
|
||||
if (styles[pref] !== true) {
|
||||
file.fail(
|
||||
'Invalid unordered list-item marker style `' +
|
||||
pref +
|
||||
"`: use either `'-'`, `'*'`, or `'+'`"
|
||||
)
|
||||
}
|
||||
|
||||
visit(ast, 'list', visitor);
|
||||
visit(tree, 'list', visitor)
|
||||
|
||||
function visitor(node) {
|
||||
var items = node.children;
|
||||
var children = node.children
|
||||
var length = node.ordered ? 0 : children.length
|
||||
var index = -1
|
||||
var child
|
||||
var marker
|
||||
|
||||
if (node.ordered) {
|
||||
return;
|
||||
}
|
||||
while (++index < length) {
|
||||
child = children[index]
|
||||
|
||||
items.forEach(visitItem);
|
||||
if (!generated(child)) {
|
||||
marker = contents
|
||||
.slice(start(child).offset, start(child.children[0]).offset)
|
||||
.replace(/\[[x ]?]\s*$/i, '')
|
||||
.replace(/\s/g, '')
|
||||
|
||||
function visitItem(item) {
|
||||
var head = item.children[0];
|
||||
var initial = start(item).offset;
|
||||
var final = start(head).offset;
|
||||
var marker;
|
||||
|
||||
if (generated(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
marker = contents.slice(initial, final).replace(/\s/g, '');
|
||||
|
||||
/* Support checkboxes. */
|
||||
marker = marker.replace(/\[[x ]?]\s*$/i, '');
|
||||
|
||||
if (!preferred) {
|
||||
preferred = marker;
|
||||
} else if (marker !== preferred) {
|
||||
file.message('Marker style should be `' + preferred + '`', item);
|
||||
if (pref) {
|
||||
if (marker !== pref) {
|
||||
file.message('Marker style should be `' + pref + '`', child)
|
||||
}
|
||||
} else {
|
||||
pref = marker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var control = require('remark-message-control');
|
||||
var control = require('remark-message-control')
|
||||
|
||||
module.exports = lint;
|
||||
module.exports = lint
|
||||
|
||||
/* `remark-lint`. This adds support for ignoring stuff from
|
||||
* messages (`<!--lint ignore-->`).
|
||||
* All rules are in their own packages and presets. */
|
||||
function lint() {
|
||||
this.use(lintMessageControl);
|
||||
this.use(lintMessageControl)
|
||||
}
|
||||
|
||||
function lintMessageControl() {
|
||||
return control({name: 'lint', source: 'remark-lint'});
|
||||
return control({name: 'lint', source: 'remark-lint'})
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* settings that enforce consistency.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports.plugins = [
|
||||
require('remark-lint'),
|
||||
@ -20,4 +20,4 @@ module.exports.plugins = [
|
||||
[require('remark-lint-rule-style'), 'consistent'],
|
||||
[require('remark-lint-strong-marker'), 'consistent'],
|
||||
[require('remark-lint-table-cell-padding'), 'consistent']
|
||||
];
|
||||
]
|
||||
|
@ -90,7 +90,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports.plugins = [
|
||||
require('remark-lint'),
|
||||
@ -232,4 +232,4 @@ module.exports.plugins = [
|
||||
|
||||
/* http://www.cirosantilli.com/markdown-style-guide/#email-automatic-links.
|
||||
* Not checked. */
|
||||
];
|
||||
]
|
||||
|
@ -5,7 +5,7 @@
|
||||
* across vendors.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports.plugins = [
|
||||
require('remark-lint'),
|
||||
@ -29,4 +29,4 @@ module.exports.plugins = [
|
||||
require('remark-lint-no-shortcut-reference-link'),
|
||||
require('remark-lint-no-undefined-references'),
|
||||
require('remark-lint-no-unused-definitions')
|
||||
];
|
||||
]
|
||||
|
@ -1,60 +1,60 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var wrapped = require('wrapped');
|
||||
var wrapped = require('wrapped')
|
||||
|
||||
module.exports = factory;
|
||||
module.exports = factory
|
||||
|
||||
function factory(id, rule) {
|
||||
var parts = id.split(':');
|
||||
var source = parts[0];
|
||||
var ruleId = parts[1];
|
||||
var fn = wrapped(rule);
|
||||
var parts = id.split(':')
|
||||
var source = parts[0]
|
||||
var ruleId = parts[1]
|
||||
var fn = wrapped(rule)
|
||||
|
||||
/* istanbul ignore if - possibly useful if externalised later. */
|
||||
if (!ruleId) {
|
||||
ruleId = source;
|
||||
source = null;
|
||||
ruleId = source
|
||||
source = null
|
||||
}
|
||||
|
||||
attacher.displayName = id;
|
||||
attacher.displayName = id
|
||||
|
||||
return attacher;
|
||||
return attacher
|
||||
|
||||
function attacher(raw) {
|
||||
var config = coerce(ruleId, raw);
|
||||
var severity = config[0];
|
||||
var options = config[1];
|
||||
var fatal = severity === 2;
|
||||
var config = coerce(ruleId, raw)
|
||||
var severity = config[0]
|
||||
var options = config[1]
|
||||
var fatal = severity === 2
|
||||
|
||||
return severity ? transformer : undefined;
|
||||
return severity ? transformer : undefined
|
||||
|
||||
function transformer(tree, file, next) {
|
||||
var index = file.messages.length;
|
||||
var index = file.messages.length
|
||||
|
||||
fn(tree, file, options, done);
|
||||
fn(tree, file, options, done)
|
||||
|
||||
function done(err) {
|
||||
var messages = file.messages;
|
||||
var message;
|
||||
var messages = file.messages
|
||||
var message
|
||||
|
||||
/* Add the error, if not already properly added. */
|
||||
/* istanbul ignore if - only happens for incorrect plugins */
|
||||
if (err && messages.indexOf(err) === -1) {
|
||||
try {
|
||||
file.fail(err);
|
||||
file.fail(err)
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
while (index < messages.length) {
|
||||
message = messages[index];
|
||||
message.ruleId = ruleId;
|
||||
message.source = source;
|
||||
message.fatal = fatal;
|
||||
message = messages[index]
|
||||
message.ruleId = ruleId
|
||||
message.source = source
|
||||
message.fatal = fatal
|
||||
|
||||
index++;
|
||||
index++
|
||||
}
|
||||
|
||||
next();
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,53 +62,55 @@ function factory(id, rule) {
|
||||
|
||||
/* Coerce a value to a severity--options tuple. */
|
||||
function coerce(name, value) {
|
||||
var def = 1;
|
||||
var result;
|
||||
var level;
|
||||
var def = 1
|
||||
var result
|
||||
var level
|
||||
|
||||
/* istanbul ignore if - Handled by unified in v6.0.0 */
|
||||
if (typeof value === 'boolean') {
|
||||
result = [value];
|
||||
result = [value]
|
||||
} else if (value == null) {
|
||||
result = [def];
|
||||
result = [def]
|
||||
} else if (
|
||||
typeof value === 'object' &&
|
||||
(
|
||||
typeof value[0] === 'number' ||
|
||||
(typeof value[0] === 'number' ||
|
||||
typeof value[0] === 'boolean' ||
|
||||
typeof value[0] === 'string'
|
||||
)
|
||||
typeof value[0] === 'string')
|
||||
) {
|
||||
result = value.concat();
|
||||
result = value.concat()
|
||||
} else {
|
||||
result = [1, value];
|
||||
result = [1, value]
|
||||
}
|
||||
|
||||
level = result[0];
|
||||
level = result[0]
|
||||
|
||||
if (typeof level === 'boolean') {
|
||||
level = level ? 1 : 0;
|
||||
level = level ? 1 : 0
|
||||
} else if (typeof level === 'string') {
|
||||
if (level === 'off') {
|
||||
level = 0;
|
||||
level = 0
|
||||
} else if (level === 'on' || level === 'warn') {
|
||||
level = 1;
|
||||
level = 1
|
||||
} else if (level === 'error') {
|
||||
level = 2;
|
||||
level = 2
|
||||
} else {
|
||||
level = 1;
|
||||
result = [level, result];
|
||||
level = 1
|
||||
result = [level, result]
|
||||
}
|
||||
}
|
||||
|
||||
if (level < 0 || level > 2) {
|
||||
throw new Error(
|
||||
'Invalid severity `' + level + '` for `' + name + '`, ' +
|
||||
'expected 0, 1, or 2'
|
||||
);
|
||||
'Invalid severity `' +
|
||||
level +
|
||||
'` for `' +
|
||||
name +
|
||||
'`, ' +
|
||||
'expected 0, 1, or 2'
|
||||
)
|
||||
}
|
||||
|
||||
result[0] = level;
|
||||
result[0] = level
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
@ -1,62 +1,74 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var u = require('unist-builder');
|
||||
var dox = require('dox');
|
||||
var chalk = require('chalk');
|
||||
var remark = require('remark');
|
||||
var strip = require('strip-indent');
|
||||
var trim = require('trim');
|
||||
var parseAuthor = require('parse-author');
|
||||
var remote = require('../package.json').repository;
|
||||
var find = require('./util/find');
|
||||
var presets = require('./util/presets');
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var u = require('unist-builder')
|
||||
var dox = require('dox')
|
||||
var chalk = require('chalk')
|
||||
var remark = require('remark')
|
||||
var strip = require('strip-indent')
|
||||
var trim = require('trim')
|
||||
var parseAuthor = require('parse-author')
|
||||
var remote = require('../package.json').repository
|
||||
var find = require('./util/find')
|
||||
var presets = require('./util/presets')
|
||||
|
||||
var root = path.join(process.cwd(), 'packages');
|
||||
var root = path.join(process.cwd(), 'packages')
|
||||
|
||||
presets(root).forEach(function (basename) {
|
||||
var base = path.resolve(root, basename);
|
||||
var pack = require(path.join(base, 'package.json'));
|
||||
var doc = fs.readFileSync(path.join(base, 'index.js'), 'utf8');
|
||||
var tags = dox.parseComments(doc)[0].tags;
|
||||
var author = parseAuthor(pack.author);
|
||||
var description = trim(strip(find(tags, 'fileoverview')));
|
||||
var rows = [];
|
||||
var children;
|
||||
var short = basename.replace(/^remark-/, '');
|
||||
presets(root).forEach(function(basename) {
|
||||
var base = path.resolve(root, basename)
|
||||
var pack = require(path.join(base, 'package.json'))
|
||||
var doc = fs.readFileSync(path.join(base, 'index.js'), 'utf8')
|
||||
var tags = dox.parseComments(doc)[0].tags
|
||||
var author = parseAuthor(pack.author)
|
||||
var description = trim(strip(find(tags, 'fileoverview')))
|
||||
var rows = []
|
||||
var children
|
||||
var short = basename.replace(/^remark-/, '')
|
||||
|
||||
if (basename !== pack.name) {
|
||||
throw new Error(
|
||||
'Expected package name (`' + pack.name + '`) to be the same as ' +
|
||||
'directory name (`' + basename + '`)'
|
||||
);
|
||||
'Expected package name (`' +
|
||||
pack.name +
|
||||
'`) to be the same as ' +
|
||||
'directory name (`' +
|
||||
basename +
|
||||
'`)'
|
||||
)
|
||||
}
|
||||
|
||||
rows.push(u('tableRow', [
|
||||
u('tableCell', [u('text', 'Rule')]),
|
||||
u('tableCell', [u('text', 'Setting')])
|
||||
]));
|
||||
rows.push(
|
||||
u('tableRow', [
|
||||
u('tableCell', [u('text', 'Rule')]),
|
||||
u('tableCell', [u('text', 'Setting')])
|
||||
])
|
||||
)
|
||||
|
||||
doc.replace(/require\('remark-lint-([^']+)'\)(?:, ([^\]]+)])?/g, function ($0, rule, option) {
|
||||
var url = remote + '/tree/master/packages/remark-lint-' + rule;
|
||||
doc.replace(/require\('remark-lint-([^']+)'\)(?:, ([^\]]+)])?/g, function(
|
||||
$0,
|
||||
rule,
|
||||
option
|
||||
) {
|
||||
var url = remote + '/tree/master/packages/remark-lint-' + rule
|
||||
|
||||
rows.push(u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('link', {url: url, title: null}, [u('inlineCode', rule)])
|
||||
]),
|
||||
u('tableCell', option ? [u('inlineCode', option)] : [])
|
||||
]));
|
||||
rows.push(
|
||||
u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('link', {url: url, title: null}, [u('inlineCode', rule)])
|
||||
]),
|
||||
u('tableCell', option ? [u('inlineCode', option)] : [])
|
||||
])
|
||||
)
|
||||
|
||||
return '';
|
||||
});
|
||||
return ''
|
||||
})
|
||||
|
||||
children = [
|
||||
u('html', '<!--This file is generated-->'),
|
||||
u('heading', {depth: 1}, [u('text', basename)])
|
||||
];
|
||||
]
|
||||
|
||||
children = children.concat(remark().parse(description).children);
|
||||
children = children.concat(remark().parse(description).children)
|
||||
|
||||
children.push(
|
||||
u('heading', {depth: 2}, [u('text', 'Rules')]),
|
||||
@ -70,39 +82,51 @@ presets(root).forEach(function (basename) {
|
||||
u('paragraph', [u('text', 'npm:')]),
|
||||
u('code', {lang: 'sh'}, 'npm install ' + basename),
|
||||
u('heading', {depth: 2}, [u('text', 'Usage')]),
|
||||
u('paragraph', [u('text', 'You probably want to use it on the CLI through a config file:')]),
|
||||
u('code', {lang: 'diff'}, [
|
||||
' ...',
|
||||
' "remarkConfig": {',
|
||||
'+ "plugins": ["' + short + '"]',
|
||||
' }',
|
||||
' ...'
|
||||
].join('\n')),
|
||||
u('paragraph', [
|
||||
u('text', 'You probably want to use it on the CLI through a config file:')
|
||||
]),
|
||||
u(
|
||||
'code',
|
||||
{lang: 'diff'},
|
||||
[
|
||||
' ...',
|
||||
' "remarkConfig": {',
|
||||
'+ "plugins": ["' + short + '"]',
|
||||
' }',
|
||||
' ...'
|
||||
].join('\n')
|
||||
),
|
||||
u('paragraph', [u('text', 'Or use it on the CLI directly')]),
|
||||
u('code', {lang: 'sh'}, 'remark -u ' + short + ' readme.md'),
|
||||
u('paragraph', [u('text', 'Or use this on the API:')]),
|
||||
u('code', {lang: 'diff'}, [
|
||||
' var remark = require(\'remark\');',
|
||||
' var report = require(\'vfile-reporter\');',
|
||||
'',
|
||||
' remark()',
|
||||
'+ .use(require(\'' + basename + '\'))',
|
||||
' .process(\'_Emphasis_ and **importance**\', function (err, file) {',
|
||||
' console.error(report(err || file));',
|
||||
' });'
|
||||
].join('\n')),
|
||||
u(
|
||||
'code',
|
||||
{lang: 'diff'},
|
||||
[
|
||||
" var remark = require('remark');",
|
||||
" var report = require('vfile-reporter');",
|
||||
'',
|
||||
' remark()',
|
||||
"+ .use(require('" + basename + "'))",
|
||||
" .process('_Emphasis_ and **importance**', function (err, file) {",
|
||||
' console.error(report(err || file));',
|
||||
' });'
|
||||
].join('\n')
|
||||
),
|
||||
u('heading', {depth: 2}, [u('text', 'License')]),
|
||||
u('paragraph', [
|
||||
u('link', {url: remote + '/blob/master/LICENSE'}, [u('text', pack.license)]),
|
||||
u('link', {url: remote + '/blob/master/LICENSE'}, [
|
||||
u('text', pack.license)
|
||||
]),
|
||||
u('text', ' © '),
|
||||
u('link', {url: author.url}, [u('text', author.name)])
|
||||
])
|
||||
);
|
||||
)
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(base, 'readme.md'),
|
||||
remark().stringify(u('root', children))
|
||||
);
|
||||
)
|
||||
|
||||
console.log(chalk.green('✓') + ' wrote `readme.md` in `' + basename + '`');
|
||||
});
|
||||
console.log(chalk.green('✓') + ' wrote `readme.md` in `' + basename + '`')
|
||||
})
|
||||
|
@ -1,100 +1,120 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var inspect = require('util').inspect;
|
||||
var u = require('unist-builder');
|
||||
var chalk = require('chalk');
|
||||
var remark = require('remark');
|
||||
var parseAuthor = require('parse-author');
|
||||
var remote = require('../package.json').repository;
|
||||
var rules = require('./util/rules');
|
||||
var rule = require('./util/rule');
|
||||
var presets = require('./util/presets');
|
||||
var chars = require('./characters');
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var inspect = require('util').inspect
|
||||
var u = require('unist-builder')
|
||||
var chalk = require('chalk')
|
||||
var remark = require('remark')
|
||||
var parseAuthor = require('parse-author')
|
||||
var remote = require('../package.json').repository
|
||||
var rules = require('./util/rules')
|
||||
var rule = require('./util/rule')
|
||||
var presets = require('./util/presets')
|
||||
var chars = require('./characters')
|
||||
|
||||
var root = path.join(process.cwd(), 'packages');
|
||||
var root = path.join(process.cwd(), 'packages')
|
||||
|
||||
presets = presets(root).map(function (name) {
|
||||
var doc = fs.readFileSync(path.join(root, name, 'index.js'), 'utf8');
|
||||
var packages = {};
|
||||
presets = presets(root).map(function(name) {
|
||||
var doc = fs.readFileSync(path.join(root, name, 'index.js'), 'utf8')
|
||||
var packages = {}
|
||||
|
||||
doc.replace(/require\('(remark-lint-[^']+)'\)(?:, ([^\]]+)])?/g, function ($0, rule, option) {
|
||||
packages[rule] = option || null;
|
||||
return '';
|
||||
});
|
||||
doc.replace(/require\('(remark-lint-[^']+)'\)(?:, ([^\]]+)])?/g, function(
|
||||
$0,
|
||||
rule,
|
||||
option
|
||||
) {
|
||||
packages[rule] = option || null
|
||||
return ''
|
||||
})
|
||||
|
||||
return {
|
||||
name: name,
|
||||
packages: packages
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
rules(root).forEach(function (basename) {
|
||||
var base = path.resolve(root, basename);
|
||||
var pack = require(path.join(base, 'package.json'));
|
||||
var info = rule(base);
|
||||
var tests = info.tests;
|
||||
var author = parseAuthor(pack.author);
|
||||
var short = basename.replace(/^remark-/, '');
|
||||
var includes;
|
||||
rules(root).forEach(function(basename) {
|
||||
var base = path.resolve(root, basename)
|
||||
var pack = require(path.join(base, 'package.json'))
|
||||
var info = rule(base)
|
||||
var tests = info.tests
|
||||
var author = parseAuthor(pack.author)
|
||||
var short = basename.replace(/^remark-/, '')
|
||||
var includes
|
||||
var children = [
|
||||
u('html', '<!--This file is generated-->'),
|
||||
u('heading', {depth: 1}, [u('text', basename)])
|
||||
].concat(remark().parse(info.description).children);
|
||||
].concat(remark().parse(info.description).children)
|
||||
|
||||
if (basename !== pack.name) {
|
||||
throw new Error(
|
||||
'Expected package name (`' + pack.name + '`) to be the same as ' +
|
||||
'directory name (`' + basename + '`)'
|
||||
);
|
||||
'Expected package name (`' +
|
||||
pack.name +
|
||||
'`) to be the same as ' +
|
||||
'directory name (`' +
|
||||
basename +
|
||||
'`)'
|
||||
)
|
||||
}
|
||||
|
||||
includes = presets.filter(function (preset) {
|
||||
return basename in preset.packages;
|
||||
});
|
||||
includes = presets.filter(function(preset) {
|
||||
return basename in preset.packages
|
||||
})
|
||||
|
||||
children.push(u('heading', {depth: 2}, [u('text', 'Presets')]));
|
||||
children.push(u('heading', {depth: 2}, [u('text', 'Presets')]))
|
||||
|
||||
if (includes.length === 0) {
|
||||
children.push(u('paragraph', [
|
||||
u('text', 'This rule is not included in any default preset')
|
||||
]));
|
||||
children.push(
|
||||
u('paragraph', [
|
||||
u('text', 'This rule is not included in any default preset')
|
||||
])
|
||||
)
|
||||
} else {
|
||||
children.push(
|
||||
u('paragraph', [u('text', 'This rule is included in the following presets:')]),
|
||||
u('table', {align: []}, [
|
||||
u('tableRow', [
|
||||
u('tableCell', [u('text', 'Preset')]),
|
||||
u('tableCell', [u('text', 'Setting')])
|
||||
])
|
||||
].concat(includes.map(function (preset) {
|
||||
var url = remote + '/tree/master/packages/' + preset.name;
|
||||
var option = preset.packages[pack.name];
|
||||
u('paragraph', [
|
||||
u('text', 'This rule is included in the following presets:')
|
||||
]),
|
||||
u(
|
||||
'table',
|
||||
{align: []},
|
||||
[
|
||||
u('tableRow', [
|
||||
u('tableCell', [u('text', 'Preset')]),
|
||||
u('tableCell', [u('text', 'Setting')])
|
||||
])
|
||||
].concat(
|
||||
includes.map(function(preset) {
|
||||
var url = remote + '/tree/master/packages/' + preset.name
|
||||
var option = preset.packages[pack.name]
|
||||
|
||||
return u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('link', {url: url, title: null}, [u('inlineCode', preset.name)])
|
||||
]),
|
||||
u('tableCell', option ? [u('inlineCode', option)] : [])
|
||||
]);
|
||||
})))
|
||||
);
|
||||
return u('tableRow', [
|
||||
u('tableCell', [
|
||||
u('link', {url: url, title: null}, [
|
||||
u('inlineCode', preset.name)
|
||||
])
|
||||
]),
|
||||
u('tableCell', option ? [u('inlineCode', option)] : [])
|
||||
])
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Object.keys(tests).forEach(function (setting, index) {
|
||||
var fixtures = tests[setting];
|
||||
Object.keys(tests).forEach(function(setting, index) {
|
||||
var fixtures = tests[setting]
|
||||
|
||||
if (index === 0) {
|
||||
children.push(u('heading', {depth: 2}, [u('text', 'Example')]));
|
||||
children.push(u('heading', {depth: 2}, [u('text', 'Example')]))
|
||||
}
|
||||
|
||||
Object.keys(fixtures).forEach(function (fileName) {
|
||||
var fixture = fixtures[fileName];
|
||||
var label = inspect(JSON.parse(setting));
|
||||
var clean = fixture.input;
|
||||
Object.keys(fixtures).forEach(function(fileName) {
|
||||
var fixture = fixtures[fileName]
|
||||
var label = inspect(JSON.parse(setting))
|
||||
var clean = fixture.input
|
||||
|
||||
children.push(u('heading', {depth: 5}, [u('inlineCode', fileName)]));
|
||||
children.push(u('heading', {depth: 5}, [u('inlineCode', fileName)]))
|
||||
|
||||
if (label !== 'true') {
|
||||
children.push(
|
||||
@ -103,84 +123,96 @@ rules(root).forEach(function (basename) {
|
||||
u('inlineCode', label),
|
||||
u('text', '.')
|
||||
])
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if (fixture.input != null && fixture.input.trim() !== '') {
|
||||
children.push(u('heading', {depth: 6}, [u('text', 'In')]));
|
||||
children.push(u('heading', {depth: 6}, [u('text', 'In')]))
|
||||
|
||||
chars.forEach(function (char) {
|
||||
var next = clean.replace(char.in, char.out);
|
||||
chars.forEach(function(char) {
|
||||
var next = clean.replace(char.in, char.out)
|
||||
|
||||
if (clean !== next) {
|
||||
children.push(u('paragraph', [
|
||||
u('text', 'Note: '),
|
||||
u('inlineCode', char.char),
|
||||
u('text', ' represents ' + char.name + '.')
|
||||
]));
|
||||
children.push(
|
||||
u('paragraph', [
|
||||
u('text', 'Note: '),
|
||||
u('inlineCode', char.char),
|
||||
u('text', ' represents ' + char.name + '.')
|
||||
])
|
||||
)
|
||||
|
||||
clean = next;
|
||||
clean = next
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
children.push(u('code', {lang: 'markdown'}, fixture.input));
|
||||
children.push(u('code', {lang: 'markdown'}, fixture.input))
|
||||
}
|
||||
|
||||
children.push(u('heading', {depth: 6}, [u('text', 'Out')]));
|
||||
children.push(u('heading', {depth: 6}, [u('text', 'Out')]))
|
||||
|
||||
if (fixture.output.length === 0) {
|
||||
children.push(u('paragraph', [u('text', 'No messages.')]));
|
||||
children.push(u('paragraph', [u('text', 'No messages.')]))
|
||||
} else {
|
||||
children.push(u('code', {lang: 'text'}, fixture.output.join('\n')));
|
||||
children.push(u('code', {lang: 'text'}, fixture.output.join('\n')))
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
children = children.concat([
|
||||
u('heading', {depth: 2}, [u('text', 'Install')]),
|
||||
u('code', {lang: 'sh'}, 'npm install ' + basename),
|
||||
u('heading', {depth: 2}, [u('text', 'Usage')]),
|
||||
u('paragraph', [u('text', 'You probably want to use it on the CLI through a config file:')]),
|
||||
u('code', {lang: 'diff'}, [
|
||||
' ...',
|
||||
' "remarkConfig": {',
|
||||
' "plugins": [',
|
||||
' ...',
|
||||
' "lint",',
|
||||
'+ "' + short + '",',
|
||||
' ...',
|
||||
' ]',
|
||||
' }',
|
||||
' ...'
|
||||
].join('\n')),
|
||||
u('paragraph', [
|
||||
u('text', 'You probably want to use it on the CLI through a config file:')
|
||||
]),
|
||||
u(
|
||||
'code',
|
||||
{lang: 'diff'},
|
||||
[
|
||||
' ...',
|
||||
' "remarkConfig": {',
|
||||
' "plugins": [',
|
||||
' ...',
|
||||
' "lint",',
|
||||
'+ "' + short + '",',
|
||||
' ...',
|
||||
' ]',
|
||||
' }',
|
||||
' ...'
|
||||
].join('\n')
|
||||
),
|
||||
u('paragraph', [u('text', 'Or use it on the CLI directly')]),
|
||||
u('code', {lang: 'sh'}, 'remark -u lint -u ' + short + ' readme.md'),
|
||||
u('paragraph', [u('text', 'Or use this on the API:')]),
|
||||
u('code', {lang: 'diff'}, [
|
||||
' var remark = require(\'remark\');',
|
||||
' var report = require(\'vfile-reporter\');',
|
||||
'',
|
||||
' remark()',
|
||||
' .use(require(\'remark-lint\'))',
|
||||
'+ .use(require(\'' + basename + '\'))',
|
||||
' .process(\'_Emphasis_ and **importance**\', function (err, file) {',
|
||||
' console.error(report(err || file));',
|
||||
' });'
|
||||
].join('\n')),
|
||||
u(
|
||||
'code',
|
||||
{lang: 'diff'},
|
||||
[
|
||||
" var remark = require('remark');",
|
||||
" var report = require('vfile-reporter');",
|
||||
'',
|
||||
' remark()',
|
||||
" .use(require('remark-lint'))",
|
||||
"+ .use(require('" + basename + "'))",
|
||||
" .process('_Emphasis_ and **importance**', function (err, file) {",
|
||||
' console.error(report(err || file));',
|
||||
' });'
|
||||
].join('\n')
|
||||
),
|
||||
u('heading', {depth: 2}, [u('text', 'License')]),
|
||||
u('paragraph', [
|
||||
u('link', {url: remote + '/blob/master/LICENSE'}, [u('text', pack.license)]),
|
||||
u('link', {url: remote + '/blob/master/LICENSE'}, [
|
||||
u('text', pack.license)
|
||||
]),
|
||||
u('text', ' © '),
|
||||
u('link', {url: author.url}, [u('text', author.name)])
|
||||
])
|
||||
]);
|
||||
])
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(base, 'readme.md'),
|
||||
remark().stringify(u('root', children))
|
||||
);
|
||||
)
|
||||
|
||||
console.log(
|
||||
chalk.green('✓') + ' wrote `readme.md` in `' + basename + '`'
|
||||
);
|
||||
});
|
||||
console.log(chalk.green('✓') + ' wrote `readme.md` in `' + basename + '`')
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -25,4 +25,4 @@ module.exports = [
|
||||
out: '\n',
|
||||
char: '␊'
|
||||
}
|
||||
];
|
||||
]
|
||||
|
@ -1,34 +1,37 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var path = require('path');
|
||||
var zone = require('mdast-zone');
|
||||
var u = require('unist-builder');
|
||||
var presets = require('../util/presets');
|
||||
var path = require('path')
|
||||
var zone = require('mdast-zone')
|
||||
var u = require('unist-builder')
|
||||
var presets = require('../util/presets')
|
||||
|
||||
var root = path.join(process.cwd(), 'packages');
|
||||
var root = path.join(process.cwd(), 'packages')
|
||||
|
||||
module.exports = listOfPresets;
|
||||
module.exports = listOfPresets
|
||||
|
||||
function listOfPresets() {
|
||||
return transformer;
|
||||
return transformer
|
||||
}
|
||||
|
||||
function transformer(tree) {
|
||||
zone(tree, 'presets', replace);
|
||||
zone(tree, 'presets', replace)
|
||||
}
|
||||
|
||||
function replace(start, nodes, end) {
|
||||
var items = presets(root).map(function (basename) {
|
||||
var pack = require(path.join(root, basename, 'package.json'));
|
||||
var description = pack.description.replace(/^remark preset to configure remark-lint with ?/i, '');
|
||||
return [start, u('list', {ordered: false}, presets(root).map(item)), end]
|
||||
|
||||
function item(basename) {
|
||||
var pack = require(path.join(root, basename, 'package.json'))
|
||||
var description = pack.description.replace(
|
||||
/^remark preset to configure remark-lint with ?/i,
|
||||
''
|
||||
)
|
||||
|
||||
return u('listItem', [
|
||||
u('paragraph', [
|
||||
u('link', {url: pack.repository}, [u('inlineCode', basename)]),
|
||||
u('text', ' — ' + description)
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
return [start, u('list', {ordered: false}, items), end];
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,35 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var path = require('path');
|
||||
var zone = require('mdast-zone');
|
||||
var u = require('unist-builder');
|
||||
var rules = require('../util/rules');
|
||||
var path = require('path')
|
||||
var zone = require('mdast-zone')
|
||||
var u = require('unist-builder')
|
||||
var rules = require('../util/rules')
|
||||
|
||||
var root = path.join(process.cwd(), 'packages');
|
||||
var root = path.join(process.cwd(), 'packages')
|
||||
|
||||
module.exports = listOfRules;
|
||||
module.exports = listOfRules
|
||||
|
||||
function listOfRules() {
|
||||
return transformer;
|
||||
return transformer
|
||||
}
|
||||
|
||||
function transformer(tree) {
|
||||
zone(tree, 'rules', replace);
|
||||
zone(tree, 'rules', replace)
|
||||
}
|
||||
|
||||
function replace(start, nodes, end) {
|
||||
var items = rules(root).map(function (basename) {
|
||||
var name = basename.slice('remark-lint-'.length);
|
||||
var pack = require(path.join(root, basename, 'package.json'));
|
||||
var description = pack.description.replace(/^remark-lint rule to ?/i, '');
|
||||
return [start, u('list', {ordered: false}, rules(root).map(item)), end]
|
||||
|
||||
function item(basename) {
|
||||
var name = basename.slice('remark-lint-'.length)
|
||||
var pack = require(path.join(root, basename, 'package.json'))
|
||||
var description = pack.description.replace(/^remark-lint rule to ?/i, '')
|
||||
|
||||
return u('listItem', [
|
||||
u('paragraph', [
|
||||
u('link', {url: pack.repository}, [u('inlineCode', name)]),
|
||||
u('text', ' — ' + description)
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
return [start, u('list', {ordered: false}, items), end];
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = find;
|
||||
module.exports = find
|
||||
|
||||
find.all = findAll;
|
||||
find.all = findAll
|
||||
|
||||
/* Find the first tag in `tags` with a type set to `key`. */
|
||||
function find(tags, key) {
|
||||
var value = null;
|
||||
var value = null
|
||||
|
||||
tags.some(function (tag) {
|
||||
tags.some(function(tag) {
|
||||
if (tag && tag.type === key) {
|
||||
value = tag;
|
||||
value = tag
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
return false
|
||||
})
|
||||
|
||||
return value && value.string;
|
||||
return value && value.string
|
||||
}
|
||||
|
||||
/* Find the first tag in `tags` with a type set to `key`. */
|
||||
function findAll(tags, key) {
|
||||
return tags
|
||||
.filter(function (tag) {
|
||||
return tag && tag.type === key;
|
||||
.filter(function(tag) {
|
||||
return tag && tag.type === key
|
||||
})
|
||||
.map(function(tag) {
|
||||
return tag.string
|
||||
})
|
||||
.map(function (tag) {
|
||||
return tag.string;
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs');
|
||||
var fs = require('fs')
|
||||
|
||||
module.exports = rulesSync;
|
||||
module.exports = rulesSync
|
||||
|
||||
function rulesSync(filePath) {
|
||||
return fs.readdirSync(filePath).filter(filter);
|
||||
return fs.readdirSync(filePath).filter(filter)
|
||||
}
|
||||
|
||||
function filter(basename) {
|
||||
return /remark-preset-lint/.test(basename);
|
||||
return /remark-preset-lint/.test(basename)
|
||||
}
|
||||
|
@ -1,73 +1,78 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var dox = require('dox');
|
||||
var strip = require('strip-indent');
|
||||
var trim = require('trim');
|
||||
var find = require('./find');
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var dox = require('dox')
|
||||
var strip = require('strip-indent')
|
||||
var trim = require('trim')
|
||||
var find = require('./find')
|
||||
|
||||
module.exports = ruleSync;
|
||||
module.exports = ruleSync
|
||||
|
||||
/* Get information for a rule at `filePath`. */
|
||||
function ruleSync(filePath) {
|
||||
var ruleId = path.basename(filePath);
|
||||
var result = {};
|
||||
var tests = {};
|
||||
var description;
|
||||
var code;
|
||||
var tags;
|
||||
var name;
|
||||
var ruleId = path.basename(filePath)
|
||||
var result = {}
|
||||
var tests = {}
|
||||
var description
|
||||
var code
|
||||
var tags
|
||||
var name
|
||||
|
||||
ruleId = ruleId.slice('remark-lint-'.length);
|
||||
code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf-8');
|
||||
tags = dox.parseComments(code)[0].tags;
|
||||
description = find(tags, 'fileoverview');
|
||||
name = find(tags, 'module');
|
||||
ruleId = ruleId.slice('remark-lint-'.length)
|
||||
code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf-8')
|
||||
tags = dox.parseComments(code)[0].tags
|
||||
description = find(tags, 'fileoverview')
|
||||
name = find(tags, 'module')
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (name !== ruleId) {
|
||||
throw new Error(
|
||||
ruleId + ' has an invalid `@module`: ' + name
|
||||
);
|
||||
throw new Error(ruleId + ' has an invalid `@module`: ' + name)
|
||||
}
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (!description) {
|
||||
throw new Error(ruleId + ' is missing a `@fileoverview`');
|
||||
throw new Error(ruleId + ' is missing a `@fileoverview`')
|
||||
}
|
||||
|
||||
description = strip(description);
|
||||
description = strip(description)
|
||||
|
||||
result.ruleId = ruleId;
|
||||
result.description = trim(description);
|
||||
result.tests = tests;
|
||||
result.filePath = filePath;
|
||||
result.ruleId = ruleId
|
||||
result.description = trim(description)
|
||||
result.tests = tests
|
||||
result.filePath = filePath
|
||||
|
||||
find.all(tags, 'example').map(strip).forEach(function (example) {
|
||||
var lines = example.split('\n');
|
||||
var value = strip(lines.slice(1).join('\n'));
|
||||
var info;
|
||||
var setting;
|
||||
var context;
|
||||
var name;
|
||||
find
|
||||
.all(tags, 'example')
|
||||
.map(strip)
|
||||
.forEach(check)
|
||||
|
||||
return result
|
||||
|
||||
function check(example) {
|
||||
var lines = example.split('\n')
|
||||
var value = strip(lines.slice(1).join('\n'))
|
||||
var info
|
||||
var setting
|
||||
var context
|
||||
var name
|
||||
|
||||
try {
|
||||
info = JSON.parse(lines[0]);
|
||||
info = JSON.parse(lines[0])
|
||||
} catch (err) {
|
||||
/* istanbul ignore next */
|
||||
throw new Error(
|
||||
'Could not parse example in ' + ruleId + ':\n' + err.stack
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
setting = JSON.stringify(info.setting || true);
|
||||
context = tests[setting];
|
||||
name = info.name;
|
||||
setting = JSON.stringify(info.setting || true)
|
||||
context = tests[setting]
|
||||
name = info.name
|
||||
|
||||
if (!context) {
|
||||
context = [];
|
||||
tests[setting] = context;
|
||||
context = []
|
||||
tests[setting] = context
|
||||
}
|
||||
|
||||
if (!info.label) {
|
||||
@ -76,31 +81,32 @@ function ruleSync(filePath) {
|
||||
setting: setting,
|
||||
input: value,
|
||||
output: []
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (info.label !== 'input' && info.label !== 'output') {
|
||||
throw new Error(
|
||||
'Expected `input` or `ouput` for `label` in ' +
|
||||
ruleId + ', not `' + info.label + '`'
|
||||
);
|
||||
ruleId +
|
||||
', not `' +
|
||||
info.label +
|
||||
'`'
|
||||
)
|
||||
}
|
||||
|
||||
if (!context[name]) {
|
||||
context[name] = {config: info.config || {}};
|
||||
context[name] = {config: info.config || {}}
|
||||
}
|
||||
|
||||
context[name].setting = setting;
|
||||
context[name].setting = setting
|
||||
|
||||
if (info.label === 'output') {
|
||||
value = value.split('\n');
|
||||
value = value.split('\n')
|
||||
}
|
||||
|
||||
context[name][info.label] = value;
|
||||
});
|
||||
|
||||
return result;
|
||||
context[name][info.label] = value
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs');
|
||||
var fs = require('fs')
|
||||
|
||||
module.exports = rulesSync;
|
||||
module.exports = rulesSync
|
||||
|
||||
function rulesSync(filePath) {
|
||||
return fs.readdirSync(filePath).filter(filter);
|
||||
return fs.readdirSync(filePath).filter(filter)
|
||||
}
|
||||
|
||||
function filter(basename) {
|
||||
return /remark-lint/.test(basename) && basename !== 'remark-lint';
|
||||
return /remark-lint/.test(basename) && basename !== 'remark-lint'
|
||||
}
|
||||
|
452
test.js
452
test.js
@ -1,22 +1,22 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
/* eslint-disable max-params */
|
||||
|
||||
var path = require('path');
|
||||
var test = require('tape');
|
||||
var vfile = require('to-vfile');
|
||||
var removePosition = require('unist-util-remove-position');
|
||||
var remark = require('remark');
|
||||
var rules = require('./script/util/rules');
|
||||
var rule = require('./script/util/rule');
|
||||
var lint = require('./packages/remark-lint');
|
||||
var noHeadingPunctuation = require('./packages/remark-lint-no-heading-punctuation');
|
||||
var noMultipleToplevelHeadings = require('./packages/remark-lint-no-multiple-toplevel-headings');
|
||||
var finalNewline = require('./packages/remark-lint-final-newline');
|
||||
var chars = require('./script/characters');
|
||||
var path = require('path')
|
||||
var test = require('tape')
|
||||
var vfile = require('to-vfile')
|
||||
var removePosition = require('unist-util-remove-position')
|
||||
var remark = require('remark')
|
||||
var rules = require('./script/util/rules')
|
||||
var rule = require('./script/util/rule')
|
||||
var lint = require('./packages/remark-lint')
|
||||
var noHeadingPunctuation = require('./packages/remark-lint-no-heading-punctuation')
|
||||
var noMultipleToplevelHeadings = require('./packages/remark-lint-no-multiple-toplevel-headings')
|
||||
var finalNewline = require('./packages/remark-lint-final-newline')
|
||||
var chars = require('./script/characters')
|
||||
|
||||
test('core', function (t) {
|
||||
t.test('should work', function (st) {
|
||||
test('core', function(t) {
|
||||
t.test('should work', function(st) {
|
||||
var doc = [
|
||||
'# A heading',
|
||||
'',
|
||||
@ -25,330 +25,340 @@ test('core', function (t) {
|
||||
'<!--lint ignore-->',
|
||||
'',
|
||||
'# Another main heading.'
|
||||
].join('\n');
|
||||
].join('\n')
|
||||
|
||||
st.plan(4);
|
||||
st.plan(2)
|
||||
|
||||
remark()
|
||||
.use(noHeadingPunctuation)
|
||||
.use(noMultipleToplevelHeadings)
|
||||
.use(lint)
|
||||
.process(vfile({path: 'virtual.md', contents: doc}), function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
.process(vfile({path: 'virtual.md', contents: doc}), function(err, file) {
|
||||
st.deepEqual(
|
||||
file.messages.map(String),
|
||||
[err].concat(file.messages.map(String)),
|
||||
[
|
||||
null,
|
||||
'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings',
|
||||
'virtual.md:3:1-3:24: Don’t use multiple top level headings (3:1)'
|
||||
],
|
||||
'should support `remark-lint` last'
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
remark()
|
||||
.use(lint)
|
||||
.use(noHeadingPunctuation)
|
||||
.use(noMultipleToplevelHeadings)
|
||||
.process(vfile({path: 'virtual.md', contents: doc}), function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
.process(vfile({path: 'virtual.md', contents: doc}), function(err, file) {
|
||||
st.deepEqual(
|
||||
file.messages.map(String),
|
||||
[err].concat(file.messages.map(String)),
|
||||
[
|
||||
null,
|
||||
'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings',
|
||||
'virtual.md:3:1-3:24: Don’t use multiple top level headings (3:1)'
|
||||
],
|
||||
'should support `remark-lint` first'
|
||||
);
|
||||
});
|
||||
});
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support no rules', function (st) {
|
||||
st.plan(2);
|
||||
t.test('should support no rules', function(st) {
|
||||
st.plan(1)
|
||||
|
||||
remark().use(lint).process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.deepEqual(
|
||||
file.messages.map(String),
|
||||
[],
|
||||
'should warn for missing new lines'
|
||||
);
|
||||
});
|
||||
});
|
||||
remark()
|
||||
.use(lint)
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null],
|
||||
'should warn for missing new lines'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support successful rules', function (st) {
|
||||
st.plan(2);
|
||||
t.test('should support successful rules', function(st) {
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline)
|
||||
.process('', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.deepEqual(file.messages, [], 'should support successful rules');
|
||||
});
|
||||
});
|
||||
.process('', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null],
|
||||
'should support successful rules'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a list with a severity', function (st) {
|
||||
st.plan(3);
|
||||
t.test('should support a list with a severity', function(st) {
|
||||
st.plan(2)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, [2])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
'should trigger fatally (1)'
|
||||
);
|
||||
st.equal(file.messages[0].fatal, true, 'should trigger fatally (2)');
|
||||
});
|
||||
});
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should emit fatally (1)'
|
||||
)
|
||||
st.equal(file.messages[0].fatal, true, 'should emit fatally (2)')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a boolean (`true`)', function (st) {
|
||||
t.test('should support a boolean (`true`)', function(st) {
|
||||
/* Note! This is handled by unified. */
|
||||
st.plan(2);
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, true)
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
'should trigger'
|
||||
);
|
||||
});
|
||||
});
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should emit'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a boolean (`false`)', function (st) {
|
||||
t.test('should support a boolean (`false`)', function(st) {
|
||||
/* Note! This is handled by unified. */
|
||||
st.plan(2);
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, false)
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.deepEqual(file.messages, [], 'should not trigger');
|
||||
});
|
||||
});
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null],
|
||||
'should not emit'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a list with a boolean severity (true, for on)', function (st) {
|
||||
st.plan(2);
|
||||
t.test(
|
||||
'should support a list with a boolean severity (true, for on)',
|
||||
function(st) {
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, [true])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
'should trigger'
|
||||
);
|
||||
});
|
||||
});
|
||||
remark()
|
||||
.use(finalNewline, [true])
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should emit'
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
t.test('should support a list with a boolean severity (false, for off)', function (st) {
|
||||
st.plan(2);
|
||||
t.test(
|
||||
'should support a list with boolean severity (false, for off)',
|
||||
function(st) {
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, [false])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.deepEqual(file.messages, [], 'should not trigger');
|
||||
});
|
||||
});
|
||||
remark()
|
||||
.use(finalNewline, [false])
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null],
|
||||
'should not emit'
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
t.test('should support a list with a string severity (`error`)', function (st) {
|
||||
st.plan(3);
|
||||
t.test('should support a list with string severity (`error`)', function(st) {
|
||||
st.plan(2)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, ['error'])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
'should trigger fatally (1)'
|
||||
);
|
||||
st.equal(file.messages[0].fatal, true, 'should trigger fatally (2)');
|
||||
});
|
||||
});
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should emit fatally (1)'
|
||||
)
|
||||
st.equal(file.messages[0].fatal, true, 'should emit fatally (2)')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a list with a string severity (`on`)', function (st) {
|
||||
st.plan(3);
|
||||
t.test('should support a list with a string severity (`on`)', function(st) {
|
||||
st.plan(2)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, ['on'])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should message'
|
||||
);
|
||||
st.equal(file.messages[0].fatal, false, 'should *not* trigger fatally');
|
||||
});
|
||||
});
|
||||
)
|
||||
st.equal(file.messages[0].fatal, false, 'should not emit fatally')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a list with a string severity (`warn`)', function (st) {
|
||||
st.plan(3);
|
||||
t.test('should support a list with a string severity (`warn`)', function(st) {
|
||||
st.plan(2)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, ['warn'])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.equal(
|
||||
file.messages.join(),
|
||||
'1:1: Missing newline character at end of file',
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null, '1:1: Missing newline character at end of file'],
|
||||
'should message'
|
||||
);
|
||||
st.equal(file.messages[0].fatal, false, 'should *not* trigger fatally');
|
||||
});
|
||||
});
|
||||
)
|
||||
st.equal(file.messages[0].fatal, false, 'should not emit fatally')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should support a list with a string severity (`off`)', function (st) {
|
||||
st.plan(2);
|
||||
t.test('should support a list with a string severity (`off`)', function(st) {
|
||||
st.plan(1)
|
||||
|
||||
remark()
|
||||
.use(finalNewline, ['off'])
|
||||
.process('.', function (err, file) {
|
||||
st.ifErr(err, 'should not fail');
|
||||
st.deepEqual(file.messages, [], 'should disable `final-newline`');
|
||||
});
|
||||
});
|
||||
.process('.', function(err, file) {
|
||||
st.deepEqual(
|
||||
[err].concat(file.messages.map(String)),
|
||||
[null],
|
||||
'should disable `final-newline`'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should fail on invalid severities', function (st) {
|
||||
t.test('should fail on invalid severities', function(st) {
|
||||
st.throws(
|
||||
function () {
|
||||
remark().use(finalNewline, [3]).freeze();
|
||||
function() {
|
||||
remark()
|
||||
.use(finalNewline, [3])
|
||||
.freeze()
|
||||
},
|
||||
/^Error: Invalid severity `3` for `final-newline`, expected 0, 1, or 2$/,
|
||||
'should throw when too high'
|
||||
);
|
||||
)
|
||||
|
||||
st.throws(
|
||||
function () {
|
||||
remark().use(finalNewline, [-1]).freeze();
|
||||
function() {
|
||||
remark()
|
||||
.use(finalNewline, [-1])
|
||||
.freeze()
|
||||
},
|
||||
/^Error: Invalid severity `-1` for `final-newline`, expected 0, 1, or 2$/,
|
||||
'should throw too low'
|
||||
);
|
||||
)
|
||||
|
||||
st.end();
|
||||
});
|
||||
st.end()
|
||||
})
|
||||
|
||||
t.end();
|
||||
});
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('rules', function (t) {
|
||||
var root = path.join(process.cwd(), 'packages');
|
||||
var all = rules(root);
|
||||
test('rules', function(t) {
|
||||
var root = path.join(process.cwd(), 'packages')
|
||||
var all = rules(root)
|
||||
|
||||
t.plan(all.length);
|
||||
t.plan(all.length)
|
||||
|
||||
all.forEach(each);
|
||||
all.forEach(each)
|
||||
|
||||
function each(basename) {
|
||||
var base = path.resolve(root, basename);
|
||||
var info = rule(base);
|
||||
var fn = require(base);
|
||||
var base = path.resolve(root, basename)
|
||||
var info = rule(base)
|
||||
var fn = require(base)
|
||||
|
||||
t.test(info.ruleId, one);
|
||||
t.test(info.ruleId, one)
|
||||
|
||||
function one(sst) {
|
||||
assertRule(sst, fn, info);
|
||||
function one(st) {
|
||||
assertRule(st, fn, info)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
/* Assert a rule. */
|
||||
function assertRule(t, rule, info) {
|
||||
var tests = info.tests;
|
||||
var tests = info.tests
|
||||
|
||||
Object.keys(tests).forEach(function (setting) {
|
||||
var fixture = tests[setting];
|
||||
var config = JSON.parse(setting);
|
||||
Object.keys(tests).forEach(function(setting) {
|
||||
var fixture = tests[setting]
|
||||
var config = JSON.parse(setting)
|
||||
|
||||
t.test(setting, function (st) {
|
||||
Object.keys(fixture).forEach(function (name) {
|
||||
st.test(name, function (sst) {
|
||||
assertFixture(sst, rule, info, fixture[name], name, config);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
t.test(setting, function(st) {
|
||||
Object.keys(fixture).forEach(function(name) {
|
||||
st.test(name, function(sst) {
|
||||
assertFixture(sst, rule, info, fixture[name], name, config)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.end();
|
||||
t.end()
|
||||
}
|
||||
|
||||
function assertFixture(t, rule, info, fixture, basename, setting) {
|
||||
var ruleId = info.ruleId;
|
||||
var file = vfile(basename);
|
||||
var expected = fixture.output;
|
||||
var positionless = fixture.config.positionless;
|
||||
var proc = remark().use(rule, setting).data('settings', fixture.config);
|
||||
var ruleId = info.ruleId
|
||||
var file = vfile(basename)
|
||||
var expected = fixture.output
|
||||
var positionless = fixture.config.positionless
|
||||
var proc = remark()
|
||||
.use(rule, setting)
|
||||
.data('settings', fixture.config)
|
||||
|
||||
file.contents = preprocess(fixture.input || '');
|
||||
file.contents = preprocess(fixture.input || '')
|
||||
|
||||
t.plan(positionless ? 1 : 2);
|
||||
t.plan(positionless ? 1 : 2)
|
||||
|
||||
try {
|
||||
proc.runSync(proc.parse(file), file);
|
||||
proc.runSync(proc.parse(file), file)
|
||||
} catch (err) {
|
||||
if (err && err.source !== 'remark-lint') {
|
||||
throw err;
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
t.deepEqual(
|
||||
normalize(file.messages),
|
||||
expected,
|
||||
'should equal with position'
|
||||
);
|
||||
|
||||
file.messages.forEach(function (message) {
|
||||
file.messages.forEach(function(message) {
|
||||
if (message.ruleId !== ruleId) {
|
||||
throw new Error(
|
||||
'Expected `' + ruleId + '`, not `' +
|
||||
message.ruleId + '` as `ruleId` for ' +
|
||||
message
|
||||
);
|
||||
'Expected `' +
|
||||
ruleId +
|
||||
'`, not `' +
|
||||
message.ruleId +
|
||||
'` as `ruleId` for ' +
|
||||
message
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
t.deepEqual(normalize(file.messages), expected, 'should equal with position')
|
||||
|
||||
if (!positionless) {
|
||||
file.messages = [];
|
||||
file.messages = []
|
||||
|
||||
try {
|
||||
remark()
|
||||
.use(function () {
|
||||
return removePosition;
|
||||
})
|
||||
.use(rule, setting)
|
||||
.processSync(file);
|
||||
} catch (err) {
|
||||
console.log('err: ', err);
|
||||
}
|
||||
remark()
|
||||
.use(clear)
|
||||
.use(rule, setting)
|
||||
.processSync(file)
|
||||
|
||||
t.deepEqual(
|
||||
normalize(file.messages),
|
||||
[],
|
||||
'should equal without position'
|
||||
);
|
||||
t.deepEqual(normalize(file.messages), [], 'should equal without position')
|
||||
}
|
||||
|
||||
file.messages = [];
|
||||
function clear() {
|
||||
return removePosition
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(messages) {
|
||||
return messages.map(function (message) {
|
||||
var value = String(message);
|
||||
return value.slice(value.indexOf(':') + 1);
|
||||
});
|
||||
return messages.map(function(message) {
|
||||
var value = String(message)
|
||||
return value.slice(value.indexOf(':') + 1)
|
||||
})
|
||||
}
|
||||
|
||||
function preprocess(value) {
|
||||
chars.forEach(function (char) {
|
||||
value = value.replace(char.in, char.out);
|
||||
});
|
||||
chars.forEach(function(char) {
|
||||
value = value.replace(char.in, char.out)
|
||||
})
|
||||
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user