eslint-plugin-greasemonkey 1.3.0 → 1.5.0

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
@@ -2,9 +2,12 @@
2
2
 
3
3
  # <img height=24 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/e9128375d4/assets/images/icons/eslint/purple/icon32.png"> eslint-plugin-greasemonkey
4
4
 
5
- [![](https://img.shields.io/npm/v/eslint-plugin-greasemonkey?logo=icinga&style=for-the-badge&label=Latest%20Build&color=32fcee)][epg-npm]
6
- [![](https://img.shields.io/badge/License-MIT-f1f20a.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge)](#license)
7
- [![](https://img.shields.io/badge/Requires-ESLint_%3E%3D9.0.0-4B32C3?style=for-the-badge&logo=eslint)](https://eslint.org/)
5
+ <a target="_blank" href="https://www.npmjs.com/package/eslint-plugin-greasemonkey">
6
+ <img alt="[Latest Build]" src="https://img.shields.io/npm/v/eslint-plugin-greasemonkey?logo=icinga&style=for-the-badge&label=Latest%20Build&color=32fcee"></a>
7
+ <a href="#license">
8
+ <img alt="[License: MIT]" src="https://img.shields.io/badge/License-MIT-f1f20a.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
9
+ <a target="_blank" href="https://eslint.org/">
10
+ <img alt="[Requires ESLint >=9.0.0]"src="https://img.shields.io/badge/Requires-ESLint_%3E%3D9.0.0-4B32C3?style=for-the-badge&logo=eslint" ></a>
8
11
 
9
12
  ESLint rules for Greasemonkey userscripts.
10
13
 
@@ -24,7 +27,7 @@ npm i -D eslint-plugin-greasemonkey
24
27
 
25
28
  ## Usage
26
29
 
27
- Add `greasemonkey` [rules][epg-rules] to your `eslint.config.*js`:
30
+ Add a bundled config to your `eslint.config.*js`:
28
31
 
29
32
  ```js
30
33
  ...
@@ -33,40 +36,9 @@ import greasemonkey from 'eslint-plugin-greasemonkey'
33
36
  export default [
34
37
  // other configs
35
38
  {
36
- files: ['**/*.user.js'],
37
- plugins: { greasemonkey },
38
- rules: { // recommended set w/ default options
39
- 'greasemonkey/meta-spacing': ['error', 3],
40
- 'greasemonkey/no-duplicate-keys': 'error',
41
- 'greasemonkey/no-missing-grants': 'error',
42
- 'greasemonkey/no-unused-grants': 'error',
43
- 'greasemonkey/no-unused-resources': 'error',
44
- 'greasemonkey/prefer-match': 'error',
45
- 'greasemonkey/require-matching-localized-fields': 'error',
46
- 'greasemonkey/require-meta-newline': 'error',
47
- 'greasemonkey/require-required-fields': 'error',
48
- 'greasemonkey/require-space-before-meta-key': 'error',
49
- 'greasemonkey/require-user-js-filename': 'error',
50
- 'greasemonkey/valid-field-values': ['error', { versionType: 'semver' }],
51
- 'greasemonkey/valid-grants': ['error', { allowedGrants: [] }]
52
- }
53
- }
54
- ]
55
- ```
56
-
57
- ...or use a bundled config:
58
-
59
- ```js
60
- ...
61
- import greasemonkey from 'eslint-plugin-greasemonkey'
62
-
63
- export default [
64
- // other configs
65
- {
66
- files: ['**/*.user.js'],
67
- plugins: { greasemonkey }
39
+ files: ['**/*.user.js'], plugins: { greasemonkey }
68
40
  rules: {
69
- ...greasemonkey.configs.recommended.rules, // or .strict.rules
41
+ ...greasemonkey.configs.recommended.rules, // or .strict.rules to enable all
70
42
  'greasemonkey/no-unused-resources': 'off', // selectively disable rule
71
43
  'greasemonkey/meta-spacing': ['error', 2] // selectively tweak rule
72
44
  }
@@ -82,24 +54,131 @@ export default [
82
54
 
83
55
  | ID | Description | Auto-fixable | Recommended |
84
56
  | --- | --- | :-: | :-: |
85
- | [`meta-spacing`](#meta-spacing-auto-fixable) | Aligns metadata values to the column after the longest key | ✅ | ✅ |
86
- | [`no-duplicate-keys`](#no-duplicate-keys) | Disallows duplicate metadata keys | ❌ | ✅ |
87
- | [`no-missing-grants`](#no-missing-grants) | Flags API calls w/o a matching `@grant` | ❌ | ✅ |
88
- | [`no-unused-grants`](#no-unused-grants) | Flags `@grant` entries whose APIs are never used | ❌ | ✅ |
89
- | [`no-unused-resources`](#no-unused-resources) | Flags `@resource` entries that are never referenced | ❌ | ✅ |
90
- | [`prefer-match`](#prefer-match) | Prefers `@match` over `@include` | ❌ | ✅ |
91
- | [`require-homepage-pair`](#require-homepage-pair-auto-fixable) | Requires `@homepage` and `@homepageURL` to be used together | ✅ | ❌ |
92
- | [`require-matching-localized-fields`](#require-matching-localized-fields) | Requires matching localized `@name:xx` / `@description:xx` pairs | ❌ | ✅ |
93
- | [`require-meta-newline`](#require-meta-newline-auto-fixable) | Requires a newline between the metadata and code | ✅ | ✅ |
94
- | [`require-required-fields`](#require-required-fields) | Requires essential metadata fields | ❌ | ✅ |
95
- | [`require-space-before-meta-key`](#require-space-before-meta-key-auto-fixable) | Requires exactly one space between `//` and metadata keys | ✅ | ✅ |
96
- | [`require-update-download-pair`](#require-update-download-pair) | Requires `@updateURL` and `@downloadURL` to be used together | ❌ | ❌ |
97
- | [`require-user-js-filename`](#require-user-js-filename) | Requires userscript filenames end in `.user.js` | ❌ | ✅ |
98
- | [`valid-field-values`](#valid-field-values) | Validates `@version` and URL fields | ❌ | ✅ |
99
- | [`valid-grants`](#valid-grants) | Disallows invalid `@grant` values | ❌ | ✅ |
57
+ | [`meta-order`](#meta-order) | Enforces a preferred order for metadata keys | <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"> | <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"> |
58
+ | [`meta-spacing`](#meta-spacing) | Aligns metadata values to the column after the longest key | <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"> | <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"> |
59
+ | [`no-duplicate-keys`](#no-duplicate-keys) | Disallows duplicate metadata keys | <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"> | <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"> |
60
+ | [`no-invalid-headers`](#no-invalid-headers) | Disallows invalid or unsupported metadata headers | <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"> | <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"> |
61
+ | [`no-missing-grants`](#no-missing-grants) | Flags API calls w/o a matching `@grant` | <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"> | <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"> |
62
+ | [`no-missing-resources`](#no-missing-resources) | Flags `@resource` references w/o a matching declaration | <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"> | <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"> |
63
+ | [`no-unused-grants`](#no-unused-grants) | Flags `@grant` entries whose APIs are never used | <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"> | <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"> |
64
+ | [`no-unused-resources`](#no-unused-resources) | Flags `@resource` entries that are never referenced | <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"> | <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"> |
65
+ | [`prefer-match`](#prefer-match) | Prefers `@match` over `@include` | <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"> | <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"> |
66
+ | [`require-blank-line-after-meta-block`](#require-blank-line-after-meta-block) | Requires a blank line between the metadata and code | <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"> | <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"> |
67
+ | [`require-homepage-pair`](#require-homepage-pair) | Requires `@homepage` and `@homepageURL` to be used together | <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"> | <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"> |
68
+ | [`require-matching-localized-fields`](#require-matching-localized-fields) | Requires matching localized `@name:xx` / `@description:xx` pairs | <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"> | <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"> |
69
+ | [`require-required-fields`](#require-required-fields) | Requires essential metadata fields | <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"> | <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"> |
70
+ | [`require-space-before-meta-key`](#require-space-before-meta-key) | Requires exactly one space between `//` and metadata keys | <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"> | <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"> |
71
+ | [`require-sri-hashing`](#require-sri-hashing) | Requires SRI hashes for external `@require` URLs | <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"> | <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"> |
72
+ | [`require-update-download-pair`](#require-update-download-pair) | Requires `@updateURL` and `@downloadURL` to be used together | <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"> | <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"> |
73
+ | [`require-user-js-filename`](#require-user-js-filename) | Requires userscript filenames end in `.user.js` | <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"> | <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"> |
74
+ | [`valid-field-values`](#valid-field-values) | Validates `@version` and URL fields | <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"> | <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"> |
75
+ | [`valid-grants`](#valid-grants) | Disallows invalid or unsupported `@grant` values | <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"> | <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"> |
76
+
77
+ ---
78
+
79
+ <h3 id="meta-order">
80
+ <code>meta-order</code>
81
+ <a title="Only included in strict config" href="#usage">
82
+ <img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
83
+ <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
84
+ <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-e8357a?style=flat-square" align="middle"></a>
85
+ <a title="Doesn't catch bugs" href="#meta-order">
86
+ <img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
87
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
88
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
89
+ <a title="Copy rule URL" target="_blank"
90
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23meta-order">
91
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
92
+ </h3>
93
+
94
+ > Enforces a preferred order for metadata keys.
95
+
96
+ #### Options
97
+
98
+ - `string[]` (default `[]`) – metadata keys in custom order
99
+
100
+ For example:
101
+
102
+ ```js
103
+ /* eslint greasemonkey/meta-order: ['error', ['run-in', 'inject-into']] */
104
+ ```
105
+
106
+ #### <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
107
+
108
+ ```diff
109
+ // ==UserScript==
110
+ // @name My Script
111
+ ! // @version 1.0.0
112
+ // @description A script
113
+ ! // @namespace example.com
114
+ // @author John Doe
115
+ ! // @license MIT
116
+ // @icon https://example.com/icon.png
117
+ ! // @match https://example.com/*
118
+ // @grant GM_setValue
119
+ // @require https://example.com/lib.js
120
+ // @resource logo https://example.com/logo.png
121
+ ! // @connect example.com
122
+ // ==/UserScript==
123
+ ```
124
+
125
+ #### <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
126
+
127
+ ```diff
128
+ // ==UserScript==
129
+ // @name My Script
130
+ - // @version 1.0.0
131
+ // @description A script
132
+ - // @namespace example.com
133
+ // @author John Doe
134
+ - // @license MIT
135
+ + // @namespace example.com
136
+ + // @version 1.0.0
137
+ + // @license MIT
138
+ // @icon https://example.com/icon.png
139
+ - // @match https://example.com/*
140
+ + // @connect example.com
141
+ // @grant GM_setValue
142
+ - // @require https://example.com/lib.js
143
+ - // @resource logo https://example.com/logo.png
144
+ - // @connect example.com
145
+ + // @match https://example.com/*
146
+ + // @require https://example.com/lib.js
147
+ + // @resource logo https://example.com/logo.png
148
+ // ==/UserScript==
149
+ ```
150
+
151
+ #### When not to use it
152
+
153
+ If you don't care about the order of metadata fields.
154
+
155
+ #### See also
156
+
157
+ - [`meta-spacing`](#meta-spacing)
158
+ - [`no-invalid-headers`](#no-invalid-headers)
159
+
160
+ <div align="center">
161
+
162
+ [Back to index ↑][epg-rules]
163
+
164
+ </div>
165
+
100
166
  ---
101
167
 
102
- ### `meta-spacing` (auto-fixable)
168
+ <h3 id="meta-spacing">
169
+ <code>meta-spacing</code>
170
+ <a title="Included in recommended config" href="#usage">
171
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
172
+ <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
173
+ <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-e8357a?style=flat-square" align="middle"></a>
174
+ <a title="Doesn't catch bugs" href="#meta-spacing">
175
+ <img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
176
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
177
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
178
+ <a title="Copy rule URL" target="_blank"
179
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23meta-spacing">
180
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
181
+ </h3>
103
182
 
104
183
  > Aligns metadata values to the column after the longest key.
105
184
 
@@ -107,30 +186,41 @@ export default [
107
186
 
108
187
  - `number` (default `3`) – minimum spaces after the longest key
109
188
 
110
- #### Incorrect code
189
+ #### <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
111
190
 
112
- ```js
113
- // ==UserScript==
114
- // @name My Script
115
- // @namespace example.com
116
- // @version 1.0.0
117
- // ==/UserScript==
191
+ ```diff
192
+ /* eslint greasemonkey/meta-spacing: ['error', 1] */
193
+
194
+ // ==UserScript==
195
+ ! // @name My Script
196
+ // @namespace example.com
197
+ ! // @version 1.0.0
198
+ // ==/UserScript==
118
199
  ```
119
200
 
120
- #### Correct code
121
-
122
- ```js
123
- // ==UserScript==
124
- // @name My Script
125
- // @namespace example.com
126
- // @version 1.0.0
127
- // ==/UserScript==
201
+ #### <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
202
+
203
+ ```diff
204
+ /* eslint greasemonkey/meta-spacing: ['error', 1] */
205
+
206
+ // ==UserScript==
207
+ - // @name My Script
208
+ + // @name My Script
209
+ // @namespace example.com
210
+ - // @version 1.0.0
211
+ + // @version 1.0.0
212
+ // ==/UserScript==
128
213
  ```
129
214
 
130
215
  #### When not to use it
131
216
 
132
217
  If you don't care about visual alignment in metadata blocks.
133
218
 
219
+ #### See also
220
+
221
+ - [`require-blank-line-after-meta-block`](#require-blank-line-after-meta-block)
222
+ - [`require-space-before-meta-key`](#require-space-before-meta-key)
223
+
134
224
  <div align="center">
135
225
 
136
226
  [Back to index ↑][epg-rules]
@@ -139,36 +229,52 @@ If you don't care about visual alignment in metadata blocks.
139
229
 
140
230
  ---
141
231
 
142
- ### `no-duplicate-keys`
232
+ <h3 id="no-duplicate-keys">
233
+ <code>no-duplicate-keys</code>
234
+ <a title="Included in recommended config" href="#usage">
235
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
236
+ <a title="Copy rule URL" target="_blank"
237
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-duplicate-keys">
238
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
239
+ </h3>
143
240
 
144
241
  > Disallows duplicate metadata keys. Multi-value keys are exempt (`@compatible`, `@connect`, `@description`, `@exclude`, `@exclude-match`, `@grant`, `@homepageURL`, `@include`, `@license`, `@match`, `@require`, `@resource`, `@supportURL`).
145
242
 
146
- #### Incorrect code
243
+ #### <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
147
244
 
148
- ```js
149
- // ==UserScript==
150
- // @name Foo
151
- // @name Bar
152
- // @version 1.0.0
153
- // @version 2.0.0
154
- // ==/UserScript==
245
+ ```diff
246
+ // ==UserScript==
247
+ ! // @name Foo
248
+ ! // @name Bar
249
+ ! // @version 1.0.0
250
+ ! // @version 2.0.0
251
+ // ==/UserScript==
155
252
  ```
156
253
 
157
- #### Correct code
158
-
159
- ```js
160
- // ==UserScript==
161
- // @name My Script
162
- // @version 1.0.0
163
- // @match https://a.example.com/*
164
- // @match https://b.example.com/*
165
- // ==/UserScript==
254
+ #### <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
255
+
256
+ ```diff
257
+ // ==UserScript==
258
+ - // @name Foo
259
+ - // @name Bar
260
+ - // @version 1.0.0
261
+ - // @version 2.0.0
262
+ + // @name My Script
263
+ + // @version 1.0.0
264
+ + // @match https://a.example.com/*
265
+ + // @match https://b.example.com/*
266
+ // ==/UserScript==
166
267
  ```
167
268
 
168
269
  #### When not to use it
169
270
 
170
271
  If you intentionally repeat single-value keys.
171
272
 
273
+ #### See also
274
+
275
+ - [`require-matching-localized-fields`](#require-matching-localized-fields)
276
+ - [`require-required-fields`](#require-required-fields)
277
+
172
278
  <div align="center">
173
279
 
174
280
  [Back to index ↑][epg-rules]
@@ -177,34 +283,59 @@ If you intentionally repeat single-value keys.
177
283
 
178
284
  ---
179
285
 
180
- ### `no-missing-grants`
181
-
182
- > Flags API calls (e.g. `GM_setValue`, `GM.setValue`, `CAT.agent.dom`) used w/o a matching `@grant` declaration.
286
+ <h3 id="no-invalid-headers">
287
+ <code>no-invalid-headers</code>
288
+ <a title="Included in recommended config" href="#usage">
289
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
290
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
291
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
292
+ <a title="Copy rule URL" target="_blank"
293
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-invalid-headers">
294
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
295
+ </h3>
183
296
 
184
- #### Incorrect code
297
+ > Disallows invalid or unsupported metadata headers.
185
298
 
186
- ```js
187
- // ==UserScript==
188
- // @name My Script
189
- // ==/UserScript==
299
+ #### Options
190
300
 
191
- GM_setValue('key', 'value')
192
- ```
301
+ - `target` (default `'all'`) – manager(s) whose headers to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`), as a single string or array
302
+ - `allowedHeaders` (default `[]`) – additional headers to allow (strings or regexes), as a single value or array
193
303
 
194
- #### Correct code
304
+ #### <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
195
305
 
196
- ```js
197
- // ==UserScript==
198
- // @name My Script
199
- // @grant GM_setValue
200
- // ==/UserScript==
306
+ ```diff
307
+ /* eslint greasemonkey/no-invalid-headers: ['error', { target: 'tampermonkey', allowedHeaders: /^\w*url$/i }] */
308
+
309
+ // ==UserScript==
310
+ // @name My Script
311
+ ! // @runin normal-tabs
312
+ ! // @inject-into page
313
+ // @customURL https://example.com
314
+ // ==/UserScript==
315
+ ```
201
316
 
202
- GM_setValue('key', 'value')
317
+ #### <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
318
+
319
+ ```diff
320
+ /* eslint greasemonkey/no-invalid-headers: ['error', { target: 'tampermonkey', allowedHeaders: /^\w*url$/i }] */
321
+
322
+ // ==UserScript==
323
+ // @name My Script
324
+ - // @runin normal-tabs
325
+ + // @run-in normal-tabs
326
+ - // @inject-into page
327
+ // @customURL https://example.com
328
+ // ==/UserScript==
203
329
  ```
204
330
 
205
331
  #### When not to use it
206
332
 
207
- If you use APIs through aliases or wrappers that obscure the original call site.
333
+ If your script relies heavily on custom headers and you'd rather not maintain `allowedHeaders`.
334
+
335
+ #### See also
336
+
337
+ - [`valid-field-values`](#valid-field-values)
338
+ - [`valid-grants`](#valid-grants)
208
339
 
209
340
  <div align="center">
210
341
 
@@ -214,33 +345,94 @@ If you use APIs through aliases or wrappers that obscure the original call site.
214
345
 
215
346
  ---
216
347
 
217
- ### `no-unused-grants`
348
+ <h3 id="no-missing-grants">
349
+ <code>no-missing-grants</code>
350
+ <a title="Included in recommended config" href="#usage">
351
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
352
+ <a title="Copy rule URL" target="_blank"
353
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-missing-grants">
354
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
355
+ </h3>
218
356
 
219
- > Flags `@grant` entries whose APIs are never used in the script.
357
+ > Flags API calls (e.g. `GM_setValue`, `GM.setValue`, `CAT.agent.dom`) used w/o a matching `@grant` declaration.
220
358
 
221
- #### Incorrect code
359
+ #### <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
222
360
 
223
- ```js
224
- // ==UserScript==
225
- // @grant GM_setValue
226
- // ==/UserScript==
361
+ ```diff
362
+ // ==UserScript==
363
+ // @name My Script
364
+ // ==/UserScript==
365
+
366
+ ! GM_setValue('key', 'value')
367
+ ```
227
368
 
228
- console.log('no GM_setValue call')
369
+ #### <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
370
+
371
+ ```diff
372
+ // ==UserScript==
373
+ // @name My Script
374
+ + // @grant GM_setValue
375
+ // ==/UserScript==
376
+
377
+ GM_setValue('key', 'value')
229
378
  ```
230
379
 
231
- #### Correct code
380
+ #### When not to use it
232
381
 
233
- ```js
234
- // ==UserScript==
235
- // @grant GM_setValue
236
- // ==/UserScript==
382
+ If you use APIs through aliases or wrappers that obscure the original call site.
383
+
384
+ #### See also
385
+
386
+ - [`no-unused-grants`](#no-unused-grants)
387
+ - [`valid-grants`](#valid-grants)
388
+
389
+ <div align="center">
390
+
391
+ [Back to index ↑][epg-rules]
392
+
393
+ </div>
394
+
395
+ ---
396
+
397
+ <h3 id="no-missing-resources">
398
+ <code>no-missing-resources</code>
399
+ <a title="Included in recommended config" href="#usage">
400
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
401
+ <a title="Copy rule URL" target="_blank"
402
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-missing-resources">
403
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
404
+ </h3>
405
+
406
+ > Flags `@resource` references via `GM_getResourceText()` or `GM_getResourceURL()` where no matching `@resource` is declared.
407
+
408
+ #### <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
409
+
410
+ ```diff
411
+ // ==UserScript==
412
+ // @name My Script
413
+ // ==/UserScript==
414
+
415
+ ! const css = GM_getResourceText('myCSS')
416
+ ```
417
+
418
+ #### <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
237
419
 
238
- GM_setValue('key', 'value')
420
+ ```diff
421
+ // ==UserScript==
422
+ // @name My Script
423
+ + // @resource myCSS https://example.com/style.css
424
+ // ==/UserScript==
425
+
426
+ const css = GM_getResourceText('myCSS')
239
427
  ```
240
428
 
241
429
  #### When not to use it
242
430
 
243
- If you access the granted API through an alias or computed property rather than calling it directly.
431
+ If you reference resources through an alias or computed pattern that obscures the call site.
432
+
433
+ #### See also
434
+
435
+ - [`no-unused-resources`](#no-unused-resources)
244
436
 
245
437
  <div align="center">
246
438
 
@@ -250,31 +442,43 @@ If you access the granted API through an alias or computed property rather than
250
442
 
251
443
  ---
252
444
 
253
- ### `no-unused-resources`
445
+ <h3 id="no-unused-grants">
446
+ <code>no-unused-grants</code>
447
+ <a title="Included in recommended config" href="#usage">
448
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
449
+ <a title="Copy rule URL" target="_blank"
450
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-unused-grants">
451
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
452
+ </h3>
254
453
 
255
- > Flags `@resource` entries that are never referenced via `GM_getResourceText()` or `GM_getResourceURL()`.
454
+ > Flags `@grant` entries whose APIs are never used in the script.
256
455
 
257
- #### Incorrect code
456
+ #### <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
258
457
 
259
- ```js
260
- // ==UserScript==
261
- // @resource myCSS https://example.com/style.css
262
- // ==/UserScript==
458
+ ```diff
459
+ // ==UserScript==
460
+ ! // @grant GM_setValue
461
+ // ==/UserScript==
263
462
  ```
264
463
 
265
- #### Correct code
266
-
267
- ```js
268
- // ==UserScript==
269
- // @resource myCSS https://example.com/style.css
270
- // ==/UserScript==
464
+ #### <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
271
465
 
272
- GM_getResourceText('myCSS')
466
+ ```diff
467
+ // ==UserScript==
468
+ // @grant GM_setValue
469
+ // ==/UserScript==
470
+
471
+ + GM_setValue('key', 'value')
273
472
  ```
274
473
 
275
474
  #### When not to use it
276
475
 
277
- If you pass the resource name through a variable or alias rather than using it literally.
476
+ If you access the granted API through an alias or computed property rather than calling it directly.
477
+
478
+ #### See also
479
+
480
+ - [`no-missing-grants`](#no-missing-grants)
481
+ - [`valid-grants`](#valid-grants)
278
482
 
279
483
  <div align="center">
280
484
 
@@ -284,29 +488,42 @@ If you pass the resource name through a variable or alias rather than using it l
284
488
 
285
489
  ---
286
490
 
287
- ### `prefer-match`
491
+ <h3 id="no-unused-resources">
492
+ <code>no-unused-resources</code>
493
+ <a title="Included in recommended config" href="#usage">
494
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
495
+ <a title="Copy rule URL" target="_blank"
496
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-unused-resources">
497
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
498
+ </h3>
288
499
 
289
- > Prefers `@match` over `@include` (deprecated in Chrome MV3).
500
+ > Flags `@resource` entries that are never referenced via `GM_getResourceText()` or `GM_getResourceURL()`.
290
501
 
291
- #### Incorrect code
502
+ #### <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
292
503
 
293
- ```js
294
- // ==UserScript==
295
- // @include https://example.com/*
296
- // ==/UserScript==
504
+ ```diff
505
+ // ==UserScript==
506
+ ! // @resource myCSS https://example.com/style.css
507
+ // ==/UserScript==
297
508
  ```
298
509
 
299
- #### Correct code
510
+ #### <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
300
511
 
301
- ```js
302
- // ==UserScript==
303
- // @match https://example.com/*
304
- // ==/UserScript==
512
+ ```diff
513
+ // ==UserScript==
514
+ // @resource myCSS https://example.com/style.css
515
+ // ==/UserScript==
516
+
517
+ + const css = GM_getResourceText('myCSS')
305
518
  ```
306
519
 
307
520
  #### When not to use it
308
521
 
309
- If you use `@include` in non-MV3 environments, or rely on regex syntax that `@match` cannot express.
522
+ If you pass resource names through variables or aliases rather than use them literally.
523
+
524
+ #### See also
525
+
526
+ - [`no-missing-resources`](#no-missing-resources)
310
527
 
311
528
  <div align="center">
312
529
 
@@ -316,30 +533,41 @@ If you use `@include` in non-MV3 environments, or rely on regex syntax that `@ma
316
533
 
317
534
  ---
318
535
 
319
- ### `require-homepage-pair` (auto-fixable)
536
+ <h3 id="prefer-match">
537
+ <code>prefer-match</code>
538
+ <a title="Included in recommended config" href="#usage">
539
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
540
+ <a title="Copy rule URL" target="_blank"
541
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23prefer-match">
542
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
543
+ </h3>
320
544
 
321
- > Requires `@homepage` and `@homepageURL` to be used together.
545
+ > Prefers `@match` over `@include` to align w/ <a target="_blank" href="https://developer.chrome.com/docs/extensions/reference/api/userScripts/">`chrome.userScripts`</a> syntax.
322
546
 
323
- #### Incorrect code
547
+ #### <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
324
548
 
325
- ```js
326
- // ==UserScript==
327
- // @homepage https://example.com
328
- // ==/UserScript==
549
+ ```diff
550
+ // ==UserScript==
551
+ ! // @include https://example.com/*
552
+ // ==/UserScript==
329
553
  ```
330
554
 
331
- #### Correct code
555
+ #### <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
332
556
 
333
- ```js
334
- // ==UserScript==
335
- // @homepage https://example.com
336
- // @homepageURL https://example.com
337
- // ==/UserScript==
557
+ ```diff
558
+ // ==UserScript==
559
+ - // @include https://example.com/*
560
+ + // @match https://example.com/*
561
+ // ==/UserScript==
338
562
  ```
339
563
 
340
564
  #### When not to use it
341
565
 
342
- If your target manager does not fall back to `@namespace` when a homepage key is missing (which usually produces a broken URL).
566
+ If you rely on `@include` patterns that cannot be expressed using `@match`.
567
+
568
+ #### See also
569
+
570
+ - <a target="_blank" href="https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns">Chrome match patterns</a>
343
571
 
344
572
  <div align="center">
345
573
 
@@ -349,38 +577,93 @@ If your target manager does not fall back to `@namespace` when a homepage key is
349
577
 
350
578
  ---
351
579
 
352
- ### `require-matching-localized-fields`
580
+ <h3 id="require-blank-line-after-meta-block">
581
+ <code>require-blank-line-after-meta-block</code>
582
+ <a title="Included in recommended config" href="#usage">
583
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
584
+ <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
585
+ <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-e8357a?style=flat-square" align="middle"></a>
586
+ <a title="Doesn't catch bugs" href="#require-blank-line-after-meta-block">
587
+ <img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
588
+ <a title="Copy rule URL" target="_blank"
589
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-blank-line-after-meta-block">
590
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
591
+ </h3>
592
+
593
+ > Requires a blank line between the closing `// ==/UserScript==` and the first line of code.
594
+
595
+ #### <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
596
+
597
+ ```diff
598
+ // ==UserScript==
599
+ // @name My Script
600
+ // ==/UserScript==
601
+ ! console.log('hi')
602
+ ```
353
603
 
354
- > Requires that every `@name:xx` has a matching `@description:xx` (and vice versa).
604
+ #### <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
355
605
 
356
- #### Options
606
+ ```diff
607
+ // ==UserScript==
608
+ // @name My Script
609
+ // ==/UserScript==
610
+ +
611
+ console.log('hi')
612
+ ```
613
+
614
+ #### When not to use it
357
615
 
358
- - `ignoreLanguages` (default `[]`) – language codes to skip, e.g. `['fr', 'de']`
616
+ If you don't care about visual separation between metadata and code.
359
617
 
360
- #### Incorrect code
618
+ #### See also
361
619
 
362
- ```js
363
- // ==UserScript==
364
- // @name:en My Script
365
- // @description:en Does something
366
- // @name:fr Mon Script
367
- // ==/UserScript==
620
+ - [`meta-spacing`](#meta-spacing)
621
+
622
+ <div align="center">
623
+
624
+ [Back to index ↑][epg-rules]
625
+
626
+ </div>
627
+
628
+ ---
629
+
630
+ <h3 id="require-homepage-pair">
631
+ <code>require-homepage-pair</code>
632
+ <a title="Only included in strict config" href="#usage">
633
+ <img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
634
+ <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
635
+ <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-e8357a?style=flat-square" align="middle"></a>
636
+ <a title="Copy rule URL" target="_blank"
637
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-homepage-pair">
638
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
639
+ </h3>
640
+
641
+ > Requires `@homepage` and `@homepageURL` to be used together.
642
+
643
+ #### <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
644
+
645
+ ```diff
646
+ // ==UserScript==
647
+ ! // @homepage https://example.com
648
+ // ==/UserScript==
368
649
  ```
369
650
 
370
- #### Correct code
651
+ #### <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
371
652
 
372
- ```js
373
- // ==UserScript==
374
- // @name:en My Script
375
- // @description:en Does something
376
- // @name:fr Mon Script
377
- // @description:fr Fait quelque chose
378
- // ==/UserScript==
653
+ ```diff
654
+ // ==UserScript==
655
+ // @homepage https://example.com
656
+ + // @homepageURL https://example.com
657
+ // ==/UserScript==
379
658
  ```
380
659
 
381
660
  #### When not to use it
382
661
 
383
- If you intentionally provide localized names without descriptions (or vice versa).
662
+ If your target manager does not fall back to `@namespace` when a homepage key is missing (which usually produces a broken URL).
663
+
664
+ #### See also
665
+
666
+ - [`require-update-download-pair`](#require-update-download-pair)
384
667
 
385
668
  <div align="center">
386
669
 
@@ -390,32 +673,58 @@ If you intentionally provide localized names without descriptions (or vice versa
390
673
 
391
674
  ---
392
675
 
393
- ### `require-meta-newline` (auto-fixable)
676
+ <h3 id="require-matching-localized-fields">
677
+ <code>require-matching-localized-fields</code>
678
+ <a title="Included in recommended config" href="#usage">
679
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
680
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
681
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
682
+ <a title="Copy rule URL" target="_blank"
683
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-matching-localized-fields">
684
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
685
+ </h3>
394
686
 
395
- > Requires a newline between the closing `// ==/UserScript==` comment and the first line of code.
687
+ > Requires that every `@name:xx` has a matching `@description:xx` (and vice versa).
396
688
 
397
- #### Incorrect code
689
+ #### Options
398
690
 
399
- ```js
400
- // ==UserScript==
401
- // @name My Script
402
- // ==/UserScript==
403
- console.log('hi')
404
- ```
691
+ - `ignoreLanguages` (default `[]`) – language code(s) to skip (e.g. `'de'` or `['es', 'fr']`)
405
692
 
406
- #### Correct code
693
+ #### <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
407
694
 
408
- ```js
409
- // ==UserScript==
410
- // @name My Script
411
- // ==/UserScript==
695
+ ```diff
696
+ /* eslint greasemonkey/require-matching-localized-fields: ['error', { ignoreLanguages: ['fr'] }] */
697
+
698
+ // ==UserScript==
699
+ // @name:en My Script
700
+ ! // @name:de Mein Skript
701
+ // @name:fr Mon Script
702
+ // @description:en Does something
703
+ // ==/UserScript==
704
+ ```
412
705
 
413
- console.log('hi')
706
+ #### <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
707
+
708
+ ```diff
709
+ /* eslint greasemonkey/require-matching-localized-fields: ['error', { ignoreLanguages: ['fr'] }] */
710
+
711
+ // ==UserScript==
712
+ // @name:en My Script
713
+ // @name:de Mein Skript
714
+ // @name:fr Mon Script
715
+ // @description:en Does something
716
+ + // @description:de Tut etwas
717
+ // ==/UserScript==
414
718
  ```
415
719
 
416
720
  #### When not to use it
417
721
 
418
- If you don't care about visual separation between metadata and code.
722
+ If you intentionally provide localized names without descriptions (or vice versa).
723
+
724
+ #### See also
725
+
726
+ - [`no-duplicate-keys`](#no-duplicate-keys)
727
+ - [`require-required-fields`](#require-required-fields)
419
728
 
420
729
  <div align="center">
421
730
 
@@ -425,7 +734,16 @@ If you don't care about visual separation between metadata and code.
425
734
 
426
735
  ---
427
736
 
428
- ### `require-required-fields`
737
+ <h3 id="require-required-fields">
738
+ <code>require-required-fields</code>
739
+ <a title="Included in recommended config" href="#usage">
740
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
741
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
742
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
743
+ <a title="Copy rule URL" target="_blank"
744
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-required-fields">
745
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
746
+ </h3>
429
747
 
430
748
  > Requires essential metadata fields (`@name`, `@version` by default) and at least one `@match` or `@include`.
431
749
 
@@ -433,28 +751,37 @@ If you don't care about visual separation between metadata and code.
433
751
 
434
752
  - `fields` (default `['name', 'version']`) – override the required field list
435
753
 
436
- #### Incorrect code
754
+ #### <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
437
755
 
438
- ```js
439
- // ==UserScript==
440
- // @name My Script
441
- // ==/UserScript==
756
+ ```diff
757
+ /* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', 'match'] }] */
758
+
759
+ // ==UserScript==
760
+ // @name My Script
761
+ // ==/UserScript==
442
762
  ```
443
763
 
444
- #### Correct code
764
+ #### <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
445
765
 
446
- ```js
447
- // ==UserScript==
448
- // @name My Script
449
- // @version 1.0.0
450
- // @match https://example.com/*
451
- // ==/UserScript==
766
+ ```diff
767
+ /* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', 'match'] }] */
768
+
769
+ // ==UserScript==
770
+ // @name My Script
771
+ + // @version 1.0.0
772
+ + // @match https://example.com/*
773
+ // ==/UserScript==
452
774
  ```
453
775
 
454
776
  #### When not to use it
455
777
 
456
778
  If you use a subset of metadata fields that doesn't match the convention.
457
779
 
780
+ #### See also
781
+
782
+ - [`no-duplicate-keys`](#no-duplicate-keys)
783
+ - [`require-matching-localized-fields`](#require-matching-localized-fields)
784
+
458
785
  <div align="center">
459
786
 
460
787
  [Back to index ↑][epg-rules]
@@ -463,32 +790,49 @@ If you use a subset of metadata fields that doesn't match the convention.
463
790
 
464
791
  ---
465
792
 
466
- ### `require-space-before-meta-key` (auto-fixable)
793
+ <h3 id="require-space-before-meta-key">
794
+ <code>require-space-before-meta-key</code>
795
+ <a title="Included in recommended config" href="#usage">
796
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
797
+ <a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
798
+ <img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-e8357a?style=flat-square" align="middle"></a>
799
+ <a title="Doesn't catch bugs" href="#require-space-before-meta-key">
800
+ <img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
801
+ <a title="Copy rule URL" target="_blank"
802
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-space-before-meta-key">
803
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
804
+ </h3>
467
805
 
468
806
  > Requires exactly one space between `//` and metadata keys.
469
807
 
470
- #### Incorrect code
808
+ #### <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
471
809
 
472
- ```js
473
- // ==UserScript==
474
- //@name My Script
475
- // @version 1.0.0
476
- // ==/UserScript==
810
+ ```diff
811
+ // ==UserScript==
812
+ ! //@name My Script
813
+ // @version 1.0.0
814
+ // ==/UserScript==
477
815
  ```
478
816
 
479
- #### Correct code
817
+ #### <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
480
818
 
481
- ```js
482
- // ==UserScript==
483
- // @name My Script
484
- // @version 1.0.0
485
- // ==/UserScript==
819
+ ```diff
820
+ // ==UserScript==
821
+ - //@name My Script
822
+ + // @name My Script
823
+ // @version 1.0.0
824
+ // ==/UserScript==
486
825
  ```
487
826
 
488
827
  #### When not to use it
489
828
 
490
829
  If you intentionally use a different prefix style.
491
830
 
831
+ #### See also
832
+
833
+ - [`meta-spacing`](#meta-spacing)
834
+ - [`require-blank-line-after-meta-block`](#require-blank-line-after-meta-block)
835
+
492
836
  <div align="center">
493
837
 
494
838
  [Back to index ↑][epg-rules]
@@ -497,31 +841,104 @@ If you intentionally use a different prefix style.
497
841
 
498
842
  ---
499
843
 
500
- ### `require-update-download-pair`
844
+ <h3 id="require-sri-hashing">
845
+ <code>require-sri-hashing</code>
846
+ <a title="Only included in strict config" href="#usage">
847
+ <img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
848
+ <a title="Doesn't catch bugs" href="#require-sri-hashing">
849
+ <img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
850
+ <a title="Accepts object w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
851
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
852
+ <a title="Copy rule URL" target="_blank"
853
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-sri-hashing">
854
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
855
+ </h3>
856
+
857
+ > Requires SRI hashes on `@require` and `@resource` URLs.
858
+
859
+ #### Options
860
+
861
+ - `algorithm` (default `'sha256'`) – SRI hash algorithm to enforce (`'md5'`, `'sha1'`, `'sha256'`, `'sha384'`, `'sha512'`)
862
+
863
+ #### <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
864
+
865
+ ```diff
866
+ /* eslint greasemonkey/require-sri-hashing: ['error', { algorithm: 'sha384' }] */
867
+
868
+ // ==UserScript==
869
+ ! // @require https://example.com/library.js#sha256-BASE64HASH
870
+ ! // @resource logo https://example.com/logo.png#sha256-BASE64HASH
871
+ // ==/UserScript==
872
+ ```
873
+
874
+ #### <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
875
+
876
+ ```diff
877
+ /* eslint greasemonkey/require-sri-hashing: ['error', { algorithm: 'sha384' }] */
878
+
879
+ // ==UserScript==
880
+ - // @require https://example.com/library.js#sha256-BASE64HASH
881
+ - // @resource logo https://example.com/logo.png#sha256-BASE64HASH
882
+ + // @require https://example.com/library.js#sha384-BASE64HASH
883
+ + // @resource logo https://example.com/logo.png#sha384-BASE64HASH
884
+ // ==/UserScript==
885
+ ```
886
+
887
+ #### When not to use it
888
+
889
+ If you prefer shorter URLs, or don't support users who enforce subresource integrity validation via manager settings.
890
+
891
+ #### See also
892
+
893
+ - [`no-invalid-headers`](#no-invalid-headers)
894
+ - [`require-required-fields`](#require-required-fields)
895
+ - <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=sri">Tampermonkey Subresource Integrity</a>
896
+
897
+ <div align="center">
898
+
899
+ [Back to index ↑][epg-rules]
900
+
901
+ </div>
902
+
903
+ ----
904
+
905
+ <h3 id="require-update-download-pair">
906
+ <code>require-update-download-pair</code>
907
+ <a title="Only included in strict config" href="#usage">
908
+ <img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
909
+ <a title="Copy rule URL" target="_blank"
910
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-update-download-pair">
911
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
912
+ </h3>
501
913
 
502
914
  > Requires `@updateURL` and `@downloadURL` to be used together.
503
915
 
504
- #### Incorrect code
916
+ #### <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
505
917
 
506
- ```js
507
- // ==UserScript==
508
- // @downloadURL https://example.com/script.user.js
509
- // ==/UserScript==
918
+ ```diff
919
+ // ==UserScript==
920
+ ! // @downloadURL https://example.com/script.user.js
921
+ // ==/UserScript==
510
922
  ```
511
923
 
512
- #### Correct code
924
+ #### <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
513
925
 
514
- ```js
515
- // ==UserScript==
516
- // @downloadURL https://example.com/script.user.js
517
- // @updateURL https://example.com/script.meta.js
518
- // ==/UserScript==
926
+ ```diff
927
+ // ==UserScript==
928
+ // @downloadURL https://example.com/script.user.js
929
+ + // @updateURL https://example.com/script.meta.js
930
+ // ==/UserScript==
519
931
  ```
520
932
 
521
933
  #### When not to use it
522
934
 
523
935
  If you don't target Tampermonkey (where `@updateURL` avoids re-downloading the full script on every update check).
524
936
 
937
+ #### See also
938
+
939
+ - <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=update_url#meta:updateURL"><code>@updateURL</code></a>
940
+ - [`require-homepage-pair`](#require-homepage-pair)
941
+
525
942
  <div align="center">
526
943
 
527
944
  [Back to index ↑][epg-rules]
@@ -530,10 +947,30 @@ If you don't target Tampermonkey (where `@updateURL` avoids re-downloading the f
530
947
 
531
948
  ---
532
949
 
533
- ### `require-user-js-filename`
950
+ <h3 id="require-user-js-filename">
951
+ <code>require-user-js-filename</code>
952
+ <a title="Included in recommended config" href="#usage">
953
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
954
+ <a title="Copy rule URL" target="_blank"
955
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-user-js-filename">
956
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
957
+ </h3>
534
958
 
535
959
  > Requires that files containing a userscript metadata block end in `.user.js`.
536
960
 
961
+ #### <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 filename
962
+
963
+ ```diff
964
+ ! /my-userscript.js
965
+ ```
966
+
967
+ #### <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 filename
968
+
969
+ ```diff
970
+ - /my-userscript.js
971
+ + /my-userscript.user.js
972
+ ```
973
+
537
974
  #### When not to use it
538
975
 
539
976
  If you intentionally name your userscripts something other than `*.user.js`.
@@ -546,7 +983,16 @@ If you intentionally name your userscripts something other than `*.user.js`.
546
983
 
547
984
  ---
548
985
 
549
- ### `valid-field-values`
986
+ <h3 id="valid-field-values">
987
+ <code>valid-field-values</code>
988
+ <a title="Included in recommended config" href="#usage">
989
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
990
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
991
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
992
+ <a title="Copy rule URL" target="_blank"
993
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23valid-field-values">
994
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
995
+ </h3>
550
996
 
551
997
  > Validates `@version` (semver or date format) and URL fields (`@homepage`, `@icon`, `@icon64`, `@require`, `@resource`, `@source`, `@website`, any key ending in `URL` case-insensitively).
552
998
 
@@ -554,28 +1000,39 @@ If you intentionally name your userscripts something other than `*.user.js`.
554
1000
 
555
1001
  - `versionType` (default `'semver'`) – version format to enforce (`'semver'`, `'date'`)
556
1002
 
557
- #### Incorrect code
1003
+ #### <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
558
1004
 
559
- ```js
560
- // ==UserScript==
561
- // @version abc
562
- // @homepage not-a-url
563
- // ==/UserScript==
1005
+ ```diff
1006
+ /* eslint greasemonkey/valid-field-values: ['error', { versionType: 'semver' }] */
1007
+
1008
+ // ==UserScript==
1009
+ ! // @version abc
1010
+ ! // @homepage not-a-url
1011
+ // ==/UserScript==
564
1012
  ```
565
1013
 
566
- #### Correct code
567
-
568
- ```js
569
- // ==UserScript==
570
- // @version 1.0.0
571
- // @homepage https://example.com
572
- // ==/UserScript==
1014
+ #### <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
1015
+
1016
+ ```diff
1017
+ /* eslint greasemonkey/valid-field-values: ['error', { versionType: 'semver' }] */
1018
+
1019
+ // ==UserScript==
1020
+ - // @version abc
1021
+ + // @version 1.0.0
1022
+ - // @homepage not-a-url
1023
+ + // @homepage https://example.com
1024
+ // ==/UserScript==
573
1025
  ```
574
1026
 
575
1027
  #### When not to use it
576
1028
 
577
1029
  If you use a custom versioning scheme or URLs that aren't standard.
578
1030
 
1031
+ #### See also
1032
+
1033
+ - [`no-missing-resources`](#no-missing-resources)
1034
+ - [`valid-grants`](#valid-grants)
1035
+
579
1036
  <div align="center">
580
1037
 
581
1038
  [Back to index ↑][epg-rules]
@@ -584,38 +1041,57 @@ If you use a custom versioning scheme or URLs that aren't standard.
584
1041
 
585
1042
  ---
586
1043
 
587
- ### `valid-grants`
1044
+ <h3 id="valid-grants">
1045
+ <code>valid-grants</code>
1046
+ <a title="Included in recommended config" href="#usage">
1047
+ <img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
1048
+ <a title="Accepts array w/ options" target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules#option-defaults">
1049
+ <img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
1050
+ <a title="Copy rule URL" target="_blank"
1051
+ href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23valid-grants">
1052
+ <img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
1053
+ </h3>
588
1054
 
589
- > Disallows invalid `@grant` values.
1055
+ > Disallows invalid or unsupported `@grant` values.
590
1056
 
591
1057
  #### Options
592
1058
 
593
- - `allowedGrants` (default `[]`) – additional `@grant` values to allow beyond the built-in list (covering Tampermonkey, Violentmonkey and ScriptCat)
1059
+ - `target` (default `'all'`) – manager(s) whose grants to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`, `'scriptcat'`), as a single string or array
1060
+ - `allowedGrants` (default `[]`) – additional `@grant` values to allow beyond the built-in list (covering Tampermonkey, Violentmonkey and ScriptCat), as a single string or array
594
1061
 
595
- #### Incorrect code
1062
+ #### <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
596
1063
 
597
- ```js
598
- // ==UserScript==
599
- // @grant GM_notificaiton
600
- // @grant gm_setvalue
601
- // @grant GM_xmlHttpRequest
602
- // ==/UserScript==
1064
+ ```diff
1065
+ /* eslint greasemonkey/valid-grants: ['error', { target: 'tampermonkey' }] */
1066
+
1067
+ // ==UserScript==
1068
+ ! // @grant GM_notificaiton
1069
+ ! // @grant CAT.agent.dom
1070
+ // ==/UserScript==
603
1071
  ```
604
1072
 
605
- #### Correct code
606
-
607
- ```js
608
- // ==UserScript==
609
- // @grant GM_notification
610
- // @grant GM_setValue
611
- // @grant GM_xmlhttpRequest
612
- // ==/UserScript==
1073
+ #### <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
1074
+
1075
+ ```diff
1076
+ /* eslint greasemonkey/valid-grants: ['error', { target: 'tampermonkey' }] */
1077
+
1078
+ // ==UserScript==
1079
+ - // @grant GM_notificaiton
1080
+ + // @grant GM_notification
1081
+ - // @grant CAT.agent.dom
1082
+ + // @grant GM_getTab
1083
+ // ==/UserScript==
613
1084
  ```
614
1085
 
615
1086
  #### When not to use it
616
1087
 
617
1088
  If you don't want `@grant` validation at all. For individual uncovered grants, prefer whitelisting via `allowedGrants` over disabling the rule.
618
1089
 
1090
+ #### See also
1091
+
1092
+ - [`no-missing-grants`](#no-missing-grants)
1093
+ - [`no-unused-grants`](#no-unused-grants)
1094
+
619
1095
  <div align="center">
620
1096
 
621
1097
  [Back to index ↑][epg-rules]
@@ -624,33 +1100,34 @@ If you don't want `@grant` validation at all. For individual uncovered grants, p
624
1100
 
625
1101
  ---
626
1102
 
627
- ## Shield
1103
+ ## Shield embed
628
1104
 
629
- <a href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme">
630
- <img height=32 alt="[Linted by eslint-plugin-greasemonkey]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-4b32c3?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
1105
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme">
1106
+ <img height=32 alt="[Linted by eslint-plugin-greasemonkey]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-black?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
631
1107
 
632
1108
  #### HTML
633
1109
 
634
1110
  ```html
635
1111
  <a href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme">
636
- <img height=32 alt="[Linted by eslint-plugin-greasemonkey]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-4b32c3?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
1112
+ <img height=32 alt="[Linted by eslint-plugin-greasemonkey]" src="https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-black?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge"></a>
637
1113
  ```
638
1114
 
639
1115
  #### Markdown
640
1116
 
641
1117
  ```md
642
- [![Linted by eslint-plugin-greasemonkey](https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-4b32c3?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge)](https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme)
1118
+ [![Linted by eslint-plugin-greasemonkey](https://img.shields.io/badge/Linted_by-eslint--plugin--greasemonkey-black?logo=eslint&logoColor=white&labelColor=464646&style=for-the-badge)](https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme)
643
1119
  ```
644
1120
 
645
1121
  ---
646
1122
 
647
1123
  ## License
648
1124
 
649
- [<img height=62 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/9e820e5d51/assets/images/logos/mit-license/red-pixelated.png">][epg-license]
1125
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/src/branch/main/docs/LICENSE.md">
1126
+ <img height=62 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/9e820e5d51/assets/images/logos/mit-license/red-pixelated.png"></a>
650
1127
 
651
1128
  <details>
652
1129
 
653
- <summary>Copyright © 2026 <a href="https://codeberg.org/adamlui">Adam Lui</a> under the <a href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/src/branch/main/docs/LICENSE.md">MIT license</a>.</summary><br>
1130
+ <summary>Copyright © 2026 <a target="_blank" href="https://codeberg.org/adamlui"><b>Adam Lui</b></a> under the <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/src/branch/main/docs/LICENSE.md"><b>MIT license</b></a>.</summary><br>
654
1131
 
655
1132
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
656
1133
 
@@ -664,24 +1141,20 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
664
1141
 
665
1142
  ## Related
666
1143
 
667
- <!-- eslint-plugin-constructors -->
1144
+ <!-- eslint-plugin-constructors -->
668
1145
 
669
- ### [<img height=15 width=auto src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/e9128375d4/assets/images/icons/eslint/purple/icon16.png">][epc-readme] [eslint-plugin-constructors][epc-readme]
1146
+ ### <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/#readme"><img height=15 width=auto src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/e9128375d4/assets/images/icons/eslint/purple/icon16.png"> eslint-plugin-constructors</a>
670
1147
 
671
1148
  <details>
672
1149
 
673
1150
  <summary>ESLint rules for JavaScript constructor usage.</summary><br>
674
1151
 
675
- > [<img width=555 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-constructors/6eeba7b2cb/assets/images/screenshots/vs-code/squiggles.png">][epc-readme]
1152
+ > <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/#readme"><img width=555 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-constructors/6eeba7b2cb/assets/images/screenshots/vs-code/squiggles.png"></a>
676
1153
 
677
- > [**Install**][epc-install] / [Readme][epc-readme] / [Supported rules][epc-rules]
1154
+ > <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/#installation"><b>Install</b></a> / <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/#readme">Readme</a> / <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-constructors/#supported-rules">Supported rules</a>
678
1155
 
679
1156
  </details>
680
1157
 
681
- [epc-install]: https://codeberg.org/adamlui/eslint-plugin-constructors/#installation
682
- [epc-readme]: https://codeberg.org/adamlui/eslint-plugin-constructors/#readme
683
- [epc-rules]: https://codeberg.org/adamlui/eslint-plugin-constructors/#supported-rules
684
-
685
1158
  <!-- Footer -->
686
1159
 
687
1160
  <img height=10 width="100%" src="https://cdn.staticdelivr.com/gl/adamlui/ai-web-extensions/d11d2ee/assets/images/separators/gradient-aqua.png">
@@ -689,15 +1162,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
689
1162
  <div align="center">
690
1163
 
691
1164
  <picture><source media="(prefers-color-scheme: dark)" srcset="https://cdn.staticdelivr.com/gl/adamlui/js-utils/6b0d399/assets/images/icons/tag/white/icon16.svg"><img height=14 src="https://cdn.staticdelivr.com/gl/adamlui/js-utils/6b0d399/assets/images/icons/tag/dark-gray/icon16.svg"></picture>
692
- [**Latest releases**][epg-releases] / [Report a bug][epg-bugs] / [More packages by author][adamlui-npmx] / [Back to top ↑](#top)
1165
+ <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/releases"><b>Latest releases</b></a> / <a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/issues/new">Report a bug</a> / <a target="_blank" href="https://npmx.dev/org/adamlui">More packages by author</a> / [Back to top ↑](#top)
693
1166
 
694
1167
  </div>
695
1168
 
696
1169
  <!-- Links -->
697
1170
 
698
- [adamlui-npmx]: https://npmx.dev/org/adamlui
699
- [epg-bugs]: https://codeberg.org/adamlui/eslint-plugin-greasemonkey/issues/new
700
- [epg-license]: https://codeberg.org/adamlui/eslint-plugin-constructors/src/branch/main/docs/LICENSE.md
701
- [epg-npm]: https://www.npmjs.com/package/eslint-plugin-greasemonkey
702
- [epg-releases]: https://codeberg.org/adamlui/eslint-plugin-greasemonkey/releases
703
1171
  [epg-rules]: #supported-rules