1/* 2 * Copyright (c) 2021 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 ts from 'typescript'; 17 18import { 19 INNER_COMPONENT_MEMBER_DECORATORS, 20 COMPONENT_NON_DECORATOR, 21 COMPONENT_STATE_DECORATOR, 22 COMPONENT_PROP_DECORATOR, 23 COMPONENT_LINK_DECORATOR, 24 COMPONENT_STORAGE_PROP_DECORATOR, 25 COMPONENT_STORAGE_LINK_DECORATOR, 26 COMPONENT_PROVIDE_DECORATOR, 27 COMPONENT_CONSUME_DECORATOR, 28 COMPONENT_OBJECT_LINK_DECORATOR, 29 COMPONENT_WATCH_DECORATOR, 30 COMPONENT_OBSERVED_DECORATOR, 31 OBSERVED_PROPERTY_SIMPLE, 32 OBSERVED_PROPERTY_OBJECT, 33 SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 34 SYNCHED_PROPERTY_SIMPLE_TWO_WAY, 35 SYNCHED_PROPERTY_OBJECT_TWO_WAY, 36 SYNCHED_PROPERTY_NESED_OBJECT, 37 CREATE_GET_METHOD, 38 CREATE_SET_METHOD, 39 CREATE_NEWVALUE_IDENTIFIER, 40 CREATE_CONSTRUCTOR_PARAMS, 41 ADD_PROVIDED_VAR, 42 INITIALIZE_CONSUME_FUNCTION, 43 APP_STORAGE, 44 APP_STORAGE_SET_AND_PROP, 45 APP_STORAGE_SET_AND_LINK, 46 APP_STORAGE_GET_OR_SET, 47 COMPONENT_CONSTRUCTOR_UNDEFINED, 48 SET_CONTROLLER_METHOD, 49 SET_CONTROLLER_CTR, 50 SET_CONTROLLER_CTR_TYPE, 51 BASE_COMPONENT_NAME, 52 COMPONENT_CREATE_FUNCTION, 53 COMPONENT_BUILDERPARAM_DECORATOR, 54 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 55 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, 56 COMPONENT_CUSTOM_DECORATOR 57} from './pre_define'; 58import { 59 forbiddenUseStateType, 60 BUILDIN_STYLE_NAMES 61} from './component_map'; 62import { 63 observedClassCollection, 64 enumCollection, 65 componentCollection, 66 classMethodCollection 67} from './validate_ui_syntax'; 68import { updateConstructor } from './process_component_constructor'; 69import { 70 LogType, 71 LogInfo, 72 componentInfo, 73 createFunction 74} from './utils'; 75import { 76 createReference, 77 isProperty 78} from './process_component_class'; 79import { globalProgram } from '../main'; 80 81export type ControllerType = { 82 hasController: boolean 83} 84 85export const observedPropertyDecorators: Set<string> = 86 new Set([COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR]); 87 88export const propAndLinkDecorators: Set<string> = 89 new Set([COMPONENT_PROP_DECORATOR, COMPONENT_LINK_DECORATOR]); 90 91export const appStorageDecorators: Set<string> = 92 new Set([COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, 93 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_PROP_DECORATOR]); 94 95export const mandatorySpecifyDefaultValueDecorators: Set<string> = 96 new Set([...observedPropertyDecorators, ...appStorageDecorators]); 97 98export const forbiddenSpecifyDefaultValueDecorators: Set<string> = 99 new Set([...propAndLinkDecorators, COMPONENT_CONSUME_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]); 100 101export const mandatoryToInitViaParamDecorators: Set<string> = 102 new Set([...propAndLinkDecorators, COMPONENT_OBJECT_LINK_DECORATOR]); 103 104export const setUpdateParamsDecorators: Set<string> = 105 new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR, 106 COMPONENT_BUILDERPARAM_DECORATOR 107 ]); 108 109export const immutableDecorators: Set<string> = 110 new Set([COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR]); 111 112export const simpleTypes: Set<ts.SyntaxKind> = new Set([ts.SyntaxKind.StringKeyword, 113 ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.EnumDeclaration]); 114 115export const decoratorParamSet: Set<string> = new Set(); 116 117export const stateObjectCollection: Set<string> = new Set(); 118 119export const builderParamObjectCollection: Map<string, Set<string>> = new Map(); 120 121export class UpdateResult { 122 private itemUpdate: boolean = false; 123 private ctorUpdate: boolean = false; 124 private properity: ts.PropertyDeclaration; 125 private ctor: ts.ConstructorDeclaration; 126 private variableGet: ts.GetAccessorDeclaration; 127 private variableSet: ts.SetAccessorDeclaration; 128 private updateParams: ts.Statement; 129 private deleteParams: boolean = false; 130 private controllerSet: ts.MethodDeclaration; 131 132 public setProperity(updateItem: ts.PropertyDeclaration) { 133 this.itemUpdate = true; 134 this.properity = updateItem; 135 } 136 137 public setCtor(updateCtor: ts.ConstructorDeclaration) { 138 this.ctorUpdate = true; 139 this.ctor = updateCtor; 140 } 141 142 public setControllerSet(updateControllerSet: ts.MethodDeclaration) { 143 this.controllerSet = updateControllerSet; 144 } 145 146 public getControllerSet(): ts.MethodDeclaration { 147 return this.controllerSet; 148 } 149 150 public setVariableGet(updateVariableGet: ts.GetAccessorDeclaration) { 151 this.variableGet = updateVariableGet; 152 } 153 154 public setVariableSet(updateVariableSet: ts.SetAccessorDeclaration) { 155 this.variableSet = updateVariableSet; 156 } 157 158 public setUpdateParams(updateParams: ts.Statement) { 159 this.updateParams = updateParams; 160 } 161 162 public setDeleteParams(deleteParams: boolean) { 163 this.deleteParams = deleteParams; 164 } 165 166 public isItemUpdate(): boolean { 167 return this.itemUpdate; 168 } 169 170 public isCtorUpdate(): boolean { 171 return this.ctorUpdate; 172 } 173 174 public getProperity(): ts.PropertyDeclaration { 175 return this.properity; 176 } 177 178 public getCtor(): ts.ConstructorDeclaration { 179 return this.ctor; 180 } 181 182 public getUpdateParams(): ts.Statement { 183 return this.updateParams; 184 } 185 186 public getVariableGet(): ts.GetAccessorDeclaration { 187 return this.variableGet; 188 } 189 190 public getVariableSet(): ts.SetAccessorDeclaration { 191 return this.variableSet; 192 } 193 194 public isDeleteParams(): boolean { 195 return this.deleteParams; 196 } 197} 198 199export const curPropMap: Map<string, string> = new Map(); 200 201export function processMemberVariableDecorators(parentName: ts.Identifier, 202 item: ts.PropertyDeclaration, ctorNode: ts.ConstructorDeclaration, watchMap: Map<string, ts.Node>, 203 checkController: ControllerType, log: LogInfo[], program: ts.Program, context: ts.TransformationContext, 204 hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): UpdateResult { 205 const updateResult: UpdateResult = new UpdateResult(); 206 const name: ts.Identifier = item.name as ts.Identifier; 207 if (!item.decorators || !item.decorators.length) { 208 curPropMap.set(name.escapedText.toString(), COMPONENT_NON_DECORATOR); 209 updateResult.setProperity(undefined); 210 updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_NON_DECORATOR)); 211 updateResult.setCtor(updateConstructor(ctorNode, [], [ 212 createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview, 213 interfaceNode)])); 214 updateResult.setControllerSet(createControllerSet(item, parentName, name, checkController)); 215 } else if (!item.type) { 216 validatePropertyNonType(name, log); 217 return updateResult; 218 } else if (validateCustomDecorator(item.decorators, log)) { 219 updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_CUSTOM_DECORATOR)); 220 } else { 221 processPropertyNodeDecorator(parentName, item, updateResult, ctorNode, name, watchMap, 222 log, program, context, hasPreview, interfaceNode); 223 } 224 return updateResult; 225} 226 227function createControllerSet(node: ts.PropertyDeclaration, componentName: ts.Identifier, 228 name: ts.Identifier, checkController: ControllerType): ts.MethodDeclaration { 229 if (componentCollection.customDialogs.has(componentName.getText()) && node.type && 230 node.type.getText() === SET_CONTROLLER_CTR_TYPE) { 231 checkController.hasController = true; 232 return ts.factory.createMethodDeclaration(undefined, undefined, undefined, 233 ts.factory.createIdentifier(SET_CONTROLLER_METHOD), undefined, undefined, 234 [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 235 ts.factory.createIdentifier(SET_CONTROLLER_CTR), undefined, 236 ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(SET_CONTROLLER_CTR_TYPE), 237 undefined), undefined)], undefined, ts.factory.createBlock( 238 [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 239 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), name), 240 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 241 ts.factory.createIdentifier(SET_CONTROLLER_CTR)))], true)); 242 } 243} 244 245function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.PropertyDeclaration, 246 updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, name: ts.Identifier, 247 watchMap: Map<string, ts.Node>, log: LogInfo[], program: ts.Program, 248 context: ts.TransformationContext, hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): 249 void { 250 let stateManagementDecoratorCount: number = 0; 251 for (let i = 0; i < node.decorators.length; i++) { 252 const decoratorName: string = node.decorators[i].getText().replace(/\(.*\)$/, '').trim(); 253 if (decoratorName !== COMPONENT_WATCH_DECORATOR) { 254 curPropMap.set(name.escapedText.toString(), decoratorName); 255 } 256 if (BUILDIN_STYLE_NAMES.has(decoratorName.replace('@', ''))) { 257 validateDuplicateDecorator(node.decorators[i], log); 258 } 259 if (decoratorName !== COMPONENT_WATCH_DECORATOR && isForbiddenUseStateType(node.type)) { 260 // @ts-ignore 261 validateForbiddenUseStateType(name, decoratorName, node.type.typeName.getText(), log); 262 return; 263 } 264 if (parentName.getText() === componentCollection.entryComponent && 265 mandatoryToInitViaParamDecorators.has(decoratorName)) { 266 validateHasIllegalDecoratorInEntry(parentName, name, decoratorName, log); 267 } 268 if (node.initializer && forbiddenSpecifyDefaultValueDecorators.has(decoratorName)) { 269 validatePropertyDefaultValue(name, decoratorName, log); 270 return; 271 } else if (!node.initializer && mandatorySpecifyDefaultValueDecorators.has(decoratorName)) { 272 validatePropertyNonDefaultValue(name, decoratorName, log); 273 return; 274 } 275 if (node.questionToken && mandatoryToInitViaParamDecorators.has(decoratorName)) { 276 validateHasIllegalQuestionToken(name, decoratorName, log); 277 } 278 if (!isSimpleType(node.type, program) && 279 decoratorName !== COMPONENT_BUILDERPARAM_DECORATOR) { 280 stateObjectCollection.add(name.escapedText.toString()); 281 } 282 if (decoratorName === COMPONENT_WATCH_DECORATOR && 283 validateWatchDecorator(name, node.decorators.length, log)) { 284 processWatch(node, node.decorators[i], watchMap, log); 285 } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 286 stateManagementDecoratorCount += 1; 287 processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context, 288 hasPreview, interfaceNode); 289 } 290 } 291 if (stateManagementDecoratorCount > 1) { 292 validateMultiDecorators(name, log); 293 return; 294 } 295} 296 297function processStateDecorators(node: ts.PropertyDeclaration, decorator: string, 298 updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, log: LogInfo[], 299 program: ts.Program, context: ts.TransformationContext, hasPreview:boolean, 300 interfaceNode: ts.InterfaceDeclaration): void { 301 const name: ts.Identifier = node.name as ts.Identifier; 302 updateResult.setProperity(undefined); 303 const updateState: ts.Statement[] = []; 304 const variableInitStatement: ts.Statement = 305 createVariableInitStatement(node, decorator, log, program, context, hasPreview, interfaceNode); 306 if (variableInitStatement) { 307 updateState.push(variableInitStatement); 308 } 309 addAddProvidedVar(node, name, decorator, updateState); 310 updateResult.setCtor(updateConstructor(ctorNode, [], [...updateState], false)); 311 if (decorator !== COMPONENT_BUILDERPARAM_DECORATOR) { 312 updateResult.setVariableGet(createGetAccessor(name, CREATE_GET_METHOD)); 313 updateResult.setDeleteParams(true); 314 } 315 if (!immutableDecorators.has(decorator)) { 316 updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD, node.type)); 317 } 318 if (setUpdateParamsDecorators.has(decorator)) { 319 updateResult.setUpdateParams(createUpdateParams(name, decorator)); 320 } 321} 322 323function processWatch(node: ts.PropertyDeclaration, decorator: ts.Decorator, 324 watchMap: Map<string, ts.Node>, log: LogInfo[]): void { 325 if (node.name) { 326 const propertyName: string = node.name.getText(); 327 if (decorator.expression && ts.isCallExpression(decorator.expression) && 328 decorator.expression.arguments && decorator.expression.arguments.length === 1) { 329 const currentClassMethod: Set<string> = classMethodCollection.get(node.parent.name.getText()); 330 const argument: ts.Node = decorator.expression.arguments[0]; 331 if (ts.isStringLiteral(argument)) { 332 if (currentClassMethod.has(argument.text)) { 333 watchMap.set(propertyName, argument); 334 } else { 335 log.push({ 336 type: LogType.ERROR, 337 message: `Cannot find name ${argument.getText()} in struct '${node.parent.name.getText()}'.`, 338 pos: argument.getStart() 339 }); 340 } 341 } else if (ts.isIdentifier(decorator.expression.arguments[0])) { 342 const content: string = decorator.expression.arguments[0].getText(); 343 const propertyNode: ts.PropertyAccessExpression = createPropertyAccessExpressionWithThis(content); 344 watchMap.set(propertyName, propertyNode); 345 decoratorParamSet.add(content); 346 validateWatchParam(LogType.WARN, argument.getStart(), log); 347 } else if (ts.isPropertyAccessExpression(decorator.expression.arguments[0])) { 348 watchMap.set(propertyName, decorator.expression.arguments[0]); 349 validateWatchParam(LogType.WARN, argument.getStart(), log); 350 } else { 351 validateWatchParam(LogType.ERROR, argument.getStart(), log); 352 } 353 } 354 } 355} 356 357function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: string, 358 log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean, 359 interfaceNode: ts.InterfaceDeclaration): ts.Statement { 360 const name: ts.Identifier = node.name as ts.Identifier; 361 let type: ts.TypeNode; 362 let updateState: ts.ExpressionStatement; 363 if (node.type) { 364 type = node.type; 365 } 366 switch (decorator) { 367 case COMPONENT_NON_DECORATOR: 368 updateState = updateNormalProperty(node, name, log, context); 369 break; 370 case COMPONENT_STATE_DECORATOR: 371 case COMPONENT_PROVIDE_DECORATOR: 372 updateState = updateObservedProperty(node, name, type, program); 373 break; 374 case COMPONENT_LINK_DECORATOR: 375 wrongDecoratorInPreview(node, COMPONENT_LINK_DECORATOR, hasPreview, log); 376 updateState = updateSynchedPropertyTwoWay(name, type, program); 377 break; 378 case COMPONENT_PROP_DECORATOR: 379 wrongDecoratorInPreview(node, COMPONENT_PROP_DECORATOR, hasPreview, log); 380 updateState = updateSynchedPropertyOneWay(name, type, decorator, log, program); 381 break; 382 case COMPONENT_STORAGE_PROP_DECORATOR: 383 case COMPONENT_STORAGE_LINK_DECORATOR: 384 const setFuncName: string = decorator === COMPONENT_STORAGE_PROP_DECORATOR ? 385 APP_STORAGE_SET_AND_PROP : APP_STORAGE_SET_AND_LINK; 386 updateState = updateStoragePropAndLinkProperty(node, name, setFuncName, log); 387 break; 388 case COMPONENT_OBJECT_LINK_DECORATOR: 389 updateState = updateSynchedPropertyNesedObject(name, type, decorator, log); 390 break; 391 case COMPONENT_CONSUME_DECORATOR: 392 wrongDecoratorInPreview(node, COMPONENT_CONSUME_DECORATOR, hasPreview, log); 393 updateState = updateConsumeProperty(node, name); 394 break; 395 } 396 const members = interfaceNode.members; 397 members.push(ts.factory.createPropertySignature(undefined, name, 398 ts.factory.createToken(ts.SyntaxKind.QuestionToken), type)); 399 interfaceNode = ts.factory.updateInterfaceDeclaration(interfaceNode, undefined, 400 interfaceNode.modifiers, interfaceNode.name, interfaceNode.typeParameters, 401 interfaceNode.heritageClauses, members); 402 return updateState; 403} 404 405function wrongDecoratorInPreview(node: ts.PropertyDeclaration, decorator: string, 406 hasPreview: boolean, log: LogInfo[]) { 407 if (hasPreview) { 408 log.push({ 409 type: LogType.WARN, 410 message: `The variable with ${decorator} in component with @Preview may ` + 411 `cause error in component preview mode`, 412 pos: node.getStart() 413 }); 414 } 415} 416 417function createUpdateParams(name: ts.Identifier, decorator: string): ts.Statement { 418 let updateParamsNode: ts.Statement; 419 switch (decorator) { 420 case COMPONENT_NON_DECORATOR: 421 case COMPONENT_STATE_DECORATOR: 422 case COMPONENT_PROVIDE_DECORATOR: 423 case COMPONENT_CUSTOM_DECORATOR: 424 updateParamsNode = createUpdateParamsWithIf(name); 425 break; 426 case COMPONENT_PROP_DECORATOR: 427 updateParamsNode = createUpdateParamsWithoutIf(name); 428 break; 429 case COMPONENT_BUILDERPARAM_DECORATOR: 430 updateParamsNode = createUpdateParamsWithoutIf(name); 431 break; 432 case COMPONENT_OBJECT_LINK_DECORATOR: 433 updateParamsNode = createUpdateParamsWithSet(name); 434 break; 435 } 436 return updateParamsNode; 437} 438 439function createUpdateParamsWithIf(name: ts.Identifier): ts.IfStatement { 440 return ts.factory.createIfStatement(ts.factory.createBinaryExpression( 441 ts.factory.createPropertyAccessExpression( 442 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), 443 ts.factory.createIdentifier(name.escapedText.toString())), 444 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), 445 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)), ts.factory.createBlock([ 446 createUpdateParamsWithoutIf(name)], true), undefined); 447} 448 449function createUpdateParamsWithoutIf(name: ts.Identifier): ts.ExpressionStatement { 450 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 451 createPropertyAccessExpressionWithThis(name.getText()), 452 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 453 createPropertyAccessExpressionWithParams(name.getText()))); 454} 455 456function createUpdateParamsWithSet(name: ts.Identifier): ts.ExpressionStatement { 457 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 458 ts.factory.createPropertyAccessExpression(createPropertyAccessExpressionWithThis(`__${name.getText()}`), 459 ts.factory.createIdentifier(CREATE_SET_METHOD)), undefined, 460 [createPropertyAccessExpressionWithParams(name.getText())])); 461} 462 463function updateNormalProperty(node: ts.PropertyDeclaration, name: ts.Identifier, 464 log: LogInfo[], context: ts.TransformationContext): ts.ExpressionStatement { 465 const init: ts.Expression = 466 ts.visitNode(node.initializer, visitDialogController); 467 function visitDialogController(node: ts.Node): ts.Node { 468 if (isProperty(node)) { 469 node = createReference(node as ts.PropertyAssignment); 470 } 471 return ts.visitEachChild(node, visitDialogController, context); 472 } 473 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 474 createPropertyAccessExpressionWithThis(name.getText()), 475 ts.factory.createToken(ts.SyntaxKind.EqualsToken), init || 476 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED))); 477} 478 479function updateObservedProperty(item: ts.PropertyDeclaration, name: ts.Identifier, 480 type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement { 481 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 482 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 483 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression( 484 ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE : 485 OBSERVED_PROPERTY_OBJECT), undefined, [item.initializer, ts.factory.createThis(), 486 ts.factory.createStringLiteral(name.escapedText.toString())]))); 487} 488 489function updateSynchedPropertyTwoWay(nameIdentifier: ts.Identifier, type: ts.TypeNode, 490 program: ts.Program): ts.ExpressionStatement { 491 const name: string = nameIdentifier.escapedText.toString(); 492 const functionName: string = isSimpleType(type, program) ? 493 SYNCHED_PROPERTY_SIMPLE_TWO_WAY : SYNCHED_PROPERTY_OBJECT_TWO_WAY; 494 return createInitExpressionStatementForDecorator(name, functionName, 495 createPropertyAccessExpressionWithParams(name)); 496} 497 498function updateSynchedPropertyOneWay(nameIdentifier: ts.Identifier, type: ts.TypeNode, 499 decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement { 500 const name: string = nameIdentifier.escapedText.toString(); 501 if (isSimpleType(type, program)) { 502 return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 503 createPropertyAccessExpressionWithParams(name)); 504 } else { 505 validateNonSimpleType(nameIdentifier, decoractor, log); 506 } 507} 508 509function updateStoragePropAndLinkProperty(node: ts.PropertyDeclaration, name: ts.Identifier, 510 setFuncName: string, log: LogInfo[]): ts.ExpressionStatement { 511 if (isSingleKey(node)) { 512 const key: string = getDecoratorKey(node); 513 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 514 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 515 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression( 516 ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression( 517 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(APP_STORAGE), 518 ts.factory.createIdentifier(APP_STORAGE_GET_OR_SET)), undefined, []), 519 ts.factory.createIdentifier(setFuncName)), undefined, [ts.factory.createStringLiteral(key), 520 node.initializer, ts.factory.createThis()]))); 521 } else { 522 validateAppStorageDecoractorsNonSingleKey(node, log); 523 } 524} 525 526function getDecoratorKey(node: ts.PropertyDeclaration): string { 527 let key: string; 528 // @ts-ignore 529 const keyNameNode: ts.Node = node.decorators[0].expression.arguments[0]; 530 if (ts.isIdentifier(keyNameNode)) { 531 key = keyNameNode.getText(); 532 decoratorParamSet.add(key); 533 } else if (ts.isStringLiteral(keyNameNode)) { 534 key = keyNameNode.text; 535 } 536 return key; 537} 538 539function updateSynchedPropertyNesedObject(nameIdentifier: ts.Identifier, 540 type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement { 541 if (isObservedClassType(type)) { 542 return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT, 543 createPropertyAccessExpressionWithParams(nameIdentifier.getText())); 544 } else { 545 validateNonObservedClassType(nameIdentifier, decoractor, log); 546 } 547} 548 549function updateConsumeProperty(node: ts.PropertyDeclaration, 550 nameIdentifier: ts.Identifier): ts.ExpressionStatement { 551 const name: string = nameIdentifier.getText(); 552 let propertyOrAliasName: string; 553 if (isSingleKey(node)) { 554 propertyOrAliasName = getDecoratorKey(node); 555 } else { 556 propertyOrAliasName = name; 557 } 558 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 559 createPropertyAccessExpressionWithThis(`__${name}`), 560 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression( 561 createPropertyAccessExpressionWithThis(INITIALIZE_CONSUME_FUNCTION), undefined, [ 562 ts.factory.createStringLiteral(propertyOrAliasName), ts.factory.createStringLiteral(name)]))); 563} 564 565function createCustomComponentBuilderArrowFunction(parent: ts.PropertyDeclaration, 566 jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction { 567 return ts.factory.createArrowFunction(undefined, undefined, [], undefined, 568 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([ 569 ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList( 570 [ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)], 571 ts.NodeFlags.Let)), ts.factory.createExpressionStatement(ts.factory.createCallExpression( 572 ts.factory.createPropertyAccessExpression(jsDialog, 573 ts.factory.createIdentifier(SET_CONTROLLER_METHOD)), undefined, 574 [ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 575 parent.name as ts.Identifier)])), ts.factory.createExpressionStatement( 576 createViewCreate(jsDialog))], true)); 577} 578 579export function createViewCreate(node: ts.NewExpression | ts.Identifier): ts.CallExpression { 580 return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME), 581 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node])); 582} 583 584export function createCustomComponentNewExpression(node: ts.CallExpression): ts.NewExpression { 585 const newNode: ts.NewExpression = ts.factory.createNewExpression(node.expression, 586 node.typeArguments, node.arguments.length ? node.arguments : []); 587 return addCustomComponentId(newNode); 588} 589 590function addCustomComponentId(node: ts.NewExpression): ts.NewExpression { 591 for (const item of componentCollection.customComponents) { 592 componentInfo.componentNames.add(item); 593 } 594 componentInfo.componentNames.forEach((name: string) => { 595 const nodeIdentifier: ts.Identifier | ts.PropertyAccessExpression = 596 node.expression as ts.Identifier | ts.PropertyAccessExpression; 597 let argumentsArray: ts.Expression[]; 598 if (node.arguments && node.arguments.length) { 599 argumentsArray = Array.from(node.arguments); 600 } 601 if (nodeIdentifier && (ts.isIdentifier(nodeIdentifier) && 602 nodeIdentifier.escapedText === name || ts.isPropertyAccessExpression(nodeIdentifier) && 603 ts.isIdentifier(nodeIdentifier.name) && nodeIdentifier.name.escapedText === name)) { 604 if (!argumentsArray) { 605 argumentsArray = [ts.factory.createObjectLiteralExpression([], true)]; 606 } 607 argumentsArray.unshift(ts.factory.createStringLiteral((++componentInfo.id).toString()), 608 ts.factory.createThis()); 609 node = 610 ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray); 611 } else if (argumentsArray) { 612 node = 613 ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray); 614 } 615 }); 616 return node; 617} 618 619function createInitExpressionStatementForDecorator(propertyName: string, functionName: string, 620 parameterNode: ts.Expression): ts.ExpressionStatement { 621 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 622 createPropertyAccessExpressionWithThis(`__${propertyName}`), 623 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression( 624 ts.factory.createIdentifier(functionName), undefined, [parameterNode, ts.factory.createThis(), 625 ts.factory.createStringLiteral(propertyName)]))); 626} 627 628function createPropertyAccessExpressionWithParams(propertyName: string): ts.PropertyAccessExpression { 629 return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), 630 ts.factory.createIdentifier(propertyName)); 631} 632 633function createPropertyAccessExpressionWithThis(propertyName: string): ts.PropertyAccessExpression { 634 return ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 635 ts.factory.createIdentifier(propertyName)); 636} 637 638function addAddProvidedVar(node: ts.PropertyDeclaration, name: ts.Identifier, 639 decoratorName: string, updateState: ts.Statement[]): void { 640 if (decoratorName === COMPONENT_PROVIDE_DECORATOR) { 641 let parameterName: string; 642 if (isSingleKey(node)) { 643 parameterName = getDecoratorKey(node); 644 updateState.push(createAddProvidedVar(parameterName, name)); 645 } 646 if (parameterName !== name.getText()) { 647 updateState.push(createAddProvidedVar(name.getText(), name)); 648 } 649 } 650} 651 652function createAddProvidedVar(propertyOrAliasName: string, 653 name: ts.Identifier): ts.ExpressionStatement { 654 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 655 createPropertyAccessExpressionWithThis(ADD_PROVIDED_VAR), undefined, [ 656 ts.factory.createStringLiteral(propertyOrAliasName), 657 createPropertyAccessExpressionWithThis(`__${name.getText()}`)])); 658} 659 660function createGetAccessor(item: ts.Identifier, express: string): ts.GetAccessorDeclaration { 661 const getAccessorStatement: ts.GetAccessorDeclaration = 662 ts.factory.createGetAccessorDeclaration(undefined, undefined, item, [], undefined, 663 ts.factory.createBlock([ts.factory.createReturnStatement( 664 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 665 createPropertyAccessExpressionWithThis(`__${item.getText()}`), 666 ts.factory.createIdentifier(express)), undefined, []))], true)); 667 return getAccessorStatement; 668} 669 670function createSetAccessor(item: ts.Identifier, express: string, type: ts.TypeNode): 671 ts.SetAccessorDeclaration { 672 const setAccessorStatement: ts.SetAccessorDeclaration = 673 ts.factory.createSetAccessorDeclaration(undefined, undefined, item, 674 [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 675 ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, type, 676 undefined)], ts.factory.createBlock([ts.factory.createExpressionStatement( 677 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 678 createPropertyAccessExpressionWithThis(`__${item.getText()}`), 679 ts.factory.createIdentifier(express)), undefined, 680 [ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER)]))], true)); 681 return setAccessorStatement; 682} 683 684function isForbiddenUseStateType(typeNode: ts.TypeNode): boolean { 685 if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) && 686 forbiddenUseStateType.has(typeNode.typeName.getText())) { 687 return true; 688 } 689 return false; 690} 691 692export function isSimpleType(typeNode: ts.TypeNode, program: ts.Program, log?: LogInfo[]): boolean { 693 typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); 694 let checker: ts.TypeChecker; 695 if (globalProgram.program) { 696 checker = globalProgram.program.getTypeChecker(); 697 } else if (globalProgram.watchProgram) { 698 checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker(); 699 } else if (program) { 700 checker = program.getTypeChecker(); 701 } 702 const enumType: ts.SyntaxKind = getEnumType(typeNode, checker); 703 if (simpleTypes.has(enumType || typeNode.kind) || isEnumtype(typeNode)) { 704 return true; 705 } else if (ts.isUnionTypeNode(typeNode) && typeNode.types) { 706 const types: ts.NodeArray<ts.TypeNode> = typeNode.types; 707 let basicType: boolean = false; 708 let referenceType: boolean = false; 709 for (let i = 0; i < types.length; i++) { 710 const enumType: ts.SyntaxKind = getEnumType(types[i], checker); 711 if (simpleTypes.has(enumType || types[i].kind) || isEnumtype(typeNode)) { 712 basicType = true; 713 } else { 714 referenceType = true; 715 } 716 if (basicType && referenceType && log) { 717 validateVariableType(typeNode, log); 718 return false; 719 } 720 } 721 return true; 722 } 723 return false; 724} 725 726function getEnumType(typeNode: ts.TypeNode, checker: ts.TypeChecker): ts.SyntaxKind { 727 if (!checker) { 728 return; 729 } 730 typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); 731 if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) { 732 const type: ts.Type = 733 checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(typeNode.typeName)); 734 if (type.symbol && type.symbol.valueDeclaration) { 735 return type.symbol.valueDeclaration.kind; 736 } 737 } 738} 739 740function isEnumtype(typeNode: ts.TypeNode): boolean { 741 if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) { 742 return enumCollection.has(typeNode.typeName.getText()); 743 } 744} 745 746function isObservedClassType(type: ts.TypeNode): boolean { 747 if (ts.isTypeReferenceNode(type) && observedClassCollection.has(type.getText())) { 748 return true; 749 } else if (ts.isUnionTypeNode(type) && type.types) { 750 const types: ts.NodeArray<ts.TypeNode> = type.types; 751 for (let i = 0; i < types.length; i++) { 752 if (!observedClassCollection.has(types[i].getText())) { 753 return false; 754 } 755 } 756 return true; 757 } 758 return false; 759} 760 761function validateAppStorageDecoractorsNonSingleKey(node: ts.PropertyDeclaration, 762 log: LogInfo[]): void { 763 if (ts.isIdentifier(node.decorators[0].expression)) { 764 validateDecoratorNonSingleKey(node.decorators[0].expression, log); 765 } else if (ts.isCallExpression(node.decorators[0].expression) && 766 ts.isIdentifier(node.decorators[0].expression.expression)) { 767 validateDecoratorNonSingleKey(node.decorators[0].expression.expression, log); 768 } 769} 770 771function isSingleKey(node: ts.PropertyDeclaration): boolean { 772 if (ts.isCallExpression(node.decorators[0].expression) && 773 node.decorators[0].expression.arguments && 774 node.decorators[0].expression.arguments.length === 1 && 775 (ts.isIdentifier(node.decorators[0].expression.arguments[0]) || 776 ts.isStringLiteral(node.decorators[0].expression.arguments[0]))) { 777 return true; 778 } 779} 780 781function validateMultiDecorators(name: ts.Identifier, log: LogInfo[]): void { 782 log.push({ 783 type: LogType.ERROR, 784 message: `The property '${name.escapedText.toString()}' cannot have mutilate state management decorators.`, 785 pos: name.getStart() 786 }); 787} 788 789function validateDecoratorNonSingleKey(decoratorsIdentifier: ts.Identifier, 790 log: LogInfo[]): void { 791 log.push({ 792 type: LogType.ERROR, 793 message: `The decorator ${decoratorsIdentifier.escapedText.toString()} should have a single key.`, 794 pos: decoratorsIdentifier.getStart() 795 }); 796} 797 798function validatePropertyNonDefaultValue(propertyName: ts.Identifier, decorator: string, 799 log: LogInfo[]): void { 800 log.push({ 801 type: LogType.ERROR, 802 message: `The ${decorator} property '${propertyName.getText()}' must be specified a default value.`, 803 pos: propertyName.getStart() 804 }); 805} 806 807function validatePropertyDefaultValue(propertyName: ts.Identifier, decorator: string, 808 log: LogInfo[]): void { 809 log.push({ 810 type: LogType.ERROR, 811 message: `The ${decorator} property '${propertyName.getText()}' cannot be specified a default value.`, 812 pos: propertyName.getStart() 813 }); 814} 815 816function validatePropertyNonType(propertyName: ts.Identifier, log: LogInfo[]): void { 817 log.push({ 818 type: LogType.ERROR, 819 message: `The property '${propertyName.getText()}' must specify a type.`, 820 pos: propertyName.getStart() 821 }); 822} 823 824function validateNonSimpleType(propertyName: ts.Identifier, decorator: string, 825 log: LogInfo[]): void { 826 log.push({ 827 type: LogType.ERROR, 828 message: `The type of the ${decorator} property '${propertyName.getText()}' ` + 829 `can only be string, number or boolean.`, 830 pos: propertyName.getStart() 831 }); 832} 833 834function validateNonObservedClassType(propertyName: ts.Identifier, decorator: string, 835 log: LogInfo[]): void { 836 log.push({ 837 type: LogType.ERROR, 838 message: `The type of the ${decorator} property '${propertyName.getText()}' can only be ` + 839 `objects of classes decorated with ${COMPONENT_OBSERVED_DECORATOR} class decorator in ets (not ts).`, 840 pos: propertyName.getStart() 841 }); 842} 843 844function validateHasIllegalQuestionToken(propertyName: ts.Identifier, decorator: string, 845 log: LogInfo[]): void { 846 log.push({ 847 type: LogType.WARN, 848 message: `The ${decorator} property '${propertyName.getText()}' cannot be an optional parameter.`, 849 pos: propertyName.getStart() 850 }); 851} 852 853function validateHasIllegalDecoratorInEntry(parentName: ts.Identifier, propertyName: ts.Identifier, 854 decorator: string, log: LogInfo[]): void { 855 log.push({ 856 type: LogType.WARN, 857 message: `The @Entry component '${parentName.getText()}' cannot have the ` + 858 `${decorator} property '${propertyName.getText()}'.`, 859 pos: propertyName.getStart() 860 }); 861} 862 863function validateForbiddenUseStateType(propertyName: ts.Identifier, decorator: string, type: string, 864 log: LogInfo[]): void { 865 log.push({ 866 type: LogType.ERROR, 867 message: `The ${decorator} property '${propertyName.getText()}' cannot be a '${type}' object.`, 868 pos: propertyName.getStart() 869 }); 870} 871 872function validateDuplicateDecorator(decorator: ts.Decorator, log: LogInfo[]): void { 873 log.push({ 874 type: LogType.ERROR, 875 message: `The decorator '${decorator.getText()}' cannot have the same name as the built-in ` + 876 `style attribute '${decorator.getText().replace('@', '')}'.`, 877 pos: decorator.getStart() 878 }); 879} 880 881function validateWatchDecorator(propertyName: ts.Identifier, length: number, log: LogInfo[]): boolean { 882 if (length === 1) { 883 log.push({ 884 type: LogType.ERROR, 885 message: `Regular variable '${propertyName.escapedText.toString()}' can not be decorated with @Watch.`, 886 pos: propertyName.getStart() 887 }); 888 return false; 889 } 890 return true; 891} 892 893function validateWatchParam(type: LogType, pos: number, log: LogInfo[]): void { 894 log.push({ 895 type: type, 896 message: 'The parameter should be a string.', 897 pos: pos 898 }); 899} 900 901function validateVariableType(typeNode: ts.TypeNode, log: LogInfo[]): void { 902 log.push({ 903 type: LogType.ERROR, 904 message: 'The state variable type of a struct component cannot be declared by both a simple type and an object type.', 905 pos: typeNode.getStart() 906 }); 907} 908 909function validateCustomDecorator(decorators: ts.NodeArray<ts.Decorator>, log: LogInfo[]): boolean { 910 let hasInnerDecorator: boolean = false; 911 let hasCustomDecorator: boolean = false; 912 let innerDecorator: ts.Decorator; 913 for(let i = 0; i < decorators.length; i++) { 914 let decorator: ts.Decorator = decorators[i]; 915 const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim(); 916 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 917 hasInnerDecorator = true; 918 innerDecorator = innerDecorator ? innerDecorator : decorator; 919 } else { 920 hasCustomDecorator = true; 921 } 922 } 923 if (hasCustomDecorator && hasInnerDecorator) { 924 log.push({ 925 type: LogType.ERROR, 926 message: `The inner decorator ${innerDecorator.getText()} cannot be used together with custom decorator.`, 927 pos: innerDecorator.getStart() 928 }); 929 } else if(!hasInnerDecorator) { 930 return true; 931 } 932 return false; 933} 934