Skip to content

Commit

Permalink
feat: add ban-dependencies rule
Browse files Browse the repository at this point in the history
Drops the other rules and merges into one jumbo rule:
`ban-dependencies`.

This can be configured with `presets`. For example, `presets:
['native']` would ban dependencies which have equivalent native
alternatives in your node version.
  • Loading branch information
43081j committed Mar 10, 2024
1 parent ee58de7 commit 2b22ac3
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 425 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ export default [

## Rules

- [`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)
- [`depend/ban-dependencies`](./docs/rules/ban-dependencies.md)

## License

Expand Down
28 changes: 0 additions & 28 deletions docs/rules/avoid-micro-utilities.md

This file was deleted.

76 changes: 76 additions & 0 deletions docs/rules/ban-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Bans a list of dependencies from being used

This rule bans dependencies based on a preset list or a user defined list.

## Options

### `presets`

You may choose a preset list of dependencies (or none). The following are
available:

- `microutilities` - micro utilities (e.g. one liners)
- `native` - redundant packages with native equivalents
- Note that this preset will take into account `engines` in your
`package.json` if it is set. In that only native functionality available in your
defined `engines.node` version range will be considered (or all if it isn't
set)
- `preferred` - an opinionated list of packages with better maintained and
lighter alternatives
- Note the list for this is sourced from
[`module-replacements`](https://github.com/es-tooling/module-replacements)

Example config:

```json
{
"rules": {
"depend/ban-dependencies": ["error", {
"presets": ["native"]
}
}
}
```

The **default** is `['native', 'microutilities', 'preferred']`.

### `modules`

You may also specify your own list of packages which will be disallowed
in code.

For example:

```json
{
"rules": {
"depend/ban-dependencies": ["error", {
"modules": ["im-a-banned-package"]
}
}
}
```

## Rule Details

This rule bans certain dependencies from being used.

The following patterns are considered warnings:

```ts
// with `presets: ['native']`
const isNaN = require('is-nan');
isNaN(v);
```

The following patterns are not warnings:

```ts
// with `presets: ['native']`
Number.isNaN(v);
```

## When Not To Use It

If you prefer not to restrict which dependencies are used, this rule should
be disabled.
32 changes: 0 additions & 32 deletions docs/rules/prefer-light-dependencies.md

This file was deleted.

32 changes: 0 additions & 32 deletions docs/rules/redundant-polyfills.md

This file was deleted.

8 changes: 2 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
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';
import {rule as banDependencies} from './rules/ban-dependencies.js';

export const configs = {
recommended
};

export const rules = {
'redundant-polyfills': redundantPolyfills,
'avoid-micro-utilities': avoidMicroUtils,
'prefer-light-dependencies': preferLightDependencies
'ban-dependencies': banDependencies
};
9 changes: 7 additions & 2 deletions src/replacements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ export interface DocumentedReplacement extends ReplacementLike {
moduleName: string;
}

export interface NoReplacement extends ReplacementLike {
type: 'none';
}

export type Replacement =
| NativeReplacement
| DocumentedReplacement
| SimpleReplacement;
| SimpleReplacement
| NoReplacement;

export const lighterReplacements: Replacement[] = [
export const preferredReplacements: Replacement[] = [
{
type: 'documented',
moduleName: 'npm-run-all',
Expand Down
33 changes: 0 additions & 33 deletions src/rules/avoid-micro-utilities.ts

This file was deleted.

93 changes: 93 additions & 0 deletions src/rules/ban-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {Rule} from 'eslint';
import {getDocsUrl} from '../util/rule-meta.js';
import {
microUtilities,
preferredReplacements,
nativeReplacements,
Replacement
} from '../replacements.js';
import {createReplacementListener} from '../util/imports.js';

interface BanDependenciesOptions {
presets?: string[];
modules?: string[];
}

const availablePresets: Record<string, Replacement[]> = {
microutilities: microUtilities,
native: nativeReplacements,
preferred: preferredReplacements
};

const defaultPresets = ['microutilities', 'native', 'preferred'];

export const rule: Rule.RuleModule = {
meta: {
type: 'suggestion',
docs: {
description: 'Bans a list of dependencies from being used',
url: getDocsUrl('ban-dependencies')
},
schema: [
{
type: 'object',
properties: {
presets: {
type: 'array',
items: {
type: 'string'
}
},
modules: {
type: 'array',
items: {
type: 'string'
}
}
},
additionalProperties: false
}
],
messages: {
nativeReplacement:
'"{{name}}" should be replaced with native functionality. ' +
'You can instead use {{replacement}}. Read more here: {{url}}',
documentedReplacement:
'"{{name}}" should be replaced with an alternative package. ' +
'Read more here: {{url}}',
simpleReplacement:
'"{{name}}" should be replaced with inline/local logic.' +
'{{replacement}}',
noneReplacement:
'"{{name}}" is a banned dependency. An alternative should be used.'
}
},
create: (context) => {
const options = context.options[0] as BanDependenciesOptions | undefined;
const replacements: Replacement[] = [];
const presets = options?.presets ?? defaultPresets;
const modules = options?.modules;

for (const preset of presets) {
const presetReplacements = availablePresets[preset];
if (presetReplacements) {
for (const rep of presetReplacements) {
replacements.push(rep);
}
}
}

if (modules) {
for (const mod of modules) {
replacements.push({
type: 'none',
moduleName: mod
});
}
}

return {
...createReplacementListener(context, replacements)
};
}
};
33 changes: 0 additions & 33 deletions src/rules/prefer-light-dependencies.ts

This file was deleted.

Loading

0 comments on commit 2b22ac3

Please sign in to comment.