7
7
*/
8
8
import { logging } from '@angular-devkit/core' ;
9
9
import {
10
- Rule , SchematicContext , SchematicsException , TaskId ,
10
+ Rule ,
11
+ SchematicContext ,
12
+ SchematicsException ,
13
+ TaskId ,
11
14
Tree ,
12
15
} from '@angular-devkit/schematics' ;
13
16
import { NodePackageInstallTask , RunSchematicTask } from '@angular-devkit/schematics/tasks' ;
@@ -19,7 +22,30 @@ import { NpmRepositoryPackageJson } from './npm-package-json';
19
22
import { JsonSchemaForNpmPackageJsonFiles } from './package-json' ;
20
23
import { UpdateSchema } from './schema' ;
21
24
22
- type VersionRange = string & { __ : void ; } ;
25
+ type VersionRange = string & { __VERSION_RANGE : void ; } ;
26
+ type PeerVersionTransform = string | ( ( range : string ) => string ) ;
27
+
28
+ // This is a map of packageGroupName to range extending function. If it isn't found, the range is
29
+ // kept the same.
30
+ // Angular guarantees that a major is compatible with its following major (so packages that depend
31
+ // on Angular 5 are also compatible with Angular 6). This is, in code, represented by verifying
32
+ // that all other packages that have a peer dependency of `"@angular/core": "^5.0.0"` actually
33
+ // supports 6.0, by adding that compatibility to the range, so it is `^5.0.0 || ^6.0.0`.
34
+ const peerCompatibleWhitelist : { [ name : string ] : PeerVersionTransform } = {
35
+ '@angular/core' : ( range : string ) => {
36
+ range = semver . validRange ( range ) ;
37
+ let major = 1 ;
38
+ while ( semver . ltr ( major + '.0.0' , range ) ) {
39
+ major ++ ;
40
+ if ( major >= 99 ) {
41
+ throw new SchematicsException ( `Invalid range: ${ JSON . stringify ( range ) } ` ) ;
42
+ }
43
+ }
44
+
45
+ // Add the major - 1 version as compatible with the angular compatible.
46
+ return semver . validRange ( `^${ major + 1 } .0.0-rc.0 || ${ range } ` ) || range ;
47
+ } ,
48
+ } ;
23
49
24
50
interface PackageVersionInfo {
25
51
version : VersionRange ;
@@ -41,6 +67,30 @@ interface UpdateMetadata {
41
67
migrations ?: string ;
42
68
}
43
69
70
+ function _updatePeerVersion ( infoMap : Map < string , PackageInfo > , name : string , range : string ) {
71
+ // Resolve packageGroupName.
72
+ const maybePackageInfo = infoMap . get ( name ) ;
73
+ if ( ! maybePackageInfo ) {
74
+ return range ;
75
+ }
76
+ if ( maybePackageInfo . target ) {
77
+ name = maybePackageInfo . target . updateMetadata . packageGroup [ 0 ] || name ;
78
+ } else {
79
+ name = maybePackageInfo . installed . updateMetadata . packageGroup [ 0 ] || name ;
80
+ }
81
+
82
+ const maybeTransform = peerCompatibleWhitelist [ name ] ;
83
+ if ( maybeTransform ) {
84
+ if ( typeof maybeTransform == 'function' ) {
85
+ return maybeTransform ( range ) ;
86
+ } else {
87
+ return maybeTransform ;
88
+ }
89
+ }
90
+
91
+ return range ;
92
+ }
93
+
44
94
function _validateForwardPeerDependencies (
45
95
name : string ,
46
96
infoMap : Map < string , PackageInfo > ,
@@ -90,13 +140,16 @@ function _validateReversePeerDependencies(
90
140
installedLogger . debug ( `${ installed } ...` ) ;
91
141
const peers = ( installedInfo . target || installedInfo . installed ) . packageJson . peerDependencies ;
92
142
93
- for ( const [ peer , range ] of Object . entries ( peers || { } ) ) {
143
+ for ( let [ peer , range ] of Object . entries ( peers || { } ) ) {
94
144
if ( peer != name ) {
95
145
// Only check peers to the packages we're updating. We don't care about peers
96
146
// that are unmet but we have no effect on.
97
147
continue ;
98
148
}
99
149
150
+ // Override the peer version range if it's whitelisted.
151
+ range = _updatePeerVersion ( infoMap , peer , range ) ;
152
+
100
153
if ( ! semver . satisfies ( version , range ) ) {
101
154
logger . error ( [
102
155
`Package ${ JSON . stringify ( installed ) } has an incompatible peer dependency to` ,
0 commit comments