Repository navigation
Add rule no-imported-number-format-library (GCI535) #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
utarwyn
merged 8 commits into
green-code-initiative:main
from
Nephtys:challenge-int-format
May 18, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
8a046e7
Add rule GCI535 "No imported number format library"
ef9138f
Add `test:watch` target
Nephtys efb9a69
Improve documentation
Nephtys 156f1a9
GCI535: add support for the numerable library
80220c7
GCI535[doc]: add examples, limitations and ideas for the next develop…
1bf5f19
Merge branch 'main' into challenge-int-format
utarwyn da8c029
Merge branch 'main' into challenge-int-format
utarwyn 2324c44
Apply Copilot feedbacks
utarwyn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
eslint-plugin/docs/rules/no-imported-number-format-library.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # @creedengo/no-imported-number-format-library | ||
|
|
||
| 📝 You should not format number with an external library. | ||
|
|
||
| ⚠️ This rule _warns_ in the ✅ `recommended` config. | ||
|
|
||
| <!-- end auto-generated rule header --> | ||
|
|
||
| ## Why is this an issue? | ||
|
|
||
| Importing an external library for lightweight operations increases overall size of the program. | ||
| Using native methods instead reduces the amount of memory and storage 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. | ||
|
|
||
| Depending on less external dependencies also increases the maintainability and security of your program. | ||
|
|
||
| ## Examples | ||
|
|
||
| **Example with the [numbro](https://numbrojs.com/) library, when you use | ||
| `format` method.** | ||
|
|
||
| ```js | ||
| // Example with numbro (not compliant) | ||
| import numbro from "numbro"; | ||
|
|
||
| numbro.setLanguage("en-GB"); | ||
| var string = numbro(1000).format({ | ||
| thousandSeparated: true, | ||
| }); // '1,000' | ||
|
|
||
| // Example with numerable (not compliant) | ||
| import { format } from "numerable"; | ||
| format(1000, "0,0"); | ||
|
|
||
| // Example with Intl (compliant) | ||
| new Intl.NumberFormat("en-GB").format(1000); // '1,000' | ||
| ``` | ||
|
|
||
| ## Limitations | ||
|
|
||
| As for now, only two libraries are handled by this rule : | ||
|
|
||
| - [numbro](https://www.npmjs.com/package/numbro) | ||
| - [numerable](https://www.npmjs.com/package/numerable) | ||
|
|
||
| Some candidates for the future developments are : | ||
|
|
||
| - [javascript-number-formatter](https://www.npmjs.com/package/number-format.js) | ||
| - [numeraljs](https://www.npmjs.com/package/numeral) | ||
| - [formatjs](https://www.npmjs.com/package/@formatjs/intl) | ||
|
|
||
| It’s more likely this rule won’t ever be exhaustive. | ||
|
|
||
| ## Resources | ||
|
|
||
| ### Documentation | ||
|
|
||
| - [Mozilla Web Technology for Developers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) |
85 changes: 85 additions & 0 deletions
85
eslint-plugin/lib/rules/no-imported-number-format-library.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /* | ||
| * 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 <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| "use strict"; | ||
|
|
||
| /** @type {import("eslint").Rule.RuleModule} */ | ||
| module.exports = { | ||
| meta: { | ||
| type: "suggestion", | ||
| docs: { | ||
| description: "You should not format number with an external library", | ||
| category: "eco-design", | ||
| recommended: "warn", | ||
| }, | ||
| messages: { | ||
| ShouldNotUseImportedNumberFormatLibrary: | ||
| "You should not format number with an external library", | ||
| }, | ||
| schema: [], | ||
| }, | ||
|
|
||
| create: function (context) { | ||
| let variablesNumbro = []; | ||
|
|
||
| const errorReport = (node) => ({ | ||
| node, | ||
| messageId: "ShouldNotUseImportedNumberFormatLibrary", | ||
| }); | ||
|
|
||
| return { | ||
| VariableDeclarator(node) { | ||
| if ( | ||
| node.init?.callee?.name === "numbro" && | ||
| node.id.type === "Identifier" | ||
| ) { | ||
| variablesNumbro.push(node.id.name); | ||
| } | ||
| }, | ||
| CallExpression(node) { | ||
| const formatIsCalledOnANumbroTypeVariable = | ||
| node.callee.type === "MemberExpression" && | ||
| variablesNumbro.includes(node.callee.object.name) && | ||
| node.callee.property.name === "format"; | ||
| if (formatIsCalledOnANumbroTypeVariable) { | ||
| context.report(errorReport(node.callee.property)); | ||
| } | ||
| let formatIsCalledOnNumbroInstance = | ||
| node.parent.type === "MemberExpression" && | ||
| node.callee.name === "numbro" && | ||
| node.parent.property.name === "format"; | ||
| if (formatIsCalledOnNumbroInstance) { | ||
| context.report(errorReport(node.parent.property)); | ||
| } | ||
| }, | ||
| ImportDeclaration(node) { | ||
| const importedLibraryName = node.source.value; | ||
| if (importedLibraryName === "numerable") { | ||
| const formatSpecifier = node.specifiers.find( | ||
| (specifier) => | ||
| specifier.type === "ImportSpecifier" && | ||
| specifier.imported.name === "format", | ||
| ); | ||
| if (formatSpecifier) { | ||
| context.report(errorReport(formatSpecifier)); | ||
| } | ||
| } | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
95 changes: 95 additions & 0 deletions
95
eslint-plugin/tests/lib/rules/no-imported-number-format-library.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| * 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 <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| "use strict"; | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
| // Requirements | ||
| //------------------------------------------------------------------------------ | ||
|
|
||
| const rule = require("../../../lib/rules/no-imported-number-format-library"); | ||
| const { RuleTester } = require("eslint"); | ||
| const { describe, it } = require("node:test"); | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
| // Tests | ||
| //------------------------------------------------------------------------------ | ||
|
|
||
| const ruleTester = new RuleTester({ | ||
| languageOptions: { | ||
| ecmaVersion: 6, | ||
| sourceType: "module", | ||
| }, | ||
| }); | ||
|
|
||
| const expectedIdentifierError = { | ||
| messageId: "ShouldNotUseImportedNumberFormatLibrary", | ||
| }; | ||
| const expectedImportError = { | ||
| messageId: "ShouldNotUseImportedNumberFormatLibrary", | ||
| }; | ||
|
|
||
| const tests = { | ||
| valid: [ | ||
| "new Intl.NumberFormat().format(1000);", | ||
| "numbro(1000).add(5);", | ||
| ` | ||
| const number = numbro(1000); | ||
| const number2 = numbro(2000); | ||
| number2.add(1000); | ||
| `, | ||
| "import { parse } from 'numerable';", | ||
| "import { format } from 'date-fns';", | ||
| "import mysql from 'mysql2';", | ||
| ], | ||
| invalid: [ | ||
| { | ||
| code: "numbro(1000).format({thousandSeparated: true});", | ||
| errors: [expectedIdentifierError], | ||
| }, | ||
| { | ||
| code: ` | ||
| const number = numbro(1000); | ||
| number.format({thousandSeparated: true}); | ||
| `, | ||
| errors: [expectedIdentifierError], | ||
| }, | ||
| { | ||
| code: ` | ||
| const number = numbro(1000); | ||
| const number2 = numbro(2000); | ||
| number.format({thousandSeparated: true}); | ||
| `, | ||
| errors: [expectedIdentifierError], | ||
| }, | ||
| { | ||
| code: "import { format } from 'numerable';", | ||
| errors: [expectedImportError], | ||
| }, | ||
| { | ||
| code: "import { format as myFormat} from 'numerable';", | ||
| errors: [expectedImportError], | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| describe("no-imported-number-format-library", () => { | ||
| it("no-imported-number-format-library", () => { | ||
| ruleTester.run("no-imported-number-format-library", rule, tests); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
...va/org/greencodeinitiative/creedengo/javascript/checks/NoImportedNumberFormatLibrary.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /* | ||
| * 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 <http://www.gnu.org/licenses/>. | ||
| */ | ||
| package org.greencodeinitiative.creedengo.javascript.checks; | ||
|
|
||
| import org.sonar.check.Rule; | ||
| import org.sonar.plugins.javascript.api.EslintHook; | ||
| import org.sonar.plugins.javascript.api.JavaScriptRule; | ||
| import org.sonar.plugins.javascript.api.TypeScriptRule; | ||
|
|
||
| @JavaScriptRule | ||
| @TypeScriptRule | ||
| @Rule(key = NoImportedNumberFormatLibrary.RULE_KEY) | ||
| public class NoImportedNumberFormatLibrary implements EslintHook { | ||
|
|
||
| public static final String RULE_KEY = "GCI535"; | ||
|
|
||
| @Override | ||
| public String eslintKey() { | ||
| return "@creedengo/no-imported-number-format-library"; | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| "GCI36", | ||
| "GCI505", | ||
| "GCI523", | ||
| "GCI530" | ||
| "GCI530", | ||
| "GCI535" | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import numbro from "numbro"; | ||
| import { parse, format } from "numerable"; // Non-compliant: usage of external library to format number | ||
|
|
||
| numbro(1000).format({thousandSeparated: true}); // Non-compliant: usage of external library to format number | ||
|
|
||
| let variable = numbro(1000); | ||
| variable.format({thousandSeparated: true}); // Non-compliant: usage of external library to format number | ||
|
|
||
| numbro(2000).add(1000); // Compliant | ||
|
|
||
| new Intl.NumberFormat().format(1000); // Compliant | ||
|
|
||
| format(28); | ||
| parse("29"); // Compliant |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.