Skip to content
Merged
89 changes: 63 additions & 26 deletions constraints.pro
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ repo_name(RepoUrl, RepoName) :-
RepoNameLength is End - Start,
sub_atom(RepoUrl, PrefixLength, RepoNameLength, SuffixLength, RepoName).

% True if DependencyIdent starts with '@metamask' and ends with '-controller'
% True if DependencyIdent starts with '@metamask' and ends with '-controller'.
is_controller(DependencyIdent) :-
Prefix = '@metamask/',
atom_length(Prefix, PrefixLength),
Expand Down Expand Up @@ -308,43 +308,80 @@ gen_enforced_dependency(WorkspaceCwd, DependencyIdent, 'a range optionally start
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
\+ is_valid_version_range(DependencyRange).

% All references to a workspace package must be up to date with the current
% version of that package.
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, CorrectDependencyRange, DependencyType) :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
workspace_ident(OtherWorkspaceCwd, DependencyIdent),
workspace_version(OtherWorkspaceCwd, OtherWorkspaceVersion),
atomic_list_concat(['^', OtherWorkspaceVersion], CorrectDependencyRange).

% All dependency ranges for a package must be synchronized across the monorepo
% (the least version range wins), regardless of which "*dependencies" field
% where the package appears.
% All version ranges used to reference one workspace package in another
% workspace package's `dependencies` or `devDependencies` must be the same.
% Among all references to the same dependency across the monorepo, the one with
% the smallest version range will win. (We handle `peerDependencies` in another
% constraint, as it has slightly different logic.)
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, OtherDependencyRange, DependencyType) :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, OtherDependencyRange, OtherDependencyType),
WorkspaceCwd \= OtherWorkspaceCwd,
DependencyRange \= OtherDependencyRange,
npm_version_range_out_of_sync(DependencyRange, OtherDependencyRange).
npm_version_range_out_of_sync(DependencyRange, OtherDependencyRange),
DependencyType \= 'peerDependencies',
OtherDependencyType \= 'peerDependencies'.

% If a dependency is listed under "dependencies", it should not be listed under
% "devDependencies". We match on the same dependency range so that if a
% dependency is listed under both lists, their versions are synchronized and
% then this constraint will apply and remove the "right" duplicate.
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, null, DependencyType) :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'dependencies'),
% All version ranges used to reference one workspace package in another
% workspace package's `dependencies` or `devDependencies` must match the current
% version of that package. (We handle `peerDependencies` in another rule.)
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, CorrectDependencyRange, DependencyType) :-
DependencyType \= 'peerDependencies',
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
DependencyType == 'devDependencies'.
workspace_ident(OtherWorkspaceCwd, DependencyIdent),
workspace_version(OtherWorkspaceCwd, OtherWorkspaceVersion),
atomic_list_concat(['^', OtherWorkspaceVersion], CorrectDependencyRange).

% If a controller dependency (other than `base-controller`, `eth-keyring-controller` and
% `polling-controller`) is listed under "dependencies", it should also be
% listed under "peerDependencies". Each controller is a singleton, so we need
% to ensure the versions used match expectations.
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'peerDependencies') :-
% If a workspace package is listed under another workspace package's
% `dependencies`, it should not also be listed under its `devDependencies`.
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, null, 'devDependencies') :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'dependencies').

% Each controller is a singleton, so we need to ensure the versions
% used match expectations. To accomplish this, if a controller (other than
% `base-controller`, `eth-keyring-controller` and `polling-controller`) is
% listed under a workspace package's `dependencies`, it should also be listed
% under its `peerDependencies`, and the major version of the peer dependency
% should match the major part of the current version dependency, with the minor
% and patch parts set to 0. If it is already listed there, then the major
% version should match the current version of the package and the minor and
% patch parts should be <= the corresponding parts.
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, CorrectPeerDependencyRange, 'peerDependencies') :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'dependencies'),
\+ workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, 'peerDependencies'),
is_controller(DependencyIdent),
DependencyIdent \= '@metamask/base-controller',
DependencyIdent \= '@metamask/eth-keyring-controller',
DependencyIdent \= '@metamask/polling-controller',
is_controller(DependencyIdent).
workspace_ident(DependencyWorkspaceCwd, DependencyIdent),
workspace_version(DependencyWorkspaceCwd, CurrentDependencyWorkspaceVersion),
parse_version_range(CurrentDependencyWorkspaceVersion, _, CurrentDependencyVersionMajor, _, _),
atomic_list_concat([CurrentDependencyVersionMajor, 0, 0], '.', CorrectPeerDependencyVersion),
atom_concat('^', CorrectPeerDependencyVersion, CorrectPeerDependencyRange).
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, CorrectPeerDependencyRange, 'peerDependencies') :-
workspace_has_dependency(WorkspaceCwd, DependencyIdent, SpecifiedPeerDependencyRange, 'peerDependencies'),
is_controller(DependencyIdent),
DependencyIdent \= '@metamask/base-controller',
DependencyIdent \= '@metamask/eth-keyring-controller',
DependencyIdent \= '@metamask/polling-controller',
workspace_ident(DependencyWorkspaceCwd, DependencyIdent),
workspace_version(DependencyWorkspaceCwd, CurrentDependencyVersion),
parse_version_range(CurrentDependencyVersion, _, CurrentDependencyVersionMajor, CurrentDependencyVersionMinor, CurrentDependencyVersionPatch),
parse_version_range(SpecifiedPeerDependencyRange, _, SpecifiedPeerDependencyVersionMajor, SpecifiedPeerDependencyVersionMinor, SpecifiedPeerDependencyVersionPatch),
(
(
SpecifiedPeerDependencyVersionMajor == CurrentDependencyVersionMajor,
(
SpecifiedPeerDependencyVersionMinor @< CurrentDependencyVersionMinor ;
(
SpecifiedPeerDependencyVersionMinor == CurrentDependencyVersionMinor,
SpecifiedPeerDependencyVersionPatch @=< CurrentDependencyVersionPatch
)
)
) ->
CorrectPeerDependencyRange = SpecifiedPeerDependencyRange ;
atom_concat('^', CurrentDependencyVersion, CorrectPeerDependencyRange)
).

% All packages must specify a minimum Node version of 16.
gen_enforced_field(WorkspaceCwd, 'engines.node', '>=16.0.0').
Expand Down