@@ -193,14 +193,39 @@ export class PnpmProjectDependencyManifest {
193193 // if it's been hoisted up as a top-level dependency
194194 const topLevelDependencySpecifier : DependencySpecifier | undefined =
195195 this . _pnpmShrinkwrapFile . getTopLevelDependencyVersion ( peerDependencyName ) ;
196+
197+ // Sometimes peer dependencies are hoisted but are not represented in the shrinkwrap file
198+ // (such as when implicitlyPreferredVersions is false) so we need to find the correct key
199+ // and add it ourselves
200+ if ( ! topLevelDependencySpecifier ) {
201+ const peerDependencyKeys : { [ peerDependencyName : string ] : string } =
202+ this . _parsePeerDependencyKeysFromSpecifier ( specifier ) ;
203+ if ( peerDependencyKeys . hasOwnProperty ( peerDependencyName ) ) {
204+ this . _addDependencyInternal (
205+ peerDependencyName ,
206+ peerDependencyKeys [ peerDependencyName ] ,
207+ shrinkwrapEntry
208+ ) ;
209+ continue ;
210+ }
211+ }
212+
196213 if ( ! topLevelDependencySpecifier || ! semver . valid ( topLevelDependencySpecifier . versionSpecifier ) ) {
197- const errorMessage : string =
198- `Could not find peer dependency '${ peerDependencyName } ' that satisfies version '${ dependencySemVer } '`
199- if ( this . _project . rushConfiguration . pnpmOptions && this . _project . rushConfiguration . pnpmOptions . strictPeerDependencies ) {
200- throw new InternalError ( errorMessage ) ;
214+ if (
215+ ! this . _project . rushConfiguration . pnpmOptions ||
216+ ! this . _project . rushConfiguration . pnpmOptions . strictPeerDependencies ||
217+ (
218+ shrinkwrapEntry . peerDependenciesMeta &&
219+ shrinkwrapEntry . peerDependenciesMeta . hasOwnProperty ( peerDependencyName ) &&
220+ shrinkwrapEntry . peerDependenciesMeta [ peerDependencyName ] . optional
221+ )
222+ ) {
223+ // We couldn't find the peer dependency, but we determined it's by design, skip this dependency...
224+ continue ;
201225 }
202- console . log ( `${ errorMessage } , skipping...` ) ;
203- continue ;
226+ throw new InternalError (
227+ `Could not find peer dependency '${ peerDependencyName } ' that satisfies version '${ dependencySemVer } '`
228+ ) ;
204229 }
205230
206231 this . _addDependencyInternal (
@@ -211,4 +236,32 @@ export class PnpmProjectDependencyManifest {
211236 }
212237 }
213238 }
239+
240+ /**
241+ * The version specifier for a dependency can sometimes come in the form of
242+ * '{semVer}_peerDep1@1.2.3+peerDep2@4.5.6'. This is parsed and returned as a dictionary mapping
243+ * the peer dependency to it's appropriate PNPM dependency key.
244+ */
245+ private _parsePeerDependencyKeysFromSpecifier ( specifier : string ) : { [ peerDependencyName : string ] : string } {
246+ const parsedPeerDependencyKeys : { [ peerDependencyName : string ] : string } = { } ;
247+
248+ const specifierMatches : RegExpExecArray | null = / ^ [ ^ _ ] + _ ( .+ ) $ / . exec ( specifier ) ;
249+ if ( specifierMatches ) {
250+ const combinedPeerDependencies : string = specifierMatches [ 1 ] ;
251+ // Parse "eslint@6.6.0+typescript@3.6.4" --> ["eslint@6.6.0", "typescript@3.6.4"]
252+ const peerDependencies : string [ ] = combinedPeerDependencies . split ( "+" ) ;
253+ for ( const peerDependencySpecifier of peerDependencies ) {
254+ // Parse "eslint@6.6.0" --> "eslint", "6.6.0"
255+ const peerMatches : RegExpExecArray | null = / ^ ( [ ^ + @ ] + ) @ ( .+ ) $ / . exec ( peerDependencySpecifier ) ;
256+ if ( peerMatches ) {
257+ const peerDependencyName : string = peerMatches [ 1 ] ;
258+ const peerDependencyVersion : string = peerMatches [ 2 ] ;
259+ const peerDependencyKey : string = `/${ peerDependencyName } /${ peerDependencyVersion } ` ;
260+ parsedPeerDependencyKeys [ peerDependencyName ] = peerDependencyKey ;
261+ }
262+ }
263+ }
264+
265+ return parsedPeerDependencyKeys ;
266+ }
214267}
0 commit comments