diff --git a/.gitignore b/.gitignore index 16c9e0d..63d4abe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dependency-reduced-pom.xml # Auto-generated files coverage +/node_modules/.yarn-integrity diff --git a/eslint-plugin/.eslintrc.js b/eslint-plugin/.eslintrc.js index 2f053b3..e339826 100644 --- a/eslint-plugin/.eslintrc.js +++ b/eslint-plugin/.eslintrc.js @@ -35,7 +35,7 @@ module.exports = { }, overrides: [ { - files: ["tests/**/*.js"], + files: ["tests/**/*.ts"], env: { mocha: true }, }, ], diff --git a/eslint-plugin/dist/rules.js b/eslint-plugin/dist/rules.js deleted file mode 100644 index 2fcf8d6..0000000 --- a/eslint-plugin/dist/rules.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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 . - */ - -/** - * @fileoverview JavaScript linter of Creedengo project (Sonar mode) - * @author Green Code Initiative - */ -"use strict"; - -const rules = require("../lib/rule-list"); - -module.exports = { - rules: rules.map((rule) => ({ - ruleId: `@creedengo/${rule.ruleName}`, - ruleModule: rule.ruleModule, - ruleConfig: [], - })), -}; diff --git a/eslint-plugin/docs/rules/avoid-brightness-override.md b/eslint-plugin/docs/rules/avoid-brightness-override.md deleted file mode 100644 index 50ba47c..0000000 --- a/eslint-plugin/docs/rules/avoid-brightness-override.md +++ /dev/null @@ -1,36 +0,0 @@ -# Should avoid to override brightness (`@creedengo/avoid-brightness-override`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## 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.setSystemBrightnessAsync(1); })(); // Brightness is forced here - }, []); - return ( - - Brightness Module Example - - ); -} -``` diff --git a/eslint-plugin/docs/rules/avoid-css-animations.md b/eslint-plugin/docs/rules/avoid-css-animations.md deleted file mode 100644 index f1316bc..0000000 --- a/eslint-plugin/docs/rules/avoid-css-animations.md +++ /dev/null @@ -1,41 +0,0 @@ -# Avoid usage of CSS animations (`@creedengo/avoid-css-animations`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -CSS animations, especially complex or continuous animations, can impact the performance of a web page. -They may consume significant browser resources, leading to slower page rendering and decreased user experience, -particularly on devices with limited processing power. - -On mobile devices, constant animations can contribute to increased power consumption, affecting the device's battery -life. -Limiting the usage of CSS animations helps in creating a more energy-efficient and mobile-friendly user experience. - -```jsx -
// Non-compliant -``` - -```jsx -
// Compliant -``` - -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 -functions : `translate`, `rotate` and `scale`. -You can also inform the navigator of upcoming changes with the `will-change` instruction for more optimization. - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_039_en.md) - Avoid JavaScript / - CSS animations - -### Articles & blog posts - -- [web.dev - Animations and performance](https://web.dev/articles/animations-and-performance) -- [web.dev - How to create high-performance CSS animations](https://web.dev/articles/animations-guide) diff --git a/eslint-plugin/docs/rules/avoid-high-accuracy-geolocation.md b/eslint-plugin/docs/rules/avoid-high-accuracy-geolocation.md deleted file mode 100644 index 2a207ea..0000000 --- a/eslint-plugin/docs/rules/avoid-high-accuracy-geolocation.md +++ /dev/null @@ -1,64 +0,0 @@ -# Avoid using high accuracy geolocation in web applications (`@creedengo/avoid-high-accuracy-geolocation`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -High-precision geolocation typically requires more power from the device's GPS hardware. -By requesting less accurate geolocation, you can reduce the power consumption, leading to extended battery life, which -is crucial for mobile devices. - -Obtaining highly accurate geolocation often involves more complex calculations and processing, which can increase CPU -usage. -If the application or service does not critically require pinpoint accuracy, opting for a less accurate geolocation can -help minimize the strain on the device's CPU. - -## Web -```js -var options = { enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }; // Non-compliant -navigator.geolocation.getCurrentPosition( - (pos) => console.log(pos), - (err) => console.warn(err), - options -); -``` - -In these examples, the enableHighAccuracy option is set to false (the default), indicating that the application prefers -lower-accuracy geolocation to conserve resources: - -```js -navigator.geolocation.getCurrentPosition((pos) => console.log(pos)); // Compliant by default -``` - -```js -var options = { enableHighAccuracy: false, timeout: 5000, maximumAge: 0 }; // Compliant -navigator.geolocation.getCurrentPosition( - (pos) => console.log(pos), - (err) => console.warn(err), - options -); -``` - -## React Native -In this example, we ask the user to turn on high accuracy location mode which enables network provider that uses Google Play services to improve location accuracy and location-based services: -```js -import * as Location from 'expo-location'; - -Location.enableNetworkProviderAsync(); // Non-compliant -``` - -Prefer to ask the user to turn on lower-accuracy geolocation to conserve resources: -```js -import * as Location from 'expo-location'; - -Location.requestPermissionsAsync(); // Compliant -``` - -## Resources - -### Documentation - -- [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) - - getCurrentPosition() method diff --git a/eslint-plugin/docs/rules/avoid-keep-awake.md b/eslint-plugin/docs/rules/avoid-keep-awake.md deleted file mode 100644 index 595cd0c..0000000 --- a/eslint-plugin/docs/rules/avoid-keep-awake.md +++ /dev/null @@ -1,40 +0,0 @@ -# Avoid screen keep awake (`@creedengo/avoid-keep-awake`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -To avoid draining the battery, an Android device that is left idle quickly falls asleep. -Hence, keeping the screen on should be avoided, unless it is absolutely necessary. - -> **Note**: This rule currently only supports detecting `expo-keep-awake` package usage. Support for other keep-awake packages may be added in future versions. - -```js -import { useKeepAwake } from "expo-keep-awake"; - -export default function KeepAwakeExample() { - useKeepAwake(); // Non compliant - return ( - - This screen will never sleep! - - ); -} -``` - -```js -import { activateKeepAwake } from "expo-keep-awake"; - -_activate = () => { - activateKeepAwake(); // Non-compliant - alert("Activated!"); -}; -``` - -## Resources - -### Documentation - -- [Expo Docs](https://docs.expo.dev/versions/latest/sdk/keep-awake/) - Expo KeepAwake diff --git a/eslint-plugin/docs/rules/limit-db-query-results.md b/eslint-plugin/docs/rules/limit-db-query-results.md deleted file mode 100644 index 4c1eda5..0000000 --- a/eslint-plugin/docs/rules/limit-db-query-results.md +++ /dev/null @@ -1,54 +0,0 @@ -# Should limit the number of returns for a SQL query (`@creedengo/limit-db-query-results`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -SQL queries often involve processing large amounts of data, and fetching a large number of rows can consume significant -CPU resources. -By limiting the number of rows returned, you reduce the amount of processing that needs to be done by the database -engine, which in turn lowers CPU consumption. - -Transmitting a large number of rows over a network can be resource-intensive. -By restricting the result set size, you reduce the amount of data that needs to be transferred between the database and -the application, improving network efficiency. - -If you store data about customers, you certainly don’t need to retrieve information of all at once, because the larger -the table will be, the more elements the query will return. - -```js -// Non-compliant: Direct SQL query without LIMIT -const mysql = require("mysql2"); -const connection = mysql.createConnection({ host: "localhost", user: "root" }); - -connection.query("SELECT * FROM users", (err, results) => { - if (err) throw err; - console.log(results); -}); -``` - -It may therefore be a good idea to limit the results and use pagination, for example. - -```js -// Compliant: SQL query with LIMIT clause -const mysql = require("mysql2"); -const connection = mysql.createConnection({ host: "localhost", user: "root" }); - -connection.query("SELECT * FROM users LIMIT 10", (err, results) => { - if (err) throw err; - console.log(results); -}); -``` - -## Resources - -### Documentation - -- [MySQL Reference Manual](https://dev.mysql.com/doc/refman/8.0/en/limit-optimization.html) - LIMIT Query Optimization -- [PostgreSQL: Documentation](https://www.postgresql.org/docs/current/queries-limit.html) - LIMIT and OFFSET - -### Articles & blog posts - -- [Query Performance Optimization](https://www.oreilly.com/library/view/high-performance-mysql/9780596101718/ch04.html) diff --git a/eslint-plugin/docs/rules/no-empty-image-src-attribute.md b/eslint-plugin/docs/rules/no-empty-image-src-attribute.md deleted file mode 100644 index fa7c527..0000000 --- a/eslint-plugin/docs/rules/no-empty-image-src-attribute.md +++ /dev/null @@ -1,53 +0,0 @@ -# Disallow usage of image with empty source attribute (`@creedengo/no-empty-image-src-attribute`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -When the src attribute is missing, some browsers may still attempt to make a request to the current URL (the base URL of -the document) in an attempt to fetch the image. -This can lead to unnecessary server requests, impacting performance and potentially causing errors. - -Proper use of the src attribute is essential for web accessibility. -Screen readers and other assistive technologies rely on valid image sources to provide meaningful information to users -with disabilities. -A missing src attribute can result in confusion and hinder accessibility. - -```jsx -return ( - <> - // Non-compliant - // Non-compliant - -) -``` - -The HTML specification requires the src attribute for the element, and not including it may lead to non-compliance -with standards. - -```jsx -import myLogo from "./logo.svg" - -return ( - <> - // Compliant - // Compliant - -) -``` - -This rule is build for [React](https://react.dev/) and JSX. - -## Resources - -### Documentation - -- [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML) - - Images in HTML - -### Articles & blog posts - -- [Empty image src can destroy your site](https://humanwhocodes.com/blog/2009/11/30/empty-image-src-can-destroy-your-site/) - diff --git a/eslint-plugin/docs/rules/no-import-all-from-library.md b/eslint-plugin/docs/rules/no-import-all-from-library.md deleted file mode 100644 index c864338..0000000 --- a/eslint-plugin/docs/rules/no-import-all-from-library.md +++ /dev/null @@ -1,101 +0,0 @@ -# Should not import all from library (`@creedengo/no-import-all-from-library`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -Including only the required modules decreases the overall size of the program. -This, in turn, reduces the amount of memory and storage space needed to run and store the application. -This is especially critical in environments with limited resources, such as on mobile devices or in web applications -where bandwidth and download times matter. - -Smaller programs generally have better runtime performance. -Reducing the number of unnecessary modules minimizes the amount of code that needs to be interpreted or compiled, -leading to faster execution and improved overall performance. - -## Options - - - -| Name | Type | -| :------------------------------------- | :------- | -| `importByNamespaceNotAllowedLibraries` | String[] | -| `notAllowedLibraries` | String[] | - - - -You can externally add your own libraries to be checked. -To add your own libraries you need to modify your .eslintrc.js by adding the following rule configuration: - -```js -module.exports = { - ...yourConf, - rules: { - "no-import-all-from-library": [ - "warn", - { - notAllowedLibraries: ["some-lib"], // will check for -> import someLib from "some-lib" - importByNamespaceNotAllowedLibraries: ["some-other-lib"], // will check for -> import * as someOtherLib from "some-other-lib" - }, - ], - }, -}; -``` - -## Examples - -**Example with the well-known [lodash](https://lodash.com/) library, if you only need -`isEmpty` method.** - -```js -// Example with lodash -import lodash from "lodash"; -import { isEmpty } from "lodash"; -import * as lodash from "lodash"; - -// Example with underscore -import _ from "underscore"; -``` - -**Size of your bundle, if you use the whole lodash library:** - -- **index.js - 531.46 KB** - - node_modules/lodash - 531.35 KB - - lodash.js - 531.35 KB - - index.js - 112 B - ---- - -Examples of **compliant** code for this rule: - -```js -// Example with lodash (uses submodules) -import isEmpty from "lodash/isEmpty"; -import intersect from "lodash/intersect"; - -// Example with underscore (uses esm modules) -import map from "underscore/modules/map.js"; -``` - -Size of your bundle, if you use only the "isEmpty" method: - -- **index.js - 24.42 KB** - - node_modules/lodash - 24.31 KB - - isEmpty - 1.95 KB - - \_nodeUtil.js - 995 B - - isArrayLike.js - 830 B - - ... - - index.js - 110 B - -## Resources - -### Documentation - -- [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) - - import - -### Articles & blog posts - -- [Importing modules in JavaScript, are we doing it right?](https://dev.to/dianjuar/importing-modules-in-javascript-are-we-doing-it-right-nc) diff --git a/eslint-plugin/docs/rules/no-multiple-access-dom-element.md b/eslint-plugin/docs/rules/no-multiple-access-dom-element.md deleted file mode 100644 index 6f1d2e9..0000000 --- a/eslint-plugin/docs/rules/no-multiple-access-dom-element.md +++ /dev/null @@ -1,43 +0,0 @@ -# Disallow multiple access of same DOM element (`@creedengo/no-multiple-access-dom-element`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -Accessing the Document Object Model (DOM) is a relatively expensive operation in terms of performance. -Each time you access the DOM, the browser needs to traverse the document tree to find the requested element. -By assigning the DOM object to a variable when accessed multiple times, you avoid redundant traversals, leading to -improved performance. - -Assigning the DOM object to a variable not only improves performance but also enhances code readability. -It makes the code more concise and self-explanatory. -Developers reading the code can understand that the variable holds a reference to a specific DOM element, and its -subsequent use is likely for multiple operations. - -Here's an example in JavaScript to illustrate this rule: - -```js -const width = document.getElementById('block').clientWidth; -const height = document.getElementById('block').clientHeight; // Non-compliant -``` - -```js -const blockElement = document.getElementById('block'); // Compliant -const width = blockElement.clientWidth; -const height = blockElement.clientHeight; -``` - -In the first example, getElementById is called twice, potentially resulting in two separate traversals of the DOM tree. -In the second example, the DOM element reference is cached in the `blockElement` variable, and subsequent property -accesses use this cached reference. - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_054_en.md) - Reduce DOM access - via JavaScript -- [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Learn/Performance/JavaScript#tips_for_writing_more_efficient_code) - - Tips for writing more efficient code diff --git a/eslint-plugin/docs/rules/no-multiple-style-changes.md b/eslint-plugin/docs/rules/no-multiple-style-changes.md deleted file mode 100644 index 69b40bb..0000000 --- a/eslint-plugin/docs/rules/no-multiple-style-changes.md +++ /dev/null @@ -1,49 +0,0 @@ -# Disallow multiple style changes at once (`@creedengo/no-multiple-style-changes`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -Browsers optimize rendering performance by batching and combining similar operations. -However, when making multiple CSS changes at once, it can disrupt the browser's optimization mechanisms. -Applying changes individually allows the browser to better optimize the rendering process. - -Making multiple CSS changes in a single batch can trigger multiple reflows and repaints in the browser. -Reflows and repaints are resource-intensive operations that can lead to performance issues. -Applying changes individually minimizes the number of reflows and repaints, improving overall page performance. - -Here's an example in JavaScript and CSS to illustrate this rule: - -```html - -``` - -```html - - - -``` - -In the first example, multiple CSS properties are set in a single batch, while in the second example, changes are -applied through a CSS class. - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_045_en.md) - Modify several CSS - properties at once diff --git a/eslint-plugin/docs/rules/no-torch.md b/eslint-plugin/docs/rules/no-torch.md deleted file mode 100644 index c27a51e..0000000 --- a/eslint-plugin/docs/rules/no-torch.md +++ /dev/null @@ -1,24 +0,0 @@ -# Should not programmatically enable torch mode (`@creedengo/no-torch`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - -## Why is this an issue? - -As a developer, you should avoid programmatically enabling torch mode. - -The flashlight can significantly drain the device's battery. If it is turned on without the user's knowledge, it could lead to unwanted battery consumption. - -```js -import Torch from 'react-native-torch'; // Not-compliant -``` - -```js -import axios from 'axios'; // Compliant -``` - -## Resources - -### Documentation - -- [CNUMR best practices mobile](https://github.com/cnumr/best-practices-mobile) - Torch free \ No newline at end of file diff --git a/eslint-plugin/docs/rules/prefer-collections-with-pagination.md b/eslint-plugin/docs/rules/prefer-collections-with-pagination.md deleted file mode 100644 index b46dd9b..0000000 --- a/eslint-plugin/docs/rules/prefer-collections-with-pagination.md +++ /dev/null @@ -1,57 +0,0 @@ -# Prefer API collections with pagination (`@creedengo/prefer-collections-with-pagination`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -Pagination helps in optimizing the performance of API requests, especially when dealing with large datasets. -Instead of retrieving the entire dataset in a single request, pagination allows fetching a smaller subset of data, -reducing the response time and resource usage. - -Fetching only the necessary data reduces the amount of data transmitted over the network. -This is particularly important for users on limited bandwidth or mobile devices. -Pagination ensures that only the relevant data is transferred, conserving bandwidth and improving overall network -efficiency. - -This rule is built for the [NestJS framework](https://nestjs.com) but can work with a controller `@Controller()` and a -decorated method `@Get()`. - -```ts -@Controller() -class Test { - @Get() - public find(): Promise { - } // Non-compliant -} -``` - -```ts -interface Pagination { - items: string[]; - currentPage: number; - totalPages: number; -} - -@Controller() -class Test { - @Get() - public find(): Promise { - } // Compliant -} -``` - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_076_en.md) - Avoid transferring - large amounts of data for processing tasks -- [nestjs-paginate](https://github.com/ppetzold/nestjs-paginate) - Pagination and filtering helper method using Nest.js - framework - -### Articles & blog posts - -- [How to Optimize the API Response Package](https://nordicapis.com/optimizing-the-api-response-package/) -- [Unlocking the Power of API Pagination: Best Practices and Strategies](https://dev.to/pragativerma18/unlocking-the-power-of-api-pagination-best-practices-and-strategies-4b49) diff --git a/eslint-plugin/docs/rules/prefer-lighter-formats-for-image-files.md b/eslint-plugin/docs/rules/prefer-lighter-formats-for-image-files.md deleted file mode 100644 index d9cf796..0000000 --- a/eslint-plugin/docs/rules/prefer-lighter-formats-for-image-files.md +++ /dev/null @@ -1,88 +0,0 @@ -# Prefer lighter formats for image files (`@creedengo/prefer-lighter-formats-for-image-files`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -Using appropriate image formats and optimizing image sizes is essential for improving website performance, user experience, and overall environmental impact. -Larger image file sizes consume more bandwidth, increasing the data transfer required to load a web page. -Some image formats are generally considered better for eco-friendly web design and should be used in most cases. - -We recommend using the following formats: - -- **WebP**, developed by Google, is a modern image format that provides high compression efficiency without significant loss of quality. -- **AVIF** (AV1 Image File Format) is a relatively new and highly efficient image format that is based on the AV1 video codec. -- **SVG** (Scalable Vector Graphics) is a vector image format that is based on XML. - Files are lightweight and can be scaled without loss of quality. - -```html -Unoptimized image of a cat // -Non-compliant - -Optimized image of a cat // -Compliant -``` - -Remember that the best image format may vary depending on the specific use case, content, and requirements of your website. -Always test and evaluate the performance of different formats to find the optimal balance between image quality and file size. - -### Picture - -Images often represent most of the downloaded bytes, right after videos and just before CSS and JavaScript libraries. -Optimizing images is important to reduce used bandwidth. The first step is to choose the ideal format for your -display needs. - -Raster images should be reserved for photos and interface elements that cannot be displayed with icons or CSS styles. - -The appropriate format depends on the image properties : black & white or color, color palette, need for transparency... -Among these properties, the possibility to irremediably alter images quality (lossy compression) tends to favor formats such as JPEG, JPEG XL, -AVIF, or WebP, while needing transparency and/or the impossibility to alter the image quality (lossless compression) will tend to favor -PNG or WebP lossless formats (which supports transparency). - -Format importantly impacts images size: on average, .webp images will be 30% lighter than .jpeg -images or .png images. .avif images can be up to 20% lighter than .webp image and 50% lighter than .jepg images. - -Don't forget to pay attention to browser support. .webp images will not be recognized by -old browsers and will not be displayed. It is possible to provide several formats for the same image -to overcome this issue. Some server-side modules (such as Google's modPageSpeed, also available for Apache -and Nginx) even allow you to provide the appropriate image for the browser that is calling the server. - -Many tools will help you minimize images size: - -- SQUOOSH -- CLOUDINARY -- ImageMagick -- PngCrush -- JpegTran - -### Example - -In this example, the DOM element informs the browser that there are two images: a .webp image and a -.jpg image, which is used by default. The browser will decide which image will be downloaded. If the .webp format -is supported, the image.webp image will be downloaded; otherwise, image.jpg image will be downloaded. - -```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. - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_080_en.md) - Optimize images -- [WSG UX15-2](https://w3c.github.io/sustyweb/star.html#UX15-2) - Optimizing All Image Assets for a Variety of Different Resolutions -- [RGESN 5.1](https://ecoresponsable.numerique.gouv.fr/publications/referentiel-general-ecoconception/critere/5.1/) - Référentiel général d'écoconception de services numériques 🇫🇷 - -### Articles & blog posts - -- [greenspector.com - Which image format choose to reduce energy consumption and environmental impact?](https://greenspector.com/en/which-image-format-to-choose-to-reduce-its-energy-consumption-and-its-environmental-impact/) -- [dodonut.com - The Most Efficient Web Image Formats. Use Cases For Different Types Of Images.](https://dodonut.com/blog/use-cases-of-web-image-formats/) diff --git a/eslint-plugin/docs/rules/avoid-autoplay.md b/eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md similarity index 95% rename from eslint-plugin/docs/rules/avoid-autoplay.md rename to eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md index 23a3ecc..3e09f31 100644 --- a/eslint-plugin/docs/rules/avoid-autoplay.md +++ b/eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md @@ -1,4 +1,4 @@ -# Avoid autoplay for videos and audio content (`@creedengo/avoid-autoplay`) +# Prefer on push component change detection (`@creedengo/prefer-on-push-component-change-detection`) ⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. diff --git a/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md b/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md deleted file mode 100644 index 8846090..0000000 --- a/eslint-plugin/docs/rules/prefer-shorthand-css-notations.md +++ /dev/null @@ -1,72 +0,0 @@ -# Encourage usage of shorthand CSS notations (`@creedengo/prefer-shorthand-css-notations`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -By employing a shorthand property, you can create stylesheets that are more succinct and frequently easier to read, -ultimately conserving time and energy. -By reducing the number of CSS properties, you help to reduce the weight of your application bundle, and therefore its -environmental footprint. - -## Options - -You can disable specific properties from being scanned if it does not match your needs. -To disable properties you need to modify your .eslintrc.js by adding the following rule configuration: - -```js -module.exports = { - ...yourConf, - rules: { - "prefer-shorthand-css-notations": [ - "warn", - // disable analyze of "animation-*" properties - { disableProperties: ["animation"] }, - ], - }, -}; -``` - -## Examples - -For example, the `font` shorthand consolidates various font-related properties, and the `margin` shorthand streamlines -the definition of margins around a box. - -```jsx -
- {/* Noncompliant: these properties can be grouped together in the "margin" property */} -
; -``` - -```jsx -
- {/* Compliant usage of shorthand property */} -
-``` - -This optimization can't always be done, depending on the properties you're using. -For example, if you only want to set the left margin, you must continue to use `margin-left`. - -```jsx -
- {/* Compliant because we only want a left margin */} -
-``` - -This optimization works for a number of -properties [listed here](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties#see_also). - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/fc5a1f865bafb196e4775cce8835393751d40ed8/chapters/BP_026_en.md) - - Use abbreviated CSS notations -- [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) - - Shorthand properties - -### Articles & blog posts - -- [6 CSS Shorthand properties to improve your web application](https://dev.to/cscarpitta/6-css-shorthand-properties-to-improve-your-web-application-2dbj) diff --git a/eslint-plugin/docs/rules/provide-print-css.md b/eslint-plugin/docs/rules/provide-print-css.md deleted file mode 100644 index 691a99e..0000000 --- a/eslint-plugin/docs/rules/provide-print-css.md +++ /dev/null @@ -1,56 +0,0 @@ -# Enforce providing a print stylesheet (`@creedengo/provide-print-css`) - -⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`. - - - -## Why is this an issue? - -CSS offers a set of styles specifically designed for printing. -By using CSS print styles, you can control how the content of a web page is presented when users decide to print it. -This includes adjusting font sizes, hiding unnecessary elements, and optimizing the layout to fit well on printed pages. - -Limiting the number of printed pages helps in reducing paper and ink consumption. -By optimizing the print layout, you can ensure that only essential content is printed, saving resources and contributing -to environmental sustainability. - -```html - - Web Page - - -``` - -In your HTML file, you can include a link to the print stylesheet inside the section. -Use the media attribute with a value of "print" to indicate that this stylesheet is specifically for print. - -```html - - Web Page - - -``` - -You can also use the @media print rule to define styles that should be applied when the page is printed. -Adjust font sizes, hide unnecessary elements, or make any other modifications suitable for print. - -```html - - Web Page - - -``` - -## Resources - -### Documentation - -- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_027_en.md) - Provide a print CSS - -### Articles & blog posts - -- [How to Create Printer-friendly Pages with CSS](https://www.sitepoint.com/css-printer-friendly-pages/) diff --git a/eslint-plugin/lib/rule-list.js b/eslint-plugin/lib/rule-list.ts similarity index 86% rename from eslint-plugin/lib/rule-list.js rename to eslint-plugin/lib/rule-list.ts index b0cac44..5d7df93 100644 --- a/eslint-plugin/lib/rule-list.js +++ b/eslint-plugin/lib/rule-list.ts @@ -16,21 +16,24 @@ * along with this program. If not, see . */ -"use strict"; +export type Rule = { + ruleName: string, + ruleModule: RuleModule +} -const fs = require("fs"); -const path = require("path"); +export type RuleModule = any; -const rules = []; +import * as fs from 'fs'; +import * as path from 'path'; + +export const rules: Rule[] = []; const rulesDirectory = path.resolve(__dirname, "rules"); fs.readdirSync(rulesDirectory).forEach((file) => { const ruleName = path.parse(file).name; const ruleModule = require(path.join(rulesDirectory, ruleName)); - if (ruleModule != null) { rules.push({ ruleName, ruleModule }); } }); -module.exports = rules; diff --git a/eslint-plugin/lib/rules/avoid-autoplay.js b/eslint-plugin/lib/rules/avoid-autoplay.js deleted file mode 100644 index 350d319..0000000 --- a/eslint-plugin/lib/rules/avoid-autoplay.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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 autoplay for videos and audio content", - category: "eco-design", - recommended: "warn", - }, - messages: { - NoAutoplay: "Avoid autoplay for video and audio elements.", - EnforcePreloadNone: "Set preload='none' for video and audio elements.", - NoAutoplayAndEnforcePreloadNone: - "Avoid autoplay and set preload='none' for video and audio elements.", - }, - schema: [], - }, - create(context) { - return { - JSXOpeningElement(node) { - if (node.name.name === "video" || node.name.name === "audio") { - const autoplayAttr = node.attributes.find( - (attr) => attr.name?.name.toLowerCase() === "autoplay", - ); - const preloadAttr = node.attributes.find( - (attr) => attr.name?.name.toLowerCase() === "preload", - ); - if ( - autoplayAttr && - (!preloadAttr || preloadAttr.value.value !== "none") - ) { - context.report({ - node: autoplayAttr || preloadAttr, - messageId: "NoAutoplayAndEnforcePreloadNone", - }); - } else { - if (autoplayAttr) { - context.report({ - node: autoplayAttr, - messageId: "NoAutoplay", - }); - } - - if (!preloadAttr || preloadAttr.value.value !== "none") { - context.report({ - node: preloadAttr || node, - messageId: "EnforcePreloadNone", - }); - } - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/avoid-brightness-override.js b/eslint-plugin/lib/rules/avoid-brightness-override.js deleted file mode 100644 index 7f84517..0000000 --- a/eslint-plugin/lib/rules/avoid-brightness-override.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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/lib/rules/avoid-css-animations.js b/eslint-plugin/lib/rules/avoid-css-animations.js deleted file mode 100644 index f33de5e..0000000 --- a/eslint-plugin/lib/rules/avoid-css-animations.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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?.value.expression?.properties) { - // 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/lib/rules/avoid-high-accuracy-geolocation.js b/eslint-plugin/lib/rules/avoid-high-accuracy-geolocation.js deleted file mode 100644 index a6b1123..0000000 --- a/eslint-plugin/lib/rules/avoid-high-accuracy-geolocation.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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 geolocationLibrariesMethods = { - "expo-location": ["enableNetworkProviderAsync"], -}; - -/** @type {import("eslint").Rule.RuleModule} */ -module.exports = { - meta: { - type: "suggestion", - docs: { - description: "Avoid using high accuracy geolocation in web applications", - category: "eco-design", - recommended: "warn", - }, - messages: { - AvoidUsingAccurateGeolocation: "Avoid using high accuracy geolocation", - }, - schema: [], - }, - create: function (context) { - const librariesFoundInImports = []; - - return { - ImportDeclaration(node) { - const currentLibrary = node.source.value; - - if (geolocationLibrariesMethods[currentLibrary]) { - librariesFoundInImports.push(currentLibrary); - } - }, - MemberExpression(node) { - if (librariesFoundInImports.length === 0) { - return; - } - - if ( - librariesFoundInImports.some((library) => - geolocationLibrariesMethods[library].includes(node.property.name), - ) - ) { - reportAvoidUsingAccurateGeolocation(context, node); - } - }, - Property(node) { - if ( - node?.key.name === "enableHighAccuracy" && - node?.value.value === true - ) { - reportAvoidUsingAccurateGeolocation(context, node); - } - }, - }; - }, -}; - -function reportAvoidUsingAccurateGeolocation(context, node) { - context.report({ node, messageId: "AvoidUsingAccurateGeolocation" }); -} diff --git a/eslint-plugin/lib/rules/avoid-keep-awake.js b/eslint-plugin/lib/rules/avoid-keep-awake.js deleted file mode 100644 index 06f3737..0000000 --- a/eslint-plugin/lib/rules/avoid-keep-awake.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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 keepAwakeLibrariesMethods = { - "expo-keep-awake": ["activateKeepAwake", "useKeepAwake"], -}; - -/** @type {import("eslint").Rule.RuleModule} */ -module.exports = { - meta: { - type: "suggestion", - docs: { - description: "Avoid screen keep awake", - category: "eco-design", - recommended: "warn", - }, - messages: { - AvoidKeepAwake: "Avoid screen keep awake", - }, - schema: [], - }, - create: function (context) { - const librariesFoundInImports = []; - - return { - ImportDeclaration(node) { - const currentLibrary = node.source.value; - - if (keepAwakeLibrariesMethods[currentLibrary]) { - librariesFoundInImports.push(currentLibrary); - } - }, - CallExpression(node) { - if (librariesFoundInImports.length === 0) { - return; - } - - if ( - librariesFoundInImports.some((library) => - keepAwakeLibrariesMethods[library].includes(node.callee.name), - ) - ) { - context.report({ node, messageId: "AvoidKeepAwake" }); - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/limit-db-query-results.js b/eslint-plugin/lib/rules/limit-db-query-results.js deleted file mode 100644 index e20e059..0000000 --- a/eslint-plugin/lib/rules/limit-db-query-results.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Should limit the number of returns for a SQL query", - category: "eco-design", - recommended: "warn", - }, - messages: { - LimitTheNumberOfReturns: - "Try and limit the number of data returned for a single query (by using the LIMIT keyword for example)", - }, - schema: [], - }, - create(context) { - //list of limiting clauses to check against - const limitingClauses = [ - "LIMIT", - "TOP", - "ROW_NUMBER", - "FETCH FIRST", - "WHERE", - ]; - - // List of known SQL client methods or functions - const sqlClientMethods = ["query", "execute", "run"]; - - return { - // Detect SQL queries in string literals - Literal: function (node) { - if (typeof node.value == "string") { - const query = node.value.toUpperCase(); - if ( - query.includes("SELECT") && - query.includes("FROM") && - !limitingClauses.some((clause) => query.includes(clause)) - ) { - // Check if the query is used within a SQL client - const parent = node.parent; - - if ( - parent?.type === "CallExpression" && - parent.callee.type === "MemberExpression" && - sqlClientMethods.includes(parent.callee.property.name) - ) { - context.report({ node, messageId: "LimitTheNumberOfReturns" }); - } - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/no-empty-image-src-attribute.js b/eslint-plugin/lib/rules/no-empty-image-src-attribute.js deleted file mode 100644 index ed0d148..0000000 --- a/eslint-plugin/lib/rules/no-empty-image-src-attribute.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Disallow usage of image with empty source attribute", - category: "eco-design", - recommended: "warn", - }, - messages: { - SpecifySrcAttribute: - "Make sure to specify src attribute when using .", - }, - schema: [], - }, - create(context) { - return { - JSXOpeningElement(node) { - if (node.name.name === "img") { - const srcValue = node.attributes.find( - (attr) => attr.name.name === "src", - ); - if (srcValue?.value?.value === "") { - //to prevent Empty image - context.report({ - node: srcValue, - messageId: "SpecifySrcAttribute", - }); - } else if (!srcValue) { - //to prevent - context.report({ - node, - messageId: "SpecifySrcAttribute", - }); - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/no-import-all-from-library.js b/eslint-plugin/lib/rules/no-import-all-from-library.js deleted file mode 100644 index 35af099..0000000 --- a/eslint-plugin/lib/rules/no-import-all-from-library.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Should not import all from library", - category: "eco-design", - recommended: "warn", - }, - messages: { - ShouldNotImportAllFromLibrary: - "You should not import all from library {{library}}", - }, - schema: [ - { - type: "object", - properties: { - notAllowedLibraries: { - type: "array", - items: { - type: "string", - }, - }, - importByNamespaceNotAllowedLibraries: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - ], - }, - create: function (context) { - const notAllowedLibraries = ["lodash", "underscore"]; - const importByNamespaceNotAllowedLibraries = ["lodash-es"]; - - if (context.options?.length > 0) { - const option = context.options[0]; - - if (option.notAllowedLibraries) { - notAllowedLibraries.push(...option.notAllowedLibraries); - } - - if (option.importByNamespaceNotAllowedLibraries) { - notAllowedLibraries.push( - ...option.importByNamespaceNotAllowedLibraries, - ); - } - } - - return { - ImportDeclaration(node) { - const currentLibrary = node.source.value; - - const forbiddenByName = notAllowedLibraries.includes(currentLibrary); - const forbiddenByNamespace = - importByNamespaceNotAllowedLibraries.includes(currentLibrary) && - node.specifiers.some( - (specifier) => specifier.type === "ImportNamespaceSpecifier", - ); - - if (forbiddenByName || forbiddenByNamespace) { - context.report({ - node, - messageId: "ShouldNotImportAllFromLibrary", - data: { library: currentLibrary }, - }); - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/no-multiple-access-dom-element.js b/eslint-plugin/lib/rules/no-multiple-access-dom-element.js deleted file mode 100644 index 93e4d68..0000000 --- a/eslint-plugin/lib/rules/no-multiple-access-dom-element.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Disallow multiple access of same DOM element", - category: "eco-design", - recommended: "warn", - }, - messages: { - ShouldBeAssignToVariable: - "'{{selector}}' selector is already used. Assign the result in a variable.", - }, - schema: [], - }, - create: function (context) { - const map = {}; - const DOMAccessMethods = [ - "getElementById", - "getElementsByTagName", - "getElementsByClassName", - "getElementsByName", - "querySelector", - "querySelectorAll", - ]; - - return { - CallExpression(node) { - if ( - node.callee.object?.name === "document" && - DOMAccessMethods.includes(node.callee.property.name) && - // We only accept string literals as arguments for now - node.arguments[0].type === "Literal" - ) { - const selectorValue = node.arguments[0].value; - const uniqueCallStr = node.callee.property.name + selectorValue; - // todo: legacy support of context#getScope for eslint v7 - const scope = - context.sourceCode?.getScope(node) ?? context.getScope(); - - if (map[uniqueCallStr] === scope) { - context.report({ - node, - messageId: "ShouldBeAssignToVariable", - data: { selector: selectorValue }, - }); - } else { - map[uniqueCallStr] = scope; - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/no-multiple-style-changes.js b/eslint-plugin/lib/rules/no-multiple-style-changes.js deleted file mode 100644 index 022a818..0000000 --- a/eslint-plugin/lib/rules/no-multiple-style-changes.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Disallow multiple style changes at once", - category: "eco-design", - recommended: "warn", - }, - messages: { - UseClassInstead: - "There are more than two style assignments for '{{elementName}}'. Use a class instead.", - }, - schema: [], - }, - create: function (context) { - const isNodeUseStyleProperty = (node) => - node?.object?.property?.name === "style"; - - const getNodeFullName = (node) => { - let names = []; - do { - names.unshift(node.name ?? node.property?.name); - node = node.object; - } while (node); - return names.join("."); - }; - - return { - AssignmentExpression(node) { - // Check if there is a literal assignation on a style property - if ( - node.right.type === "Literal" && - isNodeUseStyleProperty(node.left) - ) { - const domElementName = getNodeFullName(node.left.object.object); - const currentRangestart = node.left.object.object.range[0]; - - /** - * Store parent AST to check if there is more - * than one assignation on the style of the same domElement - */ - // todo: legacy support of context#getScope for eslint v7 - const scope = - context.sourceCode?.getScope(node) ?? context.getScope(); - const currentScopeASTBody = - scope.block.body.length != null - ? scope.block.body - : scope.block.body.body; - - const filtered = currentScopeASTBody.filter( - (e) => - e.type === "ExpressionStatement" && - e.expression.type === "AssignmentExpression" && - isNodeUseStyleProperty(e.expression.left) && - getNodeFullName(e.expression.left.object.object) === - domElementName, - ); - - // De-duplication, prevents multiple alerts for each line involved - const isCurrentNodeTheFirstAssignation = - filtered.length > 1 && - currentRangestart <= - filtered[0].expression.left.object.object.range[0]; - - if (isCurrentNodeTheFirstAssignation) { - context.report({ - node, - messageId: "UseClassInstead", - data: { elementName: domElementName }, - }); - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/no-torch.js b/eslint-plugin/lib/rules/no-torch.js deleted file mode 100644 index 65d924e..0000000 --- a/eslint-plugin/lib/rules/no-torch.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Should not programmatically enable torch mode", - category: "eco-design", - recommended: "warn", - }, - messages: { - ShouldNotProgrammaticallyEnablingTorchMode: - "You should not programmatically enable torch mode", - }, - schema: [], - }, - create: function (context) { - const reactNativeTorchLibrary = "react-native-torch"; - - return { - ImportDeclaration(node) { - const currentLibrary = node.source.value; - if (currentLibrary === reactNativeTorchLibrary) { - context.report({ - node, - messageId: "ShouldNotProgrammaticallyEnablingTorchMode", - }); - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/prefer-collections-with-pagination.js b/eslint-plugin/lib/rules/prefer-collections-with-pagination.js deleted file mode 100644 index 1aa8647..0000000 --- a/eslint-plugin/lib/rules/prefer-collections-with-pagination.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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 PAGINATION_KEY_WORDS = ["page", "pagination", "paginated"]; - -const isPaginationName = (name) => { - return PAGINATION_KEY_WORDS.some((keyWord) => - name.toLowerCase().includes(keyWord), - ); -}; - -const isPaginated = (objectType) => { - if (objectType.type === "TSTypeReference") { - // Pagination object should be an object, for example, it can't be an array - - if (isPaginationName(objectType.typeName.name)) { - return true; - } - } else if (objectType.type === "TSTypeLiteral") { - if ( - objectType.members?.some( - (member) => member.key != null && isPaginationName(member.key.name), - ) - ) { - return true; - } - } - - return false; -}; - -const isNestGetDecorator = (decorator) => - decorator.expression.callee.name.toLowerCase() === "get" && - (decorator.expression.arguments.length === 0 || - !decorator.expression.arguments[0].value.includes(":")); - -const isInNestControllerClass = (decorator) => - decorator.parent.parent.parent.type === "ClassDeclaration" && - decorator.parent.parent.parent.decorators.find( - (decorator) => - decorator.expression.callee.name.toLowerCase() === "controller", - ); - -const report = (context, node) => - context.report({ node, messageId: "PreferReturnCollectionsWithPagination" }); - -// Type: RuleModule<"uppercase", ...> -module.exports = { - name: "prefer-collections-with-pagination", - meta: { - docs: { - description: "Prefer API collections with pagination", - category: "eco-design", - recommended: "warn", - }, - messages: { - PreferReturnCollectionsWithPagination: - "Prefer return collections with pagination in HTTP GET", - }, - type: "suggestion", - schema: [], - }, - defaultOptions: [], - create(context) { - return { - Decorator(node) { - if (isNestGetDecorator(node) && isInNestControllerClass(node)) { - const getMethod = node.parent; - const returnType = getMethod.value.returnType?.typeAnnotation; - - if (returnType != null) { - if ( - returnType.type === "TSTypeReference" && - returnType.typeName.name === "Promise" - ) { - // todo: legacy support of typeParameters for eslint v7 - const params = ( - returnType.typeArguments ?? returnType.typeParameters - ).params; - - if (params.length === 1 && !isPaginated(params[0])) { - report(context, returnType); - } - } else if ( - returnType.type === "TSArrayType" || - !isPaginated(returnType) - ) { - report(context, returnType); - } - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/prefer-lighter-formats-for-image-files.js b/eslint-plugin/lib/rules/prefer-lighter-formats-for-image-files.js deleted file mode 100644 index f3c9c85..0000000 --- a/eslint-plugin/lib/rules/prefer-lighter-formats-for-image-files.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Prefer lighter formats for image files", - category: "eco-design", - recommended: "warn", - }, - messages: { - PreferLighterFormatsForImageFiles: - "You should use lighter formats for image files such as {{ eligibleExtensions }}", - }, - schema: [], - }, - create(context) { - const eligibleExtensions = ["webp", "avif", "svg", "jxl"]; - - return { - JSXOpeningElement(node) { - const tagName = node.name.name; - if (tagName?.toLowerCase() !== "img") return; - - const parentTagName = node.parent?.parent?.openingElement?.name?.name; - if (parentTagName?.toLowerCase() === "picture") return; - - const srcAttribut = node.attributes.find( - (attr) => attr.name.name === "src", - ); - - let srcValue = srcAttribut?.value?.value; - - if (!srcValue) return; - - srcValue = srcValue.substring(srcValue.lastIndexOf("/") + 1); - const dotIndex = srcValue.lastIndexOf("."); - - if (dotIndex === -1) return; - - const imgExtension = srcValue.substring(dotIndex + 1); - - if (eligibleExtensions.includes(imgExtension.toLowerCase())) return; - - context.report({ - node, - messageId: "PreferLighterFormatsForImageFiles", - data: { eligibleExtensions: eligibleExtensions.join(", ") }, - }); - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/prefer-on-push-component-change-detection.ts b/eslint-plugin/lib/rules/prefer-on-push-component-change-detection.ts new file mode 100644 index 0000000..b96d8ef --- /dev/null +++ b/eslint-plugin/lib/rules/prefer-on-push-component-change-detection.ts @@ -0,0 +1,99 @@ +import { + ASTUtils, + isNotNullOrUndefined, + RuleFixes, + Selectors, +} from '@angular-eslint/utils'; +import type { TSESTree } from '@typescript-eslint/utils'; + +export type Options = []; +export type MessageIds = + | 'preferOnPushComponentChangeDetection' + | 'suggestAddChangeDetectionOnPush'; +export const RULE_NAME = 'prefer-on-push-component-change-detection'; + +const METADATA_PROPERTY_NAME = 'changeDetection'; +const STRATEGY_ON_PUSH = 'ChangeDetectionStrategy.OnPush'; + +export default { + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + description: `Ensures component's \`${METADATA_PROPERTY_NAME}\` is set to \`${STRATEGY_ON_PUSH}\``, + category: 'eco-design', + recommended: 'warn' + }, + schema: [], + messages: { + PreferOnPushComponentChangeDetection: `The component's \`${METADATA_PROPERTY_NAME}\` value should be set to \`${STRATEGY_ON_PUSH}\``, + }, + }, + defaultOptions: [], + create(context: any) { + const changeDetectionMetadataProperty = Selectors.metadataProperty( + METADATA_PROPERTY_NAME, + ); + const withoutChangeDetectionDecorator = + `${Selectors.COMPONENT_CLASS_DECORATOR}:matches([expression.arguments.length=0], [expression.arguments.0.type='ObjectExpression']:not(:has(${changeDetectionMetadataProperty})))` as const; + const nonChangeDetectionOnPushProperty = + `${Selectors.COMPONENT_CLASS_DECORATOR} > CallExpression > ObjectExpression > ${changeDetectionMetadataProperty}:matches([value.type='Identifier'][value.name='undefined'], [value.object.name='ChangeDetectionStrategy'][value.property.name!='OnPush'])` as const; + const selectors = [ + withoutChangeDetectionDecorator, + nonChangeDetectionOnPushProperty, + ].join(','); + + return { + [selectors](node: TSESTree.Decorator | TSESTree.Property) { + context.report({ + node: nodeToReport(node), + messageId: 'preferOnPushComponentChangeDetection', + suggest: [ + { + messageId: 'suggestAddChangeDetectionOnPush', + fix: (fixer:any) => { + if (ASTUtils.isProperty(node)) { + return [ + RuleFixes.getImportAddFix({ + fixer, + importName: 'ChangeDetectionStrategy', + moduleName: '@angular/core', + node: node.parent.parent.parent!.parent!, + }), + ASTUtils.isMemberExpression(node.value) + ? fixer.replaceText(node.value.property, 'OnPush') + : fixer.replaceText(node.value, STRATEGY_ON_PUSH), + ].filter(isNotNullOrUndefined); + } + + return [ + RuleFixes.getImportAddFix({ + fixer, + importName: 'ChangeDetectionStrategy', + moduleName: '@angular/core', + node: node.parent, + }), + RuleFixes.getDecoratorPropertyAddFix( + node, + fixer, + `${METADATA_PROPERTY_NAME}: ${STRATEGY_ON_PUSH}`, + ), + ].filter(isNotNullOrUndefined); + }, + }, + ], + }); + }, + }; + }, +}; + +function nodeToReport(node: TSESTree.Node) { + if (!ASTUtils.isProperty(node)) { + return node; + } + + return ASTUtils.isMemberExpression(node.value) + ? node.value.property + : node.value; +} \ No newline at end of file diff --git a/eslint-plugin/lib/rules/prefer-shorthand-css-notations.js b/eslint-plugin/lib/rules/prefer-shorthand-css-notations.js deleted file mode 100644 index 8f139f6..0000000 --- a/eslint-plugin/lib/rules/prefer-shorthand-css-notations.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Encourage usage of shorthand CSS notations", - category: "eco-design", - recommended: "warn", - }, - messages: { - PreferShorthandCSSNotation: - "Prefer the shorthand CSS notation {{property}}", - }, - schema: [ - { - type: "object", - properties: { - disableProperties: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - ], - }, - create: function (context) { - const shorthandProperties = { - animation: ["animationName", "animationDuration"], - background: [ - "backgroundColor", - "backgroundImage", - "backgroundPosition", - "backgroundRepeat", - ], - border: ["borderColor", "borderStyle", "borderWidth"], - column: ["columnCount", "columnWidth"], - columnRule: ["columnRuleColor", "columnRuleStyle", "columnRuleWidth"], - flex: ["flexBasis", "flexGrow", "flexShrink"], - font: ["fontFamily", "fontSize", "fontStyle"], - grid: ["gridAutoColumns", "gridAutoFlow", "gridAutoRows"], - gridTemplate: ["gridTemplateColumns", "gridTemplateRows"], - listStyle: ["listStyleImage", "listStylePosition", "listStyleType"], - margin: ["marginTop", "marginRight", "marginBottom", "marginLeft"], - offset: ["offsetPath", "offsetPosition"], - outline: ["outlineStyle", "outlineWidth", "outlineColor"], - overflow: ["overflowX", "overflowY"], - padding: ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"], - placeContent: ["alignContent", "justifyContent"], - placeItems: ["alignItems", "justifyItems"], - placeSelf: ["alignSelf", "justifySelf"], - textDecoration: [ - "textDecorationColor", - "textDecorationLine", - "textDecorationStyle", - ], - transition: ["transitionProperty", "transitionDuration"], - }; - - const disabledProperties = context.options?.[0]?.disableProperties ?? []; - - return { - JSXOpeningElement(node) { - const styleAttribute = node.attributes.find( - (attr) => attr.name?.name === "style", - ); - if (styleAttribute?.value.expression?.properties) { - const nodePropertyNames = - styleAttribute.value.expression.properties.map( - (property) => property.key.name, - ); - - for (const [shorthandProp, matchProperties] of Object.entries( - shorthandProperties, - )) { - if ( - !disabledProperties.includes(shorthandProp) && - matchProperties.every((prop) => nodePropertyNames.includes(prop)) - ) { - return context.report({ - node: styleAttribute, - messageId: "PreferShorthandCSSNotation", - data: { property: shorthandProp }, - }); - } - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/rules/provide-print-css.js b/eslint-plugin/lib/rules/provide-print-css.js deleted file mode 100644 index 53dc229..0000000 --- a/eslint-plugin/lib/rules/provide-print-css.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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: "Enforce providing a print stylesheet", - category: "eco-design", - recommended: "warn", - }, - messages: { - noPrintCSSProvided: "Provide a print CSS", - }, - schema: [], - }, - create(context) { - const isStyleWithPrintNode = (node) => { - return ( - node.openingElement.name.name === "style" && - node.children.some((child) => { - let element = null; - if (child.value != null) { - element = child.value; - } else if (child.expression != null) { - if (child.expression.value != null) { - element = child.expression.value; - } else if (child.expression.quasis?.length > 0) { - element = child.expression.quasis[0].value.cooked; - } - } - return element?.includes("@media print"); - }) - ); - }; - - const isLinkForPrintNode = (node) => - node.openingElement.name.name === "link" && - node.openingElement.attributes.some( - (attr) => attr.name.name === "media" && attr.value.value === "print", - ); - - return { - JSXElement(node) { - if (node.openingElement.name.name === "head") { - const hasValidElement = node.children.some( - (child) => - child.openingElement != null && - (isStyleWithPrintNode(child) || isLinkForPrintNode(child)), - ); - - if (!hasValidElement) { - context.report({ node, messageId: "noPrintCSSProvided" }); - } - } - }, - }; - }, -}; diff --git a/eslint-plugin/lib/standalone.js b/eslint-plugin/lib/standalone.ts similarity index 85% rename from eslint-plugin/lib/standalone.js rename to eslint-plugin/lib/standalone.ts index 417344c..13eb555 100644 --- a/eslint-plugin/lib/standalone.js +++ b/eslint-plugin/lib/standalone.ts @@ -20,12 +20,15 @@ * @fileoverview JavaScript linter of Creedengo project (standalone mode) * @author Green Code Initiative */ -"use strict"; -const rules = require("./rule-list"); +import { RuleModule, rules } from "./rule-list"; -const allRules = {}; -const recommendedRules = {}; +type RecommendedRules = { + [recommendedRule: `@creedengo/${string}`]: string +} + +const allRules: {[ruleName: string]: RuleModule}= {}; +const recommendedRules: RecommendedRules = {}; for (let { ruleName, ruleModule } of rules) { allRules[ruleName] = ruleModule; @@ -34,12 +37,13 @@ for (let { ruleName, ruleModule } of rules) { recommendedRules[`@creedengo/${ruleName}`] = ruleConfiguration; } -const plugin = { +export const plugin = { meta: { name: "@creedengo/eslint-plugin", version: "2.1.0", // dynamically updated by the release workflow }, rules: allRules, + configs: {} }; plugin.configs = { @@ -52,5 +56,3 @@ plugin.configs = { rules: recommendedRules, }, }; - -module.exports = plugin; diff --git a/eslint-plugin/package.json b/eslint-plugin/package.json index 1a363d9..83d7bca 100644 --- a/eslint-plugin/package.json +++ b/eslint-plugin/package.json @@ -16,18 +16,18 @@ }, "license": "GPL-3.0", "author": "Green Code Initiative", - "main": "./lib/standalone.js", + "main": "./lib/standalone.ts", "files": [ "lib", - "./dist/rules.js" + "./dist/rules.ts" ], "scripts": { "clean": "rimraf dist/pack", - "lint": "yarn lint:eslint-docs && yarn lint:js", + "lint": "yarn lint:eslint-docs && yarn lint:ts", "lint:eslint-docs": "eslint-doc-generator --check", - "lint:js": "eslint .", + "lint:ts": "eslint .", "lint:fix": "eslint . --fix", - "pack:sonar": "npm pkg set main=\"./dist/rules.js\" && mkdirp dist/pack && yarn pack -o dist/pack/creedengo-eslint-plugin.tgz && npm pkg set main=\"./lib/standalone.js\"", + "pack:sonar": "npm pkg set main=\"./dist/rules.ts\" && mkdirp dist/pack && yarn pack -o dist/pack/creedengo-eslint-plugin.tgz && npm pkg set main=\"./lib/standalone.ts\"", "test": "mocha tests --recursive", "test:cov": "nyc --reporter=lcov --reporter=text mocha tests --recursive", "update:eslint-docs": "eslint-doc-generator" @@ -47,7 +47,7 @@ "nyc": "^17.1.0", "prettier": "^3.4.2", "rimraf": "^5.0.5", - "typescript": "~5.3.3" + "typescript": "^5.8.3" }, "engines": { "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" @@ -55,5 +55,12 @@ "peerDependencies": { "eslint": ">= 7 < 10" }, - "packageManager": "yarn@4.9.1" + "packageManager": "yarn@4.9.1", + "dependencies": { + "@angular-eslint/utils": "^19.4.0", + "@types/jest": "^29.5.14", + "@types/node": "^22.15.21", + "@typescript-eslint/rule-tester": "^8.32.1", + "@typescript-eslint/utils": "^8.32.1" + } } diff --git a/eslint-plugin/tests/lib/rule-list.test.js b/eslint-plugin/tests/lib/rule-list.test.ts similarity index 94% rename from eslint-plugin/tests/lib/rule-list.test.js rename to eslint-plugin/tests/lib/rule-list.test.ts index a1a2280..6adfdf9 100644 --- a/eslint-plugin/tests/lib/rule-list.test.js +++ b/eslint-plugin/tests/lib/rule-list.test.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -const assert = require("assert"); +import * as assert from 'assert'; -describe("rule-list.js", () => { +describe("rule-list.ts", () => { it("should export list of valid rule modules", () => { const rules = require("../../lib/rule-list"); assert.notEqual(rules.length, 0); diff --git a/eslint-plugin/tests/lib/rules/avoid-autoplay.js b/eslint-plugin/tests/lib/rules/avoid-autoplay.ts similarity index 91% rename from eslint-plugin/tests/lib/rules/avoid-autoplay.js rename to eslint-plugin/tests/lib/rules/avoid-autoplay.ts index d800b52..a0fdce8 100644 --- a/eslint-plugin/tests/lib/rules/avoid-autoplay.js +++ b/eslint-plugin/tests/lib/rules/avoid-autoplay.ts @@ -22,7 +22,7 @@ // Requirements //------------------------------------------------------------------------------ -const rule = require("../../../lib/rules/avoid-autoplay"); +import preferOnPushComponentChangeDetection from "../../../lib/rules/prefer-on-push-component-change-detection"; const RuleTester = require("eslint").RuleTester; //------------------------------------------------------------------------------ @@ -52,7 +52,7 @@ const BothError = { type: "JSXAttribute", }; -ruleTester.run("autoplay-audio-video-attribute-not-present", rule, { +ruleTester.run("autoplay-audio-video-attribute-not-present", preferOnPushComponentChangeDetection, { valid: [ '', '', diff --git a/eslint-plugin/tests/lib/rules/avoid-brightness-override.js b/eslint-plugin/tests/lib/rules/avoid-brightness-override.js deleted file mode 100644 index b33a496..0000000 --- a/eslint-plugin/tests/lib/rules/avoid-brightness-override.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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/eslint-plugin/tests/lib/rules/avoid-css-animations.js b/eslint-plugin/tests/lib/rules/avoid-css-animations.js deleted file mode 100644 index eaa008f..0000000 --- a/eslint-plugin/tests/lib/rules/avoid-css-animations.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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
`, - // spread attributes should not throw an error (#49) - "", - ], - - invalid: [ - { - code: "
", - errors: [ - { - messageId: "AvoidCSSAnimations", - data: { - attribute: "transition", - }, - type: "Property", - }, - ], - }, - { - code: "
", - errors: [ - { - messageId: "AvoidCSSAnimations", - data: { - attribute: "animationName", - }, - type: "Property", - }, - ], - }, - ], -}); diff --git a/eslint-plugin/tests/lib/rules/avoid-high-accuracy-geolocation.js b/eslint-plugin/tests/lib/rules/avoid-high-accuracy-geolocation.js deleted file mode 100644 index 1c32067..0000000 --- a/eslint-plugin/tests/lib/rules/avoid-high-accuracy-geolocation.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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-high-accuracy-geolocation"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parserOptions: { - ecmaVersion: 6, - sourceType: "module", - }, -}); -const expectedErrorOnProperty = { - messageId: "AvoidUsingAccurateGeolocation", - type: "Property", -}; -const expectedErrorOnMemberExpression = { - messageId: "AvoidUsingAccurateGeolocation", - type: "MemberExpression", -}; - -ruleTester.run("avoid-high-accuracy-geolocation", rule, { - valid: [ - ` - var opts = {enableHighAccuracy: false, timeout: 5000, maximumAge: 0}; - - function success(pos) { - console.log(pos.coords); - } - - function error(err) { - console.warn(err); - } - - navigator.geolocation.getCurrentPosition(success, error, opts); - `, - ` - function success(pos) { - console.log(pos.coords); - } - - navigator.geolocation.getCurrentPosition(success); - `, - ` - navigator.geolocation.getCurrentPosition(success, error, {enableHighAccuracy: false}); - `, - - ` - import axios from 'axios'; - `, - ` - import * as Location from 'expo-location'; - - Location.requestPermissionsAsync(); - `, - ], - - invalid: [ - { - code: ` - var options = {enableHighAccuracy: true, timeout: 5000, maximumAge: 0}; - - function success(pos) { - console.log(pos.coords); - } - - function error(err) { - console.warn(err); - } - - navigator.geolocation.getCurrentPosition(success, error, options); - `, - errors: [expectedErrorOnProperty], - }, - { - code: ` - navigator.geolocation.getCurrentPosition(success, error, {enableHighAccuracy: true}); - `, - errors: [expectedErrorOnProperty], - }, - { - code: ` - import * as Location from 'expo-location'; - - Location.enableNetworkProviderAsync(); - `, - errors: [expectedErrorOnMemberExpression], - }, - ], -}); diff --git a/eslint-plugin/tests/lib/rules/avoid-keep-awake.js b/eslint-plugin/tests/lib/rules/avoid-keep-awake.js deleted file mode 100644 index 6bea138..0000000 --- a/eslint-plugin/tests/lib/rules/avoid-keep-awake.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs - * Copyright © 2023 Green Code Initiative (https://green-code-initiative.org) - * - * 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-keep-awake"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ - parserOptions: { - ecmaVersion: 2022, - sourceType: "module", - ecmaFeatures: { - jsx: true, - }, - }, -}); - -const expectedErrorHook = { - messageId: "AvoidKeepAwake", - type: "CallExpression", -}; - -const expectedErrorFunction = { - messageId: "AvoidKeepAwake", - type: "CallExpression", -}; - -ruleTester.run("avoid-keep-awake", rule, { - valid: [ - { - code: ` - import React from 'react'; - import { Text, View } from 'react-native'; - - export default function ValidExample() { - return ( - - This screen will sleep! - - ); - } - `, - }, - { - code: ` - import React from 'react'; - import { useKeepAwake } from 'other-library'; - import { Button, View } from 'react-native'; - - export default class ValidExample extends React.Component { - render() { - useKeepAwake(); - return ( - - - ); - } - } - `, - }, - ], - invalid: [ - { - code: ` - import { useKeepAwake } from 'expo-keep-awake'; - import React from 'react'; - import { Text, View } from 'react-native'; - - export default function KeepAwakeExample() { - useKeepAwake(); - return ( - - This screen will never sleep! - - ); - } - `, - errors: [expectedErrorHook], - }, - { - code: ` - import { activateKeepAwake } from 'expo-keep-awake'; - import React from 'react'; - import { Button, View } from 'react-native'; - - export default class KeepAwakeExample extends React.Component { - render() { - return ( - -