eslint-plugin-flawless 0.1.4 → 0.1.5
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.
- package/README.md +10 -9
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +232 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -176,15 +176,16 @@ pnpm eslint-docs
|
|
|
176
176
|
💭
|
|
177
177
|
Requires [type information](https://typescript-eslint.io/linting/typed-linting).
|
|
178
178
|
|
|
179
|
-
| Name
|
|
180
|
-
|
|
|
181
|
-
| [jsx-shorthand-boolean](src/rules/jsx-shorthand-boolean/documentation.md)
|
|
182
|
-
| [jsx-shorthand-fragment](src/rules/jsx-shorthand-fragment/documentation.md)
|
|
183
|
-
| [naming-convention](src/rules/naming-convention/documentation.md)
|
|
184
|
-
| [no-unnecessary-use-callback](src/rules/no-unnecessary-use-callback/documentation.md)
|
|
185
|
-
| [no-unnecessary-use-memo](src/rules/no-unnecessary-use-memo/documentation.md)
|
|
186
|
-
| [
|
|
187
|
-
| [
|
|
179
|
+
| Name | Description | 🔧 | 💭 |
|
|
180
|
+
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------------------- | :-- | :-- |
|
|
181
|
+
| [jsx-shorthand-boolean](src/rules/jsx-shorthand-boolean/documentation.md) | Disallow shorthand boolean JSX attributes | 🔧 | |
|
|
182
|
+
| [jsx-shorthand-fragment](src/rules/jsx-shorthand-fragment/documentation.md) | Disallow the shorthand fragment syntax in favour of a named fragment | 🔧 | |
|
|
183
|
+
| [naming-convention](src/rules/naming-convention/documentation.md) | Enforce naming conventions for everything across a codebase | | 💭 |
|
|
184
|
+
| [no-unnecessary-use-callback](src/rules/no-unnecessary-use-callback/documentation.md) | Disallow unnecessary usage of 'useCallback' | | |
|
|
185
|
+
| [no-unnecessary-use-memo](src/rules/no-unnecessary-use-memo/documentation.md) | Disallow unnecessary usage of 'useMemo' | | |
|
|
186
|
+
| [prefer-destructuring-assignment](src/rules/prefer-destructuring-assignment/documentation.md) | Enforce destructuring assignment for component props | 🔧 | |
|
|
187
|
+
| [toml-sort-keys](src/rules/toml-sort-keys/documentation.md) | Enforce a configured sort order for TOML keys and tables | 🔧 | |
|
|
188
|
+
| [yaml-block-key-blank-lines](src/rules/yaml-block-key-blank-lines/documentation.md) | Enforce blank lines around top-level YAML block collection keys | 🔧 | |
|
|
188
189
|
|
|
189
190
|
<!-- end auto-generated rules list -->
|
|
190
191
|
|
package/dist/index.d.mts
CHANGED
|
@@ -166,6 +166,9 @@ declare const plugin: {
|
|
|
166
166
|
"no-unnecessary-use-memo": TSESLint.RuleModule<MessageIds, [], PluginDocumentation, TSESLint.RuleListener> & {
|
|
167
167
|
name: string;
|
|
168
168
|
};
|
|
169
|
+
"prefer-destructuring-assignment": TSESLint.RuleModule<"default", [], PluginDocumentation, TSESLint.RuleListener> & {
|
|
170
|
+
name: string;
|
|
171
|
+
};
|
|
169
172
|
"toml-sort-keys": TSESLint.RuleModule<"unsorted", Options, PluginDocumentation, TSESLint.RuleListener> & {
|
|
170
173
|
name: string;
|
|
171
174
|
};
|
|
@@ -201,6 +204,9 @@ declare const _default: {
|
|
|
201
204
|
"no-unnecessary-use-memo": import("${configDir}").RuleModule<MessageIds, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
202
205
|
name: string;
|
|
203
206
|
};
|
|
207
|
+
"prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
208
|
+
name: string;
|
|
209
|
+
};
|
|
204
210
|
"toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
205
211
|
name: string;
|
|
206
212
|
};
|
|
@@ -233,6 +239,9 @@ declare const _default: {
|
|
|
233
239
|
"no-unnecessary-use-memo": import("${configDir}").RuleModule<MessageIds, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
234
240
|
name: string;
|
|
235
241
|
};
|
|
242
|
+
"prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
243
|
+
name: string;
|
|
244
|
+
};
|
|
236
245
|
"toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
237
246
|
name: string;
|
|
238
247
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { findVariable } from "@typescript-eslint/utils/ast-utils";
|
|
|
8
8
|
import { getStaticTOMLValue } from "toml-eslint-parser";
|
|
9
9
|
//#region package.json
|
|
10
10
|
var name = "eslint-plugin-flawless";
|
|
11
|
-
var version = "0.1.
|
|
11
|
+
var version = "0.1.5";
|
|
12
12
|
var repository = {
|
|
13
13
|
"url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
|
|
14
14
|
"type": "git"
|
|
@@ -35,24 +35,24 @@ function createEslintRule(rule) {
|
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region src/rules/jsx-shorthand-boolean/rule.ts
|
|
38
|
-
const RULE_NAME$
|
|
39
|
-
const MESSAGE_ID$
|
|
40
|
-
const messages$
|
|
41
|
-
function create$
|
|
38
|
+
const RULE_NAME$7 = "jsx-shorthand-boolean";
|
|
39
|
+
const MESSAGE_ID$3 = "setAttributeValue";
|
|
40
|
+
const messages$7 = { [MESSAGE_ID$3]: "Set an explicit value for boolean attribute '{{name}}'." };
|
|
41
|
+
function create$7(context) {
|
|
42
42
|
const { sourceCode } = context;
|
|
43
43
|
return { JSXAttribute(node) {
|
|
44
44
|
if (node.value !== null) return;
|
|
45
45
|
context.report({
|
|
46
46
|
data: { name: sourceCode.getText(node.name) },
|
|
47
47
|
fix: (fixer) => fixer.insertTextAfter(node.name, "={true}"),
|
|
48
|
-
messageId: MESSAGE_ID$
|
|
48
|
+
messageId: MESSAGE_ID$3,
|
|
49
49
|
node
|
|
50
50
|
});
|
|
51
51
|
} };
|
|
52
52
|
}
|
|
53
53
|
const jsxShorthandBoolean = createEslintRule({
|
|
54
|
-
name: RULE_NAME$
|
|
55
|
-
create: create$
|
|
54
|
+
name: RULE_NAME$7,
|
|
55
|
+
create: create$7,
|
|
56
56
|
defaultOptions: [],
|
|
57
57
|
meta: {
|
|
58
58
|
docs: {
|
|
@@ -62,22 +62,22 @@ const jsxShorthandBoolean = createEslintRule({
|
|
|
62
62
|
},
|
|
63
63
|
fixable: "code",
|
|
64
64
|
hasSuggestions: false,
|
|
65
|
-
messages: messages$
|
|
65
|
+
messages: messages$7,
|
|
66
66
|
schema: [],
|
|
67
67
|
type: "suggestion"
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
//#endregion
|
|
71
71
|
//#region src/rules/jsx-shorthand-fragment/rule.ts
|
|
72
|
-
const RULE_NAME$
|
|
73
|
-
const MESSAGE_ID$
|
|
72
|
+
const RULE_NAME$6 = "jsx-shorthand-fragment";
|
|
73
|
+
const MESSAGE_ID$2 = "useNamedFragment";
|
|
74
74
|
const DEFAULT_FRAGMENT_NAME = "Fragment";
|
|
75
|
-
const messages$
|
|
75
|
+
const messages$6 = { [MESSAGE_ID$2]: "Use the '{{name}}' component instead of fragment shorthand syntax." };
|
|
76
76
|
const schema$1 = [{
|
|
77
77
|
description: "The identifier to use for the named fragment element.",
|
|
78
78
|
type: "string"
|
|
79
79
|
}];
|
|
80
|
-
function create$
|
|
80
|
+
function create$6(context) {
|
|
81
81
|
const name = context.options[0] ?? DEFAULT_FRAGMENT_NAME;
|
|
82
82
|
return { JSXFragment(node) {
|
|
83
83
|
const { closingFragment, openingFragment } = node;
|
|
@@ -86,14 +86,14 @@ function create$5(context) {
|
|
|
86
86
|
fix: (fixer) => {
|
|
87
87
|
return [fixer.replaceText(openingFragment, `<${name}>`), fixer.replaceText(closingFragment, `</${name}>`)];
|
|
88
88
|
},
|
|
89
|
-
messageId: MESSAGE_ID$
|
|
89
|
+
messageId: MESSAGE_ID$2,
|
|
90
90
|
node
|
|
91
91
|
});
|
|
92
92
|
} };
|
|
93
93
|
}
|
|
94
94
|
const jsxShorthandFragment = createEslintRule({
|
|
95
|
-
name: RULE_NAME$
|
|
96
|
-
create: create$
|
|
95
|
+
name: RULE_NAME$6,
|
|
96
|
+
create: create$6,
|
|
97
97
|
defaultOptions: [DEFAULT_FRAGMENT_NAME],
|
|
98
98
|
meta: {
|
|
99
99
|
defaultOptions: [DEFAULT_FRAGMENT_NAME],
|
|
@@ -104,7 +104,7 @@ const jsxShorthandFragment = createEslintRule({
|
|
|
104
104
|
},
|
|
105
105
|
fixable: "code",
|
|
106
106
|
hasSuggestions: false,
|
|
107
|
-
messages: messages$
|
|
107
|
+
messages: messages$6,
|
|
108
108
|
schema: schema$1,
|
|
109
109
|
type: "suggestion"
|
|
110
110
|
}
|
|
@@ -1269,8 +1269,8 @@ const SCHEMA = {
|
|
|
1269
1269
|
};
|
|
1270
1270
|
//#endregion
|
|
1271
1271
|
//#region src/rules/naming-convention/rule.ts
|
|
1272
|
-
const RULE_NAME$
|
|
1273
|
-
const messages$
|
|
1272
|
+
const RULE_NAME$5 = "naming-convention";
|
|
1273
|
+
const messages$5 = {
|
|
1274
1274
|
doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}",
|
|
1275
1275
|
doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}",
|
|
1276
1276
|
missingAffix: "{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}",
|
|
@@ -1300,7 +1300,7 @@ const camelCaseNamingConfig = [
|
|
|
1300
1300
|
selector: "typeLike"
|
|
1301
1301
|
}
|
|
1302
1302
|
];
|
|
1303
|
-
function create$
|
|
1303
|
+
function create$5(contextWithoutDefaults) {
|
|
1304
1304
|
const context = contextWithoutDefaults.options.length > 0 ? contextWithoutDefaults : Object.setPrototypeOf({ options: camelCaseNamingConfig }, contextWithoutDefaults);
|
|
1305
1305
|
const validators = parseOptions(context);
|
|
1306
1306
|
const compilerOptions = getParserServices(context, true).program?.getCompilerOptions() ?? {};
|
|
@@ -1620,8 +1620,8 @@ function create$4(contextWithoutDefaults) {
|
|
|
1620
1620
|
}));
|
|
1621
1621
|
}
|
|
1622
1622
|
const namingConvention = createEslintRule({
|
|
1623
|
-
name: RULE_NAME$
|
|
1624
|
-
create: create$
|
|
1623
|
+
name: RULE_NAME$5,
|
|
1624
|
+
create: create$5,
|
|
1625
1625
|
defaultOptions: camelCaseNamingConfig,
|
|
1626
1626
|
meta: {
|
|
1627
1627
|
docs: {
|
|
@@ -1631,7 +1631,7 @@ const namingConvention = createEslintRule({
|
|
|
1631
1631
|
},
|
|
1632
1632
|
fixable: void 0,
|
|
1633
1633
|
hasSuggestions: false,
|
|
1634
|
-
messages: messages$
|
|
1634
|
+
messages: messages$5,
|
|
1635
1635
|
schema: SCHEMA,
|
|
1636
1636
|
type: "suggestion"
|
|
1637
1637
|
}
|
|
@@ -1899,15 +1899,15 @@ function resolveFactory(sourceCode, node) {
|
|
|
1899
1899
|
}
|
|
1900
1900
|
//#endregion
|
|
1901
1901
|
//#region src/rules/no-unnecessary-use-callback/rule.ts
|
|
1902
|
-
const RULE_NAME$
|
|
1902
|
+
const RULE_NAME$4 = "no-unnecessary-use-callback";
|
|
1903
1903
|
const MESSAGE_ID_DEFAULT$1 = "default";
|
|
1904
1904
|
const MESSAGE_ID_INSIDE_USE_EFFECT$1 = "noUnnecessaryUseCallbackInsideUseEffect";
|
|
1905
|
-
const messages$
|
|
1905
|
+
const messages$4 = {
|
|
1906
1906
|
[MESSAGE_ID_DEFAULT$1]: "An 'useCallback' with empty deps and no references to the component scope may be unnecessary.",
|
|
1907
1907
|
[MESSAGE_ID_INSIDE_USE_EFFECT$1]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
|
|
1908
1908
|
};
|
|
1909
1909
|
const noUnnecessaryUseCallback = createEslintRule({
|
|
1910
|
-
name: RULE_NAME$
|
|
1910
|
+
name: RULE_NAME$4,
|
|
1911
1911
|
create: createUnnecessaryHookRule({
|
|
1912
1912
|
hook: "useCallback",
|
|
1913
1913
|
messageIds: {
|
|
@@ -1923,22 +1923,22 @@ const noUnnecessaryUseCallback = createEslintRule({
|
|
|
1923
1923
|
requiresTypeChecking: false
|
|
1924
1924
|
},
|
|
1925
1925
|
hasSuggestions: false,
|
|
1926
|
-
messages: messages$
|
|
1926
|
+
messages: messages$4,
|
|
1927
1927
|
schema: [],
|
|
1928
1928
|
type: "suggestion"
|
|
1929
1929
|
}
|
|
1930
1930
|
});
|
|
1931
1931
|
//#endregion
|
|
1932
1932
|
//#region src/rules/no-unnecessary-use-memo/rule.ts
|
|
1933
|
-
const RULE_NAME$
|
|
1933
|
+
const RULE_NAME$3 = "no-unnecessary-use-memo";
|
|
1934
1934
|
const MESSAGE_ID_DEFAULT = "default";
|
|
1935
1935
|
const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
|
|
1936
|
-
const messages$
|
|
1936
|
+
const messages$3 = {
|
|
1937
1937
|
[MESSAGE_ID_DEFAULT]: "An 'useMemo' with empty deps and no references to the component scope may be unnecessary.",
|
|
1938
1938
|
[MESSAGE_ID_INSIDE_USE_EFFECT]: "'{{name}}' is only used inside 1 useEffect, which may be unnecessary. You can move the computation into useEffect directly and merge the dependency arrays."
|
|
1939
1939
|
};
|
|
1940
1940
|
const noUnnecessaryUseMemo = createEslintRule({
|
|
1941
|
-
name: RULE_NAME$
|
|
1941
|
+
name: RULE_NAME$3,
|
|
1942
1942
|
create: createUnnecessaryHookRule({
|
|
1943
1943
|
hook: "useMemo",
|
|
1944
1944
|
messageIds: {
|
|
@@ -1954,12 +1954,212 @@ const noUnnecessaryUseMemo = createEslintRule({
|
|
|
1954
1954
|
requiresTypeChecking: false
|
|
1955
1955
|
},
|
|
1956
1956
|
hasSuggestions: false,
|
|
1957
|
-
messages: messages$
|
|
1957
|
+
messages: messages$3,
|
|
1958
1958
|
schema: [],
|
|
1959
1959
|
type: "suggestion"
|
|
1960
1960
|
}
|
|
1961
1961
|
});
|
|
1962
1962
|
//#endregion
|
|
1963
|
+
//#region src/rules/prefer-destructuring-assignment/rule.ts
|
|
1964
|
+
const RULE_NAME$2 = "prefer-destructuring-assignment";
|
|
1965
|
+
const MESSAGE_ID$1 = "default";
|
|
1966
|
+
const messages$2 = { [MESSAGE_ID$1]: "Use destructuring assignment for component props." };
|
|
1967
|
+
/**
|
|
1968
|
+
* Identifiers that cannot be used as a binding name in strict-mode module code.
|
|
1969
|
+
* A property may be accessed with such a name (`props.default`), but a shorthand
|
|
1970
|
+
* destructuring pattern built from it (`{ default }`) would be a syntax error,
|
|
1971
|
+
* so the autofix bails when one is encountered.
|
|
1972
|
+
*/
|
|
1973
|
+
const RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
1974
|
+
"arguments",
|
|
1975
|
+
"await",
|
|
1976
|
+
"break",
|
|
1977
|
+
"case",
|
|
1978
|
+
"catch",
|
|
1979
|
+
"class",
|
|
1980
|
+
"const",
|
|
1981
|
+
"continue",
|
|
1982
|
+
"debugger",
|
|
1983
|
+
"default",
|
|
1984
|
+
"delete",
|
|
1985
|
+
"do",
|
|
1986
|
+
"else",
|
|
1987
|
+
"enum",
|
|
1988
|
+
"eval",
|
|
1989
|
+
"export",
|
|
1990
|
+
"extends",
|
|
1991
|
+
"false",
|
|
1992
|
+
"finally",
|
|
1993
|
+
"for",
|
|
1994
|
+
"function",
|
|
1995
|
+
"if",
|
|
1996
|
+
"implements",
|
|
1997
|
+
"import",
|
|
1998
|
+
"in",
|
|
1999
|
+
"instanceof",
|
|
2000
|
+
"interface",
|
|
2001
|
+
"let",
|
|
2002
|
+
"new",
|
|
2003
|
+
"null",
|
|
2004
|
+
"package",
|
|
2005
|
+
"private",
|
|
2006
|
+
"protected",
|
|
2007
|
+
"public",
|
|
2008
|
+
"return",
|
|
2009
|
+
"static",
|
|
2010
|
+
"super",
|
|
2011
|
+
"switch",
|
|
2012
|
+
"this",
|
|
2013
|
+
"throw",
|
|
2014
|
+
"true",
|
|
2015
|
+
"try",
|
|
2016
|
+
"typeof",
|
|
2017
|
+
"var",
|
|
2018
|
+
"void",
|
|
2019
|
+
"while",
|
|
2020
|
+
"with",
|
|
2021
|
+
"yield"
|
|
2022
|
+
]);
|
|
2023
|
+
/**
|
|
2024
|
+
* Collects the unique accessed property names, in first-seen order, when every
|
|
2025
|
+
* member reference is a simple non-computed `props.<identifier>` access.
|
|
2026
|
+
*
|
|
2027
|
+
* @param memberReferences - The member accesses to inspect.
|
|
2028
|
+
* @returns The property names, or `null` when any access is not destructurable
|
|
2029
|
+
* (computed access such as `props[key]`, `props` used as a computed key, or a
|
|
2030
|
+
* property whose name is a reserved word that cannot be a binding).
|
|
2031
|
+
*/
|
|
2032
|
+
function collectPropertyNames(memberReferences) {
|
|
2033
|
+
const names = [];
|
|
2034
|
+
for (const { member, reference } of memberReferences) {
|
|
2035
|
+
if (member.object !== reference.identifier || member.computed || member.property.type !== AST_NODE_TYPES.Identifier || RESERVED_WORDS.has(member.property.name)) return null;
|
|
2036
|
+
if (!names.includes(member.property.name)) names.push(member.property.name);
|
|
2037
|
+
}
|
|
2038
|
+
return names;
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Determines whether rewriting `props.foo` to `foo` would resolve to a different
|
|
2042
|
+
* binding than the new destructured parameter at any access site.
|
|
2043
|
+
*
|
|
2044
|
+
* A name is unsafe when it is already bound in the component scope (other than by
|
|
2045
|
+
* the props parameter itself) or in any scope nested between an access and the
|
|
2046
|
+
* component, since the rewritten `foo` reference would then resolve to that
|
|
2047
|
+
* binding instead of the destructured prop.
|
|
2048
|
+
*
|
|
2049
|
+
* @param sourceCode - Provides scope lookup for each reference.
|
|
2050
|
+
* @param componentScope - The component function's scope.
|
|
2051
|
+
* @param propsVariable - The props parameter variable (excluded from the check).
|
|
2052
|
+
* @param memberReferences - The member accesses that would be rewritten.
|
|
2053
|
+
* @returns `true` if any rewritten name would collide with an existing binding.
|
|
2054
|
+
*/
|
|
2055
|
+
function wouldShadowExistingBinding(sourceCode, componentScope, propsVariable, memberReferences) {
|
|
2056
|
+
for (const { member, reference } of memberReferences) {
|
|
2057
|
+
const { name } = member.property;
|
|
2058
|
+
let scope = sourceCode.getScope(reference.identifier);
|
|
2059
|
+
while (scope !== null) {
|
|
2060
|
+
if (scope.variables.some((variable) => variable.name === name && variable !== propsVariable)) return true;
|
|
2061
|
+
if (scope === componentScope) break;
|
|
2062
|
+
scope = scope.upper;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return false;
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Reports every `props.<member>` access on a component's props parameter,
|
|
2069
|
+
* attaching an autofix when the parameter can be safely destructured.
|
|
2070
|
+
*
|
|
2071
|
+
* @param context - The rule context.
|
|
2072
|
+
* @param scope - The component function's scope.
|
|
2073
|
+
* @param propsParameter - The props parameter identifier.
|
|
2074
|
+
* @param propertyVariable - The resolved variable for the props parameter.
|
|
2075
|
+
*/
|
|
2076
|
+
function reportComponent(context, scope, propsParameter, propertyVariable) {
|
|
2077
|
+
const memberReferences = [];
|
|
2078
|
+
let hasNonMemberReference = false;
|
|
2079
|
+
for (const reference of propertyVariable.references) {
|
|
2080
|
+
const { parent } = reference.identifier;
|
|
2081
|
+
if (parent.type === AST_NODE_TYPES.MemberExpression) memberReferences.push({
|
|
2082
|
+
member: parent,
|
|
2083
|
+
reference
|
|
2084
|
+
});
|
|
2085
|
+
else hasNonMemberReference = true;
|
|
2086
|
+
}
|
|
2087
|
+
if (memberReferences.length === 0) return;
|
|
2088
|
+
memberReferences.sort((left, right) => left.member.range[0] - right.member.range[0]);
|
|
2089
|
+
const propertyNames = collectPropertyNames(memberReferences);
|
|
2090
|
+
if (!(!hasNonMemberReference && propertyNames !== null && !wouldShadowExistingBinding(context.sourceCode, scope, propertyVariable, memberReferences))) {
|
|
2091
|
+
for (const { member } of memberReferences) context.report({
|
|
2092
|
+
messageId: MESSAGE_ID$1,
|
|
2093
|
+
node: member
|
|
2094
|
+
});
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
const needsParentheses = propsParameter.parent.type === AST_NODE_TYPES.ArrowFunctionExpression && context.sourceCode.getTokenAfter(propsParameter)?.value === "=>";
|
|
2098
|
+
const destructured = `{ ${propertyNames.join(", ")} }`;
|
|
2099
|
+
const pattern = needsParentheses ? `(${destructured})` : destructured;
|
|
2100
|
+
const nameEnd = propsParameter.typeAnnotation?.range[0] ?? propsParameter.range[1];
|
|
2101
|
+
for (const [index, { member }] of memberReferences.entries()) {
|
|
2102
|
+
const propertyName = member.property.name;
|
|
2103
|
+
context.report({
|
|
2104
|
+
fix(fixer) {
|
|
2105
|
+
const replaceAccess = fixer.replaceText(member, propertyName);
|
|
2106
|
+
if (index === 0) return [fixer.replaceTextRange([propsParameter.range[0], nameEnd], pattern), replaceAccess];
|
|
2107
|
+
return replaceAccess;
|
|
2108
|
+
},
|
|
2109
|
+
messageId: MESSAGE_ID$1,
|
|
2110
|
+
node: member
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Faithfully ports `react-x/prefer-destructuring-assignment`, removed from
|
|
2116
|
+
* `eslint-plugin-react-x` in v5.0.0 (deprecated due to low usage). Component
|
|
2117
|
+
* detection is delegated to `@eslint-react/core` so it matches the upstream
|
|
2118
|
+
* semantics.
|
|
2119
|
+
*
|
|
2120
|
+
* Unlike upstream, this port adds an autofix that rewrites the props parameter
|
|
2121
|
+
* into a destructuring pattern (`(props) => props.id` becomes `({ id }) => id`).
|
|
2122
|
+
* The fix is only offered when it is unambiguously safe; see
|
|
2123
|
+
* {@link collectPropertyNames} and {@link wouldShadowExistingBinding}.
|
|
2124
|
+
*
|
|
2125
|
+
* @param context - The rule context.
|
|
2126
|
+
* @returns The rule listener.
|
|
2127
|
+
*/
|
|
2128
|
+
function create$2(context) {
|
|
2129
|
+
const { api, visitor } = core.getFunctionComponentCollector(context);
|
|
2130
|
+
return {
|
|
2131
|
+
...visitor,
|
|
2132
|
+
"Program:exit": function(program) {
|
|
2133
|
+
for (const component of api.getAllComponents(program)) {
|
|
2134
|
+
if (component.name === null || component.isExportDefaultDeclaration) continue;
|
|
2135
|
+
const [propsParameter] = component.node.params;
|
|
2136
|
+
if (propsParameter?.type !== AST_NODE_TYPES.Identifier) continue;
|
|
2137
|
+
const scope = context.sourceCode.getScope(component.node);
|
|
2138
|
+
const propertyVariable = scope.variables.find((variable) => variable.name === propsParameter.name);
|
|
2139
|
+
if (propertyVariable === void 0) continue;
|
|
2140
|
+
reportComponent(context, scope, propsParameter, propertyVariable);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
const preferDestructuringAssignment = createEslintRule({
|
|
2146
|
+
name: RULE_NAME$2,
|
|
2147
|
+
create: create$2,
|
|
2148
|
+
defaultOptions: [],
|
|
2149
|
+
meta: {
|
|
2150
|
+
docs: {
|
|
2151
|
+
description: "Enforce destructuring assignment for component props",
|
|
2152
|
+
recommended: false,
|
|
2153
|
+
requiresTypeChecking: false
|
|
2154
|
+
},
|
|
2155
|
+
fixable: "code",
|
|
2156
|
+
hasSuggestions: false,
|
|
2157
|
+
messages: messages$2,
|
|
2158
|
+
schema: [],
|
|
2159
|
+
type: "problem"
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
2162
|
+
//#endregion
|
|
1963
2163
|
//#region src/rules/toml-sort-keys/rule.ts
|
|
1964
2164
|
const RULE_NAME$1 = "toml-sort-keys";
|
|
1965
2165
|
const MESSAGE_ID = "unsorted";
|
|
@@ -2260,6 +2460,7 @@ const plugin = {
|
|
|
2260
2460
|
"naming-convention": namingConvention,
|
|
2261
2461
|
"no-unnecessary-use-callback": noUnnecessaryUseCallback,
|
|
2262
2462
|
"no-unnecessary-use-memo": noUnnecessaryUseMemo,
|
|
2463
|
+
"prefer-destructuring-assignment": preferDestructuringAssignment,
|
|
2263
2464
|
"toml-sort-keys": tomlSortKeys,
|
|
2264
2465
|
"yaml-block-key-blank-lines": yamlBlockKeyBlankLines
|
|
2265
2466
|
}
|