diff --git a/.eslintrc.json b/.eslintrc.json index 2f067a53..84c785d3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,7 +8,8 @@ }, "plugins": [ "jsdoc", - "node" + "node", + "unicorn" ], "extends": [ "eslint:all", @@ -113,6 +114,62 @@ "node/prefer-global/url-search-params": "error", "node/prefer-global/url": "error", "node/prefer-promises/dns": "error", - "node/prefer-promises/fs": "off" + "node/prefer-promises/fs": "off", + + "unicorn/better-regex": "off", + "unicorn/catch-error-name": "error", + "unicorn/consistent-function-scoping": "off", + "unicorn/custom-error-definition": "error", + "unicorn/error-message": "error", + "unicorn/escape-case": "error", + "unicorn/expiring-todo-comments": "error", + "unicorn/explicit-length-check": "error", + "unicorn/filename-case": "off", + "unicorn/import-index": "error", + "unicorn/new-for-builtins": "error", + "unicorn/no-abusive-eslint-disable": "error", + "unicorn/no-array-instanceof": "error", + "unicorn/no-console-spaces": "error", + "unicorn/no-fn-reference-in-iterator": "off", + "unicorn/no-for-loop": "error", + "unicorn/no-hex-escape": "error", + "unicorn/no-keyword-prefix": "off", + "unicorn/no-nested-ternary": "error", + "unicorn/no-new-buffer": "error", + "unicorn/no-null": "off", + "unicorn/no-object-as-default-parameter": "error", + "unicorn/no-process-exit": "error", + "unicorn/no-reduce": "error", + "unicorn/no-unreadable-array-destructuring": "error", + "unicorn/no-unsafe-regex": "off", + "unicorn/no-unused-properties": "error", + "unicorn/no-useless-undefined": "error", + "unicorn/no-zero-fractions": "error", + "unicorn/number-literal-case": "error", + "unicorn/prefer-add-event-listener": "error", + "unicorn/prefer-array-find": "error", + "unicorn/prefer-dataset": "error", + "unicorn/prefer-event-key": "error", + "unicorn/prefer-flat-map": "error", + "unicorn/prefer-includes": "error", + "unicorn/prefer-modern-dom-apis": "error", + "unicorn/prefer-negative-index": "error", + "unicorn/prefer-node-append": "error", + "unicorn/prefer-node-remove": "error", + "unicorn/prefer-number-properties": "error", + "unicorn/prefer-optional-catch-binding": "error", + "unicorn/prefer-query-selector": "error", + "unicorn/prefer-reflect-apply": "error", + "unicorn/prefer-replace-all": "off", + "unicorn/prefer-set-has": "error", + "unicorn/prefer-spread": "error", + "unicorn/prefer-starts-ends-with": "error", + "unicorn/prefer-string-slice": "off", + "unicorn/prefer-text-content": "error", + "unicorn/prefer-trim-start-end": "off", + "unicorn/prefer-type-error": "error", + "unicorn/prevent-abbreviations": "off", + "unicorn/string-content": "error", + "unicorn/throw-new-error": "error" } } diff --git a/demo/default.js b/demo/default.js index 1a358b43..01e30d62 100644 --- a/demo/default.js +++ b/demo/default.js @@ -196,7 +196,8 @@ if (hashPrefix === decodedHash.substring(0, hashPrefix.length)) { markdown.value = decodedHash.substring(hashPrefix.length); } - } catch (ex) { + /* eslint-disable-next-line unicorn/prefer-optional-catch-binding */ + } catch (error) { // Invalid } } @@ -205,7 +206,8 @@ try { /* eslint-disable-next-line no-new */ new URL(rulesMd); - } catch (ex) { + /* eslint-disable-next-line unicorn/prefer-optional-catch-binding */ + } catch (error) { markdown.value = [ "# Sorry", "", diff --git a/demo/markdownlint-browser.js b/demo/markdownlint-browser.js index 0fe3334b..3b29a47b 100644 --- a/demo/markdownlint-browser.js +++ b/demo/markdownlint-browser.js @@ -2782,10 +2782,10 @@ module.exports = { // Close current run var content = line.substring(emphasisIndex, matchIndex); if (!emphasisLength) { - content = content.trimStart(); + content = content.trimLeft(); } if (!match) { - content = content.trimEnd(); + content = content.trimRight(); } var leftSpace = leftSpaceRe.test(content); var rightSpace = rightSpaceRe.test(content); diff --git a/lib/markdownlint.js b/lib/markdownlint.js index fb792564..bb5417e1 100644 --- a/lib/markdownlint.js +++ b/lib/markdownlint.js @@ -154,7 +154,7 @@ function removeFrontMatter(content, frontMatter) { const contentMatched = frontMatterMatch[0]; content = content.slice(contentMatched.length); frontMatterLines = contentMatched.split(helpers.newLineRe); - if (frontMatterLines.length && + if ((frontMatterLines.length > 0) && (frontMatterLines[frontMatterLines.length - 1] === "")) { frontMatterLines.length--; } @@ -343,7 +343,8 @@ function getEnabledRulesPerLineNumber( ...config, ...json }; - } catch (ex) { + // eslint-disable-next-line unicorn/prefer-optional-catch-binding + } catch (error) { // Ignore parse errors for inline configuration } } @@ -459,7 +460,7 @@ function lintContent( resultVersion, callback) { // Remove UTF-8 byte order marker (if present) - content = content.replace(/^\ufeff/, ""); + content = content.replace(/^\uFEFF/, ""); // Remove front matter const removeFrontMatterResult = removeFrontMatter(content, frontMatter); const frontMatterLines = removeFrontMatterResult.frontMatterLines; @@ -582,17 +583,17 @@ function lintContent( if (handleRuleFailures) { try { rule.function(params, onError); - } catch (ex) { + } catch (error) { onError({ "lineNumber": 1, - "detail": `This rule threw an exception: ${ex.message}` + "detail": `This rule threw an exception: ${error.message}` }); } } else { rule.function(params, onError); } // Record any errors (significant performance benefit from length check) - if (errors.length) { + if (errors.length > 0) { errors.sort(lineNumberComparison); const filteredErrors = errors .filter((resultVersion === 3) ? @@ -624,7 +625,7 @@ function lintContent( } return errorObject; }); - if (filteredErrors.length) { + if (filteredErrors.length > 0) { if (resultVersion === 0) { result[ruleNameFriendly] = filteredErrors; } else { @@ -636,9 +637,9 @@ function lintContent( // Run all rules try { ruleList.forEach(forRule); - } catch (ex) { + } catch (error) { cache.clear(); - return callback(ex); + return callback(error); } cache.clear(); return callback(null, result); @@ -819,8 +820,8 @@ function parseConfiguration(name, content, parsers) { (parsers || [ JSON.parse ]).every((parser) => { try { config = parser(content); - } catch (ex) { - errors.push(ex.message); + } catch (error) { + errors.push(error.message); } return !config; }); diff --git a/lib/md001.js b/lib/md001.js index 56105bbd..698caeea 100644 --- a/lib/md001.js +++ b/lib/md001.js @@ -11,7 +11,7 @@ module.exports = { "function": function MD001(params, onError) { let prevLevel = 0; filterTokens(params, "heading_open", function forToken(token) { - const level = parseInt(token.tag.slice(1), 10); + const level = Number.parseInt(token.tag.slice(1), 10); if (prevLevel && (level > prevLevel)) { addErrorDetailIf(onError, token.lineNumber, "h" + (prevLevel + 1), "h" + level); diff --git a/lib/md012.js b/lib/md012.js index 71d809ba..b8056b3d 100644 --- a/lib/md012.js +++ b/lib/md012.js @@ -13,7 +13,7 @@ module.exports = { const maximum = Number(params.config.maximum || 1); let count = 0; forEachLine(lineMetadata(), (line, lineIndex, inCode) => { - count = (inCode || line.trim().length) ? 0 : count + 1; + count = (inCode || (line.trim().length > 0)) ? 0 : count + 1; if (maximum < count) { addErrorDetailIf( onError, diff --git a/lib/md037.js b/lib/md037.js index 7f871fba..4774468f 100644 --- a/lib/md037.js +++ b/lib/md037.js @@ -32,10 +32,10 @@ module.exports = { // Close current run let content = line.substring(emphasisIndex, matchIndex); if (!emphasisLength) { - content = content.trimStart(); + content = content.trimLeft(); } if (!match) { - content = content.trimEnd(); + content = content.trimRight(); } const leftSpace = leftSpaceRe.test(content); const rightSpace = rightSpaceRe.test(content); diff --git a/package.json b/package.json index 9977a09f..f74bb22d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test-cover": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 node test/markdownlint-test.js", "test-declaration": "cd example/typescript && tsc && node type-check.js", "test-extra": "node test/markdownlint-test-extra.js", - "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, node/no-missing-require: 0, node/no-extraneous-require: 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, unicorn/prefer-add-event-listener: 0, unicorn/prefer-query-selector: 0, unicorn/prefer-replace-all: 0\" demo && eslint --rule \"no-console: 0, no-invalid-this: 0, no-shadow: 0, object-property-newline: 0, node/no-missing-require: 0, node/no-extraneous-require: 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", @@ -43,6 +43,7 @@ "eslint": "~7.8.1", "eslint-plugin-jsdoc": "~30.3.1", "eslint-plugin-node": "~11.1.0", + "eslint-plugin-unicorn": "~21.0.0", "globby": "~11.0.1", "js-yaml": "~3.14.0", "make-dir-cli": "~2.0.0", diff --git a/test/markdownlint-test-repos.js b/test/markdownlint-test-repos.js index 2149a40f..a81c142a 100644 --- a/test/markdownlint-test-repos.js +++ b/test/markdownlint-test-repos.js @@ -55,7 +55,7 @@ function lintTestRepo(test, globPatterns, configPath) { }; return markdownlintPromise(options).then((results) => { const resultsString = results.toString(); - if (resultsString.length) { + if (resultsString.length > 0) { // eslint-disable-next-line no-console console.log(resultsString); } diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 73028870..f5ea6ccd 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -24,7 +24,7 @@ const configSchema = require("../schema/markdownlint-config-schema.json"); const homepage = packageJson.homepage; const version = packageJson.version; -const deprecatedRuleNames = [ "MD002", "MD006" ]; +const deprecatedRuleNames = new Set([ "MD002", "MD006" ]); /** * Create a test function for the specified test file. @@ -128,7 +128,11 @@ function createTestForFile(file) { while ((match = regex.exec(line))) { const rule = match[1]; const errors = results[rule] || []; - errors.push(match[2] ? parseInt(match[2], 10) : lineNum + 1); + errors.push( + match[2] ? + Number.parseInt(match[2], 10) : + lineNum + 1 + ); results[rule] = errors; } }); @@ -1504,7 +1508,7 @@ tape("readme", (test) => { let expected = "**[" + ruleName + "](doc/Rules.md#" + ruleName.toLowerCase() + ")** *" + ruleAliases.join("/") + "* - " + rule.description; - if (deprecatedRuleNames.includes(ruleName)) { + if (deprecatedRuleNames.has(ruleName)) { expected = "~~" + expected + "~~"; } test.equal(token.content, expected, "Rule mismatch."); @@ -1566,7 +1570,7 @@ tape("rules", (test) => { "Missing rule implementation for " + token.content + "."); const ruleName = rule.names[0]; let headingContent = ruleName + " - " + rule.description; - if (deprecatedRuleNames.includes(ruleName)) { + if (deprecatedRuleNames.has(ruleName)) { headingContent = "~~" + headingContent + "~~"; } test.equal(token.content, @@ -1580,16 +1584,16 @@ tape("rules", (test) => { }); ruleUsesParams.sort(); } - } else if (/^Tags: /.test(token.content) && rule) { + } else if (token.content.startsWith("Tags: ") && rule) { test.deepEqual(token.content.split(tagAliasParameterRe).slice(1), rule.tags, "Tag mismatch for rule " + rule.names + "."); ruleHasTags = true; - } else if (/^Aliases: /.test(token.content) && rule) { + } else if (token.content.startsWith("Aliases: ") && rule) { test.deepEqual(token.content.split(tagAliasParameterRe).slice(1), rule.names.slice(1), "Alias mismatch for rule " + rule.names + "."); ruleHasAliases = true; - } else if (/^Parameters: /.test(token.content) && rule) { + } else if (token.content.startsWith("Parameters: ") && rule) { let inDetails = false; const parameters = token.content.split(tagAliasParameterRe) .slice(1) diff --git a/test/rules/lint-javascript.js b/test/rules/lint-javascript.js index 0debaf9b..441e1a6d 100644 --- a/test/rules/lint-javascript.js +++ b/test/rules/lint-javascript.js @@ -17,7 +17,7 @@ const languageJavaScript = /js|javascript/i; function cleanJsdocRulesFromEslintConfig(config) { const cleanedConfig = { ...config }; for (const rule in config.rules) { - if (/^(jsdoc|node)\//.test(rule)) { + if (/^(jsdoc|node|unicorn)\//.test(rule)) { delete cleanedConfig.rules[rule]; } }