eslint-plugin-react-web-api
Advanced tools
Comparing version 1.11.0-beta.4 to 1.11.0-beta.7
import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint'; | ||
type MessageID$1 = "noLeakedTimeoutInEffect" | "noLeakedTimeoutInLifecycle" | "noLeakedTimeoutNoTimeoutId"; | ||
type MessageID = "noLeakedEventListenerInEffect" | "noLeakedEventListenerInLifecycle" | "noLeakedEventListenerOfInlineFunction"; | ||
@@ -11,2 +13,3 @@ | ||
readonly "no-leaked-event-listener": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>; | ||
readonly "no-leaked-timeout": _typescript_eslint_utils_ts_eslint.RuleModule<MessageID$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>; | ||
}; | ||
@@ -13,0 +16,0 @@ declare const DEFAULT_ESLINT_REACT_SETTINGS: { |
@@ -20,3 +20,3 @@ 'use strict'; | ||
var name = "eslint-plugin-react-web-api"; | ||
var version = "1.11.0-beta.4"; | ||
var version = "1.11.0-beta.7"; | ||
var createRule = shared.createRuleForPlugin("web-api"); | ||
@@ -32,18 +32,10 @@ | ||
function getCallKind(node) { | ||
return tsPattern.match(node.callee).with({ | ||
type: utils.AST_NODE_TYPES.MemberExpression, | ||
property: { | ||
type: utils.AST_NODE_TYPES.Identifier, | ||
name: tsPattern.P.select(tsPattern.P.union("addEventListener", "removeEventListener")) | ||
} | ||
}, tools.F.identity).with({ | ||
type: utils.AST_NODE_TYPES.Identifier, | ||
name: tsPattern.P.select(tsPattern.P.union("addEventListener", "removeEventListener")) | ||
}, tools.F.identity).with({ | ||
type: utils.AST_NODE_TYPES.MemberExpression, | ||
property: { | ||
type: utils.AST_NODE_TYPES.Identifier, | ||
name: "abort" | ||
} | ||
}, tools.F.constant("abort")).otherwise(tools.F.constant("other")); | ||
switch (true) { | ||
case (node.callee.type === utils.AST_NODE_TYPES.Identifier && tsPattern.isMatching(tsPattern.P.union("addEventListener", "removeEventListener"), node.callee.name)): | ||
return node.callee.name; | ||
case (node.callee.type === utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === utils.AST_NODE_TYPES.Identifier && tsPattern.isMatching(tsPattern.P.union("addEventListener", "removeEventListener"), node.callee.property.name)): | ||
return node.callee.property.name; | ||
default: | ||
return "other"; | ||
} | ||
} | ||
@@ -93,3 +85,3 @@ function getFunctionKind(node) { | ||
docs: { | ||
description: "prevent calling `addEventListener` without a corresponding `removeEventListener` in the same component or hook." | ||
description: "enforce that every 'addEventListener' in a component or custom hook has a corresponding 'removeEventListener'." | ||
}, | ||
@@ -163,3 +155,3 @@ messages: { | ||
const listeners = callKind === "addEventListener" ? aEntries : rEntries; | ||
listeners.push({ ...opts, _: node, type, callee, listener, phase: fKind }); | ||
listeners.push({ ...opts, self: node, type, callee, listener, phase: fKind }); | ||
} | ||
@@ -178,3 +170,3 @@ break; | ||
messageId: "noLeakedEventListenerInEffect", | ||
node: aEntry._, | ||
node: aEntry.self, | ||
data: { | ||
@@ -189,3 +181,3 @@ effectMethodKind: "useEffect" | ||
messageId: "noLeakedEventListenerInLifecycle", | ||
node: aEntry._ | ||
node: aEntry.self | ||
}); | ||
@@ -200,2 +192,144 @@ continue; | ||
}); | ||
var RULE_NAME2 = "no-leaked-timeout"; | ||
var functionKindPairs2 = birecord__default.default({ | ||
mount: "unmount", | ||
setup: "cleanup" | ||
}); | ||
function getCallKind2(node) { | ||
switch (true) { | ||
case (node.callee.type === utils.AST_NODE_TYPES.Identifier && tsPattern.isMatching(tsPattern.P.union("setTimeout", "clearTimeout"), node.callee.name)): | ||
return node.callee.name; | ||
case (node.callee.type === utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === utils.AST_NODE_TYPES.Identifier && tsPattern.isMatching(tsPattern.P.union("setTimeout", "clearTimeout"), node.callee.property.name)): | ||
return node.callee.property.name; | ||
default: | ||
return "other"; | ||
} | ||
} | ||
function getFunctionKind2(node) { | ||
return tsPattern.match(node).when(core.isSetupFunction, () => "setup").when(core.isCleanupFunction, () => "cleanup").when(core.isComponentDidMountFunction, () => "mount").when(core.isComponentWillUnmountFunction, () => "unmount").otherwise(() => "other"); | ||
} | ||
function getTimeoutID(node, prev) { | ||
switch (true) { | ||
case (node.type === utils.AST_NODE_TYPES.VariableDeclarator && node.init === prev): | ||
return tools.O.some(node.id); | ||
case (node.type === utils.AST_NODE_TYPES.AssignmentExpression && node.right === prev): | ||
return tools.O.some(node.left); | ||
case (node.type === utils.AST_NODE_TYPES.BlockStatement || node.type === utils.AST_NODE_TYPES.Program || node.parent === node): | ||
return tools.O.none(); | ||
default: | ||
return getTimeoutID(node.parent, node); | ||
} | ||
} | ||
var no_leaked_timeout_default = createRule({ | ||
meta: { | ||
type: "problem", | ||
docs: { | ||
description: "enforce that every 'setTimeout' in a component or custom hook has a corresponding 'clearTimeout'." | ||
}, | ||
messages: { | ||
noLeakedTimeoutInEffect: "'setTimeout' must be paired with 'clearTimeout' in {{kind}}", | ||
noLeakedTimeoutInLifecycle: "'setTimeout' must be paired with 'clearTimeout' in {{kind}}", | ||
noLeakedTimeoutNoTimeoutId: "'setTimeout' must have a timeout ID assigned to a variable" | ||
}, | ||
schema: [] | ||
}, | ||
name: RULE_NAME2, | ||
create(context) { | ||
const fStack = []; | ||
const sEntries = []; | ||
const rEntries = []; | ||
const isPairedEntry = tools.F.dual(2, (a, b) => { | ||
const aTimeoutID = a.timeoutID; | ||
const bTimeoutID = b.timeoutID; | ||
const aTimeoutIDScope = context.sourceCode.getScope(aTimeoutID); | ||
const bTimeoutIDScope = context.sourceCode.getScope(bTimeoutID); | ||
switch (true) { | ||
case (aTimeoutID.type === utils.AST_NODE_TYPES.Identifier && bTimeoutID.type === utils.AST_NODE_TYPES.Identifier): { | ||
return _var.isNodeValueEqual(aTimeoutID, bTimeoutID, [aTimeoutIDScope, bTimeoutIDScope]); | ||
} | ||
case (aTimeoutID.type === utils.AST_NODE_TYPES.AssignmentExpression && bTimeoutID.type === utils.AST_NODE_TYPES.AssignmentExpression): { | ||
return ast.isNodeEqual(aTimeoutID.left, bTimeoutID.left); | ||
} | ||
default: | ||
return _var.isNodeValueEqual(aTimeoutID, bTimeoutID, [aTimeoutIDScope, bTimeoutIDScope]); | ||
} | ||
}); | ||
return { | ||
[":function"](node) { | ||
const fKind = getFunctionKind2(node); | ||
fStack.push([node, fKind]); | ||
}, | ||
[":function:exit"]() { | ||
fStack.pop(); | ||
}, | ||
["CallExpression"](node) { | ||
const callKind = getCallKind2(node); | ||
switch (callKind) { | ||
case "setTimeout": { | ||
const [fNode, fKind] = fStack.at(-1) ?? []; | ||
if (!fNode || !fKind) break; | ||
if (!functionKindPairs2.has(fKind)) break; | ||
const timeoutIdNode = tools.O.getOrNull(getTimeoutID(node)); | ||
if (!timeoutIdNode) { | ||
context.report({ | ||
messageId: "noLeakedTimeoutNoTimeoutId", | ||
node | ||
}); | ||
break; | ||
} | ||
sEntries.push({ | ||
self: node, | ||
callee: node.callee, | ||
phase: fKind, | ||
timeoutID: timeoutIdNode | ||
}); | ||
break; | ||
} | ||
case "clearTimeout": { | ||
const [fNode, fKind] = fStack.at(-1) ?? []; | ||
if (!fNode || !fKind) break; | ||
if (!functionKindPairs2.has(fKind)) break; | ||
const [timeoutIdNode] = node.arguments; | ||
if (!timeoutIdNode) break; | ||
rEntries.push({ | ||
self: node, | ||
callee: node.callee, | ||
phase: fKind, | ||
timeoutID: timeoutIdNode | ||
}); | ||
break; | ||
} | ||
} | ||
}, | ||
["Program:exit"]() { | ||
for (const sEntry of sEntries) { | ||
if (rEntries.some(isPairedEntry(sEntry))) continue; | ||
switch (sEntry.phase) { | ||
case "setup": | ||
case "cleanup": | ||
context.report({ | ||
messageId: "noLeakedTimeoutInEffect", | ||
node: sEntry.self, | ||
data: { | ||
kind: "useEffect" | ||
} | ||
}); | ||
continue; | ||
case "mount": | ||
case "unmount": | ||
context.report({ | ||
messageId: "noLeakedTimeoutInLifecycle", | ||
node: sEntry.self, | ||
data: { | ||
kind: "componentDidMount" | ||
} | ||
}); | ||
continue; | ||
} | ||
} | ||
} | ||
}; | ||
}, | ||
defaultOptions: [] | ||
}); | ||
@@ -208,3 +342,4 @@ // src/index.ts | ||
var rules = { | ||
"no-leaked-event-listener": no_leaked_event_listener_default | ||
"no-leaked-event-listener": no_leaked_event_listener_default, | ||
"no-leaked-timeout": no_leaked_timeout_default | ||
}; | ||
@@ -211,0 +346,0 @@ var DEFAULT_ESLINT_REACT_SETTINGS = { |
{ | ||
"name": "eslint-plugin-react-web-api", | ||
"version": "1.11.0-beta.4", | ||
"version": "1.11.0-beta.7", | ||
"description": "ESLint React's ESLint plugin for interacting with Web APIs", | ||
@@ -45,15 +45,14 @@ "keywords": [ | ||
"dependencies": { | ||
"@typescript-eslint/scope-manager": "^8.1.0", | ||
"@typescript-eslint/types": "^8.1.0", | ||
"@typescript-eslint/utils": "^8.1.0", | ||
"@typescript-eslint/scope-manager": "^8.2.0", | ||
"@typescript-eslint/types": "^8.2.0", | ||
"@typescript-eslint/utils": "^8.2.0", | ||
"birecord": "^0.1.1", | ||
"remeda": "^2.11.0", | ||
"ts-pattern": "^5.3.1", | ||
"@eslint-react/core": "1.11.0-beta.4", | ||
"@eslint-react/shared": "1.11.0-beta.4", | ||
"@eslint-react/ast": "1.11.0-beta.4", | ||
"@eslint-react/jsx": "1.11.0-beta.4", | ||
"@eslint-react/tools": "1.11.0-beta.4", | ||
"@eslint-react/types": "1.11.0-beta.4", | ||
"@eslint-react/var": "1.11.0-beta.4" | ||
"@eslint-react/ast": "1.11.0-beta.7", | ||
"@eslint-react/core": "1.11.0-beta.7", | ||
"@eslint-react/jsx": "1.11.0-beta.7", | ||
"@eslint-react/shared": "1.11.0-beta.7", | ||
"@eslint-react/types": "1.11.0-beta.7", | ||
"@eslint-react/var": "1.11.0-beta.7", | ||
"@eslint-react/tools": "1.11.0-beta.7" | ||
}, | ||
@@ -60,0 +59,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
35328
14
684
0
+ Added@eslint-react/ast@1.11.0-beta.7(transitive)
+ Added@eslint-react/core@1.11.0-beta.7(transitive)
+ Added@eslint-react/jsx@1.11.0-beta.7(transitive)
+ Added@eslint-react/shared@1.11.0-beta.7(transitive)
+ Added@eslint-react/tools@1.11.0-beta.7(transitive)
+ Added@eslint-react/types@1.11.0-beta.7(transitive)
+ Added@eslint-react/var@1.11.0-beta.7(transitive)
+ Added@typescript-eslint/scope-manager@8.23.0(transitive)
+ Added@typescript-eslint/type-utils@8.23.0(transitive)
+ Added@typescript-eslint/types@8.23.0(transitive)
+ Added@typescript-eslint/typescript-estree@8.23.0(transitive)
+ Added@typescript-eslint/utils@8.23.0(transitive)
+ Added@typescript-eslint/visitor-keys@8.23.0(transitive)
+ Addedimport-fresh@3.3.1(transitive)
+ Addedsemver@7.7.1(transitive)
+ Addedstring-ts@2.2.1(transitive)
+ Addedts-api-utils@2.0.1(transitive)
- Removedremeda@^2.11.0
- Removed@eslint-react/ast@1.11.0-beta.4(transitive)
- Removed@eslint-react/core@1.11.0-beta.4(transitive)
- Removed@eslint-react/jsx@1.11.0-beta.4(transitive)
- Removed@eslint-react/shared@1.11.0-beta.4(transitive)
- Removed@eslint-react/tools@1.11.0-beta.4(transitive)
- Removed@eslint-react/types@1.11.0-beta.4(transitive)
- Removed@eslint-react/var@1.11.0-beta.4(transitive)
- Removed@typescript-eslint/scope-manager@8.22.0(transitive)
- Removed@typescript-eslint/type-utils@8.22.0(transitive)
- Removed@typescript-eslint/types@8.22.0(transitive)
- Removed@typescript-eslint/typescript-estree@8.22.0(transitive)
- Removed@typescript-eslint/utils@8.22.0(transitive)
- Removed@typescript-eslint/visitor-keys@8.22.0(transitive)
- Removedimport-fresh@3.3.0(transitive)
- Removedremeda@2.20.0(transitive)
- Removedsemver@7.7.0(transitive)
- Removedstring-ts@2.2.0(transitive)
- Removedts-api-utils@2.0.0(transitive)
- Removedtype-fest@4.33.0(transitive)