1#!/usr/bin/env python3 2# Copyright (C) 2021 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Generates TypeScript files that import all subdirectories and 16registers them with plugin registry. If you have three modules: 17- core/ 18- plugins/foo_plugin/ 19- plugins/bar_plugin/ 20In general you would like the dependency to only go one way: 21- plugins/foo_plugin/ -> core/ 22We want to avoid manually editing core/ for every plugin. 23 24This generates code like: 25 26import {plugin as fooPlugin} from '../plugins/foo_plugin'; 27import {plugin as barPlugin} from '../plugins/bar_plugin'; 28 29export default [ 30 fooPlugin, 31 barPlugin, 32]; 33""" 34 35from __future__ import print_function 36 37import os 38import argparse 39import re 40 41ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 42UI_SRC_DIR = os.path.join(ROOT_DIR, 'ui', 'src') 43PLUGINS_PATH = os.path.join(UI_SRC_DIR, 'common', 'plugins') 44 45 46def to_camel_case(s): 47 # Split string on periods and underscores 48 first, *rest = re.split(r'\.|\_', s) 49 return first + ''.join(x.title() for x in rest) 50 51 52def is_plugin_dir(dir): 53 # Ensure plugins contain a file called index.ts. This avoids the issue empty 54 # dirs are detected as plugins. 55 return os.path.isdir(dir) and os.path.exists(os.path.join(dir, 'index.ts')) 56 57 58def plugin_has_css(dir): 59 return os.path.isdir(dir) and os.path.exists(os.path.join(dir, 'styles.scss')) 60 61 62def gen_imports(input_dir, output_path): 63 paths = [os.path.join(input_dir, p) for p in os.listdir(input_dir)] 64 paths = [p for p in paths if is_plugin_dir(p)] 65 paths.sort() 66 67 output_dir = os.path.dirname(output_path) 68 69 imports = [] 70 registrations = [] 71 for path in paths: 72 rel_path = os.path.relpath(path, output_dir) 73 snake_name = os.path.basename(path) 74 camel_name = to_camel_case(snake_name) 75 imports.append(f"import {camel_name} from '{rel_path}';") 76 registrations.append(camel_name) 77 78 import_text = '\n'.join(imports) 79 registration_text = 'export default [\n' 80 for camel_name in registrations: 81 registration_text += f" {camel_name},\n" 82 registration_text += '];\n' 83 84 expected = f"{import_text}\n\n{registration_text}" 85 86 with open(f"{output_path}.ts", 'w') as f: 87 f.write(expected) 88 return True 89 90 91def gen_css_import(input_dir, output_path): 92 paths = [os.path.join(input_dir, p) for p in os.listdir(input_dir)] 93 paths = [p for p in paths if plugin_has_css(p)] 94 paths.sort() 95 96 output_dir = os.path.dirname(output_path) 97 98 imports = [] 99 for path in paths: 100 rel_path = os.path.relpath(path, output_dir) 101 imports.append(f"@import '{rel_path}/styles';") 102 103 import_text = '\n'.join(imports) 104 105 with open(f"{output_path}.scss", 'w') as f: 106 f.write(import_text) 107 return True 108 109 110def main(): 111 parser = argparse.ArgumentParser( 112 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 113 parser.add_argument('INPUT') 114 parser.add_argument('--out', required=True) 115 args = parser.parse_args() 116 input_dir = args.INPUT 117 output_path = args.out 118 119 if not os.path.isdir(input_dir): 120 print(f'INPUT argument {input_dir} must be a directory') 121 exit(1) 122 123 output_dir = os.path.dirname(output_path) 124 if output_dir and not os.path.isdir(output_dir): 125 print(f'--out ({output_path}) parent directory ({output_dir}) must exist') 126 exit(1) 127 if os.path.isdir(output_path): 128 print(f'--out ({output_path}) should not be a directory') 129 exit(1) 130 131 success = gen_imports(input_dir, output_path) 132 success = success | gen_css_import(input_dir, output_path) 133 return 0 if success else 1 134 135 136if __name__ == '__main__': 137 exit(main()) 138