1
1
import path from 'path' ;
2
2
import fs from 'fs-extra' ;
3
3
import { getLoader , logger } from '@react-native-community/cli-tools' ;
4
+ import * as fetch from 'npm-registry-fetch' ;
4
5
import chalk from 'chalk' ;
5
6
import { prompt } from 'prompts' ;
6
7
import execa from 'execa' ;
8
+ import semver from 'semver' ;
9
+ import { isProjectUsingYarn } from './yarn' ;
10
+ import { installPods } from '@react-native-community/cli-doctor' ;
7
11
8
12
interface DependencyInfo {
9
13
path : string ;
10
14
peerDependencies : { [ key : string ] : string } ;
11
15
}
12
16
13
- function isUsingYarn ( root : string ) {
14
- return fs . existsSync ( path . join ( root , 'yarn.lock' ) ) ;
17
+ async function fetchAvailableVersions ( packageName : string ) : Promise < string [ ] > {
18
+ const response = await fetch . json ( `/${ packageName } ` ) ;
19
+
20
+ return Object . keys ( response . versions || { } ) ;
21
+ }
22
+
23
+ async function calculateWorkingVersion (
24
+ ranges : string [ ] ,
25
+ availableVersions : string [ ] ,
26
+ ) : Promise < string | null > {
27
+ const sortedVersions = availableVersions
28
+ . filter ( ( version ) =>
29
+ ranges . every ( ( range ) => semver . satisfies ( version , range ) ) ,
30
+ )
31
+ . sort ( semver . rcompare ) ;
32
+
33
+ return sortedVersions . length > 0 ? sortedVersions [ 0 ] : null ;
15
34
}
16
35
17
36
function getPeerDependencies (
@@ -61,21 +80,19 @@ function getPeerDependencies(
61
80
function excludeInstalledPeerDependencies (
62
81
root : string ,
63
82
peerDependencies : Map < string , DependencyInfo > ,
64
- yarn = true ,
83
+ packageJson : any ,
65
84
) {
66
- const packageJson = require ( path . join ( root , 'package.json' ) ) ;
67
85
const missingPeerDependencies : Record < string , Record < string , string > > = { } ;
68
86
peerDependencies . forEach ( ( value , key ) => {
69
87
const missingDeps = Object . entries ( value . peerDependencies ) . reduce (
70
88
( missingDepsList , [ name , version ] ) => {
71
89
const rootPath = path . join ( root , 'node_modules' , name ) ;
72
90
if (
73
- ( yarn && ! fs . existsSync ( rootPath ) ) ||
74
- ( ! yarn &&
75
- ( fs . existsSync ( path . join ( rootPath , 'ios' ) ) ||
76
- fs . existsSync ( path . join ( rootPath , 'android' ) ) ) &&
77
- ! Object . keys ( packageJson . dependencies ) . includes ( name ) )
91
+ ( fs . existsSync ( path . join ( rootPath , 'ios' ) ) ||
92
+ fs . existsSync ( path . join ( rootPath , 'android' ) ) ) &&
93
+ ! Object . keys ( packageJson . dependencies ) . includes ( name )
78
94
) {
95
+ //@ts -ignore
79
96
missingDepsList [ name ] = version ;
80
97
}
81
98
return missingDepsList ;
@@ -93,56 +110,110 @@ function excludeInstalledPeerDependencies(
93
110
94
111
export default async function installTransitiveDeps ( ) {
95
112
const root = process . cwd ( ) ;
96
- const packageJsonPath = path . join ( root , 'package.json' ) ;
97
- const packageJson = require ( packageJsonPath ) ;
98
- const isYarn = isUsingYarn ( root ) ;
99
- const peerDependencies = getPeerDependencies ( root , packageJson ) ;
100
- const depsToInstall = excludeInstalledPeerDependencies (
101
- root ,
102
- peerDependencies ,
103
- isYarn ,
104
- ) ;
105
-
106
- const dependenciesWithMissingDeps = Object . keys ( depsToInstall ) ;
107
- if ( dependenciesWithMissingDeps . length > 0 ) {
108
- logger . warn (
109
- 'Looks like you are missing some of the peer dependencies of your libraries:\n' ,
113
+ const isYarn = ! ! isProjectUsingYarn ( root ) ;
114
+
115
+ let newDependenciesFound = true ;
116
+
117
+ while ( newDependenciesFound ) {
118
+ const packageJsonPath = path . join ( root , 'package.json' ) ;
119
+ const packageJson = JSON . parse (
120
+ fs . readFileSync ( packageJsonPath , { encoding : 'utf8' } ) ,
110
121
) ;
111
- logger . log (
112
- dependenciesWithMissingDeps
113
- . map (
114
- ( dep ) =>
115
- `\t${ chalk . bold ( dep ) } :\n ${ Object . keys ( depsToInstall [ dep ] ) . map (
116
- ( d ) => `\t- ${ d } \n` ,
117
- ) } `,
118
- )
119
- . join ( '\n' )
120
- . replace ( / , / g, '' ) ,
122
+ const peerDependencies = getPeerDependencies ( root , packageJson ) ;
123
+ const depsToInstall = excludeInstalledPeerDependencies (
124
+ root ,
125
+ peerDependencies ,
126
+ packageJson ,
121
127
) ;
122
- const { install} = await prompt ( {
123
- type : 'confirm' ,
124
- name : 'install' ,
125
- message :
126
- 'Do you want to install them now? The matching versions will be added as project dependencies and become visible for autolinking.' ,
127
- } ) ;
128
- const loader = getLoader ( { text : 'Installing peer dependencies...' } ) ;
129
-
130
- if ( install ) {
131
- let deps = new Set ( ) ;
132
- dependenciesWithMissingDeps . map ( ( dep ) => {
133
- const missingDeps = depsToInstall [ dep ] ;
134
- deps . add ( Object . keys ( missingDeps ) ) ;
128
+ const dependenciesWithMissingDeps = Object . keys ( depsToInstall ) ;
129
+
130
+ if ( dependenciesWithMissingDeps . length > 0 ) {
131
+ logger . warn (
132
+ 'Looks like you are missing some of the peer dependencies of your libraries:\n' ,
133
+ ) ;
134
+ logger . log (
135
+ dependenciesWithMissingDeps
136
+ . map (
137
+ ( dep ) =>
138
+ `\t${ chalk . bold ( dep ) } :\n ${ Object . keys ( depsToInstall [ dep ] ) . map (
139
+ ( d ) => `\t- ${ d } \n` ,
140
+ ) } `,
141
+ )
142
+ . join ( '\n' )
143
+ . replace ( / , / g, '' ) ,
144
+ ) ;
145
+
146
+ const packageToRanges : { [ pkg : string ] : string [ ] } = { } ;
147
+
148
+ for ( const dependency in depsToInstall ) {
149
+ const packages = depsToInstall [ dependency ] ;
150
+
151
+ for ( const packageName in packages ) {
152
+ if ( ! packageToRanges [ packageName ] ) {
153
+ packageToRanges [ packageName ] = [ ] ;
154
+ }
155
+ packageToRanges [ packageName ] . push ( packages [ packageName ] ) ;
156
+ }
157
+ }
158
+
159
+ const workingVersions : { [ pkg : string ] : string | null } = { } ;
160
+
161
+ for ( const packageName in packageToRanges ) {
162
+ const ranges = packageToRanges [ packageName ] ;
163
+ const availableVersions = await fetchAvailableVersions ( packageName ) ;
164
+ const workingVersion = await calculateWorkingVersion (
165
+ ranges ,
166
+ availableVersions ,
167
+ ) ;
168
+ workingVersions [ packageName ] = workingVersion ;
169
+ }
170
+
171
+ const { install} = await prompt ( {
172
+ type : 'confirm' ,
173
+ name : 'install' ,
174
+ message :
175
+ 'Do you want to install them now? The matching versions will be added as project dependencies and become visible for autolinking.' ,
135
176
} ) ;
136
- const arr = Array . from ( deps ) as string [ ] ;
137
- const flat = [ ] . concat ( ...arr ) ;
138
- loader . start ( ) ;
177
+ const loader = getLoader ( { text : 'Installing peer dependencies...' } ) ;
178
+
179
+ if ( install ) {
180
+ const arr = Object . entries ( workingVersions ) . map (
181
+ ( [ name , version ] ) => `${ name } @^${ version } ` ,
182
+ ) ;
183
+ //@ts -ignore
184
+ const flat = [ ] . concat ( ...arr ) ;
185
+
186
+ loader . start ( ) ;
139
187
140
- if ( isYarn ) {
141
- execa . sync ( 'yarn' , [ 'add' , ...flat . map ( ( dep ) => dep ) ] ) ;
188
+ if ( isYarn ) {
189
+ execa . sync ( 'yarn' , [ 'add' , ...flat . map ( ( dep ) => dep ) ] ) ;
190
+ } else {
191
+ execa . sync ( 'npm' , [ 'install' , ...flat . map ( ( dep ) => dep ) ] ) ;
192
+ }
193
+ loader . succeed ( ) ;
142
194
} else {
143
- execa . sync ( 'npm' , [ 'install' , ... flat . map ( ( dep ) => dep ) ] ) ;
195
+ newDependenciesFound = false ;
144
196
}
145
- loader . succeed ( ) ;
197
+ } else {
198
+ newDependenciesFound = false ;
146
199
}
147
200
}
201
+
202
+ return ! newDependenciesFound ;
203
+ }
204
+
205
+ export async function resolvePodsInstallation ( ) {
206
+ const { install} = await prompt ( {
207
+ type : 'confirm' ,
208
+ name : 'install' ,
209
+ message :
210
+ 'Do you want to install pods? This will make sure your transitive dependencies are linked properly.' ,
211
+ } ) ;
212
+
213
+ if ( install && process . platform === 'darwin' ) {
214
+ const loader = getLoader ( { text : 'Installing pods...' } ) ;
215
+ loader . start ( ) ;
216
+ await installPods ( loader ) ;
217
+ loader . succeed ( ) ;
218
+ }
148
219
}
0 commit comments