1
1
import * as path from 'path' ;
2
2
import * as fs from 'fs' ;
3
3
import * as cp from 'child_process' ;
4
- import parseSemver from 'parse-semver' ;
5
- import { CancellationToken , log , nonnull } from './util' ;
4
+ import findWorkspaceRoot from 'find-yarn-workspace-root' ;
5
+ import { Manifest } from './manifest' ;
6
+ import { readNodeManifest } from './package' ;
7
+ import { CancellationToken , log } from './util' ;
6
8
7
9
const exists = ( file : string ) =>
8
10
fs . promises . stat ( file ) . then (
@@ -62,51 +64,67 @@ async function checkNPM(cancellationToken?: CancellationToken): Promise<void> {
62
64
}
63
65
}
64
66
65
- function getNpmDependencies ( cwd : string ) : Promise < string [ ] > {
67
+ function getNpmDependencies ( cwd : string ) : Promise < SourceAndDestination [ ] > {
66
68
return checkNPM ( )
67
69
. then ( ( ) =>
68
70
exec ( 'npm list --production --parseable --depth=99999 --loglevel=error' , { cwd, maxBuffer : 5000 * 1024 } )
69
71
)
70
- . then ( ( { stdout } ) => stdout . split ( / [ \r \n ] / ) . filter ( dir => path . isAbsolute ( dir ) ) ) ;
71
- }
72
-
73
- interface YarnTreeNode {
74
- name : string ;
75
- children : YarnTreeNode [ ] ;
72
+ . then ( ( { stdout } ) => stdout . split ( / [ \r \n ] / ) . filter ( dir => path . isAbsolute ( dir ) )
73
+ . map ( dir => {
74
+ return {
75
+ src : dir ,
76
+ dest : path . relative ( cwd , dir )
77
+ }
78
+ } ) ) ;
76
79
}
77
80
78
81
export interface YarnDependency {
79
82
name : string ;
80
- path : string ;
83
+ path : SourceAndDestination ;
81
84
children : YarnDependency [ ] ;
82
85
}
83
86
84
- function asYarnDependency ( prefix : string , tree : YarnTreeNode , prune : boolean ) : YarnDependency | null {
85
- if ( prune && / @ [ \^ ~ ] / . test ( tree . name ) ) {
86
- return null ;
87
- }
88
-
89
- let name : string ;
90
-
91
- try {
92
- const parseResult = parseSemver ( tree . name ) ;
93
- name = parseResult . name ;
94
- } catch ( err ) {
95
- name = tree . name . replace ( / ^ ( [ ^ @ + ] ) @ .* $ / , '$1' ) ;
96
- }
97
-
98
- const dependencyPath = path . join ( prefix , name ) ;
99
- const children : YarnDependency [ ] = [ ] ;
87
+ export interface SourceAndDestination {
88
+ src : string ;
89
+ dest : string ;
90
+ }
100
91
101
- for ( const child of tree . children || [ ] ) {
102
- const dep = asYarnDependency ( path . join ( prefix , name , 'node_modules' ) , child , prune ) ;
92
+ async function asYarnDependencies ( root : string , rootDependencies : string [ ] ) : Promise < YarnDependency [ ] > {
93
+ const resolve = async ( prefix : string , dependencies : string [ ] , collected : Map < string , YarnDependency > = new Map ( ) ) : Promise < YarnDependency [ ] > => await Promise . all ( dependencies
94
+ . map ( async ( name : string ) => {
95
+ let newPrefix = prefix , depPath = null , depManifest = null ;
96
+ while ( ! depManifest && root . length <= newPrefix . length ) {
97
+ depPath = path . join ( newPrefix , 'node_modules' , name ) ;
98
+ try {
99
+ depManifest = await readNodeManifest ( depPath ) ;
100
+ } catch ( err ) {
101
+ newPrefix = path . join ( newPrefix , '..' ) ;
102
+ if ( newPrefix . length < root . length ) {
103
+ throw err ;
104
+ }
105
+ }
106
+ }
103
107
104
- if ( dep ) {
105
- children . push ( dep ) ;
106
- }
107
- }
108
+ if ( ! depPath || ! depManifest ) {
109
+ throw new Error ( `Error finding dependencies` ) ;
110
+ }
108
111
109
- return { name, path : dependencyPath , children } ;
112
+ const result : YarnDependency = {
113
+ name,
114
+ path : {
115
+ src : depPath ,
116
+ dest : path . relative ( root , depPath ) ,
117
+ } ,
118
+ children : [ ] ,
119
+ } ;
120
+ const shouldResolveChildren = ! collected . has ( depPath ) ;
121
+ collected . set ( depPath , result ) ;
122
+ if ( shouldResolveChildren ) {
123
+ result . children = await resolve ( depPath , Object . keys ( depManifest . dependencies || { } ) , collected ) ;
124
+ }
125
+ return result ;
126
+ } ) ) ;
127
+ return resolve ( root , rootDependencies ) ;
110
128
}
111
129
112
130
function selectYarnDependencies ( deps : YarnDependency [ ] , packagedDependencies : string [ ] ) : YarnDependency [ ] {
@@ -154,26 +172,10 @@ function selectYarnDependencies(deps: YarnDependency[], packagedDependencies: st
154
172
return reached . values ;
155
173
}
156
174
157
- async function getYarnProductionDependencies ( cwd : string , packagedDependencies ?: string [ ] ) : Promise < YarnDependency [ ] > {
158
- const raw = await new Promise < string > ( ( c , e ) =>
159
- cp . exec (
160
- 'yarn list --prod --json' ,
161
- { cwd, encoding : 'utf8' , env : { ...process . env } , maxBuffer : 5000 * 1024 } ,
162
- ( err , stdout ) => ( err ? e ( err ) : c ( stdout ) )
163
- )
164
- ) ;
165
- const match = / ^ { " t y p e " : " t r e e " .* $ / m. exec ( raw ) ;
166
-
167
- if ( ! match || match . length !== 1 ) {
168
- throw new Error ( 'Could not parse result of `yarn list --json`' ) ;
169
- }
170
-
175
+ async function getYarnProductionDependencies ( root : string , manifest : Manifest , packagedDependencies ?: string [ ] ) : Promise < YarnDependency [ ] > {
171
176
const usingPackagedDependencies = Array . isArray ( packagedDependencies ) ;
172
- const trees = JSON . parse ( match [ 0 ] ) . data . trees as YarnTreeNode [ ] ;
173
177
174
- let result = trees
175
- . map ( tree => asYarnDependency ( path . join ( cwd , 'node_modules' ) , tree , ! usingPackagedDependencies ) )
176
- . filter ( nonnull ) ;
178
+ let result = await asYarnDependencies ( root , Object . keys ( manifest . dependencies || { } ) ) ;
177
179
178
180
if ( usingPackagedDependencies ) {
179
181
result = selectYarnDependencies ( result , packagedDependencies ! ) ;
@@ -182,22 +184,35 @@ async function getYarnProductionDependencies(cwd: string, packagedDependencies?:
182
184
return result ;
183
185
}
184
186
185
- async function getYarnDependencies ( cwd : string , packagedDependencies ?: string [ ] ) : Promise < string [ ] > {
186
- const result = new Set ( [ cwd ] ) ;
187
+ async function getYarnDependencies ( cwd : string , root : string , manifest : Manifest , packagedDependencies ?: string [ ] ) : Promise < SourceAndDestination [ ] > {
188
+ const result : SourceAndDestination [ ] = [ {
189
+ src : cwd ,
190
+ dest : ''
191
+ } ] ;
192
+
193
+ if ( await exists ( path . join ( root , 'yarn.lock' ) ) ) {
194
+ const deps = await getYarnProductionDependencies ( root , manifest , packagedDependencies ) ;
195
+ const flatten = ( dep : YarnDependency ) => {
196
+ result . push ( dep . path ) ;
197
+ dep . children . forEach ( flatten ) ;
198
+ } ;
199
+ deps . forEach ( flatten ) ;
200
+ }
187
201
188
- const deps = await getYarnProductionDependencies ( cwd , packagedDependencies ) ;
189
- const flatten = ( dep : YarnDependency ) => {
190
- result . add ( dep . path ) ;
191
- dep . children . forEach ( flatten ) ;
192
- } ;
193
- deps . forEach ( flatten ) ;
202
+ const dedup = new Map ( ) ;
194
203
195
- return [ ...result ] ;
204
+ for ( const item of result ) {
205
+ if ( ! dedup . has ( item . src ) ) {
206
+ dedup . set ( item . src , item ) ;
207
+ }
208
+ }
209
+
210
+ return [ ...dedup . values ( ) ] ;
196
211
}
197
212
198
- export async function detectYarn ( cwd : string ) : Promise < boolean > {
213
+ export async function detectYarn ( root : string ) {
199
214
for ( const name of [ 'yarn.lock' , '.yarnrc' , '.yarnrc.yaml' , '.pnp.cjs' , '.yarn' ] ) {
200
- if ( await exists ( path . join ( cwd , name ) ) ) {
215
+ if ( await exists ( path . join ( root , name ) ) ) {
201
216
if ( ! process . env [ 'VSCE_TESTS' ] ) {
202
217
log . info (
203
218
`Detected presence of ${ name } . Using 'yarn' instead of 'npm' (to override this pass '--no-yarn' on the command line).`
@@ -211,13 +226,16 @@ export async function detectYarn(cwd: string): Promise<boolean> {
211
226
212
227
export async function getDependencies (
213
228
cwd : string ,
229
+ manifest : Manifest ,
214
230
dependencies : 'npm' | 'yarn' | 'none' | undefined ,
215
231
packagedDependencies ?: string [ ]
216
- ) : Promise < string [ ] > {
232
+ ) : Promise < SourceAndDestination [ ] > {
233
+ const root = findWorkspaceRoot ( cwd ) || cwd ;
234
+
217
235
if ( dependencies === 'none' ) {
218
- return [ cwd ] ;
219
- } else if ( dependencies === 'yarn' || ( dependencies === undefined && ( await detectYarn ( cwd ) ) ) ) {
220
- return await getYarnDependencies ( cwd , packagedDependencies ) ;
236
+ return [ { src : root , dest : '' } ] ;
237
+ } else if ( dependencies === 'yarn' || ( dependencies === undefined && ( await detectYarn ( root ) ) ) ) {
238
+ return await getYarnDependencies ( cwd , root , manifest , packagedDependencies ) ;
221
239
} else {
222
240
return await getNpmDependencies ( cwd ) ;
223
241
}
0 commit comments