idlize
187 строк · 8.4 Кб
1/*
2* Copyright (c) 2024 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 path from "path"
17import { IndentedPrinter } from "../../IndentedPrinter";
18import { Language, renameDtsToComponent, renameDtsToPeer } from "../../util";
19import { ImportsCollector } from "../ImportsCollector";
20import { PeerClass } from "../PeerClass";
21import { PeerFile } from "../PeerFile";
22import { PeerLibrary } from "../PeerLibrary";
23import { isCommonMethod } from "../inheritance";
24import { PeerMethod } from "../PeerMethod";
25import { componentToPeerClass } from "./PeersPrinter";
26import { OverloadsPrinter, collapseSameNamedMethods } from "./OverloadsPrinter";
27import { LanguageWriter, Method, MethodModifier, MethodSignature, Type, createLanguageWriter } from "../LanguageWriters";
28import { convertToCallback } from "./EventsPrinter";
29import { tsCopyrightAndWarning } from "../FileGenerators";
30
31function generateArkComponentName(component: string) {
32return `Ark${component}Component`
33}
34
35class ComponentFileVisitor {
36private readonly overloadsPrinter = new OverloadsPrinter(this.printer, this.library)
37
38constructor(
39private library: PeerLibrary,
40private file: PeerFile,
41readonly printer: LanguageWriter,
42) { }
43
44get targetBasename() {
45return renameDtsToComponent(path.basename(this.file.originalFilename), this.file.declarationTable.language)
46}
47
48private printImports(): void {
49const imports = new ImportsCollector()
50imports.addFilterByBasename(this.targetBasename)
51this.file.peersToGenerate.forEach(peer => {
52imports.addFeature("NodeAttach", "@koalaui/runtime")
53imports.addFeature("remember", "@koalaui/runtime")
54if (peer.originalParentFilename) {
55const parentBasename = renameDtsToComponent(path.basename(peer.originalParentFilename), this.file.declarationTable.language, false)
56imports.addFeature(generateArkComponentName(peer.parentComponentName!), `./${parentBasename}`)
57}
58imports.addFeatureByBasename(componentToPeerClass(peer.componentName),
59renameDtsToPeer(path.basename(peer.originalFilename), peer.declarationTable.language))
60peer.attributesTypes.forEach((attrType) =>
61imports.addFeatureByBasename(attrType.typeName,
62renameDtsToPeer(path.basename(peer.originalFilename), peer.declarationTable.language))
63)
64imports.addFeature("ArkUINodeType", "./ArkUINodeType")
65imports.addFeature("runtimeType", "./SerializerBase")
66imports.addFeature("RuntimeType", "./SerializerBase")
67imports.addFeature("isPixelMap", "./SerializerBase")
68imports.addFeature("isResource", "./SerializerBase")
69imports.addFeature("isInstanceOf", "./SerializerBase")
70imports.addFeature('ComponentBase', './ComponentBase')
71imports.addFeature('unsafeCast', './generated-utils')
72for (const method of peer.methods) {
73for (const target of method.declarationTargets)
74if (convertToCallback(peer, method, target))
75imports.addFeature("UseEventsProperties", './use_properties')
76}
77// TBD
78// peer.materializedClasses.forEach(it => {
79// imports.addFeature(it.className, `./Ark${peer.componentName}Peer`)
80// })
81})
82if (this.file.declarationTable.language === Language.TS)
83this.file.importFeatures.forEach(it => imports.addFeature(it.feature, it.module))
84imports.print(this.printer)
85}
86
87private groupOverloads(peerMethods: PeerMethod[]): PeerMethod[][] {
88const seenNames = new Set<string>()
89const groups: PeerMethod[][] = []
90for (const method of peerMethods) {
91if (seenNames.has(method.method.name))
92continue
93seenNames.add(method.method.name)
94groups.push(peerMethods.filter(it => it.method.name === method.method.name))
95}
96return groups
97}
98
99private printComponent(peer: PeerClass) {
100const callableMethods = peer.methods.filter(it => it.isCallSignature).map(it => it.method)
101const callableMethod = callableMethods.length ? collapseSameNamedMethods(callableMethods) : undefined
102const mappedCallableParams = callableMethod?.signature.args.map((it, index) => `${callableMethod.signature.argName(index)}${it.nullable ? "?" : ""}: ${it.name}`)
103const mappedCallableParamsValues = callableMethod?.signature.args.map((_, index) => callableMethod.signature.argName(index))
104const componentClassName = generateArkComponentName(peer.componentName)
105const parentComponentClassName = peer.parentComponentName ? generateArkComponentName(peer.parentComponentName!) : `ComponentBase`
106const componentFunctionName = `Ark${peer.componentName}`
107const peerClassName = componentToPeerClass(peer.componentName)
108
109this.printer.writeClass(componentClassName, (writer) => {
110writer.writeFieldDeclaration('peer', new Type(peerClassName), ['protected'], true)
111for (const grouped of this.groupOverloads(peer.methods))
112this.overloadsPrinter.printGroupedComponentOverloads(peer, grouped)
113// todo stub until we can process AttributeModifier
114if (isCommonMethod(peer.originalClassName!) || peer.originalClassName == "ContainerSpanAttribute")
115writer.print(`attributeModifier(modifier: AttributeModifier<object>): this { throw new Error("not implemented") }`)
116const attributesSignature = new MethodSignature(Type.Void, [])
117writer.writeMethodImplementation(new Method('applyAttributesFinish', attributesSignature, [MethodModifier.PUBLIC]), (writer) => {
118writer.print('// we calls this function outside of class, so need to make it public')
119writer.writeMethodCall('super', 'applyAttributesFinish', [])
120})
121}, parentComponentClassName)
122
123
124this.printer.print(`
125/** @memo */
126export function ${componentFunctionName}(
127/** @memo */
128style: ((attributes: ${componentClassName}) => void) | undefined,
129/** @memo */
130content_: (() => void) | undefined,
131${mappedCallableParams?.join(", ") ?? ""}
132) {
133const receiver = remember(() => {
134return new ${componentClassName}()
135})
136NodeAttach(() => new ${peerClassName}(ArkUINodeType.${peer.componentName}, receiver), () => {
137${callableMethod ? `receiver.${callableMethod.name}(${mappedCallableParamsValues})` : ""}
138style?.(receiver)
139content_?.()
140receiver.applyAttributesFinish()
141})
142}
143`)
144}
145
146printFile(): void {
147this.printImports()
148this.file.peersToGenerate.forEach(peer => {
149this.printComponent(peer)
150})
151}
152}
153
154class ComponentsVisitor {
155readonly components: Map<string, LanguageWriter> = new Map()
156
157constructor(
158private readonly peerLibrary: PeerLibrary,
159) { }
160
161printComponents(): void {
162for (const file of this.peerLibrary.files.values()) {
163if (!file.peersToGenerate.length)
164continue
165const writer = createLanguageWriter(Language.TS)
166const visitor = new ComponentFileVisitor(this.peerLibrary, file, writer)
167visitor.printFile()
168this.components.set(visitor.targetBasename, writer)
169}
170}
171}
172
173export function printComponents(peerLibrary: PeerLibrary): Map<string, string> {
174// TODO: support other output languages
175if (peerLibrary.declarationTable.language != Language.TS)
176return new Map()
177
178const visitor = new ComponentsVisitor(peerLibrary)
179visitor.printComponents()
180const result = new Map<string, string>()
181for (const [key, writer] of visitor.components) {
182if (writer.getOutput().length === 0) continue
183const text = tsCopyrightAndWarning(writer.getOutput().join('\n'))
184result.set(key, text)
185}
186return result
187}