Skip to content

feat: new restrict-dependency-ranges rule #998

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
merged 11 commits into from
Apr 22, 2025
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ The default settings don't conflict, and Prettier plugins can quickly fix up ord
| [require-name](docs/rules/require-name.md) | Requires the `name` property to be present. | ✔️ ✅ | | | |
| [require-types](docs/rules/require-types.md) | Requires the `types` property to be present. | | | | |
| [require-version](docs/rules/require-version.md) | Requires the `version` property to be present. | ✔️ ✅ | | | |
| [restrict-dependency-ranges](docs/rules/restrict-dependency-ranges.md) | Restricts the range of dependencies to allow or disallow specific types of ranges. | | | 💡 | |
| [sort-collections](docs/rules/sort-collections.md) | Dependencies, scripts, and configuration values must be declared in alphabetical order. | ✔️ ✅ | 🔧 | | |
| [unique-dependencies](docs/rules/unique-dependencies.md) | Checks a dependency isn't specified more than once (i.e. in `dependencies` and `devDependencies`) | ✔️ ✅ | | 💡 | |
| [valid-local-dependency](docs/rules/valid-local-dependency.md) | Checks existence of local dependencies in the package.json | ✔️ ✅ | | | |
Expand Down
177 changes: 177 additions & 0 deletions docs/rules/restrict-dependency-ranges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# restrict-dependency-ranges

💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).

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

This rule allows you to require that specific dependencies use a particular kind of semver range (e.g. `^`).
There are several options for specifying which dependencies a range type restriction should be applied to, including dependency type, package name (or name regex pattern), and version range (e.g. '<1').

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
// Require that packages with 0.x.x versions are pinned
{
forVersions: "<1",
rangeType: "pin",
},
],
},
];
```

If you provide multiple options and a dependency matches more than one of the options, the last option that matches will take precedent for that dependency.
This allows you to define more general rules that apply to all dependencies (or large groups of dependencies), and then define more granular options to apply exceptions or focus on some subset of dependencies.

## Options

`type DependencyType = 'dependencies' | 'devDependencies' | 'optionalDependencies' | 'peerDependencies';`\
`type RangeType = 'caret' | 'pin' | 'tilde';`

| Name | Type | Required |
| :------------------- | :----------------------- | :------- |
| `forDependencyTypes` | DependencyType[] | |
| `forPackages` | string[] | |
| `forVersions` | string | |
| `rangeType` | RangeType \| RangeType[] | Yes |

You can provide a single options object consisting of the above, or an array of such objects.

### `forDependencyTypes`

You can use this to apply a range type restriction for an entire group of dependencies by which type of dependencies they belong to.

Options are

- dependencies
- devDependencies
- optionalDependencies
- peerDependencies

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
// Require that all dev dependencies are pinned
{
forDependencyTypes: ["devDependencies"],
rangeType: "pin",
},
],
},
];
```

### `forPackages`

This can be the exact name of a package, or a regex pattern used to match a group of packages by name (e.g. `@typescript-eslint/*`).

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
// Restrict typescript to tilde ranges
{
forPackages: ["typescript"],
rangeType: "tilde",
},
],
},
];
```

### `forVersions`

You can use this to apply a restriction to a specific semver range.
For example, a common use case is to pin "unstable" dependencies (packages that have a version in the `0.x.x` range).
You can do this by setting `forVersions` to `'<1'`.

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
// Require that all deps should use ^
{
rangeType: "caret",
},
],
},
];
```

### `rangeType`

This is the only required option, and identifies which range type or types you want to apply to packages that match any of the other match options (or all dependencies if no other options are provided).

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
// Require that all deps should use ^
{
rangeType: "caret",
},
],
},
];
```

## Example

```ts
export default [
{
"package-json/restrict-dependency-ranges": [
"error",
[
// Apply base requirement that all deps should use ^
{
rangeType: "caret",
},

// Restrict typescript to tilde ranges
{
forPackages: ["typescript"],
rangeType: "tilde",
},

// Require that packages with 0.x.x versions are pinned
{
forVersions: "<1",
rangeType: "pin",
},
],
],
},
];
```

Example of **incorrect** code for the above configuration:

```json
{
"devDependencies": {
"eslint": "^9.18.0",
"markdownlint": "^0.37.4",
"typescript": "^5.8.0"
}
}
```

Example of **correct** code for the above configuration:

```json
{
"devDependencies": {
"eslint": "^9.18.0",
"markdownlint": "0.37.4",
"typescript": "~5.8.0"
}
}
```
2 changes: 2 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { rule as noRedundantFiles } from "./rules/no-redundant-files.js";
import { rule as orderProperties } from "./rules/order-properties.js";
import { rule as preferRepositoryShorthand } from "./rules/repository-shorthand.js";
import { rules as requireRules } from "./rules/require-properties.js";
import { rule as restrictDependencyRanges } from "./rules/restrict-dependency-ranges.js";
import { rule as sortCollections } from "./rules/sort-collections.js";
import { rule as uniqueDependencies } from "./rules/unique-dependencies.js";
import { rule as validLocalDependency } from "./rules/valid-local-dependency.js";
Expand All @@ -29,6 +30,7 @@ const rules: Record<string, PackageJsonRuleModule> = {
"order-properties": orderProperties,
...requireRules,
"repository-shorthand": preferRepositoryShorthand,
"restrict-dependency-ranges": restrictDependencyRanges,
"sort-collections": sortCollections,
"unique-dependencies": uniqueDependencies,
"valid-local-dependency": validLocalDependency,
Expand Down
Loading
Loading