1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import ts from 'typescript'; 17 18import { 19 COMPONENT_NON_DECORATOR, 20 COMPONENT_STATE_DECORATOR, 21 COMPONENT_PROP_DECORATOR, 22 COMPONENT_LINK_DECORATOR, 23 COMPONENT_STORAGE_LINK_DECORATOR, 24 COMPONENT_PROVIDE_DECORATOR, 25 COMPONENT_OBJECT_LINK_DECORATOR, 26 COMPONENT_CREATE_FUNCTION, 27 COMPONENT_POP_FUNCTION, 28 BASE_COMPONENT_NAME, 29 CUSTOM_COMPONENT_EARLIER_CREATE_CHILD, 30 COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, 31 CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID, 32 COMPONENT_CONSTRUCTOR_UNDEFINED, 33 CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION, 34 CUSTOM_COMPONENT_MARK_STATIC_FUNCTION, 35 COMPONENT_COMMON, 36 COMPONENT_CONSTRUCTOR_PARENT, 37 GENERATE_ID, 38 ELMTID, 39 VIEWSTACKPROCESSOR, 40 STARTGETACCESSRECORDINGFOR, 41 STOPGETACCESSRECORDING, 42 ALLOCATENEWELMETIDFORNEXTCOMPONENT, 43 STATE_OBJECTLINK_DECORATORS, 44 BASE_COMPONENT_NAME_PU, 45 OBSERVECOMPONENTCREATION, 46 OBSERVECOMPONENTCREATION2, 47 ISINITIALRENDER, 48 UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID, 49 COMPONENT_CUSTOM_DECORATOR, 50 $$, 51 COMPONENT_RECYCLE, 52 COMPONENT_CREATE_RECYCLE, 53 RECYCLE_NODE, 54 ABOUT_TO_REUSE, 55 COMPONENT_RERENDER_FUNCTION, 56 OBSERVE_RECYCLE_COMPONENT_CREATION, 57 FUNCTION, 58 COMPONENT_IF_UNDEFINED, 59 COMPONENT_PARAMS_LAMBDA_FUNCTION, 60 COMPONENT_PARAMS_FUNCTION, 61 COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION 62} from './pre_define'; 63import { 64 propertyCollection, 65 stateCollection, 66 linkCollection, 67 propCollection, 68 regularCollection, 69 storagePropCollection, 70 storageLinkCollection, 71 provideCollection, 72 consumeCollection, 73 objectLinkCollection, 74 isStaticViewCollection, 75 builderParamObjectCollection, 76 getLocalStorageCollection, 77 builderParamInitialization, 78 propInitialization 79} from './validate_ui_syntax'; 80import { 81 propAndLinkDecorators, 82 curPropMap, 83 createViewCreate, 84 createCustomComponentNewExpression 85} from './process_component_member'; 86import { 87 LogType, 88 LogInfo, 89 componentInfo, 90 storedFileInfo, 91} from './utils'; 92import { 93 bindComponentAttr, 94 parentConditionalExpression, 95 createComponentCreationStatement, 96 createFunction, 97 ComponentAttrInfo, 98 ifRetakeId, 99 transferBuilderCall, 100 createViewStackProcessorStatement, 101} from './process_component_build'; 102import { 103 partialUpdateConfig 104} from '../main'; 105import { 106 GLOBAL_CUSTOM_BUILDER_METHOD 107} from './component_map'; 108import { 109 createReference, 110 isProperty 111} from './process_component_class'; 112 113let decoractorMap: Map<string, Map<string, Set<string>>>; 114 115export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 116 log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false, 117 idName: ts.Expression = undefined): void { 118 decoractorMap = new Map( 119 [[COMPONENT_STATE_DECORATOR, stateCollection], 120 [COMPONENT_LINK_DECORATOR, linkCollection], 121 [COMPONENT_PROP_DECORATOR, propCollection], 122 [COMPONENT_NON_DECORATOR, regularCollection], 123 [COMPONENT_PROVIDE_DECORATOR, provideCollection], 124 [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]); 125 const componentNode: ts.CallExpression = getCustomComponentNode(node); 126 if (componentNode) { 127 const isRecycleComponent: boolean = isRecycle(name); 128 const hasChainCall: boolean = componentNode.parent && 129 ts.isPropertyAccessExpression(componentNode.parent); 130 let ischangeNode: boolean = false; 131 let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression( 132 componentNode, name, isBuilder, isGlobalBuilder); 133 let argumentsArray: ts.PropertyAssignment[]; 134 const componentAttrInfo: ComponentAttrInfo = { reuseId: null }; 135 if (isHasChild(componentNode)) { 136 // @ts-ignore 137 argumentsArray = componentNode.arguments[0].properties.slice(); 138 argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => { 139 if (isToChange(item, name)) { 140 ischangeNode = true; 141 const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment( 142 item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression)); 143 argumentsArray.splice(index, 1, propertyAssignmentNode); 144 } 145 }); 146 if (ischangeNode) { 147 const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node, 148 ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments, 149 [ts.factory.createObjectLiteralExpression(argumentsArray, true)])); 150 customComponentNewExpression = createCustomComponentNewExpression( 151 newNode.expression as ts.CallExpression, name, isBuilder); 152 } 153 } 154 let judgeIdStart: number; 155 if (partialUpdateConfig.partialUpdateMode && idName) { 156 judgeIdStart = newStatements.length; 157 } 158 if (hasChainCall) { 159 if (partialUpdateConfig.partialUpdateMode) { 160 const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement( 161 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 162 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))]; 163 const immutableStatements: ts.Statement[] = []; 164 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode, 165 log, true, false, immutableStatements, false, componentAttrInfo); 166 newStatements.push(createComponentCreationStatement(componentAttributes(COMPONENT_COMMON), 167 commomComponentNode, COMPONENT_COMMON, isGlobalBuilder, false, undefined, immutableStatements)); 168 } else { 169 newStatements.push(ts.factory.createExpressionStatement( 170 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 171 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))); 172 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log); 173 } 174 } 175 if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) { 176 newStatements.push(createRecycleComponent(isGlobalBuilder)); 177 } 178 addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode, 179 isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo); 180 if (hasChainCall) { 181 newStatements.push(ts.factory.createExpressionStatement( 182 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 183 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 184 } 185 if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) { 186 newStatements.push(componentAttributes(COMPONENT_RECYCLE)); 187 } 188 if (partialUpdateConfig.partialUpdateMode && idName) { 189 newStatements.splice(judgeIdStart, newStatements.length - judgeIdStart, 190 ifRetakeId(newStatements.slice(judgeIdStart), idName)); 191 } 192 } 193} 194 195export function isRecycle(componentName: string): boolean { 196 return storedFileInfo.getCurrentArkTsFile().recycleComponents.has(componentName); 197} 198 199function createRecycleComponent(isGlobalBuilder: boolean): ts.Statement { 200 return createComponentCreationStatement(componentAttributes(COMPONENT_RECYCLE), 201 [ts.factory.createExpressionStatement( 202 createFunction(ts.factory.createIdentifier(COMPONENT_RECYCLE), 203 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)) 204 ], COMPONENT_RECYCLE, isGlobalBuilder); 205} 206 207function componentAttributes(componentName: string): ts.Statement { 208 return ts.factory.createExpressionStatement( 209 ts.factory.createCallExpression( 210 ts.factory.createPropertyAccessExpression( 211 ts.factory.createIdentifier(componentName), 212 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION) 213 ), undefined, [] 214 )); 215} 216 217function isHasChild(node: ts.CallExpression): boolean { 218 return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) && 219 node.arguments[0].properties && node.arguments[0].properties.length > 0; 220} 221 222function isToChange(item: ts.PropertyAssignment, name: string): boolean { 223 const builderParamName: Set<string> = builderParamObjectCollection.get(name); 224 if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName && 225 builderParamName.has(item.name.getText()) && 226 !/\.(bind|call|apply)/.test(item.initializer.getText())) { 227 return true; 228 } 229 return false; 230} 231 232function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ArrowFunction { 233 let builderBindThis: ts.ExpressionStatement = ts.factory.createExpressionStatement(node); 234 if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) && 235 GLOBAL_CUSTOM_BUILDER_METHOD.has(node.expression.escapedText.toString())) { 236 builderBindThis = transferBuilderCall(ts.factory.createExpressionStatement(node), node.expression.escapedText.toString()); 237 } 238 return ts.factory.createArrowFunction(undefined, undefined, [], undefined, 239 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 240 ts.factory.createBlock([builderBindThis], true)); 241} 242 243function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 244 newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression, 245 isBuilder: boolean, isGlobalBuilder: boolean, isRecycleComponent: boolean, 246 componentAttrInfo: ComponentAttrInfo): void { 247 if (ts.isNewExpression(newNode)) { 248 const propertyArray: ts.ObjectLiteralElementLike[] = []; 249 validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder); 250 addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode, 251 isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo); 252 } 253} 254 255function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[], 256 newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[], 257 componentNode: ts.CallExpression, isBuilder: boolean, isGlobalBuilder: boolean, 258 isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo): void { 259 if (!partialUpdateConfig.partialUpdateMode) { 260 const id: string = componentInfo.id.toString(); 261 newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id, 262 ts.factory.updateExpressionStatement(node, createViewCreate(newNode)), 263 ts.factory.createObjectLiteralExpression(props, true), name)); 264 } else { 265 newStatements.push(createCustomComponent(newNode, name, componentNode, isGlobalBuilder, isBuilder, 266 isRecycleComponent, componentAttrInfo)); 267 } 268} 269 270function createChildElmtId(node: ts.CallExpression, name: string): ts.PropertyAssignment[] { 271 const propsAndObjectLinks: string[] = []; 272 const childParam: ts.PropertyAssignment[] = []; 273 if (propCollection.get(name)) { 274 propsAndObjectLinks.push(...propCollection.get(name)); 275 } 276 if (objectLinkCollection.get(name)) { 277 propsAndObjectLinks.push(...objectLinkCollection.get(name)); 278 } 279 if (node.arguments[0].properties) { 280 node.arguments[0].properties.forEach(item => { 281 if (ts.isIdentifier(item.name) && propsAndObjectLinks.includes(item.name.escapedText.toString())) { 282 childParam.push(item); 283 } 284 }); 285 } 286 return childParam; 287} 288 289function createCustomComponent(newNode: ts.NewExpression, name: string, componentNode: ts.CallExpression, 290 isGlobalBuilder: boolean, isBuilder: boolean, isRecycleComponent: boolean, 291 componentAttrInfo: ComponentAttrInfo): ts.Block { 292 let componentParameter: ts.ObjectLiteralExpression; 293 if (componentNode.arguments && componentNode.arguments.length) { 294 componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name), true); 295 } else { 296 componentParameter = ts.factory.createObjectLiteralExpression([], false); 297 } 298 const arrowArgArr: ts.ParameterDeclaration[] = [ 299 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 300 ts.factory.createIdentifier(ELMTID) 301 ), 302 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 303 ts.factory.createIdentifier(ISINITIALRENDER) 304 ) 305 ]; 306 const arrowBolck: ts.Statement[] = [ 307 createIfCustomComponent(newNode, componentNode, componentParameter, name, isGlobalBuilder, 308 isBuilder, isRecycleComponent, componentAttrInfo) 309 ]; 310 if (isRecycleComponent) { 311 arrowArgArr.push(ts.factory.createParameterDeclaration( 312 undefined, undefined, undefined, ts.factory.createIdentifier(RECYCLE_NODE), 313 undefined, undefined, ts.factory.createNull() 314 )); 315 } 316 if (isRecycleComponent || !partialUpdateConfig.optimizeComponent) { 317 arrowBolck.unshift(createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID)); 318 arrowBolck.push(createViewStackProcessorStatement(STOPGETACCESSRECORDING)); 319 } 320 const observeArgArr: ts.Node[] = [ 321 ts.factory.createArrowFunction(undefined, undefined, arrowArgArr, undefined, 322 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 323 ts.factory.createBlock(arrowBolck, true)) 324 ]; 325 if (isRecycleComponent) { 326 componentAttrInfo.reuseId ? observeArgArr.unshift(componentAttrInfo.reuseId) : 327 observeArgArr.unshift(ts.factory.createStringLiteral(name)); 328 } else if (partialUpdateConfig.optimizeComponent) { 329 observeArgArr.push(ts.factory.createNull()); 330 } 331 return ts.factory.createBlock( 332 [ 333 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 334 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 335 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 336 isRecycleComponent ? 337 ts.factory.createIdentifier(OBSERVE_RECYCLE_COMPONENT_CREATION) : 338 ts.factory.createIdentifier(partialUpdateConfig.optimizeComponent ? 339 OBSERVECOMPONENTCREATION2 : OBSERVECOMPONENTCREATION) 340 ), 341 undefined, observeArgArr as ts.Expression[])) 342 ], true); 343} 344 345function assignRecycleParams(): ts.IfStatement { 346 return ts.factory.createIfStatement( 347 ts.factory.createIdentifier(RECYCLE_NODE), 348 ts.factory.createBlock( 349 [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 350 ts.factory.createPropertyAccessExpression( 351 ts.factory.createIdentifier(RECYCLE_NODE), 352 ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION) 353 ), 354 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 355 ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION) 356 ))], 357 true 358 ), 359 undefined 360 ); 361} 362 363export function assignComponentParams(componentNode: ts.CallExpression, 364 isBuilder: boolean = false): ts.VariableStatement { 365 const isParamsLambda: boolean = true; 366 const [keyArray, valueArray]: [ts.Node[], ts.Node[]] = splitComponentParams(componentNode, isBuilder, isParamsLambda); 367 let integrateParams: boolean = false; 368 if (!keyArray.length && componentNode.arguments && componentNode.arguments.length > 0 && componentNode.arguments[0]) { 369 integrateParams = true; 370 } 371 return ts.factory.createVariableStatement( 372 undefined, 373 ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration( 374 ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION), 375 undefined, 376 undefined, 377 ts.factory.createArrowFunction( 378 undefined, 379 undefined, 380 [], 381 undefined, 382 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 383 ts.factory.createBlock( 384 [ts.factory.createReturnStatement( 385 integrateParams ? componentNode.arguments[0] : ts.factory.createObjectLiteralExpression( 386 reWriteComponentParams(keyArray, valueArray), 387 true 388 ) 389 )], 390 true 391 ) 392 ) 393 )], 394 ts.NodeFlags.Let 395 )); 396} 397 398function reWriteComponentParams(keyArray: ts.Node[], valueArray: ts.Node[]): (ts.PropertyAssignment | 399 ts.ShorthandPropertyAssignment)[] { 400 const returnProperties: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = []; 401 keyArray.forEach((item: ts.Identifier, index: number) => { 402 if (!valueArray[index]) { 403 returnProperties.push(ts.factory.createShorthandPropertyAssignment( 404 item, 405 undefined 406 )); 407 } else { 408 returnProperties.push(ts.factory.createPropertyAssignment( 409 item, 410 valueArray[index] as ts.Identifier 411 )); 412 } 413 }); 414 return returnProperties; 415} 416 417function splitComponentParams(componentNode: ts.CallExpression, isBuilder: boolean, isParamsLambda: boolean): [ts.Node[], ts.Node[]] { 418 const keyArray: ts.Node[] = []; 419 const valueArray: ts.Node[] = []; 420 if (componentNode.arguments && componentNode.arguments.length > 0 && 421 ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) { 422 componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => { 423 const newPropertyItem: ts.PropertyAssignment = 424 isProperty(propertyItem) ? createReference(propertyItem, [], isBuilder, isParamsLambda) : propertyItem; 425 keyArray.push(newPropertyItem.name); 426 valueArray.push(newPropertyItem.initializer); 427 }); 428 } 429 return [keyArray, valueArray]; 430} 431 432function createIfCustomComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression, 433 componentParameter: ts.ObjectLiteralExpression, name: string, isGlobalBuilder: boolean, isBuilder: boolean, 434 isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo): ts.IfStatement { 435 return ts.factory.createIfStatement( 436 ts.factory.createIdentifier(ISINITIALRENDER), 437 ts.factory.createBlock( 438 [ 439 assignComponentParams(componentNode, isBuilder), 440 isRecycleComponent ? assignRecycleParams() : undefined, 441 isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) : 442 createNewComponent(newNode) 443 ], true), 444 ts.factory.createBlock( 445 [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 446 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 447 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 448 ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID) 449 ), undefined, 450 [ts.factory.createIdentifier(ELMTID), componentParameter]))], true) 451 ); 452} 453 454function createNewComponent(newNode: ts.NewExpression): ts.Statement { 455 return ts.factory.createExpressionStatement( 456 ts.factory.createCallExpression( 457 ts.factory.createPropertyAccessExpression( 458 ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), 459 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION) 460 ), undefined, [newNode])); 461} 462 463function createNewRecycleComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression, 464 name: string, componentAttrInfo: ComponentAttrInfo): ts.Statement { 465 let argNode: ts.Expression[] = []; 466 const componentParam: ts.PropertyAssignment[] = []; 467 if (componentNode.arguments && componentNode.arguments.length > 0 && 468 ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) { 469 componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => { 470 const newPropertyItem: ts.PropertyAssignment = createReference(propertyItem, [], false, false, true); 471 componentParam.push(newPropertyItem); 472 }); 473 argNode = [ts.factory.createObjectLiteralExpression(componentParam, false)]; 474 } else { 475 argNode = [ts.factory.createObjectLiteralExpression([], false)]; 476 } 477 const recycleNode: ts.CallExpression = ts.factory.createCallExpression( 478 createRecyclePropertyNode(ABOUT_TO_REUSE), undefined, argNode); 479 return ts.factory.createExpressionStatement( 480 ts.factory.createCallExpression( 481 ts.factory.createPropertyAccessExpression( 482 ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), 483 ts.factory.createIdentifier(COMPONENT_CREATE_RECYCLE) 484 ), undefined, 485 [ 486 ts.factory.createConditionalExpression( 487 ts.factory.createIdentifier(RECYCLE_NODE), 488 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 489 ts.factory.createIdentifier(RECYCLE_NODE), 490 ts.factory.createToken(ts.SyntaxKind.ColonToken), 491 newNode 492 ), 493 ts.factory.createBinaryExpression( 494 ts.factory.createIdentifier(RECYCLE_NODE), 495 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), 496 ts.factory.createNull() 497 ), 498 componentAttrInfo.reuseId ? componentAttrInfo.reuseId as ts.Expression : 499 ts.factory.createStringLiteral(name), 500 ts.factory.createArrowFunction(undefined, undefined, [], undefined, 501 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 502 ts.factory.createBlock([ 503 ts.factory.createIfStatement( 504 ts.factory.createBinaryExpression( 505 ts.factory.createIdentifier(RECYCLE_NODE), 506 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 507 ts.factory.createBinaryExpression( 508 ts.factory.createTypeOfExpression( 509 createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION)), 510 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 511 ts.factory.createStringLiteral(FUNCTION) 512 )), 513 ts.factory.createBlock([ 514 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 515 createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION), 516 undefined, 517 [] 518 )) 519 ], true), 520 ts.factory.createBlock( 521 [ 522 ts.factory.createIfStatement(ts.factory.createBinaryExpression( 523 createRecyclePropertyNode(ABOUT_TO_REUSE), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 524 ts.factory.createBinaryExpression( 525 ts.factory.createTypeOfExpression(createRecyclePropertyNode(ABOUT_TO_REUSE)), 526 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 527 ts.factory.createStringLiteral(FUNCTION) 528 )), 529 ts.factory.createBlock([ts.factory.createExpressionStatement(recycleNode)], true)), 530 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 531 createRecyclePropertyNode(COMPONENT_RERENDER_FUNCTION), undefined, [] 532 )) 533 ], 534 true 535 ) 536 )], true)) 537 ])); 538} 539 540function createRecyclePropertyNode(recycleFunctionName: string): ts.PropertyAccessExpression { 541 return ts.factory.createPropertyAccessExpression( 542 ts.factory.createIdentifier(RECYCLE_NODE), ts.factory.createIdentifier(recycleFunctionName)); 543} 544 545function validateCustomComponentPrams(node: ts.CallExpression, name: string, 546 props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void { 547 const curChildProps: Set<string> = new Set([]); 548 const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments; 549 const propertySet: Set<string> = getCollectionSet(name, propertyCollection); 550 const linkSet: Set<string> = getCollectionSet(name, linkCollection); 551 if (nodeArguments && nodeArguments.length === 1 && 552 ts.isObjectLiteralExpression(nodeArguments[0])) { 553 const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression; 554 nodeArgument.properties.forEach(item => { 555 if (item.name && ts.isIdentifier(item.name)) { 556 curChildProps.add(item.name.escapedText.toString()); 557 } 558 validateStateManagement(item, name, log, isBuilder); 559 if (isNonThisProperty(item, linkSet)) { 560 if (isToChange(item as ts.PropertyAssignment, name)) { 561 item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment, 562 item.name, changeNodeFromCallToArrow(item.initializer)); 563 } 564 props.push(item); 565 } 566 }); 567 } 568 if (!storedFileInfo.getCurrentArkTsFile().compFromDETS.has(name)) { 569 validateInitDecorator(node, name, curChildProps, log); 570 } 571 validateMandatoryToInitViaParam(node, name, curChildProps, log); 572} 573 574function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression { 575 let temp: any = node.expression; 576 let child: any = null; 577 let componentNode: any = null; 578 while (temp) { 579 if (ts.isIdentifier(temp)) { 580 child = temp; 581 break; 582 } 583 temp = temp.expression; 584 } 585 if (child) { 586 let parent = child.parent; 587 while (parent) { 588 if (ts.isExpressionStatement(parent)) { 589 break; 590 } 591 if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) { 592 componentNode = parent; 593 break; 594 } 595 parent = parent.parent; 596 } 597 } 598 return componentNode; 599} 600 601function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> { 602 if (!collection) { 603 return new Set([]); 604 } 605 return collection.get(name) || new Set([]); 606} 607 608function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 609 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 610 propertySet.has(node.name.escapedText.toString())) { 611 return true; 612 } 613 return false; 614} 615 616function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 617 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 618 (node.initializer.escapedText && node.initializer.escapedText.includes('$') || 619 ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 620 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 621 ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) { 622 return false; 623 } 624 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 625 !propertySet.has(node.name.escapedText.toString())) { 626 return true; 627 } 628 return false; 629} 630 631function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string, 632 log: LogInfo[], isBuilder: boolean): void { 633 validateForbiddenToInitViaParam(node, customComponentName, log); 634 checkFromParentToChild(node, customComponentName, log, isBuilder); 635} 636 637function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string, 638 log: LogInfo[], isBuilder: boolean): void { 639 let propertyName: string; 640 if (ts.isIdentifier(node.name)) { 641 propertyName = node.name.escapedText.toString(); 642 } 643 const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName); 644 let parentPropertyName: string; 645 if (curPropertyKind) { 646 if (isInitFromParent(node)) { 647 parentPropertyName = 648 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log); 649 let parentPropertyKind: string = curPropMap.get(parentPropertyName); 650 if (!parentPropertyKind) { 651 parentPropertyKind = COMPONENT_NON_DECORATOR; 652 } 653 if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 654 validateIllegalInitFromParent( 655 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log); 656 } 657 } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) && 658 curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) { 659 if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) { 660 validateIllegalInitFromParent(node, propertyName, curPropertyKind, 661 node.initializer.getText(), COMPONENT_NON_DECORATOR, log); 662 } 663 } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer && 664 (ts.isPropertyAccessExpression(node.initializer) || 665 ts.isElementAccessExpression(node.initializer) || ts.isIdentifier(node.initializer))) { 666 return; 667 } else { 668 parentPropertyName = 669 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName; 670 const parentPropertyKind = COMPONENT_NON_DECORATOR; 671 if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 672 if (isBuilder && judgeStructAssigned$$(node)) { 673 log.push({ 674 type: LogType.WARN, 675 message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` + 676 `${curPropertyKind} property '${propertyName}' by yourself.`, 677 // @ts-ignore 678 pos: node.initializer ? node.initializer.getStart() : node.getStart() 679 }); 680 } else { 681 validateIllegalInitFromParent( 682 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN); 683 } 684 } 685 } 686 } 687} 688 689function judgeStructAssigned$$(node: ts.ObjectLiteralElementLike): boolean { 690 return partialUpdateConfig.partialUpdateMode && node.initializer && 691 ts.isPropertyAccessExpression(node.initializer) && 692 node.initializer.expression && ts.isIdentifier(node.initializer.expression) && 693 node.initializer.expression.escapedText.toString() === $$; 694} 695 696function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean { 697 if (ts.isPropertyAssignment(node) && node.initializer) { 698 if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 699 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 700 ts.isIdentifier(node.initializer.name)) { 701 return true; 702 } else if (ts.isIdentifier(node.initializer) && 703 matchStartWithDollar(node.initializer.getText())) { 704 return true; 705 } 706 } 707} 708 709function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean { 710 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) && 711 !matchStartWithDollar(node.initializer.getText())) { 712 return true; 713 } 714} 715 716function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string, 717 log: LogInfo[]): string { 718 const initExpression: ts.Expression = node.initializer; 719 if (!initExpression) { 720 return undefined; 721 } 722 let parentPropertyName: string = initExpression.getText(); 723 if (curPropertyKind === COMPONENT_LINK_DECORATOR) { 724 // @ts-ignore 725 const initName: ts.Identifier = initExpression.name || initExpression; 726 if (hasDollar(initExpression)) { 727 parentPropertyName = initName.getText().replace(/^\$/, ''); 728 } else { 729 parentPropertyName = initName.getText(); 730 } 731 } else { 732 if (hasDollar(initExpression)) { 733 validateNonLinkWithDollar(node, log); 734 } else { 735 // @ts-ignore 736 if (node.initializer && node.initializer.name) { 737 parentPropertyName = node.initializer.name.getText(); 738 } 739 } 740 } 741 742 return parentPropertyName; 743} 744 745function isCorrectInitFormParent(parent: string, child: string): boolean { 746 switch (child) { 747 case COMPONENT_STATE_DECORATOR: 748 case COMPONENT_PROP_DECORATOR: 749 case COMPONENT_PROVIDE_DECORATOR: 750 return true; 751 case COMPONENT_NON_DECORATOR: 752 if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR, 753 COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) { 754 return true; 755 } 756 break; 757 case COMPONENT_LINK_DECORATOR: 758 case COMPONENT_OBJECT_LINK_DECORATOR: 759 return ![COMPONENT_NON_DECORATOR].includes(parent); 760 } 761 return false; 762} 763 764function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string { 765 for (const item of decoractorMap.entries()) { 766 if (getCollectionSet(customComponentName, item[1]).has(propertyName)) { 767 return item[0]; 768 } 769 } 770} 771 772function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement { 773 return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList( 774 [ts.factory.createVariableDeclaration(ts.factory.createIdentifier( 775 `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode( 776 ts.factory.createIdentifier(name)), 777 ts.factory.createConditionalExpression( 778 ts.factory.createParenthesizedExpression( 779 ts.factory.createBinaryExpression( 780 createConditionParent(isBuilder), 781 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 782 ts.factory.createPropertyAccessExpression( 783 createConditionParent(isBuilder), 784 ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID) 785 ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken), 786 ts.factory.createAsExpression(ts.factory.createCallExpression( 787 ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder), 788 ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined, 789 [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID), 790 undefined, []) : ts.factory.createStringLiteral(id)]), 791 ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))), 792 ts.factory.createToken(ts.SyntaxKind.ColonToken), 793 ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)))], ts.NodeFlags.Let)); 794} 795 796export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression { 797 return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(); 798} 799 800function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement, 801 newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement { 802 const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`; 803 return ts.factory.createIfStatement(ts.factory.createBinaryExpression( 804 ts.factory.createIdentifier(viewName), 805 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken), 806 ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)), 807 ts.factory.createBlock([node], true), 808 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 809 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier( 810 viewName), ts.factory.createIdentifier( 811 `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])), 812 isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined, 813 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 814 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`), 815 ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined, 816 [ts.factory.createIdentifier(viewName)]))], true)); 817} 818 819function createStaticIf(name: string): ts.IfStatement { 820 return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression( 821 ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression( 822 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 823 ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])), 824 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 825 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 826 ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)), 827 undefined, []))], true), undefined); 828} 829 830function hasDollar(initExpression: ts.Expression): boolean { 831 if (ts.isPropertyAccessExpression(initExpression) && 832 matchStartWithDollar(initExpression.name.getText())) { 833 return true; 834 } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) { 835 return true; 836 } else { 837 return false; 838 } 839} 840 841function matchStartWithDollar(name: string): boolean { 842 return /^\$/.test(name); 843} 844 845function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike, 846 customComponentName: string, log: LogInfo[]): void { 847 const forbiddenToInitViaParamSet: Set<string> = new Set([ 848 ...getCollectionSet(customComponentName, storageLinkCollection), 849 ...getCollectionSet(customComponentName, storagePropCollection), 850 ...getCollectionSet(customComponentName, consumeCollection) 851 ]); 852 const localStorageSet: Set<string> = new Set(); 853 getLocalStorageCollection(customComponentName, localStorageSet); 854 if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) { 855 log.push({ 856 type: LogType.ERROR, 857 message: `Property '${node.name.getText()}' in the custom component '${customComponentName}'` + 858 ` cannot initialize here (forbidden to specify).`, 859 pos: node.name.getStart() 860 }); 861 } 862} 863 864function validateNonExistentProperty(node: ts.ObjectLiteralElementLike, 865 customComponentName: string, log: LogInfo[]): void { 866 log.push({ 867 type: LogType.ERROR, 868 message: `Property '${node.name.escapedText.toString()}' does not exist on type '${customComponentName}'.`, 869 pos: node.name.getStart() 870 }); 871} 872 873function validateMandatoryToAssignmentViaParam(node: ts.CallExpression, customComponentName: string, 874 curChildProps: Set<string>, log: LogInfo[]): void { 875 if (builderParamObjectCollection.get(customComponentName) && 876 builderParamObjectCollection.get(customComponentName).size) { 877 builderParamObjectCollection.get(customComponentName).forEach((item) => { 878 if (!curChildProps.has(item)) { 879 log.push({ 880 type: LogType.ERROR, 881 message: `The property decorated with @BuilderParam '${item}' must be assigned a value .`, 882 pos: node.getStart() 883 }); 884 } 885 }); 886 } 887} 888 889function validateMandatoryToInitViaParam(node: ts.CallExpression, customComponentName: string, 890 curChildProps: Set<string>, log: LogInfo[]): void { 891 const mandatoryToInitViaParamSet: Set<string> = new Set([ 892 ...getCollectionSet(customComponentName, linkCollection), 893 ...getCollectionSet(customComponentName, objectLinkCollection)]); 894 mandatoryToInitViaParamSet.forEach(item => { 895 if (item && !curChildProps.has(item)) { 896 log.push({ 897 type: LogType.WARN, 898 message: `Property '${item}' in the custom component '${customComponentName}'` + 899 ` is missing (mandatory to specify).`, 900 pos: node.getStart() 901 }); 902 } 903 }); 904} 905 906function validateInitDecorator(node: ts.CallExpression, customComponentName: string, 907 curChildProps: Set<string>, log: LogInfo[]): void { 908 const mandatoryToInitViaParamSet: Set<string> = new Set([ 909 ...getCollectionSet(customComponentName, builderParamObjectCollection), 910 ...getCollectionSet(customComponentName, propCollection)]); 911 const decoratorVariable: Set<string> = new Set([ 912 ...(builderParamInitialization.get(customComponentName) || []), 913 ...(propInitialization.get(customComponentName) || [])]); 914 mandatoryToInitViaParamSet.forEach((item: string) => { 915 if (item && !curChildProps.has(item) && decoratorVariable && !decoratorVariable.has(item)) { 916 log.push({ 917 type: LogType.ERROR, 918 message: `Property '${item}' in the custom component '${customComponentName}'` + 919 ` is missing assignment or initialization.`, 920 pos: node.getStart() 921 }); 922 } 923 }); 924} 925 926function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string, 927 curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string, 928 log: LogInfo[], inputType: LogType = undefined): void { 929 let type: LogType = LogType.ERROR; 930 if (inputType) { 931 type = inputType; 932 } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes( 933 parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) { 934 type = LogType.WARN; 935 } 936 log.push({ 937 type: type, 938 message: `The ${parentPropertyKind} property '${parentPropertyName}' cannot be assigned to ` + 939 `the ${curPropertyKind} property '${propertyName}'.`, 940 // @ts-ignore 941 pos: node.initializer ? node.initializer.getStart() : node.getStart() 942 }); 943} 944 945function validateLinkWithoutDollar(node: ts.PropertyAssignment, log: LogInfo[]): void { 946 log.push({ 947 type: LogType.ERROR, 948 message: `The @Link property '${node.name.getText()}' should initialize` + 949 ` using '$' to create a reference to a @State or @Link variable.`, 950 pos: node.initializer.getStart() 951 }); 952} 953 954function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void { 955 log.push({ 956 type: LogType.ERROR, 957 message: `Property '${node.name.getText()}' cannot initialize` + 958 ` using '$' to create a reference to a variable.`, 959 pos: node.initializer.getStart() 960 }); 961} 962