From 9a85ffdccb1df9209ceeb697b728867e85794376 Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Mon, 3 Feb 2025 19:15:19 +0300 Subject: [PATCH 1/3] WIP Signed-off-by: Alexander Gorshenev --- arkoala/ets-plugin/src/ArkExpander.ts | 2 +- arkoala/ets-plugin/src/PropertyTranslators.ts | 117 ++++++++++++------ arkoala/ets-plugin/src/StructOptions.ts | 10 +- arkoala/ets-plugin/src/StructTransformer.ts | 14 ++- 4 files changed, 94 insertions(+), 49 deletions(-) diff --git a/arkoala/ets-plugin/src/ArkExpander.ts b/arkoala/ets-plugin/src/ArkExpander.ts index 500f86317..4a89f1217 100644 --- a/arkoala/ets-plugin/src/ArkExpander.ts +++ b/arkoala/ets-plugin/src/ArkExpander.ts @@ -131,7 +131,7 @@ export function arkExpandFile( const extensionStylesTransformer = new ExtensionStylesTransformer(sourceFile, ctx, typeChecker, importer) const preprocessorTransformer = new EtsFirstArgTransformer(sourceFile, ctx, importer, callTable) const styleTransformer = new StyleTransformer(sourceFile, ctx, typeChecker, importer, extensionStylesTransformer, callTable) - const structTransformer = new StructTransformer(sourceFile, ctx, typeChecker, importer, nameTable, entryTracker, callTable) + const structTransformer = new StructTransformer(sourceFile, ctx, typeChecker, importer, nameTable, entryTracker, callTable, extras) const nameCollector = new NameCollector(sourceFile, ctx, nameTable, issueTable) const dollarTransformer = new DollarTransformer(sourceFile, ctx, nameTable) const abilityTransformer = new AbilityTransformer(sourceFile, ctx, importer) diff --git a/arkoala/ets-plugin/src/PropertyTranslators.ts b/arkoala/ets-plugin/src/PropertyTranslators.ts index 3f328e7a8..212beda1c 100644 --- a/arkoala/ets-plugin/src/PropertyTranslators.ts +++ b/arkoala/ets-plugin/src/PropertyTranslators.ts @@ -73,10 +73,24 @@ import { throwError, WatchDecorator, } from "./utils" +import { TransformationContext } from "typescript" +import { MessageCode, reportError } from "./diagnostics" + +export class PropertyTranslatorContext { + constructor( + public importer: Importer, + public sourceFile: ts.SourceFile, + public extras?: ts.TransformerExtras, + ) {} +} {} + export abstract class PropertyTranslator { private cachedType: ts.TypeNode | undefined // do not analyze this.property.initializer every time - constructor(protected property: ts.PropertyDeclaration, protected importer: Importer) { } + constructor( + protected property: ts.PropertyDeclaration, + protected context: PropertyTranslatorContext + ) { } get propertyName(): string { return idTextOrError(this.property.name) @@ -101,7 +115,21 @@ export abstract class PropertyTranslator { } createStateOf(type: ts.TypeNode | undefined, ...initializer: ts.Expression[]): ts.Expression { - return createStateOf(this.importer, type, ...initializer) + return createStateOf(this.context.importer, type, ...initializer) + } + + typeOrErrorType(type: ts.TypeNode|undefined): ts.TypeNode { + if (!type) { + reportError( + MessageCode.EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED, + `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`, + this.property, + this.context.sourceFile, + this.context.extras + ) + } + return this.property.type ?? + ts.factory.createTypeReferenceNode("") } translateStateMember( @@ -161,7 +189,7 @@ export abstract class PropertyTranslator { mutableState(type: ts.TypeNode,): ts.TypeNode { return ts.factory.createTypeReferenceNode( - id(this.importer.withRuntimeImport("MutableState")), + id(this.context.importer.withRuntimeImport("MutableState")), [type] ) } @@ -177,8 +205,8 @@ export abstract class PropertyTranslator { undefined, ts.factory.createTypeReferenceNode( syncedProperty - ? this.importer.withAdaptorImport("SyncedProperty") - : this.importer.withRuntimeImport("MutableState"), + ? this.context.importer.withAdaptorImport("SyncedProperty") + : this.context.importer.withRuntimeImport("MutableState"), [ this.propertyType ] @@ -197,7 +225,7 @@ export abstract class PropertyTranslator { protected createAppStorageState(decoratorName: string): ts.Expression { return ts.factory.createCallExpression( - id(this.importer.withAdaptorImport("AppStorageLinkState")), + id(this.context.importer.withAdaptorImport("AppStorageLinkState")), [this.propertyType], [ findDecoratorArgument(filterDecorators(this.property), decoratorName, 0), @@ -208,7 +236,7 @@ export abstract class PropertyTranslator { protected createLocalStorageState(decoratorName: string): ts.Expression { return ts.factory.createCallExpression( - id(this.importer.withAdaptorImport("StorageLinkState")), + id(this.context.importer.withAdaptorImport("StorageLinkState")), [this.propertyType], [ createThisFieldAccess(LocalStoragePropertyName), @@ -281,7 +309,7 @@ export abstract class PropertyTranslator { ) ), ts.factory.createCallExpression( - id(this.importer.withCommonImport("observableProxy")), + id(this.context.importer.withCommonImport("observableProxy")), undefined, [ id("value"), @@ -339,7 +367,7 @@ export abstract class PropertyTranslator { translateInitializerOfSyncedProperty(constructorName: SyncedPropertyConstructor, withValue?: ts.Expression): ts.Expression { return ts.factory.createCallExpression( - id(this.importer.withAdaptorImport(constructorName)), + id(this.context.importer.withAdaptorImport(constructorName)), this.property.type ? [this.property.type] : undefined, withValue ? [withValue] : [] ) @@ -400,14 +428,22 @@ export abstract class PropertyTranslator { protected stateImplField(impl: StateImplementation): { name: ts.Identifier; type: ts.TypeNode }[] { const originalName = asIdentifier(this.property.name) - const originalType = this.property.type ?? throwError( - `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}` - ) + if (!this.property.type) { + reportError( + MessageCode.EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED, + `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`, + this.property, + this.context.sourceFile, + this.context.extras + ) + } + const originalType = this.property.type ?? + ts.factory.createTypeReferenceNode("") const backingName = backingFieldName(asIdentifier(this.property.name)) const backingType = ts.factory.createTypeReferenceNode( (impl === StateImplementation.SyncedProperty) ? impl : - this.importer.withRuntimeImport(impl), + this.context.importer.withRuntimeImport(impl), [originalType] ) @@ -419,9 +455,16 @@ export abstract class PropertyTranslator { protected plainImplField(): { name: ts.Identifier; type: ts.TypeNode }[] { const name = asIdentifier(this.property.name) - const type = this.property.type ?? throwError( - `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}` - ) + if (!this.property.type) { + reportError( + MessageCode.EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED, + `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`, + this.property, + this.context.sourceFile, + this.context.extras + ) + } + const type = this.property.type ?? ts.factory.createTypeReferenceNode("ErrorType") return [{ name, type }] } @@ -505,7 +548,7 @@ class Prop extends PropertyTranslator { return this.translateToUpdateSyncedProperty(createNullableAccessor(initializers(), this.propertyName)) } translateToBuildParameter(): ts.ParameterDeclaration { - return translatePropOrObjectLinkToBuildParameter(this.property, this.importer) + return translatePropOrObjectLinkToBuildParameter(this.property, this.context.importer) } } @@ -547,7 +590,7 @@ class ObjectLink extends PropertyTranslator { return this.translateToUpdateSyncedProperty(createNullableAccessor(initializers(), this.propertyName)) } translateToBuildParameter(): ts.ParameterDeclaration { - return translatePropOrObjectLinkToBuildParameter(this.property, this.importer) + return translatePropOrObjectLinkToBuildParameter(this.property, this.context.importer) } } @@ -595,7 +638,7 @@ class StorageLink extends PropertyTranslator { public implField(): { name: ts.Identifier; type: ts.TypeNode }[] { const name = backingFieldName(asIdentifier(this.property.name)) const type = ts.factory.createTypeReferenceNode( - this.importer.withRuntimeImport("MutableState"), + this.context.importer.withRuntimeImport("MutableState"), [this.property.type ?? throwError(`Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`)] ) @@ -694,8 +737,8 @@ export class BuilderParam extends PropertyTranslator { return this.initializePlain(initializers) } - constructor(protected property: ts.PropertyDeclaration, protected importer: Importer, private typechecker: ts.TypeChecker) { - super(property, importer) + constructor(protected property: ts.PropertyDeclaration, protected context: PropertyTranslatorContext) { + super(property, context) } translateMember(): ts.ClassElement[] { return this.translatePlainMember( @@ -720,8 +763,8 @@ class PlainProperty extends PropertyTranslator { return this.initializePlain(initializers) } - constructor(protected property: ts.PropertyDeclaration, protected importer: Importer, private typechecker: ts.TypeChecker) { - super(property, importer) + constructor(protected property: ts.PropertyDeclaration, protected context: PropertyTranslatorContext) { + super(property, context) } translateMember(): ts.ClassElement[] { return this.translatePlainMember( @@ -763,20 +806,20 @@ function translatePropOrObjectLinkToBuildParameter(property: ts.PropertyDeclarat ) } -export function classifyProperty(member: ts.ClassElement, importer: Importer, typechecker: ts.TypeChecker): PropertyTranslator | undefined { +export function classifyProperty(member: ts.ClassElement, context: PropertyTranslatorContext): PropertyTranslator | undefined { if (!ts.isPropertyDeclaration(member)) return undefined - if (isState(member)) return new State(member, importer) - if (isStorageProp(member)) return new StorageProp(member, importer) - if (isStorageLink(member)) return new StorageLink(member, importer) - if (isLocalStorageLink(member)) return new LocalStorageLink(member, importer) - if (isLocalStorageProp(member)) return new LocalStorageProp(member, importer) - if (isLink(member)) return new Link(member, importer) - if (isProp(member)) return new Prop(member, importer) - if (isObjectLink(member)) return new ObjectLink(member, importer) - if (isProvide(member)) return new Provide(member, importer) - if (isConsume(member)) return new Consume(member, importer) - if (isBuilderParam(member)) return new BuilderParam(member, importer, typechecker) - - return new PlainProperty(member, importer, typechecker) + if (isState(member)) return new State(member, context) + if (isStorageProp(member)) return new StorageProp(member, context) + if (isStorageLink(member)) return new StorageLink(member, context) + if (isLocalStorageLink(member)) return new LocalStorageLink(member, context) + if (isLocalStorageProp(member)) return new LocalStorageProp(member, context) + if (isLink(member)) return new Link(member, context) + if (isProp(member)) return new Prop(member, context) + if (isObjectLink(member)) return new ObjectLink(member, context) + if (isProvide(member)) return new Provide(member, context) + if (isConsume(member)) return new Consume(member, context) + if (isBuilderParam(member)) return new BuilderParam(member, context) + + return new PlainProperty(member, context) } diff --git a/arkoala/ets-plugin/src/StructOptions.ts b/arkoala/ets-plugin/src/StructOptions.ts index ec68a5fe2..957b9e868 100644 --- a/arkoala/ets-plugin/src/StructOptions.ts +++ b/arkoala/ets-plugin/src/StructOptions.ts @@ -1,22 +1,22 @@ import * as ts from "@koalaui/ets-tsc" import { sourceStructName } from "./ApiUtils" import { adaptorClassName, isDefined, throwError } from "./utils" -import { classifyProperty } from "./PropertyTranslators" +import { PropertyTranslatorContext, classifyProperty } from "./PropertyTranslators" import { Importer } from "./Importer" import { ImportExport } from "./import-export"; export class StructOptions { private importExport: ImportExport constructor( - private typechecker: ts.TypeChecker, - private importer: Importer + private context: PropertyTranslatorContext, + private typechecker: ts.TypeChecker ) { this.importExport = new ImportExport(this.typechecker) } private implProperties(node: ts.StructDeclaration): { name: ts.Identifier, type: ts.TypeNode }[] { return node.members - .map(it => classifyProperty(it, this.importer, this.typechecker)) + .map(it => classifyProperty(it, this.context)) .filter(isDefined) .flatMap(it => it.implField()) } @@ -53,7 +53,7 @@ export class StructOptions { public updatedInitializersValue(node: ts.StructDeclaration, instance: ts.Identifier) { return ts.factory.createObjectLiteralExpression( node.members - .map(it => classifyProperty(it, this.importer, this.typechecker)) + .map(it => classifyProperty(it, this.context)) .filter(isDefined) .flatMap(it => it.toInitialization(instance)), true diff --git a/arkoala/ets-plugin/src/StructTransformer.ts b/arkoala/ets-plugin/src/StructTransformer.ts index 765bca56b..9cedcda75 100644 --- a/arkoala/ets-plugin/src/StructTransformer.ts +++ b/arkoala/ets-plugin/src/StructTransformer.ts @@ -62,7 +62,7 @@ import { voidLambdaType, WatchDecorator } from './utils' -import { BuilderParam, classifyProperty, ClassState, PropertyTranslator } from './PropertyTranslators' +import { BuilderParam, classifyProperty, ClassState, PropertyTranslator, PropertyTranslatorContext } from './PropertyTranslators' import { asIdentifier, assignment, @@ -89,7 +89,7 @@ import { StructOptions } from "./StructOptions" export class StructTransformer extends AbstractVisitor { private structOptions: StructOptions - + private propertyTranslatorContext: PropertyTranslatorContext constructor( sourceFile: ts.SourceFile, ctx: ts.TransformationContext, @@ -97,12 +97,14 @@ export class StructTransformer extends AbstractVisitor { public importer: Importer, public nameTable: NameTable, public entryTracker: EntryTracker, - public callTable: CallTable + public callTable: CallTable, + public extras?: ts.TransformerExtras ) { super(sourceFile, ctx) this.importer.addAdaptorImport(adaptorComponentName("PageTransitionEnter")) this.importer.addAdaptorImport(adaptorComponentName("PageTransitionExit")) - this.structOptions = new StructOptions(this.typechecker, this.importer) + this.propertyTranslatorContext = new PropertyTranslatorContext(importer, sourceFile, extras) + this.structOptions = new StructOptions(this.propertyTranslatorContext, this.typechecker) } dropImportEtsExtension(node: ts.ImportDeclaration, oldLiteral: string): ts.ImportDeclaration { @@ -840,7 +842,7 @@ export class StructTransformer extends AbstractVisitor { ) const propertyTranslators = filterDefined( - node.members.map(it => classifyProperty(it, this.importer, this.typechecker)) + node.members.map(it => classifyProperty(it, this.propertyTranslatorContext)) ) const translatedMembers = this.translateStructMembers(node, propertyTranslators) @@ -1130,7 +1132,7 @@ export class StructTransformer extends AbstractVisitor { if (!ts.isPropertyDeclaration(it) || !isState(it)) { return it } - return new ClassState(it, this.importer).translateMember() + return new ClassState(it, this.propertyTranslatorContext).translateMember() }) const createdClass = ts.factory.updateClassDeclaration( -- Gitee From 63e4e88b979fae5c750f66699c88d4d5032116e4 Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Mon, 3 Feb 2025 19:19:28 +0300 Subject: [PATCH 2/3] more Signed-off-by: Alexander Gorshenev --- arkoala/ets-plugin/src/PropertyTranslators.ts | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/arkoala/ets-plugin/src/PropertyTranslators.ts b/arkoala/ets-plugin/src/PropertyTranslators.ts index 212beda1c..7b956e0bc 100644 --- a/arkoala/ets-plugin/src/PropertyTranslators.ts +++ b/arkoala/ets-plugin/src/PropertyTranslators.ts @@ -70,10 +70,8 @@ import { StorageLinkDecorator, StoragePropDecorator, SyncedPropertyConstructor, - throwError, WatchDecorator, } from "./utils" -import { TransformationContext } from "typescript" import { MessageCode, reportError } from "./diagnostics" export class PropertyTranslatorContext { @@ -118,17 +116,17 @@ export abstract class PropertyTranslator { return createStateOf(this.context.importer, type, ...initializer) } - typeOrErrorType(type: ts.TypeNode|undefined): ts.TypeNode { + typeOrErrorType(node: ts.Node, type: ts.TypeNode|undefined): ts.TypeNode { if (!type) { reportError( MessageCode.EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED, - `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`, - this.property, + `Expected struct field to be explicitly typed in arkts 2.0 app: ${node.getText()}`, + node, this.context.sourceFile, this.context.extras ) } - return this.property.type ?? + return type ?? ts.factory.createTypeReferenceNode("") } @@ -437,8 +435,7 @@ export abstract class PropertyTranslator { this.context.extras ) } - const originalType = this.property.type ?? - ts.factory.createTypeReferenceNode("") + const originalType = this.typeOrErrorType(this.property, this.property.type) const backingName = backingFieldName(asIdentifier(this.property.name)) const backingType = ts.factory.createTypeReferenceNode( (impl === StateImplementation.SyncedProperty) ? @@ -455,16 +452,7 @@ export abstract class PropertyTranslator { protected plainImplField(): { name: ts.Identifier; type: ts.TypeNode }[] { const name = asIdentifier(this.property.name) - if (!this.property.type) { - reportError( - MessageCode.EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED, - `Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`, - this.property, - this.context.sourceFile, - this.context.extras - ) - } - const type = this.property.type ?? ts.factory.createTypeReferenceNode("ErrorType") + const type = this.typeOrErrorType(this.property, this.property.type) return [{ name, type }] } @@ -639,11 +627,11 @@ class StorageLink extends PropertyTranslator { const name = backingFieldName(asIdentifier(this.property.name)) const type = ts.factory.createTypeReferenceNode( this.context.importer.withRuntimeImport("MutableState"), - [this.property.type ?? throwError(`Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`)] + [this.typeOrErrorType(this.property, this.property.type)] ) const name1 = asIdentifier(this.property.name) - const type1 = this.property.type ?? throwError(`Expected struct field to be explicitly typed in arkts 2.0 app: ${this.property.getText()}`) + const type1 = this.typeOrErrorType(this.property, this.property.type) return [{ name, type }, { name: name1, type: type1 }] } -- Gitee From 511596c6e313a7303f4066782f791234f2391ff8 Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Tue, 4 Feb 2025 10:37:27 +0300 Subject: [PATCH 3/3] a forgotten file Signed-off-by: Alexander Gorshenev --- arkoala/ets-plugin/src/diagnostics.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 arkoala/ets-plugin/src/diagnostics.ts diff --git a/arkoala/ets-plugin/src/diagnostics.ts b/arkoala/ets-plugin/src/diagnostics.ts new file mode 100644 index 000000000..a580fd363 --- /dev/null +++ b/arkoala/ets-plugin/src/diagnostics.ts @@ -0,0 +1,18 @@ +import * as ts from "@koalaui/ets-tsc" + +export enum MessageCode { + EXPECTED_STRUCT_FIELD__TO_BE_EXPLICITLY_TYPED = 20001 + +} + +export function reportError(code: number, text: string, node: ts.Node, sourceFile: ts.SourceFile, extras?: ts.TransformerExtras) { + const lineAndChar = ts.getLineAndCharacterOfPosition(sourceFile, node.pos) + extras?.addDiagnostic({ + code: code, + messageText: text, + category: ts.DiagnosticCategory.Error, + file: sourceFile, + start: node.pos, + length: node.end - node.pos + }) ?? console.log(text) +} -- Gitee