1
1
import { hoist , type HoisterTree , type HoisterResult } from "./hoist"
2
2
import * as path from "path"
3
3
import * as fs from "fs"
4
- import type { NodeModuleInfo , DependencyTree , DependencyGraph , Dependency } from "./types"
4
+ import type { NodeModuleInfo , DependencyGraph , Dependency } from "./types"
5
5
import { exec , log } from "builder-util"
6
6
import { Lazy } from "lazy-val"
7
7
8
8
export abstract class NodeModulesCollector < T extends Dependency < T , OptionalsType > , OptionalsType > {
9
9
private nodeModules : NodeModuleInfo [ ] = [ ]
10
- protected dependencyPathMap : Map < string , string > = new Map ( )
11
10
protected allDependencies : Map < string , T > = new Map ( )
11
+ protected productionGraph : DependencyGraph = { }
12
12
13
13
constructor ( private readonly rootDir : string ) { }
14
14
15
15
public async getNodeModules ( ) : Promise < NodeModuleInfo [ ] > {
16
16
const tree : T = await this . getDependenciesTree ( )
17
17
const realTree : T = this . getTreeFromWorkspaces ( tree )
18
- const parsedTree : Dependency < T , OptionalsType > = this . extractRelevantData ( realTree )
18
+ this . collectAllDependencies ( realTree )
19
+ this . extractProductionDependencyGraph ( realTree , "." /*root project name*/ )
19
20
20
- this . collectAllDependencies ( parsedTree )
21
-
22
- const productionTree : DependencyTree = this . extractProductionDependencyTree ( parsedTree )
23
- const dependencyGraph : DependencyGraph = this . convertToDependencyGraph ( productionTree )
24
-
25
- const hoisterResult : HoisterResult = hoist ( this . transToHoisterTree ( dependencyGraph ) , { check : true } )
21
+ const hoisterResult : HoisterResult = hoist ( this . transToHoisterTree ( this . productionGraph ) , { check : true } )
26
22
this . _getNodeModules ( hoisterResult . dependencies , this . nodeModules )
27
23
28
24
return this . nodeModules
@@ -36,7 +32,8 @@ export abstract class NodeModulesCollector<T extends Dependency<T, OptionalsType
36
32
protected abstract readonly pmCommand : Lazy < string >
37
33
protected abstract getArgs ( ) : string [ ]
38
34
protected abstract parseDependenciesTree ( jsonBlob : string ) : T
39
- protected abstract extractProductionDependencyTree ( tree : Dependency < T , OptionalsType > ) : DependencyTree
35
+ protected abstract extractProductionDependencyGraph ( tree : Dependency < T , OptionalsType > , dependencyId : string ) : void
36
+ protected abstract collectAllDependencies ( tree : Dependency < T , OptionalsType > ) : void
40
37
41
38
protected async getDependenciesTree ( ) : Promise < T > {
42
39
const command = await this . pmCommand . value
@@ -48,35 +45,6 @@ export abstract class NodeModulesCollector<T extends Dependency<T, OptionalsType
48
45
return this . parseDependenciesTree ( dependencies )
49
46
}
50
47
51
- protected extractRelevantData ( npmTree : T ) : Dependency < T , OptionalsType > {
52
- // Do not use `...npmTree` as we are explicitly extracting the data we need
53
- const { name, version, path, workspaces, dependencies } = npmTree
54
- const tree : Dependency < T , OptionalsType > = {
55
- name,
56
- version,
57
- path,
58
- workspaces,
59
- // DFS extract subtree
60
- dependencies : this . extractInternal ( dependencies ) ,
61
- }
62
-
63
- return tree
64
- }
65
-
66
- protected extractInternal ( deps : T [ "dependencies" ] ) : T [ "dependencies" ] {
67
- return deps && Object . keys ( deps ) . length > 0
68
- ? Object . entries ( deps ) . reduce ( ( accum , [ packageName , depObjectOrVersionString ] ) => {
69
- return {
70
- ...accum ,
71
- [ packageName ] :
72
- typeof depObjectOrVersionString === "object" && Object . keys ( depObjectOrVersionString ) . length > 0
73
- ? this . extractRelevantData ( depObjectOrVersionString )
74
- : depObjectOrVersionString ,
75
- }
76
- } , { } )
77
- : undefined
78
- }
79
-
80
48
protected resolvePath ( filePath : string ) : string {
81
49
try {
82
50
const stats = fs . lstatSync ( filePath )
@@ -91,60 +59,6 @@ export abstract class NodeModulesCollector<T extends Dependency<T, OptionalsType
91
59
}
92
60
}
93
61
94
- private convertToDependencyGraph ( tree : DependencyTree , parentKey = "." ) : DependencyGraph {
95
- return Object . entries ( tree . dependencies || { } ) . reduce < DependencyGraph > ( ( acc , curr ) => {
96
- const [ packageName , dependencies ] = curr
97
- // Skip empty dependencies (like some optionalDependencies)
98
- if ( Object . keys ( dependencies ) . length === 0 ) {
99
- return acc
100
- }
101
- const version = dependencies . version || ""
102
- const newKey = `${ packageName } @${ version } `
103
- if ( ! dependencies . path ) {
104
- log . error (
105
- {
106
- packageName,
107
- data : dependencies ,
108
- parentModule : tree . name ,
109
- parentVersion : tree . version ,
110
- } ,
111
- "dependency path is undefined"
112
- )
113
- throw new Error ( "unable to parse `path` during `tree.dependencies` reduce" )
114
- }
115
- // Map dependency details: name, version and path to the dependency tree
116
- this . dependencyPathMap . set ( newKey , path . normalize ( this . resolvePath ( dependencies . path ) ) )
117
- if ( ! acc [ parentKey ] ) {
118
- acc [ parentKey ] = { dependencies : [ ] }
119
- }
120
- acc [ parentKey ] . dependencies . push ( newKey )
121
- if ( tree . implicitDependenciesInjected ) {
122
- log . debug (
123
- {
124
- dependency : packageName ,
125
- version,
126
- path : dependencies . path ,
127
- parentModule : tree . name ,
128
- parentVersion : tree . version ,
129
- } ,
130
- "converted implicit dependency"
131
- )
132
- return acc
133
- }
134
-
135
- return { ...acc , ...this . convertToDependencyGraph ( dependencies , newKey ) }
136
- } , { } )
137
- }
138
-
139
- private collectAllDependencies ( tree : Dependency < T , OptionalsType > ) {
140
- for ( const [ key , value ] of Object . entries ( tree . dependencies || { } ) ) {
141
- if ( Object . keys ( value . dependencies ?? { } ) . length > 0 ) {
142
- this . allDependencies . set ( `${ key } @${ value . version } ` , value )
143
- this . collectAllDependencies ( value )
144
- }
145
- }
146
- }
147
-
148
62
private getTreeFromWorkspaces ( tree : T ) : T {
149
63
if ( tree . workspaces && tree . dependencies ) {
150
64
const packageJson : Dependency < string , string > = require ( path . join ( this . rootDir , "package.json" ) )
@@ -186,15 +100,15 @@ export abstract class NodeModulesCollector<T extends Dependency<T, OptionalsType
186
100
187
101
for ( const d of dependencies . values ( ) ) {
188
102
const reference = [ ...d . references ] [ 0 ]
189
- const p = this . dependencyPathMap . get ( `${ d . name } @${ reference } ` )
103
+ const p = this . allDependencies . get ( `${ d . name } @${ reference } ` ) ?. path
190
104
if ( p === undefined ) {
191
105
log . debug ( { name : d . name , reference } , "cannot find path for dependency" )
192
106
continue
193
107
}
194
108
const node : NodeModuleInfo = {
195
109
name : d . name ,
196
110
version : reference ,
197
- dir : p ,
111
+ dir : this . resolvePath ( p ) ,
198
112
}
199
113
result . push ( node )
200
114
if ( d . dependencies . size > 0 ) {
0 commit comments