idlize
218 строк · 9.7 Кб
1/*
2* Copyright (c) 2022-2023 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 * as ts from 'ohos-typescript'
17import * as fs from "fs"
18import * as path from "path"
19import { Importer } from './Importer'
20import { StructTransformer } from './StructTransformer'
21import { StyleTransformer, EtsFirstArgTransformer } from './StyleTransformer'
22import { NameCollector } from './NameCollector'
23import { CallTable, IssueTable, NameTable } from './utils'
24import { LazyTransformer } from './LazyTransformer'
25import { EntryTracker } from './EntryTracker'
26import { filterOutDiagnostics } from './DiagnosticFilter'
27import { DollarTransformer } from './DollarTransformer'
28import { ExtensionStylesTransformer } from './ExtensionStylesTransformer'
29import { NewTransformer } from './NewTransformer'
30import { CustomBuilderTransformer } from './CustomBuilderTransformer'
31import { CallTransformer } from './CallTransformer'
32import { DebugVisitor } from './AbstractVisitor'
33import { LegacyCallTransformer } from './LegacyCallTransformer'
34import { AbilityTransformer } from './AbilityTransformer'
35
36interface ArkToKoOptions {
37trace?: boolean
38source?: string
39destination?: string
40arkui?: string
41moduleInfo?: (moduleName: string) => any
42}
43
44function printSourceFile(sourceFile: ts.Node, source?: string, destination?: string, extension?: string) {
45if (!ts.isSourceFile(sourceFile)) throw new Error("Expected SourceFile, got: " + ts.SyntaxKind[sourceFile.kind])
46if (!destination) console.log("OUTPUT")
47
48const printer = ts.createPrinter({ removeComments: false })
49const text = printer.printNode(ts.EmitHint.SourceFile, sourceFile, sourceFile)
50if (destination) {
51const absoluteDestination = path.resolve(destination)
52const absoluteSource = source ? path.resolve(source) : path.resolve()
53const fileBaseName = path.basename(sourceFile.fileName, extension ?? ".ets")
54const fileDirName = path.dirname(sourceFile.fileName)
55const fileRelativeName = path.relative(absoluteSource, fileDirName)
56const dir = path.join(absoluteDestination, fileRelativeName)
57const file = path.join(dir, fileBaseName + ".ts")
58console.log("KOALA: " + path.normalize(file))
59fs.mkdirSync(dir, { recursive: true })
60fs.writeFileSync(file, text, 'utf8')
61} else {
62console.log(text)
63}
64}
65
66function updateRouterDestinationsFile(destination: string|undefined, entryTracker: EntryTracker) {
67const entries = Array.from(entryTracker.entries.entries())
68const comment = "// This file has been autogenerated. Do not update manually."
69const imports: string[] = []
70const uses: string[] = []
71entries.forEach((entry, i) => {
72const [file, component] = entry
73const alias = "Entry" + i
74imports.push(`import { ${component} as ${alias} } from \"./${file}\"`)
75uses.push(`\tconst __force${alias}Use = ${alias}`)
76})
77
78const registerFunction =`
79export function registerRoutes() {
80${uses.join("\n")}
81}
82`
83const text = comment + "\n" +
84imports.join("\n") + "\n" +
85registerFunction
86
87fs.writeFileSync(path.join(destination ?? ".", "__router_initialization.ts"), text)
88}
89
90function fileIsEligible(fileName: string): boolean {
91return path.extname(fileName) == ".ets"
92}
93
94function prepareDestination(destination: string | undefined) {
95if (destination) {
96fs.mkdirSync(destination, { recursive: true })
97}
98}
99
100export function arkExpandFile(
101sourceFile: ts.SourceFile,
102arkuiPackage: string,
103typeChecker: ts.TypeChecker,
104ctx: ts.TransformationContext,
105extras: ts.TransformerExtras | undefined,
106entryTracker: EntryTracker,
107moduleInfo?: (moduleName: string) => any
108): ts.SourceFile {
109const importer = new Importer(arkuiPackage, moduleInfo)
110const nameTable = new NameTable()
111const issueTable = new IssueTable()
112const callTable = new CallTable()
113
114/* Uncomment to dump AST on entry */
115
116// const debug = new DebugVisitor(sourceFile, ctx)
117// debug.visitor(sourceFile)
118
119const callTransformer = new CallTransformer(sourceFile, ctx, typeChecker, callTable, importer)
120const lazyTransformer = new LazyTransformer(sourceFile, ctx, importer, callTable)
121const newTransformer = new NewTransformer(sourceFile, ctx, importer, issueTable)
122const customBuilderTransformer = new CustomBuilderTransformer(sourceFile, ctx, typeChecker, callTable)
123const legacyCallTransformer = new LegacyCallTransformer(sourceFile, ctx, importer, callTable)
124const extensionStylesTransformer = new ExtensionStylesTransformer(sourceFile, ctx, typeChecker, importer)
125const preprocessorTransformer = new EtsFirstArgTransformer(sourceFile, ctx, importer, callTable)
126const styleTransformer = new StyleTransformer(sourceFile, ctx, typeChecker, importer, extensionStylesTransformer, callTable)
127const structTransformer = new StructTransformer(sourceFile, ctx, typeChecker, importer, nameTable, entryTracker, callTable)
128const nameCollector = new NameCollector(sourceFile, ctx, nameTable, issueTable)
129const dollarTransformer = new DollarTransformer(sourceFile, ctx, nameTable)
130const abilityTransformer = new AbilityTransformer(sourceFile, ctx, importer)
131
132nameCollector.visitor(sourceFile)
133
134const translatedCalls = callTransformer.visitor(sourceFile) as ts.SourceFile
135const translatedDollar = dollarTransformer.visitor(translatedCalls) as ts.SourceFile
136const translatedLazy = lazyTransformer.visitor(translatedDollar) as ts.SourceFile
137const translatedNew = newTransformer.visitor(translatedLazy) as ts.SourceFile
138const translatedCustomBuilders = customBuilderTransformer.visitor(translatedNew) as ts.SourceFile
139const translatedLegacyCalls = legacyCallTransformer.visitor(translatedCustomBuilders) as ts.SourceFile
140const translatedStyleFunctions = extensionStylesTransformer.visitor(translatedLegacyCalls) as ts.SourceFile
141const preprocessedEts = preprocessorTransformer.visitor(translatedStyleFunctions) as ts.SourceFile
142const translatedStyle = styleTransformer.visitor(preprocessedEts) as ts.SourceFile
143const translatedStruct = structTransformer.visitor(translatedStyle) as ts.SourceFile
144const translatedAbility = abilityTransformer.visitor(translatedStruct) as ts.SourceFile
145
146const finalStage = translatedAbility
147
148filterOutDiagnostics(sourceFile, nameTable, issueTable, extras)
149
150return ts.factory.updateSourceFile(
151finalStage,
152[
153...importer.generate(),
154...finalStage.statements,
155// TODO: do we need a better way to obtain it?
156// ...structTranslator.entryCode
157],
158finalStage.isDeclarationFile,
159finalStage.referencedFiles,
160finalStage.typeReferenceDirectives,
161finalStage.hasNoDefaultLib,
162finalStage.libReferenceDirectives
163)
164}
165
166export default function arkExpander(program: ts.Program, userPluginOptions: ArkToKoOptions, extras: ts.TransformerExtras) {
167const pluginOptions = {
168trace: userPluginOptions.trace ?? true,
169source: userPluginOptions.source ?? ".",
170destination: userPluginOptions.destination ?? "../generated",
171arkui: userPluginOptions.arkui ?? "@koalaui/arkui",
172moduleInfo: userPluginOptions.moduleInfo
173}
174const typeChecker = program.getTypeChecker()
175prepareDestination(pluginOptions.destination)
176const entryTracker = new EntryTracker(pluginOptions.source)
177
178return (ctx: ts.TransformationContext) => {
179return (sourceFile: ts.SourceFile) => {
180
181if (!fileIsEligible(sourceFile.fileName)) {
182console.log("Verbatim TS: ", sourceFile.fileName)
183printSourceFile(sourceFile, pluginOptions.source, pluginOptions.destination, ".ts")
184return sourceFile
185} else {
186console.log("ETS->TS: " + path.normalize(sourceFile.fileName))
187}
188
189let final = arkExpandFile(sourceFile, pluginOptions.arkui, typeChecker, ctx, extras, entryTracker, pluginOptions.moduleInfo)
190
191printSourceFile(final, pluginOptions.source, pluginOptions.destination)
192updateRouterDestinationsFile(pluginOptions.destination, entryTracker)
193
194// if (structTranslator.entryFile) {
195// emitStartupFile(structTranslator.entryFile, pluginOptions.source!, pluginOptions.destination!)
196// }
197
198return final
199}
200}
201}
202
203export function makeEtsExpander(typeChecker: ts.TypeChecker, userPluginOptions: ArkToKoOptions, extras: ts.TransformerExtras) {
204const pluginOptions = {
205trace: userPluginOptions.trace ?? true,
206source: userPluginOptions.source ?? ".",
207destination: userPluginOptions.destination ?? "../generated",
208arkui: userPluginOptions.arkui ?? "@koalaui/arkui",
209moduleInfo: userPluginOptions.moduleInfo
210}
211const entryTracker = new EntryTracker(pluginOptions.source)
212
213return (ctx: ts.TransformationContext) => {
214return (sourceFile: ts.SourceFile) => {
215return arkExpandFile(sourceFile, pluginOptions.arkui, typeChecker, ctx, extras, entryTracker)
216}
217}
218}
219