eslint-plugin-chrome-extensions 2.8.1 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -83
- package/docs/LICENSE.md +1 -1
- package/package.json +14 -10
- 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 +21 -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>
|
|
@@ -44,9 +44,8 @@ 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
|
-
|
|
49
|
-
rules: {
|
|
47
|
+
files: ['**/extension/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}'], ...chromeExtensions.configs.recommended,
|
|
48
|
+
rules: { // include this block if customizing rules
|
|
50
49
|
...chromeExtensions.configs.recommended.rules,
|
|
51
50
|
'chrome-extensions/no-deprecated-apis': 'off', // selectively disable rule
|
|
52
51
|
'chrome-extensions/valid-version-field': ['error', { versionType: 'date' }] // selectively tweak rule
|
|
@@ -68,7 +67,7 @@ export default [
|
|
|
68
67
|
|
|
69
68
|
### <img height=15 width=auto src="https://www.google.com/s2/favicons?domain=oxc.rs&sz=64"> Oxlint
|
|
70
69
|
|
|
71
|
-
Add a JS bundle to your `oxlint.config
|
|
70
|
+
Add a JS/TS bundle to your `oxlint.config.*ts` (non-JS/TS parsing is unsupported by Oxlint):
|
|
72
71
|
|
|
73
72
|
```ts
|
|
74
73
|
...
|
|
@@ -78,7 +77,7 @@ export default defineConfig({
|
|
|
78
77
|
overrides: [
|
|
79
78
|
// other configs
|
|
80
79
|
{
|
|
81
|
-
files: ['**/extension/**/*.{cjs,js,jsx,mjs,ts,tsx}'],
|
|
80
|
+
files: ['**/extension/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}'],
|
|
82
81
|
jsPlugins: ['eslint-plugin-chrome-extensions'],
|
|
83
82
|
rules: {
|
|
84
83
|
...chromeExtensions.configs.recommended.rules,
|
|
@@ -96,7 +95,7 @@ export default defineConfig({
|
|
|
96
95
|
|
|
97
96
|
---
|
|
98
97
|
|
|
99
|
-
*Available JS bundles:* `recommended`, `strict` (everything), `problem` (deterministic bugs only), `security`
|
|
98
|
+
*Available JS/TS bundles:* `recommended`, `strict` (everything), `problem` (deterministic bugs only), `security`
|
|
100
99
|
|
|
101
100
|
*Available filetype bundles (ESLint only):* `manifest`, `license`, `css`, `html`, `json`, `json5`, `jsonc`
|
|
102
101
|
|
|
@@ -595,10 +594,78 @@ If your CSS is scoped inside a Shadow DOM root.
|
|
|
595
594
|
|
|
596
595
|
</details>
|
|
597
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
|
+
|
|
598
667
|
<h3 id="no-inline-event-handlers">
|
|
599
668
|
<code>no-inline-event-handlers</code>
|
|
600
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
601
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
602
669
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
603
670
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
604
671
|
<a title="Accepts array w/ options" href="#usage">
|
|
@@ -673,8 +740,6 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
|
|
|
673
740
|
|
|
674
741
|
<h3 id="no-inline-scripts">
|
|
675
742
|
<code>no-inline-scripts</code>
|
|
676
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
677
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
678
743
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
679
744
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
680
745
|
<a title="Accepts array w/ options" href="#usage">
|
|
@@ -742,76 +807,6 @@ If your HTML is a <a target="_blank" href="https://developer.chrome.com/docs/ext
|
|
|
742
807
|
|
|
743
808
|
</details>
|
|
744
809
|
|
|
745
|
-
<h3 id="no-host-page-selectors">
|
|
746
|
-
<code>no-host-page-selectors</code>
|
|
747
|
-
<a title="Only works in 'css/css' config block" href="#usage">
|
|
748
|
-
<img height=18 alt="[CSS only]" src="https://img.shields.io/badge/CSS_only-dcd6f7?style=flat-square" align="middle"></a>
|
|
749
|
-
<a title="Accepts array w/ options" href="#usage">
|
|
750
|
-
<img height=18 alt="[Configurable]" src="https://img.shields.io/badge/Configurable-black?style=flat-square" align="middle"></a>
|
|
751
|
-
<a title="Copy rule URL" target="_blank"
|
|
752
|
-
href="https://copy-to-clipboard.onrender.com/copy/https%3A%2F%2Fcodeberg.org%2Fadamlui%2Feslint-plugin-chrome-extensions%2F%23no-host-page-selectors">
|
|
753
|
-
<img height=16 width=21 alt="[Copy rule URL]" src="https://api.iconify.design/mdi/content-copy.svg?color=%23cccccc" align="middle"></a>
|
|
754
|
-
</h3>
|
|
755
|
-
|
|
756
|
-
<details>
|
|
757
|
-
|
|
758
|
-
<summary>Disallows host-page selectors (<code>html</code>, <code>body</code>, <code>:root</code>, <code>*</code>, bare elem types) in content-script CSS.</summary>
|
|
759
|
-
|
|
760
|
-
<h4 id="no-host-page-selectors-options">Options</h4>
|
|
761
|
-
|
|
762
|
-
- `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>
|
|
763
|
-
- `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>
|
|
764
|
-
- `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>
|
|
765
|
-
- `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>
|
|
766
|
-
|
|
767
|
-
#### <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
|
|
768
|
-
|
|
769
|
-
```diff
|
|
770
|
-
! * { box-sizing: border-box }
|
|
771
|
-
|
|
772
|
-
! body { margin: 0 }
|
|
773
|
-
! html { font-family: sans-serif }
|
|
774
|
-
! button:hover { opacity: 0.8 }
|
|
775
|
-
```
|
|
776
|
-
|
|
777
|
-
#### <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
|
|
778
|
-
|
|
779
|
-
```diff
|
|
780
|
-
- * { box-sizing: border-box }
|
|
781
|
-
+ :host { box-sizing: border-box }
|
|
782
|
-
|
|
783
|
-
- body { margin: 0 }
|
|
784
|
-
+ :host { margin: 0 }
|
|
785
|
-
- html { font-family: sans-serif }
|
|
786
|
-
+ :host { font-family: sans-serif }
|
|
787
|
-
- button:hover { opacity: 0.8 }
|
|
788
|
-
+ .myext-button:hover { opacity: 0.8 }
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
#### When not to use it
|
|
792
|
-
|
|
793
|
-
If you intentionally style the host page. For individual selectors you still need, prefer `allowedSelectors` over disabling the rule.
|
|
794
|
-
|
|
795
|
-
#### Introduced in
|
|
796
|
-
|
|
797
|
-
<a target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.7.0">v2.7.0</a> (October 17, 2026)
|
|
798
|
-
|
|
799
|
-
#### See also
|
|
800
|
-
|
|
801
|
-
- [`no-content-style-leak`](#no-content-style-leak)
|
|
802
|
-
<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>
|
|
803
|
-
- <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>
|
|
804
|
-
|
|
805
|
-
<div align="center">
|
|
806
|
-
|
|
807
|
-
[Back to index ↑][epce-rules]
|
|
808
|
-
|
|
809
|
-
</div>
|
|
810
|
-
|
|
811
|
-
---
|
|
812
|
-
|
|
813
|
-
</details>
|
|
814
|
-
|
|
815
810
|
<h3 id="no-insecure-fetch">
|
|
816
811
|
<code>no-insecure-fetch</code>
|
|
817
812
|
<a title="Included in recommended config" href="#usage">
|
|
@@ -1481,8 +1476,6 @@ If you intentionally target forks w/ relaxed CSPs. For individual patterns you s
|
|
|
1481
1476
|
|
|
1482
1477
|
<h3 id="no-remote-objects">
|
|
1483
1478
|
<code>no-remote-objects</code>
|
|
1484
|
-
<a title="Introduced in latest minor release" target="_blank" href="https://codeberg.org/adamlui/eslint-plugin-chrome-extensions/releases/tag/v2.8.0">
|
|
1485
|
-
<img height=16 align="middle" src="https://api.iconify.design/mdi/sprout.svg?color=%234a4a4a"></a>
|
|
1486
1479
|
<a title="Only works in 'html/html' config block" href="#usage">
|
|
1487
1480
|
<img height=18 alt="[HTML only]" src="https://img.shields.io/badge/HTML_only-a8d8ea?style=flat-square" align="middle"></a>
|
|
1488
1481
|
<a title="Accepts array w/ options" href="#usage">
|
package/docs/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
**Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui)
|
|
3
|
+
**Copyright © 2026 [Adam Lui](https://codeberg.org/adamlui).**
|
|
4
4
|
|
|
5
5
|
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:
|
|
6
6
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-chrome-extensions",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "ESLint rules for Chrome extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -72,25 +72,29 @@
|
|
|
72
72
|
"@eslint/markdown": "^7.5.1",
|
|
73
73
|
"@html-eslint/eslint-plugin": "^0.67.0",
|
|
74
74
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
75
|
-
"eslint": "^
|
|
76
|
-
"eslint
|
|
75
|
+
"@typescript-eslint/parser": "^8.72.1",
|
|
76
|
+
"eslint": "^10.14.0",
|
|
77
|
+
"eslint-plugin-constructors": "^1.7.0",
|
|
77
78
|
"eslint-plugin-eslint-plugin": "^7.6.2",
|
|
78
79
|
"eslint-plugin-import-x": "^4.17.1",
|
|
79
80
|
"eslint-plugin-n": "^18.5.0",
|
|
80
81
|
"eslint-plugin-no-secrets": "^2.3.3",
|
|
81
82
|
"eslint-plugin-no-unsanitized": "^4.1.5",
|
|
82
|
-
"eslint-plugin-package-json": "^1.
|
|
83
|
+
"eslint-plugin-package-json": "^1.12.0",
|
|
83
84
|
"eslint-plugin-promise": "^7.3.0",
|
|
84
|
-
"eslint-plugin-regexp": "^3.3.
|
|
85
|
-
"eslint-plugin-security": "^4.2.
|
|
85
|
+
"eslint-plugin-regexp": "^3.3.2",
|
|
86
|
+
"eslint-plugin-security": "^4.2.1",
|
|
86
87
|
"eslint-plugin-sonarjs": "^4.2.2",
|
|
87
|
-
"eslint-plugin-woodpecker": "^1.1.
|
|
88
|
-
"oxlint": "^1.
|
|
88
|
+
"eslint-plugin-woodpecker": "^1.1.2",
|
|
89
|
+
"oxlint": "^1.90.0",
|
|
90
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
91
|
+
"typescript-latest": "npm:typescript@^7.0.2"
|
|
89
92
|
},
|
|
90
93
|
"peerDependencies": {
|
|
91
|
-
"@eslint/css": ">=0.
|
|
94
|
+
"@eslint/css": ">=0.1.0",
|
|
92
95
|
"@eslint/json": ">=0.1.0",
|
|
93
|
-
"@html-eslint/eslint-plugin": ">=0.
|
|
96
|
+
"@html-eslint/eslint-plugin": ">=0.41.0",
|
|
97
|
+
"@typescript-eslint/parser": ">=8.0.0",
|
|
94
98
|
"eslint": ">=9.6.0"
|
|
95
99
|
},
|
|
96
100
|
"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
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import array from '../utils/array.js'
|
|
2
2
|
import html from '../utils/html.js'
|
|
3
3
|
|
|
4
|
+
const defaultOptions = { allowedSrcPrefixes: ['chrome-extension://'] }
|
|
5
|
+
|
|
4
6
|
const re = {
|
|
5
7
|
nonExecutable: /^(?:application\/(?:ld\+)?json|importmap|speculationrules|text\/(?:x-)?template)$/i,
|
|
6
8
|
script: /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi
|
|
@@ -14,7 +16,7 @@ export default {
|
|
|
14
16
|
category: 'Possible Errors',
|
|
15
17
|
html: true
|
|
16
18
|
},
|
|
17
|
-
defaultOptions: [
|
|
19
|
+
defaultOptions: [defaultOptions],
|
|
18
20
|
schema: [{
|
|
19
21
|
type: 'object',
|
|
20
22
|
properties: {
|
|
@@ -29,15 +31,17 @@ export default {
|
|
|
29
31
|
additionalProperties: false
|
|
30
32
|
}],
|
|
31
33
|
messages: {
|
|
32
|
-
inlineBody: 'Inline `<script>` bodies are blocked by MV3\'s `script-src \'self\'`.
|
|
33
|
-
|
|
34
|
+
inlineBody: 'Inline `<script>` bodies are blocked by MV3\'s `script-src \'self\'`. '
|
|
35
|
+
+ 'Move the code to an external file.',
|
|
36
|
+
remoteSrc: 'Remote `<script src="{{src}}">` is blocked by MV3\'s `script-src \'self\'`. '
|
|
37
|
+
+ 'Bundle the script or use `chrome.runtime.getURL()`.'
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
|
|
37
41
|
create(context) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
const { allowedSrcPrefixes } = { ...defaultOptions, ...context.options[0] },
|
|
43
|
+
allowedPrefixes = array.to(allowedSrcPrefixes),
|
|
44
|
+
text = context.sourceCode.text
|
|
41
45
|
let scanned = false
|
|
42
46
|
function scan() {
|
|
43
47
|
if (scanned) return
|