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