• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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