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