@@ -43,11 +43,18 @@ export class TSServiceManager {
4343export class TSService {
4444 private readonly watch : ts . WatchOfConfigFile < ts . BuilderProgram > ;
4545
46+ private readonly patchedHostSet = new WeakSet < ts . CompilerHost > ( ) ;
47+
4648 private readonly tsconfigPath : string ;
4749
4850 public readonly extraFileExtensions : string [ ] ;
4951
50- private currTarget = {
52+ private currTarget : {
53+ code : string ;
54+ filePath : string ;
55+ sourceFile ?: ts . SourceFile ;
56+ dirMap : Map < string , { name : string ; path : string } > ;
57+ } = {
5158 code : "" ,
5259 filePath : "" ,
5360 dirMap : new Map < string , { name : string ; path : string } > ( ) ,
@@ -119,7 +126,90 @@ export class TSService {
119126 tsconfigPath : string ,
120127 extraFileExtensions : string [ ]
121128 ) : ts . WatchOfConfigFile < ts . BuilderProgram > {
122- const normalizedTsconfigPaths = new Set ( [ normalizeFileName ( tsconfigPath ) ] ) ;
129+ type CreateProgram = ts . CreateProgram < ts . BuilderProgram > ;
130+
131+ const createAbstractBuilder = (
132+ ...args : Parameters < CreateProgram >
133+ ) : ReturnType < CreateProgram > => {
134+ const [
135+ rootNames ,
136+ options ,
137+ argHost ,
138+ oldProgram ,
139+ configFileParsingDiagnostics ,
140+ projectReferences ,
141+ ] = args ;
142+
143+ const host : ts . CompilerHost = argHost ! ;
144+ if ( ! this . patchedHostSet . has ( host ) ) {
145+ this . patchedHostSet . add ( host ) ;
146+
147+ const getTargetSourceFile = (
148+ fileName : string ,
149+ languageVersionOrOptions : ts . ScriptTarget | ts . CreateSourceFileOptions
150+ ) => {
151+ if (
152+ this . currTarget . filePath === normalizeFileName ( fileName ) &&
153+ isExtra ( fileName , extraFileExtensions )
154+ ) {
155+ return ( this . currTarget . sourceFile ??= ts . createSourceFile (
156+ this . currTarget . filePath ,
157+ this . currTarget . code ,
158+ languageVersionOrOptions ,
159+ true ,
160+ ts . ScriptKind . TSX
161+ ) ) ;
162+ }
163+ return null ;
164+ } ;
165+
166+ /* eslint-disable @typescript-eslint/unbound-method -- ignore */
167+ const original = {
168+ getSourceFile : host . getSourceFile ,
169+ getSourceFileByPath : host . getSourceFileByPath ! ,
170+ } ;
171+ /* eslint-enable @typescript-eslint/unbound-method -- ignore */
172+ host . getSourceFile = ( fileName , languageVersionOrOptions , ...args ) => {
173+ const originalSourceFile = original . getSourceFile . call (
174+ host ,
175+ fileName ,
176+ languageVersionOrOptions ,
177+ ...args
178+ ) ;
179+ return (
180+ getTargetSourceFile ( fileName , languageVersionOrOptions ) ??
181+ originalSourceFile
182+ ) ;
183+ } ;
184+ host . getSourceFileByPath = (
185+ fileName ,
186+ path ,
187+ languageVersionOrOptions ,
188+ ...args
189+ ) => {
190+ const originalSourceFile = original . getSourceFileByPath . call (
191+ host ,
192+ fileName ,
193+ path ,
194+ languageVersionOrOptions ,
195+ ...args
196+ ) ;
197+ return (
198+ getTargetSourceFile ( fileName , languageVersionOrOptions ) ??
199+ originalSourceFile
200+ ) ;
201+ } ;
202+ }
203+ return ts . createAbstractBuilder (
204+ rootNames ,
205+ options ,
206+ host ,
207+ oldProgram ,
208+ configFileParsingDiagnostics ,
209+ projectReferences
210+ ) ;
211+ } ;
212+
123213 const watchCompilerHost = ts . createWatchCompilerHost (
124214 tsconfigPath ,
125215 {
@@ -132,19 +222,19 @@ export class TSService {
132222 allowNonTsExtensions : true ,
133223 } ,
134224 ts . sys ,
135- ts . createAbstractBuilder ,
225+ createAbstractBuilder ,
136226 ( diagnostic ) => {
137227 throw new Error ( formatDiagnostics ( [ diagnostic ] ) ) ;
138228 } ,
139229 ( ) => {
140230 // Not reported in reportWatchStatus.
141231 } ,
142- undefined
143- // extraFileExtensions.map((extension) => ({
144- // extension,
145- // isMixedContent: true,
146- // scriptKind: ts.ScriptKind.Deferred,
147- // }))
232+ undefined ,
233+ extraFileExtensions . map ( ( extension ) => ( {
234+ extension,
235+ isMixedContent : true ,
236+ scriptKind : ts . ScriptKind . Deferred ,
237+ } ) )
148238 ) ;
149239 const original = {
150240 // eslint-disable-next-line @typescript-eslint/unbound-method -- Store original
@@ -159,11 +249,12 @@ export class TSService {
159249 getDirectories : watchCompilerHost . getDirectories ! ,
160250 } ;
161251 watchCompilerHost . getDirectories = ( dirName , ...args ) => {
162- return distinctArray (
252+ const result = distinctArray (
163253 ...original . getDirectories . call ( watchCompilerHost , dirName , ...args ) ,
164254 // Include the path to the target file if the target file does not actually exist.
165255 this . currTarget . dirMap . get ( normalizeFileName ( dirName ) ) ?. name
166256 ) ;
257+ return result ;
167258 } ;
168259 watchCompilerHost . directoryExists = ( dirName , ...args ) => {
169260 return (
@@ -173,21 +264,23 @@ export class TSService {
173264 ) ;
174265 } ;
175266 watchCompilerHost . readDirectory = ( dirName , ...args ) => {
176- const results = original . readDirectory . call (
267+ let results = original . readDirectory . call (
177268 watchCompilerHost ,
178269 dirName ,
179270 ...args
180271 ) ;
181272
182273 // Include the target file if the target file does not actually exist.
183274 const file = this . currTarget . dirMap . get ( normalizeFileName ( dirName ) ) ;
184- if ( file && file . path === this . currTarget . filePath ) {
185- results . push ( file . path ) ;
275+ if ( file ) {
276+ if ( file . path === this . currTarget . filePath ) {
277+ results . push ( file . path ) ;
278+ } else {
279+ results = results . filter ( ( f ) => file . path !== f && file . name !== f ) ;
280+ }
186281 }
187282
188- return distinctArray ( ...results ) . map ( ( result ) =>
189- toVirtualTSXFileName ( result , extraFileExtensions )
190- ) ;
283+ return distinctArray ( ...results ) ;
191284 } ;
192285 watchCompilerHost . readFile = ( fileName , ...args ) => {
193286 const realFileName = toRealFileName ( fileName , extraFileExtensions ) ;
@@ -225,35 +318,6 @@ export class TSService {
225318 if ( ! code ) {
226319 return code ;
227320 }
228- // If it's tsconfig, it will take care of rewriting the `include`.
229- if ( normalizedTsconfigPaths . has ( normalized ) ) {
230- const configJson = ts . parseConfigFileTextToJson ( realFileName , code ) ;
231- if ( ! configJson . config ) {
232- return code ;
233- }
234- if ( configJson . config . extends ) {
235- // If it references another tsconfig, rewrite the `include` for that file as well.
236- for ( const extendConfigPath of [ configJson . config . extends ] . flat ( ) ) {
237- normalizedTsconfigPaths . add (
238- normalizeFileName (
239- toAbsolutePath ( extendConfigPath , path . dirname ( normalized ) )
240- )
241- ) ;
242- }
243- }
244-
245- if ( ! configJson . config . include ) {
246- return code ;
247- }
248- const include = [ configJson . config . include ]
249- . flat ( )
250- . map ( ( s ) => toVirtualTSXFileName ( s , extraFileExtensions ) ) ;
251-
252- return JSON . stringify ( {
253- ...configJson . config ,
254- include,
255- } ) ;
256- }
257321 return transformExtraFile ( code , {
258322 filePath : normalized ,
259323 current : false ,
@@ -377,14 +441,6 @@ function getRefreshTargetFileNames(
377441 return [ fileName ] ;
378442}
379443
380- /** If the given filename has extra extensions, returns the real virtual filename. */
381- function toVirtualTSXFileName ( fileName : string , extraFileExtensions : string [ ] ) {
382- if ( isExtra ( fileName , extraFileExtensions ) ) {
383- return `${ fileName } .tsx` ;
384- }
385- return fileName ;
386- }
387-
388444/** If the given filename has extra extensions, returns the d.ts filename. */
389445function toExtraDtsFileName ( fileName : string , extraFileExtensions : string [ ] ) {
390446 if ( isExtra ( fileName , extraFileExtensions ) ) {
0 commit comments