diff --git a/CHANGELOG.md b/CHANGELOG.md index a407930..cc2110b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [#39](https://github.com/green-code-initiative/ecoCode-javascript/issues/39) Add rule `@ecocode/avoid-brightness-override` (EC522) - Add support for SonarQube up to 10.5 ### Changed diff --git a/eslint-plugin/README.md b/eslint-plugin/README.md index a5e19f7..d19d4be 100644 --- a/eslint-plugin/README.md +++ b/eslint-plugin/README.md @@ -69,6 +69,7 @@ Add `@ecocode` to the `plugins` section of your `.eslintrc`, followed by rules c | Name | Description | ⚠️ | | :------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- | +| [avoid-brightness-override](docs/rules/avoid-brightness-override.md) | Should avoid to override brightness | ✅ | | [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 | ✅ | diff --git a/eslint-plugin/docs/rules/avoid-brightness-override.md b/eslint-plugin/docs/rules/avoid-brightness-override.md new file mode 100644 index 0000000..d51fa24 --- /dev/null +++ b/eslint-plugin/docs/rules/avoid-brightness-override.md @@ -0,0 +1,36 @@ +# Should avoid to override brightness (`@ecocode/avoid-brightness-override`) + +⚠️ This rule _warns_ in the ✅ `recommended` config. + + + +## Why is this an issue? + +To avoid draining the battery, IOS and Android devices adapt the brightness of the screen depending on the environment light. + +For some reasons, developers may disable this feature programmatically. + +This feature was introduced to improve battery life, be careful when deactivating it. + +Hence, keeping forcing the screen brightness on should be avoided, unless it is absolutely necessary. + + +## Example of non compliant code + +```js +// Example with expo-brightness (Expo framework library) +import React, { useEffect } from 'react'; +import { View, Text } from 'react-native'; +import * as Brightness from 'expo-brightness'; + +export default function App() { + useEffect(() => { + (async () => { Brightness.setSystemBrightnessAsyn(1); })(); // Brightness is forced here + }, []); + return ( + + Brightness Module Example + + ); +} +``` diff --git a/eslint-plugin/lib/rules/avoid-brightness-override.js b/eslint-plugin/lib/rules/avoid-brightness-override.js new file mode 100644 index 0000000..e2c881d --- /dev/null +++ b/eslint-plugin/lib/rules/avoid-brightness-override.js @@ -0,0 +1,76 @@ +/* + * ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * 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"; + +const brightnessLibrariesMethods = { + "expo-brightness": [ + "setBrightnessAsync", + "setSystemBrightnessAsync", + "setSystemBrightnessAsync", + ], + "react-native-device-brightness": ["setBrightnessLevel"], + "react-native-screen-brightness": ["setBrightness"], + "@capacitor-community/screen-brightness": ["setBrightness"], +}; + +/** @type {import("eslint").Rule.RuleModule} */ +module.exports = { + meta: { + type: "suggestion", + docs: { + description: "Should avoid to override brightness", + category: "eco-design", + recommended: "warn", + }, + messages: { + ShouldAvoidOverrideBrightness: + "Do not force Brightness in your code, unless absolutely necessary", + }, + schema: [], + }, + create: function (context) { + const librariesFoundInImports = []; + + return { + ImportDeclaration(node) { + const currentLibrary = node.source.value; + + if (brightnessLibrariesMethods[currentLibrary]) { + librariesFoundInImports.push(currentLibrary); + } + }, + MemberExpression(node) { + if (librariesFoundInImports.length === 0) { + return; + } + + if ( + librariesFoundInImports.some((library) => + brightnessLibrariesMethods[library].includes(node.property.name), + ) + ) { + context.report({ + node, + messageId: "ShouldAvoidOverrideBrightness", + }); + } + }, + }; + }, +}; diff --git a/eslint-plugin/tests/lib/rules/avoid-brightness-override.js b/eslint-plugin/tests/lib/rules/avoid-brightness-override.js new file mode 100644 index 0000000..eb6cfa5 --- /dev/null +++ b/eslint-plugin/tests/lib/rules/avoid-brightness-override.js @@ -0,0 +1,135 @@ +/* + * ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * 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-brightness-override"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2021, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, +}); +const expectedError = { + messageId: "ShouldAvoidOverrideBrightness", + type: "MemberExpression", +}; + +ruleTester.run("avoid-brightness-override", rule, { + valid: [ + ` + import * as lodash from 'lodash'; + lodash.isEmpty(''); + `, + ` + import { ScreenBrightness } from '@capacitor-community/screen-brightness'; + + // Get the current brightness: + const {brightness: currentBrightness} = ScreenBrightness.getBrightness(); + `, + ` + import DeviceBrightness from 'react-native-device-brightness'; + + DeviceBrightness.getBrightnessLevel() + .then(function (luminous) { + // Get current brightness level + // 0 ~ 1 + console.log(luminous); + }); + `, + ` + import * as Brightness from 'expo-brightness'; + + Brightness.requestPermissionsAsync(); + `, + ` + import ScreenBrightness from 'react-native-screen-brightness'; + + ScreenBrightness.getBrightness().then(brightness => { + console.log('brightness', brightness); + }); + `, + ], + + invalid: [ + { + code: ` + import { ScreenBrightness } from '@capacitor-community/screen-brightness'; + + // Set the brightness: + const brightness = 0.5; + ScreenBrightness.setBrightness({ brightness }); + `, + errors: [expectedError], + }, + { + code: ` + import DeviceBrightness from 'react-native-device-brightness'; + + DeviceBrightness.setBrightnessLevel(0.5); + `, + errors: [expectedError], + }, + { + code: ` + import ScreenBrightness from 'react-native-screen-brightness'; + + ScreenBrightness.setBrightness(0.5); + `, + errors: [expectedError], + }, + { + code: ` + import React, { useEffect } from 'react'; + import { StyleSheet, View, Text } from 'react-native'; + import * as Brightness from 'expo-brightness'; + + export default function App() { + useEffect(() => { + (async () => { + const { status } = await Brightness.requestPermissionsAsync(); + if (status === 'granted') { + Brightness.setSystemBrightnessAsync(1); + } + })(); + }, []); + + return ( + + Brightness Module Example + + ); + } + `, + errors: [expectedError], + }, + ], +}); diff --git a/sonar-plugin/src/main/java/io/ecocode/javascript/CheckList.java b/sonar-plugin/src/main/java/io/ecocode/javascript/CheckList.java index 6b79f22..5230d8b 100644 --- a/sonar-plugin/src/main/java/io/ecocode/javascript/CheckList.java +++ b/sonar-plugin/src/main/java/io/ecocode/javascript/CheckList.java @@ -34,6 +34,7 @@ private CheckList() { public static List> getAllChecks() { return Arrays.asList( + AvoidBrightnessOverride.class, AvoidCSSAnimations.class, AvoidHighAccuracyGeolocation.class, LimitDbQueryResult.class, diff --git a/sonar-plugin/src/main/java/io/ecocode/javascript/checks/AvoidBrightnessOverride.java b/sonar-plugin/src/main/java/io/ecocode/javascript/checks/AvoidBrightnessOverride.java new file mode 100644 index 0000000..19868c6 --- /dev/null +++ b/sonar-plugin/src/main/java/io/ecocode/javascript/checks/AvoidBrightnessOverride.java @@ -0,0 +1,37 @@ +/* + * ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * 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 . + */ +package io.ecocode.javascript.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.javascript.api.EslintBasedCheck; +import org.sonar.plugins.javascript.api.JavaScriptRule; +import org.sonar.plugins.javascript.api.TypeScriptRule; + +@JavaScriptRule +@TypeScriptRule +@Rule(key = AvoidBrightnessOverride.RULE_KEY) +public class AvoidBrightnessOverride implements EslintBasedCheck { + + public static final String RULE_KEY = "EC522"; + + @Override + public String eslintKey() { + return "@ecocode/avoid-brightness-override"; + } + +}