eslint-plugin-chrome-extensions 2.8.0 → 2.9.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 +86 -88
- package/package.json +11 -7
- package/src/rules/css-spacing.js +9 -14
- package/src/rules/manifest-key-order.js +6 -5
- package/src/rules/manifest-spacing.js +5 -8
- package/src/rules/no-content-style-leak.js +4 -3
- package/src/rules/no-deprecated-apis.js +6 -6
- package/src/rules/no-global-keyframes.js +5 -8
- package/src/rules/no-host-page-selectors.js +12 -16
- package/src/rules/no-inline-event-handlers.js +7 -5
- package/src/rules/no-inline-scripts.js +10 -6
- package/src/rules/no-insecure-fetch.js +10 -6
- package/src/rules/no-insecure-messaging.js +8 -10
- package/src/rules/no-invalid-locales.js +7 -6
- package/src/rules/no-invalid-permissions.js +5 -5
- package/src/rules/no-missing-default-locale.js +5 -4
- package/src/rules/no-missing-i18n-keys.js +7 -5
- package/src/rules/no-missing-permissions.js +1 -1
- package/src/rules/no-missing-web-accessible-resources.js +10 -10
- package/src/rules/no-remote-code.js +30 -33
- package/src/rules/no-remote-objects.js +7 -6
- package/src/rules/no-remote-urls.js +11 -12
- package/src/rules/no-scrollbar-styling.js +6 -6
- package/src/rules/no-sensitive-permissions.js +6 -4
- package/src/rules/no-unrestricted-host-permissions.js +6 -7
- package/src/rules/no-unused-i18n-keys.js +5 -4
- package/src/rules/require-short-name-when-useful.js +3 -8
- package/src/rules/valid-filenames.js +9 -11
- package/src/rules/valid-license-file.js +7 -6
- package/src/rules/valid-manifest-icons.js +14 -20
- package/src/rules/valid-service-worker.js +8 -10
- package/src/rules/valid-url-fields.js +8 -13
- package/src/rules/valid-version-field.js +5 -6
- package/src/utils/extension.js +7 -3
- package/src/utils/init.js +5 -4
- package/src/utils/node.js +23 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<a href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/src/branch/main/docs/LICENSE.md">
|
|
8
8
|
<img alt="[License: MIT]" src="https://img.shields.io/badge/License-MIT-f1f20a.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
|
|
9
9
|
<a target="_blank" href="https://getnpmstats.com/?package=eslint-plugin-chrome-extensions&range=last-year&interval=monthly">
|
|
10
|
-
<img src="https://img.shields.io/npm/dm/eslint-plugin-chrome-extensions?style=for-the-badge&logo=npm&color=black"></a>
|
|
10
|
+
<img alt="[Monthly npm downloads]" src="https://img.shields.io/npm/dm/eslint-plugin-chrome-extensions?style=for-the-badge&logo=npm&color=black"></a>
|
|
11
11
|
<a href="#usage">
|
|
12
12
|
<img alt="[Supports ESLint >=9.6.0 / Oxlint >=1.65.0]" src="https://img.shields.io/badge/Supports-ESLint_%3E%3D9.6.0_%2F_Oxlint_%3E%3D1.65.0-4B32C3?style=for-the-badge&logo=eslint"></a>
|
|
13
13
|
<br><br>
|
|
@@ -33,7 +33,7 @@ npm i -D eslint-plugin-chrome-extensions
|
|
|
33
33
|
Usage
|
|
34
34
|
</h2>
|
|
35
35
|
|
|
36
|
-
### ESLint
|
|
36
|
+
### <img height=15 width=auto src="https://www.google.com/s2/favicons?domain=eslint.org&sz=64"> ESLint
|
|
37
37
|
|
|
38
38
|
Add bundled configs to your `eslint.config.*js` for each file type you need:
|
|
39
39
|
|
|
@@ -44,8 +44,7 @@ import chromeExtensions from 'eslint-plugin-chrome-extensions'
|
|
|
44
44
|
export default [
|
|
45
45
|
// other configs
|
|
46
46
|
{
|
|
47
|
-
files: ['**/extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
|
|
48
|
-
plugins: { 'chrome-extensions': chromeExtensions },
|
|
47
|
+
files: ['**/extension/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}'], ...chromeExtensions.configs.recommended,
|
|
49
48
|
rules: {
|
|
50
49
|
...chromeExtensions.configs.recommended.rules,
|
|
51
50
|
'chrome-extensions/no-deprecated-apis': 'off', // selectively disable rule
|
|
@@ -62,32 +61,37 @@ export default [
|
|
|
62
61
|
]
|
|
63
62
|
```
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
**Note:** Rule config is either a severity string (`'off'`, `'warn'`, `'error'`) or an array `[severity, options]` when the rule accepts options.
|
|
65
|
+
|
|
66
|
+
---
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
### <img height=15 width=auto src="https://www.google.com/s2/favicons?domain=oxc.rs&sz=64"> Oxlint
|
|
69
|
+
|
|
70
|
+
Add a JS bundle to your `oxlint.config.ts` (non-JS parsing is unsupported by Oxlint):
|
|
68
71
|
|
|
69
72
|
```ts
|
|
70
73
|
...
|
|
71
74
|
import chromeExtensions from 'eslint-plugin-chrome-extensions'
|
|
72
75
|
|
|
73
76
|
export default defineConfig({
|
|
74
|
-
// main config
|
|
75
77
|
overrides: [
|
|
76
|
-
// other
|
|
78
|
+
// other configs
|
|
77
79
|
{
|
|
78
|
-
files: ['**/extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
|
|
80
|
+
files: ['**/extension/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}'],
|
|
79
81
|
jsPlugins: ['eslint-plugin-chrome-extensions'],
|
|
80
82
|
rules: {
|
|
81
83
|
...chromeExtensions.configs.recommended.rules,
|
|
82
|
-
'chrome-extensions/no-deprecated-apis': '
|
|
83
|
-
'chrome-extensions/no-insecure-messaging': ['
|
|
84
|
+
'chrome-extensions/no-deprecated-apis': 'allow', // selectively disable rule
|
|
85
|
+
'chrome-extensions/no-insecure-messaging': ['deny', { disallowWildcardTarget: false }] // selectively tweak rule
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
]
|
|
87
89
|
})
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
**Note:** Rule config is either a severity string (`'
|
|
92
|
+
**Note:** Rule config is either a severity string (`'allow'`, `'warn'`, `'deny'`) or an array `[severity, options]` when the rule accepts options.
|
|
93
|
+
|
|
94
|
+
**Note:** Oxlint is typically **6-20× faster than ESLint** w/ this plugin (due to its Rust core and parallel file processing).
|
|
91
95
|
|
|
92
96
|
---
|
|
93
97
|
|
|
@@ -590,10 +594,78 @@ If your CSS is scoped inside a Shadow DOM root.
|
|
|
590
594
|
|
|
591
595
|
</details>
|
|
592
596
|
|
|
597
|
+
<h3 id="no-host-page-selectors">
|
|
598
|
+
<code>no-host-page-selectors</code>
|
|
599
|
+
<a title="Only works in 'css/css' config block" href="#usage">
|
|
600
|
+
<img height=18 alt="[CSS only]" src="https://img.shields.io/badge/CSS_only-dcd6f7?style=flat-square" align="middle"></a>
|
|
601
|
+
<a title="Accepts array w/ options" href="#usage">
|
|
602
|
+
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
603
|
+
<a title="Copy rule URL" target="_blank"
|
|
604
|
+
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-host-page-selectors">
|
|
605
|
+
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
606
|
+
</h3>
|
|
607
|
+
|
|
608
|
+
<details>
|
|
609
|
+
|
|
610
|
+
<summary>Disallows host-page selectors (<code>html</code>, <code>body</code>, <code>:root</code>, <code>*</code>, bare elem types) in content-script CSS.</summary>
|
|
611
|
+
|
|
612
|
+
<h4 id="no-host-page-selectors-options">Options</h4>
|
|
613
|
+
|
|
614
|
+
- `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body.dark']`), as a single string or array <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
615
|
+
- `disallowUniversal` (default `true`) – flag `*` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
616
|
+
- `disallowStructural` (default `true`) – flag `html`, `body`, `:root` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
617
|
+
- `disallowBareElements` (default `true`) – flag elem types (e.g. `div`, `a`) that lack `class`/`id`/`attribute` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
618
|
+
|
|
619
|
+
#### <img width=13 height=13 src="https://cdn.staticdelivr.com/gl/adamlui/eslint-plugin-greasemonkey/99f14247b2/assets/images/icons/square/with-symbol/gray.svg" align="bottom"> Incorrect code
|
|
620
|
+
|
|
621
|
+
```diff
|
|
622
|
+
! * { box-sizing: border-box }
|
|
623
|
+
|
|
624
|
+
! body { margin: 0 }
|
|
625
|
+
! html { font-family: sans-serif }
|
|
626
|
+
! button:hover { opacity: 0.8 }
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
#### <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
|
|
630
|
+
|
|
631
|
+
```diff
|
|
632
|
+
- * { box-sizing: border-box }
|
|
633
|
+
+ :host { box-sizing: border-box }
|
|
634
|
+
|
|
635
|
+
- body { margin: 0 }
|
|
636
|
+
+ :host { margin: 0 }
|
|
637
|
+
- html { font-family: sans-serif }
|
|
638
|
+
+ :host { font-family: sans-serif }
|
|
639
|
+
- button:hover { opacity: 0.8 }
|
|
640
|
+
+ .myext-button:hover { opacity: 0.8 }
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### When not to use it
|
|
644
|
+
|
|
645
|
+
If you intentionally style the host page. For individual selectors you still need, prefer `allowedSelectors` over disabling the rule.
|
|
646
|
+
|
|
647
|
+
#### Introduced in
|
|
648
|
+
|
|
649
|
+
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0">v2.7.0</a> (October 17, 2026)
|
|
650
|
+
|
|
651
|
+
#### See also
|
|
652
|
+
|
|
653
|
+
- [`no-content-style-leak`](#no-content-style-leak)
|
|
654
|
+
<a title="Only included in strict config" href="#usage"><img height=14 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
|
|
655
|
+
- <img height=12 src="https://www.google.com/s2/favicons?domain=developer.mozilla.org&sz=64"> <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/:host">MDN — `:host`</a>
|
|
656
|
+
|
|
657
|
+
<div align="center">
|
|
658
|
+
|
|
659
|
+
[Back to index ↑][epce-rules]
|
|
660
|
+
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
</details>
|
|
666
|
+
|
|
593
667
|
<h3 id="no-inline-event-handlers">
|
|
594
668
|
<code>no-inline-event-handlers</code>
|
|
595
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
596
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
597
669
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
598
670
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
599
671
|
<a title="Accepts array w/ options" href="#usage">
|
|
@@ -668,8 +740,6 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
|
|
|
668
740
|
|
|
669
741
|
<h3 id="no-inline-scripts">
|
|
670
742
|
<code>no-inline-scripts</code>
|
|
671
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
672
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
673
743
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
674
744
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
675
745
|
<a title="Accepts array w/ options" href="#usage">
|
|
@@ -737,76 +807,6 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
|
|
|
737
807
|
|
|
738
808
|
</details>
|
|
739
809
|
|
|
740
|
-
<h3 id="no-host-page-selectors">
|
|
741
|
-
<code>no-host-page-selectors</code>
|
|
742
|
-
<a title="Only works in 'css/css' config block" href="#usage">
|
|
743
|
-
<img height=18 alt="[CSS only]" src="https://img.shields.io/badge/CSS_only-dcd6f7?style=flat-square" align="middle"></a>
|
|
744
|
-
<a title="Accepts array w/ options" href="#usage">
|
|
745
|
-
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
746
|
-
<a title="Copy rule URL" target="_blank"
|
|
747
|
-
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-host-page-selectors">
|
|
748
|
-
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
749
|
-
</h3>
|
|
750
|
-
|
|
751
|
-
<details>
|
|
752
|
-
|
|
753
|
-
<summary>Disallows host-page selectors (<code>html</code>, <code>body</code>, <code>:root</code>, <code>*</code>, bare elem types) in content-script CSS.</summary>
|
|
754
|
-
|
|
755
|
-
<h4 id="no-host-page-selectors-options">Options</h4>
|
|
756
|
-
|
|
757
|
-
- `allowedSelectors` (default `[]`) – selectors to explicitly allow (e.g. `['body.dark']`), as a single string or array <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
758
|
-
- `disallowUniversal` (default `true`) – flag `*` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
759
|
-
- `disallowStructural` (default `true`) – flag `html`, `body`, `:root` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
760
|
-
- `disallowBareElements` (default `true`) – flag elem types (e.g. `div`, `a`) that lack `class`/`id`/`attribute` <a title="Introduced in v2.7.0" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0"><img height=14 alt="[Since v2.7.0]" src="https://img.shields.io/badge/since-v2.7.0-8a9199?style=flat-square" align="middle"></a>
|
|
761
|
-
|
|
762
|
-
#### <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
|
|
763
|
-
|
|
764
|
-
```diff
|
|
765
|
-
! * { box-sizing: border-box }
|
|
766
|
-
|
|
767
|
-
! body { margin: 0 }
|
|
768
|
-
! html { font-family: sans-serif }
|
|
769
|
-
! button:hover { opacity: 0.8 }
|
|
770
|
-
```
|
|
771
|
-
|
|
772
|
-
#### <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
|
|
773
|
-
|
|
774
|
-
```diff
|
|
775
|
-
- * { box-sizing: border-box }
|
|
776
|
-
+ :host { box-sizing: border-box }
|
|
777
|
-
|
|
778
|
-
- body { margin: 0 }
|
|
779
|
-
+ :host { margin: 0 }
|
|
780
|
-
- html { font-family: sans-serif }
|
|
781
|
-
+ :host { font-family: sans-serif }
|
|
782
|
-
- button:hover { opacity: 0.8 }
|
|
783
|
-
+ .myext-button:hover { opacity: 0.8 }
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
#### When not to use it
|
|
787
|
-
|
|
788
|
-
If you intentionally style the host page. For individual selectors you still need, prefer `allowedSelectors` over disabling the rule.
|
|
789
|
-
|
|
790
|
-
#### Introduced in
|
|
791
|
-
|
|
792
|
-
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0">v2.7.0</a> (October 17, 2026)
|
|
793
|
-
|
|
794
|
-
#### See also
|
|
795
|
-
|
|
796
|
-
- [`no-content-style-leak`](#no-content-style-leak)
|
|
797
|
-
<a title="Only included in strict config" href="#usage"><img height=14 alt="[Strict only]" src="https://img.shields.io/badge/Strict_only-ff8c42?style=flat-square" align="middle"></a>
|
|
798
|
-
- <img height=12 src="https://www.google.com/s2/favicons?domain=developer.mozilla.org&sz=64"> <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/:host">MDN — `:host`</a>
|
|
799
|
-
|
|
800
|
-
<div align="center">
|
|
801
|
-
|
|
802
|
-
[Back to index ↑][epce-rules]
|
|
803
|
-
|
|
804
|
-
</div>
|
|
805
|
-
|
|
806
|
-
---
|
|
807
|
-
|
|
808
|
-
</details>
|
|
809
|
-
|
|
810
810
|
<h3 id="no-insecure-fetch">
|
|
811
811
|
<code>no-insecure-fetch</code>
|
|
812
812
|
<a title="Included in recommended config" href="#usage">
|
|
@@ -1476,8 +1476,6 @@ If you intentionally target forks w/ relaxed CSPs. For individual patterns you s
|
|
|
1476
1476
|
|
|
1477
1477
|
<h3 id="no-remote-objects">
|
|
1478
1478
|
<code>no-remote-objects</code>
|
|
1479
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
1480
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
1481
1479
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
1482
1480
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
1483
1481
|
<a title="Accepts array w/ options" href="#usage">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-chrome-extensions",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "ESLint rules for Chrome extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"eslintplugin",
|
|
9
9
|
"chrome-extensions",
|
|
10
10
|
"chromium",
|
|
11
|
+
"oxlint",
|
|
12
|
+
"oxlint-plugin",
|
|
11
13
|
"security"
|
|
12
14
|
],
|
|
13
15
|
"homepage": "https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/#readme",
|
|
@@ -70,25 +72,27 @@
|
|
|
70
72
|
"@eslint/markdown": "^7.5.1",
|
|
71
73
|
"@html-eslint/eslint-plugin": "^0.67.0",
|
|
72
74
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
75
|
+
"@typescript-eslint/parser": "^8.72.1",
|
|
73
76
|
"eslint": "^10.13.0",
|
|
74
|
-
"eslint-plugin-constructors": "^1.5.
|
|
77
|
+
"eslint-plugin-constructors": "^1.5.1",
|
|
75
78
|
"eslint-plugin-eslint-plugin": "^7.6.2",
|
|
76
79
|
"eslint-plugin-import-x": "^4.17.1",
|
|
77
|
-
"eslint-plugin-n": "^18.
|
|
80
|
+
"eslint-plugin-n": "^18.5.0",
|
|
78
81
|
"eslint-plugin-no-secrets": "^2.3.3",
|
|
79
82
|
"eslint-plugin-no-unsanitized": "^4.1.5",
|
|
80
|
-
"eslint-plugin-package-json": "^1.
|
|
83
|
+
"eslint-plugin-package-json": "^1.11.0",
|
|
81
84
|
"eslint-plugin-promise": "^7.3.0",
|
|
82
85
|
"eslint-plugin-regexp": "^3.3.1",
|
|
83
86
|
"eslint-plugin-security": "^4.2.0",
|
|
84
87
|
"eslint-plugin-sonarjs": "^4.2.2",
|
|
85
|
-
"eslint-plugin-woodpecker": "^1.1.
|
|
88
|
+
"eslint-plugin-woodpecker": "^1.1.1",
|
|
86
89
|
"oxlint": "^1.89.0"
|
|
87
90
|
},
|
|
88
91
|
"peerDependencies": {
|
|
89
|
-
"@eslint/css": ">=0.
|
|
92
|
+
"@eslint/css": ">=0.1.0",
|
|
90
93
|
"@eslint/json": ">=0.1.0",
|
|
91
|
-
"@html-eslint/eslint-plugin": ">=0.
|
|
94
|
+
"@html-eslint/eslint-plugin": ">=0.41.0",
|
|
95
|
+
"@typescript-eslint/parser": ">=8.0.0",
|
|
92
96
|
"eslint": ">=9.6.0"
|
|
93
97
|
},
|
|
94
98
|
"engines": {
|
package/src/rules/css-spacing.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
import extension from '../utils/extension.js'
|
|
4
4
|
import regex from '../data/regex.js'
|
|
5
5
|
|
|
6
|
+
const defaultOptions = {
|
|
7
|
+
spaces: 2, checkBraces: true, checkBrackets: true, checkColons: true, checkCombinators: true, checkCommas: true,
|
|
8
|
+
checkParentheses: true, checkSemicolons: true
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
const re = { leadingWhitespace: /^[ \t]+/ },
|
|
7
12
|
combinators = new Set(['>', '+', '~'])
|
|
8
13
|
|
|
@@ -37,10 +42,7 @@ export default {
|
|
|
37
42
|
},
|
|
38
43
|
additionalProperties: false
|
|
39
44
|
}],
|
|
40
|
-
defaultOptions: [
|
|
41
|
-
spaces: 2, checkBraces: true, checkBrackets: true, checkColons: true,
|
|
42
|
-
checkCombinators: true, checkCommas: true, checkParentheses: true, checkSemicolons: true
|
|
43
|
-
}],
|
|
45
|
+
defaultOptions: [defaultOptions],
|
|
44
46
|
messages: {
|
|
45
47
|
wrongIndent: 'Expected {{expected}} spaces of indentation (found {{found}}).',
|
|
46
48
|
tabFound: 'Expected spaces for indentation, found tabs.',
|
|
@@ -63,16 +65,9 @@ export default {
|
|
|
63
65
|
|
|
64
66
|
create(context) {
|
|
65
67
|
if (!regex.filename.css.test(extension.file.getName(context))) return {}
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
checkBrackets = opts.checkBrackets ?? true,
|
|
70
|
-
checkColons = opts.checkColons ?? true,
|
|
71
|
-
checkCombinators = opts.checkCombinators ?? true,
|
|
72
|
-
checkCommas = opts.checkCommas ?? true,
|
|
73
|
-
checkParentheses = opts.checkParentheses ?? true,
|
|
74
|
-
checkSemicolons = opts.checkSemicolons ?? true,
|
|
75
|
-
sourceCode = context.sourceCode,
|
|
68
|
+
const { spaces, checkBraces, checkBrackets, checkColons, checkCombinators, checkCommas, checkParentheses,
|
|
69
|
+
checkSemicolons } = { ...defaultOptions, ...context.options[0] }
|
|
70
|
+
const sourceCode = context.sourceCode,
|
|
76
71
|
text = sourceCode.text,
|
|
77
72
|
lines = text.split('\n'),
|
|
78
73
|
lineOffsets = [0]
|
|
@@ -3,6 +3,8 @@ import extension from '../utils/extension.js'
|
|
|
3
3
|
import manifest from '../data/manifest.js'
|
|
4
4
|
import regex from '../data/regex.js'
|
|
5
5
|
|
|
6
|
+
const defaultOptions = { order: [] }
|
|
7
|
+
|
|
6
8
|
function keyOf(member) {
|
|
7
9
|
const keyNode = member.name || member.key
|
|
8
10
|
return keyNode?.value ?? keyNode?.name
|
|
@@ -37,20 +39,19 @@ export default {
|
|
|
37
39
|
},
|
|
38
40
|
additionalProperties: false
|
|
39
41
|
}],
|
|
40
|
-
defaultOptions: [
|
|
42
|
+
defaultOptions: [defaultOptions],
|
|
41
43
|
messages: { wrongOrder: '`{{first}}` should appear before `{{second}}`.' }
|
|
42
44
|
},
|
|
43
45
|
|
|
44
46
|
create(context) {
|
|
45
47
|
if (!regex.filename.manifest.test(extension.file.getName(context))) return {}
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
: manifest.keys.order
|
|
48
|
+
const { order } = { ...defaultOptions, ...context.options[0] },
|
|
49
|
+
master = order.length ? manifest.keys.order.filter(key => !order.includes(key)) : manifest.keys.order
|
|
49
50
|
return {
|
|
50
51
|
'Document:exit'(document) {
|
|
51
52
|
const root = document.body ; if (root?.type != 'Object') return
|
|
52
53
|
const members = root.members || root.properties || [],
|
|
53
|
-
violation = findViolation(members, master) ?? (
|
|
54
|
+
violation = findViolation(members, master) ?? (order.length && findViolation(members, order))
|
|
54
55
|
if (!violation) return
|
|
55
56
|
const [first, second] = violation
|
|
56
57
|
context.report({
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import extension from '../utils/extension.js'
|
|
2
2
|
import regex from '../data/regex.js'
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const defaultOptions = { spaces: 2, checkColons: true, checkCommas: true, checkBraces: true, checkBrackets: true },
|
|
5
|
+
re = { leadingWhitespace: /^[ \t]+/ },
|
|
5
6
|
brackets = { open: new Set(['LBrace', 'LBracket']), close: new Set(['RBrace', 'RBracket']) }
|
|
6
7
|
|
|
7
8
|
export default {
|
|
@@ -39,7 +40,7 @@ export default {
|
|
|
39
40
|
},
|
|
40
41
|
additionalProperties: false
|
|
41
42
|
}],
|
|
42
|
-
defaultOptions: [
|
|
43
|
+
defaultOptions: [defaultOptions],
|
|
43
44
|
messages: {
|
|
44
45
|
wrongIndent: 'Expected {{expected}} spaces of indentation (found {{found}}).',
|
|
45
46
|
tabFound: 'Expected spaces for indentation, found tabs.',
|
|
@@ -57,12 +58,8 @@ export default {
|
|
|
57
58
|
|
|
58
59
|
create(context) {
|
|
59
60
|
if (!regex.filename.manifest.test(extension.file.getName(context))) return {}
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
checkColons = opts.checkColons ?? true,
|
|
63
|
-
checkCommas = opts.checkCommas ?? true,
|
|
64
|
-
checkBraces = opts.checkBraces ?? true,
|
|
65
|
-
checkBrackets = opts.checkBrackets ?? true
|
|
61
|
+
const { spaces, checkColons, checkCommas, checkBraces, checkBrackets }
|
|
62
|
+
= { ...defaultOptions, ...context.options[0] }
|
|
66
63
|
return {
|
|
67
64
|
'Document'(document) {
|
|
68
65
|
const sourceCode = context.sourceCode,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import extension from '../utils/extension.js'
|
|
2
|
+
import nodeUtils from '../utils/node.js'
|
|
2
3
|
|
|
3
4
|
const re = { appendMethod: /^append(?:Child)?$/ }
|
|
4
5
|
|
|
5
6
|
function isCreateStyle(node) {
|
|
6
7
|
return node?.type == 'CallExpression'
|
|
7
8
|
&& node.callee?.property?.name == 'createElement'
|
|
8
|
-
&& node.arguments[0]
|
|
9
|
+
&& nodeUtils.getStringLiteral(node.arguments[0]) == 'style'
|
|
9
10
|
}
|
|
10
11
|
function isObjectAssign(node) {
|
|
11
12
|
return node?.type == 'CallExpression'
|
|
@@ -52,8 +53,8 @@ export default {
|
|
|
52
53
|
return {
|
|
53
54
|
VariableDeclarator(node) {
|
|
54
55
|
if (node.id.type != 'Identifier') return
|
|
55
|
-
if (isCreateStyle(node.init)) styleElems.add(node.id.name)
|
|
56
|
-
if (node.init?.type == 'Identifier' && isDocumentRef(node.init))
|
|
56
|
+
if (isCreateStyle(nodeUtils.unwrapTS(node.init))) styleElems.add(node.id.name)
|
|
57
|
+
if (nodeUtils.unwrapTS(node.init)?.type == 'Identifier' && isDocumentRef(node.init))
|
|
57
58
|
docAliases.add(node.id.name)
|
|
58
59
|
},
|
|
59
60
|
CallExpression(node) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import apiUtils from '../utils/api.js'
|
|
2
2
|
import nodeUtils from '../utils/node.js'
|
|
3
3
|
|
|
4
|
+
const defaultOptions = { target: 'mv3', allowedAPIs: [] }
|
|
5
|
+
|
|
4
6
|
export default {
|
|
5
7
|
meta: {
|
|
6
8
|
type: 'problem',
|
|
@@ -9,7 +11,7 @@ export default {
|
|
|
9
11
|
category: 'Possible Errors',
|
|
10
12
|
recommended: true
|
|
11
13
|
},
|
|
12
|
-
defaultOptions: [
|
|
14
|
+
defaultOptions: [defaultOptions],
|
|
13
15
|
schema: [{
|
|
14
16
|
type: 'object',
|
|
15
17
|
properties: {
|
|
@@ -37,13 +39,11 @@ export default {
|
|
|
37
39
|
},
|
|
38
40
|
|
|
39
41
|
create(context) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
allowedAPIs = Array.isArray(opts.allowedAPIs) ? opts.allowedAPIs
|
|
43
|
-
: opts.allowedAPIs ? [opts.allowedAPIs] : []
|
|
42
|
+
const { target, allowedAPIs } = { ...defaultOptions, ...context.options[0] },
|
|
43
|
+
allowedAPIsArray = Array.isArray(allowedAPIs) ? allowedAPIs : [allowedAPIs]
|
|
44
44
|
if (target != 'mv3') return {}
|
|
45
45
|
function isAllowed(api) {
|
|
46
|
-
for (const allowed of
|
|
46
|
+
for (const allowed of allowedAPIsArray)
|
|
47
47
|
if (api == allowed || api.startsWith(`${allowed}.`)) return true
|
|
48
48
|
return false
|
|
49
49
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import regex from '../data/regex.js'
|
|
2
2
|
|
|
3
|
+
const defaultOptions = { flagCommonNames: true },
|
|
4
|
+
re = { keyframesHeader: /^@(?:-webkit-)?keyframes\s+([\w-]+)/i, quotes: /^["']|["']$/g }
|
|
5
|
+
|
|
3
6
|
const commonNames = new Set([
|
|
4
7
|
'blink', 'bounce', 'bounce-in', 'bounce-out', 'fade', 'fade-in', 'fade-in-down', 'fade-in-left', 'fade-in-right',
|
|
5
8
|
'fade-in-up', 'fade-out', 'fade-out-down', 'fade-out-left', 'fade-out-right', 'fade-out-up', 'flip', 'flip-in',
|
|
@@ -8,10 +11,6 @@ const commonNames = new Set([
|
|
|
8
11
|
'slide-out-down', 'slide-out-left', 'slide-out-right', 'slide-out-up', 'spin', 'spinner', 'swing', 'wobble', 'zoom',
|
|
9
12
|
'zoom-in', 'zoom-out'
|
|
10
13
|
])
|
|
11
|
-
const re = {
|
|
12
|
-
keyframesHeader: /^@(?:-webkit-)?keyframes\s+([\w-]+)/i,
|
|
13
|
-
quotes: /^["']|["']$/g
|
|
14
|
-
}
|
|
15
14
|
|
|
16
15
|
export default {
|
|
17
16
|
meta: {
|
|
@@ -21,7 +20,7 @@ export default {
|
|
|
21
20
|
category: 'Possible Errors',
|
|
22
21
|
css: true
|
|
23
22
|
},
|
|
24
|
-
defaultOptions: [
|
|
23
|
+
defaultOptions: [defaultOptions],
|
|
25
24
|
schema: [{
|
|
26
25
|
type: 'object',
|
|
27
26
|
properties: {
|
|
@@ -46,9 +45,7 @@ export default {
|
|
|
46
45
|
},
|
|
47
46
|
|
|
48
47
|
create(context) {
|
|
49
|
-
const
|
|
50
|
-
requiredPrefix = opts.requiredPrefix,
|
|
51
|
-
flagCommonNames = opts.flagCommonNames !== false
|
|
48
|
+
const { requiredPrefix, flagCommonNames } = { ...defaultOptions, ...context.options[0] }
|
|
52
49
|
function getKeyframeName(node) {
|
|
53
50
|
const pv = node.prelude?.value
|
|
54
51
|
if (typeof pv == 'string' && pv.trim())
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import array from '../utils/array.js'
|
|
2
2
|
import node from '../utils/node.js'
|
|
3
3
|
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
allowedSelectors: [], disallowUniversal: true, disallowStructural: true, disallowBareElements: true }
|
|
6
|
+
|
|
4
7
|
const re = { keyframeSelector: /^(?:from|to|[\d.]+%)$/i }
|
|
5
8
|
|
|
6
9
|
export default {
|
|
@@ -11,12 +14,7 @@ export default {
|
|
|
11
14
|
category: 'Possible Errors',
|
|
12
15
|
css: true
|
|
13
16
|
},
|
|
14
|
-
defaultOptions: [
|
|
15
|
-
allowedSelectors: [],
|
|
16
|
-
disallowUniversal: true,
|
|
17
|
-
disallowStructural: true,
|
|
18
|
-
disallowBareElements: true
|
|
19
|
-
}],
|
|
17
|
+
defaultOptions: [defaultOptions],
|
|
20
18
|
schema: [{
|
|
21
19
|
type: 'object',
|
|
22
20
|
properties: {
|
|
@@ -50,15 +48,13 @@ export default {
|
|
|
50
48
|
},
|
|
51
49
|
|
|
52
50
|
create(context) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
checkStructural = opts.disallowStructural !== false,
|
|
57
|
-
checkBareElements = opts.disallowBareElements !== false
|
|
51
|
+
const { allowedSelectors, disallowUniversal, disallowStructural, disallowBareElements }
|
|
52
|
+
= { ...defaultOptions, ...context.options[0] }
|
|
53
|
+
const allowedSelectorSet = new Set(array.to(allowedSelectors))
|
|
58
54
|
function checkSelector(selector) {
|
|
59
55
|
if (!selector) return
|
|
60
56
|
const text = getSelectorText(selector).trim()
|
|
61
|
-
if (re.keyframeSelector.test(text) ||
|
|
57
|
+
if (re.keyframeSelector.test(text) || allowedSelectorSet.has(text)) return
|
|
62
58
|
const simples = []
|
|
63
59
|
for (const child of selector.children ?? []) {
|
|
64
60
|
if (node.plainType(child) == 'Combinator') break
|
|
@@ -78,15 +74,15 @@ export default {
|
|
|
78
74
|
else if (name) elementTypes.push(name)
|
|
79
75
|
}
|
|
80
76
|
}
|
|
81
|
-
if (isUniversal &&
|
|
77
|
+
if (isUniversal && disallowUniversal)
|
|
82
78
|
return context.report({ node: selector, messageId: 'universalSelector' })
|
|
83
|
-
if (isRoot &&
|
|
79
|
+
if (isRoot && disallowStructural)
|
|
84
80
|
return context.report({ node: selector, messageId: 'structuralSelector', data: { selector: text }})
|
|
85
81
|
for (const type of elementTypes) {
|
|
86
82
|
if (type == 'html' || type == 'body') {
|
|
87
|
-
if (
|
|
83
|
+
if (disallowStructural)
|
|
88
84
|
return context.report({ node: selector, messageId: 'structuralSelector', data: { selector: text }})
|
|
89
|
-
} else if (
|
|
85
|
+
} else if (disallowBareElements && !hasQualifier) {
|
|
90
86
|
return context.report({ node: selector, messageId: 'elementSelector', data: { selector: text }})
|
|
91
87
|
}
|
|
92
88
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import array from '../utils/array.js'
|
|
4
4
|
import html from '../utils/html.js'
|
|
5
5
|
|
|
6
|
+
const defaultOptions = { allowedAttributes: [] }
|
|
7
|
+
|
|
6
8
|
const re = {
|
|
7
9
|
attrNameEnd: /[\s=>]/,
|
|
8
10
|
block: {
|
|
@@ -55,7 +57,7 @@ export default {
|
|
|
55
57
|
category: 'Possible Errors',
|
|
56
58
|
html: true
|
|
57
59
|
},
|
|
58
|
-
defaultOptions: [
|
|
60
|
+
defaultOptions: [defaultOptions],
|
|
59
61
|
schema: [{
|
|
60
62
|
type: 'object',
|
|
61
63
|
properties: {
|
|
@@ -76,9 +78,9 @@ export default {
|
|
|
76
78
|
},
|
|
77
79
|
|
|
78
80
|
create(context) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
const { allowedAttributes } = { ...defaultOptions, ...context.options[0] },
|
|
82
|
+
allowedAttributesSet = new Set(array.to(allowedAttributes).map(attr => attr.toLowerCase())),
|
|
83
|
+
text = context.sourceCode.text
|
|
82
84
|
let scanned = false
|
|
83
85
|
function maskContent() {
|
|
84
86
|
return text
|
|
@@ -105,7 +107,7 @@ export default {
|
|
|
105
107
|
for (const attr of parseAttrs(attrPart)) {
|
|
106
108
|
if (!re.inlineHandler.test(attr.name)) continue
|
|
107
109
|
const attrLower = attr.name.toLowerCase()
|
|
108
|
-
if (
|
|
110
|
+
if (allowedAttributesSet.has(attrLower)) continue
|
|
109
111
|
context.report({ loc: html.offsetToLoc(text, tagEnd + attr.offset),
|
|
110
112
|
messageId: 'inlineHandler', data: { attr: attrLower }})
|
|
111
113
|
}
|