1
1
import events from 'node:events'
2
- import { existsSync , readFileSync , realpathSync } from 'node:fs'
2
+ import { readFileSync , realpathSync } from 'node:fs'
3
3
import https from 'node:https'
4
4
import path from 'node:path'
5
5
import rl from 'node:readline'
@@ -35,14 +35,16 @@ type ArboristClass = typeof BaseArborist & {
35
35
new ( ...args : any ) : typeof BaseArborist
36
36
}
37
37
38
- type EdgeClass = BaseEdge & {
38
+ type EdgeClass = Omit < BaseEdge , 'overrides' | 'reload' > & {
39
39
optional : boolean
40
40
overrides : OverrideSet | undefined
41
41
peer : boolean
42
42
peerConflicted : boolean
43
43
rawSpec : string
44
44
get spec ( ) : string
45
+ get to ( ) : NodeClass | null
45
46
new ( ...args : any ) : EdgeClass
47
+ reload ( hard ?: boolean ) : void
46
48
satisfiedBy ( node : NodeClass ) : boolean
47
49
}
48
50
@@ -78,17 +80,20 @@ type InstallEffect = {
78
80
newPackage : PURLParts
79
81
}
80
82
81
- type NodeClass = BaseNode & {
83
+ type NodeClass = Omit < BaseNode , 'edgesOut' | 'isTop' | 'parent' | 'resolve' > & {
82
84
name : string
83
85
version : string
84
86
edgesIn : Set < SafeEdge >
85
87
edgesOut : Map < string , SafeEdge >
86
88
hasShrinkwrap : boolean
87
89
inShrinkwrap : boolean | undefined
90
+ isTop : boolean | undefined
88
91
overrides : OverrideSet | undefined
89
- new ( ...args : any ) : BaseNode
92
+ parent : NodeClass | null
93
+ new ( ...args : any ) : NodeClass
90
94
addEdgeIn ( edge : SafeEdge ) : void
91
95
addEdgeOut ( edge : SafeEdge ) : void
96
+ resolve ( name : string ) : NodeClass
92
97
}
93
98
94
99
interface OverrideSet {
@@ -220,7 +225,7 @@ async function* batchScan(
220
225
}
221
226
} )
222
227
}
223
- // TODO: migrate to SDK
228
+ // TODO: Migrate to SDK.
224
229
const pkgDataReq = https
225
230
. request ( `${ API_V0_URL } /scan/batch` , {
226
231
method : 'POST' ,
@@ -243,8 +248,9 @@ async function* batchScan(
243
248
244
249
function deleteEdgeIn ( node : NodeClass , edge : SafeEdge ) {
245
250
node . edgesIn . delete ( edge )
246
- if ( edge . overrides ) {
247
- updateNodeOverrideSetDueToEdgeRemoval ( node , edge . overrides )
251
+ const { overrides } = edge
252
+ if ( overrides ) {
253
+ updateNodeOverrideSetDueToEdgeRemoval ( node , overrides )
248
254
}
249
255
}
250
256
@@ -293,35 +299,43 @@ function findSpecificOverrideSet(
293
299
overrideSet = overrideSet . parent
294
300
}
295
301
console . error ( 'Conflicting override sets' )
302
+ return undefined
296
303
}
297
304
298
305
function maybeReadfileSync ( filepath : string ) : string | undefined {
299
306
try {
300
- return existsSync ( filepath ) ? readFileSync ( filepath , 'utf8' ) : undefined
307
+ return readFileSync ( filepath , 'utf8' )
301
308
} catch { }
302
309
return undefined
303
310
}
304
311
305
312
function overrideSetsChildrenAreEqual (
306
313
overrideSet : OverrideSet ,
307
314
other : OverrideSet
308
- ) {
309
- const { children } = overrideSet
310
- const { children : otherChildren } = other
311
- if ( children . size !== otherChildren . size ) {
312
- return false
313
- }
314
- for ( const key of children . keys ( ) ) {
315
- if ( ! otherChildren . has ( key ) ) {
316
- return false
315
+ ) : boolean {
316
+ const queue : [ OverrideSet , OverrideSet ] [ ] = [ [ overrideSet , other ] ]
317
+ let pos = 0
318
+ let { length : queueLength } = queue
319
+ while ( pos < queueLength ) {
320
+ if ( pos === LOOP_SENTINEL ) {
321
+ throw new Error ( 'Detected infinite loop while comparing override sets' )
317
322
}
318
- const child = < OverrideSet > children . get ( key )
319
- const otherChild = < OverrideSet > otherChildren . get ( key )
320
- if ( child ! . value !== otherChild ! . value ) {
323
+ const { 0 : currSet , 1 : currOtherSet } = queue [ pos ++ ] !
324
+ const { children } = currSet
325
+ const { children : otherChildren } = currOtherSet
326
+ if ( children . size !== otherChildren . size ) {
321
327
return false
322
328
}
323
- if ( ! overrideSetsChildrenAreEqual ( child , otherChild ) ) {
324
- return false
329
+ for ( const key of children . keys ( ) ) {
330
+ if ( ! otherChildren . has ( key ) ) {
331
+ return false
332
+ }
333
+ const child = < OverrideSet > children . get ( key )
334
+ const otherChild = < OverrideSet > otherChildren . get ( key )
335
+ if ( child ! . value !== otherChild ! . value ) {
336
+ return false
337
+ }
338
+ queue [ queueLength ++ ] = [ child , otherChild ]
325
339
}
326
340
}
327
341
return true
@@ -479,7 +493,7 @@ function pkgidParts(pkgid: string) {
479
493
480
494
function recalculateOutEdgesOverrides ( node : NodeClass ) {
481
495
// For each edge out propagate the new overrides through.
482
- for ( const [ , edge ] of node . edgesOut ) {
496
+ for ( const edge of node . edgesOut . values ( ) ) {
483
497
edge . reload ( true )
484
498
if ( edge . to ) {
485
499
updateNodeOverrideSet ( edge . to , edge . overrides )
@@ -505,25 +519,27 @@ function updateNodeOverrideSetDueToEdgeRemoval(
505
519
node : NodeClass ,
506
520
other : OverrideSet
507
521
) {
522
+ const { overrides } = node
508
523
// If this edge's overrides isn't equal to this node's overrides, then removing
509
524
// it won't change newOverrideSet later.
510
- if ( ! node . overrides || ! overrideSetsEqual ( node . overrides , other ) ) {
525
+ if ( ! overrides || ! overrideSetsEqual ( overrides , other ) ) {
511
526
return false
512
527
}
513
528
let newOverrideSet
514
529
for ( const edge of node . edgesIn ) {
530
+ const { overrides : edgeOverrides } = edge
515
531
if ( newOverrideSet ) {
516
- newOverrideSet = findSpecificOverrideSet ( edge . overrides , newOverrideSet )
532
+ newOverrideSet = findSpecificOverrideSet ( edgeOverrides , newOverrideSet )
517
533
} else {
518
- newOverrideSet = edge . overrides
534
+ newOverrideSet = edgeOverrides
519
535
}
520
536
}
521
- if ( overrideSetsEqual ( node . overrides , newOverrideSet ) ) {
537
+ if ( overrideSetsEqual ( overrides , newOverrideSet ) ) {
522
538
return false
523
539
}
524
540
node . overrides = newOverrideSet
525
- if ( node . overrides ) {
526
- // Optimization: if there's any override set at all, then no non-extraneous
541
+ if ( newOverrideSet ) {
542
+ // Optimization: If there's any override set at all, then no non-extraneous
527
543
// node has an empty override set. So if we temporarily have no override set
528
544
// (for example, we removed all the edges in), there's no use updating all
529
545
// the edges out right now. Let's just wait until we have an actual override
@@ -565,15 +581,17 @@ function updateNodeOverrideSet(
565
581
}
566
582
const newOverrideSet = findSpecificOverrideSet ( overrides , otherOverrideSet )
567
583
if ( newOverrideSet ) {
568
- if ( ! overrideSetsEqual ( overrides , newOverrideSet ) ) {
569
- node . overrides = newOverrideSet
570
- recalculateOutEdgesOverrides ( node )
571
- return true
584
+ if ( overrideSetsEqual ( overrides , newOverrideSet ) ) {
585
+ return false
572
586
}
573
- return false
587
+ node . overrides = newOverrideSet
588
+ recalculateOutEdgesOverrides ( node )
589
+ return true
574
590
}
575
591
// This is an error condition. We can only get here if the new override set is
576
592
// in conflict with the existing.
593
+ console . error ( 'Conflicting override sets' )
594
+ return false
577
595
}
578
596
579
597
function walk (
@@ -635,8 +653,14 @@ function walk(
635
653
return needInfoOn
636
654
}
637
655
638
- // An edge in the dependency graph
639
- // Represents a dependency relationship of some kind
656
+ // Copied from
657
+ // https://github.com/npm/cli/blob/v10.9.0/workspaces/arborist/lib/edge.js:
658
+ // The npm application
659
+ // Copyright (c) npm, Inc. and Contributors
660
+ // Licensed on the terms of The Artistic License 2.0
661
+ //
662
+ // An edge in the dependency graph.
663
+ // Represents a dependency relationship of some kind.
640
664
641
665
class SafeEdge extends Edge {
642
666
#safeAccept: string | undefined
@@ -648,7 +672,6 @@ class SafeEdge extends Edge {
648
672
constructor ( options : EdgeOptions ) {
649
673
const { accept, from } = options
650
674
// Defer to supper to validate options and assign non-private values.
651
- // @ts -ignore: Incorrectly typed.
652
675
super ( options )
653
676
if ( accept !== undefined ) {
654
677
this . #safeAccept = accept || '*'
@@ -660,7 +683,7 @@ class SafeEdge extends Edge {
660
683
this . reload ( true )
661
684
}
662
685
663
- // return the edge data, and an explanation of how that edge came to be here
686
+ // Return the edge data, and an explanation of how that edge came to be here.
664
687
// @ts -ignore: Edge#explain is defined with an unused `seen = []` param.
665
688
override explain ( ) {
666
689
if ( ! this . #safeExplanation) {
@@ -733,7 +756,6 @@ class SafeEdge extends Edge {
733
756
return this . #safeAccept
734
757
}
735
758
736
- // @ts -ignore: Incorrectly typed as a property instead of an accessor.
737
759
override get error ( ) {
738
760
if ( ! this . #safeError) {
739
761
if ( ! this . #safeTo) {
@@ -770,6 +792,8 @@ class SafeEdge extends Edge {
770
792
const newTo = this . #safeFrom?. resolve ( this . name )
771
793
if ( newTo !== this . #safeTo) {
772
794
if ( this . #safeTo) {
795
+ // Instead of `this.#safeTo.edgesIn.delete(this)` we patch based on
796
+ // https://github.com/npm/cli/pull/7025.
773
797
deleteEdgeIn ( this . #safeTo, this )
774
798
}
775
799
this . #safeTo = < NodeClass > newTo ?? null
@@ -785,6 +809,8 @@ class SafeEdge extends Edge {
785
809
detach ( ) {
786
810
this . #safeExplanation = null
787
811
if ( this . #safeTo) {
812
+ // Instead of `this.#safeTo.edgesIn.delete(this)` we patch based on
813
+ // https://github.com/npm/cli/pull/7025.
788
814
deleteEdgeIn ( this . #safeTo, this )
789
815
}
790
816
if ( this . #safeFrom) {
@@ -830,11 +856,12 @@ export class SafeArborist extends Arborist {
830
856
) : Promise < NodeClass > {
831
857
// SafeArborist has suffered side effects and must be rebuilt from scratch.
832
858
const arb = new Arborist ( ...( this as any ) [ kCtorArgs ] )
833
- const ret = < NodeClass > await arb . reify ( ...args )
859
+ const ret = < unknown > await arb . reify ( ...args )
834
860
Object . assign ( this , arb )
835
- return ret
861
+ return < NodeClass > ret
836
862
}
837
863
864
+ // @ts -ignore Incorrectly typed.
838
865
override async reify (
839
866
...args : Parameters < InstanceType < ArboristClass > [ 'reify' ] >
840
867
) : Promise < NodeClass > {
0 commit comments