@@ -3,7 +3,10 @@ import { relative, dirname, sep } from 'node:path';
3
3
import { stat , readFile } from '../polyfills/fs.js' ;
4
4
import { listFiles } from '../modules/list-files.js' ;
5
5
6
- const filter = / \. ( j s | c j s | m j s | t s | c t s | m t s ) $ / ;
6
+ const importMap = new Map < string , Set < string > > ( ) ;
7
+ const processedFiles = new Set < string > ( ) ;
8
+
9
+ const extFilter = / \. ( j s | c j s | m j s | t s | c t s | m t s | j s x | t s x ) $ / ;
7
10
8
11
export const normalizePath = ( filePath : string ) =>
9
12
filePath
@@ -12,43 +15,144 @@ export const normalizePath = (filePath: string) =>
12
15
. replace ( / [ / \\ ] + / g, sep )
13
16
. replace ( / \\ / g, '/' ) ;
14
17
15
- /* c8 ignore next */
16
- export const mapTests = async ( srcDir : string , testPaths : string [ ] ) => {
17
- const allTestFiles : string [ ] = [ ] ;
18
- const allSrcFiles = await listFiles ( srcDir , { filter } ) ;
19
- const importMap = new Map < string , string [ ] > ( ) ;
18
+ export const getDeepImports = ( content : string ) : Set < string > => {
19
+ const paths : Set < string > = new Set ( ) ;
20
+ const lines = content . split ( '\n' ) ;
21
+
22
+ for ( const line of lines ) {
23
+ if ( line . includes ( 'import' ) || line . includes ( 'require' ) ) {
24
+ const path = line . match ( / [ ' " ] ( \. { 1 , 2 } \/ [ ^ ' " ] + ) [ ' " ] / ) ;
25
+
26
+ if ( path ) paths . add ( normalizePath ( path [ 1 ] . replace ( extFilter , '' ) ) ) ;
27
+ }
28
+ }
29
+
30
+ return paths ;
31
+ } ;
32
+
33
+ export const findMatchingFiles = (
34
+ srcFilesWithoutExt : Set < string > ,
35
+ srcFilesWithExt : Set < string >
36
+ ) : Set < string > => {
37
+ const matchingFiles = new Set < string > ( ) ;
38
+
39
+ srcFilesWithoutExt . forEach ( ( srcFile ) => {
40
+ const normalizedSrcFile = normalizePath ( srcFile ) ;
41
+
42
+ srcFilesWithExt . forEach ( ( fileWithExt ) => {
43
+ const normalizedFileWithExt = normalizePath ( fileWithExt ) ;
44
+
45
+ if ( normalizedFileWithExt . includes ( normalizedSrcFile ) )
46
+ matchingFiles . add ( fileWithExt ) ;
47
+ } ) ;
48
+ } ) ;
49
+
50
+ return matchingFiles ;
51
+ } ;
52
+
53
+ /* c8 ignore start */
54
+ const collectTestFiles = async (
55
+ testPaths : string [ ] ,
56
+ testFilter ?: RegExp ,
57
+ exclude ?: RegExp | RegExp [ ]
58
+ ) : Promise < Set < string > > => {
59
+ const statsPromises = testPaths . map ( ( testPath ) => stat ( testPath ) ) ;
60
+
61
+ const stats = await Promise . all ( statsPromises ) ;
20
62
21
- for ( const testPath of testPaths ) {
22
- const stats = await stat ( testPath ) ;
63
+ const listFilesPromises = stats . map ( ( stat , index ) => {
64
+ const testPath = testPaths [ index ] ;
23
65
24
- if ( stats . isDirectory ( ) ) {
25
- const testFiles = await listFiles ( testPath , { filter } ) ;
66
+ if ( stat . isDirectory ( ) )
67
+ return listFiles ( testPath , {
68
+ filter : testFilter ,
69
+ exclude,
70
+ } ) ;
26
71
27
- allTestFiles . push ( ...testFiles ) ;
28
- } else if ( stats . isFile ( ) && filter . test ( testPath ) )
29
- allTestFiles . push ( testPath ) ;
72
+ if ( stat . isFile ( ) && extFilter . test ( testPath ) ) return [ testPath ] ;
73
+ else return [ ] ;
74
+ } ) ;
75
+
76
+ const nestedTestFiles = await Promise . all ( listFilesPromises ) ;
77
+
78
+ return new Set ( nestedTestFiles . flat ( ) ) ;
79
+ } ;
80
+ /* c8 ignore stop */
81
+
82
+ /* c8 ignore start */
83
+ const processDeepImports = async (
84
+ srcFile : string ,
85
+ testFile : string ,
86
+ intersectedSrcFiles : Set < string >
87
+ ) => {
88
+ if ( processedFiles . has ( srcFile ) ) return ;
89
+ processedFiles . add ( srcFile ) ;
90
+
91
+ const srcContent = await readFile ( srcFile , 'utf-8' ) ;
92
+ const deepImports = getDeepImports ( srcContent ) ;
93
+ const matchingFiles = findMatchingFiles ( deepImports , intersectedSrcFiles ) ;
94
+
95
+ for ( const deepImport of matchingFiles ) {
96
+ if ( ! importMap . has ( deepImport ) ) importMap . set ( deepImport , new Set ( ) ) ;
97
+
98
+ importMap . get ( deepImport ) ! . add ( normalizePath ( testFile ) ) ;
99
+
100
+ await processDeepImports ( deepImport , testFile , intersectedSrcFiles ) ;
30
101
}
102
+ } ;
103
+ /* c8 ignore stop */
104
+
105
+ const createImportMap = async (
106
+ allTestFiles : Set < string > ,
107
+ allSrcFiles : Set < string >
108
+ ) => {
109
+ const intersectedSrcFiles = new Set (
110
+ Array . from ( allSrcFiles ) . filter ( ( srcFile ) => ! allTestFiles . has ( srcFile ) )
111
+ ) ;
31
112
32
- for ( const testFile of allTestFiles ) {
33
- const content = await readFile ( testFile , 'utf-8' ) ;
113
+ await Promise . all (
114
+ Array . from ( allTestFiles ) . map ( async ( testFile ) => {
115
+ const content = await readFile ( testFile , 'utf-8' ) ;
34
116
35
- for ( const srcFile of allSrcFiles ) {
36
- const relativePath = normalizePath ( relative ( dirname ( testFile ) , srcFile ) ) ;
37
- const normalizedSrcFile = normalizePath ( srcFile ) ;
117
+ for ( const srcFile of intersectedSrcFiles ) {
118
+ const relativePath = normalizePath (
119
+ relative ( dirname ( testFile ) , srcFile )
120
+ ) ;
121
+ const normalizedSrcFile = normalizePath ( srcFile ) ;
38
122
39
- /* c8 ignore start */
40
- if (
41
- content . includes ( relativePath . replace ( filter , '' ) ) ||
42
- content . includes ( normalizedSrcFile )
43
- ) {
44
- if ( ! importMap . has ( normalizedSrcFile ) )
45
- importMap . set ( normalizedSrcFile , [ ] ) ;
123
+ /* c8 ignore start */
124
+ if (
125
+ content . includes ( relativePath . replace ( extFilter , '' ) ) ||
126
+ content . includes ( normalizedSrcFile )
127
+ ) {
128
+ if ( ! importMap . has ( normalizedSrcFile ) )
129
+ importMap . set ( normalizedSrcFile , new Set ( ) ) ;
130
+ importMap . get ( normalizedSrcFile ) ! . add ( normalizePath ( testFile ) ) ;
46
131
47
- importMap . get ( normalizedSrcFile ) ! . push ( normalizePath ( testFile ) ) ;
132
+ await processDeepImports ( srcFile , testFile , intersectedSrcFiles ) ;
133
+ }
134
+ /* c8 ignore stop */
48
135
}
49
- /* c8 ignore stop */
50
- }
51
- }
136
+ } )
137
+ ) ;
138
+ } ;
139
+
140
+ /* c8 ignore next */
141
+ export const mapTests = async (
142
+ srcDir : string ,
143
+ testPaths : string [ ] ,
144
+ testFilter ?: RegExp ,
145
+ exclude ?: RegExp | RegExp [ ]
146
+ ) => {
147
+ const [ allTestFiles , allSrcFiles ] = await Promise . all ( [
148
+ collectTestFiles ( testPaths , testFilter , exclude ) ,
149
+ listFiles ( srcDir , {
150
+ filter : extFilter ,
151
+ exclude,
152
+ } ) ,
153
+ ] ) ;
154
+
155
+ await createImportMap ( allTestFiles , new Set ( allSrcFiles ) ) ;
52
156
53
157
return importMap ;
54
158
} ;
0 commit comments