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'; 17const path = require('path'); 18 19import { 20 INNER_COMPONENT_MEMBER_DECORATORS, 21 COMPONENT_NON_DECORATOR, 22 COMPONENT_STATE_DECORATOR, 23 COMPONENT_PROP_DECORATOR, 24 COMPONENT_LINK_DECORATOR, 25 COMPONENT_STORAGE_PROP_DECORATOR, 26 COMPONENT_STORAGE_LINK_DECORATOR, 27 COMPONENT_PROVIDE_DECORATOR, 28 COMPONENT_CONSUME_DECORATOR, 29 COMPONENT_OBJECT_LINK_DECORATOR, 30 COMPONENT_WATCH_DECORATOR, 31 COMPONENT_OBSERVED_DECORATOR, 32 OBSERVED_PROPERTY_SIMPLE, 33 OBSERVED_PROPERTY_OBJECT, 34 SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 35 SYNCHED_PROPERTY_SIMPLE_TWO_WAY, 36 SYNCHED_PROPERTY_OBJECT_TWO_WAY, 37 SYNCHED_PROPERTY_NESED_OBJECT, 38 CREATE_GET_METHOD, 39 CREATE_SET_METHOD, 40 CREATE_NEWVALUE_IDENTIFIER, 41 CREATE_CONSTRUCTOR_PARAMS, 42 ADD_PROVIDED_VAR, 43 INITIALIZE_CONSUME_FUNCTION, 44 APP_STORAGE, 45 APP_STORAGE_SET_AND_PROP, 46 APP_STORAGE_SET_AND_LINK, 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 EXTNAME_ETS, 57 _GENERATE_ID, 58 RMELMTID, 59 PURGEDEPENDENCYONELMTID, 60 BASICDECORATORS, 61 BASE_COMPONENT_NAME_PU, 62 OBSERVED_PROPERTY_SIMPLE_PU, 63 OBSERVED_PROPERTY_OBJECT_PU, 64 SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU, 65 SYNCHED_PROPERTY_OBJECT_TWO_WAY_PU, 66 SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 67 SYNCHED_PROPERTY_OBJECT_ONE_WAY_PU, 68 SYNCHED_PROPERTY_NESED_OBJECT_PU, 69 COMPONENT_CUSTOM_DECORATOR, 70 THIS, 71 CREATE_STORAGE_LINK, 72 CREATE_STORAGE_PROP, 73 ELMTID, 74 COMPONENT_CONSTRUCTOR_PARAMS, 75 RESERT, 76 COMPONENT_IF_UNDEFINED, 77 COMPONENT_REQUIRE_DECORATOR, 78 OBSERVED, 79 TRUE, 80 FALSE 81} from './pre_define'; 82import { 83 forbiddenUseStateType, 84 BUILDIN_STYLE_NAMES 85} from './component_map'; 86import { 87 observedClassCollection, 88 enumCollection, 89 componentCollection, 90 classMethodCollection 91} from './validate_ui_syntax'; 92import { updateConstructor } from './process_component_constructor'; 93import { 94 LogType, 95 LogInfo, 96 componentInfo, 97 storedFileInfo 98} from './utils'; 99import { 100 createReference, 101 isProperty, 102 isRegularProperty 103} from './process_component_class'; 104import { transformLog, resourceFileName } from './process_ui_syntax'; 105import { 106 globalProgram, 107 projectConfig, 108 partialUpdateConfig 109} from '../main'; 110import { 111 parentConditionalExpression, 112 createFunction, 113 getRealNodePos, 114 isWrappedBuilder 115} from './process_component_build'; 116import { 117 CUSTOM_BUILDER_METHOD, 118 INNER_CUSTOM_LOCALBUILDER_METHOD 119} from './component_map'; 120export type ControllerType = { 121 hasController: boolean; 122 unassignedControllerSet: Set<string>; 123}; 124 125export const observedPropertyDecorators: Set<string> = 126 new Set([COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR]); 127 128export const propAndLinkDecorators: Set<string> = 129 new Set([COMPONENT_PROP_DECORATOR, COMPONENT_LINK_DECORATOR]); 130 131export const appStorageDecorators: Set<string> = 132 new Set([COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, 133 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_PROP_DECORATOR]); 134 135export const mandatorySpecifyDefaultValueDecorators: Set<string> = 136 new Set([...observedPropertyDecorators, ...appStorageDecorators]); 137 138export const requireCanReleaseMandatoryDecorators: Set<string> = 139 new Set([COMPONENT_PROP_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR, 140 ...observedPropertyDecorators]); 141 142export const forbiddenSpecifyDefaultValueDecorators: Set<string> = 143 new Set([COMPONENT_LINK_DECORATOR, COMPONENT_CONSUME_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]); 144 145export const mandatoryToInitViaParamDecorators: Set<string> = 146 new Set([...propAndLinkDecorators, COMPONENT_OBJECT_LINK_DECORATOR]); 147 148export const setUpdateParamsDecorators: Set<string> = 149 new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR, 150 COMPONENT_BUILDERPARAM_DECORATOR 151 ]); 152 153export const setStateVarsDecorators: Set<string> = new Set([COMPONENT_OBJECT_LINK_DECORATOR]); 154 155export const immutableDecorators: Set<string> = 156 new Set([COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR]); 157 158export const simpleTypes: Set<ts.SyntaxKind> = new Set([ts.SyntaxKind.StringKeyword, 159 ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.EnumDeclaration]); 160 161export const decoratorParamSet: Set<string> = new Set(); 162 163export const stateObjectCollection: Set<string> = new Set(); 164 165export class UpdateResult { 166 private itemUpdate: boolean = false; 167 private ctorUpdate: boolean = false; 168 private properity: ts.PropertyDeclaration; 169 private ctor: ts.ConstructorDeclaration; 170 private variableGet: ts.GetAccessorDeclaration; 171 private variableSet: ts.SetAccessorDeclaration; 172 private updateParams: ts.Statement; 173 private deleteParams: boolean = false; 174 private controllerSet: ts.MethodDeclaration; 175 private purgeVariableDepStatement: ts.Statement; 176 private decoratorName: string; 177 private stateVarsParams: ts.Statement; 178 179 public setProperity(updateItem: ts.PropertyDeclaration): void { 180 this.itemUpdate = true; 181 this.properity = updateItem; 182 } 183 184 public setCtor(updateCtor: ts.ConstructorDeclaration): void { 185 this.ctorUpdate = true; 186 this.ctor = updateCtor; 187 } 188 189 public setControllerSet(updateControllerSet: ts.MethodDeclaration): void { 190 this.controllerSet = updateControllerSet; 191 } 192 193 public getControllerSet(): ts.MethodDeclaration { 194 return this.controllerSet; 195 } 196 197 public setVariableGet(updateVariableGet: ts.GetAccessorDeclaration): void { 198 this.variableGet = updateVariableGet; 199 } 200 201 public setVariableSet(updateVariableSet: ts.SetAccessorDeclaration): void { 202 this.variableSet = updateVariableSet; 203 } 204 205 public setUpdateParams(updateParams: ts.Statement): void { 206 this.updateParams = updateParams; 207 } 208 209 public setStateVarsParams(stateVarsParams: ts.Statement): void { 210 this.stateVarsParams = stateVarsParams; 211 } 212 213 public setDeleteParams(deleteParams: boolean): void { 214 this.deleteParams = deleteParams; 215 } 216 217 public setPurgeVariableDepStatement(purgeVariableDepStatement: ts.Statement): void { 218 this.purgeVariableDepStatement = purgeVariableDepStatement; 219 } 220 221 public setDecoratorName(decoratorName: string): void { 222 this.decoratorName = decoratorName; 223 } 224 225 public isItemUpdate(): boolean { 226 return this.itemUpdate; 227 } 228 229 public isCtorUpdate(): boolean { 230 return this.ctorUpdate; 231 } 232 233 public getProperity(): ts.PropertyDeclaration { 234 return this.properity; 235 } 236 237 public getCtor(): ts.ConstructorDeclaration { 238 return this.ctor; 239 } 240 241 public getUpdateParams(): ts.Statement { 242 return this.updateParams; 243 } 244 245 public getStateVarsParams(): ts.Statement { 246 return this.stateVarsParams; 247 } 248 249 public getPurgeVariableDepStatement(): ts.Statement { 250 return this.purgeVariableDepStatement; 251 } 252 253 public getVariableGet(): ts.GetAccessorDeclaration { 254 return this.variableGet; 255 } 256 257 public getVariableSet(): ts.SetAccessorDeclaration { 258 return this.variableSet; 259 } 260 261 public getDecoratorName(): string { 262 return this.decoratorName; 263 } 264 265 public isDeleteParams(): boolean { 266 return this.deleteParams; 267 } 268} 269 270export class PropMapManager { 271 static curPropMap: Map<string, string> = new Map(); 272 static logInfoMap: Map<string, LogInfo[]> = new Map(); 273 274 public static register(identifierName: string, decoratorName: string): void { 275 PropMapManager.curPropMap.set(identifierName, decoratorName); 276 277 if (decoratorName !== COMPONENT_NON_DECORATOR) { 278 PropMapManager.releaseLogs(identifierName, COMPONENT_NON_DECORATOR); 279 } 280 } 281 282 public static find(identifierName: string): string { 283 return PropMapManager.curPropMap.get(identifierName); 284 } 285 286 public static reserveLog(identifierName: string, decoratorName: string, log: LogInfo): void { 287 const key: string = `${identifierName}-${decoratorName}`; 288 const logInfos: LogInfo[] = PropMapManager.logInfoMap.get(key) ?? []; 289 PropMapManager.logInfoMap.set(key, [...logInfos, log]); 290 } 291 292 public static releaseLogs(identifierName: string, decoratorName: string): void { 293 const key: string = `${identifierName}-${decoratorName}`; 294 if (PropMapManager.logInfoMap.has(key)) { 295 PropMapManager.logInfoMap.delete(key); 296 } 297 } 298 299 public static reset(): void { 300 PropMapManager.curPropMap.clear(); 301 PropMapManager.logInfoMap.clear(); 302 } 303} 304 305export function processMemberVariableDecorators(parentName: ts.Identifier, 306 item: ts.PropertyDeclaration, ctorNode: ts.ConstructorDeclaration, watchMap: Map<string, ts.Node>, 307 checkController: ControllerType, log: LogInfo[], program: ts.Program, context: ts.TransformationContext, 308 hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): UpdateResult { 309 const updateResult: UpdateResult = new UpdateResult(); 310 const name: ts.Identifier = item.name as ts.Identifier; 311 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item); 312 if (isRegularProperty(decorators)) { 313 if (!name.escapedText) { 314 return updateResult; 315 } 316 PropMapManager.register(name.escapedText.toString(), COMPONENT_NON_DECORATOR); 317 updateResult.setProperity(undefined); 318 updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_NON_DECORATOR)); 319 updateResult.setCtor(updateConstructor(ctorNode, [], [ 320 createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview, 321 interfaceNode)], [])); 322 updateResult.setControllerSet(createControllerSet(item, parentName, name, checkController)); 323 if (partialUpdateConfig.partialUpdateMode) { 324 updateResult.setDeleteParams(true); 325 } 326 } else if (!item.type) { 327 validatePropertyNonType(name, log); 328 return updateResult; 329 } else if (validateCustomDecorator(decorators, log)) { 330 updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_CUSTOM_DECORATOR)); 331 } else { 332 processPropertyNodeDecorator(parentName, item, updateResult, ctorNode, name, watchMap, 333 log, program, context, hasPreview, interfaceNode); 334 } 335 if (decorators && decorators.length && validatePropDecorator(decorators)) { 336 updateResult.setStateVarsParams(createStateVarsBody(name)); 337 } 338 return updateResult; 339} 340 341function createStateVarsBody(name: ts.Identifier): ts.ExpressionStatement { 342 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 343 ts.factory.createPropertyAccessExpression( 344 ts.factory.createPropertyAccessExpression( 345 ts.factory.createThis(), 346 ts.factory.createIdentifier('__' + name.escapedText.toString()) 347 ), 348 ts.factory.createIdentifier(RESERT) 349 ), 350 undefined, 351 [ts.factory.createPropertyAccessExpression( 352 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARAMS), 353 name 354 )] 355 )); 356} 357 358function createControllerSet(node: ts.PropertyDeclaration, componentName: ts.Identifier, 359 name: ts.Identifier, checkController: ControllerType): ts.MethodDeclaration { 360 if (componentCollection.customDialogs.has(componentName.getText()) && node.type && 361 node.type.getText() === SET_CONTROLLER_CTR_TYPE) { 362 checkController.unassignedControllerSet.add(name.getText()); 363 checkController.hasController = true; 364 return ts.factory.createMethodDeclaration(undefined, undefined, 365 ts.factory.createIdentifier(SET_CONTROLLER_METHOD), undefined, undefined, 366 [ts.factory.createParameterDeclaration(undefined, undefined, 367 ts.factory.createIdentifier(SET_CONTROLLER_CTR), undefined, 368 ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(SET_CONTROLLER_CTR_TYPE), 369 undefined), undefined)], undefined, ts.factory.createBlock( 370 [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 371 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), name), 372 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 373 ts.factory.createIdentifier(SET_CONTROLLER_CTR)))], true)); 374 } 375 return undefined; 376} 377 378function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.PropertyDeclaration, 379 updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, name: ts.Identifier, 380 watchMap: Map<string, ts.Node>, log: LogInfo[], program: ts.Program, 381 context: ts.TransformationContext, hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): void { 382 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 383 const propertyDecorators: string[] = []; 384 for (let i = 0; i < decorators.length; i++) { 385 const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); 386 const includeWatchAndRequire: boolean = 387 [COMPONENT_WATCH_DECORATOR, COMPONENT_REQUIRE_DECORATOR].includes(decoratorName); 388 if (!includeWatchAndRequire) { 389 PropMapManager.register(name.escapedText.toString(), decoratorName); 390 } 391 checkDecoratorIsDuplicated(decoratorName, decorators[i], log); 392 if (checkDecoratorIsForbidden(node, includeWatchAndRequire, decoratorName, name, log)) { 393 return; 394 } 395 checkDecoratorIsIllegalInEntry(parentName, decoratorName, name, log); 396 if (checkDecoratorIsIllegalInPropertyInit(node, decoratorName, name, log)) { 397 return; 398 } 399 checkDecoratorHasIllegalQuestionToken(node, decoratorName, name, log); 400 if (!isSimpleType(node.type, program) && 401 decoratorName !== COMPONENT_BUILDERPARAM_DECORATOR) { 402 stateObjectCollection.add(name.escapedText.toString()); 403 } 404 if (decoratorName === COMPONENT_WATCH_DECORATOR && 405 validateWatchDecorator(name, decorators, log)) { 406 processWatch(node, decorators[i], watchMap, log); 407 } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 408 propertyDecorators.push(decoratorName); 409 if (decoratorName !== COMPONENT_REQUIRE_DECORATOR) { 410 processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context, 411 hasPreview, interfaceNode); 412 } 413 } 414 } 415 validatePropertyDecorator(propertyDecorators, name, log); 416} 417 418function checkDecoratorHasIllegalQuestionToken( 419 node: ts.PropertyDeclaration, decoratorName: string, name: ts.Identifier, log: LogInfo[]): void { 420 if (node.questionToken && mandatoryToInitViaParamDecorators.has(decoratorName) && 421 !(decoratorName === COMPONENT_PROP_DECORATOR && node.initializer)) { 422 validateHasIllegalQuestionToken(name, decoratorName, log); 423 } 424} 425 426function checkDecoratorIsIllegalInPropertyInit( 427 node: ts.PropertyDeclaration, decoratorName: string, name: ts.Identifier, log: LogInfo[]): boolean { 428 if (node.initializer && forbiddenSpecifyDefaultValueDecorators.has(decoratorName)) { 429 validatePropertyDefaultValue(name, decoratorName, log); 430 return true; 431 } else if (!node.initializer && !isRequireCanReleaseMandatoryDecorators(node, decoratorName) && 432 mandatorySpecifyDefaultValueDecorators.has(decoratorName)) { 433 validatePropertyNonDefaultValue(name, decoratorName, log); 434 return true; 435 } 436 return false; 437} 438 439function checkDecoratorIsIllegalInEntry( 440 parentName: ts.Identifier, decoratorName: string, name: ts.Identifier, log: LogInfo[]): void { 441 if (parentName.getText() === componentCollection.entryComponent && 442 mandatoryToInitViaParamDecorators.has(decoratorName)) { 443 validateHasIllegalDecoratorInEntry(parentName, name, decoratorName, log); 444 } 445} 446 447function checkDecoratorIsForbidden( 448 node: ts.PropertyDeclaration, includeWatchAndRequire: boolean, decoratorName: string, 449 name: ts.Identifier, log: LogInfo[]): boolean { 450 if (!includeWatchAndRequire && ts.isTypeReferenceNode(node.type) && isForbiddenUseStateType(node.type)) { 451 validateForbiddenUseStateType(name, decoratorName, node.type.typeName.getText(), log); 452 return true; 453 } 454 return false; 455} 456 457function checkDecoratorIsDuplicated(decoratorName: string, decorator: ts.Decorator, log: LogInfo[]): void { 458 if (BUILDIN_STYLE_NAMES.has(decoratorName.replace('@', ''))) { 459 validateDuplicateDecorator(decorator, log); 460 } 461} 462 463function isRequireCanReleaseMandatoryDecorators(node: ts.PropertyDeclaration, decoratorName: string): boolean { 464 if (decoratorName === COMPONENT_REQUIRE_DECORATOR) { 465 return true; 466 } 467 468 const decoratorIsNotMandatory: boolean = ts.getAllDecorators(node).find( 469 (decorator: ts.Decorator) => decorator.getText() === COMPONENT_REQUIRE_DECORATOR) && 470 requireCanReleaseMandatoryDecorators.has(decoratorName); 471 return decoratorIsNotMandatory; 472} 473 474function validatePropertyDecorator(propertyDecorators: string[], name: ts.Identifier, 475 log: LogInfo[]): void { 476 if (propertyDecorators.length > 1 && !validateRequireDecorator(propertyDecorators)) { 477 validateMultiDecorators(name, log); 478 } 479} 480 481const DECORATOR_LENGTH: number = 2; 482const SUPPORT_REQUIRE_DECORATOR: string[] = [COMPONENT_PROP_DECORATOR, 483 COMPONENT_BUILDERPARAM_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR, 484 COMPONENT_WATCH_DECORATOR 485]; 486 487function validateRequireDecorator(propertyDecorators: string[]): boolean { 488 const isSupportRequire: boolean = propertyDecorators.some((item: string) => { 489 return SUPPORT_REQUIRE_DECORATOR.includes(item); 490 }); 491 return propertyDecorators.length === DECORATOR_LENGTH && 492 propertyDecorators.includes(COMPONENT_REQUIRE_DECORATOR) && isSupportRequire; 493} 494 495function processStateDecorators(node: ts.PropertyDeclaration, decorator: string, 496 updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, log: LogInfo[], 497 program: ts.Program, context: ts.TransformationContext, hasPreview:boolean, 498 interfaceNode: ts.InterfaceDeclaration): void { 499 const name: ts.Identifier = node.name as ts.Identifier; 500 updateResult.setProperity(undefined); 501 const updateState: ts.Statement[] = []; 502 const variableInitStatement: ts.Statement = 503 createVariableInitStatement(node, decorator, log, program, context, hasPreview, interfaceNode); 504 if (variableInitStatement) { 505 updateState.push(variableInitStatement); 506 } 507 addAddProvidedVar(node, name, decorator, updateState); 508 updateResult.setCtor(updateConstructor(ctorNode, [], [...updateState], [], false)); 509 if (decorator !== COMPONENT_BUILDERPARAM_DECORATOR) { 510 updateResult.setVariableGet(createGetAccessor(name, CREATE_GET_METHOD)); 511 updateResult.setDeleteParams(true); 512 } 513 if (!immutableDecorators.has(decorator)) { 514 updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD, node.type)); 515 } 516 if (setUpdateParamsDecorators.has(decorator)) { 517 updateResult.setUpdateParams(createUpdateParams(name, decorator, node)); 518 } 519 if (projectConfig.optLazyForEach) { 520 setStateVarsDecorators.add(COMPONENT_STATE_DECORATOR); 521 } 522 if (setStateVarsDecorators.has(decorator)) { 523 updateResult.setStateVarsParams(createStateVarsParams(name, decorator)); 524 } 525 if (partialUpdateConfig.partialUpdateMode && BASICDECORATORS.has(decorator)) { 526 const variableWithUnderLink: string = '__' + name.escapedText.toString(); 527 updateResult.setDecoratorName(decorator); 528 updateResult.setPurgeVariableDepStatement(createPurgeVariableDepStatement(variableWithUnderLink)); 529 } 530} 531 532function createPurgeVariableDepStatement(variableWithUnderLink: string): ts.Statement { 533 return ts.factory.createExpressionStatement( 534 ts.factory.createCallExpression( 535 ts.factory.createPropertyAccessExpression( 536 ts.factory.createPropertyAccessExpression( 537 ts.factory.createThis(), 538 ts.factory.createIdentifier(variableWithUnderLink) 539 ), 540 ts.factory.createIdentifier(PURGEDEPENDENCYONELMTID) 541 ), 542 undefined, 543 [ts.factory.createIdentifier(RMELMTID)] 544 ) 545 ); 546} 547 548function processWatch(node: ts.PropertyDeclaration, decorator: ts.Decorator, 549 watchMap: Map<string, ts.Node>, log: LogInfo[]): void { 550 if (node.name) { 551 const propertyName: string = node.name.getText(); 552 if (decorator.expression && ts.isCallExpression(decorator.expression) && 553 decorator.expression.arguments && decorator.expression.arguments.length === 1) { 554 const currentClassMethod: Set<string> = getClassMethod(node); 555 const argument: ts.Node = decorator.expression.arguments[0]; 556 if (ts.isStringLiteral(argument)) { 557 if (currentClassMethod && currentClassMethod.has(argument.text)) { 558 watchMap.set(propertyName, argument); 559 } else { 560 log.push({ 561 type: LogType.ERROR, 562 message: `'@Watch' cannot be used with ${argument.getText()}.` + 563 ` Apply it only to parameters that correspond to existing methods.`, 564 pos: argument.getStart(), 565 code: '10905301' 566 }); 567 } 568 } else if (ts.isIdentifier(decorator.expression.arguments[0])) { 569 const content: string = decorator.expression.arguments[0].getText(); 570 const propertyNode: ts.PropertyAccessExpression = createPropertyAccessExpressionWithThis(content); 571 watchMap.set(propertyName, propertyNode); 572 decoratorParamSet.add(content); 573 validateWatchParam(LogType.WARN, argument.getStart(), log, argument.getText()); 574 } else if (ts.isPropertyAccessExpression(decorator.expression.arguments[0])) { 575 watchMap.set(propertyName, decorator.expression.arguments[0]); 576 validateWatchParam(LogType.WARN, argument.getStart(), log, argument.getText()); 577 } else { 578 validateWatchParam(LogType.ERROR, argument.getStart(), log, argument.getText()); 579 } 580 } 581 } 582} 583 584function getClassMethod(node: ts.PropertyDeclaration): Set<string> { 585 const sourceFile: ts.SourceFile = node.getSourceFile(); 586 const filePath: string = sourceFile ? sourceFile.fileName : undefined; 587 if (filePath && classMethodCollection.get(filePath)) { 588 return classMethodCollection.get(filePath).get(node.parent.name.getText()); 589 } 590 return new Set(); 591} 592 593function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: string, 594 log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean, 595 interfaceNode: ts.InterfaceDeclaration): ts.Statement { 596 const name: ts.Identifier = node.name as ts.Identifier; 597 let type: ts.TypeNode; 598 let updateState: ts.ExpressionStatement; 599 if (node.type) { 600 type = node.type; 601 } 602 switch (decorator) { 603 case COMPONENT_NON_DECORATOR: 604 updateState = updateNormalProperty(node, name, log, context); 605 break; 606 case COMPONENT_STATE_DECORATOR: 607 case COMPONENT_PROVIDE_DECORATOR: 608 updateState = !partialUpdateConfig.partialUpdateMode ? 609 updateObservedProperty(node, name, type, program) : updateObservedPropertyPU(node, name, type, program); 610 break; 611 case COMPONENT_LINK_DECORATOR: 612 wrongDecoratorInPreview(node, COMPONENT_LINK_DECORATOR, hasPreview, log); 613 updateState = !partialUpdateConfig.partialUpdateMode ? 614 updateSynchedPropertyTwoWay(name, type, program) : updateSynchedPropertyTwoWayPU(name, type, program); 615 break; 616 case COMPONENT_PROP_DECORATOR: 617 wrongDecoratorInPreview(node, COMPONENT_PROP_DECORATOR, hasPreview, log); 618 updateState = !partialUpdateConfig.partialUpdateMode ? 619 updateSynchedPropertyOneWay(name, type, decorator, log, program) : 620 updateSynchedPropertyOneWayPU(name, type, decorator, log, program); 621 break; 622 case COMPONENT_STORAGE_PROP_DECORATOR: 623 case COMPONENT_STORAGE_LINK_DECORATOR: 624 updateState = updateStoragePropAndLinkProperty(node, name, decorator); 625 break; 626 case COMPONENT_OBJECT_LINK_DECORATOR: 627 updateState = !partialUpdateConfig.partialUpdateMode ? 628 updateSynchedPropertyNesedObject(name, type, decorator, log) : 629 updateSynchedPropertyNesedObjectPU(name, type, decorator, log); 630 break; 631 case COMPONENT_CONSUME_DECORATOR: 632 wrongDecoratorInPreview(node, COMPONENT_CONSUME_DECORATOR, hasPreview, log); 633 updateState = updateConsumeProperty(node, name); 634 break; 635 case COMPONENT_BUILDERPARAM_DECORATOR: 636 updateState = updateBuilderParamProperty(node, name, log); 637 } 638 const members = interfaceNode.members; 639 members.push(ts.factory.createPropertySignature(undefined, name, 640 ts.factory.createToken(ts.SyntaxKind.QuestionToken), type)); 641 interfaceNode = ts.factory.updateInterfaceDeclaration(interfaceNode, 642 ts.getModifiers(interfaceNode), interfaceNode.name, interfaceNode.typeParameters, 643 interfaceNode.heritageClauses, members); 644 return updateState; 645} 646 647function wrongDecoratorInPreview(node: ts.PropertyDeclaration, decorator: string, 648 hasPreview: boolean, log: LogInfo[]): void { 649 if (hasPreview && projectConfig.isPreview) { 650 log.push({ 651 type: LogType.WARN, 652 message: `The variable with '${decorator}' in component with '@Preview' may ` + 653 `cause error in component preview mode`, 654 pos: node.getStart() 655 }); 656 } 657} 658 659function createUpdateParams(name: ts.Identifier, decorator: string, 660 localInitializationNode: ts.PropertyDeclaration = undefined): ts.Statement { 661 let updateParamsNode: ts.Statement; 662 switch (decorator) { 663 case COMPONENT_NON_DECORATOR: 664 case COMPONENT_STATE_DECORATOR: 665 case COMPONENT_PROVIDE_DECORATOR: 666 case COMPONENT_CUSTOM_DECORATOR: 667 updateParamsNode = createUpdateParamsWithIf(name); 668 break; 669 case COMPONENT_PROP_DECORATOR: 670 if (!partialUpdateConfig.partialUpdateMode) { 671 updateParamsNode = createUpdateParamsWithoutIf(name); 672 } else { 673 if (localInitializationNode && localInitializationNode.initializer) { 674 updateParamsNode = createUpdateParamsWithIf(name, true, 675 localInitializationNode.initializer); 676 } 677 } 678 break; 679 case COMPONENT_BUILDERPARAM_DECORATOR: 680 updateParamsNode = createUpdateParamsWithIf(name); 681 break; 682 case COMPONENT_OBJECT_LINK_DECORATOR: 683 updateParamsNode = createUpdateParamsWithSet(name); 684 break; 685 } 686 return updateParamsNode; 687} 688 689function createStateVarsParams(name: ts.Identifier, decorator: string): ts.Statement { 690 let updateParamsNode: ts.Statement; 691 switch (decorator) { 692 case COMPONENT_OBJECT_LINK_DECORATOR: 693 updateParamsNode = createUpdateParamsWithSet(name); 694 break; 695 case COMPONENT_STATE_DECORATOR: 696 updateParamsNode = createUpdateParamsForState(name); 697 break; 698 } 699 return updateParamsNode; 700} 701 702function createUpdateParamsWithIf(name: ts.Identifier, isProp: boolean = false, 703 initializeNode: ts.Expression = undefined): ts.IfStatement { 704 return ts.factory.createIfStatement(ts.factory.createBinaryExpression( 705 ts.factory.createPropertyAccessExpression( 706 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), 707 ts.factory.createIdentifier(name.escapedText.toString())), 708 isProp ? ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken) : 709 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), 710 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)), 711 isProp ? ts.factory.createBlock([createUpdateParamsWithSet(name, true, initializeNode)]) : 712 ts.factory.createBlock([ 713 createUpdateParamsWithoutIf(name)], true), undefined); 714} 715 716function createUpdateParamsForState(name: ts.Identifier): ts.IfStatement { 717 return ts.factory.createIfStatement(createPropertyAccessExpressionWithParams(name.getText()), 718 ts.factory.createBlock([createUpdateParamsWithSet(name)])); 719} 720 721function createUpdateParamsWithoutIf(name: ts.Identifier): ts.ExpressionStatement { 722 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 723 createPropertyAccessExpressionWithThis(name.getText()), 724 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 725 createPropertyAccessExpressionWithParams(name.getText()))); 726} 727 728function createUpdateParamsWithSet(name: ts.Identifier, hasElse: boolean = false, 729 initializeNode: ts.Expression = undefined): ts.ExpressionStatement { 730 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 731 ts.factory.createPropertyAccessExpression(createPropertyAccessExpressionWithThis(`__${name.getText()}`), 732 ts.factory.createIdentifier(CREATE_SET_METHOD)), undefined, 733 [hasElse ? initializeNode : createPropertyAccessExpressionWithParams(name.getText())])); 734} 735 736function updateNormalProperty(node: ts.PropertyDeclaration, name: ts.Identifier, 737 log: LogInfo[], context: ts.TransformationContext): ts.ExpressionStatement { 738 const init: ts.Expression = 739 ts.visitNode(node.initializer, visitDialogController); 740 function visitDialogController(node: ts.Node): ts.Node { 741 if (isProperty(node)) { 742 node = createReference(node as ts.PropertyAssignment, log); 743 } 744 return ts.visitEachChild(node, visitDialogController, context); 745 } 746 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 747 createPropertyAccessExpressionWithThis(name.getText()), 748 ts.factory.createToken(ts.SyntaxKind.EqualsToken), init || 749 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED))); 750} 751 752function updateObservedProperty(item: ts.PropertyDeclaration, name: ts.Identifier, 753 type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement { 754 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 755 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 756 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression( 757 ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE : 758 OBSERVED_PROPERTY_OBJECT), undefined, [item.initializer, ts.factory.createThis(), 759 ts.factory.createStringLiteral(name.escapedText.toString())]))); 760} 761 762function updateSynchedPropertyTwoWay(nameIdentifier: ts.Identifier, type: ts.TypeNode, 763 program: ts.Program): ts.ExpressionStatement { 764 const name: string = nameIdentifier.escapedText.toString(); 765 const functionName: string = isSimpleType(type, program) ? 766 SYNCHED_PROPERTY_SIMPLE_TWO_WAY : SYNCHED_PROPERTY_OBJECT_TWO_WAY; 767 return createInitExpressionStatementForDecorator(name, functionName, 768 createPropertyAccessExpressionWithParams(name)); 769} 770 771function updateSynchedPropertyOneWay(nameIdentifier: ts.Identifier, type: ts.TypeNode, 772 decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement { 773 const name: string = nameIdentifier.escapedText.toString(); 774 if (isSimpleType(type, program)) { 775 return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 776 createPropertyAccessExpressionWithParams(name)); 777 } else { 778 validateNonSimpleType(nameIdentifier, decoractor, log); 779 return undefined; 780 } 781} 782 783export function findDecoratorIndex(decorators: readonly ts.Decorator[], nameList: string[]): number { 784 return decorators.findIndex((item: ts.Decorator) => { 785 return nameList.includes(item.getText().replace(/\(.*\)$/, '').trim()); 786 }); 787} 788 789function updateStoragePropAndLinkProperty(node: ts.PropertyDeclaration, name: ts.Identifier, 790 decorator: string): ts.ExpressionStatement { 791 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 792 if (isSingleKey(node)) { 793 let setFuncName: string; 794 let storageFuncName: string; 795 const index: number = findDecoratorIndex(decorators, [decorator]); 796 const storageValue: ts.Expression[] = [ 797 decorators[index].expression.arguments[0], 798 node.initializer, 799 ts.factory.createThis(), 800 ts.factory.createStringLiteral(name.getText()) 801 ]; 802 if (!partialUpdateConfig.partialUpdateMode) { 803 setFuncName = decorator === COMPONENT_STORAGE_PROP_DECORATOR ? 804 APP_STORAGE_SET_AND_PROP : APP_STORAGE_SET_AND_LINK; 805 storageFuncName = APP_STORAGE; 806 } else { 807 setFuncName = decorator === COMPONENT_STORAGE_PROP_DECORATOR ? 808 CREATE_STORAGE_PROP : CREATE_STORAGE_LINK; 809 storageFuncName = THIS; 810 storageValue.splice(2, 1); 811 } 812 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 813 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 814 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression( 815 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(storageFuncName), 816 ts.factory.createIdentifier(setFuncName)), undefined, storageValue))); 817 } 818 return undefined; 819} 820 821function getDecoratorKey(node: ts.PropertyDeclaration, isProvided: boolean = false): [string, boolean, ts.Node, boolean] { 822 let key: string; 823 let isStringKey: boolean = false; 824 let isProvidedParamObj: boolean = false; 825 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 826 const index: number = findDecoratorIndex(decorators, [COMPONENT_PROVIDE_DECORATOR, COMPONENT_CONSUME_DECORATOR]); 827 // @ts-ignore 828 let keyNameNode: ts.Node = decorators[index].expression.arguments[0]; 829 if (ts.isIdentifier(keyNameNode)) { 830 key = keyNameNode.getText(); 831 decoratorParamSet.add(key); 832 } else if (ts.isStringLiteral(keyNameNode)) { 833 key = keyNameNode.text; 834 isStringKey = true; 835 } else if (isProvided && ts.isObjectLiteralExpression(keyNameNode) && keyNameNode.properties.length === 1 && 836 ts.isPropertyAssignment(keyNameNode.properties[0]) && keyNameNode.properties[0].initializer) { 837 key = keyNameNode.properties[0].initializer.text; 838 keyNameNode = keyNameNode.properties[0].initializer; 839 isProvidedParamObj = true; 840 } else { 841 key = keyNameNode.getText(); 842 } 843 return [key, isStringKey, keyNameNode, isProvidedParamObj]; 844} 845 846function updateSynchedPropertyNesedObject(nameIdentifier: ts.Identifier, 847 type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement { 848 if (isObservedClassType(type)) { 849 return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT, 850 createPropertyAccessExpressionWithParams(nameIdentifier.getText())); 851 } else { 852 validateNonObservedClassType(nameIdentifier, decoractor, log); 853 } 854 return undefined; 855} 856 857function updateConsumeProperty(node: ts.PropertyDeclaration, 858 nameIdentifier: ts.Identifier): ts.ExpressionStatement { 859 const name: string = nameIdentifier.getText(); 860 let propertyOrAliasName: string; 861 const propertyAndStringKey: [string?, boolean?, ts.Node?, boolean?] = []; 862 if (isSingleKey(node, true)) { 863 propertyAndStringKey.push(...getDecoratorKey(node)); 864 propertyOrAliasName = propertyAndStringKey[0]; 865 } else { 866 propertyOrAliasName = name; 867 } 868 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 869 createPropertyAccessExpressionWithThis(`__${name}`), 870 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression( 871 createPropertyAccessExpressionWithThis(INITIALIZE_CONSUME_FUNCTION), undefined, [ 872 propertyAndStringKey.length === 0 ? ts.factory.createStringLiteral(propertyOrAliasName) : 873 propertyAndStringKey.length === 4 && propertyAndStringKey[2] as ts.Expression, ts.factory.createStringLiteral(name)]))); 874} 875 876function updateBuilderParamProperty(node: ts.PropertyDeclaration, 877 nameIdentifier: ts.Identifier, log: LogInfo[]): ts.ExpressionStatement { 878 const name: string = nameIdentifier.getText(); 879 if (judgeBuilderParamAssignedByBuilder(node)) { 880 log.push({ 881 type: LogType.ERROR, 882 message: `'@BuilderParam' property can only initialized by '@Builder' function or '@LocalBuilder' method in struct.`, 883 pos: node.getStart(), 884 code: '10905101' 885 }); 886 } 887 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 888 createPropertyAccessExpressionWithThis(name), ts.factory.createToken(ts.SyntaxKind.EqualsToken), 889 node.initializer || ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 890 )); 891} 892 893export function judgeBuilderParamAssignedByBuilder(node: ts.PropertyDeclaration): boolean { 894 return node.initializer && !(node.initializer && (ts.isIdentifier(node.initializer) && 895 CUSTOM_BUILDER_METHOD.has(node.initializer.escapedText.toString()) || 896 ts.isPropertyAccessExpression(node.initializer) && node.initializer.name && 897 ts.isIdentifier(node.initializer.name) && 898 (CUSTOM_BUILDER_METHOD.has(node.initializer.name.escapedText.toString()) || 899 INNER_CUSTOM_LOCALBUILDER_METHOD.has(node.initializer.name.escapedText.toString())) || 900 isWrappedBuilder(node.initializer as ts.PropertyAccessExpression))); 901} 902 903export function createViewCreate(node: ts.NewExpression | ts.Identifier): ts.CallExpression { 904 if (partialUpdateConfig.partialUpdateMode) { 905 return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), 906 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node])); 907 } 908 return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME), 909 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node])); 910} 911 912export function createCustomComponentNewExpression(node: ts.CallExpression, name: string, 913 isBuilder: boolean = false, isGlobalBuilder: boolean = false, 914 isCutomDialog: boolean = false): ts.NewExpression { 915 const newNode: ts.NewExpression = ts.factory.createNewExpression(node.expression, 916 node.typeArguments, node.arguments.length ? node.arguments : []); 917 return addCustomComponentId(newNode, node, name, isBuilder, isGlobalBuilder, isCutomDialog); 918} 919 920function addCustomComponentId(node: ts.NewExpression, oldNode: ts.CallExpression, componentName: string, 921 isBuilder: boolean = false, isGlobalBuilder: boolean = false, 922 isCutomDialog: boolean = false): ts.NewExpression { 923 const posOfNode = transformLog.sourceFile.getLineAndCharacterOfPosition(getRealNodePos(node)); 924 const line: number = posOfNode.line + 1; 925 const col: number = posOfNode.character + 1; 926 for (const item of componentCollection.customComponents) { 927 componentInfo.componentNames.add(item); 928 } 929 componentInfo.componentNames.forEach((name: string) => { 930 let argumentsArray: ts.Expression[]; 931 if (node.arguments && node.arguments.length) { 932 argumentsArray = Array.from(node.arguments); 933 if (partialUpdateConfig.partialUpdateMode && node.arguments.length === 1) { 934 manageLocalStorageComponents(oldNode, argumentsArray); 935 } 936 } 937 if (componentName === name) { 938 if (!argumentsArray) { 939 argumentsArray = [ts.factory.createObjectLiteralExpression([], true)]; 940 if (partialUpdateConfig.partialUpdateMode) { 941 argumentsArray.push(ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)); 942 } 943 } 944 if (!partialUpdateConfig.partialUpdateMode) { 945 ++componentInfo.id; 946 argumentsArray.unshift(isBuilder ? ts.factory.createBinaryExpression( 947 ts.factory.createStringLiteral(path.basename(transformLog.sourceFile.fileName, EXTNAME_ETS) + '_'), 948 ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createIdentifier(_GENERATE_ID)) : 949 ts.factory.createStringLiteral(componentInfo.id.toString()), 950 isBuilder ? parentConditionalExpression() : ts.factory.createThis()); 951 } else { 952 argumentsArray.unshift((isGlobalBuilder || storedFileInfo.processLocalBuilder) ? parentConditionalExpression() : ts.factory.createThis()); 953 argumentsArray.push(isCutomDialog ? ts.factory.createPrefixUnaryExpression( 954 ts.SyntaxKind.MinusToken, 955 ts.factory.createNumericLiteral('1')) : ts.factory.createIdentifier(ELMTID), 956 createArrowFunctionNode(), componentParamRowAndColumn(line, col)); 957 } 958 node = 959 ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray); 960 } else if (argumentsArray) { 961 node = 962 ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray); 963 } 964 }); 965 return node; 966} 967 968function manageLocalStorageComponents(node: ts.CallExpression, argumentsArray: ts.Expression[]): void { 969 if (isLocalStorageParameter(node)) { 970 argumentsArray.unshift(ts.factory.createObjectLiteralExpression([], false)); 971 } else { 972 argumentsArray.push(ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)); 973 } 974} 975 976export function isLocalStorageParameter(node: ts.CallExpression): boolean { 977 return globalProgram.checker && globalProgram.checker.getResolvedSignature && 978 globalProgram.checker.getResolvedSignature(node) && 979 globalProgram.checker.getResolvedSignature(node).parameters && 980 globalProgram.checker.getResolvedSignature(node).parameters.length === 1 && 981 globalProgram.checker.getResolvedSignature(node).parameters[0].escapedName === '##storage'; 982} 983 984function componentParamRowAndColumn(line: number, col: number): ts.ObjectLiteralExpression { 985 return ts.factory.createObjectLiteralExpression( 986 [ 987 ts.factory.createPropertyAssignment( 988 ts.factory.createIdentifier('page'), 989 ts.factory.createStringLiteral(path.relative(process.cwd(), resourceFileName).replace(/\\/g, '/')) 990 ), 991 ts.factory.createPropertyAssignment( 992 ts.factory.createIdentifier('line'), 993 ts.factory.createNumericLiteral(line) 994 ), 995 ts.factory.createPropertyAssignment( 996 ts.factory.createIdentifier('col'), 997 ts.factory.createNumericLiteral(col) 998 ) 999 ], 1000 false 1001 ); 1002} 1003 1004function createArrowFunctionNode(): ts.ArrowFunction { 1005 return ts.factory.createArrowFunction( 1006 undefined, 1007 undefined, 1008 [], 1009 undefined, 1010 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 1011 ts.factory.createBlock( 1012 [], 1013 false 1014 ) 1015 ); 1016} 1017 1018function createInitExpressionStatementForDecorator(propertyName: string, functionName: string, 1019 parameterNode: ts.Expression): ts.ExpressionStatement { 1020 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 1021 createPropertyAccessExpressionWithThis(`__${propertyName}`), 1022 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression( 1023 ts.factory.createIdentifier(functionName), undefined, [parameterNode, ts.factory.createThis(), 1024 ts.factory.createStringLiteral(propertyName)]))); 1025} 1026 1027function createPropertyAccessExpressionWithParams(propertyName: string): ts.PropertyAccessExpression { 1028 return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), 1029 ts.factory.createIdentifier(propertyName)); 1030} 1031 1032function createPropertyAccessExpressionWithThis(propertyName: string): ts.PropertyAccessExpression { 1033 return ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 1034 ts.factory.createIdentifier(propertyName)); 1035} 1036 1037function addAddProvidedVar(node: ts.PropertyDeclaration, name: ts.Identifier, 1038 decoratorName: string, updateState: ts.Statement[]): void { 1039 if (decoratorName === COMPONENT_PROVIDE_DECORATOR) { 1040 let parameterName: string; 1041 const parameterNameAndStringKey: [string?, boolean?, ts.Node?, boolean?] = []; 1042 if (isSingleKey(node, true)) { 1043 parameterNameAndStringKey.push(...getDecoratorKey(node, true)); 1044 parameterName = parameterNameAndStringKey[0]; 1045 updateState.push(createAddProvidedVar(parameterName, name, parameterNameAndStringKey[1], parameterNameAndStringKey[2], 1046 parameterNameAndStringKey[3])); 1047 } 1048 if (parameterName !== name.getText()) { 1049 updateState.push(createAddProvidedVar(name.getText(), name, true, undefined, parameterNameAndStringKey[3])); 1050 } 1051 } 1052} 1053 1054function createAddProvidedVar(propertyOrAliasName: string, 1055 name: ts.Identifier, isString: boolean, decoratorKeyNode: ts.Node, 1056 isProvidedParamObj: boolean): ts.ExpressionStatement { 1057 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1058 createPropertyAccessExpressionWithThis(ADD_PROVIDED_VAR), undefined, [ 1059 isString ? ts.factory.createStringLiteral(propertyOrAliasName) : decoratorKeyNode as ts.Expression, 1060 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 1061 isProvidedParamObj ? ts.factory.createIdentifier(TRUE) : ts.factory.createIdentifier(FALSE)])); 1062} 1063 1064function createGetAccessor(item: ts.Identifier, express: string): ts.GetAccessorDeclaration { 1065 const getAccessorStatement: ts.GetAccessorDeclaration = 1066 ts.factory.createGetAccessorDeclaration(undefined, item, [], undefined, 1067 ts.factory.createBlock([ts.factory.createReturnStatement( 1068 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 1069 createPropertyAccessExpressionWithThis(`__${item.getText()}`), 1070 ts.factory.createIdentifier(express)), undefined, []))], true)); 1071 return getAccessorStatement; 1072} 1073 1074function createSetAccessor(item: ts.Identifier, express: string, type: ts.TypeNode): 1075 ts.SetAccessorDeclaration { 1076 const setAccessorStatement: ts.SetAccessorDeclaration = 1077 ts.factory.createSetAccessorDeclaration(undefined, item, 1078 [ts.factory.createParameterDeclaration(undefined, undefined, 1079 ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, type, 1080 undefined)], ts.factory.createBlock([ts.factory.createExpressionStatement( 1081 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 1082 createPropertyAccessExpressionWithThis(`__${item.getText()}`), 1083 ts.factory.createIdentifier(express)), undefined, 1084 [ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER)]))], true)); 1085 return setAccessorStatement; 1086} 1087 1088function isForbiddenUseStateType(typeNode: ts.TypeNode): boolean { 1089 if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) && 1090 forbiddenUseStateType.has(typeNode.typeName.getText())) { 1091 return true; 1092 } 1093 return false; 1094} 1095 1096export function isSimpleType(typeNode: ts.TypeNode, program: ts.Program, log?: LogInfo[]): boolean { 1097 typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); 1098 let checker: ts.TypeChecker; 1099 if (globalProgram.program) { 1100 checker = globalProgram.program.getTypeChecker(); 1101 } else if (globalProgram.watchProgram) { 1102 checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker(); 1103 } else if (program) { 1104 checker = program.getTypeChecker(); 1105 } 1106 return getDeclarationType(typeNode, checker, log); 1107} 1108 1109function getDeclarationType(typeNode: ts.TypeNode, checker: ts.TypeChecker, log: LogInfo[]): boolean { 1110 if (simpleTypes.has(typeNode.kind)) { 1111 return true; 1112 } 1113 if (ts.isTypeReferenceNode(typeNode) && typeNode.typeName && ts.isIdentifier(typeNode.typeName) && 1114 enumCollection.has(typeNode.typeName.escapedText.toString())) { 1115 return true; 1116 } 1117 if (checker) { 1118 const type: ts.Type = checker.getTypeFromTypeNode(typeNode); 1119 /* Enum */ 1120 if (type.flags & (32 | 1024)) { 1121 return true; 1122 } 1123 // @ts-ignore 1124 if (type.types && type.types.length) { 1125 // @ts-ignore 1126 const types = type.types; 1127 let referenceType: boolean = false; 1128 for (let i = 0; i < types.length; i++) { 1129 if (!isBasicType(types[i].flags)) { 1130 referenceType = true; 1131 } 1132 } 1133 if (!referenceType) { 1134 return true; 1135 } 1136 } 1137 } 1138 return false; 1139} 1140 1141export function isBasicType(flags: number): boolean { 1142 if (flags & (4 | /* String */ 8 | /* Number */ 16 | /* Boolean */ 32 | /* Enum */ 64 | /* BigInt */ 1143 128 | /* StringLiteral */ 256 | /* NumberLiteral */ 512 /* BooleanLiteral */| 1024 /* EnumLiteral */| 1144 2048 /* BigIntLiteral */)) { 1145 return true; 1146 } 1147 return false; 1148} 1149 1150function isObservedClassType(type: ts.TypeNode): boolean { 1151 if (judgmentTypedeclaration(type) && observedClassCollection.has(type.typeName.escapedText.toString())) { 1152 return true; 1153 } else if (ts.isUnionTypeNode(type) && type.types) { 1154 const types: ts.NodeArray<ts.TypeNode> = type.types; 1155 for (let i = 0; i < types.length; i++) { 1156 if (judgmentTypedeclaration(types[i]) && !observedClassCollection.has(types[i].typeName.escapedText.toString())) { 1157 return false; 1158 } 1159 } 1160 return true; 1161 } 1162 return false; 1163} 1164 1165function judgmentTypedeclaration(type: ts.TypeNode): boolean { 1166 return ts.isTypeReferenceNode(type) && type.typeName && ts.isIdentifier(type.typeName); 1167} 1168 1169export function isSingleKey(node: ts.PropertyDeclaration, isOptionalKey: boolean = false): boolean { 1170 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 1171 const optionalKeyDecorator: Set<string> = new Set(['Provide', 'Consume']); 1172 return decorators.some((item: ts.Decorator) => { 1173 return ts.isCallExpression(item.expression) && 1174 item.expression.arguments && 1175 item.expression.arguments.length === 1 && 1176 ts.isIdentifier(item.expression.expression) && 1177 (!isOptionalKey || optionalKeyDecorator.has(item.expression.expression.escapedText.toString())); 1178 }); 1179} 1180 1181function validateMultiDecorators(name: ts.Identifier, log: LogInfo[]): void { 1182 log.push({ 1183 type: LogType.ERROR, 1184 message: `The property '${name.escapedText.toString()}' cannot have multiple state management decorators.`, 1185 pos: name.getStart(), 1186 code: '10905302' 1187 }); 1188} 1189 1190function validatePropertyNonDefaultValue(propertyName: ts.Identifier, decorator: string, 1191 log: LogInfo[]): void { 1192 log.push({ 1193 type: LogType.ERROR, 1194 message: `The '${decorator}' property '${propertyName.getText()}' must be specified a default value.`, 1195 pos: propertyName.getStart(), 1196 code: '10905303' 1197 }); 1198} 1199 1200function validatePropertyDefaultValue(propertyName: ts.Identifier, decorator: string, 1201 log: LogInfo[]): void { 1202 log.push({ 1203 type: LogType.ERROR, 1204 message: `The '${decorator}' property cannot be specified a default value.`, 1205 pos: propertyName.getStart(), 1206 code: '10905304', 1207 solutions: ['Please initialize the rules according to the decorator.'] 1208 }); 1209} 1210 1211function validatePropertyNonType(propertyName: ts.Identifier, log: LogInfo[]): void { 1212 log.push({ 1213 type: LogType.ERROR, 1214 message: `The property '${propertyName.getText()}' must specify a type.`, 1215 pos: propertyName.getStart(), 1216 code: '10905305' 1217 }); 1218} 1219 1220function validateNonSimpleType(propertyName: ts.Identifier, decorator: string, 1221 log: LogInfo[]): void { 1222 log.push({ 1223 type: projectConfig.optLazyForEach ? LogType.WARN : LogType.ERROR, 1224 message: `The type of the ${decorator} property '${propertyName.getText()}' ` + 1225 `can only be string, number or boolean.`, 1226 pos: propertyName.getStart(), 1227 code: '10905306' 1228 }); 1229} 1230 1231function validateNonObservedClassType(propertyName: ts.Identifier, decorator: string, 1232 log: LogInfo[]): void { 1233 log.push({ 1234 type: projectConfig.optLazyForEach ? LogType.WARN : LogType.ERROR, 1235 message: `'@ObjectLink' cannot be used with this type.` + 1236 ` Apply it only to classes decorated by '@Observed' or initialized using the return value of 'makeV1Observed'.`, 1237 pos: propertyName.getStart(), 1238 code: '10905307' 1239 }); 1240 } 1241 1242function validateHasIllegalQuestionToken(propertyName: ts.Identifier, decorator: string, 1243 log: LogInfo[]): void { 1244 log.push({ 1245 type: LogType.WARN, 1246 message: `The ${decorator} property '${propertyName.getText()}' cannot be an optional parameter.`, 1247 pos: propertyName.getStart() 1248 }); 1249} 1250 1251function validateHasIllegalDecoratorInEntry(parentName: ts.Identifier, propertyName: ts.Identifier, 1252 decorator: string, log: LogInfo[]): void { 1253 log.push({ 1254 type: LogType.WARN, 1255 message: `The '@Entry' component '${parentName.getText()}' cannot have the ` + 1256 `'${decorator}' property '${propertyName.getText()}'.`, 1257 pos: propertyName.getStart() 1258 }); 1259} 1260 1261function validateForbiddenUseStateType(propertyName: ts.Identifier, decorator: string, type: string, 1262 log: LogInfo[]): void { 1263 log.push({ 1264 type: LogType.ERROR, 1265 message: `The '${decorator}' property '${propertyName.getText()}' cannot be a '${type}' object.`, 1266 pos: propertyName.getStart(), 1267 code: '10905308' 1268 }); 1269} 1270 1271function validateDuplicateDecorator(decorator: ts.Decorator, log: LogInfo[]): void { 1272 log.push({ 1273 type: LogType.ERROR, 1274 message: `The decorator '${decorator.getText()}' cannot have the same name as the built-in ` + 1275 `style attribute '${decorator.getText().replace('@', '')}'.`, 1276 pos: decorator.getStart(), 1277 code: '10905309' 1278 }); 1279} 1280 1281function validateWatchDecorator(propertyName: ts.Identifier, decorators: readonly ts.Decorator[], 1282 log: LogInfo[]): boolean { 1283 let isRegular: boolean = false; 1284 if (decorators.length === DECORATOR_LENGTH) { 1285 isRegular = decorators.some((item: ts.Decorator) => { 1286 return item.getText() === COMPONENT_REQUIRE_DECORATOR; 1287 }); 1288 } 1289 if (decorators.length === 1 || isRegular) { 1290 log.push({ 1291 type: LogType.ERROR, 1292 message: `Regular variable '${propertyName.escapedText.toString()}' can not be decorated with '@Watch'.`, 1293 pos: propertyName.getStart(), 1294 code: '10905310' 1295 }); 1296 return false; 1297 } 1298 return true; 1299} 1300 1301function validateWatchParam(type: LogType, pos: number, log: LogInfo[], paramName: string): void { 1302 log.push({ 1303 type: type, 1304 message: `'@Watch' cannot be used with '${paramName}'. Apply it only to 'string' parameters.`, 1305 pos, 1306 code: '10905311' 1307 }); 1308} 1309 1310function updateObservedPropertyPU(item: ts.PropertyDeclaration, name: ts.Identifier, 1311 type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement { 1312 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 1313 createPropertyAccessExpressionWithThis(`__${name.getText()}`), 1314 ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression( 1315 ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE_PU : 1316 OBSERVED_PROPERTY_OBJECT_PU), undefined, [ 1317 item.initializer ?? ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED), 1318 ts.factory.createThis(), 1319 ts.factory.createStringLiteral(name.escapedText.toString()) 1320 ] 1321 ) 1322 ) 1323 ); 1324} 1325 1326function updateSynchedPropertyTwoWayPU(nameIdentifier: ts.Identifier, type: ts.TypeNode, 1327 program: ts.Program): ts.ExpressionStatement { 1328 const name: string = nameIdentifier.escapedText.toString(); 1329 const functionName: string = isSimpleType(type, program) ? 1330 SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU : SYNCHED_PROPERTY_OBJECT_TWO_WAY_PU; 1331 return createInitExpressionStatementForDecorator(name, functionName, 1332 createPropertyAccessExpressionWithParams(name)); 1333} 1334 1335function updateSynchedPropertyOneWayPU(nameIdentifier: ts.Identifier, type: ts.TypeNode, 1336 decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement { 1337 const name: string = nameIdentifier.escapedText.toString(); 1338 if (isSimpleType(type, program, log)) { 1339 return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 1340 createPropertyAccessExpressionWithParams(name)); 1341 } else { 1342 return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_OBJECT_ONE_WAY_PU, 1343 createPropertyAccessExpressionWithParams(name)); 1344 } 1345} 1346 1347function updateSynchedPropertyNesedObjectPU(nameIdentifier: ts.Identifier, 1348 type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement { 1349 if (partialUpdateConfig.partialUpdateMode && projectConfig.compileMode === 'esmodule' && checkObjectLinkType(type)) { 1350 return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT_PU, 1351 createPropertyAccessExpressionWithParams(nameIdentifier.getText())); 1352 } else if ((projectConfig.compileMode !== 'esmodule' || !partialUpdateConfig.partialUpdateMode) && isObservedClassType(type)) { 1353 return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT_PU, 1354 createPropertyAccessExpressionWithParams(nameIdentifier.getText())); 1355 } else { 1356 validateNonObservedClassType(nameIdentifier, decoractor, log); 1357 return undefined; 1358 } 1359} 1360 1361function checkObjectLinkType(typeNode: ts.TypeNode): boolean { 1362 if (globalProgram.checker) { 1363 const type: ts.Type = globalProgram.checker.getTypeFromTypeNode(typeNode); 1364 if (typeNode.parent && ts.isPropertyDeclaration(typeNode.parent) && isObserved(type)) { 1365 return true; 1366 } 1367 // @ts-ignore 1368 if (type.types && type.types.length) { 1369 let observedNumber: number = 0; 1370 for (let i = 0; i < type.types.length; i++) { 1371 if (type.types[i].intrinsicName) { 1372 return false; 1373 } 1374 if (isObserved(type.types[i])) { 1375 observedNumber += 1; 1376 } 1377 } 1378 if (observedNumber === type.types.length) { 1379 return true; 1380 } else { 1381 return false; 1382 } 1383 } 1384 } 1385 return false; 1386} 1387 1388function validateCustomDecorator(decorators: readonly ts.Decorator[], log: LogInfo[]): boolean { 1389 let hasInnerDecorator: boolean = false; 1390 let hasCustomDecorator: boolean = false; 1391 let innerDecorator: ts.Decorator; 1392 for (let i = 0; i < decorators.length; i++) { 1393 const decorator: ts.Decorator = decorators[i]; 1394 const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim(); 1395 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 1396 hasInnerDecorator = true; 1397 innerDecorator = innerDecorator || decorator; 1398 } else { 1399 hasCustomDecorator = true; 1400 } 1401 } 1402 if (hasCustomDecorator && hasInnerDecorator) { 1403 log.push({ 1404 type: LogType.ERROR, 1405 message: `The inner decorator '${innerDecorator.getText()}' cannot be used together with custom decorator.`, 1406 pos: innerDecorator.getStart(), 1407 code: '10905312' 1408 }); 1409 } else if (!hasInnerDecorator) { 1410 return true; 1411 } 1412 return false; 1413} 1414 1415function validatePropDecorator(decorators: readonly ts.Decorator[]): boolean { 1416 for (let i = 0; i < decorators.length; i++) { 1417 const decorator: ts.Decorator = decorators[i]; 1418 const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim(); 1419 if (COMPONENT_PROP_DECORATOR === decoratorName) { 1420 return true; 1421 } 1422 } 1423 return false; 1424} 1425 1426function isObserved(type: ts.Type): boolean { 1427 if (type && type.getSymbol() && type.getSymbol().declarations) { 1428 return type.getSymbol().declarations.some((classDeclaration: ts.ClassDeclaration) => { 1429 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(classDeclaration); 1430 if (ts.isClassDeclaration(classDeclaration) && decorators) { 1431 return decorators.some((decorator: ts.Decorator) => { 1432 return ts.isIdentifier(decorator.expression) && decorator.expression.escapedText.toString() === OBSERVED; 1433 }); 1434 } 1435 return false; 1436 }); 1437 } 1438 return false; 1439} 1440 1441export function resetProcessComponentMember(): void { 1442 decoratorParamSet.clear(); 1443} 1444