-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Constraints don't take devDependencies into account #11
Comments
I'd be willing to look into this! How far should this support for dev dependencies go? In my opinion only exposing them via
We could support all of these by adding a new parameter to the workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType).
gen_enforced_dependency_range(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType).
gen_invalid_dependency(WorkspaceCwd, DependencyIdent, DependencyType, Reason). The example rules above would then be % Dissallow packages in devDependencies if they're already in dependencies
gen_enforced_dependency_range(WorkspaceCwd, DependencyIdent, null, devDependencies):-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, dependencies).
% Enforce packages to be listed in devDependencies if listed in peerDependencies
gen_enforced_dependency_range(WorkspaceCwd, DependencyIdent, DependencyRange, devDependencies):-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, peerDependencies). |
I actually started looking into this over the weekend, and let's just say I went all the way down the rabbit-hole. I'm not sure where I should write this down so I'm posting it here, for now… An overview of why I'm interested: I've got a couple of repositories at work, all using So, long story short, I have created
I've succeeded in implementing constraints in two of our repositories at work, catching about twenty violations in the process. Most were missing devDependencies but a couple of violations were actual bugs. For reference, this is the constraints file I've been using so far: % Data data data
pin('@angular/cdk', Version, '^'):- root_dependency_version('@angular/cdk', Version).
pin('@angular/cli', Version, '^'):- root_dependency_version('@angular/cli', Version).
pin('@angular-devkit/schematics-cli', null, '~').
pin('@angular-devkit/architect', Version, '~'):- root_dependency_version('@angular-devkit/architect', Version).
pin('@angular-devkit/build-', Version, '~'):- root_dependency_version('@angular-devkit/architect', Version).
pin('@angular-devkit/', Version, '^'):- root_dependency_version('@angular/cli', Version).
pin('@angular', Version, '^'):- root_dependency_version('@angular/core', Version).
pin('rxjs', Version, '^'):- root_dependency_version('rxjs', Version).
pin('tslib', '1.9.3', '^').
require_dependency_type('tslib', dependencies).
% Helpers
root_dependency_version(DependencyName, DependencyVersion):-
root_package(PackageName),
package_has_dependency(PackageName, DependencyName, DependencyVersion, devDependencies), !.
version_matches(Range, Version):-
atom_concat('^', Version, Range), !.
version_matches(Range, Version):-
atom_concat('~', Version, Range), !.
version_matches(Range, Version):-
Range = Version.
find_pinned_version(PackageName, null, DependencyType):-
dependency_type(DependencyType),
pin(PinnedPackage, null, _),
sub_atom(PackageName, 0, _, _, PinnedPackage).
find_pinned_version(PackageName, Version, devDependencies):-
pin(PinnedPackage, PinnedVersion, _),
sub_atom(PackageName, 0, _, _, PinnedPackage),
Version = PinnedVersion.
find_pinned_version(PackageName, Version, DependencyType):-
dependency_type(DependencyType),
DependencyType \= devDependencies,
pin(PinnedPackage, PinnedVersion, PinnedPrefix),
sub_atom(PackageName, 0, _, _, PinnedPackage),
atom_concat(PinnedPrefix, PinnedVersion, Version).
% Our actual rules
% This rule prevents packages from depending on non-workspace versions of workspace packages
gen_enforced_dependency_range(PackageName, DependencyName, DependencyVersion, DependencyType) :-
package_has_dependency(PackageName, DependencyName, _, DependencyType),
package_version(DependencyName, DependencyVersion).
% This rule forces usage of the correct dependency type
gen_enforced_dependency_range(PackageName, DependencyName, Version, DependencyType) :-
require_dependency_type(DependencyName, DependencyType),
package_has_dependency(PackageName, DependencyName, Version, _).
% This rule requires peerDependencies to be listed as devDependency
gen_enforced_dependency_range(PackageName, DependencyName, DependencyRange, devDependencies) :-
package_has_dependency(PackageName, DependencyName, PeerDependencyRange, peerDependencies),
version_matches(PeerDependencyRange, DependencyRange).
% This rule requires use of correct versions
% it potentially requires ^ or ~ for runtime dependencies except the demo which must pin all versions
gen_enforced_dependency_range(PackageName, DependencyName, DependencyVersion, DependencyType) :-
\+(private_package(PackageName)),
package_has_dependency(PackageName, DependencyName, _, DependencyType),
once(find_pinned_version(DependencyName, PinnedVersion, DependencyType)),
PinnedVersion \== null,
DependencyVersion = PinnedVersion.
gen_enforced_dependency_range(PackageName, DependencyName, DependencyVersion, DependencyType) :-
private_package(PackageName),
package_has_dependency(PackageName, DependencyName, _, DependencyType),
once(find_pinned_version(DependencyName, PinnedVersion, devDependencies)),
PinnedVersion \== null,
DependencyVersion = PinnedVersion. |
I'm happy to hear that constraints solved actual problems in your repo. I'm pretty sure that after some refinement they can become one of the most powerful tools in the Yarn toolset 😃
I won't focus on this too much - I find it awesome that you went ahead and improved the process. That said, note that Berry is meant to be mostly backward compatible with Yarn (it uses the same workspace definition, for example, with less bugs), so I'd recommend giving it a try when you get the chance 🙂
Yep, that's how I'd do it as well. I agree that adding a predicate like
I'd advise against it - workspaces aren't required to have names. Similarly, two workspaces may have the same name but different versions (I admit it's a bit of a fringe case, but it may happen). The cwd is the only identifier that will be guaranteed to be unique across a project. |
I've been looking into it :) only one of our repository doesn't really require our private npm registries, so I threw away the yarn.lock, .yarnrc and .npmrc and gave it a go. I didn't get far, sadly, because of a lack of support for PnP in internal/external tooling. Berry for some reason also refuses to run the postinstall script of the main workspace. In any case stuff I need to look into when I find the time to do so
We could keep the old versions around, i.e. have gen_invalid_dependency(WorkspaceCwd, DependencyName, DependencyRange).
gen_invalid_dependency(WorkspaceCwd, DependencyName, DependencyRange, DependencyType). Although personally I wouldn't take that road, because it increases the complexity: we'd have four predicates to query and writers of constraints are more likely to get confused. Another option might be to create a "dependency record": % Require peerDependencies to also be present in devDependencies
gen_invalid_dependency(WorkspaceCwd, DependencyName, DependencyRecord):-
dependency_type(DependencyRecord, devDependencies),
workspace_has_dependency(WorkspaceCwd, DependencyName, FoundDependencyRecord),
dependency_type(FoundDependencyRecord, peerDependencies),
dependency_version(FoundDependencyRecord, VersionRange),
dependency_version(DependencyRecord, VersionRange).
% Disallow tslib as dependency, peerDependency and devDependency
gen_invalid_dependency(WorkspaceCwd, 'tslib', DependencyRecord):-
workspace_has_dependency(WorkspaceCwd, 'tslib', _),
dependency_version(DependencyRecord, null). That way, if you don't care about the type or the version, you don't have to include it anywhere. It doesn't make the prolog code shorter or easier to understand though. It also increases the number of predicates we'd need to provide.
Yarn v1 doesn't support multiple workspaces with the same name and it blatantly ignores workspaces without name in |
Woops, fixed in #34! 😄 Regarding
Well, I'm surprised - I sure thought it did! I completely forgot I added a check to protect against ... well, that changes things. Since this restriction has apparently never been a problem before, noone relies on it and I can discard it from Berry 🤔 |
Fixed with #71! 👍 |
Describe the bug
The constraint feature implemented by the
plugin-constraints
package doesn't insert devDependencies into the fact database, so they can't be matched by the rules.To Reproduce
package.json:
constraints.pro:
Running
yarn constraints check
should report an error (since the constraints don't allow any dependency to pass), but sincelodash
is declared as a devDependency nothing is reported.Additional context
The fix would be to change the signature of
workspace_has_dependency
in order to add the dependency type. This would also be useful for peer dependencies as well (and would allow to build rules such as "reject devDependency if the package also list a dependency of the same name").The text was updated successfully, but these errors were encountered: