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