eslint-plugin-chrome-extensions 2.11.2 → 2.12.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
@@ -127,14 +127,14 @@ export default defineConfig({
127
127
 
128
128
  <h4 id="css-spacing-options">Options</h4>
129
129
 
130
- - `spaces` (default `2`) – number of spaces per indent level <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
131
- - `checkBraces` (default `true`) – enforce 1 space after `{` and before `}` in single-line rules <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
132
- - `checkBrackets` (default `true`) – enforce 0 spaces after `[` and before `]` in attribute selectors <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
133
- - `checkColons` (default `true`) – enforce 0 spaces before and 1 space after `:` in declarations, and 1 space between value tokens <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
134
- - `checkCombinators` (default `true`) – enforce 1 space on each side of `>`, `+`, `~` in selectors <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
135
- - `checkCommas` (default `true`) – enforce 0 spaces before and 1 space after `,` in selector lists <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
136
- - `checkParentheses` (default `true`) – enforce 0 spaces after `(` and before `)` in functions and pseudo-classes <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
137
- - `checkSemicolons` (default `true`) – enforce 1 space after `;` in single-line declaration blocks <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
130
+ - `spaces` (default `2`) – number of spaces per indent level
131
+ - `checkBraces` (default `true`) – enforce 1 space after `{` and before `}` in single-line rules
132
+ - `checkBrackets` (default `true`) – enforce 0 spaces after `[` and before `]` in attribute selectors
133
+ - `checkColons` (default `true`) – enforce 0 spaces before and 1 space after `:` in declarations, and 1 space between value tokens
134
+ - `checkCombinators` (default `true`) – enforce 1 space on each side of `>`, `+`, `~` in selectors
135
+ - `checkCommas` (default `true`) – enforce 0 spaces before and 1 space after `,` in selector lists
136
+ - `checkParentheses` (default `true`) – enforce 0 spaces after `(` and before `)` in functions and pseudo-classes
137
+ - `checkSemicolons` (default `true`) – enforce 1 space after `;` in single-line declaration blocks
138
138
 
139
139
  #### <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
140
140
 
@@ -203,7 +203,7 @@ If you don't care about visual consistency in your CSS, or you already use Style
203
203
 
204
204
  <h4 id="manifest-key-order-options">Options</h4>
205
205
 
206
- - `order` (default `[]`) – manifest keys in custom order (e.g. `['name', 'manifest_version']`) to selectively override built-in order with <a title="Introduced in v2.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0"><img height=14 alt="[Since v2.0.0]" src="https://img.shields.io/badge/since-v2.0.0-8a9199?style=flat-square" align="middle"></a>
206
+ - `order` (default `[]`) – manifest keys in custom order (e.g. `['name', 'manifest_version']`) to selectively override built-in order with
207
207
 
208
208
  #### <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
209
209
 
@@ -278,11 +278,11 @@ If you don't care about the order of manifest keys.
278
278
 
279
279
  <h4 id="manifest-spacing-options">Options</h4>
280
280
 
281
- - `spaces` (default `2`) – number of spaces per indent level <a title="Introduced in v2.2.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.2.0"><img height=14 alt="[Since v2.2.0]" src="https://img.shields.io/badge/since-v2.2.0-8a9199?style=flat-square" align="middle"></a>
282
- - `checkColons` (default `true`) – enforce 0 spaces before and 1 space after `:` in key/value pairs <a title="Introduced in v2.2.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.2.0"><img height=14 alt="[Since v2.2.0]" src="https://img.shields.io/badge/since-v2.2.0-8a9199?style=flat-square" align="middle"></a>
283
- - `checkCommas` (default `true`) – enforce 0 spaces before and 1 space after `,` in inline objects <a title="Introduced in v2.2.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.2.0"><img height=14 alt="[Since v2.2.0]" src="https://img.shields.io/badge/since-v2.2.0-8a9199?style=flat-square" align="middle"></a>
284
- - `checkBraces` (default `true`) – enforce 1 space after `{` and before `}` in inline objects <a title="Introduced in v2.2.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.2.0"><img height=14 alt="[Since v2.2.0]" src="https://img.shields.io/badge/since-v2.2.0-8a9199?style=flat-square" align="middle"></a>
285
- - `checkBrackets` (default `true`) – enforce 0 spaces after `[` and before `]` in inline arrays <a title="Introduced in v2.2.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.2.0"><img height=14 alt="[Since v2.2.0]" src="https://img.shields.io/badge/since-v2.2.0-8a9199?style=flat-square" align="middle"></a>
281
+ - `spaces` (default `2`) – number of spaces per indent level
282
+ - `checkColons` (default `true`) – enforce 0 spaces before and 1 space after `:` in key/value pairs
283
+ - `checkCommas` (default `true`) – enforce 0 spaces before and 1 space after `,` in inline objects
284
+ - `checkBraces` (default `true`) – enforce 1 space after `{` and before `}` in inline objects
285
+ - `checkBrackets` (default `true`) – enforce 0 spaces after `[` and before `]` in inline arrays
286
286
 
287
287
  #### <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
288
288
 
@@ -417,8 +417,8 @@ If you intentionally interact w/ host page styles.
417
417
 
418
418
  <h4 id="no-deprecated-apis-options">Options</h4>
419
419
 
420
- - `target` (default `'mv3'`) – Chrome extension manifest version whose deprecations to validate against (`'mv3'`, `'mv2'`) <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
421
- - `allowedAPIs` (default `[]`) – additional deprecated API namespaces to allow, as a single string or array <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
420
+ - `target` (default `'mv3'`) – Chrome extension manifest version whose deprecations to validate against (`'mv3'`, `'mv2'`)
421
+ - `allowedAPIs` (default `[]`) – additional deprecated API namespaces to allow, as a single string or array
422
422
 
423
423
  #### <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
424
424
 
@@ -549,8 +549,8 @@ If you rely on a Chromium fork resolving duplicate keys in a specific order.
549
549
 
550
550
  <h4 id="no-global-keyframes-options">Options</h4>
551
551
 
552
- - `flagCommonNames` (default `true`) – flag well-known animation names (`spin`, `fade-in`, `slide-in-right`, etc.) that are most likely to collide w/ host pages <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
553
- - `requiredPrefix` (default `undefined`) – flag names not starting w/ this prefix <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
552
+ - `flagCommonNames` (default `true`) – flag well-known animation names (`spin`, `fade-in`, `slide-in-right`, etc.) that are most likely to collide w/ host pages
553
+ - `requiredPrefix` (default `undefined`) – flag names not starting w/ this prefix
554
554
 
555
555
  #### <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
556
556
 
@@ -611,10 +611,10 @@ If your CSS is scoped inside a Shadow DOM root.
611
611
 
612
612
  <h4 id="no-host-page-selectors-options">Options</h4>
613
613
 
614
- - `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body.dark']`), as a single string or array <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
615
- - `disallowUniversal` (default `true`) – flag `*` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
616
- - `disallowStructural` (default `true`) – flag `html`, `body`, `:root` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
617
- - `disallowBareElements` (default `true`) – flag elem types (e.g. `div`, `a`) that lack `class`/`id`/`attribute` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
614
+ - `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body.dark']`), as a single string or array
615
+ - `disallowUniversal` (default `true`) – flag `*`
616
+ - `disallowStructural` (default `true`) – flag `html`, `body`, `:root`
617
+ - `disallowBareElements` (default `true`) – flag elem types (e.g. `div`, `a`) that lack `class`/`id`/`attribute`
618
618
 
619
619
  #### <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
620
620
 
@@ -681,7 +681,7 @@ If you intentionally style the host page. For individual selectors you still nee
681
681
 
682
682
  <h4 id="no-inline-event-handlers-options">Options</h4>
683
683
 
684
- - `allowedAttributes` (default `[]`) – inline event handler attributes to permit (e.g. `['onafterprint']`), as a single string or array <a title="Introduced in v2.8.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0"><img height=14 alt="[Since v2.8.0]" src="https://img.shields.io/badge/since-v2.8.0-8a9199?style=flat-square" align="middle"></a>
684
+ - `allowedAttributes` (default `[]`) – inline event handler attributes to permit (e.g. `['onafterprint']`), as a single string or array
685
685
 
686
686
  #### <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
687
687
 
@@ -755,7 +755,7 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
755
755
 
756
756
  <h4 id="no-inline-scripts-options">Options</h4>
757
757
 
758
- - `allowedSrcPrefixes` (default `['chrome-extension://']`) – additional `src` prefixes to permit, as a single string or array <a title="Introduced in v2.8.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0"><img height=14 alt="[Since v2.8.0]" src="https://img.shields.io/badge/since-v2.8.0-8a9199?style=flat-square" align="middle"></a>
758
+ - `allowedSrcPrefixes` (default `['chrome-extension://']`) – additional `src` prefixes to permit, as a single string or array
759
759
 
760
760
  #### <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
761
761
 
@@ -884,7 +884,7 @@ If you intentionally target HTTP endpoints.
884
884
 
885
885
  <h4 id="no-insecure-messaging-options">Options</h4>
886
886
 
887
- - `disallowWildcardTarget` (default `true`) – flag <code>postMessage()</code> calls that use <code>'*'</code> as <code>targetOrigin</code> <a title="Introduced in v2.1.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0"><img height=14 alt="[Since v2.1.0]" src="https://img.shields.io/badge/since-v2.1.0-8a9199?style=flat-square" align="middle"></a>
887
+ - `disallowWildcardTarget` (default `true`) – flag <code>postMessage()</code> calls that use <code>'*'</code> as <code>targetOrigin</code>
888
888
 
889
889
  #### <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
890
890
 
@@ -969,7 +969,7 @@ If you trust every external caller and open tab, or you intentionally broadcast
969
969
 
970
970
  <h4 id="no-invalid-locales-options">Options</h4>
971
971
 
972
- - `allowedLocales` (default `[]`) – additional locale codes to allow, as a single string or array <a title="Introduced in v2.6.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.6.0"><img height=14 alt="[Since v2.6.0]" src="https://img.shields.io/badge/since-v2.6.0-8a9199?style=flat-square" align="middle"></a>
972
+ - `allowedLocales` (default `[]`) – additional locale codes to allow, as a single string or array
973
973
 
974
974
  #### <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
975
975
 
@@ -1044,8 +1044,8 @@ If your `_locales/` is externally populated at build time. If you target a Chrom
1044
1044
 
1045
1045
  <h4 id="no-invalid-permissions-options">Options</h4>
1046
1046
 
1047
- - `target` (default `'mv3'`) – Chrome manifest version whose permissions to validate against (`'mv3'`, `'mv2'`) <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
1048
- - `allowedPermissions` (default `[]`) – additional permission names to allow beyond the built-in list, as a single string or array <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
1047
+ - `target` (default `'mv3'`) – Chrome manifest version whose permissions to validate against (`'mv3'`, `'mv2'`)
1048
+ - `allowedPermissions` (default `[]`) – additional permission names to allow beyond the built-in list, as a single string or array
1049
1049
 
1050
1050
  #### <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
1051
1051
 
@@ -1116,8 +1116,8 @@ If you don't want permission validation at all. For custom permissions from a Ch
1116
1116
 
1117
1117
  <h4 id="no-missing-default-locale-options">Options</h4>
1118
1118
 
1119
- - `ignoreKeys` (default `[]`) – keys to skip when checking locales <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
1120
- - `checkAllLocales` (default `false`) – also flag keys missing from any locale <a title="Introduced in v2.6.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.6.0"><img height=14 alt="[Since v2.6.0]" src="https://img.shields.io/badge/since-v2.6.0-8a9199?style=flat-square" align="middle"></a>
1119
+ - `ignoreKeys` (default `[]`) – keys to skip when checking locales
1120
+ - `checkAllLocales` (default `false`) – also flag keys missing from any locale
1121
1121
 
1122
1122
  #### <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
1123
1123
 
@@ -1208,7 +1208,7 @@ If your `_locales/` is externally populated at build time.
1208
1208
 
1209
1209
  <h4 id="no-missing-i18n-keys-options">Options</h4>
1210
1210
 
1211
- - `ignoreKeys` (default `[]`) – keys to skip (e.g. `['customKey']`) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
1211
+ - `ignoreKeys` (default `[]`) – keys to skip (e.g. `['customKey']`)
1212
1212
 
1213
1213
  #### <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
1214
1214
 
@@ -1425,8 +1425,8 @@ If your paths are built at runtime.
1425
1425
 
1426
1426
  <h4 id="no-remote-code-options">Options</h4>
1427
1427
 
1428
- - `patterns` (default `['dynamic-import', 'eval', 'execute-script-url', 'inline-event-handler', 'inline-script-body', 'new-function', 'remote-script-src', 'string-timer']`) – detection paths to enable, as a single string or array <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
1429
- - `checkExecuteScriptClosures` (default `true`) – additionally flag <code>chrome.scripting.executeScript({ func })</code> bodies that capture outer-scope variables <a title="Introduced in v1.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v1.0.0"><img height=14 alt="[Since v1.0.0]" src="https://img.shields.io/badge/since-v1.0.0-8a9199?style=flat-square&labelColor=464646" align="middle"></a>
1428
+ - `patterns` (default `['dynamic-import', 'eval', 'execute-script-url', 'inline-event-handler', 'inline-script-body', 'new-function', 'remote-script-src', 'string-timer']`) – detection paths to enable, as a single string or array
1429
+ - `checkExecuteScriptClosures` (default `true`) – additionally flag <code>chrome.scripting.executeScript({ func })</code> bodies that capture outer-scope variables
1430
1430
 
1431
1431
  #### <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
1432
1432
 
@@ -1491,7 +1491,7 @@ If you intentionally target forks w/ relaxed CSPs. For individual patterns you s
1491
1491
 
1492
1492
  <h4 id="no-remote-objects-options">Options</h4>
1493
1493
 
1494
- - `allowedPrefixes` (default `['chrome-extension://']`) – additional URL prefixes to permit, as a single string or array <a title="Introduced in v2.8.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0"><img height=14 alt="[Since v2.8.0]" src="https://img.shields.io/badge/since-v2.8.0-8a9199?style=flat-square" align="middle"></a>
1494
+ - `allowedPrefixes` (default `['chrome-extension://']`) – additional URL prefixes to permit, as a single string or array
1495
1495
 
1496
1496
  #### <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
1497
1497
 
@@ -1557,8 +1557,8 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
1557
1557
 
1558
1558
  <h4 id="no-remote-urls-options">Options</h4>
1559
1559
 
1560
- - `allowedSchemes` (default `['blob', 'chrome-extension', 'data']`) – URL schemes to allow (beyond relative paths, which are always permitted), as a single string or array <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
1561
- - `patterns` (default `['at-import', 'url']`) – detection paths to enable, as a single string or array (`'at-import'`, `'url'`, `'namespace'`) <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
1560
+ - `allowedSchemes` (default `['blob', 'chrome-extension', 'data']`) – URL schemes to allow (beyond relative paths, which are always permitted), as a single string or array
1561
+ - `patterns` (default `['at-import', 'url']`) – detection paths to enable, as a single string or array (`'at-import'`, `'url'`, `'namespace'`)
1562
1562
 
1563
1563
  #### <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
1564
1564
 
@@ -1633,7 +1633,7 @@ If you intentionally reference remote stylesheets, fonts, or images (e.g. Google
1633
1633
 
1634
1634
  <h4 id="no-scrollbar-styling-options">Options</h4>
1635
1635
 
1636
- - `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body::-webkit-scrollbar']`), as a single string or array <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
1636
+ - `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body::-webkit-scrollbar']`), as a single string or array
1637
1637
 
1638
1638
  #### <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
1639
1639
 
@@ -1708,7 +1708,7 @@ If you intentionally theme the host page's scrollbar. For individual selectors y
1708
1708
 
1709
1709
  <h4 id="no-sensitive-permissions-options">Options</h4>
1710
1710
 
1711
- - `allowedPermissions` (default `[]`) – sensitive permission names to explicitly allow, as a single string or array <a title="Introduced in v2.5.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.5.0"><img height=14 alt="[Since v2.5.0]" src="https://img.shields.io/badge/since-v2.5.0-8a9199?style=flat-square" align="middle"></a>
1711
+ - `allowedPermissions` (default `[]`) – sensitive permission names to explicitly allow, as a single string or array
1712
1712
 
1713
1713
  #### <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
1714
1714
 
@@ -1779,7 +1779,7 @@ If you legitimately require sensitive permissions, or prefer manual security rev
1779
1779
 
1780
1780
  <h4 id="no-unrestricted-host-permissions-options">Options</h4>
1781
1781
 
1782
- - `allowedPatterns` (default `[]`) – host patterns to explicitly allow (e.g. `['<all_urls>']`) <a title="Introduced in v2.1.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0"><img height=14 alt="[Since v2.1.0]" src="https://img.shields.io/badge/since-v2.1.0-8a9199?style=flat-square" align="middle"></a>
1782
+ - `allowedPatterns` (default `[]`) – host patterns to explicitly allow (e.g. `['<all_urls>']`)
1783
1783
 
1784
1784
  #### <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
1785
1785
 
@@ -1843,8 +1843,8 @@ If you legitimately need broad access (e.g. ad blockers or dev tools).
1843
1843
 
1844
1844
  <h4 id="no-unused-i18n-keys-options">Options</h4>
1845
1845
 
1846
- - `ignoreKeys` (default `[]`) – keys to always treat as used (e.g. `['customKey']`) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
1847
- - `scanExtensions` (default `['cjs', 'css', 'htm', 'html', 'js', 'json', 'jsx', 'md', 'mjs', 'svg', 'ts', 'tsx', 'txt']`) – file extensions to scan for references <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
1846
+ - `ignoreKeys` (default `[]`) – keys to always treat as used (e.g. `['customKey']`)
1847
+ - `scanExtensions` (default `['cjs', 'css', 'htm', 'html', 'js', 'json', 'jsx', 'md', 'mjs', 'svg', 'ts', 'tsx', 'txt']`) – file extensions to scan for references
1848
1848
 
1849
1849
  #### <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
1850
1850
 
@@ -2098,9 +2098,9 @@ If you intentionally target MV2 for a browser that still supports it (e.g. Firef
2098
2098
 
2099
2099
  <h4 id="require-short-name-when-useful-options">Options</h4>
2100
2100
 
2101
- - `maxNameLength` (default `12`) – length of `name` above which `short_name` is required <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2102
- - `always` (default `false`) – require `short_name` regardless of `name` length <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2103
- - `checkRedundant` (default `false`) – also flag `short_name` that duplicates `name` <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2101
+ - `maxNameLength` (default `12`) – length of `name` above which `short_name` is required
2102
+ - `always` (default `false`) – require `short_name` regardless of `name` length
2103
+ - `checkRedundant` (default `false`) – also flag `short_name` that duplicates `name`
2104
2104
 
2105
2105
  #### <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
2106
2106
 
@@ -2173,11 +2173,11 @@ If you primarily target a browser that doesn't support `short_name`.
2173
2173
 
2174
2174
  <h4 id="valid-filenames-options">Options</h4>
2175
2175
 
2176
- - `noReservedPrefix` (default `true`) – disallow filenames/dirs starting w/ `_` (except `_locales`) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2177
- - `noSpecialChars` (default `true`) – warn on chars outside `a-z`, `0-9`, `-`, `_`, `.` <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2178
- - `checkFileExists` (default `true`) – verify files referenced in `manifest.json` actually exist <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2179
- - `enforceCaseSensitivity` (default `true`) – verify manifest references match actual file case <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2180
- - `maxFullPathLength` (default `260`) – max full path char length <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2176
+ - `noReservedPrefix` (default `true`) – disallow filenames/dirs starting w/ `_` (except `_locales`)
2177
+ - `noSpecialChars` (default `true`) – warn on chars outside `a-z`, `0-9`, `-`, `_`, `.`
2178
+ - `checkFileExists` (default `true`) – verify files referenced in `manifest.json` actually exist
2179
+ - `enforceCaseSensitivity` (default `true`) – verify manifest references match actual file case
2180
+ - `maxFullPathLength` (default `260`) – max full path char length
2181
2181
 
2182
2182
  #### <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 names/paths
2183
2183
 
@@ -2255,8 +2255,8 @@ If you don't use conventional filenames or paths, or they're generated at build
2255
2255
 
2256
2256
  <h4 id="valid-license-file-options">Options</h4>
2257
2257
 
2258
- - `requireSPDX` (default `false`) – require <a target="_blank" href="https://spdx.dev/learn/handling-license-info/">`SPDX-License-Identifier`</a> in the license <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2259
- - `allowedLicenses` (default `[]`) – <a target="_blank" href="https://spdx.org/licenses/">SPDX license IDs</a> to restrict to (e.g. `['Apache-2.0', 'MIT']`), as a single string or array <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2258
+ - `requireSPDX` (default `false`) – require <a target="_blank" href="https://spdx.dev/learn/handling-license-info/">`SPDX-License-Identifier`</a> in the license
2259
+ - `allowedLicenses` (default `[]`) – <a target="_blank" href="https://spdx.org/licenses/">SPDX license IDs</a> to restrict to (e.g. `['Apache-2.0', 'MIT']`), as a single string or array
2260
2260
 
2261
2261
  #### <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
2262
2262
 
@@ -2323,9 +2323,9 @@ If your license is injected at build time, or you use one w/ no existing SPDX id
2323
2323
 
2324
2324
  <h4 id="valid-manifest-icons-options">Options</h4>
2325
2325
 
2326
- - `requireRecommendedSizes` (default `true`) – require all of `16`, `32`, `48`, `128` <a title="Introduced in v2.1.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0"><img height=14 alt="[Since v2.1.0]" src="https://img.shields.io/badge/since-v2.1.0-8a9199?style=flat-square" align="middle"></a>
2327
- - `checkIconExists` (default `true`) – flag icon paths that don't resolve to an existing file <a title="Introduced in v2.1.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0"><img height=14 alt="[Since v2.1.0]" src="https://img.shields.io/badge/since-v2.1.0-8a9199?style=flat-square" align="middle"></a>
2328
- - `noSVG` (default `true`) – flag SVG icon paths (unsupported by Chrome) <a title="Introduced in v2.1.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.1.0"><img height=14 alt="[Since v2.1.0]" src="https://img.shields.io/badge/since-v2.1.0-8a9199?style=flat-square" align="middle"></a>
2326
+ - `requireRecommendedSizes` (default `true`) – require all of `16`, `32`, `48`, `128`
2327
+ - `checkIconExists` (default `true`) – flag icon paths that don't resolve to an existing file
2328
+ - `noSVG` (default `true`) – flag SVG icon paths (unsupported by Chrome)
2329
2329
 
2330
2330
  #### <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
2331
2331
 
@@ -2467,9 +2467,9 @@ If you intentionally target MV2 for a browser that still supports it (e.g. Firef
2467
2467
 
2468
2468
  <h4 id="valid-service-worker-options">Options</h4>
2469
2469
 
2470
- - `checkListenerTopLevel` (default `true`) – require `chrome.*.addListener()` calls at module top level <a title="Introduced in v2.5.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.5.0"><img height=14 alt="[Since v2.5.0]" src="https://img.shields.io/badge/since-v2.5.0-8a9199?style=flat-square" align="middle"></a>
2471
- - `checkKeepaliveTimer` (default `true`) – flag sub-30s timers used as a keepalive workaround <a title="Introduced in v2.5.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.5.0"><img height=14 alt="[Since v2.5.0]" src="https://img.shields.io/badge/since-v2.5.0-8a9199?style=flat-square" align="middle"></a>
2472
- - `checkDOMglobals` (default `true`) – flag DOM globals (`window`, `document`, `localStorage`) inside the service worker <a title="Introduced in v2.5.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.5.0"><img height=14 alt="[Since v2.5.0]" src="https://img.shields.io/badge/since-v2.5.0-8a9199?style=flat-square" align="middle"></a>
2470
+ - `checkListenerTopLevel` (default `true`) – require `chrome.*.addListener()` calls at module top level
2471
+ - `checkKeepaliveTimer` (default `true`) – flag sub-30s timers used as a keepalive workaround
2472
+ - `checkDOMglobals` (default `true`) – flag DOM globals (`window`, `document`, `localStorage`) inside the service worker
2473
2473
 
2474
2474
  #### <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
2475
2475
 
@@ -2540,10 +2540,10 @@ If your background context isn't a service worker.
2540
2540
 
2541
2541
  <h4 id="valid-url-fields-options">Options</h4>
2542
2542
 
2543
- - `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>) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2544
- - `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>) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2545
- - `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> <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2546
- - `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>) <a title="Introduced in v2.4.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0"><img height=14 alt="[Since v2.4.0]" src="https://img.shields.io/badge/since-v2.4.0-8a9199?style=flat-square" align="middle"></a>
2543
+ - `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>)
2544
+ - `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>)
2545
+ - `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>
2546
+ - `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>)
2547
2547
 
2548
2548
  #### <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
2549
2549
 
@@ -2615,9 +2615,9 @@ If your URLs or patterns are generated at build time.
2615
2615
 
2616
2616
  <h4 id="valid-version-field-options">Options</h4>
2617
2617
 
2618
- - `versionType` (default `'chrome'`) – version format to enforce (`'chrome'`, `'semver'`, `'date'`) <a title="Introduced in v2.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0"><img height=14 alt="[Since v2.0.0]" src="https://img.shields.io/badge/since-v2.0.0-8a9199?style=flat-square" align="middle"></a>
2619
- - `allowFutureDate` (default `false`) – allow `version` dates later than the current date <a title="Introduced in v2.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0"><img height=14 alt="[Since v2.0.0]" src="https://img.shields.io/badge/since-v2.0.0-8a9199?style=flat-square" align="middle"></a>
2620
- - `timezone` (default `''`) – <a target="_blank" href="https://data.iana.org/time-zones/tzdb/zone1970.tab">IANA timezone</a> used to determine the current date (when omitted, only year/month are enforced) <a title="Introduced in v2.0.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.0.0"><img height=14 alt="[Since v2.0.0]" src="https://img.shields.io/badge/since-v2.0.0-8a9199?style=flat-square" align="middle"></a>
2618
+ - `versionType` (default `'chrome'`) – version format to enforce (`'chrome'`, `'semver'`, `'date'`)
2619
+ - `allowFutureDate` (default `false`) – allow `version` dates later than the current date
2620
+ - `timezone` (default `''`) – <a target="_blank" href="https://data.iana.org/time-zones/tzdb/zone1970.tab">IANA timezone</a> used to determine the current date (when omitted, only year/month are enforced)
2621
2621
 
2622
2622
  #### <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
2623
2623
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-chrome-extensions",
3
- "version": "2.11.2",
3
+ "version": "2.12.1",
4
4
  "description": "ESLint rules for Chrome extensions",
5
5
  "keywords": [
6
6
  "eslint",
@@ -46,17 +46,24 @@ export default {
46
46
  'content_scripts.matches', 'externally_connectable.matches', 'host_permissions',
47
47
  'optional_host_permissions', 'web_accessible_resources.matches'
48
48
  ]),
49
- url: new Set([
50
- 'homepage_url', 'update_url', 'oauth2.scopes',
51
- 'chrome_settings_overrides.homepage',
52
- 'chrome_settings_overrides.search_provider.alternate_urls',
53
- 'chrome_settings_overrides.search_provider.favicon_url',
54
- 'chrome_settings_overrides.search_provider.image_url',
55
- 'chrome_settings_overrides.search_provider.instant_url',
56
- 'chrome_settings_overrides.search_provider.search_url',
57
- 'chrome_settings_overrides.search_provider.suggest_url',
58
- 'chrome_settings_overrides.startup_pages'
59
- ])
49
+ url: {
50
+ all: new Set([
51
+ 'homepage_url', 'update_url', 'oauth2.scopes',
52
+ 'chrome_settings_overrides.homepage',
53
+ 'chrome_settings_overrides.search_provider.alternate_urls',
54
+ 'chrome_settings_overrides.search_provider.favicon_url',
55
+ 'chrome_settings_overrides.search_provider.image_url',
56
+ 'chrome_settings_overrides.search_provider.instant_url',
57
+ 'chrome_settings_overrides.search_provider.search_url',
58
+ 'chrome_settings_overrides.search_provider.suggest_url',
59
+ 'chrome_settings_overrides.startup_pages'
60
+ ]),
61
+ relInFF: new Set([
62
+ 'chrome_settings_overrides.homepage',
63
+ 'chrome_settings_overrides.search_provider.favicon_url'
64
+ ]),
65
+ noI18n: new Set(['homepage_url', 'update_url', 'oauth2.scopes'])
66
+ }
60
67
  },
61
68
  patterns: {
62
69
  remoteCode: [
@@ -5,11 +5,6 @@ import regex from '../data/regex.js'
5
5
 
6
6
  const defaultOptions = { order: [] }
7
7
 
8
- function keyOf(member) {
9
- const keyNode = member.name || member.key
10
- return keyNode?.value ?? keyNode?.name
11
- }
12
-
13
8
  function findViolation(members, keys) {
14
9
  const ranks = new Map(keys.map((key, index) => [key, index])),
15
10
  known = members.map(member => {
@@ -17,7 +12,26 @@ function findViolation(members, keys) {
17
12
  return { member, key, rank: ranks.get(key) }
18
13
  }).filter(item => item.rank !== undefined)
19
14
  for (let i = 1 ; i < known.length ; i++) // oxlint-disable-next-line security/detect-object-injection
20
- if (known[i -1].rank > known[i].rank) return [known[i -1], known[i]]
15
+ if (known[i - 1].rank > known[i].rank) return [known[i - 1], known[i]]
16
+ }
17
+ function isSubsequence(sub, arr) {
18
+ let i = 0
19
+ for (const item of arr) if (item === sub[i]) i++ // oxlint-disable-line security/detect-object-injection
20
+ return i === sub.length
21
+ }
22
+ function keyOf(member) {
23
+ const keyNode = member.name || member.key
24
+ return keyNode?.value ?? keyNode?.name
25
+ }
26
+ function mergeOrder(master, custom) {
27
+ if (!custom.length || isSubsequence(custom, master)) return master
28
+ const customSet = new Set(custom),
29
+ insertAt = Math.min(...custom.map(key => master.indexOf(key)).filter(idx => idx >= 0))
30
+ return [
31
+ ...master.slice(0, insertAt),
32
+ ...custom,
33
+ ...master.slice(insertAt).filter(key => !customSet.has(key))
34
+ ]
21
35
  }
22
36
 
23
37
  export default {
@@ -46,14 +60,12 @@ export default {
46
60
  create(context) {
47
61
  if (!regex.filename.manifest.test(extension.file.getName(context))) return {}
48
62
  const { order } = { ...defaultOptions, ...context.options[0] },
49
- master = order.length ? manifest.keys.order.filter(key => !order.includes(key)) : manifest.keys.order
63
+ keys = mergeOrder(manifest.keys.order, order)
50
64
  return {
51
65
  'Document:exit'(document) {
52
66
  const root = document.body ; if (root?.type != 'Object') return
53
67
  const members = root.members || root.properties || []
54
- let keys = master, violation = findViolation(members, master)
55
- if (!violation && order.length) { keys = order ; violation = findViolation(members, order) }
56
- if (!violation) return
68
+ const violation = findViolation(members, keys) ; if (!violation) return
57
69
  const [first, second] = violation
58
70
  context.report({
59
71
  node: second.member, messageId: 'wrongOrder',
@@ -1,18 +1,30 @@
1
1
  import extension from '../utils/extension.js'
2
2
  import nodeUtils from '../utils/node.js'
3
3
 
4
- const re = { appendMethod: /^append(?:Child)?$/ }
4
+ const re = {
5
+ appendMethod: /^(?:append|appendChild|prepend|replaceChildren|insertBefore)$/,
6
+ invokeMethod: /^(?:apply|call)$/,
7
+ styleName: /style/i
8
+ }
5
9
 
10
+ function flattenArgs(args) {
11
+ return args.filter(Boolean).flatMap(arg =>
12
+ arg.type != 'SpreadElement' ? [arg]
13
+ : arg.argument?.type == 'ArrayExpression' ? arg.argument.elements.filter(Boolean)
14
+ : [arg.argument]
15
+ )
16
+ }
17
+ function getPropName(node) { return node?.name ?? node?.value }
6
18
  function isCreateStyle(node) {
7
19
  return node?.type == 'CallExpression'
8
- && node.callee?.property?.name == 'createElement'
20
+ && getPropName(node.callee?.property) == 'createElement'
9
21
  && nodeUtils.getStringLiteral(node.arguments[0]) == 'style'
10
22
  }
11
23
  function isObjectAssign(node) {
12
24
  return node?.type == 'CallExpression'
13
25
  && node.callee?.type == 'MemberExpression'
14
26
  && node.callee.object?.name == 'Object'
15
- && node.callee.property?.name == 'assign'
27
+ && getPropName(node.callee.property) == 'assign'
16
28
  }
17
29
 
18
30
  export default {
@@ -37,33 +49,60 @@ export default {
37
49
  return node?.type == 'Identifier'
38
50
  && (node.name == 'document' || docAliases.has(node.name))
39
51
  }
40
- function isDocumentMember(node) {
41
- return node?.type == 'MemberExpression' && isDocumentRef(node.object)
52
+ function isDocRooted(node) {
53
+ if (!node) return false
54
+ if (node.type == 'Identifier') return isDocumentRef(node)
55
+ if (node.type == 'MemberExpression')
56
+ return node.object?.type == 'Identifier'
57
+ && (node.object.name == 'window' || node.object.name == 'self')
58
+ && getPropName(node.property) == 'document'
59
+ || isDocRooted(node.object)
60
+ if (node.type == 'CallExpression' && getPropName(node.callee?.property) != 'createElement')
61
+ return isDocRooted(node.callee)
62
+ return false
42
63
  }
43
64
  function isStyleTarget(node) {
44
- if (isCreateStyle(node)) return true
45
- if (node?.type == 'Identifier' && styleElems.has(node.name)) return true
46
- if (isObjectAssign(node) && isCreateStyle(node.arguments[0])) return true
47
- if (isObjectAssign(node)
48
- && node.arguments[0]?.type == 'Identifier'
49
- && styleElems.has(node.arguments[0].name)
65
+ if (!node) return false
66
+ if (isCreateStyle(node)
67
+ || node.type == 'Identifier' && (styleElems.has(node.name) || re.styleName.test(node.name))
50
68
  ) return true
69
+ if (isObjectAssign(node)) {
70
+ const target = node.arguments[0]
71
+ if (isCreateStyle(target)
72
+ || target?.type == 'Identifier' && styleElems.has(target.name)
73
+ ) return true
74
+ }
51
75
  return false
52
76
  }
77
+ function addAliases(pattern, source) {
78
+ if (!isDocRooted(source)) return
79
+ if (pattern.type == 'Identifier') docAliases.add(pattern.name)
80
+ else if (pattern.type == 'ObjectPattern')
81
+ for (const prop of pattern.properties) {
82
+ if (prop.type != 'Property') continue
83
+ if (prop.value?.type == 'Identifier') docAliases.add(prop.value.name)
84
+ }
85
+ }
53
86
  return {
54
87
  VariableDeclarator(node) {
55
- if (node.id.type != 'Identifier') return
56
- if (isCreateStyle(nodeUtils.unwrapTS(node.init))) styleElems.add(node.id.name)
57
- if (nodeUtils.unwrapTS(node.init)?.type == 'Identifier' && isDocumentRef(node.init))
58
- docAliases.add(node.id.name)
88
+ const init = nodeUtils.unwrapTS(node.init)
89
+ if (node.id.type == 'Identifier' && isCreateStyle(init)) styleElems.add(node.id.name)
90
+ addAliases(node.id, init)
59
91
  },
60
92
  CallExpression(node) {
61
93
  if (node.callee.type != 'MemberExpression') return
62
- const method = node.callee.property.name
63
- if (!re.appendMethod.test(method)
64
- || !isDocumentMember(node.callee.object)
94
+ const outerMethod = getPropName(node.callee.property),
95
+ isInvoke = re.invokeMethod.test(outerMethod),
96
+ callee = isInvoke ? node.callee.object : node.callee
97
+ if (callee?.type != 'MemberExpression'
98
+ || !re.appendMethod.test(getPropName(callee.property))
99
+ || !isDocRooted(callee.object)
65
100
  ) return
66
- for (const arg of node.arguments)
101
+ const args = !isInvoke ? node.arguments
102
+ : outerMethod == 'apply'
103
+ ? node.arguments[1]?.type == 'ArrayExpression' ? node.arguments[1].elements : []
104
+ : node.arguments.slice(1)
105
+ for (const arg of flattenArgs(args))
67
106
  if (isStyleTarget(arg))
68
107
  context.report({ node: arg, messageId: 'styleLeak' })
69
108
  }
@@ -6,27 +6,57 @@ import path from 'node:path'
6
6
  import apis from '../data/apis.js'
7
7
  import regex from '../data/regex.js'
8
8
 
9
- function collectUsedAPIs(manifest, root) {
10
- const files = new Set()
11
- if (manifest.background?.service_worker) files.add(manifest.background.service_worker)
12
- for (const script of manifest.background?.scripts || []) files.add(script)
9
+ const re = {
10
+ chromeAPI: /\bchrome\.\w+(?:\.\w+)*/g, // oxlint-disable-line security/detect-unsafe-regex
11
+ htmlExt: /\.html?$/i,
12
+ htmlScriptSrc: /<script\b[^>]*?\ssrc\s*=\s*(?:"([^"]+)"|'([^']+)')/gi,
13
+ urlLike: /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i
14
+ }
15
+
16
+ function collectScannableFiles(manifest) {
17
+ const files = new Set(),
18
+ add = file => { if (typeof file == 'string' && file) files.add(file) }
19
+ add(manifest.background?.service_worker)
20
+ add(manifest.background?.page)
21
+ for (const script of manifest.background?.scripts || []) add(script)
13
22
  for (const contentScript of manifest.content_scripts || [])
14
- for (const file of contentScript.js || []) files.add(file)
15
- if (manifest.action?.default_popup) files.add(manifest.action.default_popup)
16
- if (manifest.options_ui?.page) files.add(manifest.options_ui.page)
17
- if (manifest.devtools_page) files.add(manifest.devtools_page)
18
- const re = { /* oxlint-disable-next-line security/detect-unsafe-regex */
19
- chromeAPI: /\bchrome\.\w+(?:\.\w+)*/g }
20
- const used = new Set()
21
- for (const file of files) {
22
- try {
23
- const src = // oxlint-disable-next-line security/detect-non-literal-fs-filename
24
- fs.readFileSync(path.resolve(root, file), 'utf8')
25
- for (const match of src.matchAll(re.chromeAPI)) used.add(match[0])
26
- } catch {}
23
+ for (const file of contentScript.js || []) add(file)
24
+ add(manifest.action?.default_popup)
25
+ add(manifest.browser_action?.default_popup)
26
+ add(manifest.page_action?.default_popup)
27
+ add(manifest.options_ui?.page)
28
+ add(manifest.options_page)
29
+ add(manifest.devtools_page)
30
+ add(manifest.side_panel?.default_path)
31
+ for (const page of Object.values(manifest.chrome_url_overrides || {})) add(page)
32
+ return files
33
+ }
34
+ function collectUsedAPIs(manifest, root) {
35
+ const queue = [...collectScannableFiles(manifest)],
36
+ visited = new Set(),
37
+ used = new Set()
38
+ while (queue.length) {
39
+ const relPath = queue.shift() ; if (!relPath) continue
40
+ const absPath = path.resolve(root, relPath) ; if (visited.has(absPath)) continue
41
+ visited.add(absPath)
42
+ let src
43
+ try { src = fs.readFileSync(absPath, 'utf8') } // oxlint-disable-line security/detect-non-literal-fs-filename
44
+ catch { continue }
45
+ for (const match of src.matchAll(re.chromeAPI)) used.add(match[0])
46
+ if (re.htmlExt.test(relPath))
47
+ for (const nested of extractScriptSrcs(src, relPath)) queue.push(nested)
27
48
  }
28
49
  return used
29
50
  }
51
+ function extractScriptSrcs(html, htmlRelPath) {
52
+ const srcs = []
53
+ for (const match of html.matchAll(re.htmlScriptSrc)) {
54
+ const src = (match[1] ?? match[2])?.trim()
55
+ if (!src || re.urlLike.test(src)) continue
56
+ srcs.push(src.startsWith('/') ? src.slice(1) : path.join(path.dirname(htmlRelPath), src))
57
+ }
58
+ return srcs
59
+ }
30
60
 
31
61
  export default {
32
62
  meta: {
@@ -50,10 +80,10 @@ export default {
50
80
  const root = extension.findRoot(context) ; if (!root) return {}
51
81
  const usedAPIs = collectUsedAPIs(manifest, root)
52
82
  function isUsed(permission) {
53
- let knownPerm = false
54
- for (const [, perm] of apis.permissionMap)
55
- if (perm == permission) { knownPerm = true ; break }
56
- if (!knownPerm) return true
83
+ let knownPermission = false
84
+ for (const [, mappedPermission] of apis.permissionMap)
85
+ if (mappedPermission == permission) { knownPermission = true ; break }
86
+ if (!knownPermission) return true
57
87
  for (const api of usedAPIs)
58
88
  if (apiUtils.findPermission(api) == permission) return true
59
89
  return false
@@ -72,9 +102,9 @@ export default {
72
102
  const keyNode = node.name || node.key ; if (!keyNode) return
73
103
  const keyName = keyNode.value ?? keyNode.name
74
104
  if (keyName != 'permissions' && keyName != 'optional_permissions') return
75
- const arrNode = node.value ; if (arrNode?.type != 'Array') return
76
- const nodeElems = arrNode.elements || arrNode.items || []
77
- for (const elem of nodeElems) {
105
+ const arrayNode = node.value ; if (arrayNode?.type != 'Array') return
106
+ const elems = arrayNode.elements || arrayNode.items || []
107
+ for (const elem of elems) {
78
108
  const valueNode = elem.value || elem,
79
109
  permission = valueNode?.value
80
110
  if (typeof permission != 'string') continue
@@ -58,8 +58,9 @@ export default {
58
58
  allowedLicenses = array.to(rawAllowedLicenses),
59
59
  { filename } = context,
60
60
  basename = path.basename(filename)
61
- if (!regex.filename.license.test(basename) || regex.filename.script.test(basename))
62
- return {}
63
- return { Program: node => validateContent(context, node, { requireSPDX, allowedLicenses })}
61
+ return !regex.filename.license.test(basename) || regex.filename.script.test(basename) ? {} : {
62
+ Program: node => validateContent(context, node, { requireSPDX, allowedLicenses }),
63
+ Document: node => validateContent(context, node, { requireSPDX, allowedLicenses })
64
+ }
64
65
  }
65
66
  }
@@ -24,7 +24,7 @@ export default {
24
24
  create(context) {
25
25
  if (regex.filename.manifest.test(extension.file.getName(context))) { // lint it directly
26
26
  const ancestors = [], pendingReports = [], deferredMV2 = []
27
- let isMV3 = false, isFF = false
27
+ let isMV3 = false, allowsMV2bgScripts = false
28
28
  function keyOf(node) {
29
29
  const keyNode = node?.name || node?.key
30
30
  return keyNode?.value ?? keyNode?.name
@@ -41,7 +41,7 @@ export default {
41
41
  const inDataCollection = ancestors.some(ancestor =>
42
42
  keyOf(ancestor) == 'data_collection_permissions')
43
43
  if (keyName == 'manifest_version' && node.value?.value == 3) isMV3 = true
44
- if (isTopLevel && keyName == 'browser_specific_settings') isFF = true
44
+ if (isTopLevel && keyName == 'browser_specific_settings') allowsMV2bgScripts = true
45
45
  if (isTopLevel && manifestData.keys.mv2Only.has(keyName))
46
46
  pendingReports.push({ node, messageId: 'mv2Key',
47
47
  data: { key: keyName, reason: 'MV2-only key' }})
@@ -79,21 +79,21 @@ export default {
79
79
  ancestors.pop()
80
80
  if (node.type == 'Document') {
81
81
  if (!isMV3) return
82
- if (!isFF) pendingReports.push(...deferredMV2)
82
+ if (!allowsMV2bgScripts) pendingReports.push(...deferredMV2)
83
83
  for (const report of pendingReports) context.report(report)
84
84
  }
85
85
  }
86
86
  }
87
87
  }
88
88
  const manifest = extension.manifest.read(context) ; if (manifest?.manifest_version != 3) return {}
89
- const isFF = manifest.browser_specific_settings != null
89
+ const allowsMV2bgScripts = manifest.browser_specific_settings != null
90
90
  return { // oxlint-disable security/detect-object-injection
91
91
  Program(node) {
92
92
  for (const key of Object.keys(manifest))
93
93
  if (manifestData.keys.mv2Only.has(key))
94
94
  context.report({ node, messageId: 'mv2Key', data: { key, reason: 'MV2-only key' }})
95
95
  for (const key of manifestData.keys.ff.mv3BG)
96
- if (manifest.background?.[key] && !isFF)
96
+ if (manifest.background?.[key] && !allowsMV2bgScripts)
97
97
  context.report({ node, messageId: 'mv2Key',
98
98
  data: { key: `background.${key}`, reason: 'Use background.service_worker instead' }})
99
99
  if (manifest.background?.persistent != null)
@@ -107,8 +107,8 @@ export default {
107
107
  for (const directive of manifestData.csp.forbiddenDirectives)
108
108
  if (cspPages.includes(directive))
109
109
  context.report({ node, messageId: 'unsafeCSP', data: { directive }})
110
- if (isFF) { // validate data collection permissions
111
- const dcp = manifest.browser_specific_settings?.gecko?.data_collection_permissions
110
+ if (manifest.browser_specific_settings?.gecko) { // validate data collection permissions
111
+ const dcp = manifest.browser_specific_settings.gecko.data_collection_permissions
112
112
  for (const [kind, set] of [
113
113
  ['required', manifestData.keys.ff.dataCollectionRequired],
114
114
  ['optional', manifestData.keys.ff.dataCollectionOptional]
@@ -43,19 +43,23 @@ function collectNodes(node, pathArray = [], out = []) {
43
43
  return out
44
44
  }
45
45
 
46
- function isURL(value) {
47
- return value.match(regex.i18n.placeholder)?.[0] === value
46
+ function isURL(value, allowRel = false, allowI18n = false) {
47
+ return allowI18n && value.match(regex.i18n.placeholder)?.[0] === value
48
48
  || re.absoluteURL.test(value.replace(re.template, 'x'))
49
+ || (allowRel && value && !value.includes('://'))
49
50
  }
50
51
 
51
52
  function validate(context, items, options) {
52
53
  const { checkURLfields, checkMatchPatterns, allowedSchemes, allowAllURLs } = options,
53
- loc = { line: 1, column: 1 }
54
+ loc = { line: 1, column: 1 },
55
+ isFF = items.some(({ path }) => path.startsWith('browser_specific_settings.gecko'))
54
56
  function report(node, messageId, data) {
55
57
  context.report({ ...(node ? { node } : { loc }), messageId, data }) }
56
58
  for (const { path, value, node } of items) {
57
- if (checkURLfields && manifestData.paths.url.has(path)) {
58
- if (!isURL(value)) report(node, 'invalidURL', { field: path, value })
59
+ if (checkURLfields && manifestData.paths.url.all.has(path)) {
60
+ const allowRel = isFF && manifestData.paths.url.relInFF.has(path),
61
+ allowI18n = !manifestData.paths.url.noI18n.has(path)
62
+ if (!isURL(value, allowRel, allowI18n)) report(node, 'invalidURL', { field: path, value })
59
63
  } else if (checkMatchPatterns && manifestData.paths.match.has(path)) {
60
64
  if (value == '<all_urls>') {
61
65
  if (!allowAllURLs) report(node, 'allURLs', { field: path })