diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba8e922f..7659f8b0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,7 @@ jobs: fail-fast: false matrix: node-version: + - 22 - 21 - 20 - 18 @@ -22,7 +23,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - run: npm install --force # ts-eslint does not support eslint v9 yet. + - run: npm install - run: npm test lint: @@ -32,7 +33,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "lts/*" - - run: npm install --force # ts-eslint does not support eslint v9 yet. + - run: npm install - run: npm run lint eslint8: @@ -42,7 +43,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "lts/*" - - run: npm install --force # ts-eslint does not support eslint v9 yet. + - run: npm install - run: npm install --save-dev eslint@8 - run: npm test @@ -54,6 +55,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "lts/*" - - run: npm install --force # ts-eslint does not support eslint v9 yet. + - run: npm install - run: npm install --save-dev eslint@8 # eslint-remote-tester does not support eslint v9. - run: npm run test:remote diff --git a/.npmrc b/.npmrc index 3f4a3eaf..02996f37 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ registry = https://registry.npmjs.org/ package-lock = false +force = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 724e8cb9..2756022b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,14 @@ * update dependency markdownlint-cli to ^0.38.0 ([#410](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/410)) ([6b53c5b](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/6b53c5b7b8bc9e19dcb86796ab29019f89c449fc)) * update dependency markdownlint-cli to ^0.39.0 ([#431](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/431)) ([f005a2c](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/f005a2c0231b8b77f6862dca81b4a6e3099e0493)) +## [6.2.0](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v6.1.0...v6.2.0) (2024-06-25) + + +### Features + +* eslint-utils => @eslint-community/eslint-utils ([#474](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/474)) ([6db5604](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/6db5604b41341dc0f41cc99f81edc0913aa661ac)) +* no-property-in-node add additionalNodeTypeFiles option ([#484](https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/484)) ([6cdef14](https://github.com/eslint-community/eslint-plugin-eslint-plugin/commit/6cdef146cea7270364b254f23e04786a89c65dfe)) + ## [6.1.0](https://github.com/eslint-community/eslint-plugin-eslint-plugin/compare/v6.0.0...v6.1.0) (2024-04-25) diff --git a/docs/rules/no-property-in-node.md b/docs/rules/no-property-in-node.md index 1a44c31c..7d986ad0 100644 --- a/docs/rules/no-property-in-node.md +++ b/docs/rules/no-property-in-node.md @@ -54,3 +54,33 @@ module.exports = { }, }; ``` + +## Options + + + +| Name | Description | Type | +| :------------------------ | :----------------------------------------------------------------------------------- | :---- | +| `additionalNodeTypeFiles` | Any additional regular expressions to consider source files defining AST Node types. | Array | + + + +### `additionalNodeTypeFiles` + +Here is an example of how the additionalNodeTypeFiles option can be configured in an ESLint configuration file: + +```json +{ + "rules": { + "eslint-plugin/no-property-in-node": [ + "error", + { + "additionalNodeTypeFiles": [ + "/packages[/\\]types[/\\]dist[/\\]generated[/\\]ast-spec.d.ts/", + "/custom/path/to/types/definition.d.ts/" + ] + } + ] + } +} +``` diff --git a/eslint.config.js b/eslint.config.js index ad269749..ecc609db 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -15,15 +15,15 @@ const compat = new FlatCompat({ module.exports = [ ...compat.extends( 'not-an-aardvark/node', - 'plugin:eslint-comments/recommended', + 'plugin:@eslint-community/eslint-comments/recommended', 'plugin:prettier/recommended', 'plugin:unicorn/recommended', ), pluginN.configs['flat/recommended'], { rules: { - 'eslint-comments/no-unused-disable': 'error', - 'eslint-comments/require-description': 'error', + '@eslint-community/eslint-comments/no-unused-disable': 'error', + '@eslint-community/eslint-comments/require-description': 'error', 'unicorn/consistent-function-scoping': 'off', 'unicorn/no-array-callback-reference': 'off', @@ -70,7 +70,7 @@ module.exports = [ 'no-unused-vars': 'off', strict: 'off', - 'eslint-comments/require-description': 'off', + '@eslint-community/eslint-comments/require-description': 'off', 'unicorn/filename-case': 'off', }, diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js index 038556d7..0ea7c857 100644 --- a/lib/rules/fixer-return.js +++ b/lib/rules/fixer-return.js @@ -10,7 +10,7 @@ // ------------------------------------------------------------------------------ const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js index d3994428..364c340f 100644 --- a/lib/rules/no-missing-placeholders.js +++ b/lib/rules/no-missing-placeholders.js @@ -6,7 +6,7 @@ 'use strict'; const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js index 974f2ff7..816fedd1 100644 --- a/lib/rules/no-only-tests.js +++ b/lib/rules/no-only-tests.js @@ -5,7 +5,7 @@ const { isCommaToken, isOpeningBraceToken, isClosingBraceToken, -} = require('eslint-utils'); +} = require('@eslint-community/eslint-utils'); /** @type {import('eslint').Rule.RuleModule} */ module.exports = { diff --git a/lib/rules/no-property-in-node.js b/lib/rules/no-property-in-node.js index bd974af1..1f6563f6 100644 --- a/lib/rules/no-property-in-node.js +++ b/lib/rules/no-property-in-node.js @@ -1,6 +1,6 @@ 'use strict'; -const typedNodeSourceFileTesters = [ +const defaultTypedNodeSourceFileTesters = [ /@types[/\\]estree[/\\]index\.d\.ts/, /@typescript-eslint[/\\]types[/\\]dist[/\\]generated[/\\]ast-spec\.d\.ts/, ]; @@ -26,9 +26,10 @@ const typedNodeSourceFileTesters = [ * ``` * * @param {import('typescript').Type} type + * @param {RegExp[]} typedNodeSourceFileTesters * @returns Whether the type seems to include a known ESTree or TSESTree AST node. */ -function isAstNodeType(type) { +function isAstNodeType(type, typedNodeSourceFileTesters) { return (type.types || [type]) .filter((typePart) => typePart.getProperty('type')) .flatMap( @@ -55,13 +56,33 @@ module.exports = { requiresTypeChecking: true, url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/no-property-in-node.md', }, - schema: [], + schema: [ + { + type: 'object', + properties: { + additionalNodeTypeFiles: { + description: + 'Any additional regular expressions to consider source files defining AST Node types.', + elements: { type: 'string' }, + type: 'array', + }, + }, + additionalProperties: false, + }, + ], messages: { in: 'Prefer checking specific node properties instead of a broad `in`.', }, }, create(context) { + const typedNodeSourceFileTesters = [ + ...defaultTypedNodeSourceFileTesters, + ...(context.options[0]?.additionalNodeTypeFiles?.map( + (filePath) => new RegExp(filePath), + ) ?? []), + ]; + return { 'BinaryExpression[operator=in]'(node) { // TODO: Switch this to ESLintUtils.getParserServices with typescript-eslint@>=6 @@ -77,7 +98,7 @@ module.exports = { const tsNode = services.esTreeNodeToTSNodeMap.get(node.right); const type = checker.getTypeAtLocation(tsNode); - if (isAstNodeType(type)) { + if (isAstNodeType(type, typedNodeSourceFileTesters)) { context.report({ messageId: 'in', node }); } }, diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js index d6e7dc93..ca95c952 100644 --- a/lib/rules/no-unused-placeholders.js +++ b/lib/rules/no-unused-placeholders.js @@ -6,7 +6,7 @@ 'use strict'; const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js index 4ee51006..1d932748 100644 --- a/lib/rules/prefer-message-ids.js +++ b/lib/rules/prefer-message-ids.js @@ -1,7 +1,7 @@ 'use strict'; const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js index 4a5a7dfc..a1555945 100644 --- a/lib/rules/prefer-placeholders.js +++ b/lib/rules/prefer-placeholders.js @@ -6,7 +6,7 @@ 'use strict'; const utils = require('../utils'); -const { findVariable } = require('eslint-utils'); +const { findVariable } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js index d7f17a21..a73e9d0b 100644 --- a/lib/rules/report-message-format.js +++ b/lib/rules/report-message-format.js @@ -5,7 +5,7 @@ 'use strict'; -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); // ------------------------------------------------------------------------------ diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js index 081b4ff6..58156bf4 100644 --- a/lib/rules/require-meta-docs-description.js +++ b/lib/rules/require-meta-docs-description.js @@ -1,6 +1,6 @@ 'use strict'; -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); // ------------------------------------------------------------------------------ diff --git a/lib/rules/require-meta-docs-recommended.js b/lib/rules/require-meta-docs-recommended.js index d620f34b..f2cdeee8 100644 --- a/lib/rules/require-meta-docs-recommended.js +++ b/lib/rules/require-meta-docs-recommended.js @@ -1,6 +1,6 @@ 'use strict'; -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); /** @type {import('eslint').Rule.RuleModule} */ diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js index af3e6d40..d5039eee 100644 --- a/lib/rules/require-meta-docs-url.js +++ b/lib/rules/require-meta-docs-url.js @@ -10,7 +10,7 @@ const path = require('path'); const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ----------------------------------------------------------------------------- // Rule Definition diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js index 3b9ec681..8e275f6d 100644 --- a/lib/rules/require-meta-fixable.js +++ b/lib/rules/require-meta-fixable.js @@ -5,7 +5,7 @@ 'use strict'; -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); // ------------------------------------------------------------------------------ diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js index a7b5547a..1759eecf 100644 --- a/lib/rules/require-meta-has-suggestions.js +++ b/lib/rules/require-meta-has-suggestions.js @@ -1,7 +1,7 @@ 'use strict'; const utils = require('../utils'); -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); // ------------------------------------------------------------------------------ // Rule Definition diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js index 9c3e908c..fc64242b 100644 --- a/lib/rules/require-meta-schema.js +++ b/lib/rules/require-meta-schema.js @@ -1,6 +1,6 @@ 'use strict'; -const { findVariable } = require('eslint-utils'); +const { findVariable } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); // ------------------------------------------------------------------------------ diff --git a/lib/rules/require-meta-type.js b/lib/rules/require-meta-type.js index 1b3a14c4..24036071 100644 --- a/lib/rules/require-meta-type.js +++ b/lib/rules/require-meta-type.js @@ -5,7 +5,7 @@ 'use strict'; -const { getStaticValue } = require('eslint-utils'); +const { getStaticValue } = require('@eslint-community/eslint-utils'); const utils = require('../utils'); const VALID_TYPES = new Set(['problem', 'suggestion', 'layout']); diff --git a/lib/utils.js b/lib/utils.js index d59555e5..d584a1e9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,6 +1,9 @@ 'use strict'; -const { getStaticValue, findVariable } = require('eslint-utils'); +const { + getStaticValue, + findVariable, +} = require('@eslint-community/eslint-utils'); const estraverse = require('estraverse'); const functionTypes = new Set([ diff --git a/package.json b/package.json index 10159af3..19e26a97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-eslint-plugin", - "version": "6.1.0", + "version": "6.2.0", "description": "An ESLint plugin for linting ESLint plugins", "author": "Teddy Katz", "main": "./lib/index.js", @@ -40,7 +40,7 @@ }, "homepage": "https://github.com/eslint-community/eslint-plugin-eslint-plugin#readme", "dependencies": { - "eslint-utils": "^3.0.0", + "@eslint-community/eslint-utils": "^4.4.0", "estraverse": "^5.3.0" }, "nyc": { @@ -52,6 +52,7 @@ "devDependencies": { "@commitlint/cli": "^19.2.2", "@commitlint/config-conventional": "^19.2.2", + "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", "@eslint/eslintrc": "^3.0.2", "@eslint/js": "^9.0.0", "@release-it/conventional-changelog": "^8.0.1", @@ -61,23 +62,22 @@ "@typescript-eslint/utils": "^7.7.0", "chai": "^4.4.1", "dirty-chai": "^2.0.1", - "eslint": "^9.0.0", + "eslint": "^9.1.1", "eslint-config-not-an-aardvark": "^2.1.0", "eslint-config-prettier": "^9.1.0", "eslint-doc-generator": "^1.7.0", - "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-plugin": "file:./", - "eslint-plugin-markdown": "^4.0.1", + "eslint-plugin-markdown": "^5.0.0", "eslint-plugin-n": "^17.2.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^52.0.0", + "eslint-plugin-unicorn": "^53.0.0", "eslint-remote-tester": "^3.0.1", "eslint-scope": "^8.0.1", "espree": "^10.0.1", "globals": "^15.0.0", "husky": "^9.0.11", "lodash": "^4.17.21", - "markdownlint-cli": "^0.39.0", + "markdownlint-cli": "^0.41.0", "mocha": "^10.4.0", "npm-package-json-lint": "^7.1.0", "npm-run-all2": "^6.1.2", diff --git a/tests/lib/rules/no-property-in-node.js b/tests/lib/rules/no-property-in-node.js index bc5387c1..62181a3b 100644 --- a/tests/lib/rules/no-property-in-node.js +++ b/tests/lib/rules/no-property-in-node.js @@ -93,6 +93,21 @@ ruleTester.run('no-property-in-node', rule, { }, }; `, + { + code: ` + interface Node { + type: string; + } + declare const node: Node; + 'a' in node; + export {}; + `, + options: [ + { + additionalNodeTypeFiles: [/not-found/], + }, + ], + }, ], invalid: [ { @@ -163,5 +178,29 @@ ruleTester.run('no-property-in-node', rule, { }, ], }, + { + code: ` + interface Node { + type: string; + } + declare const node: Node; + 'a' in node; + export {}; + `, + options: [ + { + additionalNodeTypeFiles: [/lib[/\\]fixtures[/\\]estree\.ts/], + }, + ], + errors: [ + { + column: 9, + line: 6, + endColumn: 20, + endLine: 6, + messageId: 'in', + }, + ], + }, ], });