1
1
const { resolve, relative, sep } = require ( 'node:path' )
2
2
const archy = require ( 'archy' )
3
3
const npa = require ( 'npm-package-arg' )
4
- const { output, log } = require ( 'proc-log' )
4
+ const { output } = require ( 'proc-log' )
5
5
const ArboristWorkspaceCmd = require ( '../arborist-cmd.js' )
6
6
const localeCompare = require ( '@isaacs/string-locale-compare' ) ( 'en' )
7
7
@@ -19,72 +19,6 @@ const _problems = Symbol('problems')
19
19
const _required = Symbol ( 'required' )
20
20
const _type = Symbol ( 'type' )
21
21
22
- function bfs ( tree , visit , getChildren ) {
23
- const queue = [ tree ]
24
- let finalResult = null
25
- const seenNodes = new Map ( )
26
- const problems = new Set ( )
27
-
28
- seenNodes . set ( tree . path , tree )
29
- while ( queue . length > 0 ) {
30
- const node = queue . shift ( )
31
-
32
- const result = visit ( node , problems )
33
- if ( ! finalResult ) {
34
- finalResult = result
35
- }
36
- queue . push ( ...getChildren ( node , result , seenNodes ) )
37
- }
38
- return [ finalResult , seenNodes , problems ]
39
- }
40
-
41
- const deepCopyNodes = ( nodes ) => {
42
- return nodes . map ( child => {
43
- const copiedChild = { ...child }
44
- if ( child . nodes && Array . isArray ( child . nodes ) ) {
45
- copiedChild . nodes = deepCopyNodes ( child . nodes )
46
- }
47
- return copiedChild
48
- } )
49
- }
50
-
51
- function dfs (
52
- node ,
53
- getChildren ,
54
- visit ,
55
- seenNodes = new Map ( ) ,
56
- problems = new Set ( ) ,
57
- cache = new Map ( ) ,
58
- encounterCount = new Map ( )
59
- ) {
60
- // Track the number of encounters for the current node
61
- // Why because we want to start storing after the node is identified as a deduped edge
62
- const count = ( encounterCount . get ( node . path ) || 0 ) + 1
63
- encounterCount . set ( node . path , count )
64
-
65
- // Start caching only after the third encounter
66
- if ( cache . has ( node . path ) ) {
67
- return [ cache . get ( node . path ) , seenNodes , problems ]
68
- }
69
-
70
- const result = visit ( node , problems )
71
- if ( count > 1 ) {
72
- cache . set ( node . path , result )
73
- }
74
-
75
- // Get children of current node
76
- const children = getChildren ( node , result , seenNodes )
77
-
78
- // Recurse on each child
79
- for ( const child of children ) {
80
- const [ cResult ] = dfs ( child , getChildren , visit , seenNodes , problems , cache , encounterCount )
81
- result [ _include ] = result [ _include ] || cResult [ _include ]
82
- cResult [ _include ] && result . nodes . push ( cResult )
83
- }
84
-
85
- return [ result , seenNodes , problems ]
86
- }
87
-
88
22
class LS extends ArboristWorkspaceCmd {
89
23
static description = 'List installed packages'
90
24
static name = 'ls'
@@ -170,15 +104,20 @@ class LS extends ArboristWorkspaceCmd {
170
104
return true
171
105
}
172
106
173
- const getChildren = ( node , nodeResult , seenNodes ) => {
107
+ const getChildren = ( node , nodeResult , seenNodes , traversePathMap ) => {
174
108
const seenPaths = new Set ( )
175
109
const workspace = node . isWorkspace
176
110
const currentDepth = workspace ? 0 : node [ _depth ]
177
111
const target = ( node . target ) ?. edgesOut
178
112
113
+ const traversePath = [ ...( traversePathMap . get ( nodeResult [ _parent ] ) || [ ] ) ]
114
+ const isCircular = traversePath ?. includes ( node . realpath + '-' + node . pkgid )
115
+ traversePath . push ( node . realpath + '-' + node . pkgid )
116
+ traversePathMap . set ( nodeResult , traversePath )
117
+
179
118
const shouldSkipChildren =
180
- ( currentDepth > depthToPrint )
181
- || nodeResult ?. isCircular
119
+ ( currentDepth > depthToPrint ) || ! nodeResult
120
+ || isCircular
182
121
183
122
return ( shouldSkipChildren || ! target )
184
123
? [ ]
@@ -215,12 +154,6 @@ class LS extends ArboristWorkspaceCmd {
215
154
}
216
155
}
217
156
218
- item . traversePath = ( node [ _parent ] ?. traversePath || [ ] ) . concat ( [ node . realpath + '-' + node . pkgid ] )
219
-
220
- if ( node [ _parent ] ?. traversePath ?. includes ( node . realpath + '-' + node . pkgid ) ) {
221
- item . isCircular = true
222
- }
223
-
224
157
// return a promise so we don't blow the stack
225
158
return item
226
159
}
@@ -232,7 +165,23 @@ class LS extends ArboristWorkspaceCmd {
232
165
233
166
// add root node of tree to list of seenNodes
234
167
235
- const [ result , seenNodes , problems ] = dfs ( tree , getChildren , visit )
168
+ const seenNodes = new Map ( )
169
+ const problems = new Set ( )
170
+ const cache = new Map ( )
171
+ const traversePathMap = new Map ( )
172
+
173
+ seenNodes . set ( tree . path , tree )
174
+
175
+ const result = exploreDependencyGraph (
176
+ tree ,
177
+ getChildren ,
178
+ visit ,
179
+ { json, parseable } ,
180
+ seenNodes ,
181
+ problems ,
182
+ cache ,
183
+ traversePathMap
184
+ )
236
185
237
186
// handle the special case of a broken package.json in the root folder
238
187
const [ rootError ] = tree . errors . filter ( e =>
@@ -286,6 +235,61 @@ class LS extends ArboristWorkspaceCmd {
286
235
287
236
module . exports = LS
288
237
238
+ const exploreDependencyGraph = (
239
+ node ,
240
+ getChildren ,
241
+ visit ,
242
+ { json, parseable } ,
243
+ seenNodes ,
244
+ problems ,
245
+ cache ,
246
+ traversePathMap ,
247
+ encounterCount = new Map ( )
248
+ ) => {
249
+ // Track the number of encounters for the current node
250
+ // Why because we want to start storing/caching after the node is identified as a deduped edge
251
+ const count = node . path ? ( encounterCount . get ( node . path ) || 0 ) + 1 : 0
252
+ node . path && encounterCount . set ( node . path , count )
253
+
254
+ if ( node . path && cache . has ( node . path ) ) {
255
+ return cache . get ( node . path )
256
+ }
257
+
258
+ const currentNodeResult = visit ( node , problems )
259
+ if ( count > 2 ) {
260
+ cache . set ( node . path , currentNodeResult )
261
+ }
262
+
263
+ // Get children of current node
264
+ const children = getChildren ( node , currentNodeResult , seenNodes , traversePathMap )
265
+
266
+ // Recurse on each child
267
+ for ( const child of children ) {
268
+ const childResult = exploreDependencyGraph (
269
+ child ,
270
+ getChildren ,
271
+ visit ,
272
+ { json, parseable } ,
273
+ seenNodes ,
274
+ problems ,
275
+ cache ,
276
+ traversePathMap ,
277
+ encounterCount
278
+ )
279
+ currentNodeResult [ _include ] = currentNodeResult [ _include ] || childResult [ _include ]
280
+ if ( childResult [ _include ] && ! parseable ) {
281
+ if ( json ) {
282
+ currentNodeResult . dependencies = currentNodeResult . dependencies || { }
283
+ currentNodeResult . dependencies [ childResult [ _name ] ] = childResult
284
+ } else {
285
+ currentNodeResult . nodes . push ( childResult )
286
+ }
287
+ }
288
+ }
289
+
290
+ return currentNodeResult
291
+ }
292
+
289
293
const isGitNode = ( node ) => {
290
294
if ( ! node . resolved ) {
291
295
return
@@ -323,26 +327,6 @@ const getProblems = (node, { global }) => {
323
327
return problems
324
328
}
325
329
326
- // annotates _parent and _include metadata into the resulting
327
- // item obj allowing for filtering out results during output
328
- const augmentItemWithIncludeMetadata = ( node , item ) => {
329
- // item[_parent] = node[_parent]
330
- // item[_include] = node[_include]
331
-
332
- // append current item to its parent.nodes which is the
333
- // structure expected by archy in order to print tree
334
- // if (node[_include]) {
335
- // // includes all ancestors of included node
336
- // let p = node[_parent]
337
- // while (p) {
338
- // p[_include] = true
339
- // p = p[_parent]
340
- // }
341
- // }
342
-
343
- return item
344
- }
345
-
346
330
const getHumanOutputItem = ( node , { args, chalk, global, long } ) => {
347
331
const { pkgid, path } = node
348
332
const workspacePkgId = chalk . blueBright ( pkgid )
@@ -406,7 +390,7 @@ const getHumanOutputItem = (node, { args, chalk, global, long }) => {
406
390
}
407
391
408
392
const getJsonOutputItem = ( node , { global, long } ) => {
409
- const item = { }
393
+ const item = { [ _include ] : node [ _include ] }
410
394
411
395
if ( node . version ) {
412
396
item . version = node . version
@@ -463,7 +447,7 @@ const getJsonOutputItem = (node, { global, long }) => {
463
447
item . problems = [ ...node [ _problems ] ]
464
448
}
465
449
466
- return augmentItemWithIncludeMetadata ( node , item )
450
+ return item
467
451
}
468
452
469
453
const filterByEdgesTypes = ( { link, omit } ) => ( edge ) => {
@@ -571,9 +555,9 @@ const augmentNodesWithMetadata = ({
571
555
return node
572
556
}
573
557
574
- const sortAlphabetically = ( { pkgid : a } , { pkgid : b } ) => localeCompare ( b , a )
558
+ const sortAlphabetically = ( { pkgid : a } , { pkgid : b } ) => localeCompare ( a , b )
575
559
576
- const humanOutput = ( { chalk, result, seenItems , unicode } ) => {
560
+ const humanOutput = ( { chalk, result, unicode } ) => {
577
561
// we need to traverse the entire tree in order to determine which items
578
562
// should be included (since a nested transitive included dep will make it
579
563
// so that all its ancestors should be displayed)
@@ -592,7 +576,7 @@ const humanOutput = ({ chalk, result, seenItems, unicode }) => {
592
576
return chalk . reset ( archyOutput )
593
577
}
594
578
595
- const jsonOutput = ( { path, problems, result, rootError, seenItems } ) => {
579
+ const jsonOutput = ( { path, problems, result, rootError } ) => {
596
580
if ( problems . size ) {
597
581
result . problems = [ ...problems ]
598
582
}
@@ -609,19 +593,6 @@ const jsonOutput = ({ path, problems, result, rootError, seenItems }) => {
609
593
// should be included (since a nested transitive included dep will make it
610
594
// so that all its ancestors should be displayed)
611
595
// here is where we put items in their expected place for json output
612
- for ( const item of seenItems ) {
613
- delete item . traversePath
614
- delete item . isCircular
615
- // append current item to its parent item.dependencies obj in order
616
- // to provide a json object structure that represents the installed tree
617
- if ( item [ _include ] && item [ _parent ] ) {
618
- if ( ! item [ _parent ] . dependencies ) {
619
- item [ _parent ] . dependencies = { }
620
- }
621
-
622
- item [ _parent ] . dependencies [ item [ _name ] ] = item
623
- }
624
- }
625
596
626
597
return result
627
598
}
0 commit comments