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  BASE_COMPONENT_NAME_PU,
70  OBSERVED_PROPERTY_SIMPLE_PU,
71  OBSERVED_PROPERTY_OBJECT_PU,
72  SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU,
73  SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU,
74  SYNCHED_PROPERTY_NESED_OBJECT_PU,
75  OBSERVED_PROPERTY_ABSTRACT_PU,
76  CREATE_LOCAL_STORAGE_LINK,
77  CREATE_LOCAL_STORAGE_PROP,
78  COMPONENT_UPDATE_STATE_VARS,
79  COMPONENT_WATCH_DECORATOR,
80  $$,
81  COMPONENT_UPDATE_ELMT_ID,
82  OLD_ELMT_ID,
83  NEW_ELMT_ID,
84  UPDATE_RECYCLE_ELMT_ID,
85  GET_ENTRYNAME,
86  COMPONENT_PARAMS_FUNCTION,
87  FUNCTION,
88  COMPONENT_PARAMS_LAMBDA_FUNCTION,
89  DECORATOR_COMPONENT_FREEZEWHENINACTIVE,
90  INIT_ALLOW_COMPONENT_FREEZE,
91  FINALIZE_CONSTRUCTION,
92  PROTOTYPE,
93  REFLECT,
94  CREATE_SET_METHOD,
95  COMPONENT_REQUIRE_DECORATOR,
96  CONTEXT_STACK,
97  COMPONENT_IF_UNDEFINED,
98  COMPONENT_POP_FUNCTION,
99  PUSH,
100  PUV2_VIEW_BASE,
101  COMPONENT_LOCAL_BUILDER_DECORATOR
102} from './pre_define';
103import {
104  BUILDIN_STYLE_NAMES,
105  CUSTOM_BUILDER_METHOD,
106  INNER_STYLE_FUNCTION,
107  INTERFACE_NODE_SET,
108  STYLES_ATTRIBUTE,
109  INNER_CUSTOM_BUILDER_METHOD
110} from './component_map';
111import {
112  componentCollection,
113  linkCollection,
114  localStorageLinkCollection,
115  localStoragePropCollection,
116  builderParamObjectCollection
117} from './validate_ui_syntax';
118import {
119  addConstructor,
120  getInitConstructor,
121  updateConstructor
122} from './process_component_constructor';
123import {
124  ControllerType,
125  processMemberVariableDecorators,
126  UpdateResult,
127  stateObjectCollection,
128  curPropMap,
129  decoratorParamSet,
130  isSimpleType,
131  isSingleKey,
132  findDecoratorIndex
133} from './process_component_member';
134import {
135  processComponentBuild,
136  processComponentBlock
137} from './process_component_build';
138import {
139  LogType,
140  LogInfo,
141  hasDecorator,
142  getPossibleBuilderTypeParameter,
143  storedFileInfo,
144  removeDecorator
145} from './utils';
146import {
147  partialUpdateConfig,
148  projectConfig
149} from '../main';
150import {
151  builderTypeParameter,
152  initializeMYIDS,
153  globalBuilderParamAssignment
154} from './process_ui_syntax';
155import constantDefine from './constant_define';
156import processStructComponentV2, { StructInfo } from './process_struct_componentV2';
157
158export function processComponentClass(node: ts.StructDeclaration, context: ts.TransformationContext,
159  log: LogInfo[], program: ts.Program): ts.ClassDeclaration {
160  const decoratorNode: readonly ts.Decorator[] = ts.getAllDecorators(node);
161  const memberNode: ts.ClassElement[] =
162    processMembers(node.members, node.name, context, decoratorNode, log, program, checkPreview(node));
163  return ts.factory.createClassDeclaration(ts.getModifiers(node), node.name,
164    node.typeParameters, updateHeritageClauses(node, log), memberNode);
165}
166
167function checkPreview(node: ts.StructDeclaration): boolean {
168  let hasPreview: boolean = false;
169  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
170  if (node && decorators) {
171    for (let i = 0; i < decorators.length; i++) {
172      const name: string = decorators[i].getText().replace(/\([^\(\)]*\)/, '').trim();
173      if (name === COMPONENT_DECORATOR_PREVIEW) {
174        hasPreview = true;
175        break;
176      }
177    }
178  }
179  return hasPreview;
180}
181
182export type BuildCount = {
183  count: number;
184};
185export type FreezeParamType = {
186  componentFreezeParam: ts.Expression;
187};
188function processMembers(members: ts.NodeArray<ts.ClassElement>, parentComponentName: ts.Identifier,
189  context: ts.TransformationContext, decoratorNode: readonly ts.Decorator[], log: LogInfo[],
190  program: ts.Program, hasPreview: boolean): ts.ClassElement[] {
191  const buildCount: BuildCount = { count: 0 };
192  let ctorNode: any = getInitConstructor(members, parentComponentName);
193  const newMembers: ts.ClassElement[] = [];
194  const watchMap: Map<string, ts.Node> = new Map();
195  const updateParamsStatements: ts.Statement[] = [];
196  const stateVarsStatements: ts.Statement[] = [];
197  const purgeVariableDepStatements: ts.Statement[] = [];
198  const rerenderStatements: ts.Statement[] = [];
199  const deleteParamsStatements: ts.PropertyDeclaration[] = [];
200  const checkController: ControllerType =
201    { hasController: !componentCollection.customDialogs.has(parentComponentName.getText()) };
202  const interfaceNode = ts.factory.createInterfaceDeclaration(undefined,
203    parentComponentName.getText() + INTERFACE_NAME_SUFFIX, undefined, undefined, []);
204  members.forEach((item: ts.ClassElement) => {
205    let updateItem: ts.ClassElement;
206    if (ts.isPropertyDeclaration(item)) {
207      if (isStaticProperty(item)) {
208        newMembers.push(item);
209        validateDecorators(item, log);
210      } else {
211        addPropertyMember(item, newMembers, program, parentComponentName.getText(), log);
212        const result: UpdateResult = processMemberVariableDecorators(parentComponentName, item,
213          ctorNode, watchMap, checkController, log, program, context, hasPreview, interfaceNode);
214        if (result.isItemUpdate()) {
215          updateItem = result.getProperity();
216        } else {
217          updateItem = item;
218        }
219        if (result.getVariableGet()) {
220          newMembers.push(result.getVariableGet());
221        }
222        if (result.getVariableSet()) {
223          newMembers.push(result.getVariableSet());
224        }
225        if (result.isCtorUpdate()) {
226          ctorNode = result.getCtor();
227        }
228        if (result.getUpdateParams()) {
229          updateParamsStatements.push(result.getUpdateParams());
230        }
231        if (result.getStateVarsParams()) {
232          stateVarsStatements.push(result.getStateVarsParams());
233        }
234        if (result.isDeleteParams()) {
235          deleteParamsStatements.push(item);
236        }
237        if (result.getControllerSet()) {
238          newMembers.push(result.getControllerSet());
239        }
240        processPropertyUnchanged(result, purgeVariableDepStatements);
241      }
242    }
243    if (ts.isMethodDeclaration(item) && item.name) {
244      updateItem =
245        processComponentMethod(item, context, log, buildCount);
246    }
247    if (updateItem) {
248      newMembers.push(updateItem);
249    }
250  });
251  INTERFACE_NODE_SET.add(interfaceNode);
252  validateBuildMethodCount(buildCount, parentComponentName, log);
253  validateHasController(parentComponentName, checkController, log);
254  if (storedFileInfo.getCurrentArkTsFile().recycleComponents.has(parentComponentName.getText())) {
255    newMembers.unshift(addDeleteParamsFunc(deleteParamsStatements, true));
256  }
257  newMembers.unshift(addDeleteParamsFunc(deleteParamsStatements));
258  addIntoNewMembers(newMembers, parentComponentName, updateParamsStatements,
259    purgeVariableDepStatements, rerenderStatements, stateVarsStatements);
260  if (partialUpdateConfig.partialUpdateMode) {
261    const creezeParam: FreezeParamType = {
262      componentFreezeParam: undefined
263    };
264    const isFreezeComponent: boolean = decoratorAssignParams(decoratorNode, context, creezeParam);
265    ctorNode = updateConstructor(ctorNode, [], assignParams(parentComponentName.getText()),
266      isFreezeComponent ? decoratorComponentParam(creezeParam) : [], true);
267  }
268  newMembers.unshift(addConstructor(ctorNode, watchMap, parentComponentName));
269  if (componentCollection.entryComponent === parentComponentName.escapedText.toString() &&
270    partialUpdateConfig.partialUpdateMode && projectConfig.minAPIVersion > 10) {
271    newMembers.push(getEntryNameFunction(componentCollection.entryComponent));
272  }
273  curPropMap.clear();
274  return newMembers;
275}
276
277export function decoratorAssignParams(decoratorNode: readonly ts.Decorator[], context: ts.TransformationContext,
278  creezeParam: FreezeParamType): boolean {
279  if (decoratorNode && Array.isArray(decoratorNode) && decoratorNode.length) {
280    return decoratorNode.some((item: ts.Decorator) => {
281      if (isFreezeComponents(item, context, creezeParam)) {
282        return true;
283      } else {
284        return false;
285      }
286    });
287  } else {
288    return false;
289  }
290}
291
292function isFreezeComponents(decorator: ts.Decorator, context: ts.TransformationContext,
293  creezeParam: FreezeParamType): boolean {
294  let isComponentAssignParent: boolean = false;
295  ts.visitNode(decorator, visitComponentParament);
296  function visitComponentParament(decorator: ts.Node): ts.Node {
297    if (ts.isPropertyAssignment(decorator) && decorator.name && decorator.name.text &&
298      decorator.name.text.toString() === DECORATOR_COMPONENT_FREEZEWHENINACTIVE) {
299      isComponentAssignParent = true;
300      creezeParam.componentFreezeParam = decorator.initializer;
301      return decorator;
302    }
303    return ts.visitEachChild(decorator, visitComponentParament, context);
304  }
305  return isComponentAssignParent;
306}
307
308export function getEntryNameFunction(entryName: string): ts.MethodDeclaration {
309  return ts.factory.createMethodDeclaration(
310    [ts.factory.createToken(ts.SyntaxKind.StaticKeyword)],
311    undefined,
312    ts.factory.createIdentifier(GET_ENTRYNAME),
313    undefined,
314    undefined,
315    [],
316    ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
317    ts.factory.createBlock(
318      [ts.factory.createReturnStatement(ts.factory.createStringLiteral(entryName))],
319      true
320    )
321  );
322}
323
324function assignParams(parentComponentName: string): ts.Statement[] {
325  return [ts.factory.createIfStatement(
326    ts.factory.createBinaryExpression(
327      ts.factory.createTypeOfExpression(ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)),
328      ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
329      ts.factory.createStringLiteral(FUNCTION)
330    ),
331    ts.factory.createBlock(
332      [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
333        ts.factory.createPropertyAccessExpression(
334          ts.factory.createThis(),
335          ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION)
336        ),
337        ts.factory.createToken(ts.SyntaxKind.EqualsToken),
338        ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)
339      ))],
340      true
341    )
342  )];
343}
344
345function decoratorComponentParam(freezeParam: FreezeParamType): ts.IfStatement[] {
346  return [ts.factory.createIfStatement(
347    ts.factory.createBinaryExpression(
348      ts.factory.createElementAccessExpression(
349        ts.factory.createSuper(),
350        ts.factory.createStringLiteral(INIT_ALLOW_COMPONENT_FREEZE)
351      ),
352      ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
353      ts.factory.createBinaryExpression(
354        ts.factory.createTypeOfExpression(ts.factory.createElementAccessExpression(
355          ts.factory.createSuper(),
356          ts.factory.createStringLiteral(INIT_ALLOW_COMPONENT_FREEZE)
357        )),
358        ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
359        ts.factory.createStringLiteral(FUNCTION)
360      )
361    ),
362    ts.factory.createBlock(
363      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
364        ts.factory.createElementAccessExpression(
365          ts.factory.createSuper(),
366          ts.factory.createStringLiteral(INIT_ALLOW_COMPONENT_FREEZE)
367        ),
368        undefined,
369        [freezeParam.componentFreezeParam]
370      ))],
371      true
372    ),
373    undefined
374  )];
375}
376
377function isStaticProperty(property: ts.PropertyDeclaration): boolean {
378  const modifiers: readonly ts.Modifier[] =
379    ts.canHaveModifiers(property) ? ts.getModifiers(property) : undefined;
380  return modifiers && modifiers.length && modifiers.some(modifier => {
381    return modifier.kind === ts.SyntaxKind.StaticKeyword;
382  });
383}
384
385function validateDecorators(item: ts.ClassElement, log: LogInfo[]): void {
386  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
387  if (decorators && decorators.length) {
388    decorators.map((decorator: ts.Decorator) => {
389      const decoratorName: string = decorator.getText();
390      if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
391        log.push({
392          type: LogType.ERROR,
393          message: `The static variable of struct cannot be used together with built-in decorators.`,
394          pos: item.getStart()
395        });
396      }
397    });
398  }
399}
400
401function processPropertyUnchanged(
402  result: UpdateResult,
403  purgeVariableDepStatements: ts.Statement[]
404): void {
405  if (partialUpdateConfig.partialUpdateMode) {
406    if (result.getPurgeVariableDepStatement()) {
407      purgeVariableDepStatements.push(result.getPurgeVariableDepStatement());
408    }
409  }
410}
411
412function addIntoNewMembers(
413  newMembers: ts.ClassElement[],
414  parentComponentName: ts.Identifier,
415  updateParamsStatements: ts.Statement[],
416  purgeVariableDepStatements: ts.Statement[],
417  rerenderStatements: ts.Statement[],
418  stateVarsStatements: ts.Statement[]
419): void {
420  if (partialUpdateConfig.partialUpdateMode) {
421    newMembers.unshift(
422      addInitialParamsFunc(updateParamsStatements, parentComponentName),
423      addUpdateStateVarsFunc(stateVarsStatements, parentComponentName),
424      addPurgeVariableDepFunc(purgeVariableDepStatements)
425    );
426    newMembers.push(addRerenderFunc(rerenderStatements));
427  } else {
428    newMembers.unshift(addUpdateParamsFunc(updateParamsStatements, parentComponentName));
429  }
430}
431
432export function isRegularProperty(decorators: readonly ts.Decorator[]): boolean {
433  if (decorators && decorators.length) {
434    if (decorators.length === 1 && decorators[0].getText() === COMPONENT_REQUIRE_DECORATOR) {
435      return true;
436    }
437    return false;
438  }
439  return true;
440}
441
442function addPropertyMember(item: ts.ClassElement, newMembers: ts.ClassElement[],
443  program: ts.Program, parentComponentName: string, log: LogInfo[]): void {
444  const propertyItem: ts.PropertyDeclaration = item as ts.PropertyDeclaration;
445  let decoratorName: string;
446  let updatePropertyItem: ts.PropertyDeclaration;
447  const type: ts.TypeNode = propertyItem.type;
448  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(propertyItem);
449  if (isRegularProperty(decorators)) {
450    updatePropertyItem = createPropertyDeclaration(propertyItem, type, true);
451    newMembers.push(updatePropertyItem);
452  } else {
453    for (let i = 0; i < decorators.length; i++) {
454      let newType: ts.TypeNode;
455      decoratorName = decorators[i].getText().replace(/\(.*\)$/, '').trim();
456      let isLocalStorage: boolean = false;
457      if (!partialUpdateConfig.partialUpdateMode) {
458        newType = createTypeReference(decoratorName, type, log, program);
459      } else {
460        newType = createTypeReferencePU(decoratorName, type, log, program);
461      }
462      if (
463        decoratorName === COMPONENT_LOCAL_STORAGE_LINK_DECORATOR ||
464        decoratorName === COMPONENT_LOCAL_STORAGE_PROP_DECORATOR
465      ) {
466        isLocalStorage = true;
467      }
468      const newUpdatePropertyItem = createPropertyDeclaration(
469        propertyItem, newType, false, isLocalStorage, parentComponentName);
470      if (!updatePropertyItem) {
471        updatePropertyItem = newUpdatePropertyItem;
472      } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName) &&
473        ![COMPONENT_WATCH_DECORATOR, COMPONENT_REQUIRE_DECORATOR].includes(decoratorName)) {
474        updatePropertyItem = newUpdatePropertyItem;
475      }
476    }
477    if (updatePropertyItem) {
478      newMembers.push(updatePropertyItem);
479    }
480  }
481}
482
483function createPropertyDeclaration(propertyItem: ts.PropertyDeclaration, newType: ts.TypeNode | undefined,
484  normalVar: boolean, isLocalStorage: boolean = false, parentComponentName: string = null): ts.PropertyDeclaration {
485  if (typeof newType === undefined) {
486    return undefined;
487  }
488  let prefix: string = '';
489  if (!normalVar) {
490    prefix = '__';
491  }
492  const privateM: ts.ModifierToken<ts.SyntaxKind.PrivateKeyword> =
493    ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword);
494  const modifiers: readonly ts.Modifier[] =
495    ts.canHaveModifiers(propertyItem) ? ts.getModifiers(propertyItem) : undefined;
496  return ts.factory.updatePropertyDeclaration(propertyItem,
497    ts.concatenateDecoratorsAndModifiers(undefined, modifiers || [privateM]), prefix + propertyItem.name.getText(),
498    propertyItem.questionToken, newType, isLocalStorage ?
499      createLocalStroageCallExpression(propertyItem, propertyItem.name.getText(),
500        parentComponentName) : undefined);
501}
502
503function createLocalStroageCallExpression(node: ts.PropertyDeclaration, name: string,
504  parentComponentName: string): ts.CallExpression {
505  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
506  if (isSingleKey(node)) {
507    const localStorageLink: Set<string> = localStorageLinkCollection.get(parentComponentName).get(name);
508    const localStorageProp: Set<string> = localStoragePropCollection.get(parentComponentName).get(name);
509    let localFuncName: string;
510    const index: number = findDecoratorIndex(decorators,
511      [COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_PROP_DECORATOR]);
512    const localValue: ts.Expression[] = [
513      decorators[index].expression.arguments[0],
514      node.initializer ? node.initializer : ts.factory.createNumericLiteral(COMPONENT_CONSTRUCTOR_UNDEFINED),
515      ts.factory.createThis(),
516      ts.factory.createStringLiteral(name || COMPONENT_CONSTRUCTOR_UNDEFINED)
517    ];
518    if (!partialUpdateConfig.partialUpdateMode) {
519      localFuncName = localStorageLink && !localStorageProp ? COMPONENT_SET_AND_LINK :
520        COMPONENT_SET_AND_PROP;
521    } else {
522      localFuncName = localStorageLink && !localStorageProp ? CREATE_LOCAL_STORAGE_LINK :
523        CREATE_LOCAL_STORAGE_PROP;
524      localValue.splice(-2, 1);
525    }
526    return ts.factory.createCallExpression(
527      ts.factory.createPropertyAccessExpression(
528        !partialUpdateConfig.partialUpdateMode ?
529          ts.factory.createPropertyAccessExpression(
530            ts.factory.createThis(),
531            ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_LOCALSTORAGE}_`)
532          ) : ts.factory.createThis(),
533        ts.factory.createIdentifier(localFuncName)
534      ),
535      [node.type],
536      localValue
537    );
538  }
539  return undefined;
540}
541export interface builderConditionType {
542  isBuilder: boolean,
543  isLocalBuilder: boolean
544}
545export function processComponentMethod(node: ts.MethodDeclaration, context: ts.TransformationContext,
546  log: LogInfo[], buildCount: BuildCount): ts.MethodDeclaration {
547  let updateItem: ts.MethodDeclaration = node;
548  const name: string = node.name.getText();
549  const customBuilder: ts.Decorator[] = [];
550  const builderCondition: builderConditionType = {
551    isBuilder: false,
552    isLocalBuilder: false
553  };
554  if (builderParamObjectCollection.get(componentCollection.currentClassName)) {
555    storedFileInfo.builderLikeCollection =
556      new Set([...builderParamObjectCollection.get(componentCollection.currentClassName), ...CUSTOM_BUILDER_METHOD]);
557  } else {
558    storedFileInfo.builderLikeCollection = CUSTOM_BUILDER_METHOD;
559  }
560  if (name === COMPONENT_BUILD_FUNCTION) {
561    storedFileInfo.processBuilder = false;
562    storedFileInfo.processGlobalBuilder = false;
563    buildCount.count = buildCount.count + 1;
564    if (node.parameters.length) {
565      log.push({
566        type: LogType.ERROR,
567        message: `The 'build' method can not have arguments.`,
568        pos: node.getStart()
569      });
570    }
571    const buildNode: ts.MethodDeclaration = processComponentBuild(node, log);
572    updateItem = processBuildMember(buildNode, context, log);
573  } else if (node.body && ts.isBlock(node.body)) {
574    if (name === COMPONENT_TRANSITION_FUNCTION) {
575      updateItem = ts.factory.updateMethodDeclaration(node, ts.getModifiers(node),
576        node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters,
577        node.type, processComponentBlock(node.body, false, log, true));
578    } else if (isBuilderOrLocalBuilder(node, builderCondition, customBuilder)) {
579      storedFileInfo.processBuilder = true;
580      storedFileInfo.processGlobalBuilder = false;
581      if (builderCondition.isLocalBuilder) {
582        storedFileInfo.processLocalBuilder = true;
583      }
584      CUSTOM_BUILDER_METHOD.add(name);
585      INNER_CUSTOM_BUILDER_METHOD.add(name);
586      builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters);
587      const parameters: ts.NodeArray<ts.ParameterDeclaration> = ts.factory.createNodeArray(Array.from(node.parameters));
588      if (!builderCondition.isLocalBuilder) {
589        parameters.push(createParentParameter());
590      }
591      if (projectConfig.optLazyForEach) {
592        parameters.push(initializeMYIDS());
593      }
594      const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
595      const componentBlock: ts.Block = processComponentBlock(node.body, false, log, false, true);
596      if (partialUpdateConfig.partialUpdateMode && builderCondition.isLocalBuilder &&
597        node.body.statements.length) {
598        componentBlock.statements.unshift(globalBuilderParamAssignment());
599      }
600      let builderNode: ts.MethodDeclaration | ts.PropertyDeclaration = ts.factory.updateMethodDeclaration(node,
601        ts.concatenateDecoratorsAndModifiers(removeDecorator(customBuilder, 'Builder'), modifiers),
602        node.asteriskToken, node.name, node.questionToken, node.typeParameters,
603        parameters, node.type, componentBlock);
604      builderTypeParameter.params = [];
605      updateItem = processBuildMember(builderNode, context, log, true);
606      if (builderCondition.isLocalBuilder) {
607        checkDecoratorMethod(node, modifiers, log);
608        updateItem = localBuilderNode(node, updateItem.body);
609      }
610      storedFileInfo.processBuilder = false;
611      storedFileInfo.processLocalBuilder = false;
612    } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
613      if (node.parameters && node.parameters.length === 0) {
614        if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) {
615          INNER_STYLE_FUNCTION.set(name, node.body);
616          STYLES_ATTRIBUTE.add(name);
617          BUILDIN_STYLE_NAMES.add(name);
618          decoratorParamSet.add(STYLES);
619        }
620      } else {
621        log.push({
622          type: LogType.ERROR,
623          message: `@Styles can't have parameters.`,
624          pos: node.getStart()
625        });
626      }
627      return undefined;
628    }
629  }
630  return updateItem;
631}
632
633function checkDecoratorMethod(node: ts.MethodDeclaration, modifiers: readonly ts.Modifier[], log: LogInfo[]): void {
634  if (modifiers && modifiers.length) {
635    for (let i = 0; i < modifiers.length; i++) {
636      if (modifiers[i].kind && modifiers[i].kind === ts.SyntaxKind.StaticKeyword) {
637        log.push({
638          type: LogType.ERROR,
639          message: `Static methods in custom components cannot be decorated by @LocalBuilder.`,
640          pos: node.getStart()
641        });
642        return;
643      }
644    }
645  }
646}
647
648export function isBuilderOrLocalBuilder(node: ts.MethodDeclaration | ts.FunctionDeclaration, builderCondition: builderConditionType,
649  customBuilder: ts.Decorator[] = undefined): boolean {
650  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
651  if (decorators && decorators.length) {
652    for (let i = 0; i < decorators.length; i++) {
653      const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').replace(/\s*/g, '').trim();
654      if ([COMPONENT_LOCAL_BUILDER_DECORATOR, COMPONENT_BUILDER_DECORATOR].includes(originalDecortor)) {
655        if (originalDecortor === COMPONENT_BUILDER_DECORATOR) {
656          builderCondition.isBuilder = true;
657          if (customBuilder) {
658            customBuilder.push(...decorators.slice(i + 1), ...decorators.slice(0, i));
659          }
660        } else {
661          builderCondition.isLocalBuilder = true;
662        }
663        return true;
664      }
665    }
666  }
667  return false;
668}
669
670function localBuilderNode(node: ts.MethodDeclaration, componentBlock: ts.Block): ts.PropertyDeclaration {
671  return ts.factory.createPropertyDeclaration(
672    undefined, node.name, undefined, undefined,
673    ts.factory.createArrowFunction(
674      undefined, undefined, node.parameters ? node.parameters : [], undefined,
675      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
676      componentBlock
677    )
678  );
679}
680
681export function createParentParameter(): ts.ParameterDeclaration {
682  return ts.factory.createParameterDeclaration(undefined, undefined,
683    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), undefined, undefined,
684    ts.factory.createIdentifier(NULL));
685}
686
687export function processBuildMember(node: ts.MethodDeclaration | ts.FunctionDeclaration, context: ts.TransformationContext,
688  log: LogInfo[], isBuilder = false): ts.MethodDeclaration | ts.FunctionDeclaration {
689  return ts.visitNode(node, visitBuild);
690  function visitBuild(node: ts.Node): ts.Node {
691    if (isGeometryView(node)) {
692      node = processGeometryView(node as ts.ExpressionStatement, log);
693    }
694    if (isProperty(node)) {
695      node = createReference(node as ts.PropertyAssignment, log, isBuilder);
696    }
697    if (isNeedGetRawObject(node)) {
698      return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
699        ts.factory.createIdentifier(FOREACH_OBSERVED_OBJECT),
700        ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [node]);
701    }
702    return ts.visitEachChild(node, visitBuild, context);
703  }
704}
705
706function checkStateName(node: ts.PropertyAccessExpression): string {
707  if (node.expression && !node.expression.expression && node.name && ts.isIdentifier(node.name)) {
708    return node.name.escapedText.toString();
709  }
710  return null;
711}
712
713function isNeedGetRawObject(node: ts.Node): boolean {
714  return !isInComponentV2Context() && ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name) &&
715    stateObjectCollection.has(checkStateName(node)) && node.parent && ts.isCallExpression(node.parent) &&
716    ts.isPropertyAccessExpression(node.parent.expression) && node !== node.parent.expression &&
717    node.parent.expression.name.escapedText.toString() !== FOREACH_GET_RAW_OBJECT;
718}
719
720function isInComponentV2Context(): boolean {
721  if (componentCollection.currentClassName) {
722    const parentStructInfo: StructInfo =
723      processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName);
724    return parentStructInfo.isComponentV2;
725  }
726  return false;
727}
728
729function isGeometryView(node: ts.Node): boolean {
730  if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) {
731    const call: ts.CallExpression = node.expression;
732    const exp: ts.Expression = call.expression;
733    const args: ts.NodeArray<ts.Expression> = call.arguments;
734    if (ts.isPropertyAccessExpression(exp) && ts.isIdentifier(exp.expression) &&
735      exp.expression.escapedText.toString() === GEOMETRY_VIEW && ts.isIdentifier(exp.name) &&
736      exp.name.escapedText.toString() === COMPONENT_CREATE_FUNCTION && args && args.length === 1 &&
737      (ts.isArrowFunction(args[0]) || ts.isFunctionExpression(args[0]))) {
738      return true;
739    }
740  }
741  return false;
742}
743
744function processGeometryView(node: ts.ExpressionStatement,
745  log: LogInfo[]): ts.ExpressionStatement {
746  const exp: ts.CallExpression = node.expression as ts.CallExpression;
747  const arg: ts.ArrowFunction | ts.FunctionExpression =
748    exp.arguments[0] as ts.ArrowFunction | ts.FunctionExpression;
749  return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(exp,
750    exp.expression, undefined, [ts.factory.createArrowFunction(undefined, undefined, arg.parameters,
751      undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
752      getGeometryReaderFunctionBlock(arg, log))]));
753}
754
755function getGeometryReaderFunctionBlock(node: ts.ArrowFunction | ts.FunctionExpression,
756  log: LogInfo[]): ts.Block {
757  let blockNode: ts.Block;
758  if (ts.isBlock(node.body)) {
759    blockNode = node.body;
760  } else if (ts.isArrowFunction(node) && ts.isCallExpression(node.body)) {
761    blockNode = ts.factory.createBlock([ts.factory.createExpressionStatement(node.body)]);
762  }
763  return processComponentBlock(blockNode, false, log);
764}
765
766export function updateHeritageClauses(node: ts.StructDeclaration, log: LogInfo[],
767  isComponentV2: boolean = false): ts.NodeArray<ts.HeritageClause> {
768  if (node.heritageClauses && !checkHeritageClauses(node)) {
769    log.push({
770      type: LogType.ERROR,
771      message: 'The struct component is not allowed to extends other class or implements other interface.',
772      pos: node.heritageClauses.pos
773    });
774  }
775  const result: ts.HeritageClause[] = [];
776  const heritageClause: ts.HeritageClause = createHeritageClause(isComponentV2);
777  result.push(heritageClause);
778  return ts.factory.createNodeArray(result);
779}
780
781function checkHeritageClauses(node: ts.StructDeclaration): boolean {
782  if (node.heritageClauses.length === 1 && node.heritageClauses[0].types &&
783    node.heritageClauses[0].types.length === 1) {
784    const expressionNode: ts.ExpressionWithTypeArguments = node.heritageClauses[0].types[0];
785    if (expressionNode.expression && ts.isIdentifier(expressionNode.expression) &&
786      expressionNode.expression.escapedText.toString() === CUSTOM_COMPONENT) {
787      return true;
788    }
789  }
790  return false;
791}
792
793export function isProperty(node: ts.Node): Boolean {
794  if (judgmentParentType(node)) {
795    if (node.parent.parent.expression && ts.isIdentifier(node.parent.parent.expression) &&
796      !BUILDIN_STYLE_NAMES.has(node.parent.parent.expression.escapedText.toString()) &&
797      componentCollection.customComponents.has(
798        node.parent.parent.expression.escapedText.toString())) {
799      return true;
800    } else if (ts.isPropertyAccessExpression(node.parent.parent.expression) &&
801      ts.isIdentifier(node.parent.parent.expression.expression) &&
802      componentCollection.customComponents.has(
803        node.parent.parent.expression.name.escapedText.toString())) {
804      return true;
805    }
806  }
807  return false;
808}
809
810function judgmentParentType(node: ts.Node): boolean {
811  return ts.isPropertyAssignment(node) && node.name && ts.isIdentifier(node.name) &&
812    node.parent && ts.isObjectLiteralExpression(node.parent) && node.parent.parent &&
813    (ts.isCallExpression(node.parent.parent) || ts.isEtsComponentExpression(node.parent.parent));
814}
815
816export function createReference(node: ts.PropertyAssignment, log: LogInfo[], isBuilder = false,
817  isParamsLambda: boolean = false, isRecycleComponent: boolean = false): ts.PropertyAssignment {
818  const linkParentComponent: string[] = getParentNode(node, linkCollection).slice(1);
819  const propertyName: ts.Identifier = node.name as ts.Identifier;
820  let initText: string;
821  const LINK_REG: RegExp = /^\$/g;
822  if (isRecycleComponent && ts.isShorthandPropertyAssignment(node)) {
823    return node;
824  }
825  const initExpression: ts.Expression = node.initializer;
826  let is$$: boolean = false;
827  if (ts.isIdentifier(initExpression) &&
828    initExpression.escapedText.toString().match(LINK_REG)) {
829    initText = initExpression.escapedText.toString().replace(LINK_REG, '');
830  } else if (ts.isPropertyAccessExpression(initExpression) && initExpression.expression &&
831    initExpression.expression.kind === ts.SyntaxKind.ThisKeyword &&
832    ts.isIdentifier(initExpression.name) && initExpression.name.escapedText.toString().match(LINK_REG)) {
833    initText = initExpression.name.escapedText.toString().replace(LINK_REG, '');
834  } else if (isBuilder && ts.isPropertyAccessExpression(initExpression) && initExpression.expression &&
835    ts.isIdentifier(initExpression.expression) && initExpression.expression.escapedText.toString() === $$ &&
836    ts.isIdentifier(initExpression.name) && linkParentComponent.includes(propertyName.escapedText.toString())) {
837    is$$ = true;
838    initText = initExpression.name.escapedText.toString();
839  } else if (isMatchInitExpression(initExpression) &&
840    linkParentComponent.includes(propertyName.escapedText.toString())) {
841    initText = initExpression.name.escapedText.toString().replace(LINK_REG, '');
842  }
843  if (initText) {
844    node = addDoubleUnderline(node, propertyName, initText, is$$, isParamsLambda, isRecycleComponent);
845  }
846  return node;
847}
848
849function isMatchInitExpression(initExpression: ts.Expression): boolean {
850  return ts.isPropertyAccessExpression(initExpression) &&
851    initExpression.expression &&
852    initExpression.expression.kind === ts.SyntaxKind.ThisKeyword &&
853    ts.isIdentifier(initExpression.name);
854}
855
856function addDoubleUnderline(node: ts.PropertyAssignment, propertyName: ts.Identifier,
857  initText: string, is$$ = false, isParamsLambda: boolean, isRecycleComponent: boolean): ts.PropertyAssignment {
858  return ts.factory.updatePropertyAssignment(node, propertyName,
859    ts.factory.createPropertyAccessExpression(
860      is$$ && partialUpdateConfig.partialUpdateMode ? ts.factory.createIdentifier($$) : ts.factory.createThis(),
861      isParamsLambda || isRecycleComponent ? ts.factory.createIdentifier(initText) : ts.factory.createIdentifier(`__${initText}`)));
862}
863
864function getParentNode(node: ts.PropertyAssignment, collection: Map<string, Set<string>>): string[] {
865  const grandparentNode: ts.NewExpression = node.parent.parent as ts.NewExpression;
866  const grandparentExpression: ts.Identifier | ts.PropertyAccessExpression =
867    grandparentNode.expression as ts.Identifier | ts.PropertyAccessExpression;
868  let parentComponent: Set<string> = new Set();
869  let grandparentName: string;
870  if (ts.isIdentifier(grandparentExpression)) {
871    grandparentName = grandparentExpression.escapedText.toString();
872    parentComponent = collection.get(grandparentName);
873  } else if (ts.isPropertyAccessExpression(grandparentExpression)) {
874    if (storedFileInfo.isAsPageImport) {
875      grandparentName = grandparentExpression.getText();
876    } else {
877      grandparentName = grandparentExpression.name.escapedText.toString();
878    }
879    parentComponent = collection.get(grandparentName);
880  } else {
881    // ignore
882  }
883  if (!parentComponent) {
884    parentComponent = new Set();
885  }
886  return [grandparentName, ...parentComponent];
887}
888
889function addUpdateParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier):
890  ts.MethodDeclaration {
891  return createParamsInitBlock(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, statements, parentComponentName);
892}
893
894function addInitialParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration {
895  return createParamsInitBlock(COMPONENT_CONSTRUCTOR_INITIAL_PARAMS, statements, parentComponentName);
896}
897
898function addUpdateStateVarsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): ts.MethodDeclaration {
899  return createParamsInitBlock(COMPONENT_UPDATE_STATE_VARS, statements, parentComponentName);
900}
901
902function addPurgeVariableDepFunc(statements: ts.Statement[]): ts.MethodDeclaration {
903  return ts.factory.createMethodDeclaration(
904    undefined, undefined,
905    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PURGE_VARIABLE_DEP),
906    undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined,
907      ts.factory.createIdentifier(RMELMTID), undefined, undefined, undefined)], undefined,
908    ts.factory.createBlock(statements, true));
909}
910
911function addDeleteParamsFunc(statements: ts.PropertyDeclaration[],
912  updateRecyle: boolean = false): ts.MethodDeclaration {
913  const deleteStatements: ts.ExpressionStatement[] = [];
914  const updateStatements: ts.ExpressionStatement[] = [];
915  statements.forEach((statement: ts.PropertyDeclaration) => {
916    const name: ts.Identifier = statement.name as ts.Identifier;
917    let paramsStatement: ts.ExpressionStatement;
918    const decorators: readonly ts.Decorator[] = ts.getAllDecorators(statement);
919    const isRegular: boolean = isRegularProperty(decorators);
920    if (!partialUpdateConfig.partialUpdateMode || !isRegular) {
921      paramsStatement = createParamsWithUnderlineStatement(name);
922    }
923    if (partialUpdateConfig.partialUpdateMode && !isRegular) {
924      updateStatements.push(createElmtIdWithUnderlineStatement(name));
925    }
926    deleteStatements.push(paramsStatement);
927  });
928  if (partialUpdateConfig.partialUpdateMode && updateRecyle) {
929    return createRecycleElmt(updateStatements);
930  }
931  const defaultStatement: ts.ExpressionStatement =
932    ts.factory.createExpressionStatement(ts.factory.createCallExpression(
933      ts.factory.createPropertyAccessExpression(
934        ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
935          ts.factory.createIdentifier(CREATE_CONSTRUCTOR_SUBSCRIBER_MANAGER),
936          ts.factory.createIdentifier(CREATE_CONSTRUCTOR_GET_FUNCTION)), undefined, []),
937        ts.factory.createIdentifier(CREATE_CONSTRUCTOR_DELETE_FUNCTION)),
938      undefined, [ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
939        ts.factory.createThis(), ts.factory.createIdentifier(
940          !partialUpdateConfig.partialUpdateMode ?
941            ABOUT_TO_BE_DELETE_FUNCTION_ID : ABOUT_TO_BE_DELETE_FUNCTION_ID__)),
942      undefined, [])]));
943  deleteStatements.push(defaultStatement);
944  if (partialUpdateConfig.partialUpdateMode) {
945    const aboutToBeDeletedInternalStatement: ts.ExpressionStatement = createDeletedInternalStatement();
946    deleteStatements.push(aboutToBeDeletedInternalStatement);
947  }
948  const deleteParamsMethod: ts.MethodDeclaration =
949    createParamsInitBlock(COMPONENT_CONSTRUCTOR_DELETE_PARAMS, deleteStatements);
950  return deleteParamsMethod;
951}
952
953function createRecycleElmt(statements: ts.Statement[]): ts.MethodDeclaration {
954  return ts.factory.createMethodDeclaration(undefined, undefined,
955    ts.factory.createIdentifier(UPDATE_RECYCLE_ELMT_ID), undefined, undefined, [
956      ts.factory.createParameterDeclaration(undefined, undefined,
957        ts.factory.createIdentifier(OLD_ELMT_ID)),
958      ts.factory.createParameterDeclaration(undefined, undefined,
959        ts.factory.createIdentifier(NEW_ELMT_ID))
960    ], undefined, ts.factory.createBlock(statements, true));
961}
962
963function createParamsWithUnderlineStatement(name: ts.Identifier): ts.ExpressionStatement {
964  return ts.factory.createExpressionStatement(
965    ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
966      ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
967        ts.factory.createIdentifier(`__${name.escapedText.toString()}`)),
968      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_DELETE_PARAMS)), undefined, []));
969}
970
971function createElmtIdWithUnderlineStatement(name: ts.Identifier): ts.ExpressionStatement {
972  return ts.factory.createExpressionStatement(
973    ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
974      ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
975        ts.factory.createIdentifier(`__${name.escapedText.toString()}`)),
976      ts.factory.createIdentifier(COMPONENT_UPDATE_ELMT_ID)), undefined, [
977      ts.factory.createIdentifier(OLD_ELMT_ID), ts.factory.createIdentifier(NEW_ELMT_ID)
978    ]));
979}
980
981function createDeletedInternalStatement(): ts.ExpressionStatement {
982  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
983    ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
984      ts.factory.createIdentifier(ABOUTTOBEDELETEDINTERNAL)), undefined, []));
985}
986
987export function addRerenderFunc(statements: ts.Statement[]): ts.MethodDeclaration {
988  const updateDirtyElementStatement: ts.Statement = ts.factory.createExpressionStatement(
989    ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
990      ts.factory.createThis(), ts.factory.createIdentifier(UPDATEDIRTYELEMENTS)), undefined, []));
991  statements.push(updateDirtyElementStatement);
992  if (storedFileInfo.hasLocalBuilderInFile) {
993    statements.unshift(contextStackPushOrPop(ts.factory.createIdentifier(PUSH), [ts.factory.createThis()]));
994    statements.push(contextStackPushOrPop(ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), []));
995  }
996  return ts.factory.createMethodDeclaration(undefined, undefined,
997    ts.factory.createIdentifier(COMPONENT_RERENDER_FUNCTION), undefined, undefined, [], undefined,
998    ts.factory.createBlock(statements, true));
999}
1000
1001function createParamsInitBlock(express: string, statements: ts.Statement[],
1002  parentComponentName?: ts.Identifier): ts.MethodDeclaration {
1003  const methodDeclaration: ts.MethodDeclaration = ts.factory.createMethodDeclaration(
1004    undefined, undefined, ts.factory.createIdentifier(express), undefined, undefined,
1005    [ts.factory.createParameterDeclaration(undefined, undefined,
1006      express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined :
1007        ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), undefined,
1008      express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined :
1009        ts.factory.createTypeReferenceNode(
1010          ts.factory.createIdentifier(parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined),
1011      undefined)], undefined, ts.factory.createBlock(statements, true));
1012  return methodDeclaration;
1013}
1014
1015export function validateBuildMethodCount(buildCount: BuildCount, parentComponentName: ts.Identifier,
1016  log: LogInfo[]): void {
1017  if (buildCount.count !== 1) {
1018    log.push({
1019      type: LogType.ERROR,
1020      message: `struct '${parentComponentName.getText()}' must be at least or at most one 'build' method.` +
1021        `Solutions:>A structurally modified page must have at least one and no more than one'build' method.`,
1022      pos: parentComponentName.getStart()
1023    });
1024  }
1025}
1026
1027function validateHasController(componentName: ts.Identifier, checkController: ControllerType,
1028  log: LogInfo[]): void {
1029  if (!checkController.hasController) {
1030    log.push({
1031      type: LogType.ERROR,
1032      message: '@CustomDialog component should have a property of the CustomDialogController type.',
1033      pos: componentName.pos
1034    });
1035  }
1036}
1037
1038function createHeritageClause(isComponentV2: boolean = false): ts.HeritageClause {
1039  if (partialUpdateConfig.partialUpdateMode) {
1040    return ts.factory.createHeritageClause(
1041      ts.SyntaxKind.ExtendsKeyword,
1042      [ts.factory.createExpressionWithTypeArguments(
1043        ts.factory.createIdentifier(isComponentV2 ? constantDefine.STRUCT_PARENT : BASE_COMPONENT_NAME_PU),
1044        [])]
1045    );
1046  }
1047  return ts.factory.createHeritageClause(
1048    ts.SyntaxKind.ExtendsKeyword,
1049    [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier(BASE_COMPONENT_NAME), [])]
1050  );
1051}
1052
1053function createTypeReference(decoratorName: string, type: ts.TypeNode, log: LogInfo[],
1054  program: ts.Program): ts.TypeNode {
1055  let newType: ts.TypeNode;
1056  switch (decoratorName) {
1057    case COMPONENT_STATE_DECORATOR:
1058    case COMPONENT_PROVIDE_DECORATOR:
1059      newType = ts.factory.createTypeReferenceNode(
1060        isSimpleType(type, program, log) ?
1061          OBSERVED_PROPERTY_SIMPLE :
1062          OBSERVED_PROPERTY_OBJECT,
1063        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1064      );
1065      break;
1066    case COMPONENT_LINK_DECORATOR:
1067    case COMPONENT_CONSUME_DECORATOR:
1068      newType = ts.factory.createTypeReferenceNode(
1069        isSimpleType(type, program, log) ?
1070          SYNCHED_PROPERTY_SIMPLE_TWO_WAY :
1071          SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
1072        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1073      );
1074      break;
1075    case COMPONENT_PROP_DECORATOR:
1076      newType = ts.factory.createTypeReferenceNode(
1077        SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
1078        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1079      );
1080      break;
1081    case COMPONENT_OBJECT_LINK_DECORATOR:
1082      newType = ts.factory.createTypeReferenceNode(
1083        SYNCHED_PROPERTY_NESED_OBJECT,
1084        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1085      );
1086      break;
1087    case COMPONENT_STORAGE_PROP_DECORATOR:
1088    case COMPONENT_STORAGE_LINK_DECORATOR:
1089      newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [
1090        type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1091      ]);
1092      break;
1093    case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR:
1094    case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR:
1095      newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [
1096        type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1097      ]);
1098      break;
1099  }
1100  return newType;
1101}
1102
1103function createTypeReferencePU(decoratorName: string, type: ts.TypeNode, log: LogInfo[],
1104  program: ts.Program): ts.TypeNode {
1105  let newType: ts.TypeNode;
1106  switch (decoratorName) {
1107    case COMPONENT_STATE_DECORATOR:
1108    case COMPONENT_PROVIDE_DECORATOR:
1109      newType = ts.factory.createTypeReferenceNode(
1110        isSimpleType(type, program, log) ?
1111          OBSERVED_PROPERTY_SIMPLE_PU :
1112          OBSERVED_PROPERTY_OBJECT_PU,
1113        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1114      );
1115      break;
1116    case COMPONENT_LINK_DECORATOR:
1117      newType = ts.factory.createTypeReferenceNode(
1118        isSimpleType(type, program, log) ?
1119          SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU :
1120          SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU,
1121        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1122      );
1123      break;
1124    case COMPONENT_PROP_DECORATOR:
1125      newType = ts.factory.createTypeReferenceNode(
1126        SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU,
1127        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1128      );
1129      break;
1130    case COMPONENT_OBJECT_LINK_DECORATOR:
1131      newType = ts.factory.createTypeReferenceNode(
1132        SYNCHED_PROPERTY_NESED_OBJECT_PU,
1133        [type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
1134      );
1135      break;
1136    case COMPONENT_CONSUME_DECORATOR:
1137    case COMPONENT_STORAGE_PROP_DECORATOR:
1138    case COMPONENT_STORAGE_LINK_DECORATOR:
1139      newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [
1140        type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1141      ]);
1142      break;
1143    case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR:
1144    case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR:
1145      newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT_PU, [
1146        type || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
1147      ]);
1148      break;
1149  }
1150  return newType;
1151}
1152
1153export function checkFinalizeConstruction(): ts.Statement {
1154  return ts.factory.createIfStatement(
1155    ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken,
1156      ts.factory.createParenthesizedExpression(ts.factory.createBinaryExpression(
1157        ts.factory.createStringLiteral(FINALIZE_CONSTRUCTION),
1158        ts.factory.createToken(ts.SyntaxKind.InKeyword), ts.factory.createPropertyAccessExpression(
1159          ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), ts.factory.createIdentifier(PROTOTYPE))))
1160    ),
1161    ts.factory.createBlock(
1162      [
1163        ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1164          ts.factory.createPropertyAccessExpression(
1165            ts.factory.createIdentifier(REFLECT), ts.factory.createIdentifier(CREATE_SET_METHOD)
1166          ), undefined,
1167          [
1168            ts.factory.createPropertyAccessExpression(
1169              ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU), ts.factory.createIdentifier(PROTOTYPE)
1170            ),
1171            ts.factory.createStringLiteral(FINALIZE_CONSTRUCTION),
1172            ts.factory.createArrowFunction(undefined, undefined, [], undefined,
1173              ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
1174              ts.factory.createBlock([], false)
1175            )
1176          ]
1177        ))
1178      ], true), undefined);
1179}
1180
1181export function checkContextStack(): ts.Statement {
1182  return ts.factory.createIfStatement(
1183    ts.factory.createBinaryExpression(
1184      ts.factory.createPropertyAccessExpression(
1185        ts.factory.createIdentifier(PUV2_VIEW_BASE),
1186        ts.factory.createIdentifier(CONTEXT_STACK)
1187      ),
1188      ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
1189      ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)
1190    ),
1191    ts.factory.createBlock(
1192      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1193        ts.factory.createPropertyAccessExpression(
1194          ts.factory.createIdentifier(REFLECT),
1195          ts.factory.createIdentifier(CREATE_SET_METHOD)
1196        ),
1197        undefined,
1198        [
1199          ts.factory.createIdentifier(PUV2_VIEW_BASE),
1200          ts.factory.createStringLiteral(CONTEXT_STACK),
1201          ts.factory.createArrayLiteralExpression(
1202            [],
1203            false
1204          )
1205        ]
1206      ))],
1207      true
1208    ),
1209    undefined
1210  );
1211}
1212
1213export function contextStackPushOrPop(pushOrPop: ts.Identifier, param: ts.ThisExpression[] | []): ts.ExpressionStatement {
1214  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
1215    ts.factory.createPropertyAccessExpression(
1216      ts.factory.createIdentifier(PUV2_VIEW_BASE),
1217      ts.factory.createIdentifier(CONTEXT_STACK)
1218    ),
1219    ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1220    ts.factory.createCallExpression(
1221      ts.factory.createPropertyAccessExpression(
1222        ts.factory.createPropertyAccessExpression(
1223          ts.factory.createIdentifier(PUV2_VIEW_BASE),
1224          ts.factory.createIdentifier(CONTEXT_STACK)
1225        ),
1226        pushOrPop
1227      ),
1228      undefined,
1229      param
1230    )
1231  ));
1232}
1233