Skip to content

Commit 0a5f37c

Browse files
anthony-oDryoneoVincent-Letourmy
committed
Add rule "Prefer lighter formats for image files"
Co-authored-by: Lucas Leroux <leroux.lucas@hotmail.com> Co-authored-by: Vincent-Letourmy <letourmyvincent@outlook.fr>
1 parent b80b84e commit 0a5f37c

8 files changed

Lines changed: 342 additions & 14 deletions

File tree

eslint-plugin/README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,19 @@ Add `@ecocode` to the `plugins` section of your `.eslintrc`, followed by rules c
6767
⚠️ Configurations set to warn in.\
6868
✅ Set in the `recommended` configuration.
6969

70-
| Name | Description | ⚠️ |
71-
| :------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- |
72-
| [avoid-css-animations](docs/rules/avoid-css-animations.md) | Avoid usage of CSS animations ||
73-
| [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications. ||
74-
| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query ||
75-
| [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute ||
76-
| [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library ||
77-
| [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element. ||
78-
| [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once. ||
79-
| [prefer-collections-with-pagination](docs/rules/prefer-collections-with-pagination.md) | Prefer API collections with pagination. ||
80-
| [prefer-shorthand-css-notations](docs/rules/prefer-shorthand-css-notations.md) | Encourage usage of shorthand CSS notations ||
81-
| [provide-print-css](docs/rules/provide-print-css.md) | Enforce providing a print stylesheet ||
70+
| Name | Description | ⚠️ |
71+
| :--------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- |
72+
| [avoid-css-animations](docs/rules/avoid-css-animations.md) | Avoid usage of CSS animations ||
73+
| [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications. ||
74+
| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query ||
75+
| [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute ||
76+
| [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library ||
77+
| [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element. ||
78+
| [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once. ||
79+
| [prefer-collections-with-pagination](docs/rules/prefer-collections-with-pagination.md) | Prefer API collections with pagination. ||
80+
| [prefer-lighter-formats-for-image-files](docs/rules/prefer-lighter-formats-for-image-files.md) | Prefer lighter formats for image files ||
81+
| [prefer-shorthand-css-notations](docs/rules/prefer-shorthand-css-notations.md) | Encourage usage of shorthand CSS notations ||
82+
| [provide-print-css](docs/rules/provide-print-css.md) | Enforce providing a print stylesheet ||
8283

8384
<!-- end auto-generated rules list -->
8485

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Prefer lighter formats for image files (`@ecocode/prefer-lighter-formats-for-image-files`)
2+
3+
⚠️ This rule _warns_ in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
⚠️ This rule _warns_ in the ✅ `recommended` config.
8+
9+
## Why is this an issue?
10+
11+
Using appropriate image formats and optimizing image sizes is essential for improving website performance, user experience, and overall environmental impact.
12+
Larger image file sizes consume more bandwidth, increasing the data transfer required to load a web page.
13+
Some image formats are generally considered better for eco-friendly web design and should be used in most cases.
14+
15+
We recommend using the following formats:
16+
17+
- *WebP*, developed by Google, is a modern image format that provides high compression efficiency without significant loss of quality.
18+
- *AVIF* (AV1 Image File Format) is a relatively new and highly efficient image format that is based on the AV1 video codec.
19+
- *SVG* (Scalable Vector Graphics) is a vector image format that is based on XML.
20+
Files are lightweight and can be scaled without loss of quality.
21+
22+
```html
23+
<img src="./assets/images/cat.jpg" alt="Unoptimized image of a cat"/> // Non-compliant
24+
25+
<img src="./assets/images/cat.webp" alt="Optimized image of a cat"/> // 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+
- https://github.com/cnumr/best-practices/blob/main/chapters/BP_080_en.md[CNUMR best practices] - Optimize images
82+
83+
### Articles & blog posts
84+
85+
- https://greenspector.com/en/which-image-format-to-choose-to-reduce-its-energy-consumption-and-its-environmental-impact/[greenspector.com - Which image format choose to reduce energy consumption and environmental impact?]
86+
- https://dodonut.com/blog/use-cases-of-web-image-formats/[dodonut.com - The Most Efficient Web Image Formats. Use Cases For Different Types Of Images.]
87+
88+
### WSG
89+
- [UX15-2: Optimizing All Image Assets for a Variety of Different Resolutions](https://w3c.github.io/sustyweb/star.html#UX15-2)
90+
91+
### RGESN 5.1
92+
93+
- https://ecoresponsable.numerique.gouv.fr/publications/referentiel-general-ecoconception/critere/5.1/
94+
95+
#### Objective
96+
Reduce the size of files downloaded by users.
97+
98+
#### How to implement
99+
Choose the image format best suited to the type of image and display context: use vector formats such as svg whenever possible (illustrations, icons, logos, graphs, etc.), jpg for photos and png for illustrations with solid colors.
100+
101+
#### Means of test or control
102+
Evaluate the relevance of the image format displayed.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
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+
DefineFormatsForImageFiles:
32+
"You should define a format for your image files and use light formats such as {{ eligibleExtensions }}",
33+
PreferLighterFormatsForImageFiles:
34+
"You should use lighter formats for image files such as : {{ eligibleExtensions }}",
35+
},
36+
schema: [],
37+
},
38+
create(context) {
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 eligibleExtensions = ["webp", "avif", "svg", "jxl"];
48+
49+
const srcAttribut = node.attributes.find(
50+
(attr) => attr.name.name === "src",
51+
);
52+
53+
let srcValue = srcAttribut?.value?.value;
54+
55+
if (!srcValue) return;
56+
57+
srcValue = srcValue.substring(srcValue.lastIndexOf("/") + 1);
58+
const dotIndex = srcValue.lastIndexOf(".");
59+
60+
if (dotIndex === -1) {
61+
context.report({
62+
node,
63+
messageId: "DefineFormatsForImageFiles",
64+
data: { eligibleExtensions: eligibleExtensions.join(", ") },
65+
});
66+
return;
67+
}
68+
69+
const imgExtension = srcValue.substring(dotIndex + 1);
70+
71+
if (eligibleExtensions.includes(imgExtension.toLowerCase())) return;
72+
73+
context.report({
74+
node,
75+
messageId: "PreferLighterFormatsForImageFiles",
76+
data: { eligibleExtensions: eligibleExtensions.join(", ") },
77+
});
78+
},
79+
};
80+
},
81+
};
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
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+
const defineFormatsForImageFilesError = {
42+
messageId: "DefineFormatsForImageFiles",
43+
type: "JSXOpeningElement",
44+
};
45+
const preferLighterFormatsForImageFilesError = {
46+
messageId: "PreferLighterFormatsForImageFiles",
47+
type: "JSXOpeningElement",
48+
};
49+
50+
ruleTester.run("prefer-lighter-formats-for-image-files", rule, {
51+
valid: [
52+
`
53+
<img src="./assets/images/cat.webp" alt="A cat"/>
54+
`,
55+
`
56+
<img src="./assets/images/cat.avif" alt="A cat"/>
57+
`,
58+
`
59+
<img src="./assets/images/cat.jxl" alt="A cat"/>
60+
`,
61+
`
62+
<picture>
63+
<source srcSet="image.webp" type="image/webp" />
64+
<img src="image.jpg" alt="..." />
65+
</picture>
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+
code: `
87+
<img src="./assets/images/cat" alt="A cat"/>
88+
`,
89+
errors: [defineFormatsForImageFilesError],
90+
},
91+
{
92+
code: `
93+
<img src="assets/images.dir/cat" alt="A cat"/>
94+
`,
95+
errors: [defineFormatsForImageFilesError],
96+
},
97+
{
98+
code: `
99+
<img src="./assets/images.dir/cat" alt="A cat"/>
100+
`,
101+
errors: [defineFormatsForImageFilesError],
102+
},
103+
],
104+
});

sonar-plugin/src/main/java/io/ecocode/javascript/CheckList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
4242
NoMultipleAccessDomElement.class,
4343
NoMultipleStyleChanges.class,
4444
PreferCollectionsWithPagination.class,
45+
PreferLighterFormatsForImageFiles.class,
4546
PreferShorthandCSSNotations.class,
4647
ProvidePrintCSS.class
4748
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* ecoCode JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
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 io.ecocode.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 = "EC31";
31+
32+
@Override
33+
public String eslintKey() {
34+
return "@ecocode/prefer-lighter-formats-for-image-files";
35+
}
36+
37+
}

sonar-plugin/src/main/resources/io/ecocode/profiles/ecocode_javascript_profile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"EC25",
1010
"EC26",
1111
"EC29",
12-
"EC30"
12+
"EC30",
13+
"EC31"
1314
]
1415
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "ecoCode",
33
"ruleKeys": [
4-
"EC13"
4+
"EC13",
5+
"EC31"
56
]
67
}

0 commit comments

Comments
 (0)