eslint-plugin-greasemonkey 1.5.2 → 1.7.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 +83 -48
- package/package.json +5 -5
- package/src/data/apis.js +19 -0
- package/src/data/deprecations.js +6 -2
- package/src/data/headers.js +14 -7
- package/src/index.js +8 -12
- package/src/rules/meta-order.js +10 -5
- package/src/rules/meta-spacing.js +12 -3
- package/src/rules/no-invalid-grants.js +1 -1
- package/src/rules/require-required-fields.js +7 -2
- package/src/rules/require-sri-hashing.js +28 -16
- package/src/rules/valid-field-values.js +23 -0
- package/src/utils.js +81 -6
package/README.md
CHANGED
|
@@ -46,13 +46,13 @@ export default [
|
|
|
46
46
|
rules: {
|
|
47
47
|
...greasemonkey.configs.recommended.rules,
|
|
48
48
|
'greasemonkey/no-unused-resources': 'off', // selectively disable rule
|
|
49
|
-
'greasemonkey/meta-spacing': ['error', 2]
|
|
49
|
+
'greasemonkey/meta-spacing': ['error', { spaces: 2 }] // selectively tweak rule
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
]
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `greasemonkey`, `scriptcat`
|
|
55
|
+
Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `greasemonkey`, `scriptcat`, `adguard`
|
|
56
56
|
|
|
57
57
|
**Note:** Rule config is either a severity string (`'off'`, `'warn'`, `'error'`) or an array `[severity, options]` when the rule accepts options.
|
|
58
58
|
|
|
@@ -70,10 +70,10 @@ Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `gr
|
|
|
70
70
|
<a title="Only included in strict config" href="#usage">
|
|
71
71
|
<img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
|
|
72
72
|
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
73
|
-
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-
|
|
73
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
74
74
|
<a title="Doesn't prevent bugs" href="#meta-order">
|
|
75
75
|
<img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
|
|
76
|
-
<a title="Accepts array w/ options"
|
|
76
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
77
77
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
78
78
|
<a title="Copy rule URL" target="_blank"
|
|
79
79
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23meta-order">
|
|
@@ -86,7 +86,7 @@ Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `gr
|
|
|
86
86
|
|
|
87
87
|
#### Options
|
|
88
88
|
|
|
89
|
-
- `
|
|
89
|
+
- `order` (default `[]`) – metadata keys in custom order (e.g. `['license', 'namespace']`) to selectively override built-in order with
|
|
90
90
|
|
|
91
91
|
#### <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
|
|
92
92
|
|
|
@@ -97,7 +97,7 @@ Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `gr
|
|
|
97
97
|
// @description A script
|
|
98
98
|
! // @namespace example.com
|
|
99
99
|
// @author John Doe
|
|
100
|
-
|
|
100
|
+
// @license MIT
|
|
101
101
|
// ==/UserScript==
|
|
102
102
|
```
|
|
103
103
|
|
|
@@ -110,10 +110,9 @@ Avaialble bundles: `recommended`, `strict`, `tampermonkey`, `violentmonkey`, `gr
|
|
|
110
110
|
// @description A script
|
|
111
111
|
- // @namespace example.com
|
|
112
112
|
// @author John Doe
|
|
113
|
-
- // @license MIT
|
|
114
113
|
+ // @namespace example.com
|
|
115
114
|
+ // @version 1.0.0
|
|
116
|
-
|
|
115
|
+
// @license MIT
|
|
117
116
|
// ==/UserScript==
|
|
118
117
|
```
|
|
119
118
|
|
|
@@ -145,10 +144,10 @@ If you don't care about the order of metadata fields.
|
|
|
145
144
|
<a title="Included in recommended config" href="#usage">
|
|
146
145
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
147
146
|
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
148
|
-
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-
|
|
147
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
149
148
|
<a title="Doesn't prevent bugs" href="#meta-spacing">
|
|
150
149
|
<img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
|
|
151
|
-
<a title="Accepts array w/ options"
|
|
150
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
152
151
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
153
152
|
<a title="Copy rule URL" target="_blank"
|
|
154
153
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23meta-spacing">
|
|
@@ -161,12 +160,12 @@ If you don't care about the order of metadata fields.
|
|
|
161
160
|
|
|
162
161
|
#### Options
|
|
163
162
|
|
|
164
|
-
- `
|
|
163
|
+
- `spaces` (default `3`) – minimum spaces after the longest key
|
|
165
164
|
|
|
166
165
|
#### <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
|
|
167
166
|
|
|
168
167
|
```diff
|
|
169
|
-
/* eslint greasemonkey/meta-spacing: ['error', 1] */
|
|
168
|
+
/* eslint greasemonkey/meta-spacing: ['error', { spaces: 1 }] */
|
|
170
169
|
|
|
171
170
|
// ==UserScript==
|
|
172
171
|
! // @name My Script
|
|
@@ -178,7 +177,7 @@ If you don't care about the order of metadata fields.
|
|
|
178
177
|
#### <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
|
|
179
178
|
|
|
180
179
|
```diff
|
|
181
|
-
/* eslint greasemonkey/meta-spacing: ['error', 1] */
|
|
180
|
+
/* eslint greasemonkey/meta-spacing: ['error', { spaces: 1 }] */
|
|
182
181
|
|
|
183
182
|
// ==UserScript==
|
|
184
183
|
- // @name My Script
|
|
@@ -276,11 +275,21 @@ If you intentionally repeat single-value keys.
|
|
|
276
275
|
|
|
277
276
|
</details>
|
|
278
277
|
|
|
279
|
-
<h3 id="no-invalid-grants">
|
|
278
|
+
<h3 id="no-invalid-grants"><a deprecated id="valid-grants"></a>
|
|
280
279
|
<code>no-invalid-grants</code>
|
|
281
280
|
<a title="Included in recommended config" href="#usage">
|
|
282
281
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
283
|
-
<a title="
|
|
282
|
+
<a title="Optimized in tampermonkey config" href="#usage">
|
|
283
|
+
<img height=18 alt="[Tampermonkey]" src="https://img.shields.io/badge/Tampermonkey-00485b?style=flat-square" align="middle"></a>
|
|
284
|
+
<a title="Optimized in violentmonkey config" href="#usage">
|
|
285
|
+
<img height=18 alt="[Violentmonkey]" src="https://img.shields.io/badge/Violentmonkey-f01e07?style=flat-square" align="middle"></a>
|
|
286
|
+
<a title="Optimized in greasemonkey config" href="#usage">
|
|
287
|
+
<img height=18 alt="[Greasemonkey]" src="https://img.shields.io/badge/Greasemonkey-6c4d8c?style=flat-square" align="middle"></a>
|
|
288
|
+
<a title="Optimized in scriptcat config" href="#usage">
|
|
289
|
+
<img height=18 alt="[ScriptCat]" src="https://img.shields.io/badge/ScriptCat-1296dc?style=flat-square" align="middle"></a>
|
|
290
|
+
<a title="Optimized in adguard config" href="#usage">
|
|
291
|
+
<img height=18 alt="[AdGuard]" src="https://img.shields.io/badge/AdGuard-68bd72?style=flat-square" align="middle"></a>
|
|
292
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
284
293
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
285
294
|
<a title="Copy rule URL" target="_blank"
|
|
286
295
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-invalid-grants">
|
|
@@ -293,8 +302,8 @@ If you intentionally repeat single-value keys.
|
|
|
293
302
|
|
|
294
303
|
#### Options
|
|
295
304
|
|
|
296
|
-
- `target` (default `'all'`) – manager(s) whose grants to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`, `'scriptcat'`), as a single string or array
|
|
297
|
-
- `allowedGrants` (default `[]`) – additional `@grant` values to allow beyond the built-in list (covering Tampermonkey, Violentmonkey and
|
|
305
|
+
- `target` (default `'all'`) – manager(s) whose grants to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`, `'scriptcat'`, `'adguard'`), as a single string or array
|
|
306
|
+
- `allowedGrants` (default `[]`) – additional `@grant` values to allow beyond the built-in list (covering Tampermonkey, Violentmonkey, Greasemonkey, ScriptCat and AdGuard), as a single string or array
|
|
298
307
|
|
|
299
308
|
#### <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
|
|
300
309
|
|
|
@@ -363,7 +372,17 @@ If you don't want `@grant` validation at all. For individual uncovered grants, p
|
|
|
363
372
|
<code>no-invalid-headers</code>
|
|
364
373
|
<a title="Included in recommended config" href="#usage">
|
|
365
374
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
366
|
-
<a title="
|
|
375
|
+
<a title="Optimized in tampermonkey config" href="#usage">
|
|
376
|
+
<img height=18 alt="[Tampermonkey]" src="https://img.shields.io/badge/Tampermonkey-00485b?style=flat-square" align="middle"></a>
|
|
377
|
+
<a title="Optimized in violentmonkey config" href="#usage">
|
|
378
|
+
<img height=18 alt="[Violentmonkey]" src="https://img.shields.io/badge/Violentmonkey-f01e07?style=flat-square" align="middle"></a>
|
|
379
|
+
<a title="Optimized in greasemonkey config" href="#usage">
|
|
380
|
+
<img height=18 alt="[Greasemonkey]" src="https://img.shields.io/badge/Greasemonkey-6c4d8c?style=flat-square" align="middle"></a>
|
|
381
|
+
<a title="Optimized in scriptcat config" href="#usage">
|
|
382
|
+
<img height=18 alt="[ScriptCat]" src="https://img.shields.io/badge/ScriptCat-1296dc?style=flat-square" align="middle"></a>
|
|
383
|
+
<a title="Optimized in adguard config" href="#usage">
|
|
384
|
+
<img height=18 alt="[AdGuard]" src="https://img.shields.io/badge/AdGuard-68bd72?style=flat-square" align="middle"></a>
|
|
385
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
367
386
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
368
387
|
<a title="Copy rule URL" target="_blank"
|
|
369
388
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23no-invalid-headers">
|
|
@@ -376,7 +395,7 @@ If you don't want `@grant` validation at all. For individual uncovered grants, p
|
|
|
376
395
|
|
|
377
396
|
#### Options
|
|
378
397
|
|
|
379
|
-
- `target` (default `'all'`) – manager(s) whose headers to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`), as a single string or array
|
|
398
|
+
- `target` (default `'all'`) – manager(s) whose headers to validate against (`'all'`, `'tampermonkey'`, `'violentmonkey'`, `'greasemonkey'`, `'scriptcat'`, `'adguard'`), as a single string or array
|
|
380
399
|
- `allowedHeaders` (default `[]`) – additional headers to allow (strings or regexes), as a single value or array
|
|
381
400
|
|
|
382
401
|
#### <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,8 +406,8 @@ If you don't want `@grant` validation at all. For individual uncovered grants, p
|
|
|
387
406
|
// ==UserScript==
|
|
388
407
|
// @name My Script
|
|
389
408
|
! // @runin normal-tabs
|
|
409
|
+
! // @customURI https://example.com
|
|
390
410
|
! // @inject-into page
|
|
391
|
-
// @customURL https://example.com
|
|
392
411
|
// ==/UserScript==
|
|
393
412
|
```
|
|
394
413
|
|
|
@@ -401,8 +420,9 @@ If you don't want `@grant` validation at all. For individual uncovered grants, p
|
|
|
401
420
|
// @name My Script
|
|
402
421
|
- // @runin normal-tabs
|
|
403
422
|
+ // @run-in normal-tabs
|
|
423
|
+
- // @customURI https://example.com
|
|
424
|
+
+ // @customURL https://example.com
|
|
404
425
|
- // @inject-into page
|
|
405
|
-
// @customURL https://example.com
|
|
406
426
|
// ==/UserScript==
|
|
407
427
|
```
|
|
408
428
|
|
|
@@ -440,7 +460,7 @@ If your script relies heavily on custom headers and you'd rather not maintain `a
|
|
|
440
460
|
|
|
441
461
|
<details>
|
|
442
462
|
|
|
443
|
-
<summary>Flags API calls (e.g. <code>GM_setValue</code>, <code>
|
|
463
|
+
<summary>Flags API calls (e.g. <code>GM_setValue</code>, <code>CAT.agent.dom</code>) used w/o a matching <code>@grant</code> declaration.</summary>
|
|
444
464
|
|
|
445
465
|
#### <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
|
|
446
466
|
|
|
@@ -497,7 +517,7 @@ If you use APIs through aliases or wrappers that obscure the original call site.
|
|
|
497
517
|
|
|
498
518
|
<details>
|
|
499
519
|
|
|
500
|
-
<summary>Flags <code>@resource</code> references via <code>
|
|
520
|
+
<summary>Flags <code>@resource</code> references via <code>GM_getResource<Text|URL>()</code> w/o a matching <code>@resource</code> declaration.</summary>
|
|
501
521
|
|
|
502
522
|
#### <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
|
|
503
523
|
|
|
@@ -660,7 +680,7 @@ If you pass resource names through variables or aliases rather than use them lit
|
|
|
660
680
|
|
|
661
681
|
<details>
|
|
662
682
|
|
|
663
|
-
<summary>Prefers <code>@match</code> over <code>@include</code>
|
|
683
|
+
<summary>Prefers <code>@match</code> over <code>@include</code> for safer URL matching.</summary>
|
|
664
684
|
|
|
665
685
|
#### <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
|
|
666
686
|
|
|
@@ -689,7 +709,8 @@ If you rely on `@include` patterns that cannot be expressed using `@match`.
|
|
|
689
709
|
|
|
690
710
|
#### See also
|
|
691
711
|
|
|
692
|
-
- <a target="_blank" href="https://
|
|
712
|
+
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?locale=en&q=include#meta:match">Tampermonkey — `@match`</a>
|
|
713
|
+
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?locale=en&q=include#meta:include">Tampermonkey — `@include`</a>
|
|
693
714
|
|
|
694
715
|
<div align="center">
|
|
695
716
|
|
|
@@ -701,12 +722,12 @@ If you rely on `@include` patterns that cannot be expressed using `@match`.
|
|
|
701
722
|
|
|
702
723
|
</details>
|
|
703
724
|
|
|
704
|
-
<h3 id="require-blank-line-after-meta-block">
|
|
725
|
+
<h3 id="require-blank-line-after-meta-block"><a deprecated id="require-meta-newline"></a>
|
|
705
726
|
<code>require-blank-line-after-meta-block</code>
|
|
706
727
|
<a title="Included in recommended config" href="#usage">
|
|
707
728
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
708
729
|
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
709
|
-
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-
|
|
730
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
710
731
|
<a title="Doesn't prevent bugs" href="#require-blank-line-after-meta-block">
|
|
711
732
|
<img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
|
|
712
733
|
<a title="Copy rule URL" target="_blank"
|
|
@@ -769,7 +790,7 @@ If you don't care about visual separation between metadata and code.
|
|
|
769
790
|
<a title="Only included in strict config" href="#usage">
|
|
770
791
|
<img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
|
|
771
792
|
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
772
|
-
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-
|
|
793
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
773
794
|
<a title="Copy rule URL" target="_blank"
|
|
774
795
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-homepage-pair">
|
|
775
796
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -798,7 +819,7 @@ If you don't care about visual separation between metadata and code.
|
|
|
798
819
|
|
|
799
820
|
#### When not to use it
|
|
800
821
|
|
|
801
|
-
If
|
|
822
|
+
If you don't care about ensuring homepage URL compatibility across different managers.
|
|
802
823
|
|
|
803
824
|
#### Introduced in
|
|
804
825
|
|
|
@@ -807,6 +828,8 @@ If your target manager does not fall back to `@namespace` when a homepage key is
|
|
|
807
828
|
#### See also
|
|
808
829
|
|
|
809
830
|
- [`require-update-download-pair`](#require-update-download-pair)
|
|
831
|
+
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=homepage#meta:homepage">Tampermonkey `@homepage`, `@homepageURL`, `@website`, `@source`</a>
|
|
832
|
+
- <a target="_blank" href="https://docs.scriptcat.org/en/docs/dev/meta/#homepage-homepageurl-website">ScriptCat `@homepage`, `@homepageURL`, `@website`</a>
|
|
810
833
|
|
|
811
834
|
<div align="center">
|
|
812
835
|
|
|
@@ -822,7 +845,7 @@ If your target manager does not fall back to `@namespace` when a homepage key is
|
|
|
822
845
|
<code>require-matching-localized-fields</code>
|
|
823
846
|
<a title="Included in recommended config" href="#usage">
|
|
824
847
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
825
|
-
<a title="Accepts array w/ options"
|
|
848
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
826
849
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
827
850
|
<a title="Copy rule URL" target="_blank"
|
|
828
851
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-matching-localized-fields">
|
|
@@ -891,7 +914,7 @@ If you intentionally provide localized names without descriptions (or vice versa
|
|
|
891
914
|
<code>require-required-fields</code>
|
|
892
915
|
<a title="Included in recommended config" href="#usage">
|
|
893
916
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
894
|
-
<a title="Accepts array w/ options"
|
|
917
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
895
918
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
896
919
|
<a title="Copy rule URL" target="_blank"
|
|
897
920
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-required-fields">
|
|
@@ -900,16 +923,17 @@ If you intentionally provide localized names without descriptions (or vice versa
|
|
|
900
923
|
|
|
901
924
|
<details>
|
|
902
925
|
|
|
903
|
-
<summary>Requires essential metadata fields (<code>@name</code>, <code>@version</code>
|
|
926
|
+
<summary>Requires essential metadata fields (<code>@name</code>, <code>@version</code> and at least one <code>@match</code> or <code>@include</code>).</summary>
|
|
904
927
|
|
|
905
928
|
#### Options
|
|
906
929
|
|
|
907
930
|
- `fields` (default `['name', 'version']`) – override the required field list
|
|
931
|
+
- `requireMatchOrInclude` (default `true`) – require `@match` or `@include`
|
|
908
932
|
|
|
909
933
|
#### <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
|
|
910
934
|
|
|
911
935
|
```diff
|
|
912
|
-
/* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', '
|
|
936
|
+
/* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', 'license'] }] */
|
|
913
937
|
|
|
914
938
|
// ==UserScript==
|
|
915
939
|
// @name My Script
|
|
@@ -919,18 +943,19 @@ If you intentionally provide localized names without descriptions (or vice versa
|
|
|
919
943
|
#### <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
|
|
920
944
|
|
|
921
945
|
```diff
|
|
922
|
-
/* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', '
|
|
946
|
+
/* eslint greasemonkey/require-required-fields: ['error', { fields: ['name', 'version', 'license'] }] */
|
|
923
947
|
|
|
924
948
|
// ==UserScript==
|
|
925
949
|
// @name My Script
|
|
926
950
|
+ // @version 1.0.0
|
|
951
|
+
+ // @license MIT
|
|
927
952
|
+ // @match https://example.com/*
|
|
928
953
|
// ==/UserScript==
|
|
929
954
|
```
|
|
930
955
|
|
|
931
956
|
#### When not to use it
|
|
932
957
|
|
|
933
|
-
If
|
|
958
|
+
If your script is a work-in-progress, or metadata is injected at build time.
|
|
934
959
|
|
|
935
960
|
#### Introduced in
|
|
936
961
|
|
|
@@ -956,7 +981,7 @@ If you use a subset of metadata fields that doesn't match the convention.
|
|
|
956
981
|
<a title="Included in recommended config" href="#usage">
|
|
957
982
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
958
983
|
<a title="Accepts eslint --fix" target="_blank" href="https://eslint.org/docs/latest/use/command-line-interface#--fix">
|
|
959
|
-
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-
|
|
984
|
+
<img height=18 alt="[Auto-fixable]" src="https://img.shields.io/badge/Auto--fixable-3b5bdb?style=flat-square" align="middle"></a>
|
|
960
985
|
<a title="Doesn't prevent bugs" href="#require-space-before-meta-key">
|
|
961
986
|
<img height=18 alt="[Stylistic]" src="https://img.shields.io/badge/Stylistic-af68ff?style=flat-square" align="middle"></a>
|
|
962
987
|
<a title="Copy rule URL" target="_blank"
|
|
@@ -1014,7 +1039,7 @@ If you intentionally use a different prefix style.
|
|
|
1014
1039
|
<code>require-sri-hashing</code>
|
|
1015
1040
|
<a title="Only included in strict config" href="#usage">
|
|
1016
1041
|
<img height=18 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
|
|
1017
|
-
<a title="Accepts array w/ options"
|
|
1042
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
1018
1043
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
1019
1044
|
<a title="Copy rule URL" target="_blank"
|
|
1020
1045
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-sri-hashing">
|
|
@@ -1027,7 +1052,7 @@ If you intentionally use a different prefix style.
|
|
|
1027
1052
|
|
|
1028
1053
|
#### Options
|
|
1029
1054
|
|
|
1030
|
-
- `algorithm` (default `'sha256'`) – SRI hash algorithm to enforce (`'md5'`, `'sha1'`, `'sha256'`, `'sha384'`, `'sha512'`)
|
|
1055
|
+
- `algorithm` (default `'sha256'`) – SRI hash algorithm(s) to enforce (`'md5'`, `'sha1'`, `'sha256'`, `'sha384'`, `'sha512'`), as a single string or array
|
|
1031
1056
|
|
|
1032
1057
|
#### <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
|
|
1033
1058
|
|
|
@@ -1067,7 +1092,8 @@ If you prefer shorter URLs, or don't support users who strictly enforce subresou
|
|
|
1067
1092
|
|
|
1068
1093
|
- [`no-invalid-headers`](#no-invalid-headers)
|
|
1069
1094
|
- [`require-required-fields`](#require-required-fields)
|
|
1070
|
-
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=sri">Tampermonkey Subresource Integrity</a>
|
|
1095
|
+
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=sri">Tampermonkey — Subresource Integrity</a>
|
|
1096
|
+
- <a target="_blank" href="https://docs.scriptcat.org/en/docs/dev/meta/#resource-integrity-verification">ScriptCat — Resource Integrity Verification</a>
|
|
1071
1097
|
|
|
1072
1098
|
<div align="center">
|
|
1073
1099
|
|
|
@@ -1081,8 +1107,12 @@ If you prefer shorter URLs, or don't support users who strictly enforce subresou
|
|
|
1081
1107
|
|
|
1082
1108
|
<h3 id="require-update-download-pair">
|
|
1083
1109
|
<code>require-update-download-pair</code>
|
|
1084
|
-
<a title="
|
|
1085
|
-
<img height=18 alt="[Strict
|
|
1110
|
+
<a title="Included in strict config" href="#usage">
|
|
1111
|
+
<img height=18 alt="[Strict]" src="https://img.shields.io/badge/Strict-ff8c42?style=flat-square" align="middle"></a>
|
|
1112
|
+
<a title="Included in tampermonkey config" href="#usage">
|
|
1113
|
+
<img height=18 alt="[Tampermonkey]" src="https://img.shields.io/badge/Tampermonkey-00485b?style=flat-square" align="middle"></a>
|
|
1114
|
+
<a title="Included in scriptcat config" href="#usage">
|
|
1115
|
+
<img height=18 alt="[ScriptCat]" src="https://img.shields.io/badge/ScriptCat-1296dc?style=flat-square" align="middle"></a>
|
|
1086
1116
|
<a title="Copy rule URL" target="_blank"
|
|
1087
1117
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23require-update-download-pair">
|
|
1088
1118
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
@@ -1111,7 +1141,7 @@ If you prefer shorter URLs, or don't support users who strictly enforce subresou
|
|
|
1111
1141
|
|
|
1112
1142
|
#### When not to use it
|
|
1113
1143
|
|
|
1114
|
-
If you don't target Tampermonkey (where `@updateURL` avoids re-downloading the full script on every update check).
|
|
1144
|
+
If you don't target Tampermonkey or ScriptCat (where `@updateURL` avoids re-downloading the full script on every update check).
|
|
1115
1145
|
|
|
1116
1146
|
#### Introduced in
|
|
1117
1147
|
|
|
@@ -1119,8 +1149,9 @@ If you don't target Tampermonkey (where `@updateURL` avoids re-downloading the f
|
|
|
1119
1149
|
|
|
1120
1150
|
#### See also
|
|
1121
1151
|
|
|
1122
|
-
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=update_url#meta:updateURL"><code>@updateURL</code></a>
|
|
1123
1152
|
- [`require-homepage-pair`](#require-homepage-pair)
|
|
1153
|
+
- <a target="_blank" href="https://www.tampermonkey.net/documentation.php?q=update_url#meta:updateURL">Tampermonkey — `@updateURL`</a>
|
|
1154
|
+
- <a target="_blank" href="https://docs.scriptcat.org/en/docs/dev/meta/#updateurl">ScriptCat — `@updateURL`</a>
|
|
1124
1155
|
|
|
1125
1156
|
<div align="center">
|
|
1126
1157
|
|
|
@@ -1180,9 +1211,9 @@ If you intentionally name your userscripts something other than `*.user.js`.
|
|
|
1180
1211
|
<code>valid-field-values</code>
|
|
1181
1212
|
<a title="Included in recommended config" href="#usage">
|
|
1182
1213
|
<img height=18 alt="[Recommended]" src="https://img.shields.io/badge/Recommended-f1f20a?style=flat-square" align="middle"></a>
|
|
1183
|
-
<a title="Accepts array w/ options"
|
|
1214
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
1184
1215
|
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
1185
|
-
<a title="Copy rule URL" target="_blank"
|
|
1216
|
+
<a title="Copy rule URL" target="_blank"
|
|
1186
1217
|
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-greasemonkey%2F%23valid-field-values">
|
|
1187
1218
|
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
1188
1219
|
</h3>
|
|
@@ -1194,6 +1225,8 @@ If you intentionally name your userscripts something other than `*.user.js`.
|
|
|
1194
1225
|
#### Options
|
|
1195
1226
|
|
|
1196
1227
|
- `versionType` (default `'semver'`) – version format to enforce (`'semver'`, `'date'`)
|
|
1228
|
+
- `allowFutureDate` (default `false`) – allow `@version` dates later than the current date
|
|
1229
|
+
- `timezone` (default `''`) – <a target="_blank" href="https://data.iana.org/time-zones/tzdb/zone1970.tab">IANA timezone</a> used to determine the current date (when omitted, only year/month are enforced)
|
|
1197
1230
|
|
|
1198
1231
|
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
1199
1232
|
|
|
@@ -1238,6 +1271,8 @@ If you use a custom versioning scheme or URLs that aren't standard.
|
|
|
1238
1271
|
|
|
1239
1272
|
</div>
|
|
1240
1273
|
|
|
1274
|
+
</details>
|
|
1275
|
+
|
|
1241
1276
|
---
|
|
1242
1277
|
|
|
1243
1278
|
## Shield embed
|
|
@@ -1255,14 +1290,14 @@ If you use a custom versioning scheme or URLs that aren't standard.
|
|
|
1255
1290
|
#### Markdown
|
|
1256
1291
|
|
|
1257
1292
|
```md
|
|
1258
|
-
[](https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#readme)
|
|
1293
|
+
[![[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)
|
|
1259
1294
|
```
|
|
1260
1295
|
|
|
1261
1296
|
---
|
|
1262
1297
|
|
|
1263
1298
|
## License
|
|
1264
1299
|
|
|
1265
|
-
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-
|
|
1300
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-greasemonkey/src/branch/main/docs/LICENSE.md">
|
|
1266
1301
|
<img height=62 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/9e820e5d51/assets/images/logos/mit-license/red-pixelated.png"></a>
|
|
1267
1302
|
<br><br>
|
|
1268
1303
|
|
|
@@ -1303,7 +1338,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
1303
1338
|
<div align="center">
|
|
1304
1339
|
|
|
1305
1340
|
<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>
|
|
1306
|
-
<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://
|
|
1341
|
+
<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://www.npmjs.com/~adamlui">More packages by author</a> / [Back to top ↑](#top)
|
|
1307
1342
|
|
|
1308
1343
|
</div>
|
|
1309
1344
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-greasemonkey",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "ESLint rules for Greasemonkey/Tampermonkey userscripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -63,20 +63,20 @@
|
|
|
63
63
|
"eslint": ">=9.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@e18e/eslint-plugin": "^0.8.
|
|
66
|
+
"@e18e/eslint-plugin": "^0.8.1",
|
|
67
67
|
"@eslint/js": "^10.0.1",
|
|
68
68
|
"@eslint/json": "^2.1.0",
|
|
69
69
|
"@eslint/markdown": "^8.0.3",
|
|
70
70
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
71
|
-
"eslint": "^10.
|
|
72
|
-
"eslint-plugin-constructors": "^1.
|
|
71
|
+
"eslint": "^10.11.0",
|
|
72
|
+
"eslint-plugin-constructors": "^1.3.0",
|
|
73
73
|
"eslint-plugin-eslint-plugin": "^7.6.2",
|
|
74
74
|
"eslint-plugin-import-x": "^4.17.1",
|
|
75
75
|
"eslint-plugin-n": "^18.3.0",
|
|
76
76
|
"eslint-plugin-no-secrets": "^2.3.3",
|
|
77
77
|
"eslint-plugin-no-unsanitized": "^4.1.5",
|
|
78
78
|
"eslint-plugin-promise": "^7.3.0",
|
|
79
|
-
"eslint-plugin-regexp": "^3.3.
|
|
79
|
+
"eslint-plugin-regexp": "^3.3.1",
|
|
80
80
|
"eslint-plugin-security": "^4.0.1",
|
|
81
81
|
"globals": "^17.12.0",
|
|
82
82
|
"husky": "^9.1.7",
|
package/src/data/apis.js
CHANGED
|
@@ -25,11 +25,30 @@ const tmAPIs = new Set([
|
|
|
25
25
|
'GM.getTab', 'GM.getTabs', 'GM.saveTab', 'GM.webRequest'
|
|
26
26
|
])
|
|
27
27
|
|
|
28
|
+
const agAPIs = new Set([
|
|
29
|
+
'GM_info', 'GM.info',
|
|
30
|
+
'GM_setValue', 'GM.setValue',
|
|
31
|
+
'GM_getValue', 'GM.getValue',
|
|
32
|
+
'GM_listValues', 'GM.listValues',
|
|
33
|
+
'GM_deleteValue', 'GM.deleteValue',
|
|
34
|
+
'GM_getResourceURL', 'GM.getResourceUrl',
|
|
35
|
+
'GM_setClipboard', 'GM.setClipboard',
|
|
36
|
+
'GM_xmlhttpRequest', 'GM.xmlHttpRequest',
|
|
37
|
+
'GM_openInTab', 'GM.openInTab',
|
|
38
|
+
'GM_notification',
|
|
39
|
+
'unsafeWindow',
|
|
40
|
+
'GM_getResourceText',
|
|
41
|
+
'GM_addStyle',
|
|
42
|
+
'GM_log',
|
|
43
|
+
'GM_addElement', 'GM.addElement'
|
|
44
|
+
])
|
|
45
|
+
|
|
28
46
|
export const allAPIs = new Set([...gmLegacyAPIs, ...gmModernAPIs, ...scAPIs])
|
|
29
47
|
|
|
30
48
|
export function supportsAPI({ manager, api }) {
|
|
31
49
|
return !allAPIs.has(api) ? false
|
|
32
50
|
: api.startsWith('CAT') ? manager == 'scriptcat'
|
|
33
51
|
: tmAPIs.has(api) ? manager == 'tampermonkey'
|
|
52
|
+
: manager == 'adguard' ? agAPIs.has(api)
|
|
34
53
|
: true
|
|
35
54
|
}
|
package/src/data/deprecations.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
// Delete a line to end support
|
|
2
|
+
|
|
1
3
|
export const rules = { // old: new
|
|
2
|
-
'
|
|
3
|
-
'
|
|
4
|
+
'meta-spacing': { 'meta-spacing': { spaces: 3 }},
|
|
5
|
+
'meta-order': { 'meta-order': { order: [] }},
|
|
6
|
+
'valid-grants': 'no-invalid-grants', // 2026-9-16 announced removal @ v2
|
|
7
|
+
'require-meta-newline': 'require-blank-line-after-meta-block' // 2026-9-13 announced removal @ v2
|
|
4
8
|
}
|
|
5
9
|
|
|
6
10
|
export default { rules }
|
package/src/data/headers.js
CHANGED
|
@@ -2,21 +2,28 @@ export const universalHeaders = new Set([
|
|
|
2
2
|
'antifeature', 'author', 'compatible', 'connect', 'contributionAmount', 'contributionURL', 'copyright',
|
|
3
3
|
'defaulticon', 'description', 'downloadURL', 'exclude', 'exclude-match', 'grant', 'homepage', 'homepageURL',
|
|
4
4
|
'icon', 'icon64', 'include', 'license', 'match', 'name', 'namespace', 'noframes', 'require', 'resource',
|
|
5
|
-
'run-at', 'source', 'supportURL', '
|
|
5
|
+
'run-at', 'source', 'supportURL', 'updateURL', 'version', 'website'
|
|
6
6
|
])
|
|
7
7
|
|
|
8
8
|
export const managerHeaders = new Map([
|
|
9
|
-
['
|
|
10
|
-
['
|
|
11
|
-
['
|
|
12
|
-
['run-in', ['tampermonkey']],
|
|
9
|
+
['unwrap', ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat']],
|
|
10
|
+
['run-in', ['tampermonkey', 'scriptcat']],
|
|
11
|
+
['nocompat', ['tampermonkey']],
|
|
13
12
|
['sandbox', ['tampermonkey']],
|
|
13
|
+
['user-agent', ['tampermonkey']],
|
|
14
14
|
['webRequest', ['tampermonkey']],
|
|
15
|
-
['
|
|
15
|
+
['inject-into', ['violentmonkey', 'scriptcat']],
|
|
16
|
+
['top-level-await', ['violentmonkey']],
|
|
17
|
+
['background', ['scriptcat']],
|
|
18
|
+
['crontab', ['scriptcat']],
|
|
19
|
+
['definition', ['scriptcat']],
|
|
20
|
+
['early-start', ['scriptcat']],
|
|
21
|
+
['require-css', ['scriptcat']],
|
|
22
|
+
['storageName', ['scriptcat']]
|
|
16
23
|
])
|
|
17
24
|
|
|
18
25
|
export const localizedHeaders = ['name', 'description', 'antifeature']
|
|
19
|
-
export const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey']
|
|
26
|
+
export const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard']
|
|
20
27
|
export const validHeaders = new Map()
|
|
21
28
|
|
|
22
29
|
validHeaders.set('all', new Set([...universalHeaders, ...managerHeaders.keys()]))
|
package/src/index.js
CHANGED
|
@@ -22,6 +22,8 @@ import requireUpdateDownloadPair from './rules/require-update-download-pair.js'
|
|
|
22
22
|
import requireUserJsFilename from './rules/require-user-js-filename.js'
|
|
23
23
|
import validFieldValues from './rules/valid-field-values.js'
|
|
24
24
|
|
|
25
|
+
import { initDeprecatedAlias } from './utils.js'
|
|
26
|
+
|
|
25
27
|
const plugin = {
|
|
26
28
|
rules: {
|
|
27
29
|
'meta-order': metaOrder,
|
|
@@ -49,16 +51,8 @@ const plugin = {
|
|
|
49
51
|
for (const [ruleId, rule] of Object.entries(plugin.rules))
|
|
50
52
|
rule.meta.docs.url = `https://codeberg.org/adamlui/eslint-plugin-greasemonkey/#${ruleId}`
|
|
51
53
|
|
|
52
|
-
for (const
|
|
53
|
-
|
|
54
|
-
plugin.rules[oldName] = {
|
|
55
|
-
...newRule,
|
|
56
|
-
meta: {
|
|
57
|
-
...newRule.meta, docs: { ...newRule.meta.docs, recommended: false },
|
|
58
|
-
deprecated: { message: `Use \`${newName}\` instead.`, replacedBy: [{ rule: { name: newName }}] }
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
54
|
+
for (const entry of Object.entries(oldRules))
|
|
55
|
+
initDeprecatedAlias(plugin, entry)
|
|
62
56
|
|
|
63
57
|
plugin.configs = {
|
|
64
58
|
recommended: {
|
|
@@ -87,13 +81,15 @@ plugin.configs = {
|
|
|
87
81
|
}
|
|
88
82
|
}
|
|
89
83
|
|
|
90
|
-
for (const manager of ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat'])
|
|
84
|
+
for (const manager of ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard'])
|
|
91
85
|
plugin.configs[manager] = {
|
|
92
86
|
plugins: { greasemonkey: plugin },
|
|
93
87
|
rules: {
|
|
94
88
|
...plugin.configs.recommended.rules,
|
|
95
89
|
'greasemonkey/no-invalid-grants': ['error', { target: manager }],
|
|
96
|
-
'greasemonkey/no-invalid-headers': ['error', { target: manager }]
|
|
90
|
+
'greasemonkey/no-invalid-headers': ['error', { target: manager }],
|
|
91
|
+
...(['tampermonkey', 'scriptcat', 'adguard'].includes(manager) &&
|
|
92
|
+
{ 'greasemonkey/require-update-download-pair': 'error' })
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
95
|
|
package/src/rules/meta-order.js
CHANGED
|
@@ -35,16 +35,21 @@ export default {
|
|
|
35
35
|
},
|
|
36
36
|
fixable: 'code',
|
|
37
37
|
schema: [{
|
|
38
|
-
type: '
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
order: {
|
|
41
|
+
type: 'array', items: { type: 'string' },
|
|
42
|
+
description: 'Metadata keys in preferred order; unspecified keys are ignored'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
additionalProperties: false
|
|
41
46
|
}],
|
|
42
|
-
defaultOptions: [[]],
|
|
47
|
+
defaultOptions: [{ order: [] }],
|
|
43
48
|
messages: { wrongOrder: '@{{first}} should appear before @{{second}}' }
|
|
44
49
|
},
|
|
45
50
|
|
|
46
51
|
create(context) {
|
|
47
|
-
const custom = context.options[0] ?? [],
|
|
52
|
+
const custom = context.options[0]?.order ?? [],
|
|
48
53
|
master = custom.length ? defaultOrder.filter(key => !custom.includes(key)) : defaultOrder
|
|
49
54
|
return {
|
|
50
55
|
Program() {
|
|
@@ -11,13 +11,22 @@ export default {
|
|
|
11
11
|
recommended: true
|
|
12
12
|
},
|
|
13
13
|
fixable: 'whitespace',
|
|
14
|
-
schema: [{
|
|
15
|
-
|
|
14
|
+
schema: [{
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
spaces: {
|
|
18
|
+
type: 'number',
|
|
19
|
+
description: 'Minimum spaces after the longest key (default 3)'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
}],
|
|
24
|
+
defaultOptions: [{ spaces: 3 }],
|
|
16
25
|
messages: { misaligned: 'Metadata values should be aligned at column {{column}}' }
|
|
17
26
|
},
|
|
18
27
|
|
|
19
28
|
create(context) {
|
|
20
|
-
const minSpaces = context.options[0] ?? 3
|
|
29
|
+
const minSpaces = context.options[0]?.spaces ?? 3
|
|
21
30
|
return {
|
|
22
31
|
Program() {
|
|
23
32
|
const sourceCode = context.sourceCode,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { extractMetadata } from '../utils.js'
|
|
2
2
|
import { allAPIs, supportsAPI } from '../data/apis.js'
|
|
3
3
|
|
|
4
|
-
const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat'],
|
|
4
|
+
const managers = ['tampermonkey', 'violentmonkey', 'greasemonkey', 'scriptcat', 'adguard'],
|
|
5
5
|
specialGrants = new Set(['none', 'unsafeWindow', 'window.close', 'window.focus', 'window.onurlchange']),
|
|
6
6
|
allGrants = new Set([...allAPIs, ...specialGrants]),
|
|
7
7
|
sortedGrants = new Map(Array.from(allGrants, grant => [grant.toLowerCase(), grant]))
|
|
@@ -15,6 +15,10 @@ export default {
|
|
|
15
15
|
type: 'array',
|
|
16
16
|
items: { type: 'string' },
|
|
17
17
|
description: 'Required metadata fields (default: name, version)'
|
|
18
|
+
},
|
|
19
|
+
requireMatchOrInclude: {
|
|
20
|
+
type: 'boolean',
|
|
21
|
+
description: 'Require a match or include metadata field (default: true)'
|
|
18
22
|
}
|
|
19
23
|
},
|
|
20
24
|
additionalProperties: false
|
|
@@ -25,7 +29,8 @@ export default {
|
|
|
25
29
|
|
|
26
30
|
create(context) {
|
|
27
31
|
const options = context.options[0] || {},
|
|
28
|
-
requiredFields = options.fields || ['name', 'version']
|
|
32
|
+
requiredFields = options.fields || ['name', 'version'],
|
|
33
|
+
requireMatchOrInclude = options.requireMatchOrInclude ?? true
|
|
29
34
|
return {
|
|
30
35
|
Program(node) {
|
|
31
36
|
const metaEntries = extractMetadata(context.sourceCode),
|
|
@@ -33,7 +38,7 @@ export default {
|
|
|
33
38
|
for (const field of requiredFields)
|
|
34
39
|
if (!keys.has(field))
|
|
35
40
|
context.report({ node, messageId: 'missingField', data: { field }})
|
|
36
|
-
if (
|
|
41
|
+
if (requireMatchOrInclude && !['include', 'match'].some(field => keys.has(field)))
|
|
37
42
|
context.report({ node, messageId: 'missingField', data: { field: 'match or include' }})
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extractMetadata } from '../utils.js'
|
|
2
2
|
|
|
3
|
-
const re = {
|
|
3
|
+
const re = {
|
|
4
|
+
hashEntry: /^(sha1|sha256|sha384|sha512|md5)[=-]([a-z0-9+/]+={0,2})$/i, hashFragment: /#([^#]+)$/, hashSep: /[,;]/ }
|
|
4
5
|
|
|
5
6
|
export default {
|
|
6
7
|
meta: {
|
|
@@ -15,9 +16,14 @@ export default {
|
|
|
15
16
|
description: 'SRI hashing options',
|
|
16
17
|
properties: {
|
|
17
18
|
algorithm: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
anyOf: [
|
|
20
|
+
{ type: 'string', enum: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'] },
|
|
21
|
+
{ type: 'array',
|
|
22
|
+
items: { type: 'string', enum: ['md5', 'sha1', 'sha256', 'sha384', 'sha512'] },
|
|
23
|
+
minItems: 1, uniqueItems: true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
description: 'SRI hash algorithm(s) to accept, as a single string or array (default: sha256)'
|
|
21
27
|
}
|
|
22
28
|
},
|
|
23
29
|
additionalProperties: false
|
|
@@ -25,31 +31,37 @@ export default {
|
|
|
25
31
|
defaultOptions: [{ algorithm: 'sha256' }],
|
|
26
32
|
messages: {
|
|
27
33
|
missingHash: 'Missing SRI hash for @{{key}}.',
|
|
28
|
-
invalidHash: 'Invalid SRI hash for @{{key}}.
|
|
34
|
+
invalidHash: 'Invalid SRI hash for @{{key}}.',
|
|
35
|
+
unsupportedAlgorithm: 'SRI hash for @{{key}} uses {{detected}}; expected {{expected}}.'
|
|
29
36
|
}
|
|
30
37
|
},
|
|
31
38
|
|
|
32
39
|
create(context) {
|
|
33
|
-
const
|
|
40
|
+
const rawAlgorithm = context.options[0]?.algorithm ?? 'sha256',
|
|
41
|
+
algorithms = (Array.isArray(rawAlgorithm) ? rawAlgorithm : [rawAlgorithm]).map(a => a.toLowerCase()),
|
|
42
|
+
expected = algorithms.join(' or ')
|
|
34
43
|
return {
|
|
35
44
|
'Program:exit'() {
|
|
36
45
|
for (const entry of extractMetadata(context.sourceCode)) {
|
|
37
46
|
if (entry.key != 'require' && entry.key != 'resource') continue
|
|
38
|
-
const
|
|
39
|
-
if (!
|
|
47
|
+
const fragment = entry.value.trim().match(re.hashFragment)?.[1]
|
|
48
|
+
if (!fragment) {
|
|
40
49
|
context.report({ node: entry.comment, messageId: 'missingHash', data: { key: entry.key } })
|
|
41
50
|
continue
|
|
42
51
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
52
|
+
const hashes = fragment.split(re.hashSep).map(hash => hash.trim()).filter(Boolean),
|
|
53
|
+
parsed = hashes.map(hash => hash.match(re.hashEntry)).filter(Boolean)
|
|
54
|
+
if (!parsed.length) {
|
|
55
|
+
context.report({ node: entry.comment, messageId: 'invalidHash', data: { key: entry.key } })
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
if (!parsed.some(match => algorithms.includes(match[1].toLowerCase()))) {
|
|
59
|
+
const detected = parsed.map(match => match[1].toLowerCase()).join(', ')
|
|
45
60
|
context.report({
|
|
46
|
-
node: entry.comment, messageId: '
|
|
47
|
-
data: {
|
|
48
|
-
key: entry.key,
|
|
49
|
-
detected: match ? ` Detected ${match[1].toLowerCase()}.` : '',
|
|
50
|
-
expected: algorithm
|
|
51
|
-
}
|
|
61
|
+
node: entry.comment, messageId: 'unsupportedAlgorithm',
|
|
62
|
+
data: { key: entry.key, detected, expected }
|
|
52
63
|
})
|
|
64
|
+
}
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
}
|
|
@@ -28,6 +28,14 @@ export default {
|
|
|
28
28
|
type: 'array',
|
|
29
29
|
items: { type: 'string' },
|
|
30
30
|
description: 'Allowed @grant values to check against; if empty, no grant validation'
|
|
31
|
+
},
|
|
32
|
+
allowFutureDate: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
description: 'Allow @version dates later than the current date (default: false)'
|
|
35
|
+
},
|
|
36
|
+
timezone: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'IANA timezone for determining the current date (when omitted, only year and month are checked)'
|
|
31
39
|
}
|
|
32
40
|
},
|
|
33
41
|
additionalProperties: false
|
|
@@ -35,6 +43,7 @@ export default {
|
|
|
35
43
|
defaultOptions: [{}],
|
|
36
44
|
messages: {
|
|
37
45
|
invalidVersion: 'Invalid @version: must be a valid {{versionType}} (expected format: {{example}})',
|
|
46
|
+
futureVersion: 'Invalid @version: date cannot be in the future',
|
|
38
47
|
invalidURL: 'Invalid URL for @{{field}}',
|
|
39
48
|
invalidGrant: 'Unknown @grant: "{{grant}}"'
|
|
40
49
|
}
|
|
@@ -44,7 +53,15 @@ export default {
|
|
|
44
53
|
const options = context.options[0] || {},
|
|
45
54
|
versionType = options.versionType || 'semver',
|
|
46
55
|
allowedGrants = options.allowedGrants || [],
|
|
56
|
+
allowFutureDate = options.allowFutureDate ?? false,
|
|
57
|
+
timezone = options.timezone,
|
|
47
58
|
example = versionType == 'semver' ? '1.2.3' : 'YYYY.M.D or YYYY.MM.DD'
|
|
59
|
+
const now = timezone
|
|
60
|
+
? Object.fromEntries(new Intl.DateTimeFormat('en', {
|
|
61
|
+
timeZone: timezone, year: 'numeric', month: 'numeric', day: 'numeric'
|
|
62
|
+
}).formatToParts().filter(({ type }) => ['year', 'month', 'day'].includes(type))
|
|
63
|
+
.map(({ type, value }) => [type, Number(value)]))
|
|
64
|
+
: { year: new Date().getUTCFullYear(), month: new Date().getUTCMonth() +1 }
|
|
48
65
|
return {
|
|
49
66
|
Program() {
|
|
50
67
|
const metaEntries = extractMetadata(context.sourceCode)
|
|
@@ -66,9 +83,15 @@ export default {
|
|
|
66
83
|
const isValid = versionType == 'semver' ? re.semVer.test(entry.value)
|
|
67
84
|
: versionType == 'date' ? re.dateVer.test(entry.value)
|
|
68
85
|
: false
|
|
86
|
+
const [year, month, day] = versionType == 'date' ? entry.value.split('.').map(Number) : []
|
|
69
87
|
if (!isValid)
|
|
70
88
|
context.report({
|
|
71
89
|
node: entry.comment, messageId: 'invalidVersion', data: { versionType, example }})
|
|
90
|
+
else if (versionType == 'date' && !allowFutureDate && (
|
|
91
|
+
year > now.year || year == now.year && month > now.month
|
|
92
|
+
|| timezone && year == now.year && month == now.month && day > now.day
|
|
93
|
+
))
|
|
94
|
+
context.report({ node: entry.comment, messageId: 'futureVersion' })
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
97
|
}
|
package/src/utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const re = { whitespace: /\s/ }
|
|
2
|
+
|
|
1
3
|
export function extractMetadata(sourceCode) {
|
|
2
4
|
const comments = sourceCode.getAllComments(),
|
|
3
5
|
metaEntries = []
|
|
@@ -7,17 +9,90 @@ export function extractMetadata(sourceCode) {
|
|
|
7
9
|
if (!inMetaBlock && text == '==UserScript==') inMetaBlock = true
|
|
8
10
|
else if (inMetaBlock && text == '==/UserScript==') break
|
|
9
11
|
else if (inMetaBlock && text.startsWith('@')) { // capture entry
|
|
10
|
-
const spaceIdx = text.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
metaEntries.push({ key, value, comment, line: comment.loc.start.line })
|
|
15
|
-
}
|
|
12
|
+
const spaceIdx = text.search(re.whitespace),
|
|
13
|
+
key = ( spaceIdx == -1 ? text.slice(1) : text.slice(1, spaceIdx) ).trim(),
|
|
14
|
+
value = spaceIdx == -1 ? '' : text.slice(spaceIdx +1).trim()
|
|
15
|
+
if (key) metaEntries.push({ key, value, comment, line: comment.loc.start.line })
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
return metaEntries
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function initDeprecatedAlias(plugin, [oldName, alias]) { /* eslint-disable security/detect-object-injection */
|
|
22
|
+
const routes = typeof alias == 'string' ? { [alias]: null } // 'old-rule': 'new-rule'
|
|
23
|
+
: Array.isArray(alias) ? Object.fromEntries(alias.map(entry =>
|
|
24
|
+
typeof entry == 'string' ? [entry, null] // ['new-rule-1', 'new-rule-2']
|
|
25
|
+
: Object.entries(entry)[0])) // [{ 'new-rule': { 'old-opt': 'new-opt' }}]
|
|
26
|
+
: alias // { 'new-rule': null | { 'new-opt': default } | { 'old-val': 'new-val' } | { ..., 'new-opt': default }}
|
|
27
|
+
const targetNames = Object.keys(routes),
|
|
28
|
+
isRename = !targetNames.includes(oldName),
|
|
29
|
+
originals = Object.fromEntries(targetNames.map(name => [name, plugin.rules[name]])),
|
|
30
|
+
source = originals[oldName] ?? originals[targetNames[0]]
|
|
31
|
+
plugin.rules[oldName] = {
|
|
32
|
+
meta: {
|
|
33
|
+
...source.meta,
|
|
34
|
+
defaultOptions: [],
|
|
35
|
+
schema: [{ type: ['object', 'array', 'string', 'number', 'boolean', 'null'] }],
|
|
36
|
+
docs: { ...source.meta.docs, recommended: isRename ? false : source.meta.docs.recommended },
|
|
37
|
+
messages: {
|
|
38
|
+
...source.meta.messages,
|
|
39
|
+
deprecatedOption: `'{{old}}' is deprecated. Use '{{new}}' instead.`
|
|
40
|
+
},
|
|
41
|
+
...(isRename ? { deprecated: {
|
|
42
|
+
message: `Use ${targetNames.map(name => `\`${name}\``).join(', ')} instead.`,
|
|
43
|
+
replacedBy: targetNames.map(name => ({ rule: { name }}))
|
|
44
|
+
}} : {})
|
|
45
|
+
},
|
|
46
|
+
create(context) {
|
|
47
|
+
const opts = context.options[0],
|
|
48
|
+
isObj = opts?.constructor === Object
|
|
49
|
+
return Object.assign({}, ...Object.entries(routes).map(([targetName, mapping]) => {
|
|
50
|
+
let newOpts = opts, deprecation
|
|
51
|
+
if (mapping) {
|
|
52
|
+
const entries = Object.entries(mapping),
|
|
53
|
+
namedRemap = entries.every(([, val]) => typeof val == 'string'),
|
|
54
|
+
wrapKey = namedRemap && entries.length > 1 ? entries.at(-1)[0] : null,
|
|
55
|
+
valMap = wrapKey ? Object.fromEntries(entries.slice(0, -1)) : mapping
|
|
56
|
+
if (namedRemap) {
|
|
57
|
+
if (typeof opts == 'string') { // bare old val → remap, optionally wrapped
|
|
58
|
+
if (valMap[opts]) deprecation = { old: opts, new: valMap[opts] }
|
|
59
|
+
const remapped = valMap[opts] ?? opts
|
|
60
|
+
newOpts = wrapKey ? { [wrapKey]: remapped } : remapped
|
|
61
|
+
} else if (wrapKey) { // object opts (or defaults) → remap inner val, warn if deprecated
|
|
62
|
+
const raw = isObj ? (opts[wrapKey] ?? mapping[wrapKey]) : mapping[wrapKey],
|
|
63
|
+
remapped = valMap[raw]
|
|
64
|
+
if (remapped) deprecation = { old: raw, new: remapped }
|
|
65
|
+
newOpts = { [wrapKey]: remapped ?? raw }
|
|
66
|
+
} else { // object opts → old key → new key
|
|
67
|
+
newOpts = { ...opts }
|
|
68
|
+
for (const [oldKey, newKey] of entries)
|
|
69
|
+
if (opts?.[oldKey] !== undefined) {
|
|
70
|
+
newOpts[newKey] = opts[oldKey] ; delete newOpts[oldKey] }
|
|
71
|
+
}
|
|
72
|
+
} else { // { new-opt: default } — positional wrap
|
|
73
|
+
const [newKey, defaultValue] = entries[0]
|
|
74
|
+
if (opts !== undefined && !isObj)
|
|
75
|
+
deprecation = { old: JSON.stringify(opts), new: `{ ${newKey}: ${JSON.stringify(opts)} }` }
|
|
76
|
+
newOpts = { [newKey]: isObj ? (opts[newKey] ?? defaultValue) : (opts ?? defaultValue) }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const aliasContext = Object.create(context)
|
|
80
|
+
Object.defineProperty(aliasContext, 'options',
|
|
81
|
+
{ value: [newOpts], writable: true, configurable: true, enumerable: true })
|
|
82
|
+
const visitors = { ...originals[targetName].create(aliasContext) }
|
|
83
|
+
if (deprecation) {
|
|
84
|
+
const ruleProgram = visitors.Program
|
|
85
|
+
visitors.Program = function(node) {
|
|
86
|
+
context.report({ node, messageId: 'deprecatedOption', data: deprecation })
|
|
87
|
+
if (ruleProgram) ruleProgram.call(this, node)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return visitors
|
|
91
|
+
}))
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} /* eslint-enable security/detect-object-injection */
|
|
95
|
+
|
|
21
96
|
export function resolveTemplate(node) {
|
|
22
97
|
if (node.type == 'ConditionalExpression') {
|
|
23
98
|
const branches = [node.consequent, node.alternate].map(resolveTemplate)
|