@@ -12,147 +12,148 @@ public function __construct(
1212
1313 public function computeRootMerge (string $ projectRoot , array $ packagePaths , array $ options = []): array
1414 {
15-
16- $ opts = [
17- 'strategy_require ' => (string ) ($ options ['strategy_require ' ] ?? 'union-caret ' ),
18- 'strategy_require_dev ' => (string ) ($ options ['strategy_require-dev ' ] ?? 'union-caret ' ),
15+ $ normalizedOptions = [
16+ 'strategy_require ' => (string ) ($ options ['strategy_require ' ] ?? 'union-caret ' ),
17+ 'strategy_require_dev ' => (string ) ($ options ['strategy_require-dev ' ] ?? 'union-caret ' ),
1918 'exclude_monorepo_packages ' => (bool ) ($ options ['exclude_monorepo_packages ' ] ?? true ),
20- 'monorepo_names ' => (array ) ($ options ['monorepo_names ' ] ?? []),
19+ 'monorepo_names ' => (array ) ($ options ['monorepo_names ' ] ?? []),
2120 ];
2221
23- $ monorepo = array_map ('strtolower ' , array_values ($ opts ['monorepo_names ' ]));
22+ $ monorepoNames = array_map ('strtolower ' , array_values ($ normalizedOptions ['monorepo_names ' ]));
2423
25- $ reqs = [];
26- $ devs = [];
27- $ byDepConstraints = [
28- 'require ' => [],
24+ $ requiredDependencies = [];
25+ $ devDependencies = [];
26+ $ constraintsByDependency = [
27+ 'require ' => [],
2928 'require-dev ' => [],
3029 ];
3130
32- foreach ($ packagePaths as $ relPath ) {
33- $ pc = $ this ->reader ->read (rtrim ($ projectRoot , '/ ' ) . '/ ' . $ relPath . '/composer.json ' );
34- if ($ pc === []) {
31+ foreach ($ packagePaths as $ relativePath ) {
32+ $ composerJson = $ this ->reader ->read (rtrim ($ projectRoot , '/ ' ) . '/ ' . $ relativePath . '/composer.json ' );
33+ if ($ composerJson === []) {
3534 continue ;
3635 }
3736
38- $ name = strtolower ((string ) ($ pc ['name ' ] ?? $ relPath ));
39- foreach ((array ) ($ pc ['require ' ] ?? []) as $ dep => $ ver ) {
40- if (!is_string ($ dep )) {
37+ $ packageName = strtolower ((string ) ($ composerJson ['name ' ] ?? $ relativePath ));
38+ foreach ((array ) ($ composerJson ['require ' ] ?? []) as $ dependency => $ version ) {
39+ if (!is_string ($ dependency ) || ! is_string ( $ version )) {
4140 continue ;
4241 }
4342
44- if (! is_string ( $ ver )) {
43+ if ($ normalizedOptions [ ' exclude_monorepo_packages ' ] && in_array ( strtolower ( $ dependency ), $ monorepoNames , true )) {
4544 continue ;
4645 }
4746
48- if ($ opts ['exclude_monorepo_packages ' ] && in_array (strtolower ($ dep ), $ monorepo , true )) {
49- continue ;
50- }
51-
52- $ byDepConstraints ['require ' ][$ dep ][$ name ] = $ ver ;
47+ $ constraintsByDependency ['require ' ][$ dependency ][$ packageName ] = $ version ;
5348 }
5449
55- foreach ((array ) ($ pc ['require-dev ' ] ?? []) as $ dep => $ ver ) {
56- if (!is_string ($ dep )) {
50+ foreach ((array ) ($ composerJson ['require-dev ' ] ?? []) as $ dependency => $ version ) {
51+ if (!is_string ($ dependency ) || ! is_string ( $ version )) {
5752 continue ;
5853 }
5954
60- if (! is_string ( $ ver )) {
55+ if ($ normalizedOptions [ ' exclude_monorepo_packages ' ] && in_array ( strtolower ( $ dependency ), $ monorepoNames , true )) {
6156 continue ;
6257 }
6358
64- if ($ opts ['exclude_monorepo_packages ' ] && in_array (strtolower ($ dep ), $ monorepo , true )) {
65- continue ;
66- }
67-
68- $ byDepConstraints ['require-dev ' ][$ dep ][$ name ] = $ ver ;
59+ $ constraintsByDependency ['require-dev ' ][$ dependency ][$ packageName ] = $ version ;
6960 }
7061 }
7162
7263 $ conflicts = [];
73- $ reqs = $ this ->mergeMap ($ byDepConstraints ['require ' ], $ opts ['strategy_require ' ], $ conflicts );
74- $ devs = $ this ->mergeMap ($ byDepConstraints ['require-dev ' ], $ opts ['strategy_require_dev ' ], $ conflicts );
64+ $ requiredDependencies = $ this ->mergeMap ($ constraintsByDependency ['require ' ], $ normalizedOptions ['strategy_require ' ], $ conflicts );
65+ $ devDependencies = $ this ->mergeMap ($ constraintsByDependency ['require-dev ' ], $ normalizedOptions ['strategy_require_dev ' ], $ conflicts );
7566
76- ksort ($ reqs );
77- ksort ($ devs );
67+ ksort ($ requiredDependencies );
68+ ksort ($ devDependencies );
7869
7970 return [
80- 'require ' => $ reqs ,
81- 'require-dev ' => $ devs ,
71+ 'require ' => $ requiredDependencies ,
72+ 'require-dev ' => $ devDependencies ,
8273 'conflicts ' => array_values ($ conflicts ),
8374 ];
8475 }
8576
8677 /**
87- * @param array<string,array<string,string>> $constraintsPerDep
78+ * @param array<string,array<string,string>> $constraintsPerDependency
8879 * @param array<string,array<string,mixed>> $conflictsOut
8980 * @return array<string,string>
9081 */
91- private function mergeMap (array $ constraintsPerDep , string $ strategy , array &$ conflictsOut ): array
82+ private function mergeMap (array $ constraintsPerDependency , string $ strategy , array &$ conflictsOut ): array
9283 {
93- $ out = [];
94- foreach ($ constraintsPerDep as $ dep => $ byPkg ) {
95- $ constraint = $ this ->chooseConstraint (array_values ($ byPkg ), $ strategy , $ dep , $ byPkg , $ conflictsOut );
84+ $ mergedConstraints = [];
85+ foreach ($ constraintsPerDependency as $ dependency => $ versionsByPackage ) {
86+ $ constraint = $ this ->chooseConstraint (
87+ array_values ($ versionsByPackage ),
88+ $ strategy ,
89+ $ dependency ,
90+ $ versionsByPackage ,
91+ $ conflictsOut
92+ );
9693 if ($ constraint !== null ) {
97- $ out [ $ dep ] = $ constraint ;
94+ $ mergedConstraints [ $ dependency ] = $ constraint ;
9895 }
9996 }
10097
101- return $ out ;
98+ return $ mergedConstraints ;
10299 }
103100
104101 /**
105102 * @param list<string> $constraints
106- * @param array<string,string> $byPkg
103+ * @param array<string,string> $versionsByPackage
107104 */
108- private function chooseConstraint (array $ constraints , string $ strategy , string $ dep , array $ byPkg , array &$ conflictsOut ): ?string
105+ private function chooseConstraint (array $ constraints , string $ strategy , string $ dependency , array $ versionsByPackage , array &$ conflictsOut ): ?string
109106 {
110107 $ strategy = strtolower ($ strategy );
111- $ norm = array_map ([$ this ,'normalizeConstraint ' ], array_filter ($ constraints , 'is_string ' ));
112- if ($ norm === []) {
108+ $ normalized = array_map ([$ this ,'normalizeConstraint ' ], array_filter ($ constraints , 'is_string ' ));
109+ if ($ normalized === []) {
113110 return null ;
114111 }
115112
116113 if ($ strategy === 'union-caret ' ) {
117- return $ this ->chooseUnionCaret ($ norm , $ dep , $ byPkg , $ conflictsOut );
114+ return $ this ->chooseUnionCaret ($ normalized , $ dependency , $ versionsByPackage , $ conflictsOut );
118115 }
119116
120117 if ($ strategy === 'union-loose ' ) {
121118 return '* ' ;
122119 }
123120
124121 if ($ strategy === 'max ' ) {
125- return $ this ->maxLowerBound ($ norm );
122+ return $ this ->maxLowerBound ($ normalized );
126123 }
127124
128125 if ($ strategy === 'intersect ' ) {
129126 // naive: if all share same major series, pick max lower bound; else conflict
130- $ majors = array_unique (array_map (static fn ($ c ): int => $ c ['major ' ], $ norm ));
131- if (count ($ majors ) > 1 ) {
132- $ this ->recordConflict ($ dep , $ byPkg , $ conflictsOut , 'intersect-empty ' );
127+ $ majorVersions = array_unique (array_map (static fn ($ c ): int => $ c ['major ' ], $ normalized ));
128+ if (count ($ majorVersions ) > 1 ) {
129+ $ this ->recordConflict ($ dependency , $ versionsByPackage , $ conflictsOut , 'intersect-empty ' );
133130 return null ;
134131 }
135132
136- return $ this ->maxLowerBound ($ norm );
133+ return $ this ->maxLowerBound ($ normalized );
137134 }
138135
139136 // default fallback
140- return $ this ->chooseUnionCaret ($ norm , $ dep , $ byPkg , $ conflictsOut );
137+ return $ this ->chooseUnionCaret ($ normalized , $ dependency , $ versionsByPackage , $ conflictsOut );
141138 }
142139
143140 /** @param list<array{raw:string,major:int,minor:int,patch:int,type:string}> $norm */
144- private function chooseUnionCaret (array $ norm , string $ dep , array $ byPkg , array &$ conflictsOut ): string
141+ private function chooseUnionCaret (array $ norm , string $ dependency , array $ versionsByPackage , array &$ conflictsOut ): string
145142 {
146143 // Prefer highest ^MAJOR.MINOR; if any non-caret constraints exist, record a conflict and still pick a sane default.
147144 $ caret = array_values (array_filter ($ norm , static fn ($ c ): bool => $ c ['type ' ] === 'caret ' ));
148145 if ($ caret !== []) {
149146 usort ($ caret , [$ this ,'cmpSemver ' ]);
150147 $ best = end ($ caret );
148+ if (count ($ caret ) !== count ($ norm )) {
149+ $ this ->recordConflict ($ dependency , $ versionsByPackage , $ conflictsOut , 'non-caret-mixed ' );
150+ }
151+
151152 return '^ ' . $ best ['major ' ] . '. ' . $ best ['minor ' ];
152153 }
153154
154155 // If exact pins or ranges exist, pick the "max lower bound" and record conflict
155- $ this ->recordConflict ($ dep , $ byPkg , $ conflictsOut , 'non-caret-mixed ' );
156+ $ this ->recordConflict ($ dependency , $ versionsByPackage , $ conflictsOut , 'non-caret-mixed ' );
156157 return $ this ->maxLowerBound ($ norm );
157158 }
158159
@@ -169,13 +170,13 @@ private function maxLowerBound(array $norm): string
169170 return $ best ['raw ' ];
170171 }
171172
172- /** @param array<string,string> $byPkg */
173- private function recordConflict (string $ dep , array $ byPkg , array &$ conflictsOut , string $ reason ): void
173+ /** @param array<string,string> $versionsByPackage */
174+ private function recordConflict (string $ dependency , array $ versionsByPackage , array &$ conflictsOut , string $ reason ): void
174175 {
175- $ conflictsOut [$ dep ] = [
176- 'package ' => $ dep ,
177- 'versions ' => array_values (array_unique (array_values ($ byPkg ))),
178- 'packages ' => array_keys ($ byPkg ),
176+ $ conflictsOut [$ dependency ] = [
177+ 'package ' => $ dependency ,
178+ 'versions ' => array_values (array_unique (array_values ($ versionsByPackage ))),
179+ 'packages ' => array_keys ($ versionsByPackage ),
179180 'reason ' => $ reason ,
180181 ];
181182 }
0 commit comments