@@ -259,10 +259,56 @@ def get_relative_path_from_output_directory(file_to_resolve):
259259 ]
260260 tsconfig ['checkJs' ] = True
261261
262+ # Maps paths from the Ninja generated directory (`gen/`) to the TypeScript output
263+ # directory (`tsc/`) used by split compilation targets.
264+ def to_tsc_path (p ):
265+ p_norm = path .normpath (p )
266+ gen_dir = path .normpath (path .join (os .getcwd (), 'gen' ))
267+ tsc_dir = path .normpath (path .join (os .getcwd (), 'tsc' ))
268+ if p_norm == gen_dir :
269+ return tsc_dir
270+ if p_norm .startswith (gen_dir + os .sep ):
271+ return p_norm .replace (gen_dir + os .sep , tsc_dir + os .sep , 1 )
272+ if p_norm == 'gen' :
273+ return 'tsc'
274+ if p_norm .startswith (f'gen{ os .sep } ' ):
275+ return f'tsc{ os .sep } { p_norm [4 :]} '
276+ return p
277+
262278 if (opts .deps is not None ):
263- tsconfig ['references' ] = [{'path' : src } for src in opts .deps ]
279+
280+ # Resolves project references to prefer `*-tsconfig.ref.json` across both `gen/`
281+ # and `tsc/` locations, ensuring interoperability with split compilation targets.
282+ def resolve_ref (src ):
283+ if src .endswith ('-tsconfig.json' ):
284+ ref_src = src [:- len ('-tsconfig.json' )] + '-tsconfig.ref.json'
285+ abs_ref = path .join (tsconfig_output_directory , ref_src )
286+ if path .exists (abs_ref ):
287+ return ref_src
288+ abs_tsc_ref = to_tsc_path (abs_ref )
289+ if path .exists (abs_tsc_ref ):
290+ return path .relpath (abs_tsc_ref , tsconfig_output_directory )
291+ abs_json = path .join (tsconfig_output_directory , src )
292+ if path .exists (abs_json ):
293+ return src
294+ abs_tsc_json = to_tsc_path (abs_json )
295+ if path .exists (abs_tsc_json ):
296+ return path .relpath (abs_tsc_json ,
297+ tsconfig_output_directory )
298+ if path .exists (path .dirname (abs_tsc_ref )):
299+ return path .relpath (abs_tsc_ref , tsconfig_output_directory )
300+ return ref_src
301+ return src
302+
303+ tsconfig ['references' ] = [{
304+ 'path' : resolve_ref (src )
305+ } for src in opts .deps ]
264306 if (not opts .verify_lib_check ):
265307 tsconfig ['compilerOptions' ]['skipLibCheck' ] = True
308+ # Disables redirection to source files so tsc resolves imports against emitted `.d.ts`
309+ # files of referenced projects instead of jumping directly to `.ts` sources.
310+ tsconfig ['compilerOptions' ][
311+ 'disableSourceOfProjectReferenceRedirect' ] = True
266312 tsconfig ['compilerOptions' ][
267313 'rootDir' ] = get_relative_path_from_output_directory (
268314 opts .front_end_directory )
@@ -305,8 +351,64 @@ def get_relative_path_from_output_directory(file_to_resolve):
305351 if maybe_update_tsconfig_file (tsconfig_output_location , tsconfig ) == 1 :
306352 return 1
307353
354+ # Emits a companion `*-tsconfig.ref.json` configured with declaration-only emit
355+ # and composite mode so downstream targets can reference this target.
356+ if tsconfig_output_location .endswith ('-tsconfig.json' ):
357+ tsconfig_ref_location = tsconfig_output_location [:- len (
358+ '-tsconfig.json' )] + '-tsconfig.ref.json'
359+ else :
360+ tsconfig_ref_location = tsconfig_output_location + '.ref.json'
361+ front_end_rel_dir = get_relative_path_from_output_directory (
362+ opts .front_end_directory )
363+ tsconfig_ref = {
364+ 'compilerOptions' : {
365+ 'composite' : True ,
366+ 'declaration' : True ,
367+ 'emitDeclarationOnly' : True ,
368+ 'skipLibCheck' : True ,
369+ 'rootDir' : front_end_rel_dir ,
370+ 'outDir' : '.' ,
371+ 'declarationDir' : '.' ,
372+ },
373+ 'files' :
374+ [get_relative_path_from_output_directory (x ) for x in all_ts_files ],
375+ }
376+ if 'references' in tsconfig :
377+ tsconfig_ref ['references' ] = tsconfig ['references' ]
378+ if maybe_update_tsconfig_file (tsconfig_ref_location , tsconfig_ref ) == 1 :
379+ return 1
380+
381+ # Mirrors `*-tsconfig.ref.json` to the corresponding `tsc/` directory so split
382+ # compilation consumers looking in `tsc/` can resolve project references.
383+ tsc_ref_location = to_tsc_path (tsconfig_ref_location )
384+ if tsc_ref_location != tsconfig_ref_location :
385+ tsc_ref_dir = path .dirname (tsc_ref_location )
386+ os .makedirs (tsc_ref_dir , exist_ok = True )
387+ tsc_tsconfig_ref = dict (tsconfig_ref )
388+ tsc_tsconfig_ref ['compilerOptions' ] = dict (
389+ tsconfig_ref ['compilerOptions' ])
390+ if 'rootDir' in tsc_tsconfig_ref ['compilerOptions' ]:
391+ abs_root = path .join (
392+ tsconfig_output_directory ,
393+ tsc_tsconfig_ref ['compilerOptions' ]['rootDir' ])
394+ tsc_tsconfig_ref ['compilerOptions' ]['rootDir' ] = path .relpath (
395+ abs_root , tsc_ref_dir )
396+ if 'files' in tsc_tsconfig_ref :
397+ tsc_tsconfig_ref ['files' ] = [
398+ path .relpath (path .join (tsconfig_output_directory , f ),
399+ tsc_ref_dir ) for f in tsc_tsconfig_ref ['files' ]
400+ ]
401+ if 'references' in tsconfig_ref :
402+ tsc_tsconfig_ref ['references' ] = []
403+ for ref in tsconfig_ref ['references' ]:
404+ abs_ref = path .join (tsconfig_output_directory , ref ['path' ])
405+ tsc_tsconfig_ref ['references' ].append (
406+ {'path' : path .relpath (abs_ref , tsc_ref_dir )})
407+ if maybe_update_tsconfig_file (tsc_ref_location , tsc_tsconfig_ref ) == 1 :
408+ return 1
409+
308410 # If there are no sources to compile, we can bail out and don't call tsc.
309- # That's because tsc can successfully compile dependents solely on the
411+ # That's because tsc can successfully compile dependents solely on
310412 # the tsconfig.json
311413 if len (sources ) == 0 and not opts .verify_lib_check :
312414 return 0
@@ -341,6 +443,40 @@ def get_relative_path_from_output_directory(file_to_resolve):
341443 print ('' )
342444 return 1
343445
446+ # Synchronizes generated and source `.d.ts` files to `tsc/` when compiling into `gen/`,
447+ # providing declaration files required by downstream split compilation targets.
448+ tsc_out_dir = to_tsc_path (tsconfig_output_directory )
449+ if tsc_out_dir != tsconfig_output_directory :
450+ os .makedirs (tsc_out_dir , exist_ok = True )
451+ for src_fname in sources :
452+ gen_rel = path .relpath (
453+ path .join (os .getcwd (), src_fname ),
454+ path .join (os .getcwd (), opts .front_end_directory ))
455+ if gen_rel .endswith ('.d.ts' ):
456+ gen_fname = gen_rel
457+ else :
458+ gen_fname = path .splitext (gen_rel )[0 ] + '.d.ts'
459+ gen_path = path .join (tsconfig_output_directory , gen_fname )
460+ dts_contents = None
461+ if path .exists (gen_path ):
462+ with open (gen_path , 'rb' ) as src_fp :
463+ dts_contents = src_fp .read ()
464+ elif gen_rel .endswith ('.d.ts' ) and path .exists (
465+ path .join (os .getcwd (), src_fname )):
466+ with open (path .join (os .getcwd (), src_fname ), 'rb' ) as src_fp :
467+ dts_contents = src_fp .read ()
468+ if dts_contents is not None :
469+ tsc_dts_path = path .join (tsc_out_dir , gen_fname )
470+ os .makedirs (path .dirname (tsc_dts_path ), exist_ok = True )
471+ should_write = True
472+ if path .exists (tsc_dts_path ):
473+ with open (tsc_dts_path , 'rb' ) as dst_fp :
474+ if dst_fp .read () == dts_contents :
475+ should_write = False
476+ if should_write :
477+ with open (tsc_dts_path , 'wb' ) as dst_fp :
478+ dst_fp .write (dts_contents )
479+
344480 return 0
345481
346482
0 commit comments