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 16 import ts from 'typescript'; 17 18 import { 19 COMPONENT_STATE_DECORATOR, 20 COMPONENT_PROVIDE_DECORATOR, 21 COMPONENT_LINK_DECORATOR, 22 COMPONENT_PROP_DECORATOR, 23 COMPONENT_STORAGE_LINK_DECORATOR, 24 COMPONENT_STORAGE_PROP_DECORATOR, 25 COMPONENT_OBJECT_LINK_DECORATOR, 26 COMPONENT_CONSUME_DECORATOR, 27 SYNCHED_PROPERTY_NESED_OBJECT, 28 SYNCHED_PROPERTY_SIMPLE_TWO_WAY, 29 SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 30 OBSERVED_PROPERTY_OBJECT, 31 OBSERVED_PROPERTY_SIMPLE, 32 COMPONENT_BUILD_FUNCTION, 33 BASE_COMPONENT_NAME, 34 CREATE_CONSTRUCTOR_PARAMS, 35 COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, 36 COMPONENT_CONSTRUCTOR_INITIAL_PARAMS, 37 COMPONENT_CONSTRUCTOR_PURGE_VARIABLE_DEP, 38 COMPONENT_CONSTRUCTOR_DELETE_PARAMS, 39 COMPONENT_DECORATOR_PREVIEW, 40 CREATE_CONSTRUCTOR_SUBSCRIBER_MANAGER, 41 ABOUT_TO_BE_DELETE_FUNCTION_ID, 42 ABOUT_TO_BE_DELETE_FUNCTION_ID__, 43 CREATE_CONSTRUCTOR_GET_FUNCTION, 44 CREATE_CONSTRUCTOR_DELETE_FUNCTION, 45 FOREACH_OBSERVED_OBJECT, 46 FOREACH_GET_RAW_OBJECT, 47 COMPONENT_BUILDER_DECORATOR, 48 COMPONENT_TRANSITION_FUNCTION, 49 COMPONENT_CREATE_FUNCTION, 50 GEOMETRY_VIEW, 51 COMPONENT_STYLES_DECORATOR, 52 STYLES, 53 INTERFACE_NAME_SUFFIX, 54 OBSERVED_PROPERTY_ABSTRACT, 55 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 56 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, 57 COMPONENT_CONSTRUCTOR_LOCALSTORAGE, 58 COMPONENT_SET_AND_LINK, 59 COMPONENT_SET_AND_PROP, 60 COMPONENT_CONSTRUCTOR_UNDEFINED, 61 CUSTOM_COMPONENT, 62 COMPONENT_CONSTRUCTOR_PARENT, 63 NULL, 64 INNER_COMPONENT_MEMBER_DECORATORS, 65 COMPONENT_RERENDER_FUNCTION, 66 RMELMTID, 67 ABOUTTOBEDELETEDINTERNAL, 68 UPDATEDIRTYELEMENTS, 69 LINKS_DECORATORS, 70 BASE_COMPONENT_NAME_PU, 71 OBSERVED_PROPERTY_SIMPLE_PU, 72 OBSERVED_PROPERTY_OBJECT_PU, 73 SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU, 74 SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 75 SYNCHED_PROPERTY_NESED_OBJECT_PU, 76 OBSERVED_PROPERTY_ABSTRACT_PU, 77 CREATE_LOCAL_STORAGE_LINK, 78 CREATE_LOCAL_STORAGE_PROP, 79 COMPONENT_UPDATE_STATE_VARS, 80 COMPONENT_WATCH_DECORATOR, 81 $$, 82 COMPONENT_UPDATE_ELMT_ID, 83 OLD_ELMT_ID, 84 NEW_ELMT_ID, 85 UPDATE_RECYCLE_ELMT_ID, 86 DECORATOR_TYPE_ANY, 87 COMPONENT_CONSTRUCTOR_PARAMS, 88 COMPONENT_PARAMS_FUNCTION, 89 FUNCTION, 90 COMPONENT_PARAMS_LAMBDA_FUNCTION 91 } from './pre_define'; 92 import { 93 BUILDIN_STYLE_NAMES, 94 CUSTOM_BUILDER_METHOD, 95 INNER_STYLE_FUNCTION, 96 INTERFACE_NODE_SET, 97 STYLES_ATTRIBUTE, 98 INNER_CUSTOM_BUILDER_METHOD 99 } from './component_map'; 100 import { 101 componentCollection, 102 linkCollection, 103 localStorageLinkCollection, 104 localStoragePropCollection, 105 propCollection 106 } from './validate_ui_syntax'; 107 import { 108 addConstructor, 109 getInitConstructor, 110 updateConstructor 111 } from './process_component_constructor'; 112 import { 113 ControllerType, 114 processMemberVariableDecorators, 115 UpdateResult, 116 stateObjectCollection, 117 curPropMap, 118 decoratorParamSet, 119 isSimpleType, 120 } from './process_component_member'; 121 import { 122 processComponentBuild, 123 processComponentBlock 124 } from './process_component_build'; 125 import { 126 LogType, 127 LogInfo, 128 hasDecorator, 129 getPossibleBuilderTypeParameter, 130 storedFileInfo, 131 } from './utils'; 132 import { 133 partialUpdateConfig, 134 globalProgram 135 } from '../main'; 136 import { builderTypeParameter } from './process_ui_syntax'; 137 import { isRecycle } from './process_custom_component'; 138 139 export function processComponentClass(node: ts.StructDeclaration, context: ts.TransformationContext, 140 log: LogInfo[], program: ts.Program): ts.ClassDeclaration { 141 const memberNode: ts.ClassElement[] = 142 processMembers(node.members, node.name, context, log, program, checkPreview(node)); 143 return ts.factory.createClassDeclaration(undefined, node.modifiers, node.name, 144 node.typeParameters, updateHeritageClauses(node, log), memberNode); 145 } 146 147 function checkPreview(node: ts.ClassDeclaration) { 148 let hasPreview: boolean = false; 149 if (node && node.decorators) { 150 for (let i = 0; i < node.decorators.length; i++) { 151 const name: string = node.decorators[i].getText().replace(/\([^\(\)]*\)/, '').trim(); 152 if (name === COMPONENT_DECORATOR_PREVIEW) { 153 hasPreview = true; 154 break; 155 } 156 } 157 } 158 return hasPreview; 159 } 160 161 type BuildCount = { 162 count: number; 163 } 164 165 function processMembers(members: ts.NodeArray<ts.ClassElement>, parentComponentName: ts.Identifier, 166 context: ts.TransformationContext, log: LogInfo[], program: ts.Program, hasPreview: boolean): ts.ClassElement[] { 167 const buildCount: BuildCount = { count: 0 }; 168 let ctorNode: any = getInitConstructor(members, parentComponentName); 169 const newMembers: ts.ClassElement[] = []; 170 const watchMap: Map<string, ts.Node> = new Map(); 171 const updateParamsStatements: ts.Statement[] = []; 172 const stateVarsStatements: ts.Statement[] = []; 173 const purgeVariableDepStatements: ts.Statement[] = []; 174 const rerenderStatements: ts.Statement[] = []; 175 const deleteParamsStatements: ts.PropertyDeclaration[] = []; 176 const checkController: ControllerType = 177 { hasController: !componentCollection.customDialogs.has(parentComponentName.getText()) }; 178 const interfaceNode = ts.factory.createInterfaceDeclaration(undefined, undefined, 179 parentComponentName.getText() + INTERFACE_NAME_SUFFIX, undefined, undefined, []); 180 members.forEach((item: ts.ClassElement) => { 181 let updateItem: ts.ClassElement; 182 if (ts.isPropertyDeclaration(item)) { 183 if (isStaticProperty(item)) { 184 newMembers.push(item); 185 validateDecorators(item, log); 186 } else { 187 addPropertyMember(item, newMembers, program, parentComponentName.getText(), log); 188 const result: UpdateResult = processMemberVariableDecorators(parentComponentName, item, 189 ctorNode, watchMap, checkController, log, program, context, hasPreview, interfaceNode); 190 if (result.isItemUpdate()) { 191 updateItem = result.getProperity(); 192 } else { 193 updateItem = item; 194 } 195 if (result.getVariableGet()) { 196 newMembers.push(result.getVariableGet()); 197 } 198 if (result.getVariableSet()) { 199 newMembers.push(result.getVariableSet()); 200 } 201 if (result.isCtorUpdate()) { 202 ctorNode = result.getCtor(); 203 } 204 if (result.getUpdateParams()) { 205 updateParamsStatements.push(result.getUpdateParams()); 206 } 207 if (result.getStateVarsParams()) { 208 stateVarsStatements.push(result.getStateVarsParams()); 209 } 210 if (result.isDeleteParams()) { 211 deleteParamsStatements.push(item); 212 } 213 if (result.getControllerSet()) { 214 newMembers.push(result.getControllerSet()); 215 } 216 processPropertyUnchanged(result, purgeVariableDepStatements); 217 } 218 } 219 if (ts.isMethodDeclaration(item) && item.name) { 220 updateItem = 221 processComponentMethod(item, parentComponentName, context, log, buildCount); 222 } 223 if (updateItem) { 224 newMembers.push(updateItem); 225 } 226 }); 227 INTERFACE_NODE_SET.add(interfaceNode); 228 validateBuildMethodCount(buildCount, parentComponentName, log); 229 validateHasController(parentComponentName, checkController, log); 230 if (storedFileInfo.getCurrentArkTsFile().recycleComponents.has(parentComponentName.getText())) { 231 newMembers.unshift(addDeleteParamsFunc(deleteParamsStatements, true)); 232 } 233 newMembers.unshift(addDeleteParamsFunc(deleteParamsStatements)); 234 addIntoNewMembers(newMembers, parentComponentName, updateParamsStatements, 235 purgeVariableDepStatements, rerenderStatements, stateVarsStatements); 236 if (partialUpdateConfig.partialUpdateMode) { 237 ctorNode = updateConstructor(ctorNode, [], assignParams(parentComponentName.getText()), true); 238 } 239 newMembers.unshift(addConstructor(ctorNode, watchMap, parentComponentName)); 240 curPropMap.clear(); 241 return newMembers; 242 } 243 244 function assignParams(parentComponentName: string): ts.Statement[] { 245 return [ts.factory.createIfStatement( 246 ts.factory.createBinaryExpression( 247 ts.factory.createTypeOfExpression(ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)), 248 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 249 ts.factory.createStringLiteral(FUNCTION) 250 ), 251 ts.factory.createBlock( 252 [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 253 ts.factory.createPropertyAccessExpression( 254 ts.factory.createThis(), 255 ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION) 256 ), 257 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 258 ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION) 259 ))], 260 true 261 ) 262 )]; 263 } 264 265 function isStaticProperty(property: ts.PropertyDeclaration): boolean { 266 return property.modifiers && property.modifiers.length && property.modifiers.some(modifier => { 267 return modifier.kind === ts.SyntaxKind.StaticKeyword; 268 }); 269 } 270 271 function validateDecorators(item: ts.ClassElement, log: LogInfo[]): void { 272 if (item.decorators && item.decorators.length) { 273 item.decorators.map((decorator: ts.Decorator) => { 274 const decoratorName: string = decorator.getText(); 275 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 276 log.push({ 277 type: LogType.ERROR, 278 message: `The static variable of struct cannot be used together with built-in decorators.`, 279 pos: item.getStart() 280 }); 281 } 282 }); 283 } 284 } 285 286 function processPropertyUnchanged( 287 result: UpdateResult, 288 purgeVariableDepStatements: ts.Statement[] 289 ): void { 290 if (partialUpdateConfig.partialUpdateMode) { 291 if (result.getPurgeVariableDepStatement()) { 292 purgeVariableDepStatements.push(result.getPurgeVariableDepStatement()); 293 } 294 } 295 } 296 297 function addIntoNewMembers( 298 newMembers: ts.ClassElement[], 299 parentComponentName: ts.Identifier, 300 updateParamsStatements: ts.Statement[], 301 purgeVariableDepStatements: ts.Statement[], 302 rerenderStatements: ts.Statement[], 303 stateVarsStatements: ts.Statement[] 304 ): void { 305 if (partialUpdateConfig.partialUpdateMode) { 306 newMembers.unshift( 307 addInitialParamsFunc(updateParamsStatements, parentComponentName), 308 addUpdateStateVarsFunc(stateVarsStatements, parentComponentName), 309 addPurgeVariableDepFunc(purgeVariableDepStatements) 310 ); 311 newMembers.push(addRerenderFunc(rerenderStatements)); 312 } else { 313 newMembers.unshift(addUpdateParamsFunc(updateParamsStatements, parentComponentName)); 314 } 315 } 316 317 function addPropertyMember(item: ts.ClassElement, newMembers: ts.ClassElement[], 318 program: ts.Program, parentComponentName: string, log: LogInfo[]): void { 319 const propertyItem: ts.PropertyDeclaration = item as ts.PropertyDeclaration; 320 let decoratorName: string; 321 let updatePropertyItem: ts.PropertyDeclaration; 322 const type: ts.TypeNode = propertyItem.type; 323 if (!propertyItem.decorators || propertyItem.decorators.length === 0) { 324 updatePropertyItem = createPropertyDeclaration(propertyItem, type, true); 325 newMembers.push(updatePropertyItem); 326 } else if (propertyItem.decorators) { 327 for (let i = 0; i < propertyItem.decorators.length; i++) { 328 let newType: ts.TypeNode; 329 decoratorName = propertyItem.decorators[i].getText().replace(/\(.*\)$/, '').trim(); 330 let isLocalStorage: boolean = false; 331 if (!partialUpdateConfig.partialUpdateMode) { 332 newType = createTypeReference(decoratorName, type, log, program); 333 } else { 334 newType = createTypeReferencePU(decoratorName, type, log, program); 335 } 336 if ( 337 decoratorName === COMPONENT_LOCAL_STORAGE_LINK_DECORATOR || 338 decoratorName === COMPONENT_LOCAL_STORAGE_PROP_DECORATOR 339 ) { 340 isLocalStorage = true; 341 } 342 const newUpdatePropertyItem = createPropertyDeclaration( 343 propertyItem, newType, false, isLocalStorage, parentComponentName); 344 if (!updatePropertyItem) { 345 updatePropertyItem = newUpdatePropertyItem; 346 } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName) && 347 decoratorName !== COMPONENT_WATCH_DECORATOR) { 348 updatePropertyItem = newUpdatePropertyItem; 349 } 350 } 351 if (updatePropertyItem) { 352 newMembers.push(updatePropertyItem); 353 } 354 } 355 } 356 357 function createPropertyDeclaration(propertyItem: ts.PropertyDeclaration, newType: ts.TypeNode | undefined, 358 normalVar: boolean, isLocalStorage: boolean = false, parentComponentName: string = null): ts.PropertyDeclaration { 359 if (typeof newType === undefined) { 360 return undefined; 361 } 362 let prefix: string = ''; 363 if (!normalVar) { 364 prefix = '__'; 365 } 366 const privateM: ts.ModifierToken<ts.SyntaxKind.PrivateKeyword> = 367 ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword); 368 return ts.factory.updatePropertyDeclaration(propertyItem, undefined, 369 propertyItem.modifiers || [privateM], prefix + propertyItem.name.getText(), 370 propertyItem.questionToken, newType, isLocalStorage ? 371 createLocalStroageCallExpression(propertyItem, propertyItem.name.getText(), 372 parentComponentName) : undefined); 373 } 374 375 function createLocalStroageCallExpression(node: ts.PropertyDeclaration, name: string, 376 parentComponentName: string): ts.CallExpression { 377 const localStorageLink: Set<string> = localStorageLinkCollection.get(parentComponentName).get(name); 378 const localStorageProp: Set<string> = localStoragePropCollection.get(parentComponentName).get(name); 379 let localFuncName: string; 380 const localValue: ts.Expression[] = [ 381 ts.factory.createStringLiteral(localStorageLink && !localStorageProp ? 382 Array.from(localStorageLink)[0] : !localStorageLink && localStorageProp ? 383 Array.from(localStorageProp)[0] : COMPONENT_CONSTRUCTOR_UNDEFINED), 384 node.initializer ? node.initializer : ts.factory.createNumericLiteral(COMPONENT_CONSTRUCTOR_UNDEFINED), 385 ts.factory.createThis(), ts.factory.createStringLiteral(name || COMPONENT_CONSTRUCTOR_UNDEFINED) 386 ]; 387 if (!partialUpdateConfig.partialUpdateMode) { 388 localFuncName = localStorageLink && !localStorageProp ? COMPONENT_SET_AND_LINK : 389 COMPONENT_SET_AND_PROP; 390 } else { 391 localFuncName = localStorageLink && !localStorageProp ? CREATE_LOCAL_STORAGE_LINK : 392 CREATE_LOCAL_STORAGE_PROP; 393 localValue.splice(-2, 1); 394 } 395 return ts.factory.createCallExpression( 396 ts.factory.createPropertyAccessExpression( 397 !partialUpdateConfig.partialUpdateMode ? 398 ts.factory.createPropertyAccessExpression( 399 ts.factory.createThis(), 400 ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_LOCALSTORAGE}_`) 401 ) : ts.factory.createThis(), 402 ts.factory.createIdentifier(localFuncName) 403 ), 404 [node.type], 405 localValue 406 ); 407 } 408 409 function processComponentMethod(node: ts.MethodDeclaration, parentComponentName: ts.Identifier, 410 context: ts.TransformationContext, log: LogInfo[], buildCount: BuildCount): ts.MethodDeclaration { 411 let updateItem: ts.MethodDeclaration = node; 412 const name: string = node.name.getText(); 413 const customBuilder: ts.Decorator[] = []; 414 if (name === COMPONENT_BUILD_FUNCTION) { 415 buildCount.count = buildCount.count + 1; 416 if (node.parameters.length) { 417 log.push({ 418 type: LogType.ERROR, 419 message: `The 'build' method can not have arguments.`, 420 pos: node.getStart() 421 }); 422 } 423 const buildNode: ts.MethodDeclaration = processComponentBuild(node, log); 424 updateItem = processBuildMember(buildNode, context, log); 425 } else if (node.body && ts.isBlock(node.body)) { 426 if (name === COMPONENT_TRANSITION_FUNCTION) { 427 updateItem = ts.factory.updateMethodDeclaration(node, node.decorators, node.modifiers, 428 node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, 429 node.type, processComponentBlock(node.body, false, log, true)); 430 } else if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR, customBuilder)) { 431 CUSTOM_BUILDER_METHOD.add(name); 432 INNER_CUSTOM_BUILDER_METHOD.add(name); 433 builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters); 434 let parameters: ts.NodeArray<ts.ParameterDeclaration> = ts.factory.createNodeArray(Array.from(node.parameters)); 435 parameters.push(createParentParameter()); 436 const builderNode: ts.MethodDeclaration = ts.factory.updateMethodDeclaration(node, customBuilder, 437 node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, 438 parameters, node.type, processComponentBlock(node.body, false, log, false, true)); 439 builderTypeParameter.params = []; 440 updateItem = processBuildMember(builderNode, context, log, true); 441 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 442 if (node.parameters && node.parameters.length === 0) { 443 if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) { 444 INNER_STYLE_FUNCTION.set(name, node.body); 445 STYLES_ATTRIBUTE.add(name); 446 BUILDIN_STYLE_NAMES.add(name); 447 decoratorParamSet.add(STYLES); 448 } 449 } else { 450 log.push({ 451 type: LogType.ERROR, 452 message: `@Styles can't have parameters.`, 453 pos: node.getStart() 454 }); 455 } 456 return; 457 } 458 } 459 return updateItem; 460 } 461 462 export function createParentParameter(): ts.ParameterDeclaration { 463 return ts.factory.createParameterDeclaration(undefined, undefined, undefined, 464 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), undefined, undefined, 465 ts.factory.createIdentifier(NULL)); 466 } 467 468 export function processBuildMember(node: ts.MethodDeclaration | ts.FunctionDeclaration, context: ts.TransformationContext, 469 log: LogInfo[], isBuilder = false): ts.MethodDeclaration | ts.FunctionDeclaration { 470 return ts.visitNode(node, visitBuild); 471 function visitBuild(node: ts.Node): ts.Node { 472 if (isGeometryView(node)) { 473 node = processGeometryView(node as ts.ExpressionStatement, log); 474 } 475 if (isProperty(node)) { 476 node = createReference(node as ts.PropertyAssignment, log, isBuilder); 477 } 478 if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name) && 479 stateObjectCollection.has(checkStateName(node)) && node.parent && ts.isCallExpression(node.parent) && 480 ts.isPropertyAccessExpression(node.parent.expression) && node !== node.parent.expression && 481 node.parent.expression.name.escapedText.toString() !== FOREACH_GET_RAW_OBJECT) { 482 return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 483 ts.factory.createIdentifier(FOREACH_OBSERVED_OBJECT), 484 ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [node]); 485 } 486 return ts.visitEachChild(node, visitBuild, context); 487 } 488 function checkStateName(node: ts.PropertyAccessExpression): string { 489 if (node.expression && !node.expression.expression && node.name && ts.isIdentifier(node.name)) { 490 return node.name.escapedText.toString(); 491 } 492 return null; 493 } 494 } 495 496 function isGeometryView(node: ts.Node): boolean { 497 if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) { 498 const call: ts.CallExpression = node.expression; 499 const exp: ts.Expression = call.expression; 500 const args: ts.NodeArray<ts.Expression> = call.arguments; 501 if (ts.isPropertyAccessExpression(exp) && ts.isIdentifier(exp.expression) && 502 exp.expression.escapedText.toString() === GEOMETRY_VIEW && ts.isIdentifier(exp.name) && 503 exp.name.escapedText.toString() === COMPONENT_CREATE_FUNCTION && args && args.length === 1 && 504 (ts.isArrowFunction(args[0]) || ts.isFunctionExpression(args[0]))) { 505 return true; 506 } 507 } 508 return false; 509 } 510 511 function processGeometryView(node: ts.ExpressionStatement, 512 log: LogInfo[]): ts.ExpressionStatement { 513 const exp: ts.CallExpression = node.expression as ts.CallExpression; 514 const arg: ts.ArrowFunction | ts.FunctionExpression = 515 exp.arguments[0] as ts.ArrowFunction | ts.FunctionExpression; 516 return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(exp, 517 exp.expression, undefined, [ts.factory.createArrowFunction(undefined, undefined, arg.parameters, 518 undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 519 getGeometryReaderFunctionBlock(arg, log))])); 520 } 521 522 function getGeometryReaderFunctionBlock(node: ts.ArrowFunction | ts.FunctionExpression, 523 log: LogInfo[]): ts.Block { 524 let blockNode: ts.Block; 525 if (ts.isBlock(node.body)) { 526 blockNode = node.body; 527 } else if (ts.isArrowFunction(node) && ts.isCallExpression(node.body)) { 528 blockNode = ts.factory.createBlock([ts.factory.createExpressionStatement(node.body)]); 529 } 530 return processComponentBlock(blockNode, false, log); 531 } 532 533 function updateHeritageClauses(node: ts.StructDeclaration, log: LogInfo[]) 534 : ts.NodeArray<ts.HeritageClause> { 535 if (node.heritageClauses && !checkHeritageClauses(node)) { 536 log.push({ 537 type: LogType.ERROR, 538 message: 'The struct component is not allowed to extends other class or implements other interface.', 539 pos: node.heritageClauses.pos 540 }); 541 } 542 const result:ts.HeritageClause[] = []; 543 const heritageClause:ts.HeritageClause = createHeritageClause(); 544 result.push(heritageClause); 545 return ts.factory.createNodeArray(result); 546 } 547 548 function checkHeritageClauses(node: ts.StructDeclaration): boolean { 549 if (node.heritageClauses.length === 1 && node.heritageClauses[0].types && 550 node.heritageClauses[0].types.length === 1) { 551 const expressionNode: ts.ExpressionWithTypeArguments = node.heritageClauses[0].types[0]; 552 if (expressionNode.expression && ts.isIdentifier(expressionNode.expression) && 553 expressionNode.expression.escapedText.toString() === CUSTOM_COMPONENT) { 554 return true; 555 } 556 } 557 return false; 558 } 559 560 export function isProperty(node: ts.Node): Boolean { 561 if (judgmentParentType(node)) { 562 if (node.parent.parent.expression && ts.isIdentifier(node.parent.parent.expression) && 563 !BUILDIN_STYLE_NAMES.has(node.parent.parent.expression.escapedText.toString()) && 564 componentCollection.customComponents.has( 565 node.parent.parent.expression.escapedText.toString())) { 566 return true; 567 } else if (ts.isPropertyAccessExpression(node.parent.parent.expression) && 568 ts.isIdentifier(node.parent.parent.expression.expression) && 569 componentCollection.customComponents.has( 570 node.parent.parent.expression.name.escapedText.toString())) { 571 return true; 572 } 573 } 574 return false; 575 } 576 577 function judgmentParentType(node: ts.Node): boolean { 578 return ts.isPropertyAssignment(node) && node.name && ts.isIdentifier(node.name) && 579 node.parent && ts.isObjectLiteralExpression(node.parent) && node.parent.parent && 580 (ts.isCallExpression(node.parent.parent) || ts.isEtsComponentExpression(node.parent.parent)); 581 } 582 583 export function createReference(node: ts.PropertyAssignment, log: LogInfo[], isBuilder = false, 584 isParamsLambda: boolean = false, isRecycleComponent: boolean = false): ts.PropertyAssignment { 585 const linkParentComponent: string[] = getParentNode(node, linkCollection).slice(1); 586 const propParentComponent: string[] = getParentNode(node, propCollection).slice(1); 587 const propertyName: ts.Identifier = node.name as ts.Identifier; 588 let initText: string; 589 const LINK_REG: RegExp = /^\$/g; 590 if (isRecycleComponent && ts.isShorthandPropertyAssignment(node)) { 591 return node; 592 } 593 const initExpression: ts.Expression = node.initializer; 594 let is$$: boolean = false; 595 if (ts.isIdentifier(initExpression) && 596 initExpression.escapedText.toString().match(LINK_REG)) { 597 initText = initExpression.escapedText.toString().replace(LINK_REG, ''); 598 } else if (ts.isPropertyAccessExpression(initExpression) && initExpression.expression && 599 initExpression.expression.kind === ts.SyntaxKind.ThisKeyword && 600 ts.isIdentifier(initExpression.name) && initExpression.name.escapedText.toString().match(LINK_REG)) { 601 initText = initExpression.name.escapedText.toString().replace(LINK_REG, ''); 602 } else if (isBuilder && ts.isPropertyAccessExpression(initExpression) && initExpression.expression && 603 ts.isIdentifier(initExpression.expression) && initExpression.expression.escapedText.toString() === $$ && 604 ts.isIdentifier(initExpression.name) && linkParentComponent.includes(propertyName.escapedText.toString())) { 605 is$$ = true; 606 initText = initExpression.name.escapedText.toString(); 607 } else if (isMatchInitExpression(initExpression) && 608 linkParentComponent.includes(propertyName.escapedText.toString())) { 609 initText = initExpression.name.escapedText.toString().replace(LINK_REG, ''); 610 } 611 if (initText) { 612 node = addDoubleUnderline(node, propertyName, initText, is$$, isParamsLambda, isRecycleComponent); 613 } 614 return node; 615 } 616 617 function isMatchInitExpression(initExpression: ts.Expression): boolean { 618 return ts.isPropertyAccessExpression(initExpression) && 619 initExpression.expression && 620 initExpression.expression.kind === ts.SyntaxKind.ThisKeyword && 621 ts.isIdentifier(initExpression.name); 622 } 623 624 function addDoubleUnderline(node: ts.PropertyAssignment, propertyName: ts.Identifier, 625 initText: string, is$$ = false, isParamsLambda: boolean, isRecycleComponent: boolean): ts.PropertyAssignment { 626 return ts.factory.updatePropertyAssignment(node, propertyName, 627 ts.factory.createPropertyAccessExpression( 628 is$$ && partialUpdateConfig.partialUpdateMode ? ts.factory.createIdentifier($$) : ts.factory.createThis(), 629 isParamsLambda || isRecycleComponent ? ts.factory.createIdentifier(initText) : ts.factory.createIdentifier(`__${initText}`))); 630 } 631 632 function getParentNode(node: ts.PropertyAssignment, collection: Map<string, Set<string>>): string[] { 633 const grandparentNode: ts.NewExpression = node.parent.parent as ts.NewExpression; 634 const grandparentExpression: ts.Identifier | ts.PropertyAccessExpression = 635 grandparentNode.expression as ts.Identifier | ts.PropertyAccessExpression; 636 let parentComponent: Set<string> = new Set(); 637 let grandparentName: string; 638 if (ts.isIdentifier(grandparentExpression)) { 639 grandparentName = grandparentExpression.escapedText.toString(); 640 parentComponent = collection.get(grandparentName); 641 } else if (ts.isPropertyAccessExpression(grandparentExpression)) { 642 grandparentName = grandparentExpression.name.escapedText.toString(); 643 parentComponent = collection.get(grandparentName); 644 } else { 645 // ignore 646 } 647 if (!parentComponent) { 648 parentComponent = new Set(); 649 } 650 return [grandparentName, ...parentComponent]; 651 } 652 653 function addUpdateParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): 654 ts.MethodDeclaration { 655 return createParamsInitBlock(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, statements, parentComponentName); 656 } 657 658 function addInitialParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration { 659 return createParamsInitBlock(COMPONENT_CONSTRUCTOR_INITIAL_PARAMS, statements, parentComponentName); 660 } 661 662 function addUpdateStateVarsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration { 663 return createParamsInitBlock(COMPONENT_UPDATE_STATE_VARS, statements, parentComponentName); 664 } 665 666 function addPurgeVariableDepFunc(statements: ts.Statement[]): ts.MethodDeclaration { 667 return ts.factory.createMethodDeclaration( 668 undefined, undefined, undefined, 669 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PURGE_VARIABLE_DEP), 670 undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 671 ts.factory.createIdentifier(RMELMTID), undefined, undefined, undefined)], undefined, 672 ts.factory.createBlock(statements, true)); 673 } 674 675 function addDeleteParamsFunc(statements: ts.PropertyDeclaration[], 676 updateRecyle: boolean = false): ts.MethodDeclaration { 677 const deleteStatements: ts.ExpressionStatement[] = []; 678 const updateStatements: ts.ExpressionStatement[] = []; 679 statements.forEach((statement: ts.PropertyDeclaration) => { 680 const name: ts.Identifier = statement.name as ts.Identifier; 681 let paramsStatement: ts.ExpressionStatement; 682 if (!partialUpdateConfig.partialUpdateMode || statement.decorators) { 683 paramsStatement = createParamsWithUnderlineStatement(name); 684 } 685 if (partialUpdateConfig.partialUpdateMode && statement.decorators) { 686 updateStatements.push(createElmtIdWithUnderlineStatement(name)); 687 } 688 deleteStatements.push(paramsStatement); 689 }); 690 if (partialUpdateConfig.partialUpdateMode && updateRecyle) { 691 return createRecycleElmt(updateStatements); 692 } 693 const defaultStatement: ts.ExpressionStatement = 694 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 695 ts.factory.createPropertyAccessExpression( 696 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 697 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_SUBSCRIBER_MANAGER), 698 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_GET_FUNCTION)), undefined, []), 699 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_DELETE_FUNCTION)), 700 undefined, [ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 701 ts.factory.createThis(), ts.factory.createIdentifier( 702 !partialUpdateConfig.partialUpdateMode ? 703 ABOUT_TO_BE_DELETE_FUNCTION_ID : ABOUT_TO_BE_DELETE_FUNCTION_ID__)), 704 undefined, [])])); 705 deleteStatements.push(defaultStatement); 706 if (partialUpdateConfig.partialUpdateMode) { 707 const aboutToBeDeletedInternalStatement: ts.ExpressionStatement = createDeletedInternalStatement(); 708 deleteStatements.push(aboutToBeDeletedInternalStatement); 709 } 710 const deleteParamsMethod: ts.MethodDeclaration = 711 createParamsInitBlock(COMPONENT_CONSTRUCTOR_DELETE_PARAMS, deleteStatements); 712 return deleteParamsMethod; 713 } 714 715 function createRecycleElmt(statements: ts.Statement[]): ts.MethodDeclaration { 716 return ts.factory.createMethodDeclaration(undefined, undefined, undefined, 717 ts.factory.createIdentifier(UPDATE_RECYCLE_ELMT_ID), undefined, undefined, [ 718 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 719 ts.factory.createIdentifier(OLD_ELMT_ID)), 720 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 721 ts.factory.createIdentifier(NEW_ELMT_ID)) 722 ], undefined, ts.factory.createBlock(statements, true)); 723 } 724 725 function createParamsWithUnderlineStatement(name: ts.Identifier): ts.ExpressionStatement { 726 return ts.factory.createExpressionStatement( 727 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 728 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 729 ts.factory.createIdentifier(`__${name.escapedText.toString()}`)), 730 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_DELETE_PARAMS)), undefined, [])); 731 } 732 733 function createElmtIdWithUnderlineStatement(name: ts.Identifier): ts.ExpressionStatement { 734 return ts.factory.createExpressionStatement( 735 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 736 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 737 ts.factory.createIdentifier(`__${name.escapedText.toString()}`)), 738 ts.factory.createIdentifier(COMPONENT_UPDATE_ELMT_ID)), undefined, [ 739 ts.factory.createIdentifier(OLD_ELMT_ID), ts.factory.createIdentifier(NEW_ELMT_ID) 740 ])); 741 } 742 743 function createDeletedInternalStatement(): ts.ExpressionStatement { 744 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 745 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 746 ts.factory.createIdentifier(ABOUTTOBEDELETEDINTERNAL)), undefined, [])); 747 } 748 749 function addRerenderFunc(statements: ts.Statement[]): ts.MethodDeclaration { 750 let updateDirtyElementStatement: ts.Statement = ts.factory.createExpressionStatement( 751 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 752 ts.factory.createThis(), ts.factory.createIdentifier(UPDATEDIRTYELEMENTS)), undefined, [])); 753 statements.push(updateDirtyElementStatement); 754 return ts.factory.createMethodDeclaration(undefined, undefined, undefined, 755 ts.factory.createIdentifier(COMPONENT_RERENDER_FUNCTION), undefined, undefined, [], undefined, 756 ts.factory.createBlock(statements, true)); 757 } 758 759 function createParamsInitBlock(express: string, statements: ts.Statement[], 760 parentComponentName?: ts.Identifier): ts.MethodDeclaration { 761 const methodDeclaration: ts.MethodDeclaration = ts.factory.createMethodDeclaration(undefined, 762 undefined, undefined, ts.factory.createIdentifier(express), undefined, undefined, 763 [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 764 express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : 765 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), undefined, 766 express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : 767 ts.factory.createTypeReferenceNode( 768 ts.factory.createIdentifier(parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined), 769 undefined)], undefined, ts.factory.createBlock(statements, true)); 770 return methodDeclaration; 771 } 772 773 function validateBuildMethodCount(buildCount: BuildCount, parentComponentName: ts.Identifier, 774 log: LogInfo[]): void { 775 if (buildCount.count !== 1) { 776 log.push({ 777 type: LogType.ERROR, 778 message: `struct '${parentComponentName.getText()}' must be at least or at most one 'build' method.`, 779 pos: parentComponentName.getStart() 780 }); 781 } 782 } 783 784 function validateHasController(componentName: ts.Identifier, checkController: ControllerType, 785 log: LogInfo[]): void { 786 if (!checkController.hasController) { 787 log.push({ 788 type: LogType.ERROR, 789 message: '@CustomDialog component should have a property of the CustomDialogController type.', 790 pos: componentName.pos 791 }); 792 } 793 } 794 795 function createHeritageClause(): ts.HeritageClause { 796 if (partialUpdateConfig.partialUpdateMode) { 797 return ts.factory.createHeritageClause( 798 ts.SyntaxKind.ExtendsKeyword, 799 [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), [])] 800 ); 801 } 802 return ts.factory.createHeritageClause( 803 ts.SyntaxKind.ExtendsKeyword, 804 [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier(BASE_COMPONENT_NAME), [])] 805 ); 806 } 807 808 function createTypeReference(decoratorName: string, type: ts.TypeNode, log: LogInfo[], 809 program: ts.Program): ts.TypeNode { 810 let newType: ts.TypeNode; 811 switch (decoratorName) { 812 case COMPONENT_STATE_DECORATOR: 813 case COMPONENT_PROVIDE_DECORATOR: 814 newType = ts.factory.createTypeReferenceNode( 815 isSimpleType(type, program, log) 816 ? OBSERVED_PROPERTY_SIMPLE 817 : OBSERVED_PROPERTY_OBJECT, 818 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 819 ); 820 break; 821 case COMPONENT_LINK_DECORATOR: 822 case COMPONENT_CONSUME_DECORATOR: 823 newType = ts.factory.createTypeReferenceNode( 824 isSimpleType(type, program, log) 825 ? SYNCHED_PROPERTY_SIMPLE_TWO_WAY 826 : SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 827 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 828 ); 829 break; 830 case COMPONENT_PROP_DECORATOR: 831 newType = ts.factory.createTypeReferenceNode( 832 SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 833 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 834 ); 835 break; 836 case COMPONENT_OBJECT_LINK_DECORATOR: 837 newType = ts.factory.createTypeReferenceNode( 838 SYNCHED_PROPERTY_NESED_OBJECT, 839 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 840 ); 841 break; 842 case COMPONENT_STORAGE_PROP_DECORATOR: 843 case COMPONENT_STORAGE_LINK_DECORATOR: 844 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [ 845 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 846 ]); 847 break; 848 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR: 849 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 850 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [ 851 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 852 ]); 853 break; 854 } 855 return newType; 856 } 857 858 function createTypeReferencePU(decoratorName: string, type: ts.TypeNode, log: LogInfo[], 859 program: ts.Program): ts.TypeNode { 860 let newType: ts.TypeNode; 861 switch (decoratorName) { 862 case COMPONENT_STATE_DECORATOR: 863 case COMPONENT_PROVIDE_DECORATOR: 864 newType = ts.factory.createTypeReferenceNode( 865 isSimpleType(type, program, log) 866 ? OBSERVED_PROPERTY_SIMPLE_PU 867 : OBSERVED_PROPERTY_OBJECT_PU, 868 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 869 ); 870 break; 871 case COMPONENT_LINK_DECORATOR: 872 newType = ts.factory.createTypeReferenceNode( 873 isSimpleType(type, program, log) 874 ? SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU 875 : SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 876 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 877 ); 878 break; 879 case COMPONENT_PROP_DECORATOR: 880 newType = ts.factory.createTypeReferenceNode( 881 SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 882 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 883 ); 884 break; 885 case COMPONENT_OBJECT_LINK_DECORATOR: 886 newType = ts.factory.createTypeReferenceNode( 887 SYNCHED_PROPERTY_NESED_OBJECT_PU, 888 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 889 ); 890 break; 891 case COMPONENT_CONSUME_DECORATOR: 892 case COMPONENT_STORAGE_PROP_DECORATOR: 893 case COMPONENT_STORAGE_LINK_DECORATOR: 894 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [ 895 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 896 ]); 897 break; 898 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR: 899 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 900 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [ 901 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 902 ]); 903 break; 904 } 905 return newType; 906 } 907