diff --git a/.eslint-doc-generatorrc.js b/.eslint-doc-generatorrc.js index 5f25c0a6..a3756beb 100644 --- a/.eslint-doc-generatorrc.js +++ b/.eslint-doc-generatorrc.js @@ -1,9 +1,7 @@ -'use strict'; - -const prettier = require('prettier'); +import prettier from 'prettier'; /** @type {import('eslint-doc-generator').GenerateOptions} */ -module.exports = { +const config = { ignoreConfig: [ 'all', 'all-type-checked', @@ -29,3 +27,5 @@ module.exports = { urlConfigs: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin#presets', }; + +export default config; diff --git a/.prettierrc.js b/.prettierrc.js index 534e6d35..ae4b0277 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,5 +1,3 @@ -'use strict'; - -module.exports = { +export default { singleQuote: true, }; diff --git a/commitlint.config.js b/commitlint.config.js index 0cf61d7e..3f5e287f 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,3 +1 @@ -'use strict'; - -module.exports = { extends: ['@commitlint/config-conventional'] }; +export default { extends: ['@commitlint/config-conventional'] }; diff --git a/configs/all-type-checked.js b/configs/all-type-checked.js index 59bbf2b3..84309cba 100644 --- a/configs/all-type-checked.js +++ b/configs/all-type-checked.js @@ -3,8 +3,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/all-type-checked']; -module.exports = plugin.configs['flat/all-type-checked']; +export default config; diff --git a/configs/all.js b/configs/all.js index e237d769..cf48e487 100644 --- a/configs/all.js +++ b/configs/all.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/all']; -module.exports = plugin.configs['flat/all']; +export default config; diff --git a/configs/recommended.js b/configs/recommended.js index bb282caa..17c2ffad 100644 --- a/configs/recommended.js +++ b/configs/recommended.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/recommended']; -module.exports = plugin.configs['flat/recommended']; +export default config; diff --git a/configs/rules-recommended.js b/configs/rules-recommended.js index 09d8f57a..8e79f61a 100644 --- a/configs/rules-recommended.js +++ b/configs/rules-recommended.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/rules-recommended']; -module.exports = plugin.configs['flat/rules-recommended']; +export default config; diff --git a/configs/rules.js b/configs/rules.js index 71ca5852..f3414360 100644 --- a/configs/rules.js +++ b/configs/rules.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/rules']; -module.exports = plugin.configs['flat/rules']; +export default config; diff --git a/configs/tests-recommended.js b/configs/tests-recommended.js index 0a467239..367f5d17 100644 --- a/configs/tests-recommended.js +++ b/configs/tests-recommended.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/tests-recommended']; -module.exports = plugin.configs['flat/tests-recommended']; +export default config; diff --git a/configs/tests.js b/configs/tests.js index 184d3bab..38ced25c 100644 --- a/configs/tests.js +++ b/configs/tests.js @@ -4,8 +4,8 @@ * @author 唯然 */ -'use strict'; +import plugin from '../lib/index.js'; -const plugin = require('../lib/index.js'); +const config = plugin.configs['flat/tests']; -module.exports = plugin.configs['flat/tests']; +export default config; diff --git a/eslint.config.js b/eslint.config.js index b25c54b1..e789fccf 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,17 +1,18 @@ -'use strict'; - -const js = require('@eslint/js'); -const { FlatCompat } = require('@eslint/eslintrc'); -const markdown = require('eslint-plugin-markdown'); -const pluginN = require('eslint-plugin-n'); -const eslintPluginConfig = require('eslint-plugin-eslint-plugin/configs/all'); +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import js from '@eslint/js'; +import { FlatCompat } from '@eslint/eslintrc'; +import markdown from 'eslint-plugin-markdown'; +import pluginN from 'eslint-plugin-n'; +import eslintPluginConfig from 'eslint-plugin-eslint-plugin/configs/all'; +const dirname = path.dirname(fileURLToPath(import.meta.url)); const compat = new FlatCompat({ - baseDirectory: __dirname, + baseDirectory: dirname, recommendedConfig: js.configs.recommended, }); -module.exports = [ +export default [ ...compat.extends( 'not-an-aardvark/node', 'plugin:@eslint-community/eslint-comments/recommended', diff --git a/lib/index.js b/lib/index.js index d71340a3..83f4a93e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,15 +3,44 @@ * @author Teddy Katz */ -'use strict'; - // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const fs = require('fs'); -const path = require('path'); -const packageMetadata = require('../package'); +import packageMetadata from '../package.json' with { type: 'json' }; +import consistentOutput from './rules/consistent-output.js'; +import fixerReturn from './rules/fixer-return.js'; +import metaPropertyOrdering from './rules/meta-property-ordering.js'; +import noDeprecatedContextMethods from './rules/no-deprecated-context-methods.js'; +import noDeprecatedReportApi from './rules/no-deprecated-report-api.js'; +import noIdenticalTests from './rules/no-identical-tests.js'; +import noMetaReplacedBy from './rules/no-meta-replaced-by.js'; +import noMetaSchemaDefault from './rules/no-meta-schema-default.js'; +import noMissingMessageIds from './rules/no-missing-message-ids.js'; +import noMissingPlaceholders from './rules/no-missing-placeholders.js'; +import noOnlyTests from './rules/no-only-tests.js'; +import noPropertyInNode from './rules/no-property-in-node.js'; +import noUnusedMessageIds from './rules/no-unused-message-ids.js'; +import noUnusedPlaceholders from './rules/no-unused-placeholders.js'; +import noUselessTokenRange from './rules/no-useless-token-range.js'; +import preferMessageIds from './rules/prefer-message-ids.js'; +import preferObjectRule from './rules/prefer-object-rule.js'; +import preferOutputNull from './rules/prefer-output-null.js'; +import preferPlaceholders from './rules/prefer-placeholders.js'; +import preferReplaceText from './rules/prefer-replace-text.js'; +import reportMessageFormat from './rules/report-message-format.js'; +import requireMetaDefaultOptions from './rules/require-meta-default-options.js'; +import requireMetaDocsDescription from './rules/require-meta-docs-description.js'; +import requireMetaDocsRecommended from './rules/require-meta-docs-recommended.js'; +import requireMetaDocsUrl from './rules/require-meta-docs-url.js'; +import requireMetaFixable from './rules/require-meta-fixable.js'; +import requireMetaHasSuggestions from './rules/require-meta-has-suggestions.js'; +import requireMetaSchemaDescription from './rules/require-meta-schema-description.js'; +import requireMetaSchema from './rules/require-meta-schema.js'; +import requireMetaType from './rules/require-meta-type.js'; +import testCasePropertyOrdering from './rules/test-case-property-ordering.js'; +import testCaseShorthandStrings from './rules/test-case-shorthand-strings.js'; + const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, ''); const configFilters = { @@ -31,16 +60,40 @@ const configFilters = { // ------------------------------------------------------------------------------ // import all rules in lib/rules -const allRules = Object.fromEntries( - fs - .readdirSync(`${__dirname}/rules`) - .filter((fileName) => fileName.endsWith('.js') && /^[^._]/.test(fileName)) - .map((fileName) => fileName.replace(/\.js$/, '')) - .map((ruleName) => [ - ruleName, - require(path.join(__dirname, 'rules', ruleName)), - ]), -); +const allRules = { + 'consistent-output': consistentOutput, + 'fixer-return': fixerReturn, + 'meta-property-ordering': metaPropertyOrdering, + 'no-deprecated-context-methods': noDeprecatedContextMethods, + 'no-deprecated-report-api': noDeprecatedReportApi, + 'no-identical-tests': noIdenticalTests, + 'no-meta-replaced-by': noMetaReplacedBy, + 'no-meta-schema-default': noMetaSchemaDefault, + 'no-missing-message-ids': noMissingMessageIds, + 'no-missing-placeholders': noMissingPlaceholders, + 'no-only-tests': noOnlyTests, + 'no-property-in-node': noPropertyInNode, + 'no-unused-message-ids': noUnusedMessageIds, + 'no-unused-placeholders': noUnusedPlaceholders, + 'no-useless-token-range': noUselessTokenRange, + 'prefer-message-ids': preferMessageIds, + 'prefer-object-rule': preferObjectRule, + 'prefer-output-null': preferOutputNull, + 'prefer-placeholders': preferPlaceholders, + 'prefer-replace-text': preferReplaceText, + 'report-message-format': reportMessageFormat, + 'require-meta-default-options': requireMetaDefaultOptions, + 'require-meta-docs-description': requireMetaDocsDescription, + 'require-meta-docs-recommended': requireMetaDocsRecommended, + 'require-meta-docs-url': requireMetaDocsUrl, + 'require-meta-fixable': requireMetaFixable, + 'require-meta-has-suggestions': requireMetaHasSuggestions, + 'require-meta-schema-description': requireMetaSchemaDescription, + 'require-meta-schema': requireMetaSchema, + 'require-meta-type': requireMetaType, + 'test-case-property-ordering': testCasePropertyOrdering, + 'test-case-shorthand-strings': testCaseShorthandStrings, +}; /** @type {import("eslint").ESLint.Plugin} */ const plugin = { @@ -87,4 +140,4 @@ Object.assign( }, {}), ); -module.exports = plugin; +export default plugin; diff --git a/lib/rules/consistent-output.js b/lib/rules/consistent-output.js index 42cbf3ae..b0eb7f93 100644 --- a/lib/rules/consistent-output.js +++ b/lib/rules/consistent-output.js @@ -3,16 +3,14 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -71,3 +69,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js index 11e7d57f..0f29d8ac 100644 --- a/lib/rules/fixer-return.js +++ b/lib/rules/fixer-return.js @@ -3,21 +3,19 @@ * @author 薛定谔的猫 */ -'use strict'; - // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -167,3 +165,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js index 27e29218..1871187e 100644 --- a/lib/rules/meta-property-ordering.js +++ b/lib/rules/meta-property-ordering.js @@ -2,9 +2,8 @@ * @fileoverview Enforces the order of meta properties */ -'use strict'; +import * as utils from '../utils.js'; -const utils = require('../utils'); const { getKeyName, getRuleInfo } = utils; const defaultOrder = [ @@ -24,7 +23,7 @@ const defaultOrder = [ // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -111,3 +110,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-deprecated-context-methods.js b/lib/rules/no-deprecated-context-methods.js index c4f6134b..416ec988 100644 --- a/lib/rules/no-deprecated-context-methods.js +++ b/lib/rules/no-deprecated-context-methods.js @@ -3,9 +3,7 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; const DEPRECATED_PASSTHROUGHS = { getSource: 'getText', @@ -35,7 +33,7 @@ const DEPRECATED_PASSTHROUGHS = { // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -96,3 +94,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-deprecated-report-api.js b/lib/rules/no-deprecated-report-api.js index eb0c07c2..a0e3f830 100644 --- a/lib/rules/no-deprecated-report-api.js +++ b/lib/rules/no-deprecated-report-api.js @@ -3,16 +3,14 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -81,3 +79,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js index c7c5867f..2dfce64c 100644 --- a/lib/rules/no-identical-tests.js +++ b/lib/rules/no-identical-tests.js @@ -3,16 +3,14 @@ * @author 薛定谔的猫 */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -85,3 +83,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-meta-replaced-by.js b/lib/rules/no-meta-replaced-by.js index 36aa1297..e6f59523 100644 --- a/lib/rules/no-meta-replaced-by.js +++ b/lib/rules/no-meta-replaced-by.js @@ -2,16 +2,14 @@ * @fileoverview Disallows the usage of `meta.replacedBy` property */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -61,3 +59,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-meta-schema-default.js b/lib/rules/no-meta-schema-default.js index ccba978a..26190088 100644 --- a/lib/rules/no-meta-schema-default.js +++ b/lib/rules/no-meta-schema-default.js @@ -1,14 +1,12 @@ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -111,3 +109,5 @@ module.exports = { } }, }; + +export default rule; diff --git a/lib/rules/no-missing-message-ids.js b/lib/rules/no-missing-message-ids.js index 0bc2d9ed..214a9e35 100644 --- a/lib/rules/no-missing-message-ids.js +++ b/lib/rules/no-missing-message-ids.js @@ -1,13 +1,11 @@ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -99,3 +97,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js index 996e8004..f4f126db 100644 --- a/lib/rules/no-missing-placeholders.js +++ b/lib/rules/no-missing-placeholders.js @@ -3,17 +3,15 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -129,3 +127,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js index 3aa267d2..43bf79d1 100644 --- a/lib/rules/no-only-tests.js +++ b/lib/rules/no-only-tests.js @@ -1,14 +1,12 @@ -'use strict'; - -const utils = require('../utils'); -const { +import * as utils from '../utils.js'; +import { isCommaToken, isOpeningBraceToken, isClosingBraceToken, -} = require('@eslint-community/eslint-utils'); +} from '@eslint-community/eslint-utils'; /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -96,3 +94,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-property-in-node.js b/lib/rules/no-property-in-node.js index f21e0060..5a92b749 100644 --- a/lib/rules/no-property-in-node.js +++ b/lib/rules/no-property-in-node.js @@ -1,6 +1,4 @@ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; const defaultTypedNodeSourceFileTesters = [ /@types[/\\]estree[/\\]index\.d\.ts/, @@ -47,7 +45,7 @@ function isAstNodeType(type, typedNodeSourceFileTesters) { } /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -108,3 +106,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-unused-message-ids.js b/lib/rules/no-unused-message-ids.js index 69036140..492e817d 100644 --- a/lib/rules/no-unused-message-ids.js +++ b/lib/rules/no-unused-message-ids.js @@ -1,13 +1,11 @@ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -137,3 +135,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js index d7169c9a..7fbf9cf6 100644 --- a/lib/rules/no-unused-placeholders.js +++ b/lib/rules/no-unused-placeholders.js @@ -3,17 +3,15 @@ * @author 薛定谔的猫 */ -'use strict'; - -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -125,3 +123,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js index 6c6d6214..5cf35fec 100644 --- a/lib/rules/no-useless-token-range.js +++ b/lib/rules/no-useless-token-range.js @@ -3,16 +3,14 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -172,3 +170,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js index cdf66642..cefbab65 100644 --- a/lib/rules/prefer-message-ids.js +++ b/lib/rules/prefer-message-ids.js @@ -1,14 +1,12 @@ -'use strict'; - -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -107,3 +105,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/prefer-object-rule.js b/lib/rules/prefer-object-rule.js index 1b0e98f1..85faaedf 100644 --- a/lib/rules/prefer-object-rule.js +++ b/lib/rules/prefer-object-rule.js @@ -2,16 +2,14 @@ * @author Brad Zacher */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -81,3 +79,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/prefer-output-null.js b/lib/rules/prefer-output-null.js index 4d9e90e5..99819814 100644 --- a/lib/rules/prefer-output-null.js +++ b/lib/rules/prefer-output-null.js @@ -3,16 +3,14 @@ * @author 薛定谔的猫 */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -75,3 +73,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js index f5c88fc1..4ace1011 100644 --- a/lib/rules/prefer-placeholders.js +++ b/lib/rules/prefer-placeholders.js @@ -3,17 +3,15 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); -const { findVariable } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { findVariable } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -100,3 +98,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/prefer-replace-text.js b/lib/rules/prefer-replace-text.js index 173cc509..2a1041e1 100644 --- a/lib/rules/prefer-replace-text.js +++ b/lib/rules/prefer-replace-text.js @@ -3,16 +3,14 @@ * @author 薛定谔的猫 */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -89,3 +87,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js index 4fce24b8..b9d7588f 100644 --- a/lib/rules/report-message-format.js +++ b/lib/rules/report-message-format.js @@ -3,17 +3,15 @@ * @author Teddy Katz */ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -137,3 +135,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-default-options.js b/lib/rules/require-meta-default-options.js index 9ac37575..1e5d0a9c 100644 --- a/lib/rules/require-meta-default-options.js +++ b/lib/rules/require-meta-default-options.js @@ -1,9 +1,7 @@ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -102,3 +100,5 @@ module.exports = { return {}; }, }; + +export default rule; diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js index b3cf5272..d2101144 100644 --- a/lib/rules/require-meta-docs-description.js +++ b/lib/rules/require-meta-docs-description.js @@ -1,7 +1,5 @@ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition @@ -10,7 +8,7 @@ const utils = require('../utils'); const DEFAULT_PATTERN = new RegExp('^(enforce|require|disallow)'); /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -102,3 +100,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-docs-recommended.js b/lib/rules/require-meta-docs-recommended.js index 67851ffc..5ba260c4 100644 --- a/lib/rules/require-meta-docs-recommended.js +++ b/lib/rules/require-meta-docs-recommended.js @@ -1,7 +1,5 @@ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; /** * @param {import('eslint').Rule.RuleFixer} fixer @@ -22,7 +20,7 @@ function insertRecommendedProperty(fixer, objectNode, recommendedValue) { } /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -124,3 +122,5 @@ module.exports = { return {}; }, }; + +export default rule; diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js index cb3e35d3..12bb2b35 100644 --- a/lib/rules/require-meta-docs-url.js +++ b/lib/rules/require-meta-docs-url.js @@ -2,22 +2,20 @@ * @author Toru Nagashima */ -'use strict'; - // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- -const path = require('path'); -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import path from 'path'; +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ----------------------------------------------------------------------------- // Rule Definition // ----------------------------------------------------------------------------- /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -165,3 +163,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js index e69ca4fc..0eb99eb6 100644 --- a/lib/rules/require-meta-fixable.js +++ b/lib/rules/require-meta-fixable.js @@ -3,17 +3,15 @@ * @author Teddy Katz */ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -136,3 +134,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js index c8f75b3f..b9e2f44b 100644 --- a/lib/rules/require-meta-has-suggestions.js +++ b/lib/rules/require-meta-has-suggestions.js @@ -1,14 +1,12 @@ -'use strict'; - -const utils = require('../utils'); -const { getStaticValue } = require('@eslint-community/eslint-utils'); +import * as utils from '../utils.js'; +import { getStaticValue } from '@eslint-community/eslint-utils'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -165,3 +163,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-schema-description.js b/lib/rules/require-meta-schema-description.js index 842d576f..f9bfe7b0 100644 --- a/lib/rules/require-meta-schema-description.js +++ b/lib/rules/require-meta-schema-description.js @@ -1,14 +1,12 @@ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -112,3 +110,5 @@ module.exports = { } }, }; + +export default rule; diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js index 59cf4575..45d28c91 100644 --- a/lib/rules/require-meta-schema.js +++ b/lib/rules/require-meta-schema.js @@ -1,13 +1,11 @@ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -139,3 +137,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/require-meta-type.js b/lib/rules/require-meta-type.js index a6aec027..d0d77103 100644 --- a/lib/rules/require-meta-type.js +++ b/lib/rules/require-meta-type.js @@ -3,10 +3,8 @@ * @author 薛定谔的猫 */ -'use strict'; - -const { getStaticValue } = require('@eslint-community/eslint-utils'); -const utils = require('../utils'); +import { getStaticValue } from '@eslint-community/eslint-utils'; +import * as utils from '../utils.js'; const VALID_TYPES = new Set(['problem', 'suggestion', 'layout']); // ------------------------------------------------------------------------------ @@ -14,7 +12,7 @@ const VALID_TYPES = new Set(['problem', 'suggestion', 'layout']); // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'problem', docs: { @@ -75,3 +73,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js index f6b4dd2f..911d39a1 100644 --- a/lib/rules/test-case-property-ordering.js +++ b/lib/rules/test-case-property-ordering.js @@ -3,9 +3,7 @@ * @author 薛定谔的猫 */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; const defaultOrder = [ 'filename', @@ -25,7 +23,7 @@ const defaultOrder = [ // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -109,3 +107,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/rules/test-case-shorthand-strings.js b/lib/rules/test-case-shorthand-strings.js index 0cf22333..81cd4f57 100644 --- a/lib/rules/test-case-shorthand-strings.js +++ b/lib/rules/test-case-shorthand-strings.js @@ -3,16 +3,14 @@ * @author Teddy Katz */ -'use strict'; - -const utils = require('../utils'); +import * as utils from '../utils.js'; // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ /** @type {import('eslint').Rule.RuleModule} */ -module.exports = { +const rule = { meta: { type: 'suggestion', docs: { @@ -127,3 +125,5 @@ module.exports = { }; }, }; + +export default rule; diff --git a/lib/utils.js b/lib/utils.js index bc15d2c0..a672d491 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,10 +1,5 @@ -'use strict'; - -const { - getStaticValue, - findVariable, -} = require('@eslint-community/eslint-utils'); -const estraverse = require('estraverse'); +import { getStaticValue, findVariable } from '@eslint-community/eslint-utils'; +import estraverse from 'estraverse'; const functionTypes = new Set([ 'FunctionExpression', @@ -46,7 +41,7 @@ const INTERESTING_RULE_KEYS = new Set(['create', 'meta']); */ function collectInterestingProperties(properties, interestingKeys) { return properties.reduce((parsedProps, prop) => { - const keyValue = module.exports.getKeyName(prop); + const keyValue = getKeyName(prop); if (interestingKeys.has(keyValue)) { // In TypeScript, unwrap any usage of `{} as const`. parsedProps[keyValue] = @@ -343,685 +338,656 @@ function collectArrayElements(node) { return []; } -module.exports = { - /** - * Performs static analysis on an AST to try to determine the final value of `module.exports`. - * @param {{ast: ASTNode, scopeManager?: ScopeManager}} sourceCode The object contains `Program` AST node, and optional `scopeManager` - * @returns {Object} An object with keys `meta`, `create`, and `isNewStyle`. `meta` and `create` correspond to the AST nodes - for the final values of `module.exports.meta` and `module.exports.create`. `isNewStyle` will be `true` if `module.exports` - is an object, and `false` if module.exports is just the `create` function. If no valid ESLint rule info can be extracted - from the file, the return value will be `null`. - */ - getRuleInfo({ ast, scopeManager }) { - const exportNodes = - ast.sourceType === 'module' - ? getRuleExportsESM(ast, scopeManager) - : getRuleExportsCJS(ast, scopeManager); - - const createExists = Object.prototype.hasOwnProperty.call( - exportNodes, - 'create', - ); - if (!createExists) { - return null; - } +/** +* Performs static analysis on an AST to try to determine the final value of `module.exports`. +* @param {{ast: ASTNode, scopeManager?: ScopeManager}} sourceCode The object contains `Program` AST node, and optional `scopeManager` +* @returns {Object} An object with keys `meta`, `create`, and `isNewStyle`. `meta` and `create` correspond to the AST nodes +for the final values of `module.exports.meta` and `module.exports.create`. `isNewStyle` will be `true` if `module.exports` +is an object, and `false` if `module.exports` is just the `create` function. If no valid ESLint rule info can be extracted +from the file, the return value will be `null`. +*/ +export function getRuleInfo({ ast, scopeManager }) { + const exportNodes = + ast.sourceType === 'module' + ? getRuleExportsESM(ast, scopeManager) + : getRuleExportsCJS(ast, scopeManager); + + const createExists = Object.prototype.hasOwnProperty.call( + exportNodes, + 'create', + ); + if (!createExists) { + return null; + } - // If create/meta are defined in variables, get their values. - for (const key of Object.keys(exportNodes)) { - if (exportNodes[key] && exportNodes[key].type === 'Identifier') { - const value = findVariableValue(exportNodes[key], scopeManager); - if (value) { - exportNodes[key] = value; - } + // If create/meta are defined in variables, get their values. + for (const key of Object.keys(exportNodes)) { + if (exportNodes[key] && exportNodes[key].type === 'Identifier') { + const value = findVariableValue(exportNodes[key], scopeManager); + if (value) { + exportNodes[key] = value; } } + } - const createIsFunction = isNormalFunctionExpression(exportNodes.create); - if (!createIsFunction) { - return null; - } + const createIsFunction = isNormalFunctionExpression(exportNodes.create); + if (!createIsFunction) { + return null; + } - return Object.assign({ isNewStyle: true, meta: null }, exportNodes); - }, + return Object.assign({ isNewStyle: true, meta: null }, exportNodes); +} - /** - * Gets all the identifiers referring to the `context` variable in a rule source file. Note that this function will - * only work correctly after traversing the AST has started (e.g. in the first `Program` node). - * @param {RuleContext} scopeManager - * @param {ASTNode} ast The `Program` node for the file - * @returns {Set} A Set of all `Identifier` nodes that are references to the `context` value for the file - */ - getContextIdentifiers(scopeManager, ast) { - const ruleInfo = module.exports.getRuleInfo({ ast, scopeManager }); +/** + * Gets all the identifiers referring to the `context` variable in a rule source file. Note that this function will + * only work correctly after traversing the AST has started (e.g. in the first `Program` node). + * @param {RuleContext} scopeManager + * @param {ASTNode} ast The `Program` node for the file + * @returns {Set} A Set of all `Identifier` nodes that are references to the `context` value for the file + */ +export function getContextIdentifiers(scopeManager, ast) { + const ruleInfo = getRuleInfo({ ast, scopeManager }); - if ( - !ruleInfo || - ruleInfo.create.params.length === 0 || - ruleInfo.create.params[0].type !== 'Identifier' - ) { - return new Set(); - } + if ( + !ruleInfo || + ruleInfo.create.params.length === 0 || + ruleInfo.create.params[0].type !== 'Identifier' + ) { + return new Set(); + } - return new Set( - scopeManager - .getDeclaredVariables(ruleInfo.create) - .find((variable) => variable.name === ruleInfo.create.params[0].name) - .references.map((ref) => ref.identifier), - ); - }, - - /** - * Gets the key name of a Property, if it can be determined statically. - * @param {ASTNode} node The `Property` node - * @param {Scope} scope - * @returns {string|null} The key name, or `null` if the name cannot be determined statically. - */ - getKeyName(property, scope) { - if (!property.key) { - // likely a SpreadElement or another non-standard node - return null; - } - if (property.key.type === 'Identifier') { - if (property.computed) { - // Variable key: { [myVariable]: 'hello world' } - if (scope) { - const staticValue = getStaticValue(property.key, scope); - return staticValue ? staticValue.value : null; - } - // TODO: ensure scope is always passed to getKeyName() so we don't need to handle the case where it's not passed. - return null; - } - return property.key.name; - } - if (property.key.type === 'Literal') { - return '' + property.key.value; - } - if ( - property.key.type === 'TemplateLiteral' && - property.key.quasis.length === 1 - ) { - return property.key.quasis[0].value.cooked; - } + return new Set( + scopeManager + .getDeclaredVariables(ruleInfo.create) + .find((variable) => variable.name === ruleInfo.create.params[0].name) + .references.map((ref) => ref.identifier), + ); +} + +/** + * Gets the key name of a Property, if it can be determined statically. + * @param {ASTNode} node The `Property` node + * @param {Scope} scope + * @returns {string|null} The key name, or `null` if the name cannot be determined statically. + */ +export function getKeyName(property, scope) { + if (!property.key) { + // likely a SpreadElement or another non-standard node return null; - }, - - /** - * Extracts the body of a function if the given node is a function - * - * @param {ASTNode} node - * @returns {ExpressionStatement[]} - */ - extractFunctionBody(node) { - if ( - node.type === 'ArrowFunctionExpression' || - node.type === 'FunctionExpression' - ) { - if (node.body.type === 'BlockStatement') { - return node.body.body; + } + if (property.key.type === 'Identifier') { + if (property.computed) { + // Variable key: { [myVariable]: 'hello world' } + if (scope) { + const staticValue = getStaticValue(property.key, scope); + return staticValue ? staticValue.value : null; } - - return [node.body]; + // TODO: ensure scope is always passed to getKeyName() so we don't need to handle the case where it's not passed. + return null; } + return property.key.name; + } + if (property.key.type === 'Literal') { + return '' + property.key.value; + } + if ( + property.key.type === 'TemplateLiteral' && + property.key.quasis.length === 1 + ) { + return property.key.quasis[0].value.cooked; + } + return null; +} - return []; - }, - - /** - * Checks the given statements for possible test info - * - * @param {RuleContext} context The `context` variable for the source file itself - * @param {ASTNode[]} statements The statements to check - * @param {Set} variableIdentifiers - * @returns {CallExpression[]} - */ - checkStatementsForTestInfo( - context, - statements, - variableIdentifiers = new Set(), +/** + * Extracts the body of a function if the given node is a function + * + * @param {ASTNode} node + * @returns {ExpressionStatement[]} + */ +export function extractFunctionBody(node) { + if ( + node.type === 'ArrowFunctionExpression' || + node.type === 'FunctionExpression' ) { - const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 - const runCalls = []; - - for (const statement of statements) { - if (statement.type === 'VariableDeclaration') { - for (const declarator of statement.declarations) { - if (!declarator.init) { - continue; - } + if (node.body.type === 'BlockStatement') { + return node.body.body; + } - const extracted = module.exports.extractFunctionBody(declarator.init); + return [node.body]; + } - runCalls.push( - ...module.exports.checkStatementsForTestInfo( - context, - extracted, - variableIdentifiers, - ), - ); + return []; +} - if ( - isRuleTesterConstruction(declarator.init) && - declarator.id.type === 'Identifier' - ) { - const vars = sourceCode.getDeclaredVariables - ? sourceCode.getDeclaredVariables(declarator) - : context.getDeclaredVariables(declarator); - vars.forEach((variable) => { - variable.references - .filter((ref) => ref.isRead()) - .forEach((ref) => variableIdentifiers.add(ref.identifier)); - }); - } +/** + * Checks the given statements for possible test info + * + * @param {RuleContext} context The `context` variable for the source file itself + * @param {ASTNode[]} statements The statements to check + * @param {Set} variableIdentifiers + * @returns {CallExpression[]} + */ +export function checkStatementsForTestInfo( + context, + statements, + variableIdentifiers = new Set(), +) { + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9 + const runCalls = []; + + for (const statement of statements) { + if (statement.type === 'VariableDeclaration') { + for (const declarator of statement.declarations) { + if (!declarator.init) { + continue; } - } - if (statement.type === 'FunctionDeclaration') { + const extracted = extractFunctionBody(declarator.init); + runCalls.push( - ...module.exports.checkStatementsForTestInfo( + ...checkStatementsForTestInfo( context, - statement.body.body, + extracted, variableIdentifiers, ), ); + + if ( + isRuleTesterConstruction(declarator.init) && + declarator.id.type === 'Identifier' + ) { + const vars = sourceCode.getDeclaredVariables + ? sourceCode.getDeclaredVariables(declarator) + : context.getDeclaredVariables(declarator); + vars.forEach((variable) => { + variable.references + .filter((ref) => ref.isRead()) + .forEach((ref) => variableIdentifiers.add(ref.identifier)); + }); + } } + } - if (statement.type === 'IfStatement') { - const body = - statement.consequent.type === 'BlockStatement' - ? statement.consequent.body - : [statement.consequent]; + if (statement.type === 'FunctionDeclaration') { + runCalls.push( + ...checkStatementsForTestInfo( + context, + statement.body.body, + variableIdentifiers, + ), + ); + } - runCalls.push( - ...module.exports.checkStatementsForTestInfo( - context, - body, - variableIdentifiers, - ), - ); + if (statement.type === 'IfStatement') { + const body = + statement.consequent.type === 'BlockStatement' + ? statement.consequent.body + : [statement.consequent]; - continue; - } + runCalls.push( + ...checkStatementsForTestInfo(context, body, variableIdentifiers), + ); - const expression = - statement.type === 'ExpressionStatement' - ? statement.expression - : statement; + continue; + } - if (expression.type !== 'CallExpression') { - continue; - } + const expression = + statement.type === 'ExpressionStatement' + ? statement.expression + : statement; - for (const arg of expression.arguments) { - const extracted = module.exports.extractFunctionBody(arg); + if (expression.type !== 'CallExpression') { + continue; + } - runCalls.push( - ...module.exports.checkStatementsForTestInfo( - context, - extracted, - variableIdentifiers, - ), - ); - } + for (const arg of expression.arguments) { + const extracted = extractFunctionBody(arg); - if ( - expression.callee.type === 'MemberExpression' && - (isRuleTesterConstruction(expression.callee.object) || - variableIdentifiers.has(expression.callee.object)) && - expression.callee.property.type === 'Identifier' && - expression.callee.property.name === 'run' - ) { - runCalls.push(expression); - } + runCalls.push( + ...checkStatementsForTestInfo(context, extracted, variableIdentifiers), + ); } - return runCalls; - }, - - /** - * Performs static analysis on an AST to try to find test cases - * @param {RuleContext} context The `context` variable for the source file itself - * @param {ASTNode} ast The `Program` node for the file. - * @returns {object} An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests - */ - getTestInfo(context, ast) { - const runCalls = module.exports.checkStatementsForTestInfo( - context, - ast.body, - ); + if ( + expression.callee.type === 'MemberExpression' && + (isRuleTesterConstruction(expression.callee.object) || + variableIdentifiers.has(expression.callee.object)) && + expression.callee.property.type === 'Identifier' && + expression.callee.property.name === 'run' + ) { + runCalls.push(expression); + } + } + + return runCalls; +} + +/** + * Performs static analysis on an AST to try to find test cases + * @param {RuleContext} context The `context` variable for the source file itself + * @param {ASTNode} ast The `Program` node for the file. + * @returns {object} An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests + */ +export function getTestInfo(context, ast) { + const runCalls = checkStatementsForTestInfo(context, ast.body); + + return runCalls + .filter( + (call) => + call.arguments.length >= 3 && + call.arguments[2].type === 'ObjectExpression', + ) + .map((call) => call.arguments[2]) + .map((run) => { + const validProperty = run.properties.find( + (prop) => getKeyName(prop) === 'valid', + ); + const invalidProperty = run.properties.find( + (prop) => getKeyName(prop) === 'invalid', + ); + + return { + valid: + validProperty && validProperty.value.type === 'ArrayExpression' + ? validProperty.value.elements.filter(Boolean) + : [], + invalid: + invalidProperty && invalidProperty.value.type === 'ArrayExpression' + ? invalidProperty.value.elements.filter(Boolean) + : [], + }; + }); +} + +/** + * Gets information on a report, given the ASTNode of context.report(). + * @param {ASTNode} node The ASTNode of context.report() + * @param {Context} context + */ +export function getReportInfo(node, context) { + const reportArgs = node.arguments; + + // If there is exactly one argument, the API expects an object. + // Otherwise, if the second argument is a string, the arguments are interpreted as + // ['node', 'message', 'data', 'fix']. + // Otherwise, the arguments are interpreted as ['node', 'loc', 'message', 'data', 'fix']. + + if (reportArgs.length === 0) { + return null; + } + + if (reportArgs.length === 1) { + if (reportArgs[0].type === 'ObjectExpression') { + return reportArgs[0].properties.reduce((reportInfo, property) => { + const propName = getKeyName(property); + + if (propName !== null) { + return Object.assign(reportInfo, { [propName]: property.value }); + } + return reportInfo; + }, {}); + } + return null; + } + + let keys; + const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9 + const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when dropping eslint < v9 + const secondArgStaticValue = getStaticValue(reportArgs[1], scope); - return runCalls + if ( + (secondArgStaticValue && typeof secondArgStaticValue.value === 'string') || + reportArgs[1].type === 'TemplateLiteral' + ) { + keys = ['node', 'message', 'data', 'fix']; + } else if ( + reportArgs[1].type === 'ObjectExpression' || + reportArgs[1].type === 'ArrayExpression' || + (reportArgs[1].type === 'Literal' && + typeof reportArgs[1].value !== 'string') || + (secondArgStaticValue && + ['object', 'number'].includes(typeof secondArgStaticValue.value)) + ) { + keys = ['node', 'loc', 'message', 'data', 'fix']; + } else { + // Otherwise, we can't statically determine what argument means what, so no safe fix is possible. + return null; + } + + return Object.fromEntries( + keys + .slice(0, reportArgs.length) + .map((key, index) => [key, reportArgs[index]]), + ); +} + +/** + * Gets a set of all `sourceCode` identifiers. + * @param {ScopeManager} scopeManager + * @param {ASTNode} ast The AST of the file. This must have `parent` properties. + * @returns {Set} A set of all identifiers referring to the `SourceCode` object. + */ +export function getSourceCodeIdentifiers(scopeManager, ast) { + return new Set( + [...getContextIdentifiers(scopeManager, ast)] .filter( - (call) => - call.arguments.length >= 3 && - call.arguments[2].type === 'ObjectExpression', + (identifier) => + identifier.parent && + identifier.parent.type === 'MemberExpression' && + identifier === identifier.parent.object && + identifier.parent.property.type === 'Identifier' && + identifier.parent.property.name === 'getSourceCode' && + identifier.parent.parent.type === 'CallExpression' && + identifier.parent === identifier.parent.parent.callee && + identifier.parent.parent.parent.type === 'VariableDeclarator' && + identifier.parent.parent === identifier.parent.parent.parent.init && + identifier.parent.parent.parent.id.type === 'Identifier', ) - .map((call) => call.arguments[2]) - .map((run) => { - const validProperty = run.properties.find( - (prop) => module.exports.getKeyName(prop) === 'valid', - ); - const invalidProperty = run.properties.find( - (prop) => module.exports.getKeyName(prop) === 'invalid', - ); + .flatMap((identifier) => + scopeManager.getDeclaredVariables(identifier.parent.parent.parent), + ) + .flatMap((variable) => variable.references) + .map((ref) => ref.identifier), + ); +} + +/** + * Insert a given property into a given object literal. + * @param {SourceCodeFixer} fixer The fixer. + * @param {Node} node The ObjectExpression node to insert a property. + * @param {string} propertyText The property code to insert. + * @returns {void} + */ +export function insertProperty(fixer, node, propertyText, sourceCode) { + if (node.properties.length === 0) { + return fixer.replaceText(node, `{\n${propertyText}\n}`); + } + return fixer.insertTextAfter( + sourceCode.getLastToken(node.properties.at(-1)), + `,\n${propertyText}`, + ); +} +/** + * Collect all context.report({...}) violation/suggestion-related nodes into a standardized array for convenience. + * @param {Object} reportInfo - Result of getReportInfo(). + * @returns {messageId?: String, message?: String, data?: Object, fix?: Function}[] + */ +export function collectReportViolationAndSuggestionData(reportInfo) { + return [ + // Violation message + { + messageId: reportInfo.messageId, + message: reportInfo.message, + data: reportInfo.data, + fix: reportInfo.fix, + }, + // Suggestion messages + ...collectArrayElements(reportInfo.suggest) + .map((suggestObjNode) => { + if (suggestObjNode.type !== 'ObjectExpression') { + // Ignore non-objects (like variables or function calls). + return null; + } return { - valid: - validProperty && validProperty.value.type === 'ArrayExpression' - ? validProperty.value.elements.filter(Boolean) - : [], - invalid: - invalidProperty && invalidProperty.value.type === 'ArrayExpression' - ? invalidProperty.value.elements.filter(Boolean) - : [], + messageId: findObjectPropertyValueByKeyName( + suggestObjNode, + 'messageId', + ), + message: findObjectPropertyValueByKeyName(suggestObjNode, 'desc'), // Note: suggestion message named `desc` + data: findObjectPropertyValueByKeyName(suggestObjNode, 'data'), + fix: findObjectPropertyValueByKeyName(suggestObjNode, 'fix'), }; - }); - }, - - /** - * Gets information on a report, given the ASTNode of context.report(). - * @param {ASTNode} node The ASTNode of context.report() - * @param {Context} context - */ - getReportInfo(node, context) { - const reportArgs = node.arguments; - - // If there is exactly one argument, the API expects an object. - // Otherwise, if the second argument is a string, the arguments are interpreted as - // ['node', 'message', 'data', 'fix']. - // Otherwise, the arguments are interpreted as ['node', 'loc', 'message', 'data', 'fix']. - - if (reportArgs.length === 0) { - return null; - } + }) + .filter((item) => item !== null), + ]; +} - if (reportArgs.length === 1) { - if (reportArgs[0].type === 'ObjectExpression') { - return reportArgs[0].properties.reduce((reportInfo, property) => { - const propName = module.exports.getKeyName(property); +/** + * Whether the provided node represents an autofixer function. + * @param {Node} node + * @param {Node[]} contextIdentifiers + * @returns {boolean} + */ +export function isAutoFixerFunction(node, contextIdentifiers) { + const parent = node.parent; + return ( + ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.type) && + parent.parent.type === 'ObjectExpression' && + parent.parent.parent.type === 'CallExpression' && + contextIdentifiers.has(parent.parent.parent.callee.object) && + parent.parent.parent.callee.property.name === 'report' && + getReportInfo(parent.parent.parent).fix === node + ); +} - if (propName !== null) { - return Object.assign(reportInfo, { [propName]: property.value }); - } - return reportInfo; - }, {}); +/** + * Whether the provided node represents a suggestion fixer function. + * @param {Node} node + * @param {Node[]} contextIdentifiers + * @returns {boolean} + */ +export function isSuggestionFixerFunction(node, contextIdentifiers) { + const parent = node.parent; + return ( + (node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression') && + parent.type === 'Property' && + parent.key.type === 'Identifier' && + parent.key.name === 'fix' && + parent.parent.type === 'ObjectExpression' && + parent.parent.parent.type === 'ArrayExpression' && + parent.parent.parent.parent.type === 'Property' && + parent.parent.parent.parent.key.type === 'Identifier' && + parent.parent.parent.parent.key.name === 'suggest' && + parent.parent.parent.parent.parent.type === 'ObjectExpression' && + parent.parent.parent.parent.parent.parent.type === 'CallExpression' && + contextIdentifiers.has( + parent.parent.parent.parent.parent.parent.callee.object, + ) && + parent.parent.parent.parent.parent.parent.callee.property.name === + 'report' && + getReportInfo(parent.parent.parent.parent.parent.parent).suggest === + parent.parent.parent + ); +} + +/** + * List all properties contained in an object. + * Evaluates and includes any properties that may be behind spreads. + * @param {Node} objectNode + * @param {ScopeManager} scopeManager + * @returns {Node[]} the list of all properties that could be found + */ +export function evaluateObjectProperties(objectNode, scopeManager) { + if (!objectNode || objectNode.type !== 'ObjectExpression') { + return []; + } + + return objectNode.properties.flatMap((property) => { + if (property.type === 'SpreadElement') { + const value = findVariableValue(property.argument, scopeManager); + if (value && value.type === 'ObjectExpression') { + return value.properties; } - return null; + return []; } + return [property]; + }); +} - let keys; - const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9 - const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when dropping eslint < v9 - const secondArgStaticValue = getStaticValue(reportArgs[1], scope); +export function getMetaDocsProperty(propertyName, ruleInfo, scopeManager) { + const metaNode = ruleInfo.meta; - if ( - (secondArgStaticValue && - typeof secondArgStaticValue.value === 'string') || - reportArgs[1].type === 'TemplateLiteral' - ) { - keys = ['node', 'message', 'data', 'fix']; - } else if ( - reportArgs[1].type === 'ObjectExpression' || - reportArgs[1].type === 'ArrayExpression' || - (reportArgs[1].type === 'Literal' && - typeof reportArgs[1].value !== 'string') || - (secondArgStaticValue && - ['object', 'number'].includes(typeof secondArgStaticValue.value)) - ) { - keys = ['node', 'loc', 'message', 'data', 'fix']; - } else { - // Otherwise, we can't statically determine what argument means what, so no safe fix is possible. - return null; - } + const docsNode = evaluateObjectProperties(metaNode, scopeManager).find( + (p) => p.type === 'Property' && getKeyName(p) === 'docs', + ); - return Object.fromEntries( - keys - .slice(0, reportArgs.length) - .map((key, index) => [key, reportArgs[index]]), - ); - }, - - /** - * Gets a set of all `sourceCode` identifiers. - * @param {ScopeManager} scopeManager - * @param {ASTNode} ast The AST of the file. This must have `parent` properties. - * @returns {Set} A set of all identifiers referring to the `SourceCode` object. - */ - getSourceCodeIdentifiers(scopeManager, ast) { - return new Set( - [...module.exports.getContextIdentifiers(scopeManager, ast)] - .filter( - (identifier) => - identifier.parent && - identifier.parent.type === 'MemberExpression' && - identifier === identifier.parent.object && - identifier.parent.property.type === 'Identifier' && - identifier.parent.property.name === 'getSourceCode' && - identifier.parent.parent.type === 'CallExpression' && - identifier.parent === identifier.parent.parent.callee && - identifier.parent.parent.parent.type === 'VariableDeclarator' && - identifier.parent.parent === identifier.parent.parent.parent.init && - identifier.parent.parent.parent.id.type === 'Identifier', - ) - .flatMap((identifier) => - scopeManager.getDeclaredVariables(identifier.parent.parent.parent), - ) - .flatMap((variable) => variable.references) - .map((ref) => ref.identifier), - ); - }, - - /** - * Insert a given property into a given object literal. - * @param {SourceCodeFixer} fixer The fixer. - * @param {Node} node The ObjectExpression node to insert a property. - * @param {string} propertyText The property code to insert. - * @returns {void} - */ - insertProperty(fixer, node, propertyText, sourceCode) { - if (node.properties.length === 0) { - return fixer.replaceText(node, `{\n${propertyText}\n}`); + const metaPropertyNode = evaluateObjectProperties( + docsNode?.value, + scopeManager, + ).find((p) => p.type === 'Property' && getKeyName(p) === propertyName); + + return { docsNode, metaNode, metaPropertyNode }; +} + +/** + * Get the `meta.messages` node from a rule. + * @param {RuleInfo} ruleInfo + * @param {ScopeManager} scopeManager + * @returns {Node|undefined} + */ +export function getMessagesNode(ruleInfo, scopeManager) { + if (!ruleInfo) { + return; + } + + const metaNode = ruleInfo.meta; + const messagesNode = evaluateObjectProperties(metaNode, scopeManager).find( + (p) => p.type === 'Property' && getKeyName(p) === 'messages', + ); + + if (messagesNode) { + if (messagesNode.value.type === 'ObjectExpression') { + return messagesNode.value; } - return fixer.insertTextAfter( - sourceCode.getLastToken(node.properties.at(-1)), - `,\n${propertyText}`, - ); - }, - - /** - * Collect all context.report({...}) violation/suggestion-related nodes into a standardized array for convenience. - * @param {Object} reportInfo - Result of getReportInfo(). - * @returns {messageId?: String, message?: String, data?: Object, fix?: Function}[] - */ - collectReportViolationAndSuggestionData(reportInfo) { - return [ - // Violation message - { - messageId: reportInfo.messageId, - message: reportInfo.message, - data: reportInfo.data, - fix: reportInfo.fix, - }, - // Suggestion messages - ...collectArrayElements(reportInfo.suggest) - .map((suggestObjNode) => { - if (suggestObjNode.type !== 'ObjectExpression') { - // Ignore non-objects (like variables or function calls). - return null; - } - return { - messageId: findObjectPropertyValueByKeyName( - suggestObjNode, - 'messageId', - ), - message: findObjectPropertyValueByKeyName(suggestObjNode, 'desc'), // Note: suggestion message named `desc` - data: findObjectPropertyValueByKeyName(suggestObjNode, 'data'), - fix: findObjectPropertyValueByKeyName(suggestObjNode, 'fix'), - }; - }) - .filter((item) => item !== null), - ]; - }, - - /** - * Whether the provided node represents an autofixer function. - * @param {Node} node - * @param {Node[]} contextIdentifiers - * @returns {boolean} - */ - isAutoFixerFunction(node, contextIdentifiers) { - const parent = node.parent; - return ( - ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.type) && - parent.parent.type === 'ObjectExpression' && - parent.parent.parent.type === 'CallExpression' && - contextIdentifiers.has(parent.parent.parent.callee.object) && - parent.parent.parent.callee.property.name === 'report' && - module.exports.getReportInfo(parent.parent.parent).fix === node - ); - }, - - /** - * Whether the provided node represents a suggestion fixer function. - * @param {Node} node - * @param {Node[]} contextIdentifiers - * @returns {boolean} - */ - isSuggestionFixerFunction(node, contextIdentifiers) { - const parent = node.parent; - return ( - (node.type === 'FunctionExpression' || - node.type === 'ArrowFunctionExpression') && - parent.type === 'Property' && - parent.key.type === 'Identifier' && - parent.key.name === 'fix' && - parent.parent.type === 'ObjectExpression' && - parent.parent.parent.type === 'ArrayExpression' && - parent.parent.parent.parent.type === 'Property' && - parent.parent.parent.parent.key.type === 'Identifier' && - parent.parent.parent.parent.key.name === 'suggest' && - parent.parent.parent.parent.parent.type === 'ObjectExpression' && - parent.parent.parent.parent.parent.parent.type === 'CallExpression' && - contextIdentifiers.has( - parent.parent.parent.parent.parent.parent.callee.object, - ) && - parent.parent.parent.parent.parent.parent.callee.property.name === - 'report' && - module.exports.getReportInfo(parent.parent.parent.parent.parent.parent) - .suggest === parent.parent.parent - ); - }, - - /** - * List all properties contained in an object. - * Evaluates and includes any properties that may be behind spreads. - * @param {Node} objectNode - * @param {ScopeManager} scopeManager - * @returns {Node[]} the list of all properties that could be found - */ - evaluateObjectProperties(objectNode, scopeManager) { - if (!objectNode || objectNode.type !== 'ObjectExpression') { - return []; + const value = findVariableValue(messagesNode.value, scopeManager); + if (value && value.type === 'ObjectExpression') { + return value; } + } +} - return objectNode.properties.flatMap((property) => { - if (property.type === 'SpreadElement') { - const value = findVariableValue(property.argument, scopeManager); - if (value && value.type === 'ObjectExpression') { - return value.properties; - } - return []; - } - return [property]; - }); - }, +/** + * Get the list of messageId properties from `meta.messages` for a rule. + * @param {RuleInfo} ruleInfo + * @param {ScopeManager} scopeManager + * @returns {Node[]|undefined} + */ +export function getMessageIdNodes(ruleInfo, scopeManager) { + const messagesNode = getMessagesNode(ruleInfo, scopeManager); - getMetaDocsProperty(propertyName, ruleInfo, scopeManager) { - const metaNode = ruleInfo.meta; + return messagesNode && messagesNode.type === 'ObjectExpression' + ? evaluateObjectProperties(messagesNode, scopeManager) + : undefined; +} - const docsNode = module.exports - .evaluateObjectProperties(metaNode, scopeManager) - .find( - (p) => p.type === 'Property' && module.exports.getKeyName(p) === 'docs', - ); +/** + * Get the messageId property from a rule's `meta.messages` that matches the given `messageId`. + * @param {String} messageId - the messageId to check for + * @param {RuleInfo} ruleInfo + * @param {ScopeManager} scopeManager + * @param {Scope} scope + * @returns {Node|undefined} The matching messageId property from `meta.messages`. + */ +export function getMessageIdNodeById(messageId, ruleInfo, scopeManager, scope) { + return getMessageIdNodes(ruleInfo, scopeManager).find( + (p) => p.type === 'Property' && getKeyName(p, scope) === messageId, + ); +} - const metaPropertyNode = module.exports - .evaluateObjectProperties(docsNode?.value, scopeManager) - .find( - (p) => - p.type === 'Property' && - module.exports.getKeyName(p) === propertyName, - ); +export function getMetaSchemaNode(metaNode, scopeManager) { + return evaluateObjectProperties(metaNode, scopeManager).find( + (p) => p.type === 'Property' && getKeyName(p) === 'schema', + ); +} + +export function getMetaSchemaNodeProperty(schemaNode, scopeManager) { + if (!schemaNode) { + return null; + } - return { docsNode, metaNode, metaPropertyNode }; - }, - - /** - * Get the `meta.messages` node from a rule. - * @param {RuleInfo} ruleInfo - * @param {ScopeManager} scopeManager - * @returns {Node|undefined} - */ - getMessagesNode(ruleInfo, scopeManager) { - if (!ruleInfo) { + let { value } = schemaNode; + if (value.type === 'Identifier' && value.name !== 'undefined') { + const variable = findVariable( + scopeManager.acquire(value) || scopeManager.globalScope, + value, + ); + + // If we can't find the declarator, we have to assume it's in correct type + if ( + !variable || + !variable.defs || + !variable.defs[0] || + !variable.defs[0].node || + variable.defs[0].node.type !== 'VariableDeclarator' || + !variable.defs[0].node.init + ) { return; } - const metaNode = ruleInfo.meta; - const messagesNode = module.exports - .evaluateObjectProperties(metaNode, scopeManager) - .find( - (p) => - p.type === 'Property' && module.exports.getKeyName(p) === 'messages', - ); + value = variable.defs[0].node.init; + } - if (messagesNode) { - if (messagesNode.value.type === 'ObjectExpression') { - return messagesNode.value; - } - const value = findVariableValue(messagesNode.value, scopeManager); - if (value && value.type === 'ObjectExpression') { - return value; - } - } - }, - - /** - * Get the list of messageId properties from `meta.messages` for a rule. - * @param {RuleInfo} ruleInfo - * @param {ScopeManager} scopeManager - * @returns {Node[]|undefined} - */ - getMessageIdNodes(ruleInfo, scopeManager) { - const messagesNode = module.exports.getMessagesNode(ruleInfo, scopeManager); - - return messagesNode && messagesNode.type === 'ObjectExpression' - ? module.exports.evaluateObjectProperties(messagesNode, scopeManager) - : undefined; - }, - - /** - * Get the messageId property from a rule's `meta.messages` that matches the given `messageId`. - * @param {String} messageId - the messageId to check for - * @param {RuleInfo} ruleInfo - * @param {ScopeManager} scopeManager - * @param {Scope} scope - * @returns {Node|undefined} The matching messageId property from `meta.messages`. - */ - getMessageIdNodeById(messageId, ruleInfo, scopeManager, scope) { - return module.exports - .getMessageIdNodes(ruleInfo, scopeManager) - .find( - (p) => - p.type === 'Property' && - module.exports.getKeyName(p, scope) === messageId, - ); - }, - - getMetaSchemaNode(metaNode, scopeManager) { - return module.exports - .evaluateObjectProperties(metaNode, scopeManager) - .find( - (p) => - p.type === 'Property' && module.exports.getKeyName(p) === 'schema', - ); - }, + return value; +} - getMetaSchemaNodeProperty(schemaNode, scopeManager) { - if (!schemaNode) { - return null; +/** + * Get the possible values that a variable was initialized to at some point. + * @param {Node} node - the Identifier node for the variable. + * @param {ScopeManager} scopeManager + * @returns {Node[]} the values that the given variable could be initialized to. + */ +export function findPossibleVariableValues(node, scopeManager) { + const variable = findVariable( + scopeManager.acquire(node) || scopeManager.globalScope, + node, + ); + return ((variable && variable.references) || []).flatMap((ref) => { + if ( + ref.writeExpr && + (ref.writeExpr.parent.type !== 'AssignmentExpression' || + ref.writeExpr.parent.operator === '=') + ) { + // Given node `x`, get `123` from `x = 123;`. + // Ignore assignments with other operators like `x += 'abc';'`; + return [ref.writeExpr]; } + return []; + }); +} - let { value } = schemaNode; - if (value.type === 'Identifier' && value.name !== 'undefined') { - const variable = findVariable( - scopeManager.acquire(value) || scopeManager.globalScope, - value, - ); +/** + * @param {Node} node + * @returns {boolean} Whether the node is an Identifier with name `undefined`. + */ +export function isUndefinedIdentifier(node) { + return node.type === 'Identifier' && node.name === 'undefined'; +} - // If we can't find the declarator, we have to assume it's in correct type - if ( - !variable || - !variable.defs || - !variable.defs[0] || - !variable.defs[0].node || - variable.defs[0].node.type !== 'VariableDeclarator' || - !variable.defs[0].node.init - ) { - return; - } +/** + * Check whether a variable's definition is from a function parameter. + * @param {Node} node - the Identifier node for the variable. + * @param {ScopeManager} scopeManager + * @returns {boolean} whether the variable comes from a function parameter + */ +export function isVariableFromParameter(node, scopeManager) { + const variable = findVariable( + scopeManager.acquire(node) || scopeManager.globalScope, + node, + ); - value = variable.defs[0].node.init; - } + return variable?.defs[0]?.type === 'Parameter'; +} - return value; - }, +export function getSourceCode(context) { + // TODO: remove contet.getSourceCode() when dropping eslint < v9 + return context.sourceCode || context.getSourceCode(); +} - /** - * Get the possible values that a variable was initialized to at some point. - * @param {Node} node - the Identifier node for the variable. - * @param {ScopeManager} scopeManager - * @returns {Node[]} the values that the given variable could be initialized to. - */ - findPossibleVariableValues(node, scopeManager) { - const variable = findVariable( - scopeManager.acquire(node) || scopeManager.globalScope, - node, - ); - return ((variable && variable.references) || []).flatMap((ref) => { - if ( - ref.writeExpr && - (ref.writeExpr.parent.type !== 'AssignmentExpression' || - ref.writeExpr.parent.operator === '=') - ) { - // Given node `x`, get `123` from `x = 123;`. - // Ignore assignments with other operators like `x += 'abc';'`; - return [ref.writeExpr]; - } - return []; - }); - }, - - /** - * @param {Node} node - * @returns {boolean} Whether the node is an Identifier with name `undefined`. - */ - isUndefinedIdentifier(node) { - return node.type === 'Identifier' && node.name === 'undefined'; - }, - - /** - * Check whether a variable's definition is from a function parameter. - * @param {Node} node - the Identifier node for the variable. - * @param {ScopeManager} scopeManager - * @returns {boolean} whether the variable comes from a function parameter - */ - isVariableFromParameter(node, scopeManager) { - const variable = findVariable( - scopeManager.acquire(node) || scopeManager.globalScope, - node, - ); +export function getScope(context) { + // TODO: remove contet.getScope() when dropping eslint < v9 + const sourceCode = context.sourceCode || context.getSourceCode(); + return sourceCode.getScope?.(sourceCode.ast) || context.getScope(); +} + +export function getparserServices(context) { + // TODO: remove context.parserServices when dropping eslint < v9 + return (context.sourceCode || context).parserServices; +} - return variable?.defs[0]?.type === 'Parameter'; - }, - - getSourceCode(context) { - // TODO: remove contet.getSourceCode() when dropping eslint < v9 - return context.sourceCode || context.getSourceCode(); - }, - - getScope(context) { - // TODO: remove contet.getScope() when dropping eslint < v9 - const sourceCode = context.sourceCode || context.getSourceCode(); - return sourceCode.getScope?.(sourceCode.ast) || context.getScope(); - }, - - getparserServices(context) { - // TODO: remove context.parserServices when dropping eslint < v9 - return (context.sourceCode || context).parserServices; - }, - - getFilename(context) { - // TODO: just use context.filename when dropping eslint < v9 - return context.filename || context.getFilename(); - }, -}; +export function getFilename(context) { + // TODO: just use context.filename when dropping eslint < v9 + return context.filename || context.getFilename(); +} diff --git a/package.json b/package.json index 63da8091..2ec7b244 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "An ESLint plugin for linting ESLint plugins", "author": "Teddy Katz", "main": "./lib/index.js", + "type": "module", "exports": { ".": "./lib/index.js", "./configs/*": "./configs/*.js", diff --git a/tests/lib/rules/no-property-in-node.js b/tests/lib/rules/no-property-in-node.js index c7225404..d5614cf7 100644 --- a/tests/lib/rules/no-property-in-node.js +++ b/tests/lib/rules/no-property-in-node.js @@ -1,8 +1,10 @@ import { RuleTester } from '../../utils/eslint-rule-tester.js'; import path from 'path'; +import { fileURLToPath } from 'url'; import rule from '../../../lib/rules/no-property-in-node.js'; import parser from '@typescript-eslint/parser'; +const dirname = path.dirname(fileURLToPath(import.meta.url)); const ruleTester = new RuleTester({ languageOptions: { parser, @@ -10,7 +12,7 @@ const ruleTester = new RuleTester({ projectService: { defaultProject: 'tsconfig.json', }, - tsconfigRootDir: path.join(__dirname, '../fixtures'), + tsconfigRootDir: path.join(dirname, '../fixtures'), }, }, });