Skip to content

Commit 3360ca3

Browse files
anthony-oDryoneoVincent-Letourmyutarwyn
authored
Add rule GCI31 "Prefer lighter formats for image files" (#46)
Co-authored-by: Lucas Leroux <[email protected]> Co-authored-by: Vincent-Letourmy <[email protected]> Co-authored-by: utarwyn <[email protected]>
1 parent a14c77c commit 3360ca3

File tree

8 files changed

+302
-15
lines changed

8 files changed

+302
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- [#40](https://github.com/green-code-initiative/creedengo-javascript/pull/40) Add rule `@creedengo/avoid-autoplay` (GCI36)
13+
- [#46](https://github.com/green-code-initiative/creedengo-javascript/pull/46) Add rule `@creedengo/prefer-lighter-formats-for-image-files` (GCI31)
1314

1415
## [2.0.0] - 2025-01-22
1516

eslint-plugin/README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,22 @@ Add `@creedengo` to the `plugins` section of your `.eslintrc`, followed by rules
7474
⚠️ Configurations set to warn in.\
7575
✅ Set in the `recommended` configuration.
7676

77-
| Name | Description | ⚠️ |
78-
| :------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- |
79-
| [avoid-autoplay](docs/rules/avoid-autoplay.md) | Avoid autoplay for videos and audio content ||
80-
| [avoid-brightness-override](docs/rules/avoid-brightness-override.md) | Should avoid to override brightness ||
81-
| [avoid-css-animations](docs/rules/avoid-css-animations.md) | Avoid usage of CSS animations ||
82-
| [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications ||
83-
| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query ||
84-
| [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute ||
85-
| [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library ||
86-
| [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element ||
87-
| [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once ||
88-
| [no-torch](docs/rules/no-torch.md) | Should not programmatically enable torch mode ||
89-
| [prefer-collections-with-pagination](docs/rules/prefer-collections-with-pagination.md) | Prefer API collections with pagination ||
90-
| [prefer-shorthand-css-notations](docs/rules/prefer-shorthand-css-notations.md) | Encourage usage of shorthand CSS notations ||
91-
| [provide-print-css](docs/rules/provide-print-css.md) | Enforce providing a print stylesheet ||
77+
| Name | Description | ⚠️ |
78+
| :--------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :- |
79+
| [avoid-autoplay](docs/rules/avoid-autoplay.md) | Avoid autoplay for videos and audio content ||
80+
| [avoid-brightness-override](docs/rules/avoid-brightness-override.md) | Should avoid to override brightness ||
81+
| [avoid-css-animations](docs/rules/avoid-css-animations.md) | Avoid usage of CSS animations ||
82+
| [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications ||
83+
| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query ||
84+
| [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute ||
85+
| [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library ||
86+
| [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element ||
87+
| [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once ||
88+
| [no-torch](docs/rules/no-torch.md) | Should not programmatically enable torch mode ||
89+
| [prefer-collections-with-pagination](docs/rules/prefer-collections-with-pagination.md) | Prefer API collections with pagination ||
90+
| [prefer-lighter-formats-for-image-files](docs/rules/prefer-lighter-formats-for-image-files.md) | Prefer lighter formats for image files ||
91+
| [prefer-shorthand-css-notations](docs/rules/prefer-shorthand-css-notations.md) | Encourage usage of shorthand CSS notations ||
92+
| [provide-print-css](docs/rules/provide-print-css.md) | Enforce providing a print stylesheet ||
9293

9394
<!-- end auto-generated rules list -->
9495

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Prefer lighter formats for image files (`@creedengo/prefer-lighter-formats-for-image-files`)
2+
3+
⚠️ This rule _warns_ in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
## Why is this an issue?
8+
9+
Using appropriate image formats and optimizing image sizes is essential for improving website performance, user experience, and overall environmental impact.
10+
Larger image file sizes consume more bandwidth, increasing the data transfer required to load a web page.
11+
Some image formats are generally considered better for eco-friendly web design and should be used in most cases.
12+
13+
We recommend using the following formats:
14+
15+
- **WebP**, developed by Google, is a modern image format that provides high compression efficiency without significant loss of quality.
16+
- **AVIF** (AV1 Image File Format) is a relatively new and highly efficient image format that is based on the AV1 video codec.
17+
- **SVG** (Scalable Vector Graphics) is a vector image format that is based on XML.
18+
Files are lightweight and can be scaled without loss of quality.
19+
20+
```html
21+
<img src="./assets/images/cat.jpg" alt="Unoptimized image of a cat" /> //
22+
Non-compliant
23+
24+
<img src="./assets/images/cat.webp" alt="Optimized image of a cat" /> //
25+
Compliant
26+
```
27+
28+
Remember that the best image format may vary depending on the specific use case, content, and requirements of your website.
29+
Always test and evaluate the performance of different formats to find the optimal balance between image quality and file size.
30+
31+
### Picture
32+
33+
Images often represent most of the downloaded bytes, right after videos and just before CSS and JavaScript libraries.
34+
Optimizing images is important to reduce used bandwidth. The first step is to choose the ideal format for your
35+
display needs.
36+
37+
Raster images should be reserved for photos and interface elements that cannot be displayed with icons or CSS styles.
38+
39+
The appropriate format depends on the image properties : black & white or color, color palette, need for transparency...
40+
Among these properties, the possibility to irremediably alter images quality (lossy compression) tends to favor formats such as JPEG, JPEG XL,
41+
AVIF, or WebP, while needing transparency and/or the impossibility to alter the image quality (lossless compression) will tend to favor
42+
PNG or WebP lossless formats (which supports transparency).
43+
44+
Format importantly impacts images size: on average, .webp images will be 30% lighter than .jpeg
45+
images or .png images. .avif images can be up to 20% lighter than .webp image and 50% lighter than .jepg images.
46+
47+
Don't forget to pay attention to browser support. .webp images will not be recognized by
48+
old browsers and will not be displayed. It is possible to provide several formats for the same image
49+
to overcome this issue. Some server-side modules (such as Google's modPageSpeed, also available for Apache
50+
and Nginx) even allow you to provide the appropriate image for the browser that is calling the server.
51+
52+
Many tools will help you minimize images size:
53+
54+
- SQUOOSH
55+
- CLOUDINARY
56+
- ImageMagick
57+
- PngCrush
58+
- JpegTran
59+
60+
### Example
61+
62+
In this example, the DOM <picture> element informs the browser that there are two images: a .webp image and a
63+
.jpg image, which is used by default. The browser will decide which image will be downloaded. If the .webp format
64+
is supported, the image.webp image will be downloaded; otherwise, image.jpg image will be downloaded.
65+
66+
```html
67+
<picture>
68+
<source srcset="image.webp" type="image/webp" />
69+
<img src="image.jpg" alt="..." loading="lazy" />
70+
</picture>
71+
```
72+
73+
Also remember to consider browser compatibility.
74+
Older browsers may not recognize .webp/.avif images and fail to display them.
75+
To address this issue, you can supply multiple formats for the same image.
76+
77+
## Resources
78+
79+
### Documentation
80+
81+
- [CNUMR best practices](https://github.com/cnumr/best-practices/blob/main/chapters/BP_080_en.md) - Optimize images
82+
- [WSG UX15-2](https://w3c.github.io/sustyweb/star.html#UX15-2) - Optimizing All Image Assets for a Variety of Different Resolutions
83+
- [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 🇫🇷
84+
85+
### Articles & blog posts
86+
87+
- [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/)
88+
- [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/)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
"use strict";
20+
21+
/** @type {import('eslint').Rule.RuleModule} */
22+
module.exports = {
23+
meta: {
24+
type: "suggestion",
25+
docs: {
26+
description: "Prefer lighter formats for image files",
27+
category: "eco-design",
28+
recommended: "warn",
29+
},
30+
messages: {
31+
PreferLighterFormatsForImageFiles:
32+
"You should use lighter formats for image files such as {{ eligibleExtensions }}",
33+
},
34+
schema: [],
35+
},
36+
create(context) {
37+
const eligibleExtensions = ["webp", "avif", "svg", "jxl"];
38+
39+
return {
40+
JSXOpeningElement(node) {
41+
const tagName = node.name.name;
42+
if (tagName?.toLowerCase() !== "img") return;
43+
44+
const parentTagName = node.parent?.parent?.openingElement?.name?.name;
45+
if (parentTagName?.toLowerCase() === "picture") return;
46+
47+
const srcAttribut = node.attributes.find(
48+
(attr) => attr.name.name === "src",
49+
);
50+
51+
let srcValue = srcAttribut?.value?.value;
52+
53+
if (!srcValue) return;
54+
55+
srcValue = srcValue.substring(srcValue.lastIndexOf("/") + 1);
56+
const dotIndex = srcValue.lastIndexOf(".");
57+
58+
if (dotIndex === -1) return;
59+
60+
const imgExtension = srcValue.substring(dotIndex + 1);
61+
62+
if (eligibleExtensions.includes(imgExtension.toLowerCase())) return;
63+
64+
context.report({
65+
node,
66+
messageId: "PreferLighterFormatsForImageFiles",
67+
data: { eligibleExtensions: eligibleExtensions.join(", ") },
68+
});
69+
},
70+
};
71+
},
72+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
"use strict";
20+
21+
//------------------------------------------------------------------------------
22+
// Requirements
23+
//------------------------------------------------------------------------------
24+
25+
const rule = require("../../../lib/rules/prefer-lighter-formats-for-image-files");
26+
const RuleTester = require("eslint").RuleTester;
27+
28+
//------------------------------------------------------------------------------
29+
// Tests
30+
//------------------------------------------------------------------------------
31+
32+
const ruleTester = new RuleTester({
33+
parserOptions: {
34+
ecmaVersion: 2021,
35+
sourceType: "module",
36+
ecmaFeatures: {
37+
jsx: true,
38+
},
39+
},
40+
});
41+
42+
const preferLighterFormatsForImageFilesError = {
43+
messageId: "PreferLighterFormatsForImageFiles",
44+
type: "JSXOpeningElement",
45+
};
46+
47+
ruleTester.run("prefer-lighter-formats-for-image-files", rule, {
48+
valid: [
49+
`
50+
<img src="./assets/images/cat.webp" alt="A cat"/>
51+
`,
52+
`
53+
<img src="./assets/images/cat.avif" alt="A cat"/>
54+
`,
55+
`
56+
<img src="./assets/images/cat.jxl" alt="A cat"/>
57+
`,
58+
`
59+
<picture>
60+
<source srcSet="image.webp" type="image/webp" />
61+
<img src="image.jpg" alt="..." />
62+
</picture>
63+
`,
64+
`
65+
<img src="./assets/images/cat" alt="A cat" />
66+
`,
67+
`
68+
<img src="" alt="" />
69+
`,
70+
],
71+
72+
invalid: [
73+
{
74+
code: `
75+
<img src="./assets/images/cat.jpg" alt="A cat"/>
76+
`,
77+
errors: [preferLighterFormatsForImageFilesError],
78+
},
79+
{
80+
code: `
81+
<img src="./assets/images/cat.png" alt="A cat"/>
82+
`,
83+
errors: [preferLighterFormatsForImageFilesError],
84+
},
85+
],
86+
});

sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
4545
NoMultipleStyleChanges.class,
4646
NoTorch.class,
4747
PreferCollectionsWithPagination.class,
48+
PreferLighterFormatsForImageFiles.class,
4849
PreferShorthandCSSNotations.class,
4950
ProvidePrintCSS.class
5051
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.greencodeinitiative.creedengo.javascript.checks;
19+
20+
import org.sonar.check.Rule;
21+
import org.sonar.plugins.javascript.api.EslintBasedCheck;
22+
import org.sonar.plugins.javascript.api.JavaScriptRule;
23+
import org.sonar.plugins.javascript.api.TypeScriptRule;
24+
25+
@JavaScriptRule
26+
@TypeScriptRule
27+
@Rule(key = PreferLighterFormatsForImageFiles.RULE_KEY)
28+
public class PreferLighterFormatsForImageFiles implements EslintBasedCheck {
29+
30+
public static final String RULE_KEY = "GCI31";
31+
32+
@Override
33+
public String eslintKey() {
34+
return "@creedengo/prefer-lighter-formats-for-image-files";
35+
}
36+
37+
}

sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"GCI26",
1010
"GCI29",
1111
"GCI30",
12+
"GCI31",
1213
"GCI36",
1314
"GCI523",
1415
"GCI530"

0 commit comments

Comments
 (0)