Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- [#51](https://github.com/green-code-initiative/creedengo-javascript/pull/51) Add ESLint v9 and flat config support
- [#90](https://github.com/green-code-initiative/creedengo-javascript/pull/90) Add avoid using resource-hungry libs rule

### Changed

Expand Down
30 changes: 30 additions & 0 deletions eslint-plugin/docs/rules/avoid-using-resource-hungry-libs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Avoid using resource hungry libraries(`@creedengo/avoid-using-resource-hungry-libs`)

⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`.

<!-- end auto-generated rule header -->

## Why is this an issue?

Avoid imports of known resource-hungry or deprecated libraries and suggest lighter alternatives.

This rule flags any import, require or dynamic import of libraries listed in the project’s deprecation dictionary (`moment`, `iconsax`, etc.), because these libraries
1. Inflate your bundle size
2. Slow parsing & compilation
3. Often lack tree-shaking support
4. May be unmaintained

```js
import moment from "moment";
const age = moment().diff(birthday, "years");
```

```js
import differenceInYears from "date-fns/differenceInYears";

const age = differenceInYears(new Date(), birthday);
```

## Resources

### Documentation
112 changes: 112 additions & 0 deletions eslint-plugin/lib/rules/avoid-using-resource-hungry-libs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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";

/**
* List of deprecated libraries and their suggested replacements.
*
* Key = package name to forbid
* Value = { reason: string, alternatives: string[] }
*/
const DEPRECATED_LIBS = {
moment: {
reason: "deprecated and heavy to load",
alternatives: ["date-fns", "dayjs"],
},
iconsax: {
reason: "does not support tree-shaking and adds large bundle size",
alternatives: ["@mui/icons-material", "lucide-react"],
},
lodash: {
reason: "too large, prefer modular imports or native methods",
alternatives: ["lodash.get", "Array.prototype.map", "Object.assign"],
},
// add more entries here as needed
};

module.exports = {
meta: {
type: "suggestion",
docs: {
description:
"Disallow importing deprecated libraries and suggest lighter alternatives",
category: "best-practice",
recommended: "warn",
},
messages: {
noDeprecatedLib:
"Library '{{name}}' is {{reason}}. Consider using {{alternatives}}.",
},
schema: [],
},

create(context) {
/**
* Report a deprecation warning on the given node.
*/
function reportDeprecation(node, libName) {
const { reason, alternatives } = DEPRECATED_LIBS[libName];
context.report({
node,
messageId: "noDeprecatedLib",
data: {
name: libName,
reason,
alternatives: alternatives.join(", "),
},
});
}

return {
// import moment from "moment";
ImportDeclaration(node) {
const pkg = node.source.value;
if (DEPRECATED_LIBS[pkg]) {
reportDeprecation(node.source, pkg);
}
},

// const moment = require("moment");
CallExpression(node) {
if (
node.callee.type === "Identifier" &&
node.callee.name === "require" &&
node.arguments.length === 1
) {
const arg = node.arguments[0];
if (
arg.type === "Literal" &&
typeof arg.value === "string" &&
DEPRECATED_LIBS[arg.value]
) {
reportDeprecation(arg, arg.value);
}
}
},

// import("moment").then(...)
ImportExpression(node) {
const src = node.source;
if (src.type === "Literal" && DEPRECATED_LIBS[src.value]) {
reportDeprecation(src, src.value);
}
},
};
},
};
87 changes: 87 additions & 0 deletions eslint-plugin/tests/lib/rules/avoid-using-resource-hungry-libs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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/avoid-using-resource-hungry-libs");
const { RuleTester } = require("eslint");

const ruleTester = new RuleTester({
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
});

const momentError = {
messageId: "noDeprecatedLib",
data: {
name: "moment",
reason: "deprecated and heavy to load",
alternatives: "date-fns, dayjs",
},
type: "Literal",
};

const iconsaxError = {
messageId: "noDeprecatedLib",
data: {
name: "iconsax",
reason: "does not support tree-shaking and adds large bundle size",
alternatives: "@mui/icons-material, lucide-react",
},
type: "Literal",
};

ruleTester.run("no-deprecated-libs", rule, {
valid: [
// imports of non-deprecated libraries
`import dayjs from "dayjs";`,
`const { format } = require("date-fns");`,
`const moment = require("luxon");`,
`import HomeIcon from "@mui/icons-material/Home";`,
`import { ReactComponent as Icon } from "some-svg";`,
`import("dayjs").then(d => console.log(d));`,
// dynamic import of non-deprecated
`import("lucide-react").then(l => console.log(l));`,
],
invalid: [
// -- moment cases --
{ code: `import moment from "moment";`, errors: [momentError] },
{ code: `import { duration } from "moment";`, errors: [momentError] },
{ code: `import "moment";`, errors: [momentError] },
{ code: `const moment = require("moment");`, errors: [momentError] },
{ code: `require("moment");`, errors: [momentError] },
{
code: `import("moment").then(m => console.log(m));`,
errors: [momentError],
},

// -- iconsax cases --
{ code: `import iconsax from "iconsax";`, errors: [iconsaxError] },
{ code: `import { IconHome } from "iconsax";`, errors: [iconsaxError] },
{ code: `import "iconsax";`, errors: [iconsaxError] },
{ code: `const iconsax = require("iconsax");`, errors: [iconsaxError] },
{ code: `require("iconsax");`, errors: [iconsaxError] },
{
code: `import("iconsax").then(i => console.log(i));`,
errors: [iconsaxError],
},
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
PreferCollectionsWithPagination.class,
PreferLighterFormatsForImageFiles.class,
PreferShorthandCSSNotations.class,
ProvidePrintCSS.class
ProvidePrintCSS.class,
AvoidUsingResourceHungryLibs.class
);
}

Expand Down
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.EslintBasedCheck;
import org.sonar.plugins.javascript.api.JavaScriptRule;
import org.sonar.plugins.javascript.api.TypeScriptRule;

@JavaScriptRule
@TypeScriptRule
@Rule(key = AvoidUsingResourceHungryLibs.RULE_KEY)
public class AvoidUsingResourceHungryLibs implements EslintBasedCheck {

public static final String RULE_KEY = "GCI36";

@Override
public String eslintKey() {
return "@creedengo/avoid-autoplay";
}

}
Loading