2020import java .nio .file .attribute .BasicFileAttributes ;
2121import java .util .ArrayList ;
2222import java .util .Arrays ;
23+ import java .util .Comparator ;
2324import java .util .LinkedHashMap ;
2425import java .util .LinkedHashSet ;
2526import java .util .List ;
2829import java .util .concurrent .ExecutorService ;
2930import java .util .concurrent .Executors ;
3031import java .util .concurrent .TimeUnit ;
32+ import java .util .stream .Collectors ;
3133import java .util .stream .Stream ;
3234
3335import com .google .gson .Gson ;
@@ -536,6 +538,27 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
536538 Files .walkFileTree (externs , visitor );
537539 }
538540
541+ /**
542+ * Compares files in the order they should be extracted.
543+ * <p>
544+ * The ordering of tsconfig.json files can affect extraction results. Since we
545+ * extract any given source file at most once, and a source file can be included from
546+ * multiple tsconfig.json files, we sometimes have to choose arbitrarily which tsconfig.json
547+ * to use for a given file (which is based on this ordering).
548+ * <p>
549+ * We sort them to help ensure reproducible extraction. Additionally, deeply nested files are
550+ * preferred over shallow ones to help ensure files are extracted with the most specific
551+ * tsconfig.json file.
552+ */
553+ public static final Comparator <Path > PATH_ORDERING = new Comparator <Path >() {
554+ public int compare (Path f1 , Path f2 ) {
555+ if (f1 .getNameCount () != f2 .getNameCount ()) {
556+ return f2 .getNameCount () - f1 .getNameCount ();
557+ }
558+ return f1 .compareTo (f2 );
559+ }
560+ };
561+
539562 /** Extract all supported candidate files that pass the filters. */
540563 private void extractSource () throws IOException {
541564 // default extractor
@@ -554,6 +577,14 @@ private void extractSource() throws IOException {
554577 Set <Path > filesToExtract = new LinkedHashSet <>();
555578 List <Path > tsconfigFiles = new ArrayList <>();
556579 findFilesToExtract (defaultExtractor , filesToExtract , tsconfigFiles );
580+
581+ tsconfigFiles = tsconfigFiles .stream ()
582+ .sorted (PATH_ORDERING )
583+ .collect (Collectors .toList ());
584+
585+ filesToExtract = filesToExtract .stream ()
586+ .sorted (PATH_ORDERING )
587+ .collect (Collectors .toCollection (() -> new LinkedHashSet <>()));
557588
558589 DependencyInstallationResult dependencyInstallationResult = DependencyInstallationResult .empty ;
559590 if (!tsconfigFiles .isEmpty () && this .installDependencies ) {
@@ -902,7 +933,7 @@ private Set<Path> extractTypeScript(
902933 logEndProcess (start , "Done opening project " + projectFile );
903934 // Extract all files belonging to this project which are also matched
904935 // by our include/exclude filters.
905- List <File > typeScriptFiles = new ArrayList <File >();
936+ List <Path > typeScriptFiles = new ArrayList <Path >();
906937 for (File sourceFile : project .getSourceFiles ()) {
907938 Path sourcePath = sourceFile .toPath ();
908939 if (!files .contains (normalizePath (sourcePath ))) continue ;
@@ -912,9 +943,10 @@ private Set<Path> extractTypeScript(
912943 continue ;
913944 }
914945 if (!extractedFiles .contains (sourcePath )) {
915- typeScriptFiles .add (sourcePath . toFile () );
946+ typeScriptFiles .add (sourcePath );
916947 }
917948 }
949+ typeScriptFiles .sort (PATH_ORDERING );
918950 extractTypeScriptFiles (typeScriptFiles , extractedFiles , extractor , extractorState );
919951 tsParser .closeProject (projectFile );
920952 }
@@ -926,11 +958,11 @@ private Set<Path> extractTypeScript(
926958 }
927959
928960 // Extract remaining TypeScript files.
929- List <File > remainingTypeScriptFiles = new ArrayList <File >();
961+ List <Path > remainingTypeScriptFiles = new ArrayList <>();
930962 for (Path f : files ) {
931963 if (!extractedFiles .contains (f )
932964 && FileType .forFileExtension (f .toFile ()) == FileType .TYPESCRIPT ) {
933- remainingTypeScriptFiles .add (f . toFile () );
965+ remainingTypeScriptFiles .add (f );
934966 }
935967 }
936968 if (!remainingTypeScriptFiles .isEmpty ()) {
@@ -1018,15 +1050,18 @@ public void verifyTypeScriptInstallation(ExtractorState extractorState) {
10181050 }
10191051
10201052 public void extractTypeScriptFiles (
1021- List <File > files ,
1053+ List <Path > files ,
10221054 Set <Path > extractedFiles ,
10231055 FileExtractor extractor ,
10241056 ExtractorState extractorState ) {
1025- extractorState .getTypeScriptParser ().prepareFiles (files );
1026- for (File f : files ) {
1027- Path path = f .toPath ();
1057+ List <File > list = files
1058+ .stream ()
1059+ .sorted (PATH_ORDERING )
1060+ .map (p -> p .toFile ()).collect (Collectors .toList ());
1061+ extractorState .getTypeScriptParser ().prepareFiles (list );
1062+ for (Path path : files ) {
10281063 extractedFiles .add (path );
1029- extract (extractor , f . toPath () , extractorState );
1064+ extract (extractor , path , extractorState );
10301065 }
10311066 }
10321067
0 commit comments