• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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