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