eslint-plugin-flawless 0.1.9 → 0.1.10

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/dist/index.d.mts CHANGED
@@ -24,7 +24,7 @@ interface JsxShorthandFragmentOptions {
24
24
  */
25
25
  readonly mode?: Mode;
26
26
  }
27
- type Options$3 = [JsxShorthandFragmentOptions?];
27
+ type Options$4 = [JsxShorthandFragmentOptions?];
28
28
  type Mode = "element" | "syntax";
29
29
  //#endregion
30
30
  //#region src/rules/naming-convention/utils/enums.d.ts
@@ -135,7 +135,7 @@ interface NamingSelector {
135
135
  //#endregion
136
136
  //#region src/rules/naming-convention/rule.d.ts
137
137
  type MessageIds$2 = "doesNotMatchFormat" | "doesNotMatchFormatTrimmed" | "missingAffix" | "missingUnderscore" | "satisfyCustom" | "unexpectedUnderscore";
138
- type Options$2 = Array<NamingSelector>;
138
+ type Options$3 = Array<NamingSelector>;
139
139
  //#endregion
140
140
  //#region src/rules/no-unnecessary-use-callback/rule.d.ts
141
141
  declare const MESSAGE_ID_DEFAULT$1 = "default";
@@ -147,6 +147,16 @@ declare const MESSAGE_ID_DEFAULT = "default";
147
147
  declare const MESSAGE_ID_INSIDE_USE_EFFECT = "noUnnecessaryUseMemoInsideUseEffect";
148
148
  type MessageIds = typeof MESSAGE_ID_DEFAULT | typeof MESSAGE_ID_INSIDE_USE_EFFECT;
149
149
  //#endregion
150
+ //#region src/rules/prefer-parameter-destructuring/rule.d.ts
151
+ type Options$2 = [{
152
+ /**
153
+ * Whether the autofix may hoist pattern defaults and computed keys past
154
+ * earlier statements, reordering their side effects. Defaults to `true`;
155
+ * set to `false` to withhold the fix in those cases.
156
+ */
157
+ allowSideEffectReordering?: boolean;
158
+ }];
159
+ //#endregion
150
160
  //#region src/rules/purity/rule.d.ts
151
161
  interface PurityOptions {
152
162
  /**
@@ -182,10 +192,10 @@ declare const plugin: {
182
192
  "jsx-shorthand-boolean": TSESLint.RuleModule<"setAttributeValue", [], PluginDocumentation, TSESLint.RuleListener> & {
183
193
  name: string;
184
194
  };
185
- "jsx-shorthand-fragment": TSESLint.RuleModule<MessageIds$3, Options$3, PluginDocumentation, TSESLint.RuleListener> & {
195
+ "jsx-shorthand-fragment": TSESLint.RuleModule<MessageIds$3, Options$4, PluginDocumentation, TSESLint.RuleListener> & {
186
196
  name: string;
187
197
  };
188
- "naming-convention": TSESLint.RuleModule<MessageIds$2, Options$2, PluginDocumentation, TSESLint.RuleListener> & {
198
+ "naming-convention": TSESLint.RuleModule<MessageIds$2, Options$3, PluginDocumentation, TSESLint.RuleListener> & {
189
199
  name: string;
190
200
  };
191
201
  "no-unnecessary-use-callback": TSESLint.RuleModule<MessageIds$1, [], PluginDocumentation, TSESLint.RuleListener> & {
@@ -197,7 +207,7 @@ declare const plugin: {
197
207
  "prefer-destructuring-assignment": TSESLint.RuleModule<"default", [], PluginDocumentation, TSESLint.RuleListener> & {
198
208
  name: string;
199
209
  };
200
- "prefer-parameter-destructuring": TSESLint.RuleModule<"default", [], PluginDocumentation, TSESLint.RuleListener> & {
210
+ "prefer-parameter-destructuring": TSESLint.RuleModule<"default", Options$2, PluginDocumentation, TSESLint.RuleListener> & {
201
211
  name: string;
202
212
  };
203
213
  purity: TSESLint.RuleModule<"impureCall", Options$1, PluginDocumentation, TSESLint.RuleListener> & {
@@ -226,10 +236,10 @@ declare const _default: {
226
236
  "jsx-shorthand-boolean": import("${configDir}").RuleModule<"setAttributeValue", [], PluginDocumentation, import("${configDir}").RuleListener> & {
227
237
  name: string;
228
238
  };
229
- "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
239
+ "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$4, PluginDocumentation, import("${configDir}").RuleListener> & {
230
240
  name: string;
231
241
  };
232
- "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
242
+ "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
233
243
  name: string;
234
244
  };
235
245
  "no-unnecessary-use-callback": import("${configDir}").RuleModule<MessageIds$1, [], PluginDocumentation, import("${configDir}").RuleListener> & {
@@ -241,7 +251,7 @@ declare const _default: {
241
251
  "prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
242
252
  name: string;
243
253
  };
244
- "prefer-parameter-destructuring": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
254
+ "prefer-parameter-destructuring": import("${configDir}").RuleModule<"default", Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
245
255
  name: string;
246
256
  };
247
257
  purity: import("${configDir}").RuleModule<"impureCall", Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
@@ -267,10 +277,10 @@ declare const _default: {
267
277
  "jsx-shorthand-boolean": import("${configDir}").RuleModule<"setAttributeValue", [], PluginDocumentation, import("${configDir}").RuleListener> & {
268
278
  name: string;
269
279
  };
270
- "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
280
+ "jsx-shorthand-fragment": import("${configDir}").RuleModule<MessageIds$3, Options$4, PluginDocumentation, import("${configDir}").RuleListener> & {
271
281
  name: string;
272
282
  };
273
- "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
283
+ "naming-convention": import("${configDir}").RuleModule<MessageIds$2, Options$3, PluginDocumentation, import("${configDir}").RuleListener> & {
274
284
  name: string;
275
285
  };
276
286
  "no-unnecessary-use-callback": import("${configDir}").RuleModule<MessageIds$1, [], PluginDocumentation, import("${configDir}").RuleListener> & {
@@ -282,7 +292,7 @@ declare const _default: {
282
292
  "prefer-destructuring-assignment": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
283
293
  name: string;
284
294
  };
285
- "prefer-parameter-destructuring": import("${configDir}").RuleModule<"default", [], PluginDocumentation, import("${configDir}").RuleListener> & {
295
+ "prefer-parameter-destructuring": import("${configDir}").RuleModule<"default", Options$2, PluginDocumentation, import("${configDir}").RuleListener> & {
286
296
  name: string;
287
297
  };
288
298
  purity: import("${configDir}").RuleModule<"impureCall", Options$1, PluginDocumentation, import("${configDir}").RuleListener> & {
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.9";
11
+ var version = "0.1.10";
12
12
  var repository = {
13
13
  "url": "git+https://github.com/christopher-buss/eslint-plugin-flawless.git",
14
14
  "type": "git"
@@ -2310,8 +2310,8 @@ function isNodeLike(value) {
2310
2310
  }
2311
2311
  /**
2312
2312
  * Checks whether a pattern subtree contains `await` or `yield` (in a computed
2313
- * key or default value). Parameter initializers may not contain either, so the
2314
- * fix bails.
2313
+ * key or default value). Parameter initializers may not contain either, so no
2314
+ * signature form exists and the destructuring is not reported.
2315
2315
  *
2316
2316
  * @param root - The pattern to walk.
2317
2317
  * @param visitorKeys - The parser's visitor keys, used to walk without
@@ -2398,6 +2398,21 @@ function hasUseStrictDirective(body) {
2398
2398
  return false;
2399
2399
  }
2400
2400
  /**
2401
+ * Checks whether evaluating the pattern can execute arbitrary code — a default
2402
+ * value or a computed key — as opposed to only performing property reads.
2403
+ *
2404
+ * @param node - The pattern node to walk.
2405
+ * @returns `true` when the pattern contains a default value or computed key.
2406
+ */
2407
+ function patternExecutesCode(node) {
2408
+ if (node.type === AST_NODE_TYPES.AssignmentPattern) return true;
2409
+ if (node.type === AST_NODE_TYPES.Property) return node.computed || patternExecutesCode(node.value);
2410
+ if (node.type === AST_NODE_TYPES.ObjectPattern) return node.properties.some((property) => patternExecutesCode(property));
2411
+ if (node.type === AST_NODE_TYPES.ArrayPattern) return node.elements.some((element) => element !== null && patternExecutesCode(element));
2412
+ if (node.type === AST_NODE_TYPES.RestElement) return patternExecutesCode(node.argument);
2413
+ return false;
2414
+ }
2415
+ /**
2401
2416
  * Computes the removal range for a declaration, swallowing the whole line
2402
2417
  * (indentation and trailing newline) when the declaration is alone on it.
2403
2418
  *
@@ -2418,20 +2433,31 @@ function statementRemovalRange({ text }, statement) {
2418
2433
  /**
2419
2434
  * Builds the autofix, or returns `null` when the rewrite is not unambiguously
2420
2435
  * safe: unrelated sibling declarators, unmergeable or annotated patterns,
2421
- * duplicate or colliding binding names, `await`/`yield` in the pattern, or
2422
- * expressions that reference bindings unavailable at the parameter position.
2436
+ * duplicate or colliding binding names, expressions that reference bindings
2437
+ * unavailable at the parameter position, a defused temporal dead zone, or —
2438
+ * with `allowSideEffectReordering: false` — side effects that would be
2439
+ * reordered.
2423
2440
  *
2424
2441
  * @param query - The rewrite being planned.
2425
2442
  * @returns The fix plan, or `null` when only a report should be emitted.
2426
2443
  */
2427
2444
  function planFix(query) {
2428
- const { identifier, node, otherParameterNames, sourceCode, statements } = query;
2445
+ const { allowSideEffectReordering, body, identifier, node, otherParameterNames, sourceCode, statements } = query;
2429
2446
  const declaratorSet = new Set(statements.map((statement) => statement.declarator));
2430
2447
  const declarations = [...new Set(statements.map((statement) => statement.declaration))];
2431
2448
  if (!declarations.every((declaration) => {
2432
2449
  return declaration.declarations.every((declarator) => declaratorSet.has(declarator));
2433
2450
  })) return null;
2434
2451
  const patterns = statements.map((statement) => statement.pattern);
2452
+ if (!allowSideEffectReordering && patterns.some((pattern) => patternExecutesCode(pattern))) {
2453
+ const leadingStatements = new Set(body.body.slice(0, declarations.length));
2454
+ if (!declarations.every((declaration) => leadingStatements.has(declaration))) return null;
2455
+ }
2456
+ if (declarations.some((declaration) => {
2457
+ return sourceCode.getDeclaredVariables(declaration).some((declared) => {
2458
+ return declared.references.some((reference) => reference.identifier.range[0] < declaration.range[0]);
2459
+ });
2460
+ })) return null;
2435
2461
  if (patterns.some((pattern) => pattern.typeAnnotation !== void 0) && (patterns.length > 1 || identifier.typeAnnotation !== void 0)) return null;
2436
2462
  if (patterns.length > 1) {
2437
2463
  if (patterns.some((pattern) => {
@@ -2442,7 +2468,6 @@ function planFix(query) {
2442
2468
  for (const pattern of patterns) collectBoundNames(pattern, boundNames);
2443
2469
  if (new Set(boundNames).size !== boundNames.length) return null;
2444
2470
  if (boundNames.some((name) => otherParameterNames.has(name))) return null;
2445
- if (patterns.some((pattern) => containsAwaitOrYield(pattern, sourceCode.visitorKeys))) return null;
2446
2471
  if (hasUnsafePatternReferences(query, patterns)) return null;
2447
2472
  const [firstPattern] = patterns;
2448
2473
  let parameterText;
@@ -2470,9 +2495,11 @@ function planFix(query) {
2470
2495
  * {@link planFix}.
2471
2496
  *
2472
2497
  * @param context - The rule context.
2498
+ * @param optionsWithDefault - The resolved rule options.
2473
2499
  * @returns The rule listener.
2474
2500
  */
2475
- function create$3(context) {
2501
+ function create$3(context, optionsWithDefault) {
2502
+ const [{ allowSideEffectReordering = true }] = optionsWithDefault;
2476
2503
  function checkFunction(node) {
2477
2504
  const { body, params } = node;
2478
2505
  if (body.type !== AST_NODE_TYPES.BlockStatement || hasUseStrictDirective(body)) return;
@@ -2486,9 +2513,13 @@ function create$3(context) {
2486
2513
  if (variable?.defs.length !== 1) continue;
2487
2514
  const statements = collectDestructureStatements(variable, identifier, body);
2488
2515
  if (statements === null || statements.length === 0) continue;
2516
+ if (statements.some(({ pattern }) => {
2517
+ return containsAwaitOrYield(pattern, context.sourceCode.visitorKeys);
2518
+ })) continue;
2489
2519
  const otherParameterNames = collectOtherParameterNames(node, parameter);
2490
2520
  if (otherParameterNames.has(identifier.name)) continue;
2491
2521
  const plan = planFix({
2522
+ allowSideEffectReordering,
2492
2523
  body,
2493
2524
  identifier,
2494
2525
  node,
@@ -2517,8 +2548,9 @@ function create$3(context) {
2517
2548
  const preferParameterDestructuring = createEslintRule({
2518
2549
  name: RULE_NAME$3,
2519
2550
  create: create$3,
2520
- defaultOptions: [],
2551
+ defaultOptions: [{ allowSideEffectReordering: true }],
2521
2552
  meta: {
2553
+ defaultOptions: [{ allowSideEffectReordering: true }],
2522
2554
  docs: {
2523
2555
  description: "Enforce destructuring parameters in the function signature",
2524
2556
  recommended: false,
@@ -2527,7 +2559,14 @@ const preferParameterDestructuring = createEslintRule({
2527
2559
  fixable: "code",
2528
2560
  hasSuggestions: false,
2529
2561
  messages: messages$3,
2530
- schema: [],
2562
+ schema: [{
2563
+ additionalProperties: false,
2564
+ properties: { allowSideEffectReordering: {
2565
+ description: "Whether the autofix may hoist pattern defaults and computed keys past earlier statements, reordering their side effects. Set to false to withhold the fix in those cases.",
2566
+ type: "boolean"
2567
+ } },
2568
+ type: "object"
2569
+ }],
2531
2570
  type: "suggestion"
2532
2571
  }
2533
2572
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-flawless",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Your ESLint plugin description",
5
5
  "keywords": [
6
6
  "eslint",