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 ISINITIALRENDER, 47 UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID, 48 COMPONENT_CUSTOM_DECORATOR, 49 $$ 50} from './pre_define'; 51import { 52 propertyCollection, 53 stateCollection, 54 linkCollection, 55 propCollection, 56 regularCollection, 57 storagePropCollection, 58 storageLinkCollection, 59 provideCollection, 60 consumeCollection, 61 objectLinkCollection, 62 isStaticViewCollection, 63 builderParamObjectCollection, 64 getLocalStorageCollection, 65 builderParamInitialization 66} from './validate_ui_syntax'; 67import { 68 propAndLinkDecorators, 69 curPropMap, 70 createViewCreate, 71 createCustomComponentNewExpression 72} from './process_component_member'; 73import { 74 LogType, 75 LogInfo, 76 componentInfo 77} from './utils'; 78import { 79 bindComponentAttr, 80 parentConditionalExpression, 81 createComponentCreationStatement, 82 createFunction 83} from './process_component_build'; 84import { partialUpdateConfig } from '../main'; 85 86let decoractorMap: Map<string, Map<string, Set<string>>>; 87 88export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 89 log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false): void { 90 decoractorMap = new Map( 91 [[COMPONENT_STATE_DECORATOR, stateCollection], 92 [COMPONENT_LINK_DECORATOR, linkCollection], 93 [COMPONENT_PROP_DECORATOR, propCollection], 94 [COMPONENT_NON_DECORATOR, regularCollection], 95 [COMPONENT_PROVIDE_DECORATOR, provideCollection], 96 [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]); 97 const componentNode: ts.CallExpression = getCustomComponentNode(node); 98 if (componentNode) { 99 const hasChainCall: boolean = componentNode.parent && 100 ts.isPropertyAccessExpression(componentNode.parent); 101 let ischangeNode: boolean = false; 102 let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression( 103 componentNode, name, isBuilder, isGlobalBuilder); 104 let argumentsArray: ts.PropertyAssignment[]; 105 if (isHasChild(componentNode)) { 106 // @ts-ignore 107 argumentsArray = componentNode.arguments[0].properties.slice(); 108 argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => { 109 if (isToChange(item, name)) { 110 ischangeNode = true; 111 const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment( 112 item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression)); 113 argumentsArray.splice(index, 1, propertyAssignmentNode); 114 } 115 }); 116 if (ischangeNode) { 117 const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node, 118 ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments, 119 [ts.factory.createObjectLiteralExpression(argumentsArray, true)])); 120 customComponentNewExpression = createCustomComponentNewExpression( 121 newNode.expression as ts.CallExpression, name, isBuilder); 122 } 123 } 124 if (hasChainCall) { 125 if (partialUpdateConfig.partialUpdateMode) { 126 const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement( 127 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 128 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))]; 129 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode, log); 130 newStatements.push(createComponentCreationStatement(componentAttributes(), commomComponentNode, isGlobalBuilder)); 131 } else { 132 newStatements.push(ts.factory.createExpressionStatement( 133 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 134 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))); 135 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log); 136 } 137 } 138 addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode, 139 isBuilder, isGlobalBuilder); 140 if (hasChainCall) { 141 newStatements.push(ts.factory.createExpressionStatement( 142 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 143 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 144 } 145 } 146} 147 148function componentAttributes(): ts.Statement { 149 return ts.factory.createExpressionStatement( 150 ts.factory.createCallExpression( 151 ts.factory.createPropertyAccessExpression( 152 ts.factory.createIdentifier(COMPONENT_COMMON), 153 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION) 154 ), undefined, [] 155 )); 156} 157 158function isHasChild(node: ts.CallExpression): boolean { 159 return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) && 160 node.arguments[0].properties && node.arguments[0].properties.length > 0; 161} 162 163function isToChange(item: ts.PropertyAssignment, name: string): boolean { 164 const builderParamName: Set<string> = builderParamObjectCollection.get(name); 165 if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName && 166 builderParamName.has(item.name.getText()) && 167 !/\.(bind|call|apply)/.test(item.initializer.getText())) { 168 return true; 169 } 170 return false; 171} 172 173function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ArrowFunction { 174 return ts.factory.createArrowFunction(undefined, undefined, [], undefined, 175 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 176 ts.factory.createBlock([ts.factory.createExpressionStatement(node)], true)); 177} 178 179function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 180 newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression, 181 isBuilder: boolean = false, isGlobalBuilder: boolean = false): void { 182 if (ts.isNewExpression(newNode)) { 183 const propertyArray: ts.ObjectLiteralElementLike[] = []; 184 validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder); 185 addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode, isBuilder, isGlobalBuilder); 186 } 187} 188 189function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[], 190 newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[], 191 componentNode: ts.CallExpression, isBuilder: boolean = false, isGlobalBuilder: boolean = false): void { 192 if (!partialUpdateConfig.partialUpdateMode) { 193 const id: string = componentInfo.id.toString(); 194 newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id, 195 ts.factory.updateExpressionStatement(node, createViewCreate(newNode)), 196 ts.factory.createObjectLiteralExpression(props, true), name)); 197 } else { 198 newStatements.push(createCustomComponent(node, newNode, name, componentNode, isGlobalBuilder)); 199 } 200} 201 202function createChildElmtId(node: ts.CallExpression, name: string): ts.PropertyAssignment[] { 203 const propsAndObjectLinks: string[] = []; 204 const childParam: ts.PropertyAssignment[] = []; 205 if (propCollection.get(name)) { 206 propsAndObjectLinks.push(...propCollection.get(name)); 207 } 208 if (objectLinkCollection.get(name)) { 209 propsAndObjectLinks.push(...objectLinkCollection.get(name)); 210 } 211 if (node.arguments[0].properties) { 212 node.arguments[0].properties.forEach(item => { 213 if (ts.isIdentifier(item.name) && propsAndObjectLinks.includes(item.name.escapedText.toString())) { 214 childParam.push(item); 215 } 216 }); 217 } 218 return childParam; 219} 220 221function createCustomComponent(node: ts.ExpressionStatement, newNode: ts.NewExpression, name: string, 222 componentNode: ts.CallExpression, isGlobalBuilder: boolean = false): ts.Block { 223 let componentParameter: ts.ObjectLiteralExpression; 224 if (componentNode.arguments && componentNode.arguments.length) { 225 componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name), true); 226 } else { 227 componentParameter = ts.factory.createObjectLiteralExpression([], false); 228 } 229 return ts.factory.createBlock( 230 [ 231 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 232 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 233 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 234 ts.factory.createIdentifier(OBSERVECOMPONENTCREATION) 235 ), undefined, 236 [ts.factory.createArrowFunction(undefined, undefined, 237 [ 238 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 239 ts.factory.createIdentifier(ELMTID) 240 ), 241 ts.factory.createParameterDeclaration(undefined, undefined, undefined, 242 ts.factory.createIdentifier(ISINITIALRENDER) 243 ) 244 ], undefined, 245 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 246 ts.factory.createBlock( 247 [ 248 ts.factory.createExpressionStatement( 249 ts.factory.createCallExpression( 250 ts.factory.createPropertyAccessExpression( 251 ts.factory.createIdentifier(VIEWSTACKPROCESSOR), 252 ts.factory.createIdentifier(STARTGETACCESSRECORDINGFOR) 253 ), undefined, 254 [ts.factory.createIdentifier(ELMTID)] 255 )), 256 createIfCustomComponent(newNode, componentParameter, isGlobalBuilder), 257 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 258 ts.factory.createPropertyAccessExpression( 259 ts.factory.createIdentifier(VIEWSTACKPROCESSOR), 260 ts.factory.createIdentifier(STOPGETACCESSRECORDING) 261 ), 262 undefined, 263 [] 264 )) 265 ], true))])) 266 ], true); 267} 268 269function createIfCustomComponent(newNode: ts.NewExpression, componentParameter: ts.ObjectLiteralExpression, 270 isGlobalBuilder: boolean = false): ts.IfStatement { 271 return ts.factory.createIfStatement( 272 ts.factory.createIdentifier(ISINITIALRENDER), 273 ts.factory.createBlock( 274 [ 275 ts.factory.createExpressionStatement( 276 ts.factory.createCallExpression( 277 ts.factory.createPropertyAccessExpression( 278 ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), 279 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION) 280 ), undefined, [newNode])) 281 ], true), 282 ts.factory.createBlock( 283 [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 284 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 285 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 286 ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID) 287 ), undefined, 288 [ts.factory.createIdentifier(ELMTID), componentParameter]))], true) 289 ); 290} 291 292function validateCustomComponentPrams(node: ts.CallExpression, name: string, 293 props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void { 294 const curChildProps: Set<string> = new Set([]); 295 const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments; 296 const propertySet: Set<string> = getCollectionSet(name, propertyCollection); 297 const linkSet: Set<string> = getCollectionSet(name, linkCollection); 298 if (nodeArguments && nodeArguments.length === 1 && 299 ts.isObjectLiteralExpression(nodeArguments[0])) { 300 const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression; 301 nodeArgument.properties.forEach(item => { 302 if (item.name && ts.isIdentifier(item.name)) { 303 curChildProps.add(item.name.escapedText.toString()); 304 } 305 validateStateManagement(item, name, log, isBuilder); 306 if (isNonThisProperty(item, linkSet)) { 307 if (isToChange(item as ts.PropertyAssignment, name)) { 308 item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment, 309 item.name, changeNodeFromCallToArrow(item.initializer)); 310 } 311 props.push(item); 312 } 313 }); 314 } 315 validateInitDecorator(node, name, curChildProps, log); 316} 317 318function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression { 319 let temp: any = node.expression; 320 let child: any = null; 321 let componentNode: any = null; 322 while (temp) { 323 if (ts.isIdentifier(temp)) { 324 child = temp; 325 break; 326 } 327 temp = temp.expression; 328 } 329 if (child) { 330 let parent = child.parent; 331 while (parent) { 332 if (ts.isExpressionStatement(parent)) { 333 break; 334 } 335 if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) { 336 componentNode = parent; 337 break; 338 } 339 parent = parent.parent; 340 } 341 } 342 return componentNode; 343} 344 345function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> { 346 if (!collection) { 347 return new Set([]); 348 } 349 return collection.get(name) || new Set([]); 350} 351 352function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 353 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 354 propertySet.has(node.name.escapedText.toString())) { 355 return true; 356 } 357 return false; 358} 359 360function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 361 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 362 (node.initializer.escapedText && node.initializer.escapedText.includes('$') || 363 ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 364 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 365 ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) { 366 return false; 367 } 368 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 369 !propertySet.has(node.name.escapedText.toString())) { 370 return true; 371 } 372 return false; 373} 374 375function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string, 376 log: LogInfo[], isBuilder: boolean): void { 377 validateForbiddenToInitViaParam(node, customComponentName, log); 378 checkFromParentToChild(node, customComponentName, log, isBuilder); 379} 380 381function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string, 382 log: LogInfo[], isBuilder: boolean): void { 383 let propertyName: string; 384 if (ts.isIdentifier(node.name)) { 385 propertyName = node.name.escapedText.toString(); 386 } 387 const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName); 388 let parentPropertyName: string; 389 if (curPropertyKind) { 390 if (isInitFromParent(node)) { 391 parentPropertyName = 392 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log); 393 let parentPropertyKind: string = curPropMap.get(parentPropertyName); 394 if (!parentPropertyKind) { 395 parentPropertyKind = COMPONENT_NON_DECORATOR; 396 } 397 if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 398 validateIllegalInitFromParent( 399 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log); 400 } 401 } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) && 402 curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) { 403 if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) { 404 validateIllegalInitFromParent(node, propertyName, curPropertyKind, 405 node.initializer.getText(), COMPONENT_NON_DECORATOR, log); 406 } 407 } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer && 408 (ts.isPropertyAccessExpression(node.initializer) || 409 ts.isElementAccessExpression(node.initializer))) { 410 return; 411 } else { 412 parentPropertyName = 413 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName; 414 const parentPropertyKind = COMPONENT_NON_DECORATOR; 415 if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 416 if (isBuilder && judgeStructAssigned$$(node)) { 417 log.push({ 418 type: LogType.WARN, 419 message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` + 420 `${curPropertyKind} property '${propertyName}' by yourself.`, 421 // @ts-ignore 422 pos: node.initializer ? node.initializer.getStart() : node.getStart() 423 }); 424 } else { 425 validateIllegalInitFromParent( 426 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN); 427 } 428 } 429 } 430 } 431} 432 433function judgeStructAssigned$$(node: ts.ObjectLiteralElementLike): boolean { 434 return partialUpdateConfig.partialUpdateMode && node.initializer && 435 ts.isPropertyAccessExpression(node.initializer) && 436 node.initializer.expression && ts.isIdentifier(node.initializer.expression) && 437 node.initializer.expression.escapedText.toString() === $$; 438} 439 440function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean { 441 if (ts.isPropertyAssignment(node) && node.initializer) { 442 if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 443 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 444 ts.isIdentifier(node.initializer.name)) { 445 return true; 446 } else if (ts.isIdentifier(node.initializer) && 447 matchStartWithDollar(node.initializer.getText())) { 448 return true; 449 } 450 } 451} 452 453function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean { 454 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) && 455 !matchStartWithDollar(node.initializer.getText())) { 456 return true; 457 } 458} 459 460function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string, 461 log: LogInfo[]): string { 462 const initExpression: ts.Expression = node.initializer; 463 if (!initExpression) { 464 return undefined; 465 } 466 let parentPropertyName: string = initExpression.getText(); 467 if (curPropertyKind === COMPONENT_LINK_DECORATOR) { 468 // @ts-ignore 469 const initName: ts.Identifier = initExpression.name || initExpression; 470 if (hasDollar(initExpression)) { 471 parentPropertyName = initName.getText().replace(/^\$/, ''); 472 } else { 473 parentPropertyName = initName.getText(); 474 } 475 } else { 476 if (hasDollar(initExpression)) { 477 validateNonLinkWithDollar(node, log); 478 } else { 479 // @ts-ignore 480 if (node.initializer && node.initializer.name) { 481 parentPropertyName = node.initializer.name.getText(); 482 } 483 } 484 } 485 486 return parentPropertyName; 487} 488 489function isCorrectInitFormParent(parent: string, child: string): boolean { 490 switch (child) { 491 case COMPONENT_STATE_DECORATOR: 492 case COMPONENT_PROP_DECORATOR: 493 case COMPONENT_PROVIDE_DECORATOR: 494 return true; 495 case COMPONENT_NON_DECORATOR: 496 if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR, 497 COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) { 498 return true; 499 } 500 break; 501 case COMPONENT_LINK_DECORATOR: 502 return ![COMPONENT_NON_DECORATOR].includes(parent); 503 } 504 return false; 505} 506 507function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string { 508 for (const item of decoractorMap.entries()) { 509 if (getCollectionSet(customComponentName, item[1]).has(propertyName)) { 510 return item[0]; 511 } 512 } 513} 514 515function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement { 516 return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList( 517 [ts.factory.createVariableDeclaration(ts.factory.createIdentifier( 518 `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode( 519 ts.factory.createIdentifier(name)), 520 ts.factory.createConditionalExpression( 521 ts.factory.createParenthesizedExpression( 522 ts.factory.createBinaryExpression( 523 createConditionParent(isBuilder), 524 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 525 ts.factory.createPropertyAccessExpression( 526 createConditionParent(isBuilder), 527 ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID) 528 ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken), 529 ts.factory.createAsExpression(ts.factory.createCallExpression( 530 ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder), 531 ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined, 532 [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID), 533 undefined, []) : ts.factory.createStringLiteral(id)]), 534 ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))), 535 ts.factory.createToken(ts.SyntaxKind.ColonToken), 536 ts.factory.createIdentifier('undefined')))], ts.NodeFlags.Let)); 537} 538 539export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression { 540 return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(); 541} 542 543function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement, 544 newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement { 545 const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`; 546 return ts.factory.createIfStatement(ts.factory.createBinaryExpression( 547 ts.factory.createIdentifier(viewName), 548 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken), 549 ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)), 550 ts.factory.createBlock([node], true), 551 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 552 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier( 553 viewName), ts.factory.createIdentifier( 554 `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])), 555 isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined, 556 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 557 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`), 558 ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined, 559 [ts.factory.createIdentifier(viewName)]))], true)); 560} 561 562function createStaticIf(name: string): ts.IfStatement { 563 return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression( 564 ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression( 565 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 566 ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])), 567 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 568 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 569 ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)), 570 undefined, []))], true), undefined); 571} 572 573function hasDollar(initExpression: ts.Expression): boolean { 574 if (ts.isPropertyAccessExpression(initExpression) && 575 matchStartWithDollar(initExpression.name.getText())) { 576 return true; 577 } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) { 578 return true; 579 } else { 580 return false; 581 } 582} 583 584function matchStartWithDollar(name: string): boolean { 585 return /^\$/.test(name); 586} 587 588function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike, 589 customComponentName: string, log: LogInfo[]): void { 590 const forbiddenToInitViaParamSet: Set<string> = new Set([ 591 ...getCollectionSet(customComponentName, storageLinkCollection), 592 ...getCollectionSet(customComponentName, storagePropCollection), 593 ...getCollectionSet(customComponentName, consumeCollection) 594 ]); 595 const localStorageSet: Set<string> = new Set(); 596 getLocalStorageCollection(customComponentName, localStorageSet); 597 if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) { 598 log.push({ 599 type: localStorageSet.has(node.name.getText()) ? LogType.WARN : LogType.ERROR, 600 message: `Property '${node.name.getText()}' in the custom component '${customComponentName}'` + 601 ` cannot initialize here (forbidden to specify).`, 602 pos: node.name.getStart() 603 }); 604 } 605} 606 607function validateNonExistentProperty(node: ts.ObjectLiteralElementLike, 608 customComponentName: string, log: LogInfo[]): void { 609 log.push({ 610 type: LogType.ERROR, 611 message: `Property '${node.name.escapedText.toString()}' does not exist on type '${customComponentName}'.`, 612 pos: node.name.getStart() 613 }); 614} 615 616function validateMandatoryToAssignmentViaParam(node: ts.CallExpression, customComponentName: string, 617 curChildProps: Set<string>, log: LogInfo[]): void { 618 if (builderParamObjectCollection.get(customComponentName) && 619 builderParamObjectCollection.get(customComponentName).size) { 620 builderParamObjectCollection.get(customComponentName).forEach((item) => { 621 if (!curChildProps.has(item)) { 622 log.push({ 623 type: LogType.ERROR, 624 message: `The property decorated with @BuilderParam '${item}' must be assigned a value .`, 625 pos: node.getStart() 626 }); 627 } 628 }); 629 } 630} 631 632function validateMandatoryToInitViaParam(node: ts.ExpressionStatement, customComponentName: string, 633 curChildProps: Set<string>, log: LogInfo[]): void { 634 const mandatoryToInitViaParamSet: Set<string> = new Set([ 635 ...getCollectionSet(customComponentName, propCollection), 636 ...getCollectionSet(customComponentName, linkCollection), 637 ...getCollectionSet(customComponentName, objectLinkCollection)]); 638 mandatoryToInitViaParamSet.forEach(item => { 639 if (!curChildProps.has(item)) { 640 log.push({ 641 type: LogType.ERROR, 642 message: `Property '${item}' in the custom component '${customComponentName}'` + 643 ` is missing (mandatory to specify).`, 644 pos: node.getStart() 645 }); 646 } 647 }); 648} 649 650function validateInitDecorator(node: ts.CallExpression, customComponentName: string, 651 curChildProps: Set<string>, log: LogInfo[]): void { 652 const mandatoryToInitViaParamSet: Set<string> = new Set([ 653 ...getCollectionSet(customComponentName, builderParamObjectCollection)]); 654 const decoratorVariable: Set<string> = builderParamInitialization.get(customComponentName); 655 mandatoryToInitViaParamSet.forEach((item: string) => { 656 if (item && !curChildProps.has(item) && decoratorVariable && !decoratorVariable.has(item)) { 657 log.push({ 658 type: LogType.ERROR, 659 message: `Property '${item}' in the custom component '${customComponentName}'` + 660 ` is missing assignment or initialization.`, 661 pos: node.getStart() 662 }); 663 } 664 }); 665} 666 667function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string, 668 curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string, 669 log: LogInfo[], inputType: LogType = undefined): void { 670 let type: LogType = LogType.ERROR; 671 if (inputType) { 672 type = inputType; 673 } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes( 674 parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) { 675 type = LogType.WARN; 676 } 677 log.push({ 678 type: type, 679 message: `The ${parentPropertyKind} property '${parentPropertyName}' cannot be assigned to ` + 680 `the ${curPropertyKind} property '${propertyName}'.`, 681 // @ts-ignore 682 pos: node.initializer ? node.initializer.getStart() : node.getStart() 683 }); 684} 685 686function validateLinkWithoutDollar(node: ts.PropertyAssignment, log: LogInfo[]): void { 687 log.push({ 688 type: LogType.ERROR, 689 message: `The @Link property '${node.name.getText()}' should initialize` + 690 ` using '$' to create a reference to a @State or @Link variable.`, 691 pos: node.initializer.getStart() 692 }); 693} 694 695function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void { 696 log.push({ 697 type: LogType.ERROR, 698 message: `Property '${node.name.getText()}' cannot initialize` + 699 ` using '$' to create a reference to a variable.`, 700 pos: node.initializer.getStart() 701 }); 702} 703