1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { program } from "commander" 17import { exit } from "process" 18import * as ts from 'typescript'; 19import * as path from 'path'; 20import * as fs from 'fs'; 21import { componentInterfaceCollector, interfaceTransformer, addMemoTransformer } from "./interface_converter" 22import { ComponentFile } from './component_file'; 23import { exportAllTransformer } from './add_export' 24import { addImportTransformer } from './add_import' 25import uiconfig from './arkui_config_util' 26 27function getFiles(dir: string, fileFilter: (f: string) => boolean): string[] { 28 const result: string[] = [] 29 const dirents = fs.readdirSync(dir, { withFileTypes: true }) 30 for (const entry of dirents) { 31 const fullPath = path.join(dir, entry.name) 32 if (entry.isFile() && fileFilter(fullPath) && !uiconfig.notUIFile(fullPath)) { 33 let addFile: boolean = true 34 if (uiconfig.isHdsComponent) { 35 addFile = entry.name.startsWith("@hms.hds.") 36 } 37 if (addFile) { 38 result.push(fullPath) 39 uiconfig.addComponentFile(fullPath) 40 } 41 } 42 } 43 return result 44} 45 46function convertFiles(files: string[]): string[] { 47 const result: string[] = [] 48 for (const file of files) { 49 const dest = file.replace(".d.ets", ".d.ts") 50 fs.copyFile(file, dest, () => { }) 51 result.push(dest) 52 } 53 return result 54} 55 56function getTransformationResult(sourceFile: ts.SourceFile, program: ts.Program, componentFile: ComponentFile): ts.TransformationResult<ts.SourceFile> { 57 if (uiconfig.isHdsComponent) { 58 return ts.transform(sourceFile, [interfaceTransformer(program, componentFile), exportAllTransformer()]); 59 } 60 return ts.transform(sourceFile, [interfaceTransformer(program, componentFile), exportAllTransformer(), addImportTransformer()]); 61} 62 63function printResult(source: string, file: ComponentFile) { 64 const outPath = path.join(options.targetDir, file.outFileName) 65 fs.mkdirSync(path.dirname(outPath), { recursive: true }); 66 fs.writeFileSync(outPath, source.concat(file.concactSource)) 67} 68 69function main() { 70 uiconfig.loadConfig(options); 71 const files = getFiles(options.inputDir, f => f.endsWith(".d.ets")); 72 const program = ts.createProgram(files, { allowJs: true }); 73 const { printFile } = ts.createPrinter({ removeComments: false }); 74 const componentFileMap = new Map<string, ComponentFile>(); 75 const componentFileCallback = (f: string) => { 76 return (context: ts.TransformationContext) => { 77 return (sourceFile: ts.SourceFile) => { 78 const componentFile = new ComponentFile(f, sourceFile); 79 componentFileMap.set(f, componentFile); 80 ts.transform(sourceFile, [componentInterfaceCollector(program, componentFile)]); 81 return sourceFile; 82 } 83 } 84 } 85 86 const transformerCallback = (f: string) => { 87 return (context: ts.TransformationContext) => { 88 return (sourceFile: ts.SourceFile) => { 89 const componentFile = componentFileMap.get(f)!; 90 const result = getTransformationResult(sourceFile, program, componentFile); 91 const transformedFile = ts.createSourceFile(f, printFile(result.transformed[0]), ts.ScriptTarget.Latest, true); 92 const transformedSource = ts.createPrinter().printFile(transformedFile); 93 printResult(transformedSource, componentFile); 94 return ts.createSourceFile("", "", ts.ScriptTarget.Latest, true); 95 } 96 } 97 } 98 99 // Step1 collect all component dependencies 100 files.forEach(f => { 101 try { 102 const content = fs.readFileSync(f, 'utf-8'); 103 ts.transpileModule(content, { 104 compilerOptions: { 105 target: ts.ScriptTarget.ES2017, 106 }, 107 fileName: f, 108 transformers: { before: [componentFileCallback(f)] } 109 }) 110 } catch (e) { 111 console.log("Error collecting file: ", f, e); 112 exit(1) 113 } 114 }) 115 116 // Step2 make transformation 117 files.forEach(f => { 118 try { 119 const content = fs.readFileSync(f, 'utf-8'); 120 ts.transpileModule(content, { 121 compilerOptions: { 122 target: ts.ScriptTarget.ES2017, 123 }, 124 fileName: f, 125 transformers: { before: [transformerCallback(f)] } 126 }) 127 } catch (e) { 128 console.log("Error transforming file: ", f, e); 129 exit(1) 130 } 131 }) 132} 133 134const options = program 135 .option('--input-dir <path>', "Path of where d.ets exist") 136 .option('--target-dir <path>', "Path to generate d.ets file") 137 .option('--config-path <path>', "Path to folder with config files") 138 .option('--use-memo-m3', "Generate code with m3 @memo annotations and functions with @ComponentBuilder", false) 139 .parse() 140 .opts() 141 142main() 143