diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a1b097..4ca0260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added + +- [#21](https://github.com/green-code-initiative/ecoCode-javascript/pull/21) Add rule `@ecocode/avoid-css-animations` - [#19](https://github.com/green-code-initiative/ecoCode-javascript/pull/19) add rule `@ecocode/no-empty-image-src-attribute` - [#18](https://github.com/green-code-initiative/ecoCode-javascript/pull/18) add rule `@ecocode/limit-db-query-results` - [#14](https://github.com/green-code-initiative/ecoCode-javascript/pull/14) Create SonarQube plugin diff --git a/eslint-plugin/README.md b/eslint-plugin/README.md index 07f5451..0caeb20 100644 --- a/eslint-plugin/README.md +++ b/eslint-plugin/README.md @@ -58,6 +58,7 @@ Add `@ecocode` to the `plugins` section of your `.eslintrc`, followed by rules c | Name | Description | ⚠️ | | :------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- | +| [avoid-css-animations](docs/rules/avoid-css-animations.md) | Avoid usage of CSS animations | ✅ | | [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications. | ✅ | | [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query | ✅ | | [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute | ✅ | diff --git a/eslint-plugin/docs/rules/avoid-css-animations.md b/eslint-plugin/docs/rules/avoid-css-animations.md new file mode 100644 index 0000000..7fca356 --- /dev/null +++ b/eslint-plugin/docs/rules/avoid-css-animations.md @@ -0,0 +1,31 @@ +# Avoid usage of CSS animations (`@ecocode/avoid-css-animations`) + +⚠️ This rule _warns_ in the ✅ `recommended` config. + + + +## Rule Details + +This rule aims to limit the usage of all types of CSS animations which can be very costly in terms of CPU and memory. +They must only be used when they are indispensable and should be limited to the CSS properties `opacity` and `transform` +with it's associated functions : `translate`, `rotate` and `scale`. You can also inform the navigator of upcoming +changes with the `will-change` instruction for more optimization. + +## Examples + +Examples of **non compliant** code for this rule: + +```js +
+``` + +Examples of **compliant** code for this rule: + +```js +
+``` + +## Further details + +This recommendation is made by the +CNUMR: [Avoid JavaScript / CSS animations](https://github.com/cnumr/best-practices/blob/main/chapters/BP_039_en.md) diff --git a/eslint-plugin/lib/rules/avoid-css-animations.js b/eslint-plugin/lib/rules/avoid-css-animations.js new file mode 100644 index 0000000..9f9d8cc --- /dev/null +++ b/eslint-plugin/lib/rules/avoid-css-animations.js @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2023 Green Code Initiative + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +"use strict"; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "suggestion", + docs: { + description: "Avoid usage of CSS animations", + category: "eco-design", + recommended: "warn", + }, + messages: { + AvoidCSSAnimations: "Avoid using {{attribute}} in CSS.", + }, + schema: [], + }, + create(context) { + const forbiddenProperties = ["transition", "animation"]; + return { + JSXOpeningElement(node) { + const styleAttribute = node.attributes.find( + (attribute) => attribute.name.name === "style", + ); + + if (styleAttribute && styleAttribute.value.expression) { + // To prevent (for example)
+ const property = styleAttribute.value.expression.properties.find( + (prop) => + forbiddenProperties.some((forbidProp) => + prop.key.name.includes(forbidProp), + ), + ); + if (property != null) { + context.report({ + node: property, + messageId: "AvoidCSSAnimations", + data: { + attribute: property.key.name, + }, + }); + } + } + }, + }; + }, +}; diff --git a/eslint-plugin/tests/lib/rules/avoid-css-animations.js b/eslint-plugin/tests/lib/rules/avoid-css-animations.js new file mode 100644 index 0000000..20a2fce --- /dev/null +++ b/eslint-plugin/tests/lib/rules/avoid-css-animations.js @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2023 Green Code Initiative + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/avoid-css-animations"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2021, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run("avoid-css-animations", rule, { + valid: [ + ` + import React from 'react'; + import './styles.css'; // External CSS file + + const MyComponent = () => { + return
This content is styled using an external CSS file.
; + }; + + export default MyComponent; + `, + `
Hello world
`, + `
My red element
`, + ], + + invalid: [ + { + code: "
", + errors: [ + { + messageId: "AvoidCSSAnimations", + data: { + attribute: "transition", + }, + type: "Property", + }, + ], + }, + { + code: "
", + errors: [ + { + messageId: "AvoidCSSAnimations", + data: { + attribute: "animationName", + }, + type: "Property", + }, + ], + }, + ], +});