diff --git a/CHANGELOG.md b/CHANGELOG.md
index 549e22a..5d22fe0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- [#84](https://github.com/green-code-initiative/creedengo-javascript/pull/84) Add rule GCI535 "No imported number format library"
+
## [3.1.0] - 2026-05-10
### Added
diff --git a/eslint-plugin/README.md b/eslint-plugin/README.md
index 52f0aff..8bf01c4 100644
--- a/eslint-plugin/README.md
+++ b/eslint-plugin/README.md
@@ -90,6 +90,7 @@ export default [
| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query | ✅ |
| [no-empty-image-src-attribute](docs/rules/no-empty-image-src-attribute.md) | Disallow usage of image with empty source attribute | ✅ |
| [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library | ✅ |
+| [no-imported-number-format-library](docs/rules/no-imported-number-format-library.md) | You should not format number with an external library | ✅ |
| [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element | ✅ |
| [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once | ✅ |
| [no-torch](docs/rules/no-torch.md) | Should not programmatically enable torch mode | ✅ |
diff --git a/eslint-plugin/docs/rules/no-imported-number-format-library.md b/eslint-plugin/docs/rules/no-imported-number-format-library.md
new file mode 100644
index 0000000..332a003
--- /dev/null
+++ b/eslint-plugin/docs/rules/no-imported-number-format-library.md
@@ -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.
+
+
+
+## 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)
diff --git a/eslint-plugin/lib/rules/no-imported-number-format-library.js b/eslint-plugin/lib/rules/no-imported-number-format-library.js
new file mode 100644
index 0000000..0cb16da
--- /dev/null
+++ b/eslint-plugin/lib/rules/no-imported-number-format-library.js
@@ -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 .
+ */
+
+"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));
+ }
+ }
+ },
+ };
+ },
+};
diff --git a/eslint-plugin/tests/lib/rules/no-imported-number-format-library.test.js b/eslint-plugin/tests/lib/rules/no-imported-number-format-library.test.js
new file mode 100644
index 0000000..f9b79e6
--- /dev/null
+++ b/eslint-plugin/tests/lib/rules/no-imported-number-format-library.test.js
@@ -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 .
+ */
+
+"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);
+ });
+});
diff --git a/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java b/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java
index 97e36ce..820272a 100644
--- a/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java
+++ b/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java
@@ -42,6 +42,7 @@ public static List> getAllHooks() {
LimitDbQueryResult.class,
NoEmptyImageSrcAttribute.class,
NoImportAllFromLibrary.class,
+ NoImportedNumberFormatLibrary.class,
NoMultipleAccessDomElement.class,
NoMultipleStyleChanges.class,
NoTorch.class,
diff --git a/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/checks/NoImportedNumberFormatLibrary.java b/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/checks/NoImportedNumberFormatLibrary.java
new file mode 100644
index 0000000..5af104d
--- /dev/null
+++ b/sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/checks/NoImportedNumberFormatLibrary.java
@@ -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 .
+ */
+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";
+ }
+
+}
diff --git a/sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json b/sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json
index af1a0b3..c542671 100644
--- a/sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json
+++ b/sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json
@@ -13,6 +13,7 @@
"GCI36",
"GCI505",
"GCI523",
- "GCI530"
+ "GCI530",
+ "GCI535"
]
}
diff --git a/test-project/src/no-imported-number-format-library.js b/test-project/src/no-imported-number-format-library.js
new file mode 100644
index 0000000..664c2e4
--- /dev/null
+++ b/test-project/src/no-imported-number-format-library.js
@@ -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