eslint-plugin-react-web-api 3.0.0-next.8 → 3.0.0-next.80

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +5 -16
  2. package/dist/index.js +187 -68
  3. package/package.json +12 -11
package/dist/index.d.ts CHANGED
@@ -1,20 +1,9 @@
1
- import * as _eslint_react_shared0 from "@eslint-react/shared";
1
+ import { ESLint, Linter } from "eslint";
2
2
 
3
3
  //#region src/index.d.ts
4
- declare const _default: {
5
- configs: {
6
- recommended: {
7
- plugins: {};
8
- name?: string;
9
- rules?: Record<string, _eslint_react_shared0.RuleConfig>;
10
- settings?: _eslint_react_shared0.SettingsConfig;
11
- };
12
- };
13
- meta: {
14
- name: string;
15
- version: string;
16
- };
17
- rules: Record<string, _eslint_react_shared0.CompatibleRule>;
4
+ type ConfigName = "recommended";
5
+ declare const finalPlugin: ESLint.Plugin & {
6
+ configs: Record<ConfigName, Linter.Config>;
18
7
  };
19
8
  //#endregion
20
- export { _default as default };
9
+ export { finalPlugin as default };
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- import { DEFAULT_ESLINT_REACT_SETTINGS, WEBSITE_URL, defineRuleListener, getConfigAdapters } from "@eslint-react/shared";
1
+ import { DEFAULT_ESLINT_REACT_SETTINGS, WEBSITE_URL, defineRuleListener } from "@eslint-react/shared";
2
2
  import * as ast from "@eslint-react/ast";
3
3
  import * as core from "@eslint-react/core";
4
- import { dual, or, unit } from "@eslint-react/eff";
5
- import { findEnclosingAssignmentTarget, findProperty, findVariable, getVariableDefinitionNode, isAssignmentTargetEqual, isNodeEqual } from "@eslint-react/var";
4
+ import { findEnclosingAssignmentTarget, isAssignmentTargetEqual, isValueEqual, resolve } from "@eslint-react/var";
6
5
  import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
7
6
  import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
8
7
  import { P, isMatching, match } from "ts-pattern";
@@ -25,25 +24,130 @@ var __exportAll = (all, no_symbols) => {
25
24
  };
26
25
 
27
26
  //#endregion
28
- //#region src/configs/recommended.ts
29
- var recommended_exports = /* @__PURE__ */ __exportAll({
30
- name: () => name$1,
31
- rules: () => rules,
32
- settings: () => settings
33
- });
34
- const name$1 = "react-web-api/recommended";
35
- const rules = {
36
- "react-web-api/no-leaked-event-listener": "warn",
37
- "react-web-api/no-leaked-interval": "warn",
38
- "react-web-api/no-leaked-resize-observer": "warn",
39
- "react-web-api/no-leaked-timeout": "warn"
40
- };
41
- const settings = { "react-x": DEFAULT_ESLINT_REACT_SETTINGS };
27
+ //#region package.json
28
+ var name$1 = "eslint-plugin-react-web-api";
29
+ var version = "3.0.0-next.80";
42
30
 
43
31
  //#endregion
44
- //#region package.json
45
- var name = "eslint-plugin-react-web-api";
46
- var version = "3.0.0-next.8";
32
+ //#region ../../../.pkgs/eff/dist/index.js
33
+ function or(a, b) {
34
+ return (data) => a(data) || b(data);
35
+ }
36
+ /**
37
+ * Creates a function that can be used in a data-last (aka `pipe`able) or
38
+ * data-first style.
39
+ *
40
+ * The first parameter to `dual` is either the arity of the uncurried function
41
+ * or a predicate that determines if the function is being used in a data-first
42
+ * or data-last style.
43
+ *
44
+ * Using the arity is the most common use case, but there are some cases where
45
+ * you may want to use a predicate. For example, if you have a function that
46
+ * takes an optional argument, you can use a predicate to determine if the
47
+ * function is being used in a data-first or data-last style.
48
+ *
49
+ * You can pass either the arity of the uncurried function or a predicate
50
+ * which determines if the function is being used in a data-first or
51
+ * data-last style.
52
+ *
53
+ * **Example** (Using arity to determine data-first or data-last style)
54
+ *
55
+ * ```ts
56
+ * import { dual, pipe } from "effect/Function"
57
+ *
58
+ * const sum = dual<
59
+ * (that: number) => (self: number) => number,
60
+ * (self: number, that: number) => number
61
+ * >(2, (self, that) => self + that)
62
+ *
63
+ * console.log(sum(2, 3)) // 5
64
+ * console.log(pipe(2, sum(3))) // 5
65
+ * ```
66
+ *
67
+ * **Example** (Using call signatures to define the overloads)
68
+ *
69
+ * ```ts
70
+ * import { dual, pipe } from "effect/Function"
71
+ *
72
+ * const sum: {
73
+ * (that: number): (self: number) => number
74
+ * (self: number, that: number): number
75
+ * } = dual(2, (self: number, that: number): number => self + that)
76
+ *
77
+ * console.log(sum(2, 3)) // 5
78
+ * console.log(pipe(2, sum(3))) // 5
79
+ * ```
80
+ *
81
+ * **Example** (Using a predicate to determine data-first or data-last style)
82
+ *
83
+ * ```ts
84
+ * import { dual, pipe } from "effect/Function"
85
+ *
86
+ * const sum = dual<
87
+ * (that: number) => (self: number) => number,
88
+ * (self: number, that: number) => number
89
+ * >(
90
+ * (args) => args.length === 2,
91
+ * (self, that) => self + that
92
+ * )
93
+ *
94
+ * console.log(sum(2, 3)) // 5
95
+ * console.log(pipe(2, sum(3))) // 5
96
+ * ```
97
+ *
98
+ * @param arity - The arity of the uncurried function or a predicate that determines if the function is being used in a data-first or data-last style.
99
+ * @param body - The function to be curried.
100
+ * @since 1.0.0
101
+ */
102
+ const dual = function(arity, body) {
103
+ if (typeof arity === "function") return function() {
104
+ return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
105
+ };
106
+ switch (arity) {
107
+ case 0:
108
+ case 1: throw new RangeError(`Invalid arity ${arity}`);
109
+ case 2: return function(a, b) {
110
+ if (arguments.length >= 2) return body(a, b);
111
+ return function(self) {
112
+ return body(self, a);
113
+ };
114
+ };
115
+ case 3: return function(a, b, c) {
116
+ if (arguments.length >= 3) return body(a, b, c);
117
+ return function(self) {
118
+ return body(self, a, b);
119
+ };
120
+ };
121
+ default: return function() {
122
+ if (arguments.length >= arity) return body.apply(this, arguments);
123
+ const args = arguments;
124
+ return function(self) {
125
+ return body(self, ...args);
126
+ };
127
+ };
128
+ }
129
+ };
130
+ /**
131
+ * Composes two functions, `ab` and `bc` into a single function that takes in an argument `a` of type `A` and returns a result of type `C`.
132
+ * The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
133
+ *
134
+ * @param self - The first function to apply (or the composed function in data-last style).
135
+ * @param bc - The second function to apply.
136
+ * @returns A composed function that applies both functions in sequence.
137
+ * @example
138
+ * ```ts
139
+ * import * as assert from "node:assert"
140
+ * import { compose } from "effect/Function"
141
+ *
142
+ * const increment = (n: number) => n + 1;
143
+ * const square = (n: number) => n * n;
144
+ *
145
+ * assert.strictEqual(compose(increment, square)(2), 9);
146
+ * ```
147
+ *
148
+ * @since 1.0.0
149
+ */
150
+ const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
47
151
 
48
152
  //#endregion
49
153
  //#region src/types/component-phase.ts
@@ -64,11 +168,11 @@ function getDocsUrl(ruleName) {
64
168
  const createRule = ESLintUtils.RuleCreator(getDocsUrl);
65
169
 
66
170
  //#endregion
67
- //#region src/rules/no-leaked-event-listener.ts
171
+ //#region src/rules/no-leaked-event-listener/no-leaked-event-listener.ts
68
172
  const RULE_NAME$3 = "no-leaked-event-listener";
69
173
  const defaultOptions = {
70
174
  capture: false,
71
- signal: unit
175
+ signal: null
72
176
  };
73
177
  function getCallKind$3(node) {
74
178
  switch (true) {
@@ -80,23 +184,21 @@ function getCallKind$3(node) {
80
184
  function getFunctionKind$1(node) {
81
185
  return getPhaseKindOfFunction(node) ?? "other";
82
186
  }
83
- function getSignalValueExpression(node, initialScope) {
84
- if (node == null) return unit;
187
+ function getSignalValueExpression(context, node) {
188
+ if (node == null) return null;
85
189
  switch (node.type) {
86
- case AST_NODE_TYPES.Identifier: return getSignalValueExpression(getVariableDefinitionNode(findVariable(node, initialScope), 0), initialScope);
190
+ case AST_NODE_TYPES.Identifier: return getSignalValueExpression(context, resolve(context, node));
87
191
  case AST_NODE_TYPES.MemberExpression: return node;
88
- default: return unit;
192
+ default: return null;
89
193
  }
90
194
  }
91
- function getOptions(node, initialScope) {
92
- function findProp(properties, propName) {
93
- return findProperty(propName, properties, initialScope);
94
- }
195
+ function getOptions(context, node) {
196
+ const initialScope = context.sourceCode.getScope(node);
95
197
  function getOpts(node) {
96
198
  switch (node.type) {
97
199
  case AST_NODE_TYPES.Identifier: {
98
- const variableNode = getVariableDefinitionNode(findVariable(node, initialScope), 0);
99
- if (variableNode?.type === AST_NODE_TYPES.ObjectExpression) return getOpts(variableNode);
200
+ const initNode = resolve(context, node);
201
+ if (initNode?.type === AST_NODE_TYPES.ObjectExpression) return getOpts(initNode);
100
202
  return defaultOptions;
101
203
  }
102
204
  case AST_NODE_TYPES.Literal: return {
@@ -104,17 +206,17 @@ function getOptions(node, initialScope) {
104
206
  capture: Boolean(node.value)
105
207
  };
106
208
  case AST_NODE_TYPES.ObjectExpression: {
107
- const vCapture = match(findProp(node.properties, "capture")).with(P.nullish, () => false).with({ type: AST_NODE_TYPES.Property }, (prop) => {
209
+ const vCapture = match(ast.findProperty(node.properties, "capture")).with(P.nullish, () => false).with({ type: AST_NODE_TYPES.Property }, (prop) => {
108
210
  const value = prop.value;
109
211
  switch (value.type) {
110
212
  case AST_NODE_TYPES.Literal: return Boolean(value.value);
111
213
  default: return Boolean(getStaticValue(value, initialScope)?.value);
112
214
  }
113
215
  }).otherwise(() => false);
114
- const pSignal = findProp(node.properties, "signal");
216
+ const pSignal = ast.findProperty(node.properties, "signal");
115
217
  return {
116
218
  capture: vCapture,
117
- signal: pSignal?.type === AST_NODE_TYPES.Property ? getSignalValueExpression(pSignal.value, initialScope) : unit
219
+ signal: pSignal?.type === AST_NODE_TYPES.Property ? getSignalValueExpression(context, pSignal.value) : null
118
220
  };
119
221
  }
120
222
  default: return defaultOptions;
@@ -154,16 +256,16 @@ function create$3(context) {
154
256
  const { type: aType, callee: aCallee, capture: aCapture, listener: aListener, phase: aPhase } = aEntry;
155
257
  const { type: rType, callee: rCallee, capture: rCapture, listener: rListener, phase: rPhase } = rEntry;
156
258
  if (!isInversePhase(aPhase, rPhase)) return false;
157
- return isSameObject(aCallee, rCallee) && ast.isNodeEqual(aListener, rListener) && isNodeEqual(aType, rType, [context.sourceCode.getScope(aType), context.sourceCode.getScope(rType)]) && aCapture === rCapture;
259
+ return isSameObject(aCallee, rCallee) && ast.isNodeEqual(aListener, rListener) && isValueEqual(context, aType, rType) && aCapture === rCapture;
158
260
  }
159
261
  function checkInlineFunction(node, callKind, options) {
160
262
  const listener = node.arguments.at(1);
161
263
  if (!ast.isFunction(listener)) return;
162
264
  if (options.signal != null) return;
163
265
  context.report({
266
+ data: { eventMethodKind: callKind },
164
267
  messageId: "unexpectedInlineFunction",
165
- node: listener,
166
- data: { eventMethodKind: callKind }
268
+ node: listener
167
269
  });
168
270
  }
169
271
  return defineRuleListener({
@@ -185,31 +287,31 @@ function create$3(context) {
185
287
  if (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.object.type === AST_NODE_TYPES.Identifier && core.isInitializedFromReactNative(node.callee.object.name, context.sourceCode.getScope(node))) return;
186
288
  const [type, listener, options] = node.arguments;
187
289
  if (type == null || listener == null) return;
188
- const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
290
+ const opts = options == null ? defaultOptions : getOptions(context, options);
189
291
  const { callee } = node;
190
292
  checkInlineFunction(node, callKind, opts);
191
293
  aEntries.push({
192
294
  ...opts,
193
295
  type,
194
- node,
195
296
  callee,
196
297
  listener,
197
298
  method: "addEventListener",
299
+ node,
198
300
  phase: fKind
199
301
  });
200
302
  }).with("removeEventListener", (callKind) => {
201
303
  const [type, listener, options] = node.arguments;
202
304
  if (type == null || listener == null) return;
203
- const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
305
+ const opts = options == null ? defaultOptions : getOptions(context, options);
204
306
  const { callee } = node;
205
307
  checkInlineFunction(node, callKind, opts);
206
308
  rEntries.push({
207
309
  ...opts,
208
310
  type,
209
- node,
210
311
  callee,
211
312
  listener,
212
313
  method: "removeEventListener",
314
+ node,
213
315
  phase: fKind
214
316
  });
215
317
  }).with("abort", () => {
@@ -224,9 +326,9 @@ function create$3(context) {
224
326
  case "setup":
225
327
  case "cleanup":
226
328
  context.report({
329
+ data: { effectMethodKind: "useEffect" },
227
330
  messageId: "expectedRemoveEventListenerInCleanup",
228
- node: aEntry.node,
229
- data: { effectMethodKind: "useEffect" }
331
+ node: aEntry.node
230
332
  });
231
333
  continue;
232
334
  case "mount":
@@ -243,7 +345,7 @@ function create$3(context) {
243
345
  }
244
346
 
245
347
  //#endregion
246
- //#region src/rules/no-leaked-interval.ts
348
+ //#region src/rules/no-leaked-interval/no-leaked-interval.ts
247
349
  const RULE_NAME$2 = "no-leaked-interval";
248
350
  function getCallKind$2(node) {
249
351
  switch (true) {
@@ -302,8 +404,8 @@ function create$2(context) {
302
404
  }
303
405
  sEntries.push({
304
406
  kind: "interval",
305
- node,
306
407
  callee: node.callee,
408
+ node,
307
409
  phase: fEntry.kind,
308
410
  timerId: intervalIdNode
309
411
  });
@@ -317,8 +419,8 @@ function create$2(context) {
317
419
  if (intervalIdNode == null) break;
318
420
  cEntries.push({
319
421
  kind: "interval",
320
- node,
321
422
  callee: node.callee,
423
+ node,
322
424
  phase: fEntry.kind,
323
425
  timerId: intervalIdNode
324
426
  });
@@ -333,17 +435,17 @@ function create$2(context) {
333
435
  case "setup":
334
436
  case "cleanup":
335
437
  context.report({
438
+ data: { kind: "useEffect" },
336
439
  messageId: "expectedClearIntervalInCleanup",
337
- node: sEntry.node,
338
- data: { kind: "useEffect" }
440
+ node: sEntry.node
339
441
  });
340
442
  continue;
341
443
  case "mount":
342
444
  case "unmount":
343
445
  context.report({
446
+ data: { kind: "componentDidMount" },
344
447
  messageId: "expectedClearIntervalInUnmount",
345
- node: sEntry.node,
346
- data: { kind: "componentDidMount" }
448
+ node: sEntry.node
347
449
  });
348
450
  continue;
349
451
  }
@@ -353,14 +455,14 @@ function create$2(context) {
353
455
  }
354
456
 
355
457
  //#endregion
356
- //#region src/rules/no-leaked-resize-observer.ts
458
+ //#region src/rules/no-leaked-resize-observer/no-leaked-resize-observer.ts
357
459
  const RULE_NAME$1 = "no-leaked-resize-observer";
358
460
  function isNewResizeObserver(node) {
359
461
  return node?.type === AST_NODE_TYPES.NewExpression && node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === "ResizeObserver";
360
462
  }
361
463
  function isFromObserver(context, node) {
362
464
  switch (true) {
363
- case node.type === AST_NODE_TYPES.Identifier: return isNewResizeObserver(getVariableDefinitionNode(findVariable(node, context.sourceCode.getScope(node)), 0));
465
+ case node.type === AST_NODE_TYPES.Identifier: return isNewResizeObserver(resolve(context, node));
364
466
  case node.type === AST_NODE_TYPES.MemberExpression: return isFromObserver(context, node.object);
365
467
  default: return false;
366
468
  }
@@ -416,9 +518,9 @@ function create$1(context) {
416
518
  match(getCallKind$1(context, node)).with("disconnect", () => {
417
519
  dEntries.push({
418
520
  kind: "ResizeObserver",
419
- node,
420
521
  callee: node.callee,
421
522
  method: "disconnect",
523
+ node,
422
524
  observer: object,
423
525
  phase: fKind
424
526
  });
@@ -427,10 +529,10 @@ function create$1(context) {
427
529
  if (element == null) return;
428
530
  oEntries.push({
429
531
  kind: "ResizeObserver",
430
- node,
431
532
  callee: node.callee,
432
533
  element,
433
534
  method: "observe",
535
+ node,
434
536
  observer: object,
435
537
  phase: fKind
436
538
  });
@@ -439,10 +541,10 @@ function create$1(context) {
439
541
  if (element == null) return;
440
542
  uEntries.push({
441
543
  kind: "ResizeObserver",
442
- node,
443
544
  callee: node.callee,
444
545
  element,
445
546
  method: "unobserve",
547
+ node,
446
548
  observer: object,
447
549
  phase: fKind
448
550
  });
@@ -495,7 +597,7 @@ function create$1(context) {
495
597
  }
496
598
 
497
599
  //#endregion
498
- //#region src/rules/no-leaked-timeout.ts
600
+ //#region src/rules/no-leaked-timeout/no-leaked-timeout.ts
499
601
  const RULE_NAME = "no-leaked-timeout";
500
602
  function getCallKind(node) {
501
603
  switch (true) {
@@ -553,8 +655,8 @@ function create(context) {
553
655
  }
554
656
  sEntries.push({
555
657
  kind: "timeout",
556
- node,
557
658
  callee: node.callee,
659
+ node,
558
660
  phase: fEntry.kind,
559
661
  timerId: timeoutIdNode
560
662
  });
@@ -565,8 +667,8 @@ function create(context) {
565
667
  if (timeoutIdNode == null) break;
566
668
  rEntries.push({
567
669
  kind: "timeout",
568
- node,
569
670
  callee: node.callee,
671
+ node,
570
672
  phase: fEntry.kind,
571
673
  timerId: timeoutIdNode
572
674
  });
@@ -581,17 +683,17 @@ function create(context) {
581
683
  case "setup":
582
684
  case "cleanup":
583
685
  context.report({
686
+ data: { kind: "useEffect" },
584
687
  messageId: "expectedClearTimeoutInCleanup",
585
- node: sEntry.node,
586
- data: { kind: "useEffect" }
688
+ node: sEntry.node
587
689
  });
588
690
  continue;
589
691
  case "mount":
590
692
  case "unmount":
591
693
  context.report({
694
+ data: { kind: "componentDidMount" },
592
695
  messageId: "expectedClearTimeoutInUnmount",
593
- node: sEntry.node,
594
- data: { kind: "componentDidMount" }
696
+ node: sEntry.node
595
697
  });
596
698
  continue;
597
699
  }
@@ -604,7 +706,7 @@ function create(context) {
604
706
  //#region src/plugin.ts
605
707
  const plugin = {
606
708
  meta: {
607
- name,
709
+ name: name$1,
608
710
  version
609
711
  },
610
712
  rules: {
@@ -615,13 +717,30 @@ const plugin = {
615
717
  }
616
718
  };
617
719
 
720
+ //#endregion
721
+ //#region src/configs/recommended.ts
722
+ var recommended_exports = /* @__PURE__ */ __exportAll({
723
+ name: () => name,
724
+ plugins: () => plugins,
725
+ rules: () => rules,
726
+ settings: () => settings
727
+ });
728
+ const name = "react-web-api/recommended";
729
+ const rules = {
730
+ "react-web-api/no-leaked-event-listener": "warn",
731
+ "react-web-api/no-leaked-interval": "warn",
732
+ "react-web-api/no-leaked-resize-observer": "warn",
733
+ "react-web-api/no-leaked-timeout": "warn"
734
+ };
735
+ const plugins = { "react-web-api": plugin };
736
+ const settings = { "react-x": DEFAULT_ESLINT_REACT_SETTINGS };
737
+
618
738
  //#endregion
619
739
  //#region src/index.ts
620
- const { toFlatConfig } = getConfigAdapters("react-web-api", plugin);
621
- var src_default = {
740
+ const finalPlugin = {
622
741
  ...plugin,
623
- configs: { ["recommended"]: toFlatConfig(recommended_exports) }
742
+ configs: { ["recommended"]: recommended_exports }
624
743
  };
625
744
 
626
745
  //#endregion
627
- export { src_default as default };
746
+ export { finalPlugin as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-web-api",
3
- "version": "3.0.0-next.8",
3
+ "version": "3.0.0-next.80",
4
4
  "description": "ESLint React's ESLint plugin for interacting with Web APIs",
5
5
  "keywords": [
6
6
  "react",
@@ -43,21 +43,22 @@
43
43
  "@typescript-eslint/utils": "canary",
44
44
  "birecord": "^0.1.1",
45
45
  "ts-pattern": "^5.9.0",
46
- "@eslint-react/ast": "3.0.0-next.8",
47
- "@eslint-react/core": "3.0.0-next.8",
48
- "@eslint-react/eff": "3.0.0-next.8",
49
- "@eslint-react/shared": "3.0.0-next.8",
50
- "@eslint-react/var": "3.0.0-next.8"
46
+ "@eslint-react/ast": "3.0.0-next.80",
47
+ "@eslint-react/core": "3.0.0-next.80",
48
+ "@eslint-react/shared": "3.0.0-next.80",
49
+ "@eslint-react/var": "3.0.0-next.80"
51
50
  },
52
51
  "devDependencies": {
53
52
  "@types/react": "^19.2.14",
54
53
  "@types/react-dom": "^19.2.3",
55
- "tsdown": "^0.20.3",
56
- "@local/configs": "0.0.0"
54
+ "eslint": "^10.0.3",
55
+ "tsdown": "^0.21.0",
56
+ "@local/configs": "0.0.0",
57
+ "@local/eff": "3.0.0-beta.72"
57
58
  },
58
59
  "peerDependencies": {
59
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
60
- "typescript": ">=4.8.4 <6.0.0"
60
+ "eslint": "^10.0.0",
61
+ "typescript": "*"
61
62
  },
62
63
  "engines": {
63
64
  "node": ">=22.0.0"
@@ -68,6 +69,6 @@
68
69
  "scripts": {
69
70
  "build": "tsdown",
70
71
  "lint:publish": "publint",
71
- "lint:ts": "tsc --noEmit"
72
+ "lint:ts": "tsl"
72
73
  }
73
74
  }