11import { existsSync } from 'fs' ;
22import { join , resolve } from 'path' ;
33import { Project , Node , ts , SyntaxKind } from 'ts-morph' ;
4+ import chalk from 'chalk' ;
45import { ChangedFiles , getChangedFiles } from './git' ;
56import { findRootNode , getPackageNameByPath } from './utils' ;
67import { TrueAffected , TrueAffectedProject } from './types' ;
@@ -21,14 +22,29 @@ const ignoredRootNodeTypes = [
2122
2223export const DEFAULT_INCLUDE_TEST_FILES = / \. ( s p e c | t e s t ) \. ( t s | j s ) x ? / ;
2324
25+ const DEFAULT_LOGGER = {
26+ ...console ,
27+ debug : process . env [ 'DEBUG' ] === 'true' ? console . debug : ( ) => { } ,
28+ } ;
29+
2430export const trueAffected = async ( {
2531 cwd,
2632 rootTsConfig,
2733 base = 'origin/main' ,
2834 projects,
2935 include = [ DEFAULT_INCLUDE_TEST_FILES ] ,
36+ logger = DEFAULT_LOGGER ,
3037 __experimentalLockfileCheck = false ,
3138} : TrueAffected ) => {
39+ logger . debug ( 'Getting affected projects' ) ;
40+ if ( rootTsConfig != null ) {
41+ logger . debug (
42+ `Creating project with root tsconfig from ${ chalk . bold (
43+ resolve ( cwd , rootTsConfig )
44+ ) } `
45+ ) ;
46+ }
47+
3248 const project = new Project ( {
3349 compilerOptions : {
3450 allowJs : true ,
@@ -41,23 +57,27 @@ export const trueAffected = async ({
4157 } ) ,
4258 } ) ;
4359
44- const implicitDeps = (
45- projects . filter (
46- ( { implicitDependencies = [ ] } ) => implicitDependencies . length > 0
47- ) as Required < TrueAffectedProject > [ ]
48- ) . reduce (
49- ( acc , { name, implicitDependencies } ) =>
50- acc . set ( name , implicitDependencies ) ,
51- new Map < string , string [ ] > ( )
52- ) ;
53-
5460 projects . forEach (
55- ( { sourceRoot, tsConfig = join ( sourceRoot , 'tsconfig.json' ) } ) => {
61+ ( { name , sourceRoot, tsConfig = join ( sourceRoot , 'tsconfig.json' ) } ) => {
5662 const tsConfigPath = resolve ( cwd , tsConfig ) ;
5763
5864 if ( existsSync ( tsConfigPath ) ) {
65+ logger . debug (
66+ `Adding source files for project ${ chalk . bold (
67+ name
68+ ) } from tsconfig at ${ chalk . bold ( tsConfigPath ) } `
69+ ) ;
70+
5971 project . addSourceFilesFromTsConfig ( tsConfigPath ) ;
6072 } else {
73+ logger . debug (
74+ `Could not find a tsconfig for project ${ chalk . bold (
75+ name
76+ ) } , adding source files paths in ${ chalk . bold (
77+ resolve ( cwd , sourceRoot )
78+ ) } `
79+ ) ;
80+
6181 project . addSourceFilesAtPaths (
6282 join ( resolve ( cwd , sourceRoot ) , '**/*.{ts,js}' )
6383 ) ;
@@ -70,11 +90,13 @@ export const trueAffected = async ({
7090 cwd,
7191 } ) ;
7292
93+ logger . debug ( `Found ${ chalk . bold ( changedFiles . length ) } changed files` ) ;
94+
7395 const sourceChangedFiles = changedFiles . filter (
7496 ( { filePath } ) => project . getSourceFile ( resolve ( cwd , filePath ) ) != null
7597 ) ;
7698
77- const ignoredPaths = [ './node_modules' , './dist' , './.git' ] ;
99+ const ignoredPaths = [ './node_modules' , './build' , './ dist', './.git' ] ;
78100
79101 const nonSourceChangedFiles = changedFiles
80102 . filter (
@@ -83,12 +105,26 @@ export const trueAffected = async ({
83105 ! filePath . endsWith ( lockFileName ) &&
84106 project . getSourceFile ( resolve ( cwd , filePath ) ) == null
85107 )
86- . flatMap ( ( { filePath : changedFilePath } ) =>
87- findNonSourceAffectedFiles ( cwd , changedFilePath , ignoredPaths )
108+ . flatMap ( ( { filePath : changedFilePath } ) => {
109+ logger . debug (
110+ `Finding non-source affected files for ${ chalk . bold ( changedFilePath ) } `
111+ ) ;
112+
113+ return findNonSourceAffectedFiles ( cwd , changedFilePath , ignoredPaths ) ;
114+ } ) ;
115+
116+ if ( nonSourceChangedFiles . length > 0 ) {
117+ logger . debug (
118+ `Found ${ chalk . bold (
119+ nonSourceChangedFiles . length
120+ ) } non-source affected files`
88121 ) ;
122+ }
89123
90124 let changedFilesByLockfile : ChangedFiles [ ] = [ ] ;
91125 if ( __experimentalLockfileCheck && hasLockfileChanged ( changedFiles ) ) {
126+ logger . debug ( 'Lockfile has changed, finding affected files' ) ;
127+
92128 changedFilesByLockfile = findAffectedFilesByLockfile (
93129 cwd ,
94130 base ,
@@ -115,6 +151,14 @@ export const trueAffected = async ({
115151 . map ( ( { filePath } ) => getPackageNameByPath ( filePath , projects ) )
116152 . filter ( ( v ) : v is string => v != null ) ;
117153
154+ if ( changedIncludedFilesPackages . length > 0 ) {
155+ logger . debug (
156+ `Found ${ chalk . bold (
157+ changedIncludedFilesPackages . length
158+ ) } affected packages from included files`
159+ ) ;
160+ }
161+
118162 const affectedPackages = new Set < string > ( changedIncludedFilesPackages ) ;
119163 const visitedIdentifiers = new Map < string , string [ ] > ( ) ;
120164
@@ -137,17 +181,36 @@ export const trueAffected = async ({
137181 const identifierName = identifier . getText ( ) ;
138182 const path = rootNode . getSourceFile ( ) . getFilePath ( ) ;
139183
184+ logger . debug (
185+ `Found identifier ${ chalk . bold ( identifierName ) } in ${ chalk . bold ( path ) } `
186+ ) ;
187+
140188 if ( identifierName && path ) {
141- const visited = visitedIdentifiers . get ( identifierName ) ?? [ ] ;
142- if ( visited . includes ( path ) ) return ;
143- visitedIdentifiers . set ( identifierName , [ ...visited , path ] ) ;
189+ const visited = visitedIdentifiers . get ( path ) ?? [ ] ;
190+ if ( visited . includes ( identifierName ) ) {
191+ logger . debug (
192+ `Already visited ${ chalk . bold ( identifierName ) } in ${ chalk . bold ( path ) } `
193+ ) ;
194+
195+ return ;
196+ }
197+
198+ visitedIdentifiers . set ( path , [ ...visited , identifierName ] ) ;
199+
200+ logger . debug (
201+ `Visiting ${ chalk . bold ( identifierName ) } in ${ chalk . bold ( path ) } `
202+ ) ;
144203 }
145204
146205 refs . forEach ( ( node ) => {
147206 const sourceFile = node . getSourceFile ( ) ;
148207 const pkg = getPackageNameByPath ( sourceFile . getFilePath ( ) , projects ) ;
149208
150- if ( pkg ) affectedPackages . add ( pkg ) ;
209+ if ( pkg ) {
210+ affectedPackages . add ( pkg ) ;
211+
212+ logger . debug ( `Added package ${ chalk . bold ( pkg ) } to affected packages` ) ;
213+ }
151214
152215 findReferencesLibs ( node ) ;
153216 } ) ;
@@ -170,7 +233,17 @@ export const trueAffected = async ({
170233
171234 const pkg = getPackageNameByPath ( sourceFile . getFilePath ( ) , projects ) ;
172235
173- if ( pkg ) affectedPackages . add ( pkg ) ;
236+ if ( pkg ) {
237+ affectedPackages . add ( pkg ) ;
238+
239+ logger . debug (
240+ `Added package ${ chalk . bold (
241+ pkg
242+ ) } to affected packages for changed line ${ chalk . bold (
243+ line
244+ ) } in ${ chalk . bold ( filePath ) } `
245+ ) ;
246+ }
174247
175248 findReferencesLibs ( changedNode ) ;
176249 } catch {
@@ -179,12 +252,30 @@ export const trueAffected = async ({
179252 } ) ;
180253 } ) ;
181254
255+ const implicitDeps = (
256+ projects . filter (
257+ ( { implicitDependencies = [ ] } ) => implicitDependencies . length > 0
258+ ) as Required < TrueAffectedProject > [ ]
259+ ) . reduce (
260+ ( acc , { name, implicitDependencies } ) =>
261+ acc . set ( name , implicitDependencies ) ,
262+ new Map < string , string [ ] > ( )
263+ ) ;
264+
182265 // add implicit deps
183266 affectedPackages . forEach ( ( pkg ) => {
184267 const deps = Array . from ( implicitDeps . entries ( ) )
185268 . filter ( ( [ , deps ] ) => deps . includes ( pkg ) )
186269 . map ( ( [ name ] ) => name ) ;
187270
271+ if ( deps . length > 0 ) {
272+ logger . debug (
273+ `Adding implicit dependencies ${ chalk . bold (
274+ deps . join ( ', ' )
275+ ) } to ${ chalk . bold ( pkg ) } `
276+ ) ;
277+ }
278+
188279 deps . forEach ( ( dep ) => affectedPackages . add ( dep ) ) ;
189280 } ) ;
190281
0 commit comments