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 GENERATE_ID, 37 ELMTID, 38 STARTGETACCESSRECORDINGFOR, 39 STOPGETACCESSRECORDING, 40 BASE_COMPONENT_NAME_PU, 41 OBSERVECOMPONENTCREATION, 42 OBSERVECOMPONENTCREATION2, 43 ISINITIALRENDER, 44 UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID, 45 $$, 46 COMPONENT_RECYCLE, 47 COMPONENT_CREATE_RECYCLE, 48 RECYCLE_NODE, 49 ABOUT_TO_REUSE, 50 COMPONENT_RERENDER_FUNCTION, 51 OBSERVE_RECYCLE_COMPONENT_CREATION, 52 FUNCTION, 53 COMPONENT_IF_UNDEFINED, 54 COMPONENT_PARAMS_LAMBDA_FUNCTION, 55 COMPONENT_PARAMS_FUNCTION, 56 COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION, 57 NAME, 58 COMPONENT_CALL, 59 COMPONENT_CONSUME_DECORATOR, 60 COMPONENT_STORAGE_PROP_DECORATOR, 61 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, 62 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 63 COMPONENTV2_LOCAL_DECORATOR, 64 COMPONENTV2_CONSUMER_DECORATOR, 65 COMPONENTV2_PROVIDER_DECORATOR 66} from './pre_define'; 67import { 68 stateCollection, 69 linkCollection, 70 propCollection, 71 regularCollection, 72 storagePropCollection, 73 storageLinkCollection, 74 provideCollection, 75 consumeCollection, 76 objectLinkCollection, 77 isStaticViewCollection, 78 builderParamObjectCollection, 79 getLocalStorageCollection, 80 builderParamInitialization, 81 propInitialization, 82 regularInitialization, 83 stateInitialization, 84 provideInitialization, 85 privateCollection, 86 regularStaticCollection, 87 componentCollection, 88 localStorageLinkCollection, 89 localStoragePropCollection 90} from './validate_ui_syntax'; 91import { 92 PropMapManager, 93 createViewCreate, 94 createCustomComponentNewExpression, 95 isLocalStorageParameter, 96 isBasicType 97} from './process_component_member'; 98import { 99 LogType, 100 LogInfo, 101 componentInfo, 102 storedFileInfo 103} from './utils'; 104import { 105 bindComponentAttr, 106 parentConditionalExpression, 107 createComponentCreationStatement, 108 createFunction, 109 ComponentAttrInfo, 110 ifRetakeId, 111 transferBuilderCall, 112 createCollectElmtIdNode, 113 createViewStackProcessorStatement, 114 BuilderParamsResult 115} from './process_component_build'; 116import { 117 partialUpdateConfig, 118 projectConfig, 119 globalProgram 120} from '../main'; 121import { 122 GLOBAL_CUSTOM_BUILDER_METHOD 123} from './component_map'; 124import { 125 createReference, 126 isProperty 127} from './process_component_class'; 128import processStructComponentV2, { StructInfo, ParamDecoratorInfo } from './process_struct_componentV2'; 129import constantDefine from './constant_define'; 130import createAstNodeUtils from './create_ast_node_utils'; 131import logMessageCollection from './log_message_collection'; 132 133let decoractorMap: Map<string, Map<string, Set<string>>>; 134 135export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 136 log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false, 137 idName: ts.Expression = undefined, builderParamsResult: BuilderParamsResult = null, 138 isInRepeatTemplate: boolean = false): void { 139 decoractorMap = new Map( 140 [[COMPONENT_STATE_DECORATOR, stateCollection], 141 [COMPONENT_LINK_DECORATOR, linkCollection], 142 [COMPONENT_PROP_DECORATOR, propCollection], 143 [COMPONENT_NON_DECORATOR, regularCollection], 144 [COMPONENT_PROVIDE_DECORATOR, provideCollection], 145 [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]); 146 const componentNode: ts.CallExpression = getCustomComponentNode(node); 147 if (componentNode) { 148 const parentComponentType: ParentType = componentCollection.currentClassName ? 149 getParentComponentType(componentCollection.currentClassName) : getParentComponentType(''); 150 const isRecycleComponent: boolean = isRecycle(name); 151 const isReuseComponentInV2: boolean = isReuseInV2(name); 152 logMessageCollection.checkNestedComponents(parentComponentType, isRecycleComponent, isReuseComponentInV2, node, log); 153 logMessageCollection.checkIfReuseV2InRepeatTemplate(isInRepeatTemplate, isReuseComponentInV2, node, log); 154 const hasChainCall: boolean = componentNode.parent && 155 ts.isPropertyAccessExpression(componentNode.parent); 156 let ischangeNode: boolean = false; 157 let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression( 158 componentNode, name, isBuilder, isGlobalBuilder); 159 let argumentsArray: ts.PropertyAssignment[]; 160 const componentAttrInfo: ComponentAttrInfo = { reuseId: null, hasIdAttr: false, attrCount: 0, reuse: '' }; 161 if (isHasChild(componentNode)) { 162 // @ts-ignore 163 argumentsArray = componentNode.arguments[0].properties.slice(); 164 argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => { 165 if (isToChange(item, name)) { 166 ischangeNode = true; 167 const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment( 168 item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression)); 169 argumentsArray.splice(index, 1, propertyAssignmentNode); 170 } 171 }); 172 if (ischangeNode) { 173 const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node, 174 ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments, 175 [ts.factory.createObjectLiteralExpression(argumentsArray, true)])); 176 customComponentNewExpression = createCustomComponentNewExpression( 177 newNode.expression as ts.CallExpression, name, isBuilder); 178 } 179 } 180 let judgeIdStart: number; 181 if (partialUpdateConfig.partialUpdateMode && idName) { 182 judgeIdStart = newStatements.length; 183 } 184 let needCommon: boolean = false; 185 if (hasChainCall) { 186 if (partialUpdateConfig.partialUpdateMode) { 187 const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement( 188 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 189 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))]; 190 const immutableStatements: ts.Statement[] = []; 191 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode, 192 log, true, false, immutableStatements, false, componentAttrInfo, isReuseComponentInV2); 193 needCommon = commomComponentNode.length > 1 || immutableStatements.length > 0; 194 if (componentAttrInfo.hasIdAttr && componentAttrInfo.attrCount === 1) { 195 commomComponentNode[0] = createCommonIdAttrNode(); 196 } 197 if (needCommon) { 198 newStatements.push(createComponentCreationStatement(componentAttributes(COMPONENT_COMMON), 199 commomComponentNode, COMPONENT_COMMON, isGlobalBuilder, false, undefined, immutableStatements, 200 builderParamsResult, isRecycleComponent)); 201 } 202 } else { 203 newStatements.push(ts.factory.createExpressionStatement( 204 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 205 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))); 206 bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log); 207 } 208 } 209 if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) { 210 newStatements.push(createRecycleComponent(isGlobalBuilder)); 211 } 212 addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode, 213 isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult, isReuseComponentInV2); 214 if (hasChainCall && (!partialUpdateConfig.partialUpdateMode || needCommon)) { 215 newStatements.push(ts.factory.createExpressionStatement( 216 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 217 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 218 } 219 if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) { 220 newStatements.push(componentAttributes(COMPONENT_RECYCLE)); 221 } 222 if (partialUpdateConfig.partialUpdateMode && idName) { 223 newStatements.splice(judgeIdStart, newStatements.length - judgeIdStart, 224 ifRetakeId(newStatements.slice(judgeIdStart), idName)); 225 } 226 } 227} 228 229function createCommonIdAttrNode(): ts.ExpressionStatement { 230 return ts.factory.createExpressionStatement( 231 createFunction(ts.factory.createIdentifier(COMPONENT_COMMON), 232 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), 233 // @ts-ignore 234 [ts.factory.createTrue()])); 235} 236 237export function isRecycle(componentName: string): boolean { 238 return storedFileInfo.getCurrentArkTsFile().recycleComponents.has(componentName); 239} 240 241export function isReuseInV2(componentName: string): boolean { 242 return storedFileInfo.getCurrentArkTsFile().reuseComponentsV2.has(componentName); 243} 244 245function createRecycleComponent(isGlobalBuilder: boolean): ts.Statement { 246 return createComponentCreationStatement(componentAttributes(COMPONENT_RECYCLE), 247 [ts.factory.createExpressionStatement( 248 createFunction(ts.factory.createIdentifier(COMPONENT_RECYCLE), 249 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)) 250 ], COMPONENT_RECYCLE, isGlobalBuilder); 251} 252 253function componentAttributes(componentName: string): ts.Statement { 254 return ts.factory.createExpressionStatement( 255 ts.factory.createCallExpression( 256 ts.factory.createPropertyAccessExpression( 257 ts.factory.createIdentifier(componentName), 258 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION) 259 ), undefined, [] 260 )); 261} 262 263function isHasChild(node: ts.CallExpression): boolean { 264 return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) && 265 node.arguments[0].properties && node.arguments[0].properties.length > 0; 266} 267 268function isToChange(item: ts.PropertyAssignment, name: string): boolean { 269 const builderParamName: Set<string> = builderParamObjectCollection.get(name); 270 if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName && 271 builderParamName.has(item.name.getText()) && 272 !/\.(bind|call|apply)/.test(item.initializer.getText())) { 273 return true; 274 } 275 return false; 276} 277 278function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ConditionalExpression { 279 let builderBindThis: ts.ExpressionStatement = ts.factory.createExpressionStatement(node); 280 if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) && 281 GLOBAL_CUSTOM_BUILDER_METHOD.has(node.expression.escapedText.toString())) { 282 builderBindThis = transferBuilderCall(ts.factory.createExpressionStatement(node), node.expression.escapedText.toString()); 283 } 284 return changeNodeFromCallToArrowDetermine(node, builderBindThis); 285} 286 287function changeNodeFromCallToArrowDetermine(node: ts.CallExpression, builderBindThis: ts.ExpressionStatement): ts.ConditionalExpression { 288 if (ts.isCallExpression(node)) { 289 return ts.factory.createConditionalExpression( 290 ts.factory.createBinaryExpression( 291 ts.factory.createTypeOfExpression(node), 292 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 293 ts.factory.createStringLiteral(FUNCTION) 294 ), 295 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 296 node, 297 ts.factory.createToken(ts.SyntaxKind.ColonToken), 298 ts.factory.createArrowFunction( 299 undefined, 300 undefined, 301 [], 302 undefined, 303 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 304 ts.factory.createBlock( 305 [builderBindThis], 306 true 307 ) 308 ) 309 ); 310 } 311 return undefined; 312} 313 314function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 315 newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression, 316 isBuilder: boolean, isGlobalBuilder: boolean, isRecycleComponent: boolean, 317 componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult, 318 isReuseComponentInV2: boolean): void { 319 if (ts.isNewExpression(newNode)) { 320 const propertyArray: ts.ObjectLiteralElementLike[] = []; 321 validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder); 322 addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode, 323 isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult, log, isReuseComponentInV2); 324 } 325} 326 327function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[], 328 newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[], 329 componentNode: ts.CallExpression, isBuilder: boolean, isGlobalBuilder: boolean, 330 isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo, 331 builderParamsResult: BuilderParamsResult, log: LogInfo[], isReuseComponentInV2: boolean): void { 332 if (!partialUpdateConfig.partialUpdateMode) { 333 const id: string = componentInfo.id.toString(); 334 newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id, 335 ts.factory.updateExpressionStatement(node, createViewCreate(newNode)), 336 ts.factory.createObjectLiteralExpression(props, true), name)); 337 } else { 338 newStatements.push(createCustomComponent(newNode, name, componentNode, isGlobalBuilder, isBuilder, 339 isRecycleComponent, componentAttrInfo, builderParamsResult, log, isReuseComponentInV2)); 340 } 341} 342 343function createChildElmtId(node: ts.CallExpression, name: string, log: LogInfo[]): ts.PropertyAssignment[] { 344 const childParam: ts.PropertyAssignment[] = []; 345 const propsAndObjectLinks: string[] = []; 346 if (propCollection.get(name)) { 347 propsAndObjectLinks.push(...propCollection.get(name)); 348 } 349 if (objectLinkCollection.get(name)) { 350 propsAndObjectLinks.push(...objectLinkCollection.get(name)); 351 } 352 if (projectConfig.optLazyForEach && storedFileInfo.processLazyForEach && stateCollection.get(name)) { 353 propsAndObjectLinks.push(...stateCollection.get(name)); 354 } 355 parseChildProperties(name, node, childParam, propsAndObjectLinks, log); 356 return childParam; 357} 358 359class ChildAndParentComponentInfo { 360 childStructInfo: StructInfo; 361 parentStructInfo: StructInfo; 362 paramDecoratorMap: Map<string, ParamDecoratorInfo>; 363 updatePropsDecoratorsV2: string[]; 364 propsAndObjectLinks: string[]; 365 childName: string; 366 forbiddenInitPropsV2: string[]; 367 updatePropsForV1Parent: string[]; 368 updatePropsForV2Parent: string[]; 369 constructor(childName: string, childNode: ts.CallExpression, propsAndObjectLinks: string[]) { 370 this.childName = childName; 371 this.propsAndObjectLinks = propsAndObjectLinks; 372 this.childStructInfo = processStructComponentV2.getAliasStructInfo(childNode) || 373 processStructComponentV2.getOrCreateStructInfo(childName); 374 this.paramDecoratorMap = this.childStructInfo.paramDecoratorMap; 375 this.updatePropsDecoratorsV2 = [...this.childStructInfo.eventDecoratorMap.keys(), ...this.paramDecoratorMap.keys()]; 376 this.parentStructInfo = componentCollection.currentClassName ? 377 processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) : 378 new StructInfo(); 379 this.forbiddenInitPropsV2 = [...this.childStructInfo.localDecoratorSet, 380 ...this.childStructInfo.providerDecoratorSet, ...this.childStructInfo.consumerDecoratorSet, 381 ...this.childStructInfo.regularSet]; 382 this.updatePropsForV1Parent = getUpdatePropsForV1Parent(); 383 this.updatePropsForV2Parent = [...this.parentStructInfo.localDecoratorSet, 384 ...this.parentStructInfo.paramDecoratorMap.keys(), ...this.parentStructInfo.providerDecoratorSet, 385 ...this.parentStructInfo.consumerDecoratorSet]; 386 } 387} 388 389function getUpdatePropsForV1Parent(): string[] { 390 const propertiesMapArr: Array<Map<string, Set<string>>> = [ 391 stateCollection, linkCollection, propCollection, 392 provideCollection, consumeCollection, objectLinkCollection, 393 storagePropCollection, storageLinkCollection 394 ]; 395 const updatePropsForParent: string[] = []; 396 if (componentCollection.currentClassName) { 397 const localStorageSet: Set<string> = new Set(); 398 getLocalStorageCollection(componentCollection.currentClassName, localStorageSet); 399 updatePropsForParent.push(...localStorageSet); 400 propertiesMapArr.forEach((item: Map<string, Set<string>>) => { 401 const value: Set<string> = item.get(componentCollection.currentClassName); 402 if (value) { 403 updatePropsForParent.push(...value); 404 } 405 }); 406 } 407 return updatePropsForParent; 408} 409 410function parseChildProperties(childName: string, node: ts.CallExpression, 411 childParam: ts.PropertyAssignment[], propsAndObjectLinks: string[], log: LogInfo[]): void { 412 const childAndParentComponentInfo: ChildAndParentComponentInfo = 413 new ChildAndParentComponentInfo(childName, node, propsAndObjectLinks); 414 if (node.arguments[0].properties) { 415 node.arguments[0].properties.forEach((item: ts.PropertyAssignment) => { 416 if (ts.isIdentifier(item.name)) { 417 const itemName: string = item.name.escapedText.toString(); 418 validateChildProperty(item, itemName, childParam, log, childAndParentComponentInfo); 419 } 420 }); 421 } 422} 423 424function getForbbidenInitPropsV2Type(itemName: string, info: ChildAndParentComponentInfo): string { 425 let typeName: string = COMPONENT_NON_DECORATOR; 426 if (info.childStructInfo.localDecoratorSet.has(itemName)) { 427 typeName = COMPONENTV2_LOCAL_DECORATOR; 428 } else if (info.childStructInfo.consumerDecoratorSet.has(itemName)) { 429 typeName = COMPONENTV2_CONSUMER_DECORATOR; 430 } else if (info.childStructInfo.providerDecoratorSet.has(itemName)) { 431 typeName = COMPONENTV2_PROVIDER_DECORATOR; 432 } 433 return typeName; 434} 435 436function validateChildProperty(item: ts.PropertyAssignment, itemName: string, 437 childParam: ts.PropertyAssignment[], log: LogInfo[], info: ChildAndParentComponentInfo): void { 438 if (info.childStructInfo.isComponentV2) { 439 if (info.forbiddenInitPropsV2.includes(itemName)) { 440 const propType: string = getForbbidenInitPropsV2Type(itemName, info); 441 log.push({ 442 type: LogType.ERROR, 443 message: `The '${propType}' property '${itemName}' in the custom component '${info.childName}'` + 444 ` cannot be initialized here (forbidden to specify).`, 445 pos: item.getStart(), 446 code: '10905324' 447 }); 448 return; 449 } 450 if (info.paramDecoratorMap.has(itemName)) { 451 childParam.push(item); 452 } 453 if (isForbiddenAssignToComponentV2(item, itemName, info)) { 454 log.push({ 455 type: LogType.ERROR, 456 message: `Property '${itemName}' in the '@ComponentV2' component '${info.childName}' is not allowed to be assigned value here.`, 457 pos: item.getStart(), 458 code: '10905323' 459 }); 460 } 461 } else { 462 if (info.propsAndObjectLinks.includes(itemName)) { 463 childParam.push(item); 464 } 465 } 466 if (isForbiddenAssignToComponentV1(item, itemName, info)) { 467 log.push({ 468 type: LogType.ERROR, 469 message: `Property '${itemName}' in the '@Component' component '${info.childName}' is not allowed to be assigned value here.`, 470 pos: item.getStart(), 471 code: '10905322' 472 }); 473 } 474 logMessageCollection.checkIfAssignToStaticProps(item, itemName, info.childStructInfo.staticPropertySet, log); 475} 476 477function isForbiddenAssignToComponentV1(item: ts.PropertyAssignment, itemName: string, 478 info: ChildAndParentComponentInfo): boolean { 479 if (info.parentStructInfo.isComponentV2 && info.childStructInfo.updatePropsDecoratorsV1.includes(itemName) && 480 isObervedProperty(item.initializer, info, false) && globalProgram.checker) { 481 const type: ts.Type = globalProgram.checker.getTypeAtLocation(item.initializer); 482 return isForbiddenTypeToComponentV1(type); 483 } 484 return false; 485} 486 487function isForbiddenTypeToComponentV1(type: ts.Type): boolean { 488 // @ts-ignore 489 if (type.types && type.types.length) { 490 // @ts-ignore 491 return !type.types.some((item: ts.Type) => { 492 return !isForbiddenTypeToComponentV1(item); 493 }); 494 } 495 const allowedTypes: string[] = ['Set', 'Map', 'Date', 'Array']; 496 const name: string = type?.getSymbol()?.getName(); 497 if (name && allowedTypes.includes(name)) { 498 return true; 499 } 500 return false; 501} 502 503function isForbiddenAssignToComponentV2(item: ts.PropertyAssignment, itemName: string, 504 info: ChildAndParentComponentInfo): boolean { 505 if (!info.parentStructInfo.isComponentV2 && info.updatePropsDecoratorsV2.includes(itemName) && 506 isObervedProperty(item.initializer, info) && globalProgram.strictChecker) { 507 const type: ts.Type = globalProgram.strictChecker.getTypeAtLocation(item.initializer); 508 return !isAllowedTypeToComponentV2(type); 509 } 510 return false; 511} 512 513function isObervedProperty(value: ts.Expression, info: ChildAndParentComponentInfo, 514 isV1Parent: boolean = true): boolean { 515 if (value && ts.isPropertyAccessExpression(value) && value.expression.kind === ts.SyntaxKind.ThisKeyword && 516 ts.isIdentifier(value.name)) { 517 const propertyName: string = value.name.escapedText.toString(); 518 return isV1Parent ? info.updatePropsForV1Parent.includes(propertyName) : 519 info.updatePropsForV2Parent.includes(propertyName); 520 } 521 return false; 522} 523 524function isAllowedTypeToComponentV2(type: ts.Type): boolean { 525 if (type) { 526 // @ts-ignore 527 if (type.types && type.types.length) { 528 // @ts-ignore 529 return type.types.some((item: ts.Type) => { 530 return isAllowedTypeToComponentV2(item); 531 }); 532 } 533 // string|number|boolean|enum|null|undefined 534 if (isAllowedTypeForBasic(type.flags)) { 535 return true; 536 } 537 } 538 return false; 539} 540 541function isAllowedTypeForBasic(flags: ts.TypeFlags): boolean { 542 if (isBasicType(flags) || (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined))) { 543 return true; 544 } 545 return false; 546} 547 548function validateInitParam(childName: string, curChildProps: Set<string>, 549 node: ts.CallExpression, log: LogInfo[], parentStructInfo: StructInfo): void { 550 const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(node) || 551 processStructComponentV2.getOrCreateStructInfo(childName); 552 const paramDecoratorMap: Map<string, ParamDecoratorInfo> = childStructInfo.paramDecoratorMap; 553 if (childStructInfo.isComponentV2) { 554 const needInitParam: string[] = []; 555 for (const item of paramDecoratorMap) { 556 if (item[1].hasRequire) { 557 needInitParam.push(item[0]); 558 } 559 } 560 needInitParam.forEach((paramName: string) => { 561 if (!curChildProps.has(paramName)) { 562 log.push({ 563 type: LogType.ERROR, 564 message: `'@Require' decorated '${paramName}' must be initialized through the component constructor.`, 565 pos: node.getStart(), 566 code: '10905321' 567 }); 568 } 569 }); 570 } else if (parentStructInfo.isComponentV2 && childStructInfo.linkDecoratorsV1.length) { 571 log.push({ 572 type: LogType.ERROR, 573 message: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`, 574 pos: node.getStart(), 575 code: '10905213' 576 }); 577 } 578} 579 580function createCustomComponent(newNode: ts.NewExpression, name: string, componentNode: ts.CallExpression, 581 isGlobalBuilder: boolean, isBuilder: boolean, isRecycleComponent: boolean, 582 componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult, log: LogInfo[], 583 isReuseComponentInV2:boolean): ts.Block { 584 let componentParameter: ts.ObjectLiteralExpression; 585 if (componentNode.arguments && componentNode.arguments.length) { 586 componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name, log), true); 587 } else { 588 componentParameter = ts.factory.createObjectLiteralExpression([], false); 589 } 590 const arrowArgArr: ts.ParameterDeclaration[] = [ 591 ts.factory.createParameterDeclaration(undefined, undefined, 592 ts.factory.createIdentifier(ELMTID) 593 ), 594 ts.factory.createParameterDeclaration(undefined, undefined, 595 ts.factory.createIdentifier(ISINITIALRENDER) 596 ) 597 ]; 598 const arrowBolck: ts.Statement[] = [ 599 projectConfig.optLazyForEach && storedFileInfo.processLazyForEach ? createCollectElmtIdNode() : undefined, 600 createIfCustomComponent(newNode, componentNode, componentParameter, name, isGlobalBuilder, 601 isBuilder, isRecycleComponent, componentAttrInfo, log) 602 ]; 603 if (isRecycleComponent) { 604 arrowArgArr.push(ts.factory.createParameterDeclaration( 605 undefined, undefined, ts.factory.createIdentifier(RECYCLE_NODE), 606 undefined, undefined, ts.factory.createNull() 607 )); 608 } else if (partialUpdateConfig.optimizeComponent && isGlobalBuilder && 609 builderParamsResult && builderParamsResult.firstParam) { 610 const paramName: ts.Identifier = builderParamsResult.firstParam.name as ts.Identifier; 611 arrowArgArr.push(ts.factory.createParameterDeclaration(undefined, undefined, 612 paramName, undefined, undefined, ts.factory.createIdentifier(`__${paramName.escapedText.toString()}__`) 613 )); 614 } 615 if (isRecycleComponent || !partialUpdateConfig.optimizeComponent) { 616 arrowBolck.unshift(createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID)); 617 arrowBolck.push(createViewStackProcessorStatement(STOPGETACCESSRECORDING)); 618 } 619 const observeArgArr: ts.Node[] = [ 620 ts.factory.createArrowFunction(undefined, undefined, arrowArgArr, undefined, 621 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 622 ts.factory.createBlock(arrowBolck, true)) 623 ]; 624 if (isRecycleComponent) { 625 componentAttrInfo.reuseId ? observeArgArr.unshift(componentAttrInfo.reuseId) : 626 observeArgArr.unshift(ts.factory.createStringLiteral(name)); 627 } else if (partialUpdateConfig.optimizeComponent) { 628 observeArgArr.push(componentPop(name)); 629 } 630 const reuseOrCreateArgArr: ts.ObjectLiteralExpression[] = [ts.factory.createObjectLiteralExpression( 631 generateReuseOrCreateArgArr(componentNode, componentAttrInfo, name, newNode), true)]; 632 return ts.factory.createBlock( 633 [ 634 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 635 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 636 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 637 isRecycleComponent ? 638 ts.factory.createIdentifier(OBSERVE_RECYCLE_COMPONENT_CREATION) : 639 isReuseComponentInV2 ? ts.factory.createIdentifier(constantDefine.REUSE_OR_CREATE_METHOD) : 640 ts.factory.createIdentifier(partialUpdateConfig.optimizeComponent ? 641 OBSERVECOMPONENTCREATION2 : OBSERVECOMPONENTCREATION) 642 ), 643 undefined, isReuseComponentInV2 ? reuseOrCreateArgArr as ts.Expression[] : observeArgArr as ts.Expression[])) 644 ], 645 true 646 ); 647} 648 649function generateReuseOrCreateArgArr(componentNode: ts.CallExpression, componentAttrInfo: ComponentAttrInfo, 650 name: string, newNode: ts.NewExpression): ts.ObjectLiteralElementLike[] { 651 const reuseParamsArr: ts.ObjectLiteralElementLike[] = []; 652 if (componentNode.expression && ts.isIdentifier(componentNode.expression)) { 653 reuseParamsArr.push(ts.factory.createPropertyAssignment( 654 ts.factory.createIdentifier(constantDefine.COMPONENT_CLASS), 655 componentNode.expression 656 )); 657 } 658 reuseParamsArr.push(createReuseParameterArrowFunction(getArgumentsToPass(componentNode), constantDefine.GET_PARAMS, name)); 659 reuseParamsArr.push(createReuseParameterArrowFunction( 660 [], constantDefine.GET_REUSE_ID, componentAttrInfo.reuse ? componentAttrInfo.reuse : name)); 661 if (newNode.arguments.length >= 6 && ts.isObjectLiteralExpression(newNode.arguments[5])) { 662 reuseParamsArr.push(generateExtraInfo(true, newNode.arguments[5])); 663 } else { 664 reuseParamsArr.push(generateExtraInfo(false)); 665 } 666 return reuseParamsArr; 667} 668 669function getArgumentsToPass(componentNode: ts.CallExpression): ts.NodeArray<ts.ObjectLiteralElementLike> | undefined[] { 670 if (componentNode.arguments && componentNode.arguments.length === 1 && 671 ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties && 672 componentNode.arguments[0].properties.length) { 673 return componentNode.arguments[0].properties; 674 } 675 return []; 676} 677 678function createReuseParameterArrowFunction(propertyArray: ts.NodeArray<ts.ObjectLiteralElementLike> | undefined[], 679 identifierName: string, name: string): ts.PropertyAssignment { 680 return ts.factory.createPropertyAssignment( 681 ts.factory.createIdentifier(identifierName), 682 ts.factory.createArrowFunction( 683 undefined, 684 undefined, 685 [], 686 undefined, 687 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 688 identifierName === constantDefine.GET_REUSE_ID ? 689 ts.factory.createStringLiteral(name) : 690 ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression( 691 propertyArray.length ? propertyArray : [], 692 true 693 )) 694 ) 695 ); 696} 697 698function generateExtraInfo(hasPositionInfo: boolean, positionInfo?: ts.ObjectLiteralExpression): ts.PropertyAssignment { 699 return ts.factory.createPropertyAssignment( 700 ts.factory.createIdentifier(constantDefine.EXTRA_INFO), 701 hasPositionInfo ? 702 positionInfo : 703 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 704 ); 705} 706 707function componentPop(name: string): ts.ObjectLiteralExpression { 708 return ts.factory.createObjectLiteralExpression( 709 [ts.factory.createPropertyAssignment( 710 ts.factory.createIdentifier(NAME), 711 ts.factory.createStringLiteral(name) 712 )], 713 false 714 ); 715} 716 717export function assignComponentParams(componentNode: ts.CallExpression, 718 isBuilder: boolean = false): ts.VariableStatement { 719 const isParamsLambda: boolean = true; 720 const [keyArray, valueArray]: [ts.Node[], ts.Node[]] = splitComponentParams(componentNode, isBuilder, isParamsLambda); 721 let integrateParams: boolean = false; 722 if (!keyArray.length && componentNode.arguments && componentNode.arguments.length > 0 && componentNode.arguments[0]) { 723 integrateParams = true; 724 } 725 return ts.factory.createVariableStatement( 726 undefined, 727 ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration( 728 ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION), 729 undefined, 730 undefined, 731 ts.factory.createArrowFunction( 732 undefined, 733 undefined, 734 [], 735 undefined, 736 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 737 ts.factory.createBlock( 738 [ts.factory.createReturnStatement( 739 integrateParams ? paramsLambdaCallBack(componentNode) : ts.factory.createObjectLiteralExpression( 740 reWriteComponentParams(keyArray, valueArray), 741 true 742 ) 743 )], 744 true 745 ) 746 ) 747 )], 748 ts.NodeFlags.Let 749 )); 750} 751 752function paramsLambdaCallBack(componentNode: ts.CallExpression): ts.Expression { 753 if (partialUpdateConfig.partialUpdateMode && componentNode.arguments.length === 1 && 754 isLocalStorageParameter(componentNode)) { 755 return ts.factory.createObjectLiteralExpression([], true); 756 } else { 757 return componentNode.arguments[0]; 758 } 759} 760 761function reWriteComponentParams(keyArray: ts.Node[], valueArray: ts.Node[]): (ts.PropertyAssignment | 762 ts.ShorthandPropertyAssignment)[] { 763 const returnProperties: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = []; 764 keyArray.forEach((item: ts.Identifier, index: number) => { 765 if (!valueArray[index]) { 766 returnProperties.push(ts.factory.createShorthandPropertyAssignment( 767 item, 768 undefined 769 )); 770 } else { 771 returnProperties.push(ts.factory.createPropertyAssignment( 772 item, 773 valueArray[index] as ts.Identifier 774 )); 775 } 776 }); 777 return returnProperties; 778} 779 780function splitComponentParams(componentNode: ts.CallExpression, isBuilder: boolean, isParamsLambda: boolean): [ts.Node[], ts.Node[]] { 781 const keyArray: ts.Node[] = []; 782 const valueArray: ts.Node[] = []; 783 if (componentNode.arguments && componentNode.arguments.length > 0 && 784 ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) { 785 componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => { 786 const newPropertyItem: ts.PropertyAssignment = 787 isProperty(propertyItem) ? createReference(propertyItem, [], isBuilder, isParamsLambda) : propertyItem; 788 keyArray.push(newPropertyItem.name); 789 valueArray.push(newPropertyItem.initializer); 790 }); 791 } 792 return [keyArray, valueArray]; 793} 794 795function createIfCustomComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression, 796 componentParameter: ts.ObjectLiteralExpression, name: string, isGlobalBuilder: boolean, isBuilder: boolean, 797 isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo, log: LogInfo[]): ts.IfStatement { 798 return ts.factory.createIfStatement( 799 ts.factory.createIdentifier(ISINITIALRENDER), 800 ts.factory.createBlock( 801 [componentParamDetachment(newNode, isRecycleComponent, name, log, componentNode), 802 isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) : 803 createNewComponent(COMPONENT_CALL, name, componentNode), 804 assignComponentParams(componentNode, isBuilder), 805 assignmentFunction(COMPONENT_CALL) 806 ], true), 807 ts.factory.createBlock( 808 [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 809 ts.factory.createPropertyAccessExpression(isGlobalBuilder ? 810 ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(), 811 ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID) 812 ), undefined, 813 [ts.factory.createIdentifier(ELMTID), componentParameter]))], true) 814 ); 815} 816 817export function assignmentFunction(componeParamName: string): ts.ExpressionStatement { 818 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 819 ts.factory.createPropertyAccessExpression( 820 ts.factory.createIdentifier(componeParamName), 821 ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION) 822 ), 823 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 824 ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION) 825 )); 826} 827 828function traverseChildComponentArgs(childParam: ts.Expression[], name: string, log: LogInfo[], 829 componentNode: ts.CallExpression): ts.Expression[] { 830 const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) || 831 processStructComponentV2.getOrCreateStructInfo(name); 832 if (!childStructInfo.isComponentV2) { 833 return childParam; 834 } 835 const objectLiteralIndex: number = 2; 836 if (childParam.length > objectLiteralIndex && ts.isObjectLiteralExpression(childParam[1]) && 837 childParam[1].properties) { 838 const newProperties: ts.PropertyAssignment[] = []; 839 childParam[1].properties.forEach((item: ts.PropertyAssignment) => { 840 if (item.name && ts.isIdentifier(item.name)) { 841 const itemName: string = item.name.escapedText.toString(); 842 updatePropertyAssignment(newProperties, itemName, item, childStructInfo, log); 843 } 844 }); 845 if (newProperties.length) { 846 return getNewArgsForCustomComponent(childParam, newProperties); 847 } 848 } 849 return childParam; 850} 851 852function getNewArgsForCustomComponent(childParam: ts.Expression[], 853 newProperties: ts.PropertyAssignment[]): ts.Expression[] { 854 const newArr: ts.Expression[] = []; 855 const newObjectLiteralNode: ts.ObjectLiteralExpression = 856 ts.factory.updateObjectLiteralExpression(childParam[1], [...childParam[1].properties, ...newProperties]); 857 childParam.forEach((item: ts.Expression, index: number) => { 858 if (index === 1) { 859 newArr.push(newObjectLiteralNode); 860 } else { 861 newArr.push(item); 862 } 863 }); 864 return newArr; 865} 866 867function updatePropertyAssignment(newProperties: ts.PropertyAssignment[], 868 itemName: string, item: ts.PropertyAssignment, childStructInfo: StructInfo, log: LogInfo[]): void { 869 if (isDoubleNonNullExpression(item.initializer)) { 870 if (isLeftHandExpression(item.initializer.expression.expression)) { 871 const result: Record<string, boolean> = { hasQuestionToken: false }; 872 traverseExpressionNode(item.initializer.expression.expression, result); 873 if (result.hasQuestionToken) { 874 log.push({ 875 type: LogType.ERROR, 876 message: `The optional character can not be used in the initial value of property '${itemName}'.`, 877 pos: item.getStart(), 878 code: '10905320' 879 }); 880 return; 881 } 882 if (childStructInfo.paramDecoratorMap.has(itemName) && 883 childStructInfo.eventDecoratorMap.has('$' + itemName)) { 884 newProperties.push(createUpdateTwoWayNode(itemName, item.initializer.expression.expression)); 885 return; 886 } 887 log.push({ 888 type: LogType.ERROR, 889 message: 'When the two-way binding syntax is used, ' + 890 `the variable '${itemName}' must be decorated with '@Param', ` + 891 `and the '@Event' variable '$` + `${itemName}' ` + `must be defined in the ${childStructInfo.structName}.`, 892 pos: item.getStart(), 893 code: '10905319' 894 }); 895 return; 896 } 897 log.push({ 898 type: LogType.ERROR, 899 message: 'When the two-way binding syntax is used, ' + 900 `the initial value of property '${itemName}' must be a variable.`, 901 pos: item.getStart(), 902 code: '10905318' 903 }); 904 return; 905 } 906} 907 908function validateTwoWayComputed(node: ts.PropertyAccessExpression, log: LogInfo[]): void { 909 if (ts.isPropertyAccessExpression(node) && node.expression && 910 node.expression.kind === ts.SyntaxKind.ThisKeyword && globalProgram.checker) { 911 const symbol: ts.Symbol = globalProgram.checker.getSymbolAtLocation(node); 912 logMessageCollection.checkTwoWayComputed(node, symbol, log); 913 } 914} 915 916function createUpdateTwoWayNode(itemName: string, leftHandExpression: ts.Expression): ts.PropertyAssignment { 917 return ts.factory.createPropertyAssignment( 918 ts.factory.createIdentifier('$' + itemName), 919 ts.factory.createArrowFunction(undefined, undefined, 920 [createAstNodeUtils.createParameterDeclaration('value')], undefined, 921 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 922 ts.factory.createBlock([ 923 ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 924 leftHandExpression, ts.factory.createToken(ts.SyntaxKind.EqualsToken), 925 ts.factory.createIdentifier('value') 926 )) 927 ], false) 928 ) 929 ); 930} 931 932function isDoubleNonNullExpression(node: ts.Expression): boolean { 933 return node && ts.isNonNullExpression(node) && ts.isNonNullExpression(node.expression); 934} 935 936function isLeftHandExpression(node: ts.Expression): boolean { 937 return node && (ts.isIdentifier(node) || ts.isPropertyAccessExpression(node)); 938} 939 940function traverseExpressionNode(node: ts.Node, result: Record<string, boolean>): void { 941 if (ts.isOptionalChain(node) && !ts.isNonNullExpression(node) && node.questionDotToken) { 942 result.hasQuestionToken = true; 943 } 944 if (!result.hasQuestionToken) { 945 ts.forEachChild(node, (child: ts.Node) => traverseExpressionNode(child, result)); 946 } 947} 948 949function componentParamDetachment(newNode: ts.NewExpression, isRecycleComponent: boolean, 950 name: string, log: LogInfo[], componentNode: ts.CallExpression): ts.VariableStatement { 951 const paramsArray: ts.Expression[] = newNode.arguments.length ? newNode.arguments : []; 952 const updateNewNode = ts.factory.updateNewExpression(newNode, newNode.expression, 953 newNode.typeArguments, traverseChildComponentArgs(paramsArray, name, log, componentNode)); 954 return ts.factory.createVariableStatement( 955 undefined, 956 ts.factory.createVariableDeclarationList( 957 [ts.factory.createVariableDeclaration( 958 ts.factory.createIdentifier(COMPONENT_CALL), 959 undefined, 960 undefined, 961 isRecycleComponent ? ts.factory.createConditionalExpression( 962 ts.factory.createIdentifier(RECYCLE_NODE), 963 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 964 ts.factory.createIdentifier(RECYCLE_NODE), 965 ts.factory.createToken(ts.SyntaxKind.ColonToken), 966 newNode) : updateNewNode 967 )], 968 ts.NodeFlags.Let 969 )); 970} 971 972function createNewComponent(componeParamName: string, name: string, 973 componentNode: ts.CallExpression): ts.Statement { 974 const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) || 975 processStructComponentV2.getOrCreateStructInfo(name); 976 return ts.factory.createExpressionStatement( 977 ts.factory.createCallExpression( 978 ts.factory.createPropertyAccessExpression( 979 ts.factory.createIdentifier( 980 childStructInfo.isComponentV2 ? constantDefine.STRUCT_PARENT : BASE_COMPONENT_NAME_PU), 981 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION) 982 ), undefined, [ts.factory.createIdentifier(componeParamName)])); 983} 984 985function createNewRecycleComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression, 986 name: string, componentAttrInfo: ComponentAttrInfo): ts.Statement { 987 let argNode: ts.Expression[] = []; 988 const componentParam: ts.PropertyAssignment[] = []; 989 if (componentNode.arguments && componentNode.arguments.length > 0 && 990 ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) { 991 componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => { 992 const newPropertyItem: ts.PropertyAssignment = isProperty(propertyItem) ? 993 createReference(propertyItem, [], false, false, true) : propertyItem; 994 componentParam.push(newPropertyItem); 995 }); 996 argNode = [ts.factory.createObjectLiteralExpression(componentParam, false)]; 997 } else { 998 argNode = [ts.factory.createObjectLiteralExpression([], false)]; 999 } 1000 const recycleNode: ts.CallExpression = ts.factory.createCallExpression( 1001 createRecyclePropertyNode(ABOUT_TO_REUSE), undefined, argNode); 1002 return ts.factory.createExpressionStatement( 1003 ts.factory.createCallExpression( 1004 ts.factory.createPropertyAccessExpression( 1005 ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), 1006 ts.factory.createIdentifier(COMPONENT_CREATE_RECYCLE) 1007 ), undefined, 1008 [ 1009 ts.factory.createIdentifier(COMPONENT_CALL), 1010 ts.factory.createBinaryExpression( 1011 ts.factory.createIdentifier(RECYCLE_NODE), 1012 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), 1013 ts.factory.createNull() 1014 ), 1015 componentAttrInfo.reuseId ? componentAttrInfo.reuseId as ts.Expression : 1016 ts.factory.createStringLiteral(name), 1017 ts.factory.createArrowFunction(undefined, undefined, [], undefined, 1018 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 1019 ts.factory.createBlock([ 1020 ts.factory.createIfStatement( 1021 ts.factory.createBinaryExpression( 1022 ts.factory.createIdentifier(RECYCLE_NODE), 1023 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1024 ts.factory.createBinaryExpression( 1025 ts.factory.createTypeOfExpression( 1026 createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION)), 1027 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 1028 ts.factory.createStringLiteral(FUNCTION) 1029 )), 1030 ts.factory.createBlock([ 1031 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1032 createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION), 1033 undefined, 1034 [] 1035 )) 1036 ], true), 1037 ts.factory.createBlock( 1038 [ 1039 ts.factory.createIfStatement(ts.factory.createBinaryExpression( 1040 createRecyclePropertyNode(ABOUT_TO_REUSE), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1041 ts.factory.createBinaryExpression( 1042 ts.factory.createTypeOfExpression(createRecyclePropertyNode(ABOUT_TO_REUSE)), 1043 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), 1044 ts.factory.createStringLiteral(FUNCTION) 1045 )), 1046 ts.factory.createBlock([ts.factory.createExpressionStatement(recycleNode)], true)), 1047 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1048 createRecyclePropertyNode(COMPONENT_RERENDER_FUNCTION), undefined, [] 1049 )) 1050 ], 1051 true 1052 ) 1053 )], true)) 1054 ])); 1055} 1056 1057function createRecyclePropertyNode(recycleFunctionName: string): ts.PropertyAccessExpression { 1058 return ts.factory.createPropertyAccessExpression( 1059 ts.factory.createIdentifier(RECYCLE_NODE), ts.factory.createIdentifier(recycleFunctionName)); 1060} 1061 1062function validateCustomComponentPrams(node: ts.CallExpression, name: string, 1063 props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void { 1064 const curChildProps: Set<string> = new Set([]); 1065 const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments; 1066 const linkSet: Set<string> = getCollectionSet(name, linkCollection); 1067 if (nodeArguments && nodeArguments.length >= 1 && 1068 ts.isObjectLiteralExpression(nodeArguments[0])) { 1069 const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression; 1070 const doubleExclamationCollection: string[] = []; 1071 const dollarPropertyCollection: string[] = []; 1072 nodeArgument.properties.forEach(item => { 1073 if (item.name && ts.isIdentifier(item.name)) { 1074 const propName: string = item.name.escapedText.toString(); 1075 curChildProps.add(propName); 1076 logMessageCollection.checkIfAssignToStaticProps(item, propName, regularStaticCollection.get(name) || new Set(), log); 1077 } 1078 validateStateManagement(item, name, log, isBuilder, doubleExclamationCollection, dollarPropertyCollection); 1079 if (isNonThisProperty(item, linkSet)) { 1080 if (isToChange(item as ts.PropertyAssignment, name)) { 1081 item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment, 1082 item.name, changeNodeFromCallToArrow(item.initializer)); 1083 } 1084 props.push(item); 1085 } 1086 }); 1087 logMessageCollection.checkIfNeedDollarEvent(doubleExclamationCollection, dollarPropertyCollection, node, log); 1088 } 1089 const parentStructInfo: StructInfo = componentCollection.currentClassName ? 1090 processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) : 1091 new StructInfo(); 1092 validateInitDecorator(node, name, curChildProps, log); 1093 validateMandatoryToInitViaParam(node, name, curChildProps, log, parentStructInfo); 1094 validateInitParam(name, curChildProps, node, log, parentStructInfo); 1095} 1096 1097function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression { 1098 let temp: any = node.expression; 1099 let child: any = null; 1100 let componentNode: any = null; 1101 while (temp) { 1102 if (ts.isIdentifier(temp)) { 1103 child = temp; 1104 break; 1105 } 1106 temp = temp.expression; 1107 } 1108 if (child) { 1109 let parent = child.parent; 1110 while (parent) { 1111 if (ts.isExpressionStatement(parent)) { 1112 break; 1113 } 1114 if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) { 1115 componentNode = parent; 1116 break; 1117 } 1118 parent = parent.parent; 1119 } 1120 } 1121 return componentNode; 1122} 1123 1124export enum ParentType { 1125 NormalComponentV1, 1126 NormalComponentV2, 1127 ReuseComponentV1, 1128 ReuseComponentV2, 1129 InvalidComponentType 1130} 1131 1132function getParentComponentType(parentName: string): ParentType { 1133 const parentCustomComponentInfo: StructInfo = parentName ? 1134 processStructComponentV2.getOrCreateStructInfo(parentName) : new StructInfo(); 1135 let type: ParentType = ParentType.InvalidComponentType; 1136 if (parentCustomComponentInfo.isComponentV1 && 1137 parentCustomComponentInfo.isReusable) { 1138 type = ParentType.ReuseComponentV1; 1139 } else if (parentCustomComponentInfo.isComponentV1 && 1140 !parentCustomComponentInfo.isReusable) { 1141 type = ParentType.NormalComponentV1; 1142 } else if (parentCustomComponentInfo.isComponentV2 && 1143 parentCustomComponentInfo.isReusableV2) { 1144 type = ParentType.ReuseComponentV2; 1145 } else if (parentCustomComponentInfo.isComponentV2 && 1146 !parentCustomComponentInfo.isReusableV2) { 1147 type = ParentType.NormalComponentV2; 1148 } 1149 return type; 1150} 1151 1152function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> { 1153 if (!collection) { 1154 return new Set([]); 1155 } 1156 return collection.get(name) || new Set([]); 1157} 1158 1159function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 1160 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 1161 propertySet.has(node.name.escapedText.toString())) { 1162 return true; 1163 } 1164 return false; 1165} 1166 1167function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean { 1168 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 1169 (node.initializer.escapedText && node.initializer.escapedText.includes('$') || 1170 ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 1171 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 1172 ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) { 1173 return false; 1174 } 1175 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 1176 !propertySet.has(node.name.escapedText.toString())) { 1177 return true; 1178 } 1179 return false; 1180} 1181 1182function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string, log: LogInfo[], 1183 isBuilder: boolean, doubleExclamationCollection: string[], dollarPropertyCollection: string[]): void { 1184 validateForbiddenToInitViaParam(node, customComponentName, log); 1185 if (componentCollection.currentClassName) { 1186 const parentStructInfo: StructInfo = 1187 processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName); 1188 if (parentStructInfo.isComponentV2) { 1189 if (ts.isPropertyAssignment(node)) { 1190 checkDoubleExclamationPropertyAssignment(node, log, doubleExclamationCollection); 1191 checkDollarPropertyAssignment(node, dollarPropertyCollection); 1192 } 1193 return; 1194 } 1195 } 1196 checkFromParentToChild(node, customComponentName, log, isBuilder); 1197} 1198 1199function checkDoubleExclamationPropertyAssignment(node: ts.PropertyAssignment, log: LogInfo[], doubleExclamationCollection: string[]): void { 1200 if (node.initializer && isDoubleNonNullExpression(node.initializer)) { 1201 if (node.initializer.expression && node.initializer.expression.expression && 1202 isLeftHandExpression(node.initializer.expression.expression)) { 1203 doubleExclamationCollection.push(node.name.getText()); 1204 validateTwoWayComputed(node.initializer.expression.expression, log); 1205 } 1206 } 1207} 1208 1209function checkDollarPropertyAssignment(node: ts.PropertyAssignment, dollarPropertyCollection: string[]): void { 1210 const regex = /^\$[a-zA-Z_][a-zA-Z0-9_]*$/; 1211 if (node.name && ts.isIdentifier(node.name) && regex.test(node.name.escapedText.toString())) { 1212 dollarPropertyCollection.push(node.name.escapedText.toString()); 1213 } 1214} 1215 1216function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string, 1217 log: LogInfo[], isBuilder: boolean): void { 1218 let propertyName: string; 1219 if (ts.isIdentifier(node.name)) { 1220 propertyName = node.name.escapedText.toString(); 1221 } 1222 const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName); 1223 let parentPropertyName: string; 1224 if (curPropertyKind) { 1225 if (isInitFromParent(node)) { 1226 parentPropertyName = 1227 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log); 1228 let parentPropertyKind: string = PropMapManager.find(parentPropertyName); 1229 if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 1230 validateIllegalInitFromParent( 1231 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log); 1232 } 1233 } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) && 1234 curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) { 1235 if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) { 1236 validateIllegalInitFromParent(node, propertyName, curPropertyKind, 1237 node.initializer.getText(), COMPONENT_NON_DECORATOR, log); 1238 } 1239 } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer && 1240 (ts.isPropertyAccessExpression(node.initializer) || 1241 ts.isElementAccessExpression(node.initializer) || ts.isIdentifier(node.initializer))) { 1242 return; 1243 } else { 1244 parentPropertyName = 1245 getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName; 1246 const parentPropertyKind = COMPONENT_NON_DECORATOR; 1247 if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) { 1248 if (isBuilder && judgeStructAssignedDollar(node)) { 1249 log.push({ 1250 type: LogType.WARN, 1251 message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` + 1252 `${curPropertyKind} property '${propertyName}' by yourself.`, 1253 // @ts-ignore 1254 pos: node.initializer ? node.initializer.getStart() : node.getStart() 1255 }); 1256 } else { 1257 validateIllegalInitFromParent( 1258 node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN); 1259 } 1260 } 1261 } 1262 } 1263} 1264 1265function judgeStructAssignedDollar(node: ts.ObjectLiteralElementLike): boolean { 1266 return partialUpdateConfig.partialUpdateMode && node.initializer && 1267 ts.isPropertyAccessExpression(node.initializer) && 1268 node.initializer.expression && ts.isIdentifier(node.initializer.expression) && 1269 node.initializer.expression.escapedText.toString() === $$; 1270} 1271 1272function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean { 1273 if (ts.isPropertyAssignment(node) && node.initializer) { 1274 if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression && 1275 node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword && 1276 ts.isIdentifier(node.initializer.name)) { 1277 return true; 1278 } else if (ts.isIdentifier(node.initializer) && 1279 matchStartWithDollar(node.initializer.getText())) { 1280 return true; 1281 } 1282 } 1283 return false; 1284} 1285 1286function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean { 1287 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) && 1288 !matchStartWithDollar(node.initializer.getText())) { 1289 return true; 1290 } 1291 return false; 1292} 1293 1294function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string, 1295 log: LogInfo[]): string { 1296 const initExpression: ts.Expression = node.initializer; 1297 if (!initExpression) { 1298 return undefined; 1299 } 1300 let parentPropertyName: string = initExpression.getText(); 1301 const symbol = globalProgram.checker?.getSymbolAtLocation(initExpression); 1302 if (curPropertyKind === COMPONENT_LINK_DECORATOR) { 1303 // @ts-ignore 1304 const initName: ts.Identifier = initExpression.name || initExpression; 1305 if (!symbol && hasDollar(initExpression)) { 1306 parentPropertyName = initName.getText().replace(/^\$/, ''); 1307 } else { 1308 parentPropertyName = initName.getText(); 1309 } 1310 } else { 1311 if (!symbol && hasDollar(initExpression)) { 1312 validateNonLinkWithDollar(node, log); 1313 } 1314 // @ts-ignore 1315 if (node.initializer && node.initializer.name) { 1316 parentPropertyName = node.initializer.name.getText(); 1317 } 1318 } 1319 1320 return parentPropertyName; 1321} 1322 1323function isCorrectInitFormParent(parent: string, child: string): boolean { 1324 switch (child) { 1325 case COMPONENT_STATE_DECORATOR: 1326 case COMPONENT_PROP_DECORATOR: 1327 case COMPONENT_PROVIDE_DECORATOR: 1328 return true; 1329 case COMPONENT_NON_DECORATOR: 1330 if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR, 1331 COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) { 1332 return true; 1333 } 1334 break; 1335 case COMPONENT_LINK_DECORATOR: 1336 case COMPONENT_OBJECT_LINK_DECORATOR: 1337 return ![COMPONENT_NON_DECORATOR].includes(parent); 1338 } 1339 return false; 1340} 1341 1342function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string { 1343 for (const item of decoractorMap.entries()) { 1344 if (getCollectionSet(customComponentName, item[1]).has(propertyName)) { 1345 return item[0]; 1346 } 1347 } 1348 return undefined; 1349} 1350 1351function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement { 1352 return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList( 1353 [ts.factory.createVariableDeclaration(ts.factory.createIdentifier( 1354 `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode( 1355 ts.factory.createIdentifier(name)), 1356 ts.factory.createConditionalExpression( 1357 ts.factory.createParenthesizedExpression( 1358 ts.factory.createBinaryExpression( 1359 createConditionParent(isBuilder), 1360 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1361 ts.factory.createPropertyAccessExpression( 1362 createConditionParent(isBuilder), 1363 ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID) 1364 ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken), 1365 ts.factory.createAsExpression(ts.factory.createCallExpression( 1366 ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder), 1367 ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined, 1368 [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID), 1369 undefined, []) : ts.factory.createStringLiteral(id)]), 1370 ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))), 1371 ts.factory.createToken(ts.SyntaxKind.ColonToken), 1372 ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)))], ts.NodeFlags.Let)); 1373} 1374 1375export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression { 1376 return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(); 1377} 1378 1379function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement, 1380 newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement { 1381 const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`; 1382 return ts.factory.createIfStatement(ts.factory.createBinaryExpression( 1383 ts.factory.createIdentifier(viewName), 1384 ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken), 1385 ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)), 1386 ts.factory.createBlock([node], true), 1387 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1388 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier( 1389 viewName), ts.factory.createIdentifier( 1390 `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])), 1391 isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined, 1392 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1393 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`), 1394 ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined, 1395 [ts.factory.createIdentifier(viewName)]))], true)); 1396} 1397 1398function createStaticIf(name: string): ts.IfStatement { 1399 return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression( 1400 ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression( 1401 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 1402 ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])), 1403 ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1404 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 1405 ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)), 1406 undefined, []))], true), undefined); 1407} 1408 1409function hasDollar(initExpression: ts.Expression): boolean { 1410 if (ts.isPropertyAccessExpression(initExpression) && 1411 matchStartWithDollar(initExpression.name.getText())) { 1412 return true; 1413 } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) { 1414 return true; 1415 } else { 1416 return false; 1417 } 1418} 1419 1420function matchStartWithDollar(name: string): boolean { 1421 return /^\$/.test(name); 1422} 1423 1424function getForbbiddenToInitViaParamType(customComponentName: string, 1425 node: ts.Identifier): string { 1426 let propType: string = COMPONENT_CONSUME_DECORATOR; 1427 const propName: string = node.escapedText.toString(); 1428 if (getCollectionSet(customComponentName, storageLinkCollection).has(propName)) { 1429 propType = COMPONENT_STORAGE_LINK_DECORATOR; 1430 } else if (getCollectionSet(customComponentName, storagePropCollection)) { 1431 propType = COMPONENT_STORAGE_PROP_DECORATOR; 1432 } else if (ifLocalStorageLink(customComponentName, propName)) { 1433 propType = COMPONENT_LOCAL_STORAGE_LINK_DECORATOR; 1434 } else if (ifLocalStorageProp(customComponentName, propName)) { 1435 propType = COMPONENT_LOCAL_STORAGE_PROP_DECORATOR; 1436 } 1437 return propType; 1438} 1439 1440function ifLocalStorageProp(componentName: string, propName: string): boolean { 1441 if (!localStoragePropCollection.get(componentName).keys) { 1442 return false; 1443 } else { 1444 const collection: Set<string> = new Set(); 1445 for (const key of localStoragePropCollection.get(componentName).keys()) { 1446 collection.add(key); 1447 } 1448 return collection.has(propName); 1449 } 1450} 1451 1452function ifLocalStorageLink(componentName: string, propName: string): boolean { 1453 if (!localStorageLinkCollection.get(componentName).keys) { 1454 return false; 1455 } else { 1456 const collection: Set<string> = new Set(); 1457 for (const key of localStorageLinkCollection.get(componentName).keys()) { 1458 collection.add(key); 1459 } 1460 return collection.has(propName); 1461 } 1462} 1463 1464function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike, 1465 customComponentName: string, log: LogInfo[]): void { 1466 const forbiddenToInitViaParamSet: Set<string> = new Set([ 1467 ...getCollectionSet(customComponentName, storageLinkCollection), 1468 ...getCollectionSet(customComponentName, storagePropCollection), 1469 ...getCollectionSet(customComponentName, consumeCollection) 1470 ]); 1471 const localStorageSet: Set<string> = new Set(); 1472 getLocalStorageCollection(customComponentName, localStorageSet); 1473 if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) { 1474 const nodeIdentifier: ts.Identifier = node.name as ts.Identifier; 1475 const propType: string = getForbbiddenToInitViaParamType(customComponentName, nodeIdentifier); 1476 log.push({ 1477 type: LogType.ERROR, 1478 message: `The '${propType}' property '${node.name.getText()}' in the custom component '${customComponentName}'` + 1479 ` cannot be initialized here (forbidden to specify).`, 1480 pos: node.name.getStart(), 1481 code: '10905317' 1482 }); 1483 } 1484} 1485 1486function validateMandatoryToInitViaParam(node: ts.CallExpression, customComponentName: string, 1487 curChildProps: Set<string>, log: LogInfo[], parentStructInfo: StructInfo): void { 1488 let mandatoryToInitViaParamSet: Set<string>; 1489 if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' && node.expression) { 1490 const overAll: string[] = [ 1491 ...getCollectionSet(node.expression.getText(), storedFileInfo.overallObjectLinkCollection)]; 1492 if (!parentStructInfo.isComponentV2) { 1493 overAll.unshift(...getCollectionSet(node.expression.getText(), storedFileInfo.overallLinkCollection)); 1494 } 1495 mandatoryToInitViaParamSet = new Set(overAll); 1496 customComponentName = node.expression.getText(); 1497 } else { 1498 const arr: string[] = [...getCollectionSet(customComponentName, objectLinkCollection)]; 1499 if (!parentStructInfo.isComponentV2) { 1500 arr.unshift(...getCollectionSet(customComponentName, linkCollection)); 1501 } 1502 mandatoryToInitViaParamSet = new Set(arr); 1503 } 1504 mandatoryToInitViaParamSet.forEach(item => { 1505 if (item && !curChildProps.has(item)) { 1506 log.push({ 1507 type: LogType.ERROR, 1508 message: `The property '${item}' in the custom component '${customComponentName}'` + 1509 ` is missing (mandatory to specify).`, 1510 pos: node.getStart(), 1511 code: '10905316' 1512 }); 1513 } 1514 }); 1515} 1516 1517function validateInitDecorator(node: ts.CallExpression, customComponentName: string, 1518 curChildProps: Set<string>, log: LogInfo[]): void { 1519 const mandatoryToInitViaParamSet: Set<string> = new Set([ 1520 ...getCollectionSet(customComponentName, builderParamObjectCollection), 1521 ...getCollectionSet(customComponentName, propCollection), 1522 ...getCollectionSet(customComponentName, regularCollection), 1523 ...getCollectionSet(customComponentName, stateCollection), 1524 ...getCollectionSet(customComponentName, provideCollection) 1525 ]); 1526 const decoratorVariable: Set<string> = new Set([ 1527 ...(builderParamInitialization.get(customComponentName) || []), 1528 ...(propInitialization.get(customComponentName) || []), 1529 ...(regularInitialization.get(customComponentName) || []), 1530 ...(stateInitialization.get(customComponentName) || []), 1531 ...(provideInitialization.get(customComponentName) || []) 1532 ]); 1533 mandatoryToInitViaParamSet.forEach((item: string) => { 1534 if (item && !curChildProps.has(item) && decoratorVariable && decoratorVariable.has(item)) { 1535 log.push({ 1536 type: LogType.ERROR, 1537 message: `'@Require' decorated '${item}' must be initialized through the component constructor.`, 1538 pos: node.getStart(), 1539 code: '10905359' 1540 }); 1541 } 1542 }); 1543 const privateParamSet: Set<string> = privateCollection.get(customComponentName) || new Set([]); 1544 curChildProps.forEach((item: string) => { 1545 if (privateParamSet.has(item)) { 1546 log.push({ 1547 type: LogType.WARN, 1548 message: `Property '${item}' is private and can not be initialized through the component constructor.`, 1549 pos: node.getStart() 1550 }); 1551 } 1552 }); 1553} 1554 1555function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string, 1556 curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string, 1557 log: LogInfo[], inputType: LogType = undefined): void { 1558 let type: LogType = LogType.ERROR; 1559 if (inputType) { 1560 type = inputType; 1561 } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes( 1562 parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) { 1563 type = LogType.WARN; 1564 } 1565 PropMapManager.reserveLog(parentPropertyName, parentPropertyKind, { 1566 type: type, 1567 message: `The '${parentPropertyKind}' property '${parentPropertyName}' cannot be assigned to ` + 1568 `the '${curPropertyKind}' property '${propertyName}'.`, 1569 // @ts-ignore 1570 pos: node.initializer ? node.initializer.getStart() : node.getStart(), 1571 code: type === LogType.ERROR ? '10905315' : undefined 1572 }); 1573} 1574 1575function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void { 1576 log.push({ 1577 type: LogType.ERROR, 1578 message: `Property '${node.name.getText()}' cannot initialize` + 1579 ` using '$' to create a reference to a variable.`, 1580 pos: node.initializer.getStart(), 1581 code: '10905314' 1582 }); 1583} 1584