WebUI: Validate all parent tsconfig files in ts_library().
Previously only the last tsconfig file in the inheritance chain would be validated (usually specified with `tsconfig_base=` or automatically inferred when not specified). Any parent files referenced via "extends: ..." would not be validated. This is in preparation of walking the tsconfig inheritance chain to automatically deduce when to use `skipLibCheck=true` to improve overall TypeScript build time performance. Bug: 397737230 Change-Id: I0ccd5fe3758fe915e437d8bc443fcb85a85c0159 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6300796 Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org> Reviewed-by: Rebekah Potter <rbpotter@chromium.org> Cr-Commit-Position: refs/heads/main@{#1424909}
This commit is contained in:
tools/typescript
4
tools/typescript/tests/project5/tsconfig_base2.json
Normal file
4
tools/typescript/tests/project5/tsconfig_base2.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig_base.json",
|
||||
"compilerOptions": {}
|
||||
}
|
@ -31,6 +31,14 @@ def _write_tsconfig_json(gen_dir, tsconfig, tsconfig_file):
|
||||
json.dump(tsconfig, generated_tsconfig, indent=2)
|
||||
return
|
||||
|
||||
|
||||
# Normalize `input_path` from being relative to _CWD, to being relative to
|
||||
# _SRC_DIR.
|
||||
def _relative_to_src(input_path):
|
||||
return os.path.relpath(os.path.normpath(os.path.join(_CWD, input_path)),
|
||||
_SRC_DIR)
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--deps', nargs='*')
|
||||
@ -77,39 +85,60 @@ def main(argv):
|
||||
|
||||
tsconfig['compilerOptions'] = collections.OrderedDict()
|
||||
|
||||
with io.open(tsconfig_base_file, encoding='utf-8', mode='r') as f:
|
||||
tsconfig_base = json.loads(f.read())
|
||||
# Recursively iterate all inherited tsconfig files, walking up the
|
||||
# inheritance chain.
|
||||
parent_tsconfig_file = tsconfig_base_file
|
||||
parent_tsconfig_counter = 0
|
||||
while parent_tsconfig_file != None:
|
||||
with io.open(parent_tsconfig_file, encoding='utf-8', mode='r') as f:
|
||||
parent_tsconfig = json.loads(f.read())
|
||||
|
||||
is_base_tsconfig = args.tsconfig_base is None or \
|
||||
args.tsconfig_base.endswith('/tools/typescript/tsconfig_base.json')
|
||||
is_tsconfig_valid, error = validateTsconfigJson(tsconfig_base,
|
||||
tsconfig_base_file,
|
||||
is_base_tsconfig)
|
||||
if not is_tsconfig_valid:
|
||||
raise AssertionError(error)
|
||||
# Validate each encountered tsconfig files against a set of constraints.
|
||||
parent_tsconfig_file_normalized = _relative_to_src(parent_tsconfig_file)
|
||||
is_base_tsconfig = parent_tsconfig_file_normalized.endswith(
|
||||
os.path.normpath('tools/typescript/tsconfig_base.json'))
|
||||
is_tsconfig_valid, error = validateTsconfigJson(
|
||||
parent_tsconfig, parent_tsconfig_file_normalized, is_base_tsconfig)
|
||||
if not is_tsconfig_valid:
|
||||
raise AssertionError(error)
|
||||
|
||||
# Work-around for https://github.com/microsoft/TypeScript/issues/30024. Need
|
||||
# to append 'trusted-types' in cases where the default configuration's
|
||||
# 'types' field is overridden, because of the Chromium patch at
|
||||
# third_party/node/typescript.patch
|
||||
# TODO(dpapad): Remove if/when the TypeScript bug has been fixed.
|
||||
if 'compilerOptions' in tsconfig_base and \
|
||||
'types' in tsconfig_base['compilerOptions']:
|
||||
types = tsconfig_base['compilerOptions']['types']
|
||||
# Work-around for https://github.com/microsoft/TypeScript/issues/30024.
|
||||
# Need to append 'trusted-types' in cases where the default
|
||||
# configuration's 'types' field is overridden, because of the Chromium
|
||||
# patch at third_party/node/patches/typescript.patch. Only look in the
|
||||
# last tsconfig in the chain for any 'types' overrides as it seems
|
||||
# sufficent since shared tsconfigs in tools/typescript/ already include
|
||||
# 'trusted-types'.
|
||||
# TODO(dpapad): Remove if/when the TypeScript bug has been fixed.
|
||||
if parent_tsconfig_counter == 0:
|
||||
if 'compilerOptions' in parent_tsconfig and \
|
||||
'types' in parent_tsconfig['compilerOptions']:
|
||||
types = parent_tsconfig['compilerOptions']['types']
|
||||
|
||||
if 'trusted-types' not in types:
|
||||
# Ensure that typeRoots is not overridden in an incompatible way.
|
||||
ERROR_MSG = ('Need to include \'third_party/node/node_modules/@types\' '
|
||||
'when overriding the default typeRoots')
|
||||
assert ('typeRoots' in tsconfig_base['compilerOptions']), ERROR_MSG
|
||||
type_roots = tsconfig_base['compilerOptions']['typeRoots']
|
||||
has_type_root = any(r.endswith('third_party/node/node_modules/@types') \
|
||||
for r in type_roots)
|
||||
assert has_type_root, ERROR_MSG
|
||||
if 'trusted-types' not in types:
|
||||
# Ensure that typeRoots is not overridden in an incompatible way.
|
||||
ERROR_MSG = (
|
||||
'Need to include \'third_party/node/node_modules/@types\' '
|
||||
'when overriding the default typeRoots')
|
||||
assert ('typeRoots'
|
||||
in parent_tsconfig['compilerOptions']), ERROR_MSG
|
||||
type_roots = parent_tsconfig['compilerOptions']['typeRoots']
|
||||
has_type_root = any(r.endswith('third_party/node/node_modules/@types') \
|
||||
for r in type_roots)
|
||||
assert has_type_root, ERROR_MSG
|
||||
|
||||
augmented_types = types.copy()
|
||||
augmented_types.append('trusted-types')
|
||||
tsconfig['compilerOptions']['types'] = augmented_types
|
||||
augmented_types = types.copy()
|
||||
augmented_types.append('trusted-types')
|
||||
tsconfig['compilerOptions']['types'] = augmented_types
|
||||
|
||||
# Calculate next step in the inheritance chain.
|
||||
extends = parent_tsconfig.get('extends', None)
|
||||
if extends != None:
|
||||
parent_tsconfig_file = os.path.normpath(
|
||||
os.path.join(os.path.dirname(parent_tsconfig_file), extends))
|
||||
parent_tsconfig_counter += 1
|
||||
else:
|
||||
parent_tsconfig_file = None
|
||||
|
||||
tsconfig['compilerOptions']['rootDir'] = root_dir
|
||||
tsconfig['compilerOptions']['outDir'] = out_dir
|
||||
|
@ -434,7 +434,46 @@ class TsLibraryTest(unittest.TestCase):
|
||||
])
|
||||
except AssertionError as err:
|
||||
self.assertTrue(
|
||||
str(err).startswith('Invalid |composite| flag detected in '))
|
||||
str(err).replace('\\', '/').startswith(
|
||||
'Invalid |composite| flag detected in '
|
||||
'tools/typescript/tests/project5/tsconfig_base.json.'
|
||||
))
|
||||
else:
|
||||
self.fail('Failed to detect error')
|
||||
|
||||
# Test error case where the project's tsconfig file inherits from another
|
||||
# tsconfig file that should fail validation.
|
||||
def testTsConfigValidationErrorInParent(self):
|
||||
self._out_folder = tempfile.mkdtemp(dir=_CWD)
|
||||
root_dir = os.path.join(_HERE_DIR, 'tests', 'project5')
|
||||
gen_dir = os.path.join(self._out_folder, 'tools', 'typescript', 'tests',
|
||||
'project5')
|
||||
try:
|
||||
ts_library.main([
|
||||
'--output_suffix',
|
||||
'build_ts',
|
||||
'--root_gen_dir',
|
||||
os.path.relpath(self._out_folder, gen_dir),
|
||||
'--root_src_dir',
|
||||
os.path.relpath(os.path.join(_HERE_DIR, 'tests'), gen_dir),
|
||||
'--root_dir',
|
||||
os.path.relpath(root_dir, _CWD),
|
||||
'--gen_dir',
|
||||
os.path.relpath(gen_dir, _CWD),
|
||||
'--out_dir',
|
||||
os.path.relpath(gen_dir, _CWD),
|
||||
'--in_files',
|
||||
'bar.ts',
|
||||
'--tsconfig_base',
|
||||
os.path.relpath(os.path.join(root_dir, 'tsconfig_base2.json'),
|
||||
gen_dir),
|
||||
])
|
||||
except AssertionError as err:
|
||||
self.assertTrue(
|
||||
str(err).replace('\\', '/').startswith(
|
||||
'Invalid |composite| flag detected in '
|
||||
'tools/typescript/tests/project5/tsconfig_base.json.'
|
||||
))
|
||||
else:
|
||||
self.fail('Failed to detect error')
|
||||
|
||||
|
@ -93,12 +93,12 @@ def validateTsconfigJson(tsconfig, tsconfig_file, is_base_tsconfig):
|
||||
for param, param_value in tsconfig['compilerOptions'].items():
|
||||
if param not in _allowed_compiler_options:
|
||||
return False, f'Disallowed |{param}| flag detected in '+ \
|
||||
f'{tsconfig_file}.'
|
||||
f'\'{tsconfig_file}\'.'
|
||||
else:
|
||||
allowed_values = _allowed_compiler_options[param]
|
||||
if (allowed_values is not None and param_value not in allowed_values):
|
||||
return False, f'Disallowed value |{param_value}| for |{param}| ' + \
|
||||
f'flag detected in {tsconfig_file}. Must be one of ' + \
|
||||
f'flag detected in \'{tsconfig_file}\'. Must be one of ' + \
|
||||
f'{allowed_values}.'
|
||||
|
||||
return True, None
|
||||
|
Reference in New Issue
Block a user