diff --git a/CHANGELOG.md b/CHANGELOG.md index e8767d7..8e5a519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#112](https://github.com/green-code-initiative/creedengo-javascript/pull/112) Clarify rule GCI9 "no-import-all-from-library" - [#113](https://github.com/green-code-initiative/creedengo-javascript/pull/113) Extend rule GCI530 "no-torch" to detect HTML5 Web API usage +- [#115](https://github.com/green-code-initiative/creedengo-javascript/pull/115) Add Vue SFC template support to JSX‑based rules ## [3.1.0] - 2026-05-10 diff --git a/README.md b/README.md index d69e101..deae27b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This project proposes rules for the following technologies: - NestJS - React (JSX) - React Native / Expo +- Vue ## 🔧 ESLint plugin diff --git a/eslint-plugin/docs/rules/avoid-autoplay.md b/eslint-plugin/docs/rules/avoid-autoplay.md index 24d9a32..ed650ba 100644 --- a/eslint-plugin/docs/rules/avoid-autoplay.md +++ b/eslint-plugin/docs/rules/avoid-autoplay.md @@ -17,6 +17,10 @@ Nevertheless, some parts of the video or audio files may be downloaded even if a will be unnecessarily downloaded even if users do not start the video playback. It is therefore necessary to force browsers not to preload anything by setting the `preload` attribute to `none`. +This rule supports both [React](https://react.dev/) (JSX) and [Vue](https://vuejs.org/) (template) syntax. + +### React (JSX) + ```jsx return ( <> @@ -28,7 +32,14 @@ return ( ); ``` -This rule is build for [React](https://react.dev/) and JSX. +### Vue + +```html + + + + +``` ## Resources diff --git a/eslint-plugin/docs/rules/avoid-css-animations.md b/eslint-plugin/docs/rules/avoid-css-animations.md index 77ff251..5eb88df 100644 --- a/eslint-plugin/docs/rules/avoid-css-animations.md +++ b/eslint-plugin/docs/rules/avoid-css-animations.md @@ -16,6 +16,10 @@ On mobile devices, constant animations can contribute to increased power consump life. Limiting the usage of CSS animations helps in creating a more energy-efficient and mobile-friendly user experience. +This rule supports both [React](https://react.dev/) (JSX) and [Vue](https://vuejs.org/) (template) syntax. + +### React (JSX) + ```jsx
// Non-compliant ``` @@ -24,6 +28,17 @@ Limiting the usage of CSS animations helps in creating a more energy-efficient a // Compliant ``` +### Vue + +```html + + + + +``` + +Vue support only checks static style attributes; :style bindings are not validated. + It's important to note that while limiting animations is generally advisable for certain scenarios, there are cases where animations contribute positively to the user experience and overall design. In this case they should be limited to the CSS properties `opacity` and `transform` with it's associated diff --git a/eslint-plugin/docs/rules/no-empty-image-src-attribute.md b/eslint-plugin/docs/rules/no-empty-image-src-attribute.md index 4d8c10f..6ae0bec 100644 --- a/eslint-plugin/docs/rules/no-empty-image-src-attribute.md +++ b/eslint-plugin/docs/rules/no-empty-image-src-attribute.md @@ -17,6 +17,10 @@ Screen readers and other assistive technologies rely on valid image sources to p with disabilities. A missing src attribute can result in confusion and hinder accessibility. +This rule supports both [React](https://react.dev/) (JSX) and [Vue](https://vuejs.org/) (template) syntax. + +### React (JSX) + ```jsx return ( <> @@ -40,7 +44,15 @@ return ( ); ``` -This rule is build for [React](https://react.dev/) and JSX. +### Vue + +```html + +
//
Non-compliant
@@ -72,6 +74,15 @@ is supported, the image.webp image will be downloaded; otherwise, image.jpg imag
```
+### Vue
+
+```html
+
+
+
+
+```
+
Also remember to consider browser compatibility.
Older browsers may not recognize .webp/.avif images and fail to display them.
To address this issue, you can supply multiple formats for the same image.
diff --git a/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md b/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md
index 707c896..6de5979 100644
--- a/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md
+++ b/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md
@@ -36,6 +36,10 @@ module.exports = {
For example, the `font` shorthand consolidates various font-related properties, and the `margin` shorthand streamlines
the definition of margins around a box.
+This rule supports both [React](https://react.dev/) (JSX) and [Vue](https://vuejs.org/) (template) syntax.
+
+### React (JSX)
+
```jsx
",
+ "
",
+ "
",
+ errors: [preferLighterFormatsForImageFilesError],
+ },
+ {
+ code: "
",
+ errors: [preferLighterFormatsForImageFilesError],
+ },
+ ],
+};
+
describe("prefer-lighter-formats-for-image-files", () => {
- it("prefer-lighter-formats-for-image-files", () => {
+ it("React", () => {
ruleTester.run("prefer-lighter-formats-for-image-files", rule, tests);
});
+
+ it("Vue", () => {
+ vueRuleTester.run("prefer-lighter-formats-for-image-files", rule, vueTests);
+ });
});
diff --git a/eslint-plugin/tests/lib/rules/prefer-shorthand-css-notations.test.js b/eslint-plugin/tests/lib/rules/prefer-shorthand-css-notations.test.js
index b432a22..d9cbcbd 100644
--- a/eslint-plugin/tests/lib/rules/prefer-shorthand-css-notations.test.js
+++ b/eslint-plugin/tests/lib/rules/prefer-shorthand-css-notations.test.js
@@ -42,11 +42,27 @@ const ruleTester = new RuleTester({
},
});
+const vueRuleTester = new RuleTester({
+ languageOptions: {
+ parser: require("vue-eslint-parser"),
+ parserOptions: {
+ ecmaVersion: 2021,
+ sourceType: "module",
+ parser: require("@typescript-eslint/parser"),
+ },
+ },
+});
+
const createError = (property) => ({
messageId: "PreferShorthandCSSNotation",
data: { property },
});
+const vueCreateError = (property) => ({
+ messageId: "PreferShorthandCSSNotation",
+ data: { property },
+});
+
const tests = {
valid: [
"",
@@ -164,8 +180,29 @@ const tests = {
],
};
+const vueTests = {
+ valid: [
+ "",
+ "",
+ ],
+ invalid: [
+ {
+ code: "",
+ errors: [vueCreateError("margin")],
+ },
+ {
+ code: "",
+ errors: [vueCreateError("border")],
+ },
+ ],
+};
+
describe("prefer-shorthand-css-notations", () => {
- it("prefer-shorthand-css-notations", () => {
+ it("React", () => {
ruleTester.run("prefer-shorthand-css-notations", rule, tests);
});
+
+ it("Vue", () => {
+ vueRuleTester.run("prefer-shorthand-css-notations", rule, vueTests);
+ });
});
diff --git a/eslint-plugin/yarn.lock b/eslint-plugin/yarn.lock
index 2164d4b..918d65c 100644
--- a/eslint-plugin/yarn.lock
+++ b/eslint-plugin/yarn.lock
@@ -48,6 +48,7 @@ __metadata:
prettier: "npm:^3.8.3"
rimraf: "npm:^6.1.3"
typescript: "npm:~5.9.3"
+ vue-eslint-parser: "npm:^10.4.0"
peerDependencies:
eslint: ^9.0.0 || ^10.0.0
languageName: unknown
@@ -560,7 +561,7 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3":
+"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.0, debug@npm:^4.4.3":
version: 4.4.3
resolution: "debug@npm:4.4.3"
dependencies:
@@ -731,7 +732,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-scope@npm:^9.1.2":
+"eslint-scope@npm:^8.2.0 || ^9.0.0, eslint-scope@npm:^9.1.2":
version: 9.1.2
resolution: "eslint-scope@npm:9.1.2"
dependencies:
@@ -750,7 +751,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1":
+"eslint-visitor-keys@npm:^4.2.0 || ^5.0.0, eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1":
version: 5.0.1
resolution: "eslint-visitor-keys@npm:5.0.1"
checksum: 10/f9cc1a57b75e0ef949545cac33d01e8367e302de4c1483266ed4d8646ee5c306376660196bbb38b004e767b7043d1e661cb4336b49eff634a1bbe75c1db709ec
@@ -802,7 +803,7 @@ __metadata:
languageName: node
linkType: hard
-"espree@npm:^11.2.0":
+"espree@npm:^10.3.0 || ^11.0.0, espree@npm:^11.2.0":
version: 11.2.0
resolution: "espree@npm:11.2.0"
dependencies:
@@ -813,7 +814,7 @@ __metadata:
languageName: node
linkType: hard
-"esquery@npm:^1.7.0":
+"esquery@npm:^1.6.0, esquery@npm:^1.7.0":
version: 1.7.0
resolution: "esquery@npm:1.7.0"
dependencies:
@@ -1399,6 +1400,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^7.6.3":
+ version: 7.8.0
+ resolution: "semver@npm:7.8.0"
+ bin:
+ semver: bin/semver.js
+ checksum: 10/039a8f68a581c03c1ac17c990316da57a79a93af9b109b712739c50cd4d464079f7e3fee31c008b472e390c7ba48a11ed2b86e91d8602bf06059d4a266db1426
+ languageName: node
+ linkType: hard
+
"semver@npm:^7.7.2, semver@npm:^7.7.3":
version: 7.7.3
resolution: "semver@npm:7.7.3"
@@ -1575,6 +1585,22 @@ __metadata:
languageName: node
linkType: hard
+"vue-eslint-parser@npm:^10.4.0":
+ version: 10.4.0
+ resolution: "vue-eslint-parser@npm:10.4.0"
+ dependencies:
+ debug: "npm:^4.4.0"
+ eslint-scope: "npm:^8.2.0 || ^9.0.0"
+ eslint-visitor-keys: "npm:^4.2.0 || ^5.0.0"
+ espree: "npm:^10.3.0 || ^11.0.0"
+ esquery: "npm:^1.6.0"
+ semver: "npm:^7.6.3"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ checksum: 10/b0d257ffc6afdc6fa47acd8633d3aa4959d1417fe182cd66c7fed02e0425dc9e38f6c4ee2182dbc098be14178801c3c731bd424eaad202b975ddf0756819259f
+ languageName: node
+ linkType: hard
+
"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"
diff --git a/test-project/eslint.config.mjs b/test-project/eslint.config.mjs
index ba13826..7e59b22 100644
--- a/test-project/eslint.config.mjs
+++ b/test-project/eslint.config.mjs
@@ -3,6 +3,7 @@ import eslint from "@eslint/js";
import { defineConfig } from "eslint/config";
import globals from "globals";
import tseslint from "typescript-eslint";
+import vueParser from "vue-eslint-parser";
export default defineConfig(
eslint.configs.recommended,
@@ -16,4 +17,16 @@ export default defineConfig(
},
},
},
+ {
+ files: ["**/*.vue"],
+ languageOptions: {
+ parser: vueParser,
+ parserOptions: {
+ ecmaVersion: 2021,
+ sourceType: "module",
+ parser: tseslint.parser, // use "espree" if not using TS
+ extraFileExtensions: [".vue"],
+ },
+ },
+ },
);
diff --git a/test-project/package.json b/test-project/package.json
index 48eccd6..48c3a9e 100644
--- a/test-project/package.json
+++ b/test-project/package.json
@@ -21,7 +21,8 @@
"eslint": "^10.2.1",
"globals": "^17.5.0",
"typescript": "~5.9.3",
- "typescript-eslint": "^8.59.1"
+ "typescript-eslint": "^8.59.1",
+ "vue-eslint-parser": "^10.4.0"
},
"packageManager": "yarn@4.14.1"
}
diff --git a/test-project/src/avoid-autoplay.js b/test-project/src/js/avoid-autoplay.js
similarity index 100%
rename from test-project/src/avoid-autoplay.js
rename to test-project/src/js/avoid-autoplay.js
diff --git a/test-project/src/avoid-brightness-override.js b/test-project/src/js/avoid-brightness-override.js
similarity index 100%
rename from test-project/src/avoid-brightness-override.js
rename to test-project/src/js/avoid-brightness-override.js
diff --git a/test-project/src/avoid-high-accuracy-geolocation.js b/test-project/src/js/avoid-high-accuracy-geolocation.js
similarity index 100%
rename from test-project/src/avoid-high-accuracy-geolocation.js
rename to test-project/src/js/avoid-high-accuracy-geolocation.js
diff --git a/test-project/src/avoid-keep-awake-react-native-fn.js b/test-project/src/js/avoid-keep-awake-react-native-fn.js
similarity index 100%
rename from test-project/src/avoid-keep-awake-react-native-fn.js
rename to test-project/src/js/avoid-keep-awake-react-native-fn.js
diff --git a/test-project/src/avoid-keep-awake-react-native-hook.js b/test-project/src/js/avoid-keep-awake-react-native-hook.js
similarity index 100%
rename from test-project/src/avoid-keep-awake-react-native-hook.js
rename to test-project/src/js/avoid-keep-awake-react-native-hook.js
diff --git a/test-project/src/import-all-from-library.js b/test-project/src/js/import-all-from-library.js
similarity index 100%
rename from test-project/src/import-all-from-library.js
rename to test-project/src/js/import-all-from-library.js
diff --git a/test-project/src/limit-db-query-results.js b/test-project/src/js/limit-db-query-results.js
similarity index 100%
rename from test-project/src/limit-db-query-results.js
rename to test-project/src/js/limit-db-query-results.js
diff --git a/test-project/src/modular-import-from-library.js b/test-project/src/js/modular-import-from-library.js
similarity index 100%
rename from test-project/src/modular-import-from-library.js
rename to test-project/src/js/modular-import-from-library.js
diff --git a/test-project/src/no-css-animations.js b/test-project/src/js/no-css-animations.js
similarity index 100%
rename from test-project/src/no-css-animations.js
rename to test-project/src/js/no-css-animations.js
diff --git a/test-project/src/no-empty-image-src-attribute.js b/test-project/src/js/no-empty-image-src-attribute.js
similarity index 100%
rename from test-project/src/no-empty-image-src-attribute.js
rename to test-project/src/js/no-empty-image-src-attribute.js
diff --git a/test-project/src/no-imported-number-format-library.js b/test-project/src/js/no-imported-number-format-library.js
similarity index 100%
rename from test-project/src/no-imported-number-format-library.js
rename to test-project/src/js/no-imported-number-format-library.js
diff --git a/test-project/src/no-multiple-access-dom-element.js b/test-project/src/js/no-multiple-access-dom-element.js
similarity index 100%
rename from test-project/src/no-multiple-access-dom-element.js
rename to test-project/src/js/no-multiple-access-dom-element.js
diff --git a/test-project/src/no-torch.js b/test-project/src/js/no-torch.js
similarity index 100%
rename from test-project/src/no-torch.js
rename to test-project/src/js/no-torch.js
diff --git a/test-project/src/prefer-lighter-formats-for-image-files.js b/test-project/src/js/prefer-lighter-formats-for-image-files.js
similarity index 100%
rename from test-project/src/prefer-lighter-formats-for-image-files.js
rename to test-project/src/js/prefer-lighter-formats-for-image-files.js
diff --git a/test-project/src/prefer-shorthand-css-notations.js b/test-project/src/js/prefer-shorthand-css-notations.js
similarity index 100%
rename from test-project/src/prefer-shorthand-css-notations.js
rename to test-project/src/js/prefer-shorthand-css-notations.js
diff --git a/test-project/src/provide-print-css.js b/test-project/src/js/provide-print-css.js
similarity index 100%
rename from test-project/src/provide-print-css.js
rename to test-project/src/js/provide-print-css.js
diff --git a/test-project/src/rule-no-multiple-style-changes.js b/test-project/src/js/rule-no-multiple-style-changes.js
similarity index 100%
rename from test-project/src/rule-no-multiple-style-changes.js
rename to test-project/src/js/rule-no-multiple-style-changes.js
diff --git a/test-project/src/prefer-collections-with-pagination.ts b/test-project/src/ts/prefer-collections-with-pagination.ts
similarity index 100%
rename from test-project/src/prefer-collections-with-pagination.ts
rename to test-project/src/ts/prefer-collections-with-pagination.ts
diff --git a/test-project/src/vue/avoid-autoplay.vue b/test-project/src/vue/avoid-autoplay.vue
new file mode 100644
index 0000000..9343518
--- /dev/null
+++ b/test-project/src/vue/avoid-autoplay.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test-project/src/vue/avoid-css-animations.vue b/test-project/src/vue/avoid-css-animations.vue
new file mode 100644
index 0000000..ff040c8
--- /dev/null
+++ b/test-project/src/vue/avoid-css-animations.vue
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test-project/src/vue/no-empty-image-src-attribute.vue b/test-project/src/vue/no-empty-image-src-attribute.vue
new file mode 100644
index 0000000..e9ddbef
--- /dev/null
+++ b/test-project/src/vue/no-empty-image-src-attribute.vue
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/test-project/src/vue/prefer-shorthand-css-notations.vue b/test-project/src/vue/prefer-shorthand-css-notations.vue
new file mode 100644
index 0000000..56821bc
--- /dev/null
+++ b/test-project/src/vue/prefer-shorthand-css-notations.vue
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/test-project/yarn.lock b/test-project/yarn.lock
index e25d90d..a7ce6cc 100644
--- a/test-project/yarn.lock
+++ b/test-project/yarn.lock
@@ -21,10 +21,10 @@ __metadata:
"@creedengo/eslint-plugin@file:../eslint-plugin::locator=creedengo-javascript-test-project%40workspace%3A.":
version: 3.1.0
- resolution: "@creedengo/eslint-plugin@file:../eslint-plugin#../eslint-plugin::hash=5bb3d4&locator=creedengo-javascript-test-project%40workspace%3A."
+ resolution: "@creedengo/eslint-plugin@file:../eslint-plugin#../eslint-plugin::hash=ed5cfe&locator=creedengo-javascript-test-project%40workspace%3A."
peerDependencies:
eslint: ^9.0.0 || ^10.0.0
- checksum: 10c0/bfe290b4ff43b71e4adcd9713fbd8f2fed54b34f5ca13db3c0d81225114f84a61bcb08d6756b2cedbdfc0ef19f2442011c1b771d2eae3885b031f74977ee03fc
+ checksum: 10c0/aa9089b62919097407454a8922f75530a349467f4d1c8b569485491f87cb383d7ff80cbb4fbb2ea931660043d49b8faad3516e87b3ed9207f1c757dc57156d57
languageName: node
linkType: hard
@@ -572,6 +572,7 @@ __metadata:
globals: "npm:^17.5.0"
typescript: "npm:~5.9.3"
typescript-eslint: "npm:^8.59.1"
+ vue-eslint-parser: "npm:^10.4.0"
languageName: unknown
linkType: soft
@@ -586,7 +587,7 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3":
+"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.0, debug@npm:^4.4.3":
version: 4.4.3
resolution: "debug@npm:4.4.3"
dependencies:
@@ -665,7 +666,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-scope@npm:^9.1.2":
+"eslint-scope@npm:^8.2.0 || ^9.0.0, eslint-scope@npm:^9.1.2":
version: 9.1.2
resolution: "eslint-scope@npm:9.1.2"
dependencies:
@@ -684,7 +685,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1":
+"eslint-visitor-keys@npm:^4.2.0 || ^5.0.0, eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1":
version: 5.0.1
resolution: "eslint-visitor-keys@npm:5.0.1"
checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678
@@ -736,7 +737,7 @@ __metadata:
languageName: node
linkType: hard
-"espree@npm:^11.2.0":
+"espree@npm:^10.3.0 || ^11.0.0, espree@npm:^11.2.0":
version: 11.2.0
resolution: "espree@npm:11.2.0"
dependencies:
@@ -747,7 +748,7 @@ __metadata:
languageName: node
linkType: hard
-"esquery@npm:^1.7.0":
+"esquery@npm:^1.6.0, esquery@npm:^1.7.0":
version: 1.7.0
resolution: "esquery@npm:1.7.0"
dependencies:
@@ -1256,6 +1257,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^7.6.3":
+ version: 7.8.0
+ resolution: "semver@npm:7.8.0"
+ bin:
+ semver: bin/semver.js
+ checksum: 10c0/8f096ca9b80ffd47b308d03f9ce8c873e27e2983f36023c559cdc92c51e8433fc23ebbfe57ec9623fc155636a6961ee989501099841ae4bb1babc8d2b3f048cd
+ languageName: node
+ linkType: hard
+
"shebang-command@npm:^2.0.0":
version: 2.0.0
resolution: "shebang-command@npm:2.0.0"
@@ -1435,6 +1445,22 @@ __metadata:
languageName: node
linkType: hard
+"vue-eslint-parser@npm:^10.4.0":
+ version: 10.4.0
+ resolution: "vue-eslint-parser@npm:10.4.0"
+ dependencies:
+ debug: "npm:^4.4.0"
+ eslint-scope: "npm:^8.2.0 || ^9.0.0"
+ eslint-visitor-keys: "npm:^4.2.0 || ^5.0.0"
+ espree: "npm:^10.3.0 || ^11.0.0"
+ esquery: "npm:^1.6.0"
+ semver: "npm:^7.6.3"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ checksum: 10c0/ded1c52dfa6e08c384da43b8369814bd105e2dc3bbb04e95faa8365af0fcba70293ff8b3749824ae3cc98988aeaaa261d5a205febff23e15667176392dcfee4a
+ languageName: node
+ linkType: hard
+
"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"