Skip to content

🚀 Feature: add restrict dependency ranges rule #959

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

Closed
3 tasks done
michaelfaith opened this issue Mar 15, 2025 · 10 comments · Fixed by #998
Closed
3 tasks done

🚀 Feature: add restrict dependency ranges rule #959

michaelfaith opened this issue Mar 15, 2025 · 10 comments · Fixed by #998
Labels
status: accepting prs Please, send a pull request to resolve this! 🙏 type: feature New enhancement or request 🚀

Comments

@michaelfaith
Copy link
Collaborator

michaelfaith commented Mar 15, 2025

Bug Report Checklist

  • I have tried restarting my IDE and the issue persists.
  • I have pulled the latest main branch of the repository.
  • I have searched for related issues and found none that matched my issue.

Overview

This is a subset of the feature request in #54, specifically for introducing a rule to allow for restricting dependency ranges, similar to restrict-ranges from eslint-plugin-json-files.

Proposing a rule named restrict-dependency-ranges with the following options (or an array of these options):

  • forDependencyTypes: ('dependencies' | 'devDependencies' | 'optionalDependences' | 'peerDependencies')[] - Apply these options to a specific type of dependencies
  • forPackages: string[] - Specific packages or regex patterns to apply these options to.
  • forVersions: string - Semver range to apply these options to (e.g. <1)
  • rangeType: 'caret' | 'tilde' | 'pin' | ('caret' | 'tilde' | 'pin)[] (required) - Specifies the type of range you want to enforce (^, ~, or no range) on the matched dependencies
@michaelfaith michaelfaith added type: feature New enhancement or request 🚀 status: in discussion Not yet ready for implementation or a pull request labels Mar 15, 2025
@michaelfaith
Copy link
Collaborator Author

@JoshuaKGoldberg would love your thoughts on this when you the time.

@JoshuaKGoldberg
Copy link
Owner

Ah thanks - super interesting. I like it. I also think we can iterate on the old restrict-ranges rules options in a few ways.

packages: string[] ... packageRegex: string

Seems to me that having both is a bit cluttered. Why have both? How about merging the two into packages: string[] allowing any number of regular expressions?

rangeType: 'caret' | 'tilde' | 'pin'

What if someone wants to allow 2 of 3, not just 1 of 3? Example: a team might be moving from one to the other, so those two are allowed, but the third has never been allowed and is still not. Proposal: maybe additionally allow an array of literals?

pinUnstable: boolean

This seems overly specific to me. The 0.x frame is indeed a pretty reasonable common use case based on semver. But what if a team is migrating from, say, a less-rigid 1.x set of internal deps to a more-rigid 2.x set? They might want to only enable pinning for 2.x. Or, heck, they might want other rulesets to apply as well - not just the pinning one.

Instead of this one specific behavior, how about a more general solution for targeting specific ranges? Maybe a forVersions field with a semver query, with making selector fields start with for*? Vaguely:

type RangeType = "caret" | "tilde" | "pin";

interface RangeRestriction {
  forPackages?: string | string[];
  forVersions?: string | string[];
  rangeType: RangeType | RangeType[];
  version?: string;
}
"restrict-ranges": ["error", {
    ranges: [
        // Pre-1.0 packages should be pinned to be careful
        {
            forPackages: '@example\/.*',
            forVersions: '<1',
            rangeType: 'pin'
        },
        // Once we hit 1.0, allow more permissive carets
        {
            forPackages: '@example\/.*',
            forVersions: '>=1',
            rangeType: 'caret'
        },
        // Also, don't upgrade (arbitrary packages for example) to 3.x
        {
            forPackages: ['@example-1\/.+', 'example-2'],
            version: '<3',
        }
    ]
}]

@michaelfaith
Copy link
Collaborator Author

michaelfaith commented Mar 18, 2025

I do like the direction you're going with this, and particularly the for* selector concept. Because I agree, the specificity of pinUnstable even prevented a very simple alternative like using ~ for "unstable", which is something I personally like to do. So, I'm aligned with that aspect of this proposal.

What i'm not quite clear on is what the intent of version is in the above. Are you thinking that should restrict matched packages to be within a particular version range? If so, I don't fully agree with that. Semantic version ranges defined in the package.json are responsible for restricting the versions that can be installed for packages. I wouldn't want to also control that as part of an eslint rule. This rule to me, controls what type of ranges can be used when specifying those ranges. I.e. you should always use a ^ unless the version is within this range, then use a ~, etc. Not controlling the actual version numbers that are allowed. That feels like you're then controlling that in two different places. I also can't really think of a real-world use case where I'd want to have that defined in an eslint rule.

And minor critique, I don't think ranges gives us anything, and I would just flatten that object to support both a single RangeRestriction object, or an array of those objects. Unless you think there are other properties outside of the range restrictions that you think we'd want to add.

@michaelfaith
Copy link
Collaborator Author

michaelfaith commented Mar 18, 2025

rangeType: 'caret' | 'tilde' | 'pin'

What if someone wants to allow 2 of 3, not just 1 of 3? Example: a team might be moving from one to the other, so those two are allowed, but the third has never been allowed and is still not. Proposal: maybe additionally allow an array of literals?

My thinking there was that they could just add another options object for that, but that could add unnecessary cruft. Seems reasonable to have it built in to support an array. 👍

@michaelfaith
Copy link
Collaborator Author

Updated the proposal in the original post

@JoshuaKGoldberg
Copy link
Owner

JoshuaKGoldberg commented Mar 19, 2025

what the intent of version is in the above ... can't really think of a real-world use case where I'd want to have that defined in an eslint rule.

Yeah, you're right. Agreed. It was confusing & unnecessary. Good call 😄.

I really like the updated proposal. 👍 from me!

@michaelfaith michaelfaith added status: accepting prs Please, send a pull request to resolve this! 🙏 and removed status: in discussion Not yet ready for implementation or a pull request labels Mar 19, 2025
@michaelfaith
Copy link
Collaborator Author

Great. I'll start looking at it this weekend. 🚀

@JoshuaKGoldberg
Copy link
Owner

@all-contributors please add @michaelfaith for ideas.

🤖 Beep boop! This comment was added automatically by all-contributors-auto-action.
Not all contributions can be detected from Git & GitHub alone. Please comment any missing contribution types this bot missed.
...and of course, thank you for contributing! 💙

Copy link
Contributor

@JoshuaKGoldberg

I've put up a pull request to add @michaelfaith! 🎉

JoshuaKGoldberg pushed a commit that referenced this issue Mar 20, 2025
Adds @michaelfaith as a contributor for ideas.

This was requested by JoshuaKGoldberg [in this
comment](#959 (comment))

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
@michaelfaith michaelfaith marked this as a duplicate of #1015 Apr 17, 2025
Copy link

🎉 This is included in version v0.30.0 🎉

The release is available on:

Cheers! 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: accepting prs Please, send a pull request to resolve this! 🙏 type: feature New enhancement or request 🚀
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants