eslint-plugin-chrome-extensions 2.4.0 → 2.4.1

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
@@ -40,7 +40,7 @@ import chromeExtensions from 'eslint-plugin-chrome-extensions'
40
40
  export default [
41
41
  // other configs
42
42
  {
43
- files: ['**/*extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
43
+ files: ['**/extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
44
44
  plugins: { 'chrome-extensions': chromeExtensions },
45
45
  rules: {
46
46
  ...chromeExtensions.configs.recommended.rules,
@@ -55,11 +55,11 @@ export default [
55
55
  }
56
56
  },
57
57
  { files: ['**/*LICENSE*'], ...chromeExtensions.configs.license },
58
- { files: ['**/*extension/**/*.css'], ...chromeExtensions.configs.css },
59
- { files: ['**/*extension/**/*.html'], ...chromeExtensions.configs.html },
60
- { files: ['**/*extension/**/*.json'], ...chromeExtensions.configs.json },
61
- { files: ['**/*extension/**/*.json5'], ...chromeExtensions.configs.json5 },
62
- { files: ['**/*extension/**/*.jsonc'], ...chromeExtensions.configs.jsonc }
58
+ { files: ['**/extension/**/*.css'], ...chromeExtensions.configs.css },
59
+ { files: ['**/extension/**/*.html'], ...chromeExtensions.configs.html },
60
+ { files: ['**/extension/**/*.json'], ...chromeExtensions.configs.json },
61
+ { files: ['**/extension/**/*.json5'], ...chromeExtensions.configs.json5 },
62
+ { files: ['**/extension/**/*.jsonc'], ...chromeExtensions.configs.jsonc }
63
63
  ]
64
64
  ```
65
65
 
@@ -488,7 +488,7 @@ If you intentionally target HTTP endpoints.
488
488
 
489
489
  <details>
490
490
 
491
- <summary>Requires sender/origin validation + filtered <code>tabs.query()</code> + explicit <code>window.postMessage()</code> target.</summary>
491
+ <summary>Requires sender/origin validation + filtered <code>chrome.tabs.query()</code> + explicit <code>window.postMessage()</code> target.</summary>
492
492
 
493
493
  <h4 id="no-insecure-messaging-options">Options</h4>
494
494
 
@@ -728,9 +728,9 @@ If your `_locales/` is externally populated at build time.
728
728
  <img height=18 alt="[HTML]" src="https://img.shields.io/badge/HTML-a8d8ea?style=flat-square" align="middle"></a>
729
729
  <a title="Works in 'json/json' config block" href="#usage">
730
730
  <img height=18 alt="[JSON]" src="https://img.shields.io/badge/JSON-a8e6cf?style=flat-square" align="middle"></a>
731
- <a title="Works in 'json5' config block" href="#usage">
731
+ <a title="Works in 'json/json5' config block" href="#usage">
732
732
  <img height=18 alt="[JSON5]" src="https://img.shields.io/badge/JSON5-c7e6b8?style=flat-square" align="middle"></a>
733
- <a title="Works in 'jsonc' config block" href="#usage">
733
+ <a title="Works in 'json/jsonc' config block" href="#usage">
734
734
  <img height=18 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
735
735
  <a title="Accepts array w/ options" href="#usage">
736
736
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -760,7 +760,7 @@ If your `_locales/` is externally populated at build time.
760
760
  }
761
761
 
762
762
  // _locales/en/messages.json
763
- { "unrelated": { "message": "…" }}
763
+ { "unrelated": { "message": "sup" }}
764
764
  ```
765
765
 
766
766
  #### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
@@ -776,7 +776,7 @@ If your `_locales/` is externally populated at build time.
776
776
  }
777
777
 
778
778
  // _locales/en/messages.json
779
- - { "unrelated": { "message": "…" }}
779
+ - { "unrelated": { "message": "sup" }}
780
780
  + {
781
781
  + "greeting": { "message": "Hello" },
782
782
  + "extName": { "message": "My Extension" }
@@ -1135,8 +1135,8 @@ If your i18n keys are referenced dynamically.
1135
1135
  <a title="Works in 'css/css' config block" href="#usage"><img height=14 alt="[CSS]" src="https://img.shields.io/badge/CSS-dcd6f7?style=flat-square" align="middle"></a>
1136
1136
  <a title="Works in 'html/html' config block" href="#usage"><img height=14 alt="[HTML]" src="https://img.shields.io/badge/HTML-a8d8ea?style=flat-square" align="middle"></a>
1137
1137
  <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[JSON]" src="https://img.shields.io/badge/JSON-a8e6cf?style=flat-square" align="middle"></a>
1138
- <a title="Works in 'json5' config block" href="#usage"><img height=14 alt="[JSON5]" src="https://img.shields.io/badge/JSON5-c7e6b8?style=flat-square" align="middle"></a>
1139
- <a title="Works in 'jsonc' config block" href="#usage"><img height=14 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
1138
+ <a title="Works in 'json/json5' config block" href="#usage"><img height=14 alt="[JSON5]" src="https://img.shields.io/badge/JSON5-c7e6b8?style=flat-square" align="middle"></a>
1139
+ <a title="Works in 'json/jsonc' config block" href="#usage"><img height=14 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
1140
1140
  <a title="Accepts array w/ options" href="#no-missing-i18n-keys-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1141
1141
  - <img height=12 src="https://www.google.com/s2/favicons?domain=developer.chrome.com&sz=64">&ensp;<a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api/i18n">Chrome for Developers — `i18n`</a>
1142
1142
 
@@ -1228,20 +1228,18 @@ If you access the granted API through an alias or computed property rather than
1228
1228
 
1229
1229
  <summary>Requires a license file in the extension.</summary>
1230
1230
 
1231
- #### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
1231
+ #### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect structure
1232
1232
 
1233
1233
  ```diff
1234
- // files
1235
1234
  /extension/manifest.json
1236
1235
  !
1237
1236
  ```
1238
1237
 
1239
- #### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct code
1238
+ #### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/green.svg" align="bottom"> Correct structure
1240
1239
 
1241
1240
  ```diff
1242
- // files
1243
1241
  /extension/manifest.json
1244
- + /extension/LICENSE
1242
+ + /extension/LICENSE.md
1245
1243
  ```
1246
1244
 
1247
1245
  #### When not to use it
@@ -1403,9 +1401,9 @@ If you primarily target a browser that doesn't support `short_name`.
1403
1401
  <img height=18 alt="[HTML]" src="https://img.shields.io/badge/HTML-a8d8ea?style=flat-square" align="middle"></a>
1404
1402
  <a title="Works in 'json/json' config block" href="#usage">
1405
1403
  <img height=18 alt="[JSON]" src="https://img.shields.io/badge/JSON-a8e6cf?style=flat-square" align="middle"></a>
1406
- <a title="Works in 'json5' config block" href="#usage">
1404
+ <a title="Works in 'json/json5' config block" href="#usage">
1407
1405
  <img height=18 alt="[JSON5]" src="https://img.shields.io/badge/JSON5-c7e6b8?style=flat-square" align="middle"></a>
1408
- <a title="Works in 'jsonc' config block" href="#usage">
1406
+ <a title="Works in 'json/jsonc' config block" href="#usage">
1409
1407
  <img height=18 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
1410
1408
  <a title="Accepts array w/ options" href="#usage">
1411
1409
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -1776,7 +1774,7 @@ If your background context isn't a service worker.
1776
1774
  <h4 id="valid-url-fields-options">Options</h4>
1777
1775
 
1778
1776
  - `checkURLfields` (default `true`) – validate URL fields (<a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/chrome-settings-override">`chrome_settings_overrides`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/homepage-url">`homepage_url`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/oauth2">`oauth2.scopes`</a>, <a target="_blank" href="https://developer.chrome.com/docs/apps/autoupdate#update_url">`update_url`</a>)
1779
- - `checkMatchPatterns` (default `true`) – validate match patterns (in <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/content-scripts">`content_scripts`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/externally-connectable">`externally_connectable`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions">`host_permissions`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions">`optional_host_permissions`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources">`web_accessible_resources`</a>)
1777
+ - `checkMatchPatterns` (default `true`) – validate <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">match patterns</a> (in <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/content-scripts">`content_scripts`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/externally-connectable">`externally_connectable`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions">`host_permissions`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions">`optional_host_permissions`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources">`web_accessible_resources`</a>)
1780
1778
  - `allowedSchemes` (default `['https', 'file']`) – permitted URL schemes for <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">match patterns</a>
1781
1779
  - `allowAllURLs` (default `false`) – allow unrestricted patterns (<a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">`<all_urls>`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">`https://*/*`</a>, <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">`*://*/*`</a>)
1782
1780
 
@@ -1810,7 +1808,7 @@ If your background context isn't a service worker.
1810
1808
 
1811
1809
  #### When not to use it
1812
1810
 
1813
- If your URLs or match patterns are generated at build time.
1811
+ If your URLs or patterns are generated at build time.
1814
1812
 
1815
1813
  #### Introduced in
1816
1814
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-chrome-extensions",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "ESLint rules for Chrome extensions",
5
5
  "keywords": [
6
6
  "eslint",
@@ -66,12 +66,14 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@e18e/eslint-plugin": "^0.8.1",
69
+ "@eslint/css": "^2.0.0",
69
70
  "@eslint/js": "^10.0.1",
70
71
  "@eslint/json": "^2.1.1",
71
72
  "@eslint/markdown": "^7.5.1",
73
+ "@html-eslint/eslint-plugin": "^0.66.1",
72
74
  "@stylistic/eslint-plugin": "^5.10.0",
73
75
  "eslint": "^10.12.0",
74
- "eslint-plugin-constructors": "^1.4.2",
76
+ "eslint-plugin-constructors": "^1.4.3",
75
77
  "eslint-plugin-eslint-plugin": "^7.6.2",
76
78
  "eslint-plugin-import-x": "^4.17.1",
77
79
  "eslint-plugin-n": "^18.4.1",
@@ -36,9 +36,26 @@ export const manifestKeyOrder = [
36
36
  'theme_experiment', 'tts_engine', 'update_url', 'version_name', 'web_accessible_resources'
37
37
  ]
38
38
 
39
+ export const matchPaths = new Set([
40
+ 'content_scripts.matches', 'externally_connectable.matches',
41
+ 'host_permissions', 'optional_host_permissions', 'web_accessible_resources.matches'
42
+ ])
43
+
39
44
  export const mv2OnlyKeys = new Set(['browser_action', 'page_action'])
40
45
 
41
46
  export const remoteCodePatterns = [
42
47
  'eval', 'new-function', 'string-timer', 'dynamic-import', 'remote-script-src', 'execute-script-url']
43
48
 
44
49
  export const serviceWorkerKeepaliveMinMS = 30000
50
+
51
+ export const urlPaths = new Set([
52
+ 'homepage_url', 'update_url', 'oauth2.scopes',
53
+ 'chrome_settings_overrides.homepage',
54
+ 'chrome_settings_overrides.search_provider.alternate_urls',
55
+ 'chrome_settings_overrides.search_provider.favicon_url',
56
+ 'chrome_settings_overrides.search_provider.image_url',
57
+ 'chrome_settings_overrides.search_provider.instant_url',
58
+ 'chrome_settings_overrides.search_provider.search_url',
59
+ 'chrome_settings_overrides.search_provider.suggest_url',
60
+ 'chrome_settings_overrides.startup_pages'
61
+ ])
@@ -56,9 +56,9 @@ export default {
56
56
  const keyName = keyNode.value ?? keyNode.name
57
57
  if (keyName != 'permissions' && keyName != 'optional_permissions') return
58
58
  const arrNode = node.value ; if (arrNode?.type != 'Array') return
59
- const elements = arrNode.elements || arrNode.items || []
60
- for (const el of elements) {
61
- const valueNode = el.value || el
59
+ const elems = arrNode.elements || arrNode.items || []
60
+ for (const elem of elems) {
61
+ const valueNode = elem.value || elem
62
62
  checkPermission(valueNode, valueNode?.value)
63
63
  }
64
64
  }
@@ -64,9 +64,9 @@ export default {
64
64
  const keyName = keyNode.value ?? keyNode.name
65
65
  if (keyName != 'permissions' && keyName != 'optional_permissions') return
66
66
  const arrNode = node.value ; if (arrNode?.type != 'Array') return
67
- const elements = arrNode.elements || arrNode.items || []
68
- for (const el of elements) {
69
- const valueNode = el.value || el
67
+ const elems = arrNode.elements || arrNode.items || []
68
+ for (const elem of elems) {
69
+ const valueNode = elem.value || elem
70
70
  const perm = valueNode?.value
71
71
  if (typeof perm != 'string') continue
72
72
  if (!permissionNodes.has(perm)) permissionNodes.set(perm, [])
@@ -62,6 +62,7 @@ export default {
62
62
  category: 'Possible Errors',
63
63
  recommended: true,
64
64
  manifest: true,
65
+ css: true,
65
66
  html: true,
66
67
  json: true
67
68
  },
@@ -64,12 +64,12 @@ export default {
64
64
  data: { directive }})
65
65
  if (inDataCollection && (keyName == 'required' || keyName == 'optional')
66
66
  && Array.isArray(node.value?.elements)
67
- ) for (const el of node.value.elements) {
68
- const value = typeof el.value == 'string' ? el.value : el.value?.value
67
+ ) for (const elem of node.value.elements) {
68
+ const value = typeof elem.value == 'string' ? elem.value : elem.value?.value
69
69
  if (typeof value == 'string') {
70
70
  const set = keyName == 'required' ? ffDataCollectionRequired : ffDataCollectionOptional
71
71
  if (!set.has(value))
72
- pendingReports.push({ node: el, messageId: 'invalidDataCollectionValue',
72
+ pendingReports.push({ node: elem, messageId: 'invalidDataCollectionValue',
73
73
  data: { value, kind: keyName }})
74
74
  }
75
75
  }
@@ -35,9 +35,10 @@ export default {
35
35
  ) context.report({ node, messageId: 'keepaliveTimer', data: { fn: node.callee.name }})
36
36
  },
37
37
  Identifier(node) {
38
- if (node.parent?.type == 'MemberExpression' && node.parent.property === node) return
39
- if (node.parent?.type == 'Property' && node.parent.key === node && !node.parent.computed) return
40
- if (!domGlobals.has(node.name)) return
38
+ if (node.parent?.type == 'MemberExpression' && node.parent.property === node
39
+ || node.parent?.type == 'Property' && node.parent.key === node && !node.parent.computed
40
+ || !domGlobals.has(node.name)
41
+ ) return
41
42
  for (let scope = context.sourceCode.getScope(node) ;
42
43
  scope && scope.type != 'global' ;
43
44
  scope = scope.upper
@@ -1,3 +1,4 @@
1
+ import { matchPaths, urlPaths } from '../data/manifest.js'
1
2
  import { getFilename } from '../utils.js'
2
3
 
3
4
  const re = {
@@ -5,28 +6,11 @@ const re = {
5
6
  match: /^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/, template: /\{[^}]+\}/g
6
7
  }
7
8
 
8
- const matchPaths = new Set([
9
- 'content_scripts.matches', 'externally_connectable.matches',
10
- 'host_permissions', 'optional_host_permissions', 'web_accessible_resources.matches'
11
- ])
12
-
13
- const urlPaths = new Set([
14
- 'homepage_url', 'update_url', 'oauth2.scopes',
15
- 'chrome_settings_overrides.homepage',
16
- 'chrome_settings_overrides.search_provider.alternate_urls',
17
- 'chrome_settings_overrides.search_provider.favicon_url',
18
- 'chrome_settings_overrides.search_provider.image_url',
19
- 'chrome_settings_overrides.search_provider.instant_url',
20
- 'chrome_settings_overrides.search_provider.search_url',
21
- 'chrome_settings_overrides.search_provider.suggest_url',
22
- 'chrome_settings_overrides.startup_pages'
23
- ])
24
-
25
9
  function collectStrings(obj, path = [], out = []) {
26
10
  if (obj == null) return out
27
11
  if (typeof obj == 'string') out.push({ path: path.join('.'), value: obj })
28
12
  else if (Array.isArray(obj))
29
- for (const el of obj) collectStrings(el, path, out)
13
+ for (const elem of obj) collectStrings(elem, path, out)
30
14
  else if (typeof obj == 'object')
31
15
  for (const key of Object.keys(obj)) // eslint-disable-next-line security/detect-object-injection
32
16
  collectStrings(obj[key], [...path, key], out)