eslint-plugin-chrome-extensions 2.3.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
@@ -31,17 +31,16 @@ npm i -D eslint-plugin-chrome-extensions
31
31
  Usage
32
32
  </h2>
33
33
 
34
- Add bundled configs to your `eslint.config.*js`:
34
+ Add bundled configs to your `eslint.config.*js` for each file type you need:
35
35
 
36
36
  ```js
37
37
  ...
38
38
  import chromeExtensions from 'eslint-plugin-chrome-extensions'
39
- import json from '@eslint/json'
40
39
 
41
40
  export default [
42
41
  // other configs
43
42
  {
44
- files: ['**/extension/**/*.js'],
43
+ files: ['**/extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
45
44
  plugins: { 'chrome-extensions': chromeExtensions },
46
45
  rules: {
47
46
  ...chromeExtensions.configs.recommended.rules,
@@ -49,17 +48,22 @@ export default [
49
48
  }
50
49
  },
51
50
  {
52
- files: ['**/manifest.json'], language: 'json/json',
53
- plugins: { 'chrome-extensions': chromeExtensions, json },
51
+ files: ['**/manifest.json'], ...chromeExtensions.configs.manifest,
54
52
  rules: {
55
53
  ...chromeExtensions.configs.manifest.rules,
56
- 'chrome-extensions/valid-version-field': ['error', { versionType: 'date' }] // selectively tweak rule
54
+ 'chrome-extensions/manifest-spacing': ['warn', { spaces: 4 }] // selectively tweak rule
57
55
  }
58
- }
56
+ },
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 }
59
63
  ]
60
64
  ```
61
65
 
62
- Available bundles: `recommended`, `strict` (everything), `problem` (deterministic bugs only), `security`, `manifest`
66
+ Available bundles: `recommended`, `strict` (everything), `problem` (deterministic bugs only), `security`, `manifest`, `license`, `css`, `html`, `json`, `json5`, `jsonc`
63
67
 
64
68
  **Note:** Rule config is either a severity string (`'off'`, `'warn'`, `'error'`) or an array `[severity, options]` when the rule accepts options.
65
69
 
@@ -72,7 +76,7 @@ Available bundles: `recommended`, `strict` (everything), `problem` (deterministi
72
76
 
73
77
  <h3 id="manifest-key-order">
74
78
  <code>manifest-key-order</code>
75
- <a title="Only works w/ 'json/json' config block" href="#usage">
79
+ <a title="Only works in 'json/json' config block" href="#usage">
76
80
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
77
81
  <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
78
82
  <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
@@ -130,10 +134,10 @@ If you don't care about the order of manifest keys.
130
134
 
131
135
  - [`require-mv3`](#require-mv3)
132
136
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
133
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
137
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
134
138
  - [`valid-mv3-schema`](#valid-mv3-schema)
135
139
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
136
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
140
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
137
141
 
138
142
  <div align="center">
139
143
 
@@ -147,7 +151,7 @@ If you don't care about the order of manifest keys.
147
151
 
148
152
  <h3 id="manifest-spacing">
149
153
  <code>manifest-spacing</code>
150
- <a title="Only works w/ 'json/json' config block" href="#usage">
154
+ <a title="Only works in 'json/json' config block" href="#usage">
151
155
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
152
156
  <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
153
157
  <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
@@ -215,12 +219,12 @@ If you don't care about visual consistency in your manifest.
215
219
  #### See also
216
220
 
217
221
  - [`manifest-key-order`](#manifest-key-order)
218
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
222
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
219
223
  <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix"><img height=14 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
220
224
  <a title="Doesn't prevent bugs" href="#manifest-key-order"><img height=14 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
221
225
  <a title="Accepts array w/ options" href="#manifest-key-order-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
222
226
  - [`no-duplicate-manifest-keys`](#no-duplicate-manifest-keys)
223
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
227
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
224
228
 
225
229
  <div align="center">
226
230
 
@@ -351,7 +355,7 @@ If you don't want deprecation validation at all. For individual deprecated APIs
351
355
 
352
356
  <h3 id="no-duplicate-manifest-keys">
353
357
  <code>no-duplicate-manifest-keys</code>
354
- <a title="Only works w/ 'json/json' config block" href="#usage">
358
+ <a title="Only works in 'json/json' config block" href="#usage">
355
359
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
356
360
  <a title="Copy rule URL" target="_blank"
357
361
  href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-duplicate-manifest-keys">
@@ -399,12 +403,12 @@ If you rely on a Chromium fork resolving duplicate keys in a specific order.
399
403
  #### See also
400
404
 
401
405
  - [`manifest-key-order`](#manifest-key-order)
402
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
406
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
403
407
  <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix"><img height=14 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
404
408
  <a title="Doesn't prevent bugs" href="#manifest-key-order"><img height=14 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
405
409
  <a title="Accepts array w/ options" href="#manifest-key-order-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
406
410
  - [`no-unused-permissions`](#no-unused-permissions)
407
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
411
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
408
412
 
409
413
  <div align="center">
410
414
 
@@ -484,7 +488,7 @@ If you intentionally target HTTP endpoints.
484
488
 
485
489
  <details>
486
490
 
487
- <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>
488
492
 
489
493
  <h4 id="no-insecure-messaging-options">Options</h4>
490
494
 
@@ -558,7 +562,7 @@ If you trust every external caller and open tab, or you intentionally broadcast
558
562
  <code>no-invalid-permissions</code>
559
563
  <a title="Included in recommended config" href="#usage">
560
564
  <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
561
- <a title="Works w/ 'json/json' config block" href="#usage">
565
+ <a title="Works in 'json/json' config block" href="#usage">
562
566
  <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
563
567
  <a title="Accepts array w/ options" href="#usage">
564
568
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -613,7 +617,186 @@ If you don't want permission validation at all. For custom permissions from a Ch
613
617
  - [`no-missing-permissions`](#no-missing-permissions)
614
618
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
615
619
  - [`no-unused-permissions`](#no-unused-permissions)
616
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
620
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
621
+
622
+ <div align="center">
623
+
624
+ [Back to index ↑][epce-rules]
625
+
626
+ </div>
627
+
628
+ ---
629
+
630
+ </details>
631
+
632
+ <h3 id="no-missing-default-locale">
633
+ <code>no-missing-default-locale</code>
634
+ <a title="Only works in 'json/json' config block" href="#usage">
635
+ <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
636
+ <a title="Accepts array w/ options" href="#usage">
637
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
638
+ <a title="Copy rule URL" target="_blank"
639
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-missing-default-locale">
640
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
641
+ </h3>
642
+
643
+ <details>
644
+
645
+ <summary>Ensures <code>default_locale</code> is declared and every <code>_locales</code> key exists in it.</summary>
646
+
647
+ <h4 id="no-missing-default-locale-options">Options</h4>
648
+
649
+ - `ignoreKeys` (default `[]`) – keys to skip when checking the default locale
650
+
651
+ #### <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
652
+
653
+ ```diff
654
+ // manifest.json
655
+ {
656
+ "manifest_version": 3,
657
+ "default_locale": "en",
658
+ "name": "__MSG_extName__"
659
+ }
660
+
661
+ // _locales/en/messages.json
662
+ {
663
+ "extName": { "message": "My Extension" }
664
+ }
665
+
666
+ // _locales/de/messages.json
667
+ {
668
+ "extName": { "message": "Meine Erweiterung" },
669
+ ! "welcomeMsg": { "message": "Willkommen" }
670
+ }
671
+ ```
672
+
673
+ #### <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
674
+
675
+ ```diff
676
+ // manifest.json
677
+ {
678
+ "manifest_version": 3,
679
+ "default_locale": "en",
680
+ "name": "__MSG_extName__"
681
+ }
682
+
683
+ // _locales/en/messages.json
684
+ {
685
+ "extName": { "message": "My Extension" },
686
+ + "welcomeMsg": { "message": "Welcome" }
687
+ }
688
+
689
+ // _locales/de/messages.json
690
+ {
691
+ "extName": { "message": "Meine Erweiterung" },
692
+ "welcomeMsg": { "message": "Willkommen" }
693
+ }
694
+ ```
695
+
696
+ #### When not to use it
697
+
698
+ If your `_locales/` is externally populated at build time.
699
+
700
+ #### Introduced in
701
+
702
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
703
+
704
+ #### See also
705
+
706
+ - <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/manifest/default-locale">Chrome for Developers — `default_locale`</a>
707
+ - <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>
708
+
709
+ <div align="center">
710
+
711
+ [Back to index ↑][epce-rules]
712
+
713
+ </div>
714
+
715
+ ---
716
+
717
+ </details>
718
+
719
+ <h3 id="no-missing-i18n-keys">
720
+ <code>no-missing-i18n-keys</code>
721
+ <a title="Included in recommended config" href="#usage">
722
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
723
+ <a title="Works in 'json/json' config block" href="#usage">
724
+ <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
725
+ <a title="Works in 'css/css' config block" href="#usage">
726
+ <img height=18 alt="[CSS]" src="https://img.shields.io/badge/CSS-dcd6f7?style=flat-square" align="middle"></a>
727
+ <a title="Works in 'html/html' config block" href="#usage">
728
+ <img height=18 alt="[HTML]" src="https://img.shields.io/badge/HTML-a8d8ea?style=flat-square" align="middle"></a>
729
+ <a title="Works in 'json/json' config block" href="#usage">
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 'json/json5' config block" href="#usage">
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 'json/jsonc' config block" href="#usage">
734
+ <img height=18 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
735
+ <a title="Accepts array w/ options" href="#usage">
736
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
737
+ <a title="Copy rule URL" target="_blank"
738
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-missing-i18n-keys">
739
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
740
+ </h3>
741
+
742
+ <details>
743
+
744
+ <summary>Flags i18n keys used but missing from <code>_locales/&lt;default_locale&gt;/messages.json</code>.</summary>
745
+
746
+ <h4 id="no-missing-i18n-keys-options">Options</h4>
747
+
748
+ - `ignoreKeys` (default `[]`) – keys to skip (e.g. `['customKey']`)
749
+
750
+ #### <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
751
+
752
+ ```diff
753
+ // content.js
754
+ ! chrome.i18n.getMessage('greeting')
755
+
756
+ // manifest.json
757
+ {
758
+ "manifest_version": 3,
759
+ ! "name": "__MSG_extName__"
760
+ }
761
+
762
+ // _locales/en/messages.json
763
+ { "unrelated": { "message": "sup" }}
764
+ ```
765
+
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
767
+
768
+ ```diff
769
+ // content.js
770
+ chrome.i18n.getMessage('greeting')
771
+
772
+ // manifest.json
773
+ {
774
+ "manifest_version": 3,
775
+ "name": "__MSG_extName__"
776
+ }
777
+
778
+ // _locales/en/messages.json
779
+ - { "unrelated": { "message": "sup" }}
780
+ + {
781
+ + "greeting": { "message": "Hello" },
782
+ + "extName": { "message": "My Extension" }
783
+ + }
784
+ ```
785
+
786
+ #### When not to use it
787
+
788
+ If your `_locales/` is externally populated at build time.
789
+
790
+ #### Introduced in
791
+
792
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
793
+
794
+ #### See also
795
+
796
+ - [`no-unused-i18n-keys`](#no-unused-i18n-keys)
797
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
798
+ <a title="Accepts array w/ options" href="#no-unused-i18n-keys-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
799
+ - <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>
617
800
 
618
801
  <div align="center">
619
802
 
@@ -677,10 +860,10 @@ If you use APIs through aliases or wrappers that obscure the original call site.
677
860
 
678
861
  - [`no-invalid-permissions`](#no-invalid-permissions)
679
862
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
680
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
863
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
681
864
  <a title="Accepts array w/ options" href="#no-invalid-permissions-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
682
865
  - [`no-unused-permissions`](#no-unused-permissions)
683
- <a title="Only works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
866
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
684
867
 
685
868
  <div align="center">
686
869
 
@@ -811,10 +994,10 @@ If you intentionally target forks w/ relaxed CSPs. For individual patterns you s
811
994
 
812
995
  - [`require-mv3`](#require-mv3)
813
996
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
814
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
997
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
815
998
  - [`valid-mv3-schema`](#valid-mv3-schema)
816
999
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
817
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1000
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
818
1001
 
819
1002
  <div align="center">
820
1003
 
@@ -828,7 +1011,7 @@ If you intentionally target forks w/ relaxed CSPs. For individual patterns you s
828
1011
 
829
1012
  <h3 id="no-unrestricted-host-permissions">
830
1013
  <code>no-unrestricted-host-permissions</code>
831
- <a title="Only works w/ 'json/json' config block" href="#usage">
1014
+ <a title="Only works in 'json/json' config block" href="#usage">
832
1015
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
833
1016
  <a title="Accepts array w/ options" href="#usage">
834
1017
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -890,9 +1073,86 @@ If you legitimately need broad access (e.g. ad blockers or dev tools).
890
1073
 
891
1074
  </details>
892
1075
 
1076
+ <h3 id="no-unused-i18n-keys">
1077
+ <code>no-unused-i18n-keys</code>
1078
+ <a title="Only works in 'json/json' config block" href="#usage">
1079
+ <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1080
+ <a title="Accepts array w/ options" href="#usage">
1081
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1082
+ <a title="Copy rule URL" target="_blank"
1083
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-unused-i18n-keys">
1084
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1085
+ </h3>
1086
+
1087
+ <details>
1088
+
1089
+ <summary>Flags i18n keys in <code>_locales/&lt;default_locale&gt;/messages.json</code> that are never referenced.</summary>
1090
+
1091
+ <h4 id="no-unused-i18n-keys-options">Options</h4>
1092
+
1093
+ - `ignoreKeys` (default `[]`) – keys to always treat as used (e.g. `['customKey']`)
1094
+ - `scanExtensions` (default `['cjs', 'css', 'htm', 'html', 'js', 'json', 'jsx', 'md', 'mjs', 'svg', 'ts', 'tsx', 'txt']`) – file extensions to scan for references
1095
+
1096
+ #### <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
1097
+
1098
+ ```diff
1099
+ // _locales/en/messages.json
1100
+ {
1101
+ "usedKey": { "message": "Used message" },
1102
+ ! "unusedKey": { "message": "Unused message" }
1103
+ }
1104
+
1105
+ // content.js
1106
+ chrome.i18n.getMessage('usedKey')
1107
+ ```
1108
+
1109
+ #### <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
1110
+
1111
+ ```diff
1112
+ // _locales/en/messages.json
1113
+ {
1114
+ "usedKey": { "message": "Used message" }
1115
+ - "unusedKey": { "message": "Unused message" }
1116
+ }
1117
+
1118
+ // content.js
1119
+ chrome.i18n.getMessage('usedKey')
1120
+ ```
1121
+
1122
+ #### When not to use it
1123
+
1124
+ If your i18n keys are referenced dynamically.
1125
+
1126
+ #### Introduced in
1127
+
1128
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1129
+
1130
+ #### See also
1131
+
1132
+ - [`no-missing-i18n-keys`](#no-missing-i18n-keys)
1133
+ <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1134
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
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
+ <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
+ <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 '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
+ <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
+ - <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
+
1143
+ <div align="center">
1144
+
1145
+ [Back to index ↑][epce-rules]
1146
+
1147
+ </div>
1148
+
1149
+ ---
1150
+
1151
+ </details>
1152
+
893
1153
  <h3 id="no-unused-permissions">
894
1154
  <code>no-unused-permissions</code>
895
- <a title="Only works w/ 'json/json' config block" href="#usage">
1155
+ <a title="Only works in 'json/json' config block" href="#usage">
896
1156
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
897
1157
  <a title="Copy rule URL" target="_blank"
898
1158
  href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-unused-permissions">
@@ -940,7 +1200,7 @@ If you access the granted API through an alias or computed property rather than
940
1200
 
941
1201
  - [`no-invalid-permissions`](#no-invalid-permissions)
942
1202
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
943
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1203
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
944
1204
  <a title="Accepts array w/ options" href="#no-invalid-permissions-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
945
1205
  - [`no-missing-permissions`](#no-missing-permissions)
946
1206
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
@@ -955,11 +1215,62 @@ If you access the granted API through an alias or computed property rather than
955
1215
 
956
1216
  </details>
957
1217
 
1218
+ <h3 id="require-license-file">
1219
+ <code>require-license-file</code>
1220
+ <a title="Only works in 'json/json' config block" href="#usage">
1221
+ <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1222
+ <a title="Copy rule URL" target="_blank"
1223
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23require-license-file">
1224
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1225
+ </h3>
1226
+
1227
+ <details>
1228
+
1229
+ <summary>Requires a license file in the extension.</summary>
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 structure
1232
+
1233
+ ```diff
1234
+ /extension/manifest.json
1235
+ !
1236
+ ```
1237
+
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
1239
+
1240
+ ```diff
1241
+ /extension/manifest.json
1242
+ + /extension/LICENSE.md
1243
+ ```
1244
+
1245
+ #### When not to use it
1246
+
1247
+ If you intentionally omit a license, or it's injected at build time.
1248
+
1249
+ #### Introduced in
1250
+
1251
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1252
+
1253
+ #### See also
1254
+
1255
+ - [`valid-license-file`](#valid-license-file)
1256
+ <a title="Only works in 'license' config block" href="#usage"><img height=14 alt="[License only]" src="https://img.shields.io/badge/License_only-559bff?style=flat-square" align="middle"></a>
1257
+ <a title="Accepts array w/ options" href="#valid-license-file-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1258
+
1259
+ <div align="center">
1260
+
1261
+ [Back to index ↑][epce-rules]
1262
+
1263
+ </div>
1264
+
1265
+ ---
1266
+
1267
+ </details>
1268
+
958
1269
  <h3 id="require-mv3">
959
1270
  <code>require-mv3</code>
960
1271
  <a title="Included in recommended config" href="#usage">
961
1272
  <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
962
- <a title="Works w/ 'json/json' config block" href="#usage">
1273
+ <a title="Works in 'json/json' config block" href="#usage">
963
1274
  <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
964
1275
  <a title="Copy rule URL" target="_blank"
965
1276
  href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23require-mv3">
@@ -1003,7 +1314,234 @@ If you intentionally target MV2 for a browser that still supports it (e.g. Firef
1003
1314
 
1004
1315
  - [`valid-mv3-schema`](#valid-mv3-schema)
1005
1316
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1006
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1317
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1318
+
1319
+ <div align="center">
1320
+
1321
+ [Back to index ↑][epce-rules]
1322
+
1323
+ </div>
1324
+
1325
+ ---
1326
+
1327
+ </details>
1328
+
1329
+ <h3 id="require-short-name-when-useful">
1330
+ <code>require-short-name-when-useful</code>
1331
+ <a title="Only works in 'json/json' config block" href="#usage">
1332
+ <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1333
+ <a title="Accepts array w/ options" href="#usage">
1334
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1335
+ <a title="Copy rule URL" target="_blank"
1336
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23require-short-name-when-useful">
1337
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1338
+ </h3>
1339
+
1340
+ <details>
1341
+
1342
+ <summary>Requires <code>short_name</code> when <code>name</code> exceeds 12 chars.</summary>
1343
+
1344
+ <h4 id="require-short-name-when-useful-options">Options</h4>
1345
+
1346
+ - `maxNameLength` (default `12`) – length of `name` above which `short_name` is required
1347
+ - `always` (default `false`) – require `short_name` regardless of `name` length
1348
+ - `checkRedundant` (default `false`) – also flag `short_name` that duplicates `name`
1349
+
1350
+ #### <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
1351
+
1352
+ ```diff
1353
+ {
1354
+ "manifest_version": 3,
1355
+ ! "name": "Extension Infinity"
1356
+ }
1357
+ ```
1358
+
1359
+ #### <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
1360
+
1361
+ ```diff
1362
+ {
1363
+ "manifest_version": 3,
1364
+ "name": "Extension Infinity",
1365
+ + "short_name": "Extension ∞"
1366
+ }
1367
+ ```
1368
+
1369
+ #### When not to use it
1370
+
1371
+ If you primarily target a browser that doesn't support `short_name`.
1372
+
1373
+ #### Introduced in
1374
+
1375
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1376
+
1377
+ #### See also
1378
+
1379
+ - <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/manifest/short-name">Chrome for Developers — `short_name`</a>
1380
+ - <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/manifest/name">Chrome for Developers — `name`</a>
1381
+
1382
+ <div align="center">
1383
+
1384
+ [Back to index ↑][epce-rules]
1385
+
1386
+ </div>
1387
+
1388
+ ---
1389
+
1390
+ </details>
1391
+
1392
+ <h3 id="valid-filenames">
1393
+ <code>valid-filenames</code>
1394
+ <a title="Included in recommended config" href="#usage">
1395
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1396
+ <a title="Works in 'json/json' config block" href="#usage">
1397
+ <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1398
+ <a title="Works in 'css/css' config block" href="#usage">
1399
+ <img height=18 alt="[CSS]" src="https://img.shields.io/badge/CSS-dcd6f7?style=flat-square" align="middle"></a>
1400
+ <a title="Works in 'html/html' config block" href="#usage">
1401
+ <img height=18 alt="[HTML]" src="https://img.shields.io/badge/HTML-a8d8ea?style=flat-square" align="middle"></a>
1402
+ <a title="Works in 'json/json' config block" href="#usage">
1403
+ <img height=18 alt="[JSON]" src="https://img.shields.io/badge/JSON-a8e6cf?style=flat-square" align="middle"></a>
1404
+ <a title="Works in 'json/json5' config block" href="#usage">
1405
+ <img height=18 alt="[JSON5]" src="https://img.shields.io/badge/JSON5-c7e6b8?style=flat-square" align="middle"></a>
1406
+ <a title="Works in 'json/jsonc' config block" href="#usage">
1407
+ <img height=18 alt="[JSONC]" src="https://img.shields.io/badge/JSONC-e6e6a8?style=flat-square" align="middle"></a>
1408
+ <a title="Accepts array w/ options" href="#usage">
1409
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1410
+ <a title="Copy rule URL" target="_blank"
1411
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-filenames">
1412
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1413
+ </h3>
1414
+
1415
+ <details>
1416
+
1417
+ <summary>Enforces valid filenames and paths.</summary>
1418
+
1419
+ <h4 id="valid-filenames-options">Options</h4>
1420
+
1421
+ - `noReservedPrefix` (default `true`) – disallow filenames/dirs starting w/ `_` (except `_locales`)
1422
+ - `noSpecialChars` (default `true`) – warn on chars outside `a-z`, `0-9`, `-`, `_`, `.`
1423
+ - `checkFileExists` (default `true`) – verify files referenced in `manifest.json` actually exist
1424
+ - `enforceCaseSensitivity` (default `true`) – verify manifest references match actual file case
1425
+ - `maxFullPathLength` (default `260`) – max full path char length
1426
+
1427
+ #### <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
1428
+
1429
+ ```diff
1430
+ // files
1431
+ ! /extension/_metadata/app.json
1432
+ ! /extension/content script.js
1433
+
1434
+ // manifest.json
1435
+ {
1436
+ ! "icons": { "16": "Icon16.png" },
1437
+ ! "background": { "service_worker": "missing-worker.js" }
1438
+ }
1439
+ ```
1440
+
1441
+ #### <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
1442
+
1443
+ ```diff
1444
+ - /extension/_metadata/app.json
1445
+ + /extension/metadata/app.json
1446
+ - /extension/content script.js
1447
+ + /extension/content-script.js
1448
+
1449
+ // manifest.json
1450
+ {
1451
+ - "icons": { "16": "Icon16.png" },
1452
+ + "icons": { "16": "icon16.png" },
1453
+ - "background": { "service_worker": "missing-worker.js" }
1454
+ + "background": { "service_worker": "service-worker.js" }
1455
+ }
1456
+ ```
1457
+
1458
+ #### When not to use it
1459
+
1460
+ If you don't use conventional filenames or paths, or they're generated at build time.
1461
+
1462
+ #### Introduced in
1463
+
1464
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1465
+
1466
+ #### See also
1467
+
1468
+ - [`no-missing-web-accessible-resources`](#no-missing-web-accessible-resources)
1469
+ <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1470
+ - [`valid-manifest-icons`](#valid-manifest-icons)
1471
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1472
+ <a title="Accepts array w/ options" href="#valid-manifest-icons-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1473
+
1474
+ <div align="center">
1475
+
1476
+ [Back to index ↑][epce-rules]
1477
+
1478
+ </div>
1479
+
1480
+ ---
1481
+
1482
+ </details>
1483
+
1484
+ <h3 id="valid-license-file">
1485
+ <code>valid-license-file</code>
1486
+ <a title="Only works in 'license' config block" href="#usage">
1487
+ <img height=18 alt="[License only]" src="https://img.shields.io/badge/License_only-559bff?style=flat-square" align="middle"></a>
1488
+ <a title="Accepts array w/ options" href="#usage">
1489
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1490
+ <a title="Copy rule URL" target="_blank"
1491
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-license-file">
1492
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1493
+ </h3>
1494
+
1495
+ <details>
1496
+
1497
+ <summary>Validates SPDX identifiers in license files.</summary>
1498
+
1499
+ <h4 id="valid-license-file-options">Options</h4>
1500
+
1501
+ - `requireSPDX` (default `false`) – require <a target="_blank" href="https://spdx.dev/learn/handling-license-info/">`SPDX-License-Identifier`</a> in the license
1502
+ - `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
1503
+
1504
+ #### <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
1505
+
1506
+ ```diff
1507
+ /* eslint chrome-extensions/valid-license-file: ['error', { requireSPDX: true, allowedLicenses: ['MIT'] }] */
1508
+
1509
+ // LICENSE.md
1510
+
1511
+ MIT License
1512
+
1513
+ Copyright © 2026 You
1514
+
1515
+ !
1516
+ ```
1517
+
1518
+ #### <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
1519
+
1520
+ ```diff
1521
+ /* eslint chrome-extensions/valid-license-file: ['error', { requireSPDX: true, allowedLicenses: ['MIT'] }] */
1522
+
1523
+ // LICENSE.md
1524
+
1525
+ MIT License
1526
+
1527
+ Copyright © 2026 You
1528
+
1529
+ + SPDX-License-Identifier: MIT
1530
+ ```
1531
+
1532
+ #### When not to use it
1533
+
1534
+ If your license is injected at build time, or you use a license w/ no SPDX identifier.
1535
+
1536
+ #### Introduced in
1537
+
1538
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1539
+
1540
+ #### See also
1541
+
1542
+ - <img height=12 src="https://www.google.com/s2/favicons?domain=spdx.org&sz=64">&ensp;<a target="_blank" href="https://spdx.org/licenses/">SPDX — License List</a>
1543
+ - [`require-license-file`](#require-license-file)
1544
+ <a title="Only works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1007
1545
 
1008
1546
  <div align="center">
1009
1547
 
@@ -1017,7 +1555,7 @@ If you intentionally target MV2 for a browser that still supports it (e.g. Firef
1017
1555
 
1018
1556
  <h3 id="valid-manifest-icons">
1019
1557
  <code>valid-manifest-icons</code>
1020
- <a title="Only works w/ 'json/json' config block" href="#usage">
1558
+ <a title="Only works in 'json/json' config block" href="#usage">
1021
1559
  <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1022
1560
  <a title="Accepts array w/ options" href="#usage">
1023
1561
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -1090,7 +1628,7 @@ If your icons are injected at build time, or you target browsers that support SV
1090
1628
  <code>valid-mv3-schema</code>
1091
1629
  <a title="Included in recommended config" href="#usage">
1092
1630
  <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1093
- <a title="Works w/ 'json/json' config block" href="#usage">
1631
+ <a title="Works in 'json/json' config block" href="#usage">
1094
1632
  <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1095
1633
  <a title="Copy rule URL" target="_blank"
1096
1634
  href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-mv3-schema">
@@ -1140,7 +1678,7 @@ If you intentionally target MV2 for a browser that still supports it (e.g. Firef
1140
1678
 
1141
1679
  - [`require-mv3`](#require-mv3)
1142
1680
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1143
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1681
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1144
1682
  - [`no-deprecated-apis`](#no-deprecated-apis)
1145
1683
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1146
1684
  <a title="Accepts array w/ options" href="#no-deprecated-apis-options"><img height=14 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
@@ -1202,7 +1740,7 @@ If your background context isn't a service worker.
1202
1740
 
1203
1741
  - [`require-mv3`](#require-mv3)
1204
1742
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1205
- <a title="Works w/ 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1743
+ <a title="Works in 'json/json' config block" href="#usage"><img height=14 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1206
1744
  - [`no-remote-code`](#no-remote-code)
1207
1745
  <a title="Included in recommended config" href="#usage"><img height=14 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1208
1746
  <a title="Included in security config" href="#usage"><img height=14 alt="[Security]" src="https://img.shields.io/badge/Security-e8357a?style=flat-square" align="middle"></a>
@@ -1218,11 +1756,84 @@ If your background context isn't a service worker.
1218
1756
 
1219
1757
  </details>
1220
1758
 
1759
+ <h3 id="valid-url-fields">
1760
+ <code>valid-url-fields</code>
1761
+ <a title="Only works in 'json/json' config block" href="#usage">
1762
+ <img height=18 alt="[Manifest only]" src="https://img.shields.io/badge/Manifest_only-999999?style=flat-square" align="middle"></a>
1763
+ <a title="Accepts array w/ options" href="#usage">
1764
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1765
+ <a title="Copy rule URL" target="_blank"
1766
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23valid-url-fields">
1767
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1768
+ </h3>
1769
+
1770
+ <details>
1771
+
1772
+ <summary>Validates URLs and match patterns in manifest fields.</summary>
1773
+
1774
+ <h4 id="valid-url-fields-options">Options</h4>
1775
+
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>)
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>)
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>
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>)
1780
+
1781
+ #### <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
1782
+
1783
+ ```diff
1784
+ {
1785
+ "manifest_version": 3,
1786
+ ! "homepage_url": "not-a-url",
1787
+ ! "host_permissions": ["https://*"],
1788
+ ! "content_scripts": [{ "js": ["content.js"], "matches": ["*://*foo.com/*"] }],
1789
+ ! "web_accessible_resources": [{ "resources": ["logo.png"], "matches": ["https://example.com/specific-path"] }]
1790
+ }
1791
+ ```
1792
+
1793
+ #### <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
1794
+
1795
+ ```diff
1796
+ {
1797
+ "manifest_version": 3,
1798
+ - "homepage_url": "not-a-url",
1799
+ + "homepage_url": "https://example.com",
1800
+ - "host_permissions": ["https://*"],
1801
+ + "host_permissions": ["https://example.com/*"],
1802
+ - "content_scripts": [{ "js": ["content.js"], "matches": ["*://*foo.com/*"] }],
1803
+ + "content_scripts": [{ "js": ["content.js"], "matches": ["*://*.foo.com/*"] }],
1804
+ - "web_accessible_resources": [{ "resources": ["logo.png"], "matches": ["https://example.com/specific-path"] }]
1805
+ + "web_accessible_resources": [{ "resources": ["logo.png"], "matches": ["https://example.com/*"] }]
1806
+ }
1807
+ ```
1808
+
1809
+ #### When not to use it
1810
+
1811
+ If your URLs or patterns are generated at build time.
1812
+
1813
+ #### Introduced in
1814
+
1815
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.4.0">v2.4.0</a> (October 13, 2026)
1816
+
1817
+ #### See also
1818
+
1819
+ - <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/develop/concepts/declare-permissions">Chrome for Developers — Declare permissions</a>
1820
+ - <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/develop/concepts/match-patterns">Chrome for Developers — Match patterns</a>
1821
+
1822
+ <div align="center">
1823
+
1824
+ [Back to index ↑][epce-rules]
1825
+
1826
+ </div>
1827
+
1828
+ ---
1829
+
1830
+ </details>
1831
+
1221
1832
  <h3 id="valid-version-field">
1222
1833
  <code>valid-version-field</code>
1223
1834
  <a title="Included in recommended config" href="#usage">
1224
1835
  <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1225
- <a title="Works w/ 'json/json' config block" href="#usage">
1836
+ <a title="Works in 'json/json' config block" href="#usage">
1226
1837
  <img height=18 alt="[Manifest]" src="https://img.shields.io/badge/Manifest-999999?style=flat-square" align="middle"></a>
1227
1838
  <a title="Accepts array w/ options" href="#usage">
1228
1839
  <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>