diff --git a/.eslintrc.js b/.eslintrc.js index b648e5ed..86f73094 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,7 +7,12 @@ module.exports = { 'not-an-aardvark/node', 'plugin:node/recommended', 'plugin:self/all', + 'plugin:unicorn/recommended', ], + parserOptions: { + ecmaVersion: 2021, + sourceType: 'script', + }, rules: { 'comma-dangle': [ 'error', @@ -22,6 +27,14 @@ module.exports = { 'self/meta-property-ordering': 'off', 'self/require-meta-docs-url': 'off', 'self/report-message-format': ['error', '^[^a-z].*.$'], + + 'unicorn/consistent-function-scoping': 'off', + 'unicorn/no-array-callback-reference': 'off', + 'unicorn/no-array-for-each': 'off', + 'unicorn/no-array-reduce': 'off', + 'unicorn/no-null': 'off', + 'unicorn/prefer-module': 'off', + 'unicorn/prevent-abbreviations': 'off', }, overrides: [ { diff --git a/lib/rules/consistent-output.js b/lib/rules/consistent-output.js index 35478e76..ab73748e 100644 --- a/lib/rules/consistent-output.js +++ b/lib/rules/consistent-output.js @@ -39,7 +39,7 @@ module.exports = { utils.getTestInfo(context, ast).forEach(testRun => { const readableCases = testRun.invalid.filter(testCase => testCase.type === 'ObjectExpression'); const casesWithoutOutput = readableCases - .filter(testCase => testCase.properties.map(utils.getKeyName).indexOf('output') === -1); + .filter(testCase => !testCase.properties.map(utils.getKeyName).includes('output')); if ( (casesWithoutOutput.length < readableCases.length) || diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js index 6a58ff48..2f25ea58 100644 --- a/lib/rules/meta-property-ordering.js +++ b/lib/rules/meta-property-ordering.js @@ -51,7 +51,7 @@ module.exports = { const violatingProps = props.filter(prop => { const curr = orderMap.has(getKeyName(prop)) ? orderMap.get(getKeyName(prop)) - : Infinity; + : Number.POSITIVE_INFINITY; return last > (last = curr); }); diff --git a/lib/rules/no-deprecated-context-methods.js b/lib/rules/no-deprecated-context-methods.js index 27c5afc6..c531b8b6 100644 --- a/lib/rules/no-deprecated-context-methods.js +++ b/lib/rules/no-deprecated-context-methods.js @@ -55,7 +55,7 @@ module.exports = { return { 'Program:exit' () { - Array.from(utils.getContextIdentifiers(context, sourceCode.ast)) + [...utils.getContextIdentifiers(context, sourceCode.ast)] .filter( contextId => contextId.parent.type === 'MemberExpression' && diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js index 4740bccd..d1756be8 100644 --- a/lib/rules/no-identical-tests.js +++ b/lib/rules/no-identical-tests.js @@ -60,8 +60,8 @@ module.exports = { propertiesSetA.add(code); }); - for (let i = 0; i < propertiesB.length; i++) { - const code = sourceCode.getText(propertiesB[i]); + for (const element of propertiesB) { + const code = sourceCode.getText(element); if (!propertiesSetA.has(code)) { return false; } diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js index ae9d95fb..30f7f768 100644 --- a/lib/rules/no-missing-placeholders.js +++ b/lib/rules/no-missing-placeholders.js @@ -56,7 +56,7 @@ module.exports = { ) { // Same regex as the one ESLint uses // https://github.com/eslint/eslint/blob/e5446449d93668ccbdb79d78cc69f165ce4fde07/lib/eslint.js#L990 - const PLACEHOLDER_MATCHER = /\{\{\s*([^{}]+?)\s*\}\}/g; + const PLACEHOLDER_MATCHER = /{{\s*([^{}]+?)\s*}}/g; let match; while ((match = PLACEHOLDER_MATCHER.exec(reportInfo.message.value || messageStaticValue.value))) { // eslint-disable-line no-extra-parens diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js index 28041914..ee088a47 100644 --- a/lib/rules/no-unused-placeholders.js +++ b/lib/rules/no-unused-placeholders.js @@ -57,7 +57,7 @@ module.exports = { ) { const message = reportInfo.message.value || messageStaticValue.value; // https://github.com/eslint/eslint/blob/2874d75ed8decf363006db25aac2d5f8991bd969/lib/linter.js#L986 - const PLACEHOLDER_MATCHER = /\{\{\s*([^{}]+?)\s*\}\}/g; + const PLACEHOLDER_MATCHER = /{{\s*([^{}]+?)\s*}}/g; const placeholdersInMessage = new Set(); message.replace(PLACEHOLDER_MATCHER, (fullMatch, term) => { diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js index ce6f5b6d..ae7c4684 100644 --- a/lib/rules/no-useless-token-range.js +++ b/lib/rules/no-useless-token-range.js @@ -103,7 +103,7 @@ module.exports = { return { 'Program:exit' (ast) { - Array.from(utils.getSourceCodeIdentifiers(context, ast)) + [...utils.getSourceCodeIdentifiers(context, ast)] .filter(identifier => identifier.parent.type === 'MemberExpression' && identifier.parent.object === identifier && identifier.parent.property.type === 'Identifier' && diff --git a/lib/rules/prefer-output-null.js b/lib/rules/prefer-output-null.js index 8e6c6306..5b1b5303 100644 --- a/lib/rules/prefer-output-null.js +++ b/lib/rules/prefer-output-null.js @@ -43,7 +43,7 @@ module.exports = { function getTestInfo (key) { if (test.type === 'ObjectExpression') { const res = test.properties.filter(item => item.key.name === key); - return res.length ? res[res.length - 1] : null; + return res.length > 0 ? res[res.length - 1] : null; } return key === 'code' ? test : null; } diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js index 54d7ff1c..6210a07e 100644 --- a/lib/rules/prefer-placeholders.js +++ b/lib/rules/prefer-placeholders.js @@ -75,7 +75,7 @@ module.exports = { } if ( - (messageNode.type === 'TemplateLiteral' && messageNode.expressions.length) || + (messageNode.type === 'TemplateLiteral' && messageNode.expressions.length > 0) || (messageNode.type === 'BinaryExpression' && messageNode.operator === '+') ) { context.report({ diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js index 78431398..27e3d6e7 100644 --- a/lib/rules/require-meta-docs-url.js +++ b/lib/rules/require-meta-docs-url.js @@ -94,6 +94,7 @@ module.exports = { message: !urlPropNode ? 'Rules should export a `meta.docs.url` property.' : + // eslint-disable-next-line unicorn/no-nested-ternary !expectedUrl ? '`meta.docs.url` property must be a string.' : /* otherwise */ '`meta.docs.url` property must be `{{expectedUrl}}`.', diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js index bdf368b2..452720e2 100644 --- a/lib/rules/require-meta-fixable.js +++ b/lib/rules/require-meta-fixable.js @@ -65,6 +65,7 @@ module.exports = { const VALID_VALUES = new Set(['code', 'whitespace', null, undefined]); const valueIsValid = metaFixableProp.value.type === 'Literal' ? VALID_VALUES.has(metaFixableProp.value.value) + // eslint-disable-next-line unicorn/no-nested-ternary : metaFixableProp.value.type === 'TemplateLiteral' && metaFixableProp.value.quasis.length === 1 ? VALID_VALUES.has(metaFixableProp.value.quasis[0].value.cooked) : metaFixableProp.value.type === 'Identifier' && metaFixableProp.value.name === 'undefined'; diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js index f7dda406..89daeee0 100644 --- a/lib/rules/test-case-property-ordering.js +++ b/lib/rules/test-case-property-ordering.js @@ -58,10 +58,8 @@ module.exports = { // current < lastChecked to catch unordered; // and lastChecked === -1 to catch extra properties before. if (current > -1 && (current < lastChecked || lastChecked === -1)) { - let orderMsg = order.filter(item => keyNames.indexOf(item) > -1); - orderMsg = orderMsg.concat( - lastChecked === -1 ? keyNames.filter(item => order.indexOf(item) === -1) : [] - ); + let orderMsg = order.filter(item => keyNames.includes(item)); + orderMsg = [...orderMsg, ...lastChecked === -1 ? keyNames.filter(item => !order.includes(item)) : []]; context.report({ node: properties[i], diff --git a/lib/utils.js b/lib/utils.js index 73beb8cd..e00aada3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -53,7 +53,7 @@ function isNormalFunctionExpressionReference (node, scopeManager) { } const definitions = createReference.resolved.defs; - if (!definitions || !definitions.length) { + if (!definitions || definitions.length === 0) { return false; } @@ -175,7 +175,7 @@ module.exports = { getContextIdentifiers (context, ast) { const ruleInfo = module.exports.getRuleInfo({ ast }); - if (!ruleInfo || !ruleInfo.create.params.length || ruleInfo.create.params[0].type !== 'Identifier') { + if (!ruleInfo || ruleInfo.create.params.length === 0 || ruleInfo.create.params[0].type !== 'Identifier') { return new Set(); } @@ -270,7 +270,7 @@ module.exports = { // ['node', 'message', 'data', 'fix']. // Otherwise, the arguments are interpreted as ['node', 'loc', 'message', 'data', 'fix']. - if (!reportArgs.length) { + if (reportArgs.length === 0) { return null; } @@ -320,7 +320,7 @@ module.exports = { * @returns {Set} A set of all identifiers referring to the `SourceCode` object. */ getSourceCodeIdentifiers (context, ast) { - return new Set(Array.from(module.exports.getContextIdentifiers(context, ast)) + return new Set([...module.exports.getContextIdentifiers(context, ast)] .filter(identifier => identifier.parent && identifier.parent.type === 'MemberExpression' && identifier === identifier.parent.object && @@ -333,9 +333,9 @@ module.exports = { identifier.parent.parent.parent.id.type === 'Identifier' ) .map(identifier => context.getDeclaredVariables(identifier.parent.parent.parent)) - .reduce((allVariables, variablesForIdentifier) => allVariables.concat(variablesForIdentifier), []) + .reduce((allVariables, variablesForIdentifier) => [...allVariables, ...variablesForIdentifier], []) .map(variable => variable.references) - .reduce((allRefs, refsForVariable) => allRefs.concat(refsForVariable), []) + .reduce((allRefs, refsForVariable) => [...allRefs, ...refsForVariable], []) .map(ref => ref.identifier)); }, diff --git a/package.json b/package.json index 81f81857..f21b41ee 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "eslint-config-not-an-aardvark": "^2.1.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-self": "^1.2.1", + "eslint-plugin-unicorn": "^31.0.0", "eslint-scope": "^5.1.1", "espree": "^7.3.0", "estraverse": "^5.0.0", diff --git a/tests/lib/index.js b/tests/lib/index.js index 1361f073..2dee7b5a 100644 --- a/tests/lib/index.js +++ b/tests/lib/index.js @@ -3,9 +3,11 @@ const assert = require('chai').assert; const plugin = require('../..'); +const RULE_NAMES = Object.keys(plugin.rules); + describe('exported plugin', () => { describe('adds a meta.docs.url property to each rule', () => { - Object.keys(plugin.rules).forEach(ruleName => { + RULE_NAMES.forEach(ruleName => { it(ruleName, () => { assert.match( plugin.rules[ruleName].meta.docs.url, diff --git a/tests/lib/utils.js b/tests/lib/utils.js index 1c60a5cc..8e28a935 100644 --- a/tests/lib/utils.js +++ b/tests/lib/utils.js @@ -1,6 +1,6 @@ 'use strict'; -const util = require('util'); +const { inspect } = require('util'); const lodash = require('lodash'); const espree = require('espree'); const eslintScope = require('eslint-scope'); @@ -118,7 +118,7 @@ describe('utils', () => { const ruleInfo = utils.getRuleInfo({ ast }); assert( lodash.isMatch(ruleInfo, CASES[ruleSource]), - `Expected \n${util.inspect(ruleInfo)}\nto match\n${util.inspect(CASES[ruleSource])}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(CASES[ruleSource])}` ); }); }); @@ -143,7 +143,7 @@ describe('utils', () => { const ruleInfo = utils.getRuleInfo({ ast, scopeManager }); assert( lodash.isMatch(ruleInfo, expected), - `Expected \n${util.inspect(ruleInfo)}\nto match\n${util.inspect(expected)}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(expected)}` ); }); } @@ -178,7 +178,7 @@ describe('utils', () => { const identifiers = utils.getContextIdentifiers(scope, ast); assert(identifiers instanceof Set, 'getContextIdentifiers should return a Set'); - Array.from(identifiers).forEach((identifier, index) => { + [...identifiers].forEach((identifier, index) => { assert.strictEqual(identifier, CASES[ruleSource](ast)[index]); }); });