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 let parameters: ts.NodeArray<ts.ParameterDeclaration> = ts.factory.createNodeArray(Array.from(node.parameters)); 387 parameters.push(createParentParameter()); 388 const builderNode: ts.MethodDeclaration = ts.factory.updateMethodDeclaration(node, customBuilder, 389 node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, 390 parameters, node.type, processComponentBlock(node.body, false, log, false, true)); 391 builderTypeParameter.params = []; 392 updateItem = processBuildMember(builderNode, context, log, true); 393 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 394 if (node.parameters && node.parameters.length === 0) { 395 if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) { 396 INNER_STYLE_FUNCTION.set(name, node.body); 397 STYLES_ATTRIBUTE.add(name); 398 BUILDIN_STYLE_NAMES.add(name); 399 decoratorParamSet.add(STYLES); 400 } 401 } else { 402 log.push({ 403 type: LogType.ERROR, 404 message: `@Styles can't have parameters.`, 405 pos: node.getStart() 406 }); 407 } 408 return; 409 } 410 } 411 return updateItem; 412} 413 414export function createParentParameter(): ts.ParameterDeclaration { 415 return ts.factory.createParameterDeclaration(undefined, undefined, undefined, 416 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), undefined, undefined, 417 ts.factory.createIdentifier(NULL)); 418} 419 420export function processBuildMember(node: ts.MethodDeclaration | ts.FunctionDeclaration, context: ts.TransformationContext, 421 log: LogInfo[], isBuilder = false): ts.MethodDeclaration | ts.FunctionDeclaration { 422 return ts.visitNode(node, visitBuild); 423 function visitBuild(node: ts.Node): ts.Node { 424 if (isGeometryView(node)) { 425 node = processGeometryView(node as ts.ExpressionStatement, log); 426 } 427 if (isProperty(node)) { 428 node = createReference(node as ts.PropertyAssignment, log, isBuilder); 429 } 430 if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name) && 431 stateObjectCollection.has(checkStateName(node)) && node.parent && ts.isCallExpression(node.parent) && 432 ts.isPropertyAccessExpression(node.parent.expression) && node !== node.parent.expression && 433 node.parent.expression.name.escapedText.toString() !== FOREACH_GET_RAW_OBJECT) { 434 return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 435 ts.factory.createIdentifier(FOREACH_OBSERVED_OBJECT), 436 ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [node]); 437 } 438 return ts.visitEachChild(node, visitBuild, context); 439 } 440 function checkStateName(node: ts.PropertyAccessExpression): string { 441 if (node.expression && !node.expression.expression && node.name && ts.isIdentifier(node.name)) { 442 return node.name.escapedText.toString(); 443 } 444 return null; 445 } 446} 447 448function isGeometryView(node: ts.Node): boolean { 449 if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) { 450 const call: ts.CallExpression = node.expression; 451 const exp: ts.Expression = call.expression; 452 const args: ts.NodeArray<ts.Expression> = call.arguments; 453 if (ts.isPropertyAccessExpression(exp) && ts.isIdentifier(exp.expression) && 454 exp.expression.escapedText.toString() === GEOMETRY_VIEW && ts.isIdentifier(exp.name) && 455 exp.name.escapedText.toString() === COMPONENT_CREATE_FUNCTION && args && args.length === 1 && 456 (ts.isArrowFunction(args[0]) || ts.isFunctionExpression(args[0]))) { 457 return true; 458 } 459 } 460 return false; 461} 462 463function processGeometryView(node: ts.ExpressionStatement, 464 log: LogInfo[]): ts.ExpressionStatement { 465 const exp: ts.CallExpression = node.expression as ts.CallExpression; 466 const arg: ts.ArrowFunction | ts.FunctionExpression = 467 exp.arguments[0] as ts.ArrowFunction | ts.FunctionExpression; 468 return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(exp, 469 exp.expression, undefined, [ts.factory.createArrowFunction(undefined, undefined, arg.parameters, 470 undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 471 getGeometryReaderFunctionBlock(arg, log))])); 472} 473 474function getGeometryReaderFunctionBlock(node: ts.ArrowFunction | ts.FunctionExpression, 475 log: LogInfo[]): ts.Block { 476 let blockNode: ts.Block; 477 if (ts.isBlock(node.body)) { 478 blockNode = node.body; 479 } else if (ts.isArrowFunction(node) && ts.isCallExpression(node.body)) { 480 blockNode = ts.factory.createBlock([ts.factory.createExpressionStatement(node.body)]); 481 } 482 return processComponentBlock(blockNode, false, log); 483} 484 485function updateHeritageClauses(node: ts.StructDeclaration, log: LogInfo[]) 486 : ts.NodeArray<ts.HeritageClause> { 487 if (node.heritageClauses && !checkHeritageClauses(node)) { 488 log.push({ 489 type: LogType.ERROR, 490 message: 'The struct component is not allowed to extends other class or implements other interface.', 491 pos: node.heritageClauses.pos 492 }); 493 } 494 const result:ts.HeritageClause[] = []; 495 const heritageClause:ts.HeritageClause = createHeritageClause(); 496 result.push(heritageClause); 497 return ts.factory.createNodeArray(result); 498} 499 500function checkHeritageClauses(node: ts.StructDeclaration): boolean { 501 if (node.heritageClauses.length === 1 && node.heritageClauses[0].types && 502 node.heritageClauses[0].types.length === 1) { 503 const expressionNode: ts.ExpressionWithTypeArguments = node.heritageClauses[0].types[0]; 504 if (expressionNode.expression && ts.isIdentifier(expressionNode.expression) && 505 expressionNode.expression.escapedText.toString() === CUSTOM_COMPONENT) { 506 return true; 507 } 508 } 509 return false; 510} 511 512export function isProperty(node: ts.Node): Boolean { 513 if (judgmentParentType(node)) { 514 if (node.parent.parent.expression && ts.isIdentifier(node.parent.parent.expression) && 515 !BUILDIN_STYLE_NAMES.has(node.parent.parent.expression.escapedText.toString()) && 516 componentCollection.customComponents.has( 517 node.parent.parent.expression.escapedText.toString())) { 518 return true; 519 } else if (ts.isPropertyAccessExpression(node.parent.parent.expression) && 520 ts.isIdentifier(node.parent.parent.expression.expression) && 521 componentCollection.customComponents.has( 522 node.parent.parent.expression.name.escapedText.toString())) { 523 return true; 524 } 525 } 526 return false; 527} 528 529function judgmentParentType(node: ts.Node): boolean { 530 return ts.isPropertyAssignment(node) && node.name && ts.isIdentifier(node.name) && 531 node.parent && ts.isObjectLiteralExpression(node.parent) && node.parent.parent && 532 (ts.isCallExpression(node.parent.parent) || ts.isEtsComponentExpression(node.parent.parent)); 533} 534 535export function createReference(node: ts.PropertyAssignment, log: LogInfo[], isBuilder = false): ts.PropertyAssignment { 536 const linkParentComponent: string[] = getParentNode(node, linkCollection).slice(1); 537 const propParentComponent: string[] = getParentNode(node, propCollection).slice(1); 538 const propertyName: ts.Identifier = node.name as ts.Identifier; 539 let initText: string; 540 const LINK_REG: RegExp = /^\$/g; 541 const initExpression: ts.Expression = node.initializer; 542 let is$$: boolean = false; 543 if (ts.isIdentifier(initExpression) && 544 initExpression.escapedText.toString().match(LINK_REG)) { 545 initText = initExpression.escapedText.toString().replace(LINK_REG, ''); 546 } else if (ts.isPropertyAccessExpression(initExpression) && initExpression.expression && 547 initExpression.expression.kind === ts.SyntaxKind.ThisKeyword && 548 ts.isIdentifier(initExpression.name) && initExpression.name.escapedText.toString().match(LINK_REG)) { 549 initText = initExpression.name.escapedText.toString().replace(LINK_REG, ''); 550 } else if (isBuilder && ts.isPropertyAccessExpression(initExpression) && initExpression.expression && 551 ts.isIdentifier(initExpression.expression) && initExpression.expression.escapedText.toString() === $$ && 552 ts.isIdentifier(initExpression.name) && linkParentComponent.includes(propertyName.escapedText.toString())) { 553 is$$ = true; 554 initText = initExpression.name.escapedText.toString(); 555 } else if (isMatchInitExpression(initExpression) && 556 linkParentComponent.includes(propertyName.escapedText.toString())) { 557 initText = initExpression.name.escapedText.toString().replace(LINK_REG, ''); 558 } 559 if (initText) { 560 node = addDoubleUnderline(node, propertyName, initText, is$$); 561 } 562 return node; 563} 564 565function isMatchInitExpression(initExpression: ts.Expression): boolean { 566 return ts.isPropertyAccessExpression(initExpression) && 567 initExpression.expression && 568 initExpression.expression.kind === ts.SyntaxKind.ThisKeyword && 569 ts.isIdentifier(initExpression.name); 570} 571 572function addDoubleUnderline(node: ts.PropertyAssignment, propertyName: ts.Identifier, 573 initText: string, is$$ = false): ts.PropertyAssignment { 574 return ts.factory.updatePropertyAssignment(node, propertyName, 575 ts.factory.createPropertyAccessExpression( 576 is$$ && partialUpdateConfig.partialUpdateMode ? ts.factory.createIdentifier($$) : ts.factory.createThis(), 577 ts.factory.createIdentifier(`__${initText}`))); 578} 579 580function getParentNode(node: ts.PropertyAssignment, collection: Map<string, Set<string>>): string[] { 581 const grandparentNode: ts.NewExpression = node.parent.parent as ts.NewExpression; 582 const grandparentExpression: ts.Identifier | ts.PropertyAccessExpression = 583 grandparentNode.expression as ts.Identifier | ts.PropertyAccessExpression; 584 let parentComponent: Set<string> = new Set(); 585 let grandparentName: string; 586 if (ts.isIdentifier(grandparentExpression)) { 587 grandparentName = grandparentExpression.escapedText.toString(); 588 parentComponent = collection.get(grandparentName); 589 } else if (ts.isPropertyAccessExpression(grandparentExpression)) { 590 grandparentName = grandparentExpression.name.escapedText.toString(); 591 parentComponent = collection.get(grandparentName); 592 } else { 593 // ignore 594 } 595 if (!parentComponent) { 596 parentComponent = new Set(); 597 } 598 return [grandparentName, ...parentComponent]; 599} 600 601function addUpdateParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): 602 ts.MethodDeclaration { 603 return createParamsInitBlock(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, statements, parentComponentName); 604} 605 606function addInitialParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration { 607 return createParamsInitBlock(COMPONENT_CONSTRUCTOR_INITIAL_PARAMS, statements, parentComponentName); 608} 609 610function addUpdateStateVarsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration { 611 return createParamsInitBlock(COMPONENT_UPDATE_STATE_VARS, statements, parentComponentName); 612} 613 614function addPurgeVariableDepFunc(statements: ts.Statement[]): ts.MethodDeclaration { 615 return ts.factory.createMethodDeclaration( 616 undefined, undefined, undefined, 617 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PURGE_VARIABLE_DEP), 618 undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 619 ts.factory.createIdentifier(RMELMTID), undefined, undefined, undefined)], undefined, 620 ts.factory.createBlock(statements, true)); 621} 622 623function addDeleteParamsFunc(statements: ts.PropertyDeclaration[]): ts.MethodDeclaration { 624 const deleteStatements: ts.ExpressionStatement[] = []; 625 statements.forEach((statement: ts.PropertyDeclaration) => { 626 const name: ts.Identifier = statement.name as ts.Identifier; 627 let paramsStatement: ts.ExpressionStatement; 628 if (!partialUpdateConfig.partialUpdateMode || statement.decorators) { 629 paramsStatement = createParamsWithUnderlineStatement(name); 630 } 631 deleteStatements.push(paramsStatement); 632 }); 633 const defaultStatement: ts.ExpressionStatement = 634 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 635 ts.factory.createPropertyAccessExpression( 636 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 637 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_SUBSCRIBER_MANAGER), 638 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_GET_FUNCTION)), undefined, []), 639 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_DELETE_FUNCTION)), 640 undefined, [ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 641 ts.factory.createThis(), ts.factory.createIdentifier( 642 !partialUpdateConfig.partialUpdateMode ? 643 ABOUT_TO_BE_DELETE_FUNCTION_ID : ABOUT_TO_BE_DELETE_FUNCTION_ID__)), 644 undefined, [])])); 645 deleteStatements.push(defaultStatement); 646 if (partialUpdateConfig.partialUpdateMode) { 647 const aboutToBeDeletedInternalStatement: ts.ExpressionStatement = createDeletedInternalStatement(); 648 deleteStatements.push(aboutToBeDeletedInternalStatement); 649 } 650 const deleteParamsMethod: ts.MethodDeclaration = 651 createParamsInitBlock(COMPONENT_CONSTRUCTOR_DELETE_PARAMS, deleteStatements); 652 return deleteParamsMethod; 653} 654 655function createParamsWithUnderlineStatement(name: ts.Identifier): ts.ExpressionStatement { 656 return ts.factory.createExpressionStatement( 657 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 658 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 659 ts.factory.createIdentifier(`__${name.escapedText.toString()}`)), 660 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_DELETE_PARAMS)), undefined, [])); 661} 662 663function createDeletedInternalStatement(): ts.ExpressionStatement { 664 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 665 ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 666 ts.factory.createIdentifier(ABOUTTOBEDELETEDINTERNAL)), undefined, [])); 667} 668 669function addRerenderFunc(statements: ts.Statement[]): ts.MethodDeclaration { 670 let updateDirtyElementStatement: ts.Statement = ts.factory.createExpressionStatement( 671 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 672 ts.factory.createThis(), ts.factory.createIdentifier(UPDATEDIRTYELEMENTS)), undefined, [])); 673 statements.push(updateDirtyElementStatement); 674 return ts.factory.createMethodDeclaration(undefined, undefined, undefined, 675 ts.factory.createIdentifier(COMPONENT_RERENDER_FUNCTION), undefined, undefined, [], undefined, 676 ts.factory.createBlock(statements, true)); 677} 678 679function createParamsInitBlock(express: string, statements: ts.Statement[], 680 parentComponentName?: ts.Identifier): ts.MethodDeclaration { 681 const methodDeclaration: ts.MethodDeclaration = ts.factory.createMethodDeclaration(undefined, 682 undefined, undefined, ts.factory.createIdentifier(express), undefined, undefined, 683 [ts.factory.createParameterDeclaration(undefined, undefined, undefined, 684 express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : 685 ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), undefined, 686 express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : 687 ts.factory.createTypeReferenceNode( 688 ts.factory.createIdentifier(parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined), 689 undefined)], undefined, ts.factory.createBlock(statements, true)); 690 return methodDeclaration; 691} 692 693function validateBuildMethodCount(buildCount: BuildCount, parentComponentName: ts.Identifier, 694 log: LogInfo[]): void { 695 if (buildCount.count !== 1) { 696 log.push({ 697 type: LogType.ERROR, 698 message: `struct '${parentComponentName.getText()}' must be at least or at most one 'build' method.`, 699 pos: parentComponentName.getStart() 700 }); 701 } 702} 703 704function validateHasController(componentName: ts.Identifier, checkController: ControllerType, 705 log: LogInfo[]): void { 706 if (!checkController.hasController) { 707 log.push({ 708 type: LogType.ERROR, 709 message: '@CustomDialog component should have a property of the CustomDialogController type.', 710 pos: componentName.pos 711 }); 712 } 713} 714 715function createHeritageClause(): ts.HeritageClause { 716 if (partialUpdateConfig.partialUpdateMode) { 717 return ts.factory.createHeritageClause( 718 ts.SyntaxKind.ExtendsKeyword, 719 [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), [])] 720 ); 721 } 722 return ts.factory.createHeritageClause( 723 ts.SyntaxKind.ExtendsKeyword, 724 [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier(BASE_COMPONENT_NAME), [])] 725 ); 726} 727 728function createTypeReference(decoratorName: string, type: ts.TypeNode, log: LogInfo[], 729 program: ts.Program): ts.TypeNode { 730 let newType: ts.TypeNode; 731 switch (decoratorName) { 732 case COMPONENT_STATE_DECORATOR: 733 case COMPONENT_PROVIDE_DECORATOR: 734 newType = ts.factory.createTypeReferenceNode( 735 isSimpleType(type, program, log) 736 ? OBSERVED_PROPERTY_SIMPLE 737 : OBSERVED_PROPERTY_OBJECT, 738 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 739 ); 740 break; 741 case COMPONENT_LINK_DECORATOR: 742 case COMPONENT_CONSUME_DECORATOR: 743 newType = ts.factory.createTypeReferenceNode( 744 isSimpleType(type, program, log) 745 ? SYNCHED_PROPERTY_SIMPLE_TWO_WAY 746 : SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 747 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 748 ); 749 break; 750 case COMPONENT_PROP_DECORATOR: 751 newType = ts.factory.createTypeReferenceNode( 752 SYNCHED_PROPERTY_SIMPLE_ONE_WAY, 753 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 754 ); 755 break; 756 case COMPONENT_OBJECT_LINK_DECORATOR: 757 newType = ts.factory.createTypeReferenceNode( 758 SYNCHED_PROPERTY_NESED_OBJECT, 759 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 760 ); 761 break; 762 case COMPONENT_STORAGE_PROP_DECORATOR: 763 case COMPONENT_STORAGE_LINK_DECORATOR: 764 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [ 765 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 766 ]); 767 break; 768 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR: 769 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 770 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [ 771 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 772 ]); 773 break; 774 } 775 return newType; 776} 777 778function createTypeReferencePU(decoratorName: string, type: ts.TypeNode, log: LogInfo[], 779 program: ts.Program): ts.TypeNode { 780 let newType: ts.TypeNode; 781 switch (decoratorName) { 782 case COMPONENT_STATE_DECORATOR: 783 case COMPONENT_PROVIDE_DECORATOR: 784 newType = ts.factory.createTypeReferenceNode( 785 isSimpleType(type, program, log) 786 ? OBSERVED_PROPERTY_SIMPLE_PU 787 : OBSERVED_PROPERTY_OBJECT_PU, 788 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 789 ); 790 break; 791 case COMPONENT_LINK_DECORATOR: 792 newType = ts.factory.createTypeReferenceNode( 793 isSimpleType(type, program, log) 794 ? SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU 795 : SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 796 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 797 ); 798 break; 799 case COMPONENT_PROP_DECORATOR: 800 newType = ts.factory.createTypeReferenceNode( 801 SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU, 802 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 803 ); 804 break; 805 case COMPONENT_OBJECT_LINK_DECORATOR: 806 newType = ts.factory.createTypeReferenceNode( 807 SYNCHED_PROPERTY_NESED_OBJECT_PU, 808 [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)] 809 ); 810 break; 811 case COMPONENT_CONSUME_DECORATOR: 812 case COMPONENT_STORAGE_PROP_DECORATOR: 813 case COMPONENT_STORAGE_LINK_DECORATOR: 814 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [ 815 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 816 ]); 817 break; 818 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR: 819 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 820 newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [ 821 type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), 822 ]); 823 break; 824 } 825 return newType; 826} 827