• 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      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