| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Overview: ts_library() supports only a small subset of all possible tsconfig |
| # configurations. Some options cannot be used in general, and some options |
| # should only be set through the ts_library() gn args corresponding to them. In |
| # the latter case, there are requirements for how some of these args can be |
| # configured. This file contains validation logic for tsconfig files and the gn |
| # args corresponding to config options to limit the possibility of unsupported |
| # configurations proliferating in the codebase. |
| |
| import os |
| |
| _CWD = os.getcwd().replace('\\', '/') |
| _HERE_DIR = os.path.dirname(__file__) |
| _SRC_DIR = os.path.normpath(os.path.join(_HERE_DIR, '..', |
| '..')).replace('\\', '/') |
| |
| # Options configured by the ts_library should not be set separately. |
| _tsconfig_compiler_options_mappings = { |
| 'composite': 'composite=true', |
| 'declaration': 'composite=true', |
| 'inlineSourceMap': 'enable_source_maps=true', |
| 'inlineSources': 'enable_source_maps=true', |
| 'outDir': 'out_dir', |
| 'paths': 'path_mappings', |
| 'rootDir': 'root_dir', |
| 'tsBuildInfoFile': 'composite=true', |
| } |
| |
| # Allowed options within tsconfig_base.json |
| _allowed_config_options = [ |
| 'extends', |
| 'compilerOptions', |
| ] |
| |
| # Allowed compilerOptions |
| _allowed_compiler_options = [ |
| 'allowUmdGlobalAccess', |
| 'isolatedModules', |
| 'lib', |
| 'noPropertyAccessFromIndexSignature', |
| 'noUncheckedIndexedAccess', |
| 'noUnusedLocals', |
| 'skipLibCheck', |
| 'strictPropertyInitialization', |
| 'typeRoots', |
| 'types', |
| 'useDefineForClassFields', |
| ] |
| |
| |
| def validateTsconfigJson(tsconfig, tsconfig_file, is_base_tsconfig): |
| # Special exception for material_web_components, which uses ts_library() |
| # in an unsupported way. |
| if 'third_party/material_web_components/tsconfig_base.json' in tsconfig_file: |
| return True, None |
| |
| # TODO(b/267329383): Migrate A11y to TypeScript. Accessibility code has |
| # different requirements for the migration because of this we need both |
| # allowjs and a custom tsconfig. |
| if 'accessibility/tsconfig.base.json' in tsconfig_file: |
| return True, None |
| |
| if not is_base_tsconfig: |
| for param in tsconfig.keys(): |
| if param not in _allowed_config_options: |
| return False, f'Invalid |{param}| option detected in ' + \ |
| f'{tsconfig_file}.Only |extends| and |compilerOptions| may ' + \ |
| 'be specified.' |
| |
| if 'compilerOptions' in tsconfig: |
| for param in _tsconfig_compiler_options_mappings.keys(): |
| if param in tsconfig['compilerOptions']: |
| tslibrary_flag = _tsconfig_compiler_options_mappings[param] |
| return False, f'Invalid |{param}| flag detected in {tsconfig_file}.' + \ |
| f' Use the dedicated |{tslibrary_flag}| attribute in '+ \ |
| 'ts_library() instead.' |
| |
| if 'ui/file_manager' in tsconfig_file: |
| # File manager uses ts_library() in an unsupported way. Just return true |
| # here for this special case. |
| return True, None |
| |
| if not is_base_tsconfig: |
| for input_param in tsconfig['compilerOptions'].keys(): |
| if input_param not in _allowed_compiler_options: |
| return False, f'Disallowed |{input_param}| flag detected in '+ \ |
| f'{tsconfig_file}.' |
| |
| return True, None |
| |
| |
| # Note 1: DO NOT add any directories here corresponding to newly added or |
| # existing TypeScript code. Instead use TypeScript, which is a requirement. |
| # Note 2: Only add a directory here if you are in the process of migrating |
| # legacy JS code to TS. Any new entries here should be accompanied by a bug |
| # tracking the TS migration. |
| def validateJavaScriptAllowed(source_dir, out_dir, is_ios): |
| # Special case for iOS, which sets the root src/ directory as the source |
| # directory for the ts_library() call, see |
| # ios/web/public/js_messaging/compile_ts.gni. |
| # We don't want to generally allow allow_js anywhere in src/ so check the |
| # output directory against the standard ios directories instead. This is a |
| # really broad check so also use the platform to make sure this is not abused |
| # elsewhere; the iOS use case of using allowJs broadly is not supported. |
| if (is_ios and '/ios/' in out_dir): |
| return True, None |
| |
| # Anything in these ChromeOS-specific directories is allowed to use allow_js. |
| # TODO (rbpotter): If possible, standardize the build setup in some of these |
| # folders such that they can be more accurately specified in the list below. |
| ash_directories = [ |
| 'ash/webui/camera_app_ui/', |
| 'ash/webui/color_internals/', |
| 'ash/webui/common/resources/', |
| 'ash/webui/diagnostics_ui/', |
| 'ash/webui/file_manager/resources/labs/', |
| # TODO(b/314827247): Migrate media_app_ui to TypeScript and remove |
| # exception. |
| 'ash/webui/media_app_ui/', |
| # TODO(b/313562946): Migrate help_app_ui mojo pipeline to TypeScript and |
| # remove. |
| 'ash/webui/help_app_ui/', |
| # TODO(b/315002705): Migrate shimless_rma to TypeScript and remove |
| # exception. |
| 'ash/webui/shimless_rma/', |
| 'ash/webui/shortcut_customization_ui/', |
| # TODO(b/267329383): Migrate A11y to TypeScript. |
| 'chrome/browser/resources/chromeos/accessibility', |
| 'ui/file_manager/', |
| ] |
| for directory in ash_directories: |
| if directory in source_dir: |
| return True, None |
| |
| # Specific exceptions for directories that are still migrating to TS. |
| migrating_directories = [ |
| # TODO(crbug.com/1337318): Migrate bluetooth-internals to TypeScript and |
| # remove exception. |
| 'chrome/browser/resources/bluetooth_internals', |
| 'chrome/browser/resources/chromeos/accessibility', |
| # TODO(crbug.com/1511758): Migrate to TypeScript. |
| 'chrome/browser/resources/device_log', |
| 'chrome/test/data/webui', |
| # TODO(crbug.com/1337318): Migrate bluetooth-internals to TypeScript and |
| # remove exception. |
| 'chrome/test/data/webui/bluetooth_internals', |
| 'chrome/test/data/webui/chromeos', |
| 'chrome/test/data/webui/chromeos/ash_common', |
| 'chrome/test/data/webui/cr_components/chromeos', |
| 'chrome/test/data/webui/nearby_share', |
| 'chrome/test/data/webui/settings/chromeos', |
| 'components/policy/resources/webui', |
| 'ui/webui/resources/js', |
| 'ui/webui/resources/mojo', |
| |
| # TODO(crbug.com/1478961) : Migrate to TypeScript. |
| 'chrome/test/data/webui/media_internals', |
| 'content/browser/resources/media', |
| |
| # TODO(b/274059668): Migrate OOBE to TypeScript. |
| 'chrome/browser/resources/chromeos/login', |
| ] |
| for directory in migrating_directories: |
| if (source_dir.endswith(directory) |
| or source_dir.endswith(directory + '/preprocessed')): |
| return True, None |
| |
| return False, 'Invalid JS file detected for input directory ' + \ |
| f'{source_dir} and output directory {out_dir}, all new ' + \ |
| 'code should be added in TypeScript.' |
| |
| |
| def getTargetPath(gen_dir, root_gen_dir): |
| root_gen_dir_from_build = os.path.normpath(os.path.join( |
| gen_dir, root_gen_dir)).replace('\\', '/') |
| return os.path.relpath(gen_dir, root_gen_dir_from_build).replace('\\', '/') |
| |
| |
| def isInAshFolder(path): |
| nested_lacros_folders = [ |
| 'chrome/browser/resources/chromeos/kerberos', |
| ] |
| if any(path.startswith(folder) for folder in nested_lacros_folders): |
| return False |
| |
| # TODO (https://crbug.com/1506296): Organize Ash WebUI code under fewer |
| # directories. |
| ash_folders = [ |
| # Source code folders |
| 'ash/webui', |
| 'chrome/browser/resources/ash', |
| 'chrome/browser/resources/chromeos', |
| 'chrome/browser/resources/nearby_internals', |
| 'chrome/browser/resources/nearby_share', |
| 'ui/file_manager', |
| |
| # Test folders |
| 'chrome/test/data/webui/chromeos', |
| 'chrome/test/data/webui/cr_components/chromeos', |
| 'chrome/test/data/webui/nearby_share', |
| 'chrome/test/data/webui/settings/chromeos', |
| ] |
| return any(path.startswith(folder) for folder in ash_folders) |
| |
| |
| def isBrowserOnlyDep(dep): |
| browser_only_deps = [ |
| '//ui/webui/resources/cr_elements', |
| '//ui/webui/resources/cr_components/localized_link', |
| '//ui/webui/resources/cr_components/managed_footnote', |
| ] |
| return any(dep.startswith(dep_folder) for dep_folder in browser_only_deps) |
| |
| |
| def isDependencyAllowed(is_ash_target, raw_dep, target_path): |
| if is_ash_target and isBrowserOnlyDep(raw_dep): |
| return False |
| |
| is_ash_dep = isInAshFolder(raw_dep[2:]) |
| if not is_ash_dep or is_ash_target: |
| return True |
| |
| exceptions = [ |
| # TODO(https://crbug.com/1506299): Remove this incorrect dependency |
| 'chrome/browser/resources/settings', |
| ] |
| |
| return target_path in exceptions |
| |
| |
| def isMappingAllowed(is_ash_target, target_path, mapping_path): |
| if is_ash_target: |
| return True |
| |
| return not isInAshFolder(mapping_path) or target_path in exceptions |
| |
| |
| # TODO (https://www.crbug.com/1412158): Remove all exceptions below and this |
| # function; these build targets rely on implicitly unmapped dependencies. |
| def isUnsupportedJsTarget(gen_dir, root_gen_dir): |
| target_path = getTargetPath(gen_dir, root_gen_dir) |
| exceptions = [ |
| 'ash/webui/color_internals/resources', |
| 'chrome/browser/resources/chromeos/accessibility/select_to_speak', |
| ] |
| return target_path in exceptions |
| |
| |
| # |root_dir| shouldn't refer to any parent directories. Specifically it should |
| # be either: |
| # - within the folder tree starting at the ts_library() target's location |
| # - within the folder tree starting at the ts_library() target's corresponding |
| # target_gen_dir location. |
| def validateRootDir(root_dir, gen_dir, root_gen_dir, is_ios): |
| root_gen_dir_from_build = os.path.normpath(os.path.join( |
| gen_dir, root_gen_dir)).replace('\\', '/') |
| target_path = os.path.relpath(gen_dir, |
| root_gen_dir_from_build).replace('\\', '/') |
| |
| # Broadly special casing ios/ for now, since compile_ts.gni relies on |
| # unsupported behavior of setting the root_dir to src/. |
| # TODO (https://www.crbug.com/1412158): Make iOS TypeScript build tools use |
| # ts_library in a supported way, or change them to not rely on ts_library. |
| if (is_ios and (target_path.startswith('ios') or '/ios/' in target_path)): |
| return True, None |
| |
| # Legacy cases supported for backward-compatibility. Do not add new targets |
| # here. The existing exceptions should be removed over time. |
| exceptions = [ |
| # ChromeOS cases |
| 'ash/webui/color_internals/mojom', |
| # TODO(b/315150183): Migrate A11y code to use path mappings. |
| 'chrome/browser/resources/chromeos/accessibility/accessibility_common', |
| 'chrome/browser/resources/chromeos/accessibility/braille_ime', |
| 'chrome/browser/resources/chromeos/accessibility/chromevox', |
| 'chrome/browser/resources/chromeos/accessibility/common', |
| 'chrome/browser/resources/chromeos/accessibility/enhanced_network_tts', |
| 'chrome/browser/resources/chromeos/accessibility/select_to_speak', |
| 'chrome/browser/resources/chromeos/accessibility/switch_access', |
| ] |
| |
| if target_path in exceptions: |
| return True, None |
| |
| target_path_src = os.path.relpath(os.path.join(_SRC_DIR, target_path), |
| _CWD).replace('\\', '/') |
| root_path_from_gen = os.path.relpath(root_dir, |
| root_gen_dir_from_build).replace( |
| '\\', '/') |
| root_path_from_src = os.path.relpath(os.path.join(_CWD, root_dir), |
| _SRC_DIR).replace('\\', '/') |
| |
| if (root_path_from_gen.startswith(target_path) |
| or root_path_from_src.startswith(target_path)): |
| return True, None |
| |
| return False, f'Error: root_dir ({root_dir}) should be within {gen_dir} ' + \ |
| f'or {target_path_src}.' |