Skip to content

Commit f2496da

Browse files
szuenddevtools-frontend-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[ts_library] Emit tsconfig.ref.json and sync to tsc in ts_library.py
Emits *-tsconfig.ref.json in ts_library.py, syncs declarations to out/<config>/tsc in bundled mode, and resolves references to tsconfig.ref.json across both gen and tsc directories. Note that this is an intermediate state so existing ts_library targets work seemlessly with ts_library_split and vice-versa. ts_library.py will be deleted once the migration is done. R=alexrudenko@chromium.org TAG=agy CONV=411b687e-caee-40f1-a4fe-cff3dd4ac2de Bug: 547522497 Change-Id: I066e9410939f410b9985370f1e2e4b491c702eae Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/8284781 Commit-Queue: Alex Rudenko <alexrudenko@chromium.org> Reviewed-by: Alex Rudenko <alexrudenko@chromium.org> Auto-Submit: Simon Zünd <szuend@chromium.org>
1 parent 7976d81 commit f2496da

1 file changed

Lines changed: 138 additions & 2 deletions

File tree

scripts/build/typescript/ts_library.py

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)