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 * as arkts from '@koalaui/libarkts'; 17 18import { DecoratorNames, generateToRecord } from './utils'; 19import { PropertyTranslator } from './base'; 20import { GetterSetter, InitializerConstructor } from './types'; 21import { backingField, expectName } from '../../common/arkts-utils'; 22 23function getLocalStorageporpValueStr(node: arkts.AstNode): string | undefined { 24 if (!arkts.isClassProperty(node) || !node.value) return undefined; 25 26 return arkts.isStringLiteral(node.value) ? node.value.str : undefined; 27} 28function getLocalStorageporpAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { 29 const isLocalStorageporpAnnotation: boolean = 30 !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.LOCAL_STORAGE_PROP; 31 32 if (isLocalStorageporpAnnotation && anno.properties.length === 1) { 33 return getLocalStorageporpValueStr(anno.properties.at(0)!); 34 } 35 return undefined; 36} 37 38function getLocalStorageporpValueInAnnotation(node: arkts.ClassProperty): string | undefined { 39 const annotations: readonly arkts.AnnotationUsage[] = node.annotations; 40 41 for (let i = 0; i < annotations.length; i++) { 42 const anno: arkts.AnnotationUsage = annotations[i]; 43 const str: string | undefined = getLocalStorageporpAnnotationValue(anno); 44 if (!!str) { 45 return str; 46 } 47 } 48 49 return undefined; 50} 51 52export class LocalStoragePropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { 53 translateMember(): arkts.AstNode[] { 54 const originalName: string = expectName(this.property.key); 55 const newName: string = backingField(originalName); 56 57 this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... 58 return this.translateWithoutInitializer(newName, originalName); 59 } 60 61 cacheTranslatedInitializer(newName: string, originalName: string): void { 62 const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); 63 const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); 64 const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); 65 currentStructInfo.initializeBody.push(initializeStruct); 66 currentStructInfo.updateBody.push(updateStruct); 67 68 if (currentStructInfo.isReusable) { 69 const toRecord = generateToRecord(newName, originalName); 70 currentStructInfo.toRecordBody.push(toRecord); 71 } 72 73 arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); 74 } 75 76 generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { 77 const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); 78 if (!localStorageporpValueStr) { 79 throw new Error('LocalStorageProp required only one value!!'); // TODO: replace this with proper error message. 80 } 81 const insideMember = arkts.factory.createMemberExpression( 82 arkts.factory.createThisExpression(), 83 arkts.factory.createIdentifier('_entry_local_storage_'), 84 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 85 false, 86 false 87 ); 88 const binaryItem = arkts.factory.createCallExpression( 89 arkts.factory.createIdentifier('StorageLinkState'), 90 this.property.typeAnnotation ? [this.property.typeAnnotation] : [], 91 [ 92 insideMember, 93 arkts.factory.createStringLiteral(localStorageporpValueStr), 94 this.property.value ?? arkts.factory.createUndefinedLiteral(), 95 ] 96 ); 97 const call = arkts.factory.createCallExpression( 98 arkts.factory.createIdentifier('propState'), 99 this.property.typeAnnotation ? [this.property.typeAnnotation] : [], 100 [ 101 arkts.factory.createMemberExpression( 102 binaryItem, 103 arkts.factory.createIdentifier('value'), 104 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 105 false, 106 false 107 ), 108 ] 109 ); 110 return arkts.factory.createAssignmentExpression( 111 arkts.factory.createMemberExpression( 112 arkts.factory.createThisExpression(), 113 arkts.factory.createIdentifier(newName), 114 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 115 false, 116 false 117 ), 118 arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, 119 call 120 ); 121 } 122 123 generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { 124 const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); 125 if (!localStorageporpValueStr) { 126 throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. 127 } 128 129 const StorageLinkStateValue = arkts.factory.createMemberExpression( 130 arkts.factory.createCallExpression( 131 arkts.factory.createIdentifier('StorageLinkState'), 132 this.property.typeAnnotation ? [this.property.typeAnnotation] : [], 133 [ 134 arkts.factory.createMemberExpression( 135 arkts.factory.createThisExpression(), 136 arkts.factory.createIdentifier('_entry_local_storage_'), 137 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 138 false, 139 false 140 ), 141 arkts.factory.createStringLiteral(localStorageporpValueStr), 142 this.property.value ?? arkts.factory.createUndefinedLiteral(), 143 ] 144 ), 145 arkts.factory.createIdentifier('value'), 146 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 147 false, 148 false 149 ); 150 151 const test = arkts.factory.createMemberExpression( 152 arkts.factory.createThisExpression(), 153 arkts.factory.createIdentifier(newName), 154 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 155 false, 156 false 157 ); 158 159 const consequent = arkts.BlockStatement.createBlockStatement([ 160 arkts.factory.createExpressionStatement( 161 arkts.factory.createCallExpression( 162 arkts.factory.createMemberExpression( 163 arkts.factory.createTSNonNullExpression(test), 164 arkts.factory.createIdentifier('update'), 165 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 166 false, 167 false 168 ), 169 undefined, 170 [StorageLinkStateValue] 171 ) 172 ), 173 ]); 174 175 return arkts.factory.createExpressionStatement(arkts.factory.createIfStatement(test, consequent)); 176 } 177} 178