@@ -265,7 +265,7 @@ export function getDefaultProject(srcFile: string): TypeScriptProjectFileDetails
265
265
} ;
266
266
267
267
project . files = increaseProjectForReferenceAndImports ( project . files ) ;
268
- project . files = project . files . concat ( getDefinitionsForNodeModules ( dir ) ) ;
268
+ project . files = project . files . concat ( getDefinitionsForNodeModules ( dir , project . files ) ) ;
269
269
project . files = uniq ( project . files . map ( consistentPath ) ) ;
270
270
271
271
return {
@@ -393,7 +393,9 @@ export function getProjectSync(pathOrSrcFile: string): TypeScriptProjectFileDeta
393
393
394
394
// Expand files to include references
395
395
project . files = increaseProjectForReferenceAndImports ( project . files ) ;
396
- project . files = project . files . concat ( getDefinitionsForNodeModules ( dir ) ) ;
396
+
397
+ // Expand files to include node_modules / package.json / typescript.definition
398
+ project . files = project . files . concat ( getDefinitionsForNodeModules ( dir , project . files ) ) ;
397
399
398
400
// Normalize to "/" for all files
399
401
// And take the uniq values
@@ -511,15 +513,73 @@ function increaseProjectForReferenceAndImports(files: string[]): string[] {
511
513
return files ;
512
514
}
513
515
516
+ /** There can be only one typing by name */
517
+ interface Typings {
518
+ [ name : string ] : {
519
+ filePath : string ;
520
+ /** Right now its just INF as we don't do version checks. First one wins! */
521
+ version : number ; // (Simple : maj * 1000000 + min). Don't care about patch
522
+ } ;
523
+ }
524
+
514
525
/**
515
- * TODO: exclude `tsd` definitions that we also have
526
+ * Spec
527
+ * We will expand on files making sure that we don't have a `typing` with the same name
528
+ * Also if two node_modules reference a similar sub project (and also recursively) then the one with latest `version` field wins
516
529
*/
517
- export function getDefinitionsForNodeModules ( projectDir : string ) : string [ ] {
530
+ function getDefinitionsForNodeModules ( projectDir : string , files : string [ ] ) : string [ ] {
531
+
532
+ /** TODO use later when we care about versions */
533
+ function versionStringToNumber ( version : string ) : number {
534
+ var [ maj , min , patch ] = version . split ( '.' ) ;
535
+ return parseInt ( maj ) * 1000000 + parseInt ( min ) ;
536
+ }
537
+
538
+ var typings : Typings = { } ;
539
+
540
+ // Find our `typings` (anything in a typings folder with extension `.d.ts` is considered a typing)
541
+ // These are INF powerful
542
+ var ourTypings = files
543
+ . filter ( f => path . basename ( path . dirname ( f ) ) == 'typings' && endsWith ( f , '.d.ts' )
544
+ || path . basename ( path . dirname ( path . dirname ( f ) ) ) == 'typings' && endsWith ( f , '.d.ts' ) ) ;
545
+ ourTypings . forEach ( f => typings [ path . basename ( f ) ] = { filePath : f , version : Infinity } ) ;
546
+ var existing = createMap ( files . map ( consistentPath ) ) ;
547
+
548
+ function addAllReferencedFilesWithMaxVersion ( file : string ) {
549
+ var dir = path . dirname ( file ) ;
550
+ try {
551
+ var content = fs . readFileSync ( file ) . toString ( ) ;
552
+ }
553
+ catch ( ex ) {
554
+ // if we cannot read a file for whatever reason just quit
555
+ return ;
556
+ }
557
+ var preProcessedFileInfo = ts . preProcessFile ( content , true ) ;
558
+ var files = preProcessedFileInfo . referencedFiles . map ( fileReference => {
559
+ // We assume reference paths are always relative
560
+ var file = path . resolve ( dir , fileReference . fileName ) ;
561
+ // Try by itself, .d.ts
562
+ if ( fs . existsSync ( file ) ) {
563
+ return file ;
564
+ }
565
+ if ( fs . existsSync ( file + '.d.ts' ) ) {
566
+ return file + '.d.ts' ;
567
+ }
568
+ } ) ;
569
+
570
+ // Only ones we don't have by name yet
571
+ // TODO: replace INF with an actual version
572
+ files = files
573
+ . filter ( f => ! typings [ path . basename ( f ) ] || typings [ path . basename ( f ) ] . version > Infinity ) ;
574
+ // Add these
575
+ files . forEach ( f => typings [ path . basename ( f ) ] = { filePath : f , version : Infinity } ) ;
576
+ // Keep expanding
577
+ files . forEach ( f => addAllReferencedFilesWithMaxVersion ( f ) ) ;
578
+ }
579
+
518
580
// Keep going up till we find node_modules
519
581
// at that point read the `package.json` for each file in node_modules
520
582
// And then if that package.json has `typescript.definition` we import that file
521
- var definitions = [ ] ;
522
-
523
583
try {
524
584
var node_modules = travelUpTheDirectoryTreeTillYouFind ( projectDir , 'node_modules' , true ) ;
525
585
@@ -529,16 +589,27 @@ export function getDefinitionsForNodeModules(projectDir: string): string[] {
529
589
var package_json = JSON . parse ( fs . readFileSync ( `${ moduleDir } /package.json` ) . toString ( ) ) ;
530
590
if ( package_json . typescript ) {
531
591
if ( package_json . typescript . definition ) {
532
- definitions . push ( path . resolve ( moduleDir , './' , package_json . typescript . definition ) ) ;
592
+ let file = path . resolve ( moduleDir , './' , package_json . typescript . definition ) ;
593
+
594
+ typings [ path . basename ( file ) ] = {
595
+ filePath : file ,
596
+ version : Infinity
597
+ } ;
598
+ // Also add any files that this `.d.ts` references as long as they don't conflict with what we have
599
+ addAllReferencedFilesWithMaxVersion ( file ) ;
533
600
}
534
601
}
535
602
}
603
+
536
604
}
537
605
catch ( ex ) {
538
606
// this is best effort only at the moment
539
607
}
540
608
541
- return definitions ;
609
+ return Object . keys ( typings )
610
+ . map ( typing => typings [ typing ] . filePath )
611
+ . map ( x => consistentPath ( x ) )
612
+ . filter ( x => ! existing [ x ] ) ;
542
613
}
543
614
544
615
export function prettyJSON ( object : any ) : string {
@@ -660,3 +731,13 @@ function getDirs(rootDir: string): string[] {
660
731
}
661
732
return dirs
662
733
}
734
+
735
+ /**
736
+ * Create a quick lookup map from list
737
+ */
738
+ export function createMap ( arr : string [ ] ) : { [ string : string ] : boolean } {
739
+ return arr . reduce ( ( result : { [ string : string ] : boolean } , key : string ) => {
740
+ result [ key ] = true ;
741
+ return result ;
742
+ } , < { [ string : string ] : boolean } > { } ) ;
743
+ }
0 commit comments