Enable jsdoc/require-jsdoc rule, fix all violations (fixes #85).

This commit is contained in:
David Anson 2020-01-23 19:42:46 -08:00
parent a1249ad24d
commit 74af9f82fb
13 changed files with 257 additions and 59 deletions

View file

@ -68,7 +68,6 @@
"jsdoc/check-indentation": "error",
"jsdoc/check-syntax": "error",
"jsdoc/match-description": "error",
"jsdoc/require-description": "error",
"jsdoc/require-jsdoc": "off"
"jsdoc/require-description": "error"
}
}

View file

@ -1,5 +1,7 @@
"use strict";
/* eslint-disable jsdoc/require-jsdoc */
(function main() {
// DOM elements
var markdown = document.getElementById("markdown");

View file

@ -58,6 +58,17 @@ module.exports.isBlankLine = function isBlankLine(line) {
return !line || !line.trim() || !line.replace(blankLineRe, "").trim();
};
/**
* Compare function for Array.prototype.sort for ascending order of numbers.
*
* @param {number} a First number.
* @param {number} b Second number.
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
*/
module.exports.numericSortAscending = function numericSortAscending(a, b) {
return a - b;
};
// Returns true iff the sorted array contains the specified element
module.exports.includesSorted = function includesSorted(array, element) {
let left = 0;
@ -126,7 +137,28 @@ module.exports.unescapeMarkdown =
});
};
// Returns the indent for a token
/**
* Return the string representation of a fence markup character.
*
* @param {string} markup Fence string.
* @returns {string} String representation.
*/
module.exports.fencedCodeBlockStyleFor =
function fencedCodeBlockStyleFor(markup) {
switch (markup[0]) {
case "~":
return "tilde";
default:
return "backtick";
}
};
/**
* Return the number of characters of indent for a token.
*
* @param {Object} token MarkdownItToken instance.
* @returns {number} Characters of indent.
*/
function indentFor(token) {
const line = token.line.replace(/^[\s>]*(> |>)/, "");
return line.length - line.trimLeft().length;
@ -144,7 +176,32 @@ module.exports.headingStyleFor = function headingStyleFor(token) {
return "setext";
};
// Calls the provided function for each matching token
/**
* Return the string representation of an unordered list marker.
*
* @param {Object} token MarkdownItToken instance.
* @returns {string} String representation.
*/
module.exports.unorderedListStyleFor = function unorderedListStyleFor(token) {
switch (token.markup) {
case "-":
return "dash";
case "+":
return "plus";
// case "*":
default:
return "asterisk";
}
};
/**
* Calls the provided function for each matching token.
*
* @param {Object} params RuleParams instance.
* @param {string} type Token type identifier.
* @param {Function} handler Callback function.
* @returns {void}
*/
function filterTokens(params, type, handler) {
params.tokens.forEach(function forToken(token) {
if (token.type === type) {
@ -338,7 +395,17 @@ module.exports.forEachInlineCodeSpan =
}
};
// Adds a generic error object via the onError callback
/**
* Adds a generic error object via the onError callback.
*
* @param {Object} onError RuleOnError instance.
* @param {number} lineNumber Line number.
* @param {string} [detail] Error details.
* @param {string} [context] Error context.
* @param {number[]} [range] Column and length of error.
* @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
* @returns {void}
*/
function addError(onError, lineNumber, detail, context, range, fixInfo) {
onError({
lineNumber,
@ -403,7 +470,12 @@ module.exports.frontMatterHasTitle =
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
};
// Gets the most common line ending, falling back to platform default
/**
* Gets the most common line ending, falling back to the platform default.
*
* @param {string} input Markdown content to analyze.
* @returns {string} Preferred line ending.
*/
function getPreferredLineEnding(input) {
let cr = 0;
let lf = 0;
@ -437,7 +509,13 @@ function getPreferredLineEnding(input) {
}
module.exports.getPreferredLineEnding = getPreferredLineEnding;
// Normalizes the fields of a fixInfo object
/**
* Normalizes the fields of a RuleOnErrorFixInfo instance.
*
* @param {Object} fixInfo RuleOnErrorFixInfo instance.
* @param {number} [lineNumber] Line number.
* @returns {Object} Normalized RuleOnErrorFixInfo instance.
*/
function normalizeFixInfo(fixInfo, lineNumber) {
return {
"lineNumber": fixInfo.lineNumber || lineNumber,
@ -447,7 +525,14 @@ function normalizeFixInfo(fixInfo, lineNumber) {
};
}
// Fixes the specifide error on a line
/**
* Fixes the specified error on a line of Markdown content.
*
* @param {string} line Line of Markdown content.
* @param {Object} fixInfo RuleOnErrorFixInfo instance.
* @param {string} lineEnding Line ending to use.
* @returns {string} Fixed content.
*/
function applyFix(line, fixInfo, lineEnding) {
const { editColumn, deleteCount, insertText } = normalizeFixInfo(fixInfo);
const editIndex = editColumn - 1;

View file

@ -79,7 +79,7 @@ declare function markdownlintSync(options: Options): {
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
* @returns {void}
*/
declare function readConfig(file: string, parsers: ReadConfigCallback | ConfigurationParser[], callback?: ReadConfigCallback): void;
declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, callback?: ReadConfigCallback): void;
/**
* Read specified configuration file synchronously.
*

View file

@ -12,7 +12,13 @@ const cache = require("./cache");
const deprecatedRuleNames = [ "MD002", "MD006" ];
// Validates the list of rules for structure and reuse
/**
* Validate the list of rules for structure and reuse.
*
* @param {Rule[]} ruleList List of rules.
* @returns {string} Error message if validation fails.
*/
function validateRuleList(ruleList) {
let result = null;
if (ruleList.length === rules.length) {
@ -22,6 +28,7 @@ function validateRuleList(ruleList) {
const allIds = {};
ruleList.forEach(function forRule(rule, index) {
const customIndex = index - rules.length;
// eslint-disable-next-line jsdoc/require-jsdoc
function newError(property) {
return new Error(
"Property '" + property + "' of custom rule at index " +
@ -72,8 +79,14 @@ function validateRuleList(ruleList) {
return result;
}
// Class for results with toString for pretty display
/**
* Creates a LintResults instance with toString for pretty display.
*
* @param {Rule[]} ruleList List of rules.
* @returns {LintResults} New LintResults instance.
*/
function newResults(ruleList) {
// eslint-disable-next-line jsdoc/require-jsdoc
function Results() {}
Results.prototype.toString = function toString(useAlias) {
const that = this;
@ -123,10 +136,17 @@ function newResults(ruleList) {
});
return results.join("\n");
};
// @ts-ignore
return new Results();
}
// Remove front matter (if present at beginning of content)
/**
* Remove front matter (if present at beginning of content).
*
* @param {string} content Markdown content.
* @param {RegExp} frontMatter Regular expression to match front matter.
* @returns {Object} Trimmed content and front matter lines.
*/
function removeFrontMatter(content, frontMatter) {
let frontMatterLines = [];
if (frontMatter) {
@ -147,7 +167,13 @@ function removeFrontMatter(content, frontMatter) {
};
}
// Annotate tokens with line/lineNumber
/**
* Annotate tokens with line/lineNumber.
*
* @param {MarkdownItToken[]} tokens Array of markdown-it tokens.
* @param {string[]} lines Lines of Markdown content.
* @returns {void}
*/
function annotateTokens(tokens, lines) {
let tbodyMap = null;
tokens.forEach(function forToken(token) {
@ -192,7 +218,12 @@ function annotateTokens(tokens, lines) {
});
}
// Map rule names/tags to canonical rule name
/**
* Map rule names/tags to canonical rule name.
*
* @param {Rule[]} ruleList List of rules.
* @returns {Object.<string, string[]>} Map of alias to rule name.
*/
function mapAliasToRuleNames(ruleList) {
const aliasToRuleNames = {};
// const tagToRuleNames = {};
@ -219,10 +250,19 @@ function mapAliasToRuleNames(ruleList) {
// console.log("* **" + tag + "** - " +
// aliasToRuleNames[tag.toUpperCase()].join(", "));
// });
// @ts-ignore
return aliasToRuleNames;
}
// Apply (and normalize) config
/**
* Apply (and normalize) configuration object.
*
* @param {Rule[]} ruleList List of rules.
* @param {Configuration} config Configuration object.
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
* names.
* @returns {Configuration} Effective configuration.
*/
function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
const defaultKey = Object.keys(config).filter(
(key) => key.toUpperCase() === "DEFAULT"
@ -253,10 +293,25 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
return effectiveConfig;
}
// Create mapping of enabled rules per line
/**
* Create a mapping of enabled rules per line.
*
* @param {Rule[]} ruleList List of rules.
* @param {string[]} lines List of content lines.
* @param {string[]} frontMatterLines List of front matter lines.
* @param {boolean} noInlineConfig Whether to allow inline configuration.
* @param {Configuration} effectiveConfig Effective configuration.
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
* names.
* @returns {Object.<string, RuleConfiguration>[]} Enabled rules for each line.
*/
function getEnabledRulesPerLineNumber(
ruleList, lines, frontMatterLines, noInlineConfig,
effectiveConfig, aliasToRuleNames) {
ruleList,
lines,
frontMatterLines,
noInlineConfig,
effectiveConfig,
aliasToRuleNames) {
let enabledRules = {};
const allRuleNames = [];
ruleList.forEach((rule) => {
@ -265,6 +320,7 @@ function getEnabledRulesPerLineNumber(
enabledRules[ruleName] = !!effectiveConfig[ruleName];
});
let capturedRules = enabledRules;
// eslint-disable-next-line jsdoc/require-jsdoc
function forMatch(match, byLine) {
const action = match[1].toUpperCase();
if (action === "CAPTURE") {
@ -312,22 +368,53 @@ function getEnabledRulesPerLineNumber(
return enabledRulesPerLineNumber;
}
// Array.sort comparison for objects in errors array
/**
* Compare function for Array.prototype.sort for ascending order of errors.
*
* @param {LintError} a First error.
* @param {LintError} b Second error.
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
*/
function lineNumberComparison(a, b) {
return a.lineNumber - b.lineNumber;
}
// Function to return true for all inputs
/**
* Filter function to include everything.
*
* @returns {boolean} True.
*/
function filterAllValues() {
return true;
}
// Function to return unique values from a sorted errors array
/**
* Function to return unique values from a sorted errors array.
*
* @param {LintError} value Error instance.
* @param {number} index Index in array.
* @param {LintError[]} array Array of errors.
* @returns {boolean} Filter value.
*/
function uniqueFilterForSortedErrors(value, index, array) {
return (index === 0) || (value.lineNumber > array[index - 1].lineNumber);
}
// Lints a single string
/**
* Lints a string containing Markdown content.
*
* @param {Rule[]} ruleList List of rules.
* @param {string} name Identifier for the content.
* @param {string} content Markdown content
* @param {Object} md markdown-it instance.
* @param {Configuration} config Configuration object.
* @param {RegExp} frontMatter Regular expression for front matter.
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
* @param {boolean} noInlineConfig Whether to allow inline configuration.
* @param {number} resultVersion Version of the LintResults object to return.
* @param {Function} callback Callback (err, result) function.
* @returns {void}
*/
function lintContent(
ruleList,
name,
@ -367,16 +454,19 @@ function lintContent(
cache.flattenedLists(helpers.flattenLists(params));
// Function to run for each rule
const result = (resultVersion === 0) ? {} : [];
// eslint-disable-next-line jsdoc/require-jsdoc
function forRule(rule) {
// Configure rule
const ruleNameFriendly = rule.names[0];
const ruleName = ruleNameFriendly.toUpperCase();
params.config = effectiveConfig[ruleName];
// eslint-disable-next-line jsdoc/require-jsdoc
function throwError(property) {
throw new Error(
"Property '" + property + "' of onError parameter is incorrect.");
}
const errors = [];
// eslint-disable-next-line jsdoc/require-jsdoc
function onError(errorInfo) {
if (!errorInfo ||
!helpers.isNumber(errorInfo.lineNumber) ||
@ -518,7 +608,21 @@ function lintContent(
return callback(null, result);
}
// Lints a single file
/**
* Lints a file containing Markdown content.
*
* @param {Rule[]} ruleList List of rules.
* @param {string} file Path of file to lint.
* @param {Object} md markdown-it instance.
* @param {Configuration} config Configuration object.
* @param {RegExp} frontMatter Regular expression for front matter.
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
* @param {boolean} noInlineConfig Whether to allow inline configuration.
* @param {number} resultVersion Version of the LintResults object to return.
* @param {boolean} synchronous Whether to execute synchronously.
* @param {Function} callback Callback (err, result) function.
* @returns {void}
*/
function lintFile(
ruleList,
file,
@ -530,6 +634,7 @@ function lintFile(
resultVersion,
synchronous,
callback) {
// eslint-disable-next-line jsdoc/require-jsdoc
function lintContentWrapper(err, content) {
if (err) {
return callback(err);
@ -545,7 +650,14 @@ function lintFile(
}
}
// Lints files and strings
/**
* Lint files and strings specified in the Options object.
*
* @param {Options} options Options object.
* @param {boolean} synchronous Whether to execute synchronously.
* @param {Function} callback Callback (err, result) function.
* @returns {void}
*/
function lintInput(options, synchronous, callback) {
// Normalize inputs
options = options || {};
@ -579,9 +691,11 @@ function lintInput(options, synchronous, callback) {
const results = newResults(ruleList);
// Helper to lint the next string or file
/* eslint-disable consistent-return */
// eslint-disable-next-line jsdoc/require-jsdoc
function lintNextItem() {
let iterating = true;
let item = null;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintNextItemCallback(err, result) {
if (err) {
iterating = false;
@ -652,7 +766,14 @@ function markdownlintSync(options) {
return results;
}
// Parses the content of a configuration file
/**
* Parse the content of a configuration file.
*
* @param {string} name Name of the configuration file.
* @param {string} content Configuration content.
* @param {ConfigurationParser[]} parsers Parsing function(s).
* @returns {Object} Configuration object and error message.
*/
function parseConfiguration(name, content, parsers) {
let config = null;
let message = "";
@ -698,6 +819,7 @@ function readConfig(file, parsers, callback) {
return callback(err);
}
// Try to parse file
// @ts-ignore
const { config, message } = parseConfiguration(file, content, parsers);
if (!config) {
return callback(new Error(message));

View file

@ -3,22 +3,9 @@
"use strict";
const { addErrorDetailIf, listItemMarkerRe,
rangeFromRegExp } = require("../helpers");
rangeFromRegExp, unorderedListStyleFor } = require("../helpers");
const { flattenedLists } = require("./cache");
// Returns the unordered list style for a list item token
function unorderedListStyleFor(token) {
switch (token.markup) {
case "-":
return "dash";
case "+":
return "plus";
// case "*":
default:
return "asterisk";
}
}
module.exports = {
"names": [ "MD004", "ul-style" ],
"description": "Unordered list style",

View file

@ -3,13 +3,9 @@
"use strict";
const { addError, filterTokens, forEachInlineCodeSpan, forEachLine,
includesSorted, newLineRe } = require("../helpers");
includesSorted, newLineRe, numericSortAscending } = require("../helpers");
const { lineMetadata } = require("./cache");
function numericSortAscending(a, b) {
return a - b;
}
module.exports = {
"names": [ "MD009", "no-trailing-spaces" ],
"description": "Trailing spaces",

View file

@ -11,6 +11,7 @@ module.exports = {
"function": function MD036(params, onError) {
const punctuation = params.config.punctuation || allPunctuation;
const re = new RegExp("[" + punctuation + "]$");
// eslint-disable-next-line jsdoc/require-jsdoc
function base(token) {
if (token.type === "paragraph_open") {
return function inParagraph(t) {

View file

@ -17,6 +17,7 @@ module.exports = {
const escapedName = escapeForRegExp(name);
const namePattern = "\\S*\\b(" + escapedName + ")\\b\\S*";
const anyNameRe = new RegExp(namePattern, "gi");
// eslint-disable-next-line jsdoc/require-jsdoc
function forToken(token) {
const fenceOffset = (token.type === "fence") ? 1 : 0;
token.content.split(newLineRe)

View file

@ -2,16 +2,7 @@
"use strict";
const { addErrorDetailIf } = require("../helpers");
function fencedCodeBlockStyleFor(markup) {
switch (markup[0]) {
case "~":
return "tilde";
default:
return "backtick";
}
}
const { addErrorDetailIf, fencedCodeBlockStyleFor } = require("../helpers");
module.exports = {
"names": [ "MD048", "code-fence-style" ],

View file

@ -18,7 +18,7 @@
"test-declaration": "cd example/typescript && tsc && node type-check.js",
"test-extra": "node test/markdownlint-test-extra.js",
"debug": "node debug node_modules/nodeunit/bin/nodeunit",
"lint": "eslint lib helpers test schema && eslint --env browser --global markdownit --global markdownlint --rule \"no-unused-vars: 0, no-extend-native: 0, max-statements: 0, no-console: 0, no-var: 0\" demo && eslint --rule \"no-console: 0, no-invalid-this: 0, no-shadow: 0, object-property-newline: 0\" example",
"lint": "eslint --max-warnings 0 lib helpers test schema && eslint --env browser --global markdownit --global markdownlint --rule \"no-unused-vars: 0, no-extend-native: 0, max-statements: 0, no-console: 0, no-var: 0\" demo && eslint --rule \"no-console: 0, no-invalid-this: 0, no-shadow: 0, object-property-newline: 0\" example",
"ci": "npm run test-cover && npm run lint && npm run test-declaration",
"build-config-schema": "node schema/build-config-schema.js",
"build-declaration": "tsc --allowJs --declaration --outDir declaration --resolveJsonModule lib/markdownlint.js && cpy declaration/lib/markdownlint.d.ts lib && rimraf declaration",

View file

@ -27,6 +27,12 @@ const version = packageJson.version;
const deprecatedRuleNames = [ "MD002", "MD006" ];
/**
* Create a test function for the specified test file.
*
* @param {string} file Test file relative path.
* @returns {Function} Test function.
*/
function createTestForFile(file) {
const markdownlintPromise = promisify(markdownlint);
return function testForFile(test) {
@ -173,6 +179,7 @@ function createTestForFile(file) {
fs.readdirSync("./test")
.filter((file) => /\.md$/.test(file))
// @ts-ignore
.forEach((file) => tape(file, createTestForFile(path.join("./test", file))));
tape("projectFiles", (test) => {
@ -1495,7 +1502,7 @@ tape("readme", (test) => {
tape("doc", (test) => {
test.plan(336);
fs.readFile("doc/Rules.md", helpers.utf8Encoding,
function readFile(err, contents) {
(err, contents) => {
test.ifError(err);
const rulesLeft = rules.slice();
let inHeading = false;
@ -1504,7 +1511,8 @@ tape("doc", (test) => {
let ruleHasAliases = true;
let ruleUsesParams = null;
const tagAliasParameterRe = /, |: | /;
function testTagsAliasesParams(r) {
// eslint-disable-next-line func-style
const testTagsAliasesParams = (r) => {
r = r || "[NO RULE]";
test.ok(ruleHasTags,
"Missing tags for rule " + r.names + ".");
@ -1512,7 +1520,7 @@ tape("doc", (test) => {
"Missing aliases for rule " + r.names + ".");
test.ok(!ruleUsesParams,
"Missing parameters for rule " + r.names + ".");
}
};
md.parse(contents, {}).forEach(function forToken(token) {
if ((token.type === "heading_open") && (token.tag === "h2")) {
inHeading = true;
@ -2041,6 +2049,7 @@ tape("applyFix", (test) => {
];
testCases.forEach((testCase) => {
const [ line, fixInfo, lineEnding, expected ] = testCase;
// @ts-ignore
const actual = helpers.applyFix(line, fixInfo, lineEnding);
test.equal(actual, expected, "Incorrect fix applied.");
});

View file

@ -8,7 +8,12 @@ const cliEngine = new eslint.CLIEngine({});
const linter = new eslint.Linter();
const languageJavaScript = /js|javascript/i;
// Helper function that removes this project's use of eslint-plugin-jsdoc
/**
* Remove references to rules from eslint-plugin-jsdoc.
*
* @param {Object} config ESLint configuration object.
* @returns {Object} ESLint configuration object.
*/
function cleanJsdocRulesFromEslintConfig(config) {
const cleanedConfig = { ...config };
for (const rule in config.rules) {