eslint-plugin-chrome-extensions 2.12.2 → 2.12.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-chrome-extensions",
3
- "version": "2.12.2",
3
+ "version": "2.12.3",
4
4
  "description": "ESLint rules for Chrome extensions",
5
5
  "keywords": [
6
6
  "eslint",
@@ -65,13 +65,13 @@
65
65
  "katex": "^0.19.1"
66
66
  },
67
67
  "eslint": {
68
- "minimatch": "^11.0.5"
68
+ "minimatch": "^11.0.7"
69
69
  },
70
70
  "eslint-plugin-import-x": {
71
- "minimatch": "^11.0.5"
71
+ "minimatch": "^11.0.7"
72
72
  },
73
73
  "eslint-plugin-sonarjs": {
74
- "minimatch": "^11.0.5"
74
+ "minimatch": "^11.0.7"
75
75
  }
76
76
  },
77
77
  "dependencies": {
@@ -100,7 +100,7 @@
100
100
  "eslint-plugin-regexp": "^3.3.3",
101
101
  "eslint-plugin-security": "^4.2.1",
102
102
  "eslint-plugin-sonarjs": "^4.2.2",
103
- "eslint-plugin-woodpecker": "^1.1.4",
103
+ "eslint-plugin-woodpecker": "^1.1.6",
104
104
  "oxlint": "^1.93.0",
105
105
  "typescript": "npm:@typescript/typescript6@^6.0.2",
106
106
  "typescript-latest": "npm:typescript@^7.0.2"
@@ -2,7 +2,7 @@ import extension from '../utils/extension.js'
2
2
  import nodeUtils from '../utils/node.js'
3
3
 
4
4
  const re = {
5
- appendMethod: /^(?:append|appendChild|prepend|replaceChildren|insertBefore)$/,
5
+ appendMethod: /^(?:append|appendChild|insertBefore|prepend|replaceChildren)$/,
6
6
  invokeMethod: /^(?:apply|call)$/,
7
7
  styleName: /style/i
8
8
  }
@@ -80,10 +80,10 @@ export default {
80
80
  const root = extension.findRoot(context) ; if (!root) return {}
81
81
  const usedAPIs = collectUsedAPIs(manifest, root)
82
82
  function isUsed(permission) {
83
- let knownPermission = false
84
- for (const [, mappedPermission] of apis.permissionMap)
85
- if (mappedPermission == permission) { knownPermission = true ; break }
86
- if (!knownPermission) return true
83
+ let knownPerm = false
84
+ for (const [, mappedPerm] of apis.permissionMap)
85
+ if (mappedPerm == permission) { knownPerm = true ; break }
86
+ if (!knownPerm) return true
87
87
  for (const api of usedAPIs)
88
88
  if (apiUtils.findPermission(api) == permission) return true
89
89
  return false
@@ -96,7 +96,7 @@ export default {
96
96
  context.report({ loc, messageId: 'unusedPermission', data: { permission }})
97
97
  return {}
98
98
  }
99
- const permissionNodes = new Map()
99
+ const permNodes = new Map()
100
100
  return {
101
101
  '*'(node) {
102
102
  const keyNode = node.name || node.key ; if (!keyNode) return
@@ -108,12 +108,12 @@ export default {
108
108
  const valueNode = elem.value || elem,
109
109
  permission = valueNode?.value
110
110
  if (typeof permission != 'string') continue
111
- if (!permissionNodes.has(permission)) permissionNodes.set(permission, [])
112
- permissionNodes.get(permission).push(valueNode)
111
+ if (!permNodes.has(permission)) permNodes.set(permission, [])
112
+ permNodes.get(permission).push(valueNode)
113
113
  }
114
114
  },
115
115
  'Document:exit'() {
116
- for (const [permission, nodes] of permissionNodes) {
116
+ for (const [permission, nodes] of permNodes) {
117
117
  if (isUsed(permission)) continue
118
118
  for (const node of nodes)
119
119
  context.report({ node, messageId: 'unusedPermission', data: { permission }})
@@ -5,6 +5,24 @@ import manifestData from '../data/manifest.js'
5
5
 
6
6
  const defaultOptions = { checkListenerTopLevel: true, checkKeepaliveTimer: true, checkDOMglobals: true }
7
7
 
8
+ function isSelfRemovingListener(node) {
9
+ const listenerFn = node.arguments[0]
10
+ if (listenerFn?.type != 'FunctionExpression' || !listenerFn.id) return false
11
+ return (function check(node) {
12
+ if (!node || typeof node.type != 'string') return false
13
+ if (node.type == 'CallExpression'
14
+ && node.callee.type == 'MemberExpression'
15
+ && node.callee.property?.name == 'removeListener'
16
+ && node.arguments[0]?.type == 'Identifier'
17
+ && node.arguments[0].name == listenerFn.id.name
18
+ ) return true
19
+ return Object.keys(node).some(function(key) {
20
+ return key != 'parent' && ( // oxlint-disable-next-line security/detect-object-injection
21
+ Array.isArray(node[key]) ? node[key].some(check) : check(node[key]))
22
+ })
23
+ })(listenerFn.body)
24
+ }
25
+
8
26
  export default {
9
27
  meta: {
10
28
  type: 'problem',
@@ -45,6 +63,7 @@ export default {
45
63
  && node.callee.type == 'MemberExpression'
46
64
  && node.callee.property.name == 'addListener'
47
65
  && nodeUtils.isInsideNestedFunction(node)
66
+ && !isSelfRemovingListener(node)
48
67
  ) context.report({ node, messageId: 'listenerNotTopLevel' })
49
68
  if (checkKeepaliveTimer
50
69
  && node.callee.type == 'Identifier'