| #!/usr/bin/env python3 |
| # Copyright (C) 2025 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import os |
| import sys |
| |
| from fnmatch import fnmatch |
| from code_format_utils import CodeFormatterBase, run_code_formatters |
| |
| |
| def node_env(repo_root): |
| """Returns a modified env var set with tools/ prepended (for node)""" |
| env = os.environ.copy() |
| env['PATH'] = os.path.join(repo_root, 'tools') + os.pathsep + env['PATH'] |
| return env |
| |
| |
| class Prettier(CodeFormatterBase): |
| |
| def __init__(self): |
| super().__init__(name='prettier', exts=['.ts', '.js', '.scss']) |
| |
| def filter_files(self, files): |
| # Filter based on extension first. |
| filtered = super().filter_files(files) |
| # Filter out changes outside of ui/ |
| filtered = [f for f in filtered if f.startswith('ui/')] |
| with open('ui/.prettierignore', 'r') as fd: |
| ignorelist = fd.read().strip().split('\n') |
| filtered = [ |
| f for f in filtered if not any(fnmatch(f, i) for i in ignorelist) |
| ] |
| return filtered |
| |
| def run_formatter(self, repo_root: str, check_only: bool, files: list[str]): |
| tool = 'node_modules/.bin/prettier' |
| ui_dir = os.path.join(repo_root, 'ui') |
| if not os.path.exists(os.path.join(ui_dir, tool)): |
| err = f'Cannot find ${tool}\nRun tools/install-build-deps --ui' |
| print(err, file=sys.stderr) |
| return 127 |
| files = [os.path.relpath(os.path.abspath(f), ui_dir) for f in files] |
| cmd = [tool, '--log-level=warn'] + ['--check' if check_only else '--write'] |
| cmd += files |
| return self.check_call(cmd, cwd=ui_dir, env=node_env(repo_root)) |
| |
| def print_fix_hint(self): |
| print('Run ui/format-sources to fix', file=sys.stderr) |
| |
| |
| class Eslint(CodeFormatterBase): |
| |
| def __init__(self): |
| super().__init__(name='eslint', exts=['.ts', '.js']) |
| |
| def filter_files(self, files): |
| # Filter based on extension first. |
| filtered = super().filter_files(files) |
| # Filter out changes outside of ui/ |
| filtered = [f for f in filtered if f.startswith('ui/')] |
| return filtered |
| |
| def run_formatter(self, repo_root: str, check_only: bool, files: list[str]): |
| out_ui_dir = os.path.join(repo_root, 'out', 'ui') |
| if not os.path.exists(out_ui_dir): |
| # Eslint requires all source dependencies to exist (e.g.: the .pbjs files), |
| # so a UI build needs to be run first. |
| print(f'Cannot run eslint because there is no UI build in {out_ui_dir}') |
| return 127 |
| tool = 'node_modules/.bin/eslint' |
| ui_dir = os.path.join(repo_root, 'ui') |
| if not os.path.exists(os.path.join(ui_dir, tool)): |
| err = f'Cannot find ${tool}\nRun tools/install-build-deps --ui' |
| print(err, file=sys.stderr) |
| return 127 |
| files = [os.path.relpath(os.path.abspath(f), ui_dir) for f in files] |
| cmd = [tool] + ([] if check_only else ['--fix']) + files |
| return self.check_call(cmd, cwd=ui_dir, env=node_env(repo_root)) |
| |
| def print_fix_hint(self): |
| print('Run ui/format-sources to fix', file=sys.stderr) |
| |
| |
| UI_CODE_FORMATTERS = [Prettier(), Eslint()] |
| |
| if __name__ == '__main__': |
| sys.exit(run_code_formatters(UI_CODE_FORMATTERS)) |