eslint-plugin-flawless 0.1.3 → 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 -7
- package/dist/index.d.mts +41 -4
- package/dist/index.mjs +527 -23
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -176,13 +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
|
-
| [
|
|
185
|
-
| [
|
|
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 | 🔧 | |
|
|
186
189
|
|
|
187
190
|
<!-- end auto-generated rules list -->
|
|
188
191
|
|
package/dist/index.d.mts
CHANGED
|
@@ -118,9 +118,19 @@ interface NamingSelector {
|
|
|
118
118
|
}
|
|
119
119
|
//#endregion
|
|
120
120
|
//#region src/rules/naming-convention/rule.d.ts
|
|
121
|
-
type MessageIds = "doesNotMatchFormat" | "doesNotMatchFormatTrimmed" | "missingAffix" | "missingUnderscore" | "satisfyCustom" | "unexpectedUnderscore";
|
|
121
|
+
type MessageIds$2 = "doesNotMatchFormat" | "doesNotMatchFormatTrimmed" | "missingAffix" | "missingUnderscore" | "satisfyCustom" | "unexpectedUnderscore";
|
|
122
122
|
type Options$1 = Array<NamingSelector>;
|
|
123
123
|
//#endregion
|
|
124
|
+
//#region src/rules/no-unnecessary-use-callback/rule.d.ts
|
|
125
|
+
declare const MESSAGE_ID_DEFAULT$1 = "default";
|
|
126
|
+
declare const MESSAGE_ID_INSIDE_USE_EFFECT$1 = "noUnnecessaryUseCallbackInsideUseEffect";
|
|
127
|
+
type MessageIds$1 = typeof MESSAGE_ID_DEFAULT$1 | typeof MESSAGE_ID_INSIDE_USE_EFFECT$1;
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/rules/no-unnecessary-use-memo/rule.d.ts
|
|
130
|
+
declare const MESSAGE_ID_DEFAULT = "default";
|
|
131
|
+
declare const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
|
|
132
|
+
type MessageIds = typeof MESSAGE_ID_DEFAULT | typeof MESSAGE_ID_INSIDE_USE_EFFECT;
|
|
133
|
+
//#endregion
|
|
124
134
|
//#region src/rules/toml-sort-keys/rule.d.ts
|
|
125
135
|
type Options = Array<SortSpec>;
|
|
126
136
|
interface SortOrderObject {
|
|
@@ -147,7 +157,16 @@ declare const plugin: {
|
|
|
147
157
|
"jsx-shorthand-fragment": TSESLint.RuleModule<"useNamedFragment", Options$2, PluginDocumentation, TSESLint.RuleListener> & {
|
|
148
158
|
name: string;
|
|
149
159
|
};
|
|
150
|
-
"naming-convention": TSESLint.RuleModule<MessageIds, Options$1, PluginDocumentation, TSESLint.RuleListener> & {
|
|
160
|
+
"naming-convention": TSESLint.RuleModule<MessageIds$2, Options$1, PluginDocumentation, TSESLint.RuleListener> & {
|
|
161
|
+
name: string;
|
|
162
|
+
};
|
|
163
|
+
"no-unnecessary-use-callback": TSESLint.RuleModule<MessageIds$1, [], PluginDocumentation, TSESLint.RuleListener> & {
|
|
164
|
+
name: string;
|
|
165
|
+
};
|
|
166
|
+
"no-unnecessary-use-memo": TSESLint.RuleModule<MessageIds, [], PluginDocumentation, TSESLint.RuleListener> & {
|
|
167
|
+
name: string;
|
|
168
|
+
};
|
|
169
|
+
"prefer-destructuring-assignment": TSESLint.RuleModule<"default", [], PluginDocumentation, TSESLint.RuleListener> & {
|
|
151
170
|
name: string;
|
|
152
171
|
};
|
|
153
172
|
"toml-sort-keys": TSESLint.RuleModule<"unsorted", Options, PluginDocumentation, TSESLint.RuleListener> & {
|
|
@@ -176,7 +195,16 @@ declare const _default: {
|
|
|
176
195
|
"jsx-shorthand-fragment": import("${configDir}").RuleModule<"useNamedFragment", Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
177
196
|
name: string;
|
|
178
197
|
};
|
|
179
|
-
"naming-convention": import("${configDir}").RuleModule<MessageIds, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
198
|
+
"naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
199
|
+
name: string;
|
|
200
|
+
};
|
|
201
|
+
"no-unnecessary-use-callback": import("${configDir}").RuleModule<MessageIds$1, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
202
|
+
name: string;
|
|
203
|
+
};
|
|
204
|
+
"no-unnecessary-use-memo": import("${configDir}").RuleModule<MessageIds, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
205
|
+
name: string;
|
|
206
|
+
};
|
|
207
|
+
"prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
180
208
|
name: string;
|
|
181
209
|
};
|
|
182
210
|
"toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
@@ -202,7 +230,16 @@ declare const _default: {
|
|
|
202
230
|
"jsx-shorthand-fragment": import("${configDir}").RuleModule<"useNamedFragment", Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
203
231
|
name: string;
|
|
204
232
|
};
|
|
205
|
-
"naming-convention": import("${configDir}").RuleModule<MessageIds, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
233
|
+
"naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
234
|
+
name: string;
|
|
235
|
+
};
|
|
236
|
+
"no-unnecessary-use-callback": import("${configDir}").RuleModule<MessageIds$1, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
237
|
+
name: string;
|
|
238
|
+
};
|
|
239
|
+
"no-unnecessary-use-memo": import("${configDir}").RuleModule<MessageIds, [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
240
|
+
name: string;
|
|
241
|
+
};
|
|
242
|
+
"prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
|
|
206
243
|
name: string;
|
|
207
244
|
};
|
|
208
245
|
"toml-sort-keys": import("${configDir}").RuleModule<"unsorted", Options, PluginDocumentation, import("${configDir}").RuleListener> & {
|
package/dist/index.mjs
CHANGED
|
@@ -3,10 +3,12 @@ import { DefinitionType, ImplicitLibVariable, PatternVisitor, ScopeType, Visitor
|
|
|
3
3
|
import { requiresQuoting } from "@typescript-eslint/type-utils";
|
|
4
4
|
import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
|
|
5
5
|
import assert from "node:assert";
|
|
6
|
+
import * as core from "@eslint-react/core";
|
|
7
|
+
import { findVariable } from "@typescript-eslint/utils/ast-utils";
|
|
6
8
|
import { getStaticTOMLValue } from "toml-eslint-parser";
|
|
7
9
|
//#region package.json
|
|
8
10
|
var name = "eslint-plugin-flawless";
|
|
9
|
-
var version = "0.1.
|
|
11
|
+
var version = "0.1.5";
|
|
10
12
|
var repository = {
|
|
11
13
|
"url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
|
|
12
14
|
"type": "git"
|
|
@@ -33,24 +35,24 @@ function createEslintRule(rule) {
|
|
|
33
35
|
}
|
|
34
36
|
//#endregion
|
|
35
37
|
//#region src/rules/jsx-shorthand-boolean/rule.ts
|
|
36
|
-
const RULE_NAME$
|
|
37
|
-
const MESSAGE_ID$
|
|
38
|
-
const messages$
|
|
39
|
-
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) {
|
|
40
42
|
const { sourceCode } = context;
|
|
41
43
|
return { JSXAttribute(node) {
|
|
42
44
|
if (node.value !== null) return;
|
|
43
45
|
context.report({
|
|
44
46
|
data: { name: sourceCode.getText(node.name) },
|
|
45
47
|
fix: (fixer) => fixer.insertTextAfter(node.name, "={true}"),
|
|
46
|
-
messageId: MESSAGE_ID$
|
|
48
|
+
messageId: MESSAGE_ID$3,
|
|
47
49
|
node
|
|
48
50
|
});
|
|
49
51
|
} };
|
|
50
52
|
}
|
|
51
53
|
const jsxShorthandBoolean = createEslintRule({
|
|
52
|
-
name: RULE_NAME$
|
|
53
|
-
create: create$
|
|
54
|
+
name: RULE_NAME$7,
|
|
55
|
+
create: create$7,
|
|
54
56
|
defaultOptions: [],
|
|
55
57
|
meta: {
|
|
56
58
|
docs: {
|
|
@@ -60,22 +62,22 @@ const jsxShorthandBoolean = createEslintRule({
|
|
|
60
62
|
},
|
|
61
63
|
fixable: "code",
|
|
62
64
|
hasSuggestions: false,
|
|
63
|
-
messages: messages$
|
|
65
|
+
messages: messages$7,
|
|
64
66
|
schema: [],
|
|
65
67
|
type: "suggestion"
|
|
66
68
|
}
|
|
67
69
|
});
|
|
68
70
|
//#endregion
|
|
69
71
|
//#region src/rules/jsx-shorthand-fragment/rule.ts
|
|
70
|
-
const RULE_NAME$
|
|
71
|
-
const MESSAGE_ID$
|
|
72
|
+
const RULE_NAME$6 = "jsx-shorthand-fragment";
|
|
73
|
+
const MESSAGE_ID$2 = "useNamedFragment";
|
|
72
74
|
const DEFAULT_FRAGMENT_NAME = "Fragment";
|
|
73
|
-
const messages$
|
|
75
|
+
const messages$6 = { [MESSAGE_ID$2]: "Use the '{{name}}' component instead of fragment shorthand syntax." };
|
|
74
76
|
const schema$1 = [{
|
|
75
77
|
description: "The identifier to use for the named fragment element.",
|
|
76
78
|
type: "string"
|
|
77
79
|
}];
|
|
78
|
-
function create$
|
|
80
|
+
function create$6(context) {
|
|
79
81
|
const name = context.options[0] ?? DEFAULT_FRAGMENT_NAME;
|
|
80
82
|
return { JSXFragment(node) {
|
|
81
83
|
const { closingFragment, openingFragment } = node;
|
|
@@ -84,14 +86,14 @@ function create$3(context) {
|
|
|
84
86
|
fix: (fixer) => {
|
|
85
87
|
return [fixer.replaceText(openingFragment, `<${name}>`), fixer.replaceText(closingFragment, `</${name}>`)];
|
|
86
88
|
},
|
|
87
|
-
messageId: MESSAGE_ID$
|
|
89
|
+
messageId: MESSAGE_ID$2,
|
|
88
90
|
node
|
|
89
91
|
});
|
|
90
92
|
} };
|
|
91
93
|
}
|
|
92
94
|
const jsxShorthandFragment = createEslintRule({
|
|
93
|
-
name: RULE_NAME$
|
|
94
|
-
create: create$
|
|
95
|
+
name: RULE_NAME$6,
|
|
96
|
+
create: create$6,
|
|
95
97
|
defaultOptions: [DEFAULT_FRAGMENT_NAME],
|
|
96
98
|
meta: {
|
|
97
99
|
defaultOptions: [DEFAULT_FRAGMENT_NAME],
|
|
@@ -102,7 +104,7 @@ const jsxShorthandFragment = createEslintRule({
|
|
|
102
104
|
},
|
|
103
105
|
fixable: "code",
|
|
104
106
|
hasSuggestions: false,
|
|
105
|
-
messages: messages$
|
|
107
|
+
messages: messages$6,
|
|
106
108
|
schema: schema$1,
|
|
107
109
|
type: "suggestion"
|
|
108
110
|
}
|
|
@@ -1267,8 +1269,8 @@ const SCHEMA = {
|
|
|
1267
1269
|
};
|
|
1268
1270
|
//#endregion
|
|
1269
1271
|
//#region src/rules/naming-convention/rule.ts
|
|
1270
|
-
const RULE_NAME$
|
|
1271
|
-
const messages$
|
|
1272
|
+
const RULE_NAME$5 = "naming-convention";
|
|
1273
|
+
const messages$5 = {
|
|
1272
1274
|
doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}",
|
|
1273
1275
|
doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}",
|
|
1274
1276
|
missingAffix: "{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}",
|
|
@@ -1298,7 +1300,7 @@ const camelCaseNamingConfig = [
|
|
|
1298
1300
|
selector: "typeLike"
|
|
1299
1301
|
}
|
|
1300
1302
|
];
|
|
1301
|
-
function create$
|
|
1303
|
+
function create$5(contextWithoutDefaults) {
|
|
1302
1304
|
const context = contextWithoutDefaults.options.length > 0 ? contextWithoutDefaults : Object.setPrototypeOf({ options: camelCaseNamingConfig }, contextWithoutDefaults);
|
|
1303
1305
|
const validators = parseOptions(context);
|
|
1304
1306
|
const compilerOptions = getParserServices(context, true).program?.getCompilerOptions() ?? {};
|
|
@@ -1618,8 +1620,8 @@ function create$2(contextWithoutDefaults) {
|
|
|
1618
1620
|
}));
|
|
1619
1621
|
}
|
|
1620
1622
|
const namingConvention = createEslintRule({
|
|
1621
|
-
name: RULE_NAME$
|
|
1622
|
-
create: create$
|
|
1623
|
+
name: RULE_NAME$5,
|
|
1624
|
+
create: create$5,
|
|
1623
1625
|
defaultOptions: camelCaseNamingConfig,
|
|
1624
1626
|
meta: {
|
|
1625
1627
|
docs: {
|
|
@@ -1629,7 +1631,7 @@ const namingConvention = createEslintRule({
|
|
|
1629
1631
|
},
|
|
1630
1632
|
fixable: void 0,
|
|
1631
1633
|
hasSuggestions: false,
|
|
1632
|
-
messages: messages$
|
|
1634
|
+
messages: messages$5,
|
|
1633
1635
|
schema: SCHEMA,
|
|
1634
1636
|
type: "suggestion"
|
|
1635
1637
|
}
|
|
@@ -1659,6 +1661,505 @@ function requiresQuoting$1(node, target) {
|
|
|
1659
1661
|
return requiresQuoting(node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`, target);
|
|
1660
1662
|
}
|
|
1661
1663
|
//#endregion
|
|
1664
|
+
//#region src/utils/nested-expressions.ts
|
|
1665
|
+
/**
|
|
1666
|
+
* Determines whether the given AST subtree contains a call or `new`
|
|
1667
|
+
* expression.
|
|
1668
|
+
*
|
|
1669
|
+
* Replaces `@eslint-react/ast`'s `getNestedCallExpressions` /
|
|
1670
|
+
* `getNestedNewExpressions` (not exposed by `@eslint-react/kit`). The `useMemo`
|
|
1671
|
+
* rule uses this to avoid flagging a factory that performs real computation.
|
|
1672
|
+
* The walk descends into every child node (skipping the `parent` back-link to
|
|
1673
|
+
* avoid cycles), so calls nested in awaits, tagged templates, computed member
|
|
1674
|
+
* expressions, call targets and the like are all detected.
|
|
1675
|
+
*
|
|
1676
|
+
* @param root - The subtree root to inspect (typically a function body).
|
|
1677
|
+
* @returns `true` if a `CallExpression` or `NewExpression` is present.
|
|
1678
|
+
*/
|
|
1679
|
+
function hasNestedCallOrNew(root) {
|
|
1680
|
+
const stack = [root];
|
|
1681
|
+
while (stack.length > 0) {
|
|
1682
|
+
const current = stack.pop();
|
|
1683
|
+
if (current === void 0) break;
|
|
1684
|
+
if (current.type === AST_NODE_TYPES.CallExpression || current.type === AST_NODE_TYPES.NewExpression) return true;
|
|
1685
|
+
pushChildNodes(current, stack);
|
|
1686
|
+
}
|
|
1687
|
+
return false;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Type guard for an AST node value encountered while walking arbitrary
|
|
1691
|
+
* properties of a parent node.
|
|
1692
|
+
*
|
|
1693
|
+
* @param value - The candidate value.
|
|
1694
|
+
* @returns `true` if the value looks like a `TSESTree.Node`.
|
|
1695
|
+
*/
|
|
1696
|
+
function isNode(value) {
|
|
1697
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Pushes every child node of `node` onto the traversal stack, skipping the
|
|
1701
|
+
* `parent` back-link.
|
|
1702
|
+
*
|
|
1703
|
+
* @param node - The node whose children to enqueue.
|
|
1704
|
+
* @param stack - The traversal stack to push onto.
|
|
1705
|
+
*/
|
|
1706
|
+
function pushChildNodes(node, stack) {
|
|
1707
|
+
for (const key of Object.keys(node)) {
|
|
1708
|
+
if (key === "parent") continue;
|
|
1709
|
+
const value = node[key];
|
|
1710
|
+
if (Array.isArray(value)) {
|
|
1711
|
+
for (const item of value) if (isNode(item)) stack.push(item);
|
|
1712
|
+
} else if (isNode(value)) stack.push(value);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
//#endregion
|
|
1716
|
+
//#region src/utils/resolve.ts
|
|
1717
|
+
/**
|
|
1718
|
+
* Resolves an identifier to the AST node that represents its value.
|
|
1719
|
+
*
|
|
1720
|
+
* This is a focused re-implementation of `@eslint-react/var`'s `resolve`
|
|
1721
|
+
* covering only the cases the unnecessary-hook rules need: variable
|
|
1722
|
+
* initializers and function/class declarations. Every other definition kind
|
|
1723
|
+
* (imports, parameters, catch bindings, ...) resolves to `null`.
|
|
1724
|
+
*
|
|
1725
|
+
* @param sourceCode - Provides the scope used to look up the binding.
|
|
1726
|
+
* @param node - The identifier to resolve.
|
|
1727
|
+
* @returns The resolved value node, or `null` when it cannot be determined.
|
|
1728
|
+
*/
|
|
1729
|
+
function resolve(sourceCode, node) {
|
|
1730
|
+
const variable = findVariable(sourceCode.getScope(node), node);
|
|
1731
|
+
if (variable === null) return null;
|
|
1732
|
+
const definition = variable.defs.at(0);
|
|
1733
|
+
if (definition === void 0) return null;
|
|
1734
|
+
if (definition.type === DefinitionType.ClassName || definition.type === DefinitionType.FunctionName) return definition.node;
|
|
1735
|
+
if (definition.type === DefinitionType.Variable) return definition.node.init;
|
|
1736
|
+
return null;
|
|
1737
|
+
}
|
|
1738
|
+
//#endregion
|
|
1739
|
+
//#region src/utils/unnecessary-hook.ts
|
|
1740
|
+
/**
|
|
1741
|
+
* Builds the `create` for the `no-unnecessary-use-memo` /
|
|
1742
|
+
* `no-unnecessary-use-callback` rules.
|
|
1743
|
+
*
|
|
1744
|
+
* Faithfully ports the rules removed from `eslint-plugin-react-x` (they were
|
|
1745
|
+
* dropped because the React Compiler makes them redundant; the Roblox /
|
|
1746
|
+
* `@rbxts/react` ecosystem has no Compiler). Hook detection is delegated to
|
|
1747
|
+
* `@eslint-react/core` so it matches the upstream semantics (fully-qualified
|
|
1748
|
+
* name resolution across imports, namespaces and `require`).
|
|
1749
|
+
*
|
|
1750
|
+
* @param config - The hook-specific configuration.
|
|
1751
|
+
* @returns A rule `create` function.
|
|
1752
|
+
* @template MessageIds - The rule's message identifiers.
|
|
1753
|
+
*/
|
|
1754
|
+
function createUnnecessaryHookRule(config) {
|
|
1755
|
+
const { hook, messageIds } = config;
|
|
1756
|
+
const skipComputation = hook === "useMemo";
|
|
1757
|
+
return (context) => {
|
|
1758
|
+
const { sourceCode } = context;
|
|
1759
|
+
const reactContext = context;
|
|
1760
|
+
const detectHookCall = hook === "useMemo" ? core.isUseMemoCall : core.isUseCallbackCall;
|
|
1761
|
+
return { VariableDeclarator(node) {
|
|
1762
|
+
const { id, init } = node;
|
|
1763
|
+
if (id.type !== AST_NODE_TYPES.Identifier || init?.type !== AST_NODE_TYPES.CallExpression || !detectHookCall(reactContext, init)) return;
|
|
1764
|
+
const [variable, ...rest] = sourceCode.getDeclaredVariables(node);
|
|
1765
|
+
if (variable === void 0 || rest.length > 0) return;
|
|
1766
|
+
const insideEffectReport = checkForUsageInsideUseEffect(sourceCode, init, messageIds.insideUseEffect);
|
|
1767
|
+
const component = sourceCode.getScope(init).block;
|
|
1768
|
+
if (!ASTUtils.isFunction(component)) return;
|
|
1769
|
+
const [argument0, argument1] = init.arguments;
|
|
1770
|
+
if (argument0 === void 0 || argument1 === void 0) return;
|
|
1771
|
+
if (skipComputation && ASTUtils.isFunction(argument0) && hasNestedCallOrNew(argument0.body)) {
|
|
1772
|
+
reportIf(context, insideEffectReport);
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
if (!hasEmptyDeps(sourceCode, argument1)) {
|
|
1776
|
+
reportIf(context, insideEffectReport);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
const factory = resolveFactory(sourceCode, argument0);
|
|
1780
|
+
if (factory === null) return;
|
|
1781
|
+
if (!referencesComponentScope(sourceCode, factory, component)) {
|
|
1782
|
+
context.report({
|
|
1783
|
+
messageId: messageIds.default,
|
|
1784
|
+
node
|
|
1785
|
+
});
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
reportIf(context, insideEffectReport);
|
|
1789
|
+
} };
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Finds the nearest ancestor that is a `useEffect`-like call.
|
|
1794
|
+
*
|
|
1795
|
+
* @param node - The node to search upward from.
|
|
1796
|
+
* @returns The enclosing effect call, or `null` when there is none.
|
|
1797
|
+
*/
|
|
1798
|
+
function findEnclosingEffect(node) {
|
|
1799
|
+
let current = node.parent;
|
|
1800
|
+
while (current !== void 0) {
|
|
1801
|
+
if (core.isUseEffectLikeCall(current)) return current;
|
|
1802
|
+
if (current.type === AST_NODE_TYPES.Program) return null;
|
|
1803
|
+
current = current.parent;
|
|
1804
|
+
}
|
|
1805
|
+
return null;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Reports the "used inside a single useEffect" case when applicable.
|
|
1809
|
+
*
|
|
1810
|
+
* @param sourceCode - Provides declared-variable and text lookups.
|
|
1811
|
+
* @param node - The hook call expression.
|
|
1812
|
+
* @param messageId - The message id to report.
|
|
1813
|
+
* @returns A report descriptor, or `null` when the case does not apply.
|
|
1814
|
+
* @template MessageIds - The rule's message identifiers.
|
|
1815
|
+
*/
|
|
1816
|
+
function checkForUsageInsideUseEffect(sourceCode, node, messageId) {
|
|
1817
|
+
if (!/use\w*Effect/u.test(sourceCode.text)) return null;
|
|
1818
|
+
const { parent } = node;
|
|
1819
|
+
if (parent.type !== AST_NODE_TYPES.VariableDeclarator || parent.id.type !== AST_NODE_TYPES.Identifier) return null;
|
|
1820
|
+
const usages = (sourceCode.getDeclaredVariables(parent).at(0)?.references ?? []).filter((reference) => reference.init !== true);
|
|
1821
|
+
if (usages.length === 0) return null;
|
|
1822
|
+
const effects = /* @__PURE__ */ new Set();
|
|
1823
|
+
for (const usage of usages) {
|
|
1824
|
+
const effect = findEnclosingEffect(usage.identifier);
|
|
1825
|
+
if (effect === null) return null;
|
|
1826
|
+
effects.add(effect);
|
|
1827
|
+
if (effects.size > 1) return null;
|
|
1828
|
+
}
|
|
1829
|
+
return {
|
|
1830
|
+
data: { name: parent.id.name },
|
|
1831
|
+
messageId,
|
|
1832
|
+
node
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Determines whether a dependency argument is an empty array (directly or via a
|
|
1837
|
+
* resolvable identifier).
|
|
1838
|
+
*
|
|
1839
|
+
* @param sourceCode - Provides scope lookup to resolve identifiers.
|
|
1840
|
+
* @param node - The dependency argument node.
|
|
1841
|
+
* @returns `true` if the dependencies are empty.
|
|
1842
|
+
*/
|
|
1843
|
+
function hasEmptyDeps(sourceCode, node) {
|
|
1844
|
+
if (node.type === AST_NODE_TYPES.ArrayExpression) return node.elements.length === 0;
|
|
1845
|
+
if (node.type === AST_NODE_TYPES.Identifier) {
|
|
1846
|
+
const resolved = resolve(sourceCode, node);
|
|
1847
|
+
return resolved?.type === AST_NODE_TYPES.ArrayExpression && resolved.elements.length === 0;
|
|
1848
|
+
}
|
|
1849
|
+
return false;
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Collects a scope together with all of its descendant scopes.
|
|
1853
|
+
*
|
|
1854
|
+
* @param scope - The root scope.
|
|
1855
|
+
* @returns The scope and every nested child scope.
|
|
1856
|
+
*/
|
|
1857
|
+
function flattenScopes(scope) {
|
|
1858
|
+
return scope.childScopes.reduce((accumulator, child) => [...accumulator, ...flattenScopes(child)], [scope]);
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* Determines whether any reference inside the factory resolves to a binding
|
|
1862
|
+
* declared in the component's scope.
|
|
1863
|
+
*
|
|
1864
|
+
* @param sourceCode - Provides scope lookup for the factory.
|
|
1865
|
+
* @param factory - The memoized factory function node.
|
|
1866
|
+
* @param component - The enclosing component function node.
|
|
1867
|
+
* @returns `true` if the factory reads from the component scope.
|
|
1868
|
+
*/
|
|
1869
|
+
function referencesComponentScope(sourceCode, factory, component) {
|
|
1870
|
+
return flattenScopes(sourceCode.getScope(factory)).flatMap((scope) => scope.references).some((reference) => reference.resolved?.scope.block === component);
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Reports the descriptor when it is present.
|
|
1874
|
+
*
|
|
1875
|
+
* @param context - The rule context.
|
|
1876
|
+
* @param descriptor - The descriptor to report, or `null` to skip.
|
|
1877
|
+
* @template MessageIds - The rule's message identifiers.
|
|
1878
|
+
*/
|
|
1879
|
+
function reportIf(context, descriptor) {
|
|
1880
|
+
if (descriptor !== null) context.report(descriptor);
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Resolves the first argument of the hook call to the underlying factory
|
|
1884
|
+
* function node, unwrapping a curried arrow (`() => () => ...`) and resolving
|
|
1885
|
+
* identifiers to their initializer.
|
|
1886
|
+
*
|
|
1887
|
+
* @param sourceCode - Provides scope lookup to resolve identifiers.
|
|
1888
|
+
* @param node - The first hook argument.
|
|
1889
|
+
* @returns The factory function node, or `null` when it is not a function.
|
|
1890
|
+
*/
|
|
1891
|
+
function resolveFactory(sourceCode, node) {
|
|
1892
|
+
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) return node.body.type === AST_NODE_TYPES.ArrowFunctionExpression ? node.body : node;
|
|
1893
|
+
if (node.type === AST_NODE_TYPES.FunctionExpression) return node;
|
|
1894
|
+
if (node.type === AST_NODE_TYPES.Identifier) {
|
|
1895
|
+
const resolved = resolve(sourceCode, node);
|
|
1896
|
+
if (resolved?.type === AST_NODE_TYPES.ArrowFunctionExpression || resolved?.type === AST_NODE_TYPES.FunctionExpression) return resolved;
|
|
1897
|
+
}
|
|
1898
|
+
return null;
|
|
1899
|
+
}
|
|
1900
|
+
//#endregion
|
|
1901
|
+
//#region src/rules/no-unnecessary-use-callback/rule.ts
|
|
1902
|
+
const RULE_NAME$4 = "no-unnecessary-use-callback";
|
|
1903
|
+
const MESSAGE_ID_DEFAULT$1 = "default";
|
|
1904
|
+
const MESSAGE_ID_INSIDE_USE_EFFECT$1 = "noUnnecessaryUseCallbackInsideUseEffect";
|
|
1905
|
+
const messages$4 = {
|
|
1906
|
+
[MESSAGE_ID_DEFAULT$1]: "An 'useCallback' with empty deps and no references to the component scope may be unnecessary.",
|
|
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
|
+
};
|
|
1909
|
+
const noUnnecessaryUseCallback = createEslintRule({
|
|
1910
|
+
name: RULE_NAME$4,
|
|
1911
|
+
create: createUnnecessaryHookRule({
|
|
1912
|
+
hook: "useCallback",
|
|
1913
|
+
messageIds: {
|
|
1914
|
+
default: MESSAGE_ID_DEFAULT$1,
|
|
1915
|
+
insideUseEffect: MESSAGE_ID_INSIDE_USE_EFFECT$1
|
|
1916
|
+
}
|
|
1917
|
+
}),
|
|
1918
|
+
defaultOptions: [],
|
|
1919
|
+
meta: {
|
|
1920
|
+
docs: {
|
|
1921
|
+
description: "Disallow unnecessary usage of 'useCallback'",
|
|
1922
|
+
recommended: false,
|
|
1923
|
+
requiresTypeChecking: false
|
|
1924
|
+
},
|
|
1925
|
+
hasSuggestions: false,
|
|
1926
|
+
messages: messages$4,
|
|
1927
|
+
schema: [],
|
|
1928
|
+
type: "suggestion"
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
//#endregion
|
|
1932
|
+
//#region src/rules/no-unnecessary-use-memo/rule.ts
|
|
1933
|
+
const RULE_NAME$3 = "no-unnecessary-use-memo";
|
|
1934
|
+
const MESSAGE_ID_DEFAULT = "default";
|
|
1935
|
+
const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
|
|
1936
|
+
const messages$3 = {
|
|
1937
|
+
[MESSAGE_ID_DEFAULT]: "An 'useMemo' with empty deps and no references to the component scope may be unnecessary.",
|
|
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
|
+
};
|
|
1940
|
+
const noUnnecessaryUseMemo = createEslintRule({
|
|
1941
|
+
name: RULE_NAME$3,
|
|
1942
|
+
create: createUnnecessaryHookRule({
|
|
1943
|
+
hook: "useMemo",
|
|
1944
|
+
messageIds: {
|
|
1945
|
+
default: MESSAGE_ID_DEFAULT,
|
|
1946
|
+
insideUseEffect: MESSAGE_ID_INSIDE_USE_EFFECT
|
|
1947
|
+
}
|
|
1948
|
+
}),
|
|
1949
|
+
defaultOptions: [],
|
|
1950
|
+
meta: {
|
|
1951
|
+
docs: {
|
|
1952
|
+
description: "Disallow unnecessary usage of 'useMemo'",
|
|
1953
|
+
recommended: false,
|
|
1954
|
+
requiresTypeChecking: false
|
|
1955
|
+
},
|
|
1956
|
+
hasSuggestions: false,
|
|
1957
|
+
messages: messages$3,
|
|
1958
|
+
schema: [],
|
|
1959
|
+
type: "suggestion"
|
|
1960
|
+
}
|
|
1961
|
+
});
|
|
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
|
|
1662
2163
|
//#region src/rules/toml-sort-keys/rule.ts
|
|
1663
2164
|
const RULE_NAME$1 = "toml-sort-keys";
|
|
1664
2165
|
const MESSAGE_ID = "unsorted";
|
|
@@ -1957,6 +2458,9 @@ const plugin = {
|
|
|
1957
2458
|
"jsx-shorthand-boolean": jsxShorthandBoolean,
|
|
1958
2459
|
"jsx-shorthand-fragment": jsxShorthandFragment,
|
|
1959
2460
|
"naming-convention": namingConvention,
|
|
2461
|
+
"no-unnecessary-use-callback": noUnnecessaryUseCallback,
|
|
2462
|
+
"no-unnecessary-use-memo": noUnnecessaryUseMemo,
|
|
2463
|
+
"prefer-destructuring-assignment": preferDestructuringAssignment,
|
|
1960
2464
|
"toml-sort-keys": tomlSortKeys,
|
|
1961
2465
|
"yaml-block-key-blank-lines": yamlBlockKeyBlankLines
|
|
1962
2466
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-flawless",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Your ESLint plugin description",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"*": "eslint --fix --cache"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@eslint-react/core": "5.10.0",
|
|
37
38
|
"@typescript-eslint/scope-manager": "8.62.1",
|
|
38
39
|
"@typescript-eslint/type-utils": "8.62.1",
|
|
39
40
|
"@typescript-eslint/utils": "8.62.1",
|