@@ -3,12 +3,38 @@ import * as fs from "fs";
3
3
import * as path from "path" ;
4
4
import * as shelljs from "shelljs" ;
5
5
import Future = require( "fibers/future" ) ;
6
- import { NpmDependencyResolver , TnsModulesCopy , NpmPluginPrepare } from "./node-modules-dest-copy" ;
6
+ import { TnsModulesCopy , NpmPluginPrepare } from "./node-modules-dest-copy" ;
7
7
import * as fiberBootstrap from "../../common/fiber-bootstrap" ;
8
- import { sleep } from "../../../lib/common/helpers" ;
8
+ import { sleep } from "../../../lib/common/helpers" ;
9
9
10
10
let glob = require ( "glob" ) ;
11
11
12
+ export class DependencyNode {
13
+ public constructor ( ) {
14
+ this . productionChildren = [ ] ;
15
+ this . name = '' ;
16
+ this . isRoot = false ;
17
+ this . parent = null ;
18
+ this . path = '' ;
19
+ }
20
+
21
+ public static create ( name : string , version : string , parent : DependencyNode ) : DependencyNode {
22
+ var n = new DependencyNode ( ) ;
23
+ n . name = name ;
24
+ n . version = version ;
25
+ n . parent = parent ;
26
+
27
+ return n ;
28
+ }
29
+
30
+ public isRoot : boolean ;
31
+ public name : string ;
32
+ public version : string ;
33
+ public parent : DependencyNode ;
34
+ public productionChildren : DependencyNode [ ] ;
35
+ public path : string ;
36
+ }
37
+
12
38
export class NodeModulesBuilder implements INodeModulesBuilder {
13
39
constructor ( private $childProcess : IChildProcess ,
14
40
private $fs : IFileSystem ,
@@ -37,7 +63,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
37
63
} , ( er : Error , files : string [ ] ) => {
38
64
fiberBootstrap . run ( ( ) => {
39
65
40
- while ( this . $lockfile . check ( ) . wait ( ) ) {
66
+ while ( this . $lockfile . check ( ) . wait ( ) ) {
41
67
sleep ( 10 ) ;
42
68
}
43
69
@@ -85,7 +111,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
85
111
let intervalId = setInterval ( ( ) => {
86
112
fiberBootstrap . run ( ( ) => {
87
113
if ( ! this . $lockfile . check ( ) . wait ( ) || future . isResolved ( ) ) {
88
- if ( ! future . isResolved ( ) ) {
114
+ if ( ! future . isResolved ( ) ) {
89
115
future . return ( ) ;
90
116
}
91
117
clearInterval ( intervalId ) ;
@@ -133,57 +159,166 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
133
159
// Force copying if the destination doesn't exist.
134
160
lastModifiedTime = null ;
135
161
}
136
-
137
- //TODO: plamen5kov: WIP
138
- let depJsonStr = this . $childProcess . exec ( "npm list --prod --json" ) . wait ( ) ;
139
- let prodDependenciesJson = JSON . parse ( depJsonStr ) ;
140
- let productionDepArray = this . getProductionDependencyNames ( prodDependenciesJson ) ;
141
162
163
+ let productionDependencies = this . getProductionDependencies ( this . $projectData . projectDir ) ;
142
164
143
- let nodeModules = this . getChangedNodeModules ( absoluteOutputPath , platform , lastModifiedTime ) . wait ( ) ;
165
+ // console.log(productionDependencies );
144
166
145
- const resolver = new NpmDependencyResolver ( this . $projectData . projectDir ) ;
146
- const resolvedDependencies = resolver . resolveDependencies ( _ . keys ( nodeModules ) , platform ) ;
167
+ // TODO: Pip3r4o - result is not used currently
168
+ // let nodeModules = this.getChangedNodeModules(absoluteOutputPath , platform, lastModifiedTime).wait( );
147
169
148
170
if ( ! this . $options . bundle ) {
149
171
const tnsModulesCopy = this . $injector . resolve ( TnsModulesCopy , {
150
172
outputRoot : absoluteOutputPath
151
173
} ) ;
152
- tnsModulesCopy . copyModules ( resolvedDependencies , platform ) ;
174
+ tnsModulesCopy . copyModules ( productionDependencies , platform ) ;
153
175
} else {
154
176
this . cleanNodeModules ( absoluteOutputPath , platform ) ;
155
177
}
156
178
157
179
const npmPluginPrepare = this . $injector . resolve ( NpmPluginPrepare , { } ) ;
158
- npmPluginPrepare . preparePlugins ( resolvedDependencies , platform ) ;
180
+ npmPluginPrepare . preparePlugins ( productionDependencies , platform ) ;
159
181
} ) . future < void > ( ) ( ) ;
160
182
}
161
183
162
- private getProductionDependencyNames ( inputJson : any ) : any {
163
- var finalDependencies :any = { } ;
164
- var queue :any = [ ] ;
184
+ public getProductionDependencies ( projectPath : string ) {
185
+ var prodDependencies : any = [ ] ;
186
+ var deps : any = [ ] ;
187
+ var seen : any = { } ;
188
+
189
+ var pJson = path . join ( projectPath , "package.json" ) ;
190
+ var nodeModulesDir = path . join ( projectPath , "node_modules" ) ;
191
+
192
+ var content = require ( pJson ) ;
193
+
194
+ Object . keys ( content . dependencies ) . forEach ( ( key ) => {
195
+ var depth = 0 ;
196
+ var directory = path . join ( nodeModulesDir , key ) ;
197
+
198
+ // find and traverse child with name `key`, parent's directory -> dep.directory
199
+ traverseChild ( key , directory , depth ) ;
200
+ } ) ;
201
+
202
+ return filterUniqueDirectories ( deps ) ;
165
203
166
- inputJson . level = 0 ;
167
- queue . push ( inputJson )
204
+ function traverseChild ( name : string , currentModulePath : string , depth : number ) {
205
+ // check if key appears in a scoped module dependency
206
+ var isScoped = name . indexOf ( '@' ) === 0 ;
168
207
169
- while ( queue . length > 0 ) {
170
- var parent = queue . pop ( ) ;
208
+ if ( ! isScoped ) {
209
+ // Check if child has been extracted in the parent's node modules, AND THEN in `node_modules`
210
+ // Slower, but prevents copying wrong versions if multiple of the same module are installed
211
+ // Will also prevent copying project's devDependency's version if current module depends on another version
212
+ var modulePath = path . join ( currentModulePath , "node_modules" , name ) ; // /node_modules/parent/node_modules/<package>
213
+ var exists = ensureModuleExists ( modulePath ) ;
171
214
172
- if ( parent . dependencies ) {
173
- for ( var dep in parent . dependencies ) {
174
- var currentDependency = parent . dependencies [ dep ] ;
175
- currentDependency . level = parent . level + 1 ;
176
- queue . push ( currentDependency ) ;
177
- finalDependencies [ dep ] = currentDependency ;
215
+ if ( exists ) {
216
+ var dep = addDependency ( deps , name , modulePath , depth + 1 ) ;
217
+
218
+ traverseModule ( modulePath , depth + 1 , dep ) ;
219
+ } else {
220
+ modulePath = path . join ( nodeModulesDir , name ) ; // /node_modules/<package>
221
+ exists = ensureModuleExists ( modulePath ) ;
222
+
223
+ if ( ! exists ) {
224
+ return ;
225
+ }
226
+
227
+ var dep = addDependency ( deps , name , modulePath , 0 ) ;
228
+
229
+ traverseModule ( modulePath , 0 , dep ) ;
230
+ }
231
+
232
+ }
233
+ // module is scoped
234
+ else {
235
+ var scopeSeparatorIndex = name . indexOf ( '/' ) ;
236
+ var scope = name . substring ( 0 , scopeSeparatorIndex ) ;
237
+ var moduleName = name . substring ( scopeSeparatorIndex + 1 , name . length ) ;
238
+ var scopedModulePath = path . join ( nodeModulesDir , scope , moduleName ) ;
239
+
240
+ var exists = ensureModuleExists ( scopedModulePath ) ;
241
+
242
+ if ( exists ) {
243
+ var dep = addDependency ( deps , name , scopedModulePath , 0 ) ;
244
+ traverseModule ( scopedModulePath , depth , dep ) ;
245
+ }
246
+ else {
247
+ scopedModulePath = path . join ( currentModulePath , "node_modules" , scope , moduleName ) ;
248
+
249
+ exists = ensureModuleExists ( scopedModulePath ) ;
250
+
251
+ if ( ! exists ) {
252
+ return ;
253
+ }
254
+
255
+ var dep = addDependency ( deps , name , scopedModulePath , depth + 1 ) ;
256
+ traverseModule ( scopedModulePath , depth + 1 , dep ) ;
257
+ }
258
+ }
259
+
260
+ function traverseModule ( modulePath : string , depth : number , currentDependency : any ) {
261
+ var packageJsonPath = path . join ( modulePath , 'package.json' ) ;
262
+ var packageJsonExists = fs . lstatSync ( packageJsonPath ) . isFile ( ) ;
263
+
264
+ if ( packageJsonExists ) {
265
+ var packageJsonContents = require ( packageJsonPath ) ;
266
+
267
+ if ( ! ! packageJsonContents . nativescript ) {
268
+ // add `nativescript` property, necessary for resolving plugins
269
+ currentDependency . nativescript = packageJsonContents . nativescript ;
270
+ }
271
+
272
+ if ( packageJsonContents . dependencies ) {
273
+ Object . keys ( packageJsonContents . dependencies ) . forEach ( ( key ) => {
274
+
275
+ traverseChild ( key , modulePath , depth ) ;
276
+ } ) ;
277
+ }
278
+ }
279
+ }
280
+
281
+ function addDependency ( deps : any [ ] , name : string , directory : string , depth : number ) {
282
+ var dep : any = { } ;
283
+ dep . name = name ;
284
+ dep . directory = directory ;
285
+ dep . depth = depth ;
286
+
287
+ deps . push ( dep ) ;
288
+
289
+ return dep ;
290
+ }
291
+
292
+ function ensureModuleExists ( modulePath : string ) : boolean {
293
+ try {
294
+ var exists = fs . lstatSync ( modulePath ) ;
295
+ return exists . isDirectory ( ) ;
296
+ } catch ( e ) {
297
+ return false ;
178
298
}
179
299
}
180
300
}
181
301
182
- return finalDependencies ;
302
+ function filterUniqueDirectories ( dependencies : any ) {
303
+ var unique : any = [ ] ;
304
+ var distinct : any = [ ] ;
305
+ for ( var i in dependencies ) {
306
+ var dep = dependencies [ i ] ;
307
+ if ( distinct . indexOf ( dep . directory ) > - 1 ) {
308
+ continue ;
309
+ }
310
+
311
+ distinct . push ( dep . directory ) ;
312
+ unique . push ( dep ) ;
313
+ }
314
+
315
+ return unique ;
316
+ }
183
317
}
184
318
185
319
public cleanNodeModules ( absoluteOutputPath : string , platform : string ) : void {
186
320
shelljs . rm ( "-rf" , absoluteOutputPath ) ;
187
- }
321
+ }
188
322
}
323
+
189
324
$injector . register ( "nodeModulesBuilder" , NodeModulesBuilder ) ;
0 commit comments