From ee58de7704753d04cb1902c7c99dff357e664220 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Sun, 10 Mar 2024 12:55:28 +0000 Subject: [PATCH] feat: add prefer-light-dependencies rule (#3) --- README.md | 1 + docs/rules/prefer-light-dependencies.md | 32 ++++++ src/main.ts | 4 +- src/rules/prefer-light-dependencies.ts | 33 ++++++ src/rules/redundant-polyfills.ts | 4 +- .../rules/prefer-light-dependencies_test.ts | 100 ++++++++++++++++++ src/test/util/rule-meta_test.ts | 34 ++++++ src/util/rule-meta.ts | 2 +- 8 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 docs/rules/prefer-light-dependencies.md create mode 100644 src/rules/prefer-light-dependencies.ts create mode 100644 src/test/rules/prefer-light-dependencies_test.ts create mode 100644 src/test/util/rule-meta_test.ts diff --git a/README.md b/README.md index 0f4c83c..bb2a253 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ export default [ - [`depend/redundant-polyfills`](./docs/rules/redundant-polyfills.md) - [`depend/avoid-micro-utilities`](./docs/rules/avoid-micro-utilities.md) +- [`depend/prefer-light-dependencies`](./docs/rules/prefer-light-dependencies.md) ## License diff --git a/docs/rules/prefer-light-dependencies.md b/docs/rules/prefer-light-dependencies.md new file mode 100644 index 0000000..e1240e2 --- /dev/null +++ b/docs/rules/prefer-light-dependencies.md @@ -0,0 +1,32 @@ +# Prefers lighter alternatives over certain dependencies + +This rule prefers lighter alternatives over possibly problematic dependencies. + +For example, dependencies known to be bloated, no longer maintained or +have security issues may be detected by this rule. + +Note this is an _opinionated_ rule, in that the dependencies detected are +driven by those in the +[module-replacements](https://github.com/es-tooling/module-replacements) +project. + +## Rule Details + +This rule detects possibly problematic dependencies. + +The following patterns are considered warnings: + +```ts +const runAll = require('npm-run-all'); +``` + +The following patterns are not warnings: + +```ts +const runAll = require('npm-run-all2'); +``` + +## When Not To Use It + +If you disagree with the opinionated list of dependencies this rule detects, +you should not use this rule. diff --git a/src/main.ts b/src/main.ts index e592f6d..06867db 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ import {recommended} from './configs/recommended.js'; import {rule as redundantPolyfills} from './rules/redundant-polyfills.js'; import {rule as avoidMicroUtils} from './rules/avoid-micro-utilities.js'; +import {rule as preferLightDependencies} from './rules/prefer-light-dependencies.js'; export const configs = { recommended @@ -8,5 +9,6 @@ export const configs = { export const rules = { 'redundant-polyfills': redundantPolyfills, - 'avoid-micro-utilities': avoidMicroUtils + 'avoid-micro-utilities': avoidMicroUtils, + 'prefer-light-dependencies': preferLightDependencies }; diff --git a/src/rules/prefer-light-dependencies.ts b/src/rules/prefer-light-dependencies.ts new file mode 100644 index 0000000..9e11973 --- /dev/null +++ b/src/rules/prefer-light-dependencies.ts @@ -0,0 +1,33 @@ +import {Rule} from 'eslint'; +import {getDocsUrl} from '../util/rule-meta.js'; +import {lighterReplacements} from '../replacements.js'; +import {createReplacementListener} from '../util/imports.js'; + +export const rule: Rule.RuleModule = { + meta: { + type: 'suggestion', + docs: { + description: 'Prefers lighter alternatives over certain dependencies', + url: getDocsUrl('prefer-light-dependencies') + }, + schema: [], + messages: { + simpleReplacement: + '"{{name}}" is redundant within your supported versions of node.' + + 'It should be replaced by the natively available ' + + '"{{replacement}}"', + nativeReplacement: + '"{{name}}" is redundant within your supported versions of node.' + + 'It should be replaced by the natively available ' + + '"{{replacement}}" ({{url}})', + documentedReplacement: + '"{{name}}" should be replaced with a lighter alternative.' + + 'For possible replacements, see {{url}}' + } + }, + create: (context) => { + return { + ...createReplacementListener(context, lighterReplacements) + }; + } +}; diff --git a/src/rules/redundant-polyfills.ts b/src/rules/redundant-polyfills.ts index ec97064..50c495f 100644 --- a/src/rules/redundant-polyfills.ts +++ b/src/rules/redundant-polyfills.ts @@ -16,11 +16,11 @@ export const rule: Rule.RuleModule = { messages: { simpleReplacement: '"{{name}}" is redundant within your supported versions of node.' + - 'It can likely be replaced by the natively available ' + + 'It should be replaced by the natively available ' + '"{{replacement}}"', nativeReplacement: '"{{name}}" is redundant within your supported versions of node.' + - 'It can likely be replaced by the natively available ' + + 'It should be replaced by the natively available ' + '"{{replacement}}" ({{url}})', documentedReplacement: '"{{name}}" is redundant within your supported versions of node.' + diff --git a/src/test/rules/prefer-light-dependencies_test.ts b/src/test/rules/prefer-light-dependencies_test.ts new file mode 100644 index 0000000..f22a536 --- /dev/null +++ b/src/test/rules/prefer-light-dependencies_test.ts @@ -0,0 +1,100 @@ +import {rule} from '../../rules/prefer-light-dependencies.js'; +import {RuleTester} from 'eslint'; +import {getReplacementsDocUrl} from '../../util/rule-meta.js'; + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2022 + } +}); + +const tseslintParser = require.resolve('@typescript-eslint/parser'); + +ruleTester.run('prefer-light-dependencies', rule, { + valid: [ + 'const foo = 303;', + { + code: `import foo = require('unknown-module');`, + parser: tseslintParser + }, + { + code: `import foo from 'unknown-module';` + }, + { + code: `const foo = require('unknown-module');` + }, + { + code: ` + const moduleName = 'npm-run-' + 'all'; + require(moduleName); + ` + }, + { + code: ` + const moduleName = 'npm-run-' + 'all'; + await import(moduleName); + ` + } + ], + + invalid: [ + { + code: `const foo = require('npm-run-all');`, + errors: [ + { + line: 1, + column: 13, + messageId: 'documentedReplacement', + data: { + name: 'npm-run-all', + url: getReplacementsDocUrl('npm-run-all') + } + } + ] + }, + { + code: `import foo from 'npm-run-all';`, + errors: [ + { + line: 1, + column: 1, + messageId: 'documentedReplacement', + data: { + name: 'npm-run-all', + url: getReplacementsDocUrl('npm-run-all') + } + } + ] + }, + { + code: `const foo = await import('npm-run-all');`, + errors: [ + { + line: 1, + column: 19, + messageId: 'documentedReplacement', + data: { + name: 'npm-run-all', + url: getReplacementsDocUrl('npm-run-all') + } + } + ] + }, + { + code: `import foo = require('npm-run-all');`, + parser: tseslintParser, + errors: [ + { + line: 1, + column: 1, + messageId: 'documentedReplacement', + data: { + name: 'npm-run-all', + url: getReplacementsDocUrl('npm-run-all') + } + } + ] + } + ] +}); diff --git a/src/test/util/rule-meta_test.ts b/src/test/util/rule-meta_test.ts new file mode 100644 index 0000000..3e4281f --- /dev/null +++ b/src/test/util/rule-meta_test.ts @@ -0,0 +1,34 @@ +import * as assert from 'node:assert'; +import {test} from 'node:test'; +import { + getDocsUrl, + getMdnUrl, + getReplacementsDocUrl +} from '../../util/rule-meta.js'; + +test('getDocsUrl', async (t) => { + await t.test('gets the url of a given rule doc', () => { + assert.equal( + getDocsUrl('bloop'), + 'https://github.com/43081j/eslint-plugin-assert/blob/main/docs/rules/bloop.md' + ); + }); +}); + +test('getMdnUrl', async (t) => { + await t.test('gets the url of a given mdn doc', () => { + assert.equal( + getMdnUrl('bloop'), + 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/bloop' + ); + }); +}); + +test('getReplacementsDocUrl', async (t) => { + await t.test('gets the url of a given replacements doc', () => { + assert.equal( + getReplacementsDocUrl('bloop'), + 'https://github.com/es-tooling/module-replacements/blob/main/docs/modules/bloop.md' + ); + }); +}); diff --git a/src/util/rule-meta.ts b/src/util/rule-meta.ts index f4d6cb8..6d6025d 100644 --- a/src/util/rule-meta.ts +++ b/src/util/rule-meta.ts @@ -22,5 +22,5 @@ export function getMdnUrl(path: string): string { * @return {string} */ export function getReplacementsDocUrl(path: string): string { - return `https://github.com/es-tooling/module-replacements/blob/main/docs/modules/${path}`; + return `https://github.com/es-tooling/module-replacements/blob/main/docs/modules/${path}.md`; }