eslint-plugin-flawless 0.1.6 → 0.1.7

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 CHANGED
@@ -176,17 +176,17 @@ pnpm eslint-docs
176
176
  💭
177
177
  Requires [type information](https://typescript-eslint.io/linting/typed-linting).
178
178
 
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
- | [purity](src/rules/purity/documentation.md) | Disallow impure calls such as `math.random` or `os.clock` during render | | |
188
- | [toml-sort-keys](src/rules/toml-sort-keys/documentation.md) | Enforce a configured sort order for TOML keys and tables | 🔧 | |
189
- | [yaml-block-key-blank-lines](src/rules/yaml-block-key-blank-lines/documentation.md) | Enforce blank lines around top-level YAML block collection keys | 🔧 | |
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) | Enforce a consistent fragment form: the shorthand `<>...</>` or 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
+ | [purity](src/rules/purity/documentation.md) | Disallow impure calls such as `math.random` or `os.clock` during render | | |
188
+ | [toml-sort-keys](src/rules/toml-sort-keys/documentation.md) | Enforce a configured sort order for TOML keys and tables | 🔧 | |
189
+ | [yaml-block-key-blank-lines](src/rules/yaml-block-key-blank-lines/documentation.md) | Enforce blank lines around top-level YAML block collection keys | 🔧 | |
190
190
 
191
191
  <!-- end auto-generated rules list -->
192
192
 
package/dist/index.d.mts CHANGED
@@ -9,7 +9,23 @@ interface PluginDocumentation {
9
9
  }
10
10
  //#endregion
11
11
  //#region src/rules/jsx-shorthand-fragment/rule.d.ts
12
- type Options$3 = [fragmentName?: string];
12
+ declare const MESSAGE_ID_NAMED = "useNamedFragment";
13
+ declare const MESSAGE_ID_SHORTHAND = "useShorthandFragment";
14
+ type MessageIds$3 = typeof MESSAGE_ID_NAMED | typeof MESSAGE_ID_SHORTHAND;
15
+ interface JsxShorthandFragmentOptions {
16
+ /**
17
+ * The identifier used for the named fragment element in `"element"` mode
18
+ * (e.g. `"Fragment"` or `"React.Fragment"`). Defaults to `"Fragment"`.
19
+ */
20
+ readonly fragmentName?: string;
21
+ /**
22
+ * Which form to enforce: `"syntax"` (shorthand `<>...</>`, the default) or
23
+ * `"element"` (a named fragment such as `<Fragment>...</Fragment>`).
24
+ */
25
+ readonly mode?: Mode;
26
+ }
27
+ type Options$3 = [JsxShorthandFragmentOptions?];
28
+ type Mode = "element" | "syntax";
13
29
  //#endregion
14
30
  //#region src/rules/naming-convention/utils/enums.d.ts
15
31
  declare const Selector: {
@@ -166,7 +182,7 @@ declare const plugin: {
166
182
  "jsx-shorthand-boolean": TSESLint.RuleModule<"setAttributeValue", [], PluginDocumentation, TSESLint.RuleListener> & {
167
183
  name: string;
168
184
  };
169
- "jsx-shorthand-fragment": TSESLint.RuleModule<"useNamedFragment", Options$3, PluginDocumentation, TSESLint.RuleListener> & {
185
+ "jsx-shorthand-fragment": TSESLint.RuleModule<MessageIds$3, Options$3, PluginDocumentation, TSESLint.RuleListener> & {
170
186
  name: string;
171
187
  };
172
188
  "naming-convention": TSESLint.RuleModule<MessageIds$2, Options$2, PluginDocumentation, TSESLint.RuleListener> & {
@@ -207,7 +223,7 @@ declare const _default: {
207
223
  "jsx-shorthand-boolean": import("${configDir}").RuleModule<"setAttributeValue", [], PluginDocumentation, import("${configDir}").RuleListener> & {
208
224
  name: string;
209
225
  };
210
- "jsx-shorthand-fragment": import("${configDir}").RuleModule<"useNamedFragment", Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
226
+ "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
211
227
  name: string;
212
228
  };
213
229
  "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
@@ -245,7 +261,7 @@ declare const _default: {
245
261
  "jsx-shorthand-boolean": import("${configDir}").RuleModule<"setAttributeValue", [], PluginDocumentation, import("${configDir}").RuleListener> & {
246
262
  name: string;
247
263
  };
248
- "jsx-shorthand-fragment": import("${configDir}").RuleModule<"useNamedFragment", Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
264
+ "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
249
265
  name: string;
250
266
  };
251
267
  "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
package/dist/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import { RuleCreator, getParserServices } from "@typescript-eslint/utils/eslint-utils";
2
+ import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
2
3
  import { DefinitionType, ImplicitLibVariable, PatternVisitor, ScopeType, Visitor } from "@typescript-eslint/scope-manager";
3
4
  import { requiresQuoting } from "@typescript-eslint/type-utils";
4
- import { ASTUtils, AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
5
5
  import assert from "node:assert";
6
6
  import * as core from "@eslint-react/core";
7
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.6";
11
+ var version = "0.1.7";
12
12
  var repository = {
13
13
  "url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
14
14
  "type": "git"
@@ -36,8 +36,8 @@ function createEslintRule(rule) {
36
36
  //#endregion
37
37
  //#region src/rules/jsx-shorthand-boolean/rule.ts
38
38
  const RULE_NAME$8 = "jsx-shorthand-boolean";
39
- const MESSAGE_ID$4 = "setAttributeValue";
40
- const messages$8 = { [MESSAGE_ID$4]: "Set an explicit value for boolean attribute '{{name}}'." };
39
+ const MESSAGE_ID$3 = "setAttributeValue";
40
+ const messages$8 = { [MESSAGE_ID$3]: "Set an explicit value for boolean attribute '{{name}}'." };
41
41
  function create$8(context) {
42
42
  const { sourceCode } = context;
43
43
  return { JSXAttribute(node) {
@@ -45,7 +45,7 @@ function create$8(context) {
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$4,
48
+ messageId: MESSAGE_ID$3,
49
49
  node
50
50
  });
51
51
  } };
@@ -70,23 +70,79 @@ const jsxShorthandBoolean = createEslintRule({
70
70
  //#endregion
71
71
  //#region src/rules/jsx-shorthand-fragment/rule.ts
72
72
  const RULE_NAME$7 = "jsx-shorthand-fragment";
73
- const MESSAGE_ID$3 = "useNamedFragment";
73
+ const MESSAGE_ID_NAMED = "useNamedFragment";
74
+ const MESSAGE_ID_SHORTHAND = "useShorthandFragment";
75
+ const DEFAULT_MODE = "syntax";
74
76
  const DEFAULT_FRAGMENT_NAME = "Fragment";
75
- const messages$7 = { [MESSAGE_ID$3]: "Use the '{{name}}' component instead of fragment shorthand syntax." };
77
+ const messages$7 = {
78
+ [MESSAGE_ID_NAMED]: "Use the '{{name}}' component instead of fragment shorthand syntax.",
79
+ [MESSAGE_ID_SHORTHAND]: "Use the fragment shorthand syntax '<>...</>' instead of the '{{name}}' component."
80
+ };
76
81
  const schema$2 = [{
77
- description: "The identifier to use for the named fragment element.",
78
- type: "string"
82
+ additionalProperties: false,
83
+ properties: {
84
+ fragmentName: {
85
+ description: "The identifier to use for the named fragment element in \"element\" mode.",
86
+ type: "string"
87
+ },
88
+ mode: {
89
+ description: "Which form to enforce: \"syntax\" (shorthand `<>...</>`, default) or \"element\" (a named fragment).",
90
+ enum: ["element", "syntax"],
91
+ type: "string"
92
+ }
93
+ },
94
+ type: "object"
79
95
  }];
96
+ /**
97
+ * Flattens a JSX element name into a dotted string, e.g. `Fragment` or
98
+ * `React.Fragment`. Returns `null` for namespaced names (`<a:b>`) which cannot
99
+ * be a fragment.
100
+ *
101
+ * @param node - The JSX element name node.
102
+ * @returns The dotted name, or `null` when it is not a plain identifier chain.
103
+ */
104
+ function jsxNameToString(node) {
105
+ if (node.type === AST_NODE_TYPES.JSXIdentifier) return node.name;
106
+ if (node.type === AST_NODE_TYPES.JSXMemberExpression) {
107
+ const object = jsxNameToString(node.object);
108
+ if (object === null) return null;
109
+ return `${object}.${node.property.name}`;
110
+ }
111
+ return null;
112
+ }
80
113
  function create$7(context) {
81
- const name = context.options[0] ?? DEFAULT_FRAGMENT_NAME;
82
- return { JSXFragment(node) {
114
+ const options = context.options[0] ?? {};
115
+ const mode = options.mode ?? DEFAULT_MODE;
116
+ const fragmentName = options.fragmentName ?? DEFAULT_FRAGMENT_NAME;
117
+ if (mode === "element") return { JSXFragment(node) {
83
118
  const { closingFragment, openingFragment } = node;
119
+ context.report({
120
+ data: { name: fragmentName },
121
+ fix: (fixer) => {
122
+ return [fixer.replaceText(openingFragment, `<${fragmentName}>`), fixer.replaceText(closingFragment, `</${fragmentName}>`)];
123
+ },
124
+ messageId: MESSAGE_ID_NAMED,
125
+ node
126
+ });
127
+ } };
128
+ const namedFragments = /* @__PURE__ */ new Set([
129
+ "Fragment",
130
+ fragmentName,
131
+ "React.Fragment"
132
+ ]);
133
+ return { JSXElement(node) {
134
+ const { openingElement } = node;
135
+ const name = jsxNameToString(openingElement.name);
136
+ if (name === null || !namedFragments.has(name)) return;
137
+ if (openingElement.attributes.length > 0) return;
84
138
  context.report({
85
139
  data: { name },
86
140
  fix: (fixer) => {
87
- return [fixer.replaceText(openingFragment, `<${name}>`), fixer.replaceText(closingFragment, `</${name}>`)];
141
+ const { closingElement } = node;
142
+ if (closingElement === null) return fixer.replaceText(node, "<></>");
143
+ return [fixer.replaceText(openingElement, "<>"), fixer.replaceText(closingElement, "</>")];
88
144
  },
89
- messageId: MESSAGE_ID$3,
145
+ messageId: MESSAGE_ID_SHORTHAND,
90
146
  node
91
147
  });
92
148
  } };
@@ -94,11 +150,17 @@ function create$7(context) {
94
150
  const jsxShorthandFragment = createEslintRule({
95
151
  name: RULE_NAME$7,
96
152
  create: create$7,
97
- defaultOptions: [DEFAULT_FRAGMENT_NAME],
153
+ defaultOptions: [{
154
+ fragmentName: DEFAULT_FRAGMENT_NAME,
155
+ mode: DEFAULT_MODE
156
+ }],
98
157
  meta: {
99
- defaultOptions: [DEFAULT_FRAGMENT_NAME],
158
+ defaultOptions: [{
159
+ fragmentName: DEFAULT_FRAGMENT_NAME,
160
+ mode: DEFAULT_MODE
161
+ }],
100
162
  docs: {
101
- description: "Disallow the shorthand fragment syntax in favour of a named fragment",
163
+ description: "Enforce a consistent fragment form: the shorthand `<>...</>` or a named fragment",
102
164
  recommended: false,
103
165
  requiresTypeChecking: false
104
166
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-flawless",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Your ESLint plugin description",
5
5
  "keywords": [
6
6
  "eslint",