@@ -3,9 +3,9 @@ import * as fs from "fs";
33import * as path from "path" ;
44import * as shelljs from "shelljs" ;
55import Future = require( "fibers/future" ) ;
6- import { NpmDependencyResolver , TnsModulesCopy , NpmPluginPrepare } from "./node-modules-dest-copy" ;
6+ import { TnsModulesCopy , NpmPluginPrepare } from "./node-modules-dest-copy" ;
77import * as fiberBootstrap from "../../common/fiber-bootstrap" ;
8- import { sleep } from "../../../lib/common/helpers" ;
8+ import { sleep } from "../../../lib/common/helpers" ;
99
1010let glob = require ( "glob" ) ;
1111
@@ -37,7 +37,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
3737 } , ( er : Error , files : string [ ] ) => {
3838 fiberBootstrap . run ( ( ) => {
3939
40- while ( this . $lockfile . check ( ) . wait ( ) ) {
40+ while ( this . $lockfile . check ( ) . wait ( ) ) {
4141 sleep ( 10 ) ;
4242 }
4343
@@ -85,7 +85,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
8585 let intervalId = setInterval ( ( ) => {
8686 fiberBootstrap . run ( ( ) => {
8787 if ( ! this . $lockfile . check ( ) . wait ( ) || future . isResolved ( ) ) {
88- if ( ! future . isResolved ( ) ) {
88+ if ( ! future . isResolved ( ) ) {
8989 future . return ( ) ;
9090 }
9191 clearInterval ( intervalId ) ;
@@ -133,57 +133,165 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
133133 // Force copying if the destination doesn't exist.
134134 lastModifiedTime = null ;
135135 }
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 ) ;
141136
137+ let productionDependencies = this . getProductionDependencies ( this . $projectData . projectDir ) ;
142138
143- let nodeModules = this . getChangedNodeModules ( absoluteOutputPath , platform , lastModifiedTime ) . wait ( ) ;
139+ // console.log(productionDependencies );
144140
145- const resolver = new NpmDependencyResolver ( this . $projectData . projectDir ) ;
146- const resolvedDependencies = resolver . resolveDependencies ( _ . keys ( nodeModules ) , platform ) ;
141+ // TODO: Pip3r4o - result is not used currently
142+ // let nodeModules = this.getChangedNodeModules(absoluteOutputPath , platform, lastModifiedTime).wait( );
147143
148144 if ( ! this . $options . bundle ) {
149145 const tnsModulesCopy = this . $injector . resolve ( TnsModulesCopy , {
150146 outputRoot : absoluteOutputPath
151147 } ) ;
152- tnsModulesCopy . copyModules ( resolvedDependencies , platform ) ;
148+ tnsModulesCopy . copyModules ( productionDependencies , platform ) ;
153149 } else {
154150 this . cleanNodeModules ( absoluteOutputPath , platform ) ;
155151 }
156152
157153 const npmPluginPrepare = this . $injector . resolve ( NpmPluginPrepare , { } ) ;
158- npmPluginPrepare . preparePlugins ( resolvedDependencies , platform ) ;
154+ npmPluginPrepare . preparePlugins ( productionDependencies , platform ) ;
159155 } ) . future < void > ( ) ( ) ;
160156 }
161157
162- private getProductionDependencyNames ( inputJson : any ) : any {
163- var finalDependencies : any = { } ;
164- var queue : any = [ ] ;
158+ public getProductionDependencies ( projectPath : string ) {
159+ var deps : any = [ ] ;
160+ var seen : any = { } ;
165161
166- inputJson . level = 0 ;
167- queue . push ( inputJson )
162+ var pJson = path . join ( projectPath , "package.json" ) ;
163+ var nodeModulesDir = path . join ( projectPath , "node_modules" ) ;
168164
169- while ( queue . length > 0 ) {
170- var parent = queue . pop ( ) ;
165+ var content = require ( pJson ) ;
171166
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 ;
167+ Object . keys ( content . dependencies ) . forEach ( ( key ) => {
168+ var depth = 0 ;
169+ var directory = path . join ( nodeModulesDir , key ) ;
170+
171+ // find and traverse child with name `key`, parent's directory -> dep.directory
172+ traverseChild ( key , directory , depth ) ;
173+ } ) ;
174+
175+ return filterUniqueDirectories ( deps ) ;
176+
177+ function traverseChild ( name : string , currentModulePath : string , depth : number ) {
178+ // check if key appears in a scoped module dependency
179+ var isScoped = name . indexOf ( '@' ) === 0 ;
180+
181+ if ( ! isScoped ) {
182+ // Check if child has been extracted in the parent's node modules, AND THEN in `node_modules`
183+ // Slower, but prevents copying wrong versions if multiple of the same module are installed
184+ // Will also prevent copying project's devDependency's version if current module depends on another version
185+ var modulePath = path . join ( currentModulePath , "node_modules" , name ) ; // /node_modules/parent/node_modules/<package>
186+ var exists = ensureModuleExists ( modulePath ) ;
187+
188+ if ( exists ) {
189+ var dep = addDependency ( deps , name , modulePath , depth + 1 ) ;
190+
191+ traverseModule ( modulePath , depth + 1 , dep ) ;
192+ } else {
193+ modulePath = path . join ( nodeModulesDir , name ) ; // /node_modules/<package>
194+ exists = ensureModuleExists ( modulePath ) ;
195+
196+ if ( ! exists ) {
197+ return ;
198+ }
199+
200+ var dep = addDependency ( deps , name , modulePath , 0 ) ;
201+
202+ traverseModule ( modulePath , 0 , dep ) ;
203+ }
204+
205+ }
206+ // module is scoped
207+ else {
208+ var scopeSeparatorIndex = name . indexOf ( '/' ) ;
209+ var scope = name . substring ( 0 , scopeSeparatorIndex ) ;
210+ var moduleName = name . substring ( scopeSeparatorIndex + 1 , name . length ) ;
211+ var scopedModulePath = path . join ( nodeModulesDir , scope , moduleName ) ;
212+
213+ var exists = ensureModuleExists ( scopedModulePath ) ;
214+
215+ if ( exists ) {
216+ var dep = addDependency ( deps , name , scopedModulePath , 0 ) ;
217+ traverseModule ( scopedModulePath , depth , dep ) ;
218+ }
219+ else {
220+ scopedModulePath = path . join ( currentModulePath , "node_modules" , scope , moduleName ) ;
221+
222+ exists = ensureModuleExists ( scopedModulePath ) ;
223+
224+ if ( ! exists ) {
225+ return ;
226+ }
227+
228+ var dep = addDependency ( deps , name , scopedModulePath , depth + 1 ) ;
229+ traverseModule ( scopedModulePath , depth + 1 , dep ) ;
230+ }
231+ }
232+
233+ function traverseModule ( modulePath : string , depth : number , currentDependency : any ) {
234+ var packageJsonPath = path . join ( modulePath , 'package.json' ) ;
235+ var packageJsonExists = fs . lstatSync ( packageJsonPath ) . isFile ( ) ;
236+
237+ if ( packageJsonExists ) {
238+ var packageJsonContents = require ( packageJsonPath ) ;
239+
240+ if ( ! ! packageJsonContents . nativescript ) {
241+ // add `nativescript` property, necessary for resolving plugins
242+ currentDependency . nativescript = packageJsonContents . nativescript ;
243+ }
244+
245+ if ( packageJsonContents . dependencies ) {
246+ Object . keys ( packageJsonContents . dependencies ) . forEach ( ( key ) => {
247+
248+ traverseChild ( key , modulePath , depth ) ;
249+ } ) ;
250+ }
251+ }
252+ }
253+
254+ function addDependency ( deps : any [ ] , name : string , directory : string , depth : number ) {
255+ var dep : any = { } ;
256+ dep . name = name ;
257+ dep . directory = directory ;
258+ dep . depth = depth ;
259+
260+ deps . push ( dep ) ;
261+
262+ return dep ;
263+ }
264+
265+ function ensureModuleExists ( modulePath : string ) : boolean {
266+ try {
267+ var exists = fs . lstatSync ( modulePath ) ;
268+ return exists . isDirectory ( ) ;
269+ } catch ( e ) {
270+ return false ;
178271 }
179272 }
180273 }
181274
182- return finalDependencies ;
275+ function filterUniqueDirectories ( dependencies : any ) {
276+ var unique : any = [ ] ;
277+ var distinct : any = [ ] ;
278+ for ( var i in dependencies ) {
279+ var dep = dependencies [ i ] ;
280+ if ( distinct . indexOf ( dep . directory ) > - 1 ) {
281+ continue ;
282+ }
283+
284+ distinct . push ( dep . directory ) ;
285+ unique . push ( dep ) ;
286+ }
287+
288+ return unique ;
289+ }
183290 }
184291
185292 public cleanNodeModules ( absoluteOutputPath : string , platform : string ) : void {
186293 shelljs . rm ( "-rf" , absoluteOutputPath ) ;
187- }
294+ }
188295}
296+
189297$injector . register ( "nodeModulesBuilder" , NodeModulesBuilder ) ;
0 commit comments