• 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';
17import path from 'path';
18
19import {
20  COMPONENT_RENDER_FUNCTION,
21  COMPONENT_CREATE_FUNCTION,
22  COMPONENT_POP_FUNCTION,
23  COMPONENT_BUTTON,
24  COMPONENT_CREATE_LABEL_FUNCTION,
25  COMPONENT_CREATE_CHILD_FUNCTION,
26  COMPONENT_FOREACH,
27  COMPONENT_LAZYFOREACH,
28  IS_RENDERING_IN_PROGRESS,
29  FOREACH_OBSERVED_OBJECT,
30  FOREACH_GET_RAW_OBJECT,
31  COMPONENT_IF,
32  COMPONENT_IF_BRANCH_ID_FUNCTION,
33  COMPONENT_IF_UNDEFINED,
34  ATTRIBUTE_ANIMATION,
35  GLOBAL_CONTEXT,
36  COMPONENT_GESTURE,
37  COMPONENT_GESTURE_GROUP,
38  GESTURE_ATTRIBUTE,
39  PARALLEL_GESTURE_ATTRIBUTE,
40  PRIORITY_GESTURE_ATTRIBUTE,
41  GESTURE_ENUM_KEY,
42  GESTURE_ENUM_VALUE_HIGH,
43  GESTURE_ENUM_VALUE_LOW,
44  GESTURE_ENUM_VALUE_PARALLEL,
45  COMPONENT_TRANSITION_NAME,
46  COMPONENT_DEBUGLINE_FUNCTION,
47  ATTRIBUTE_STATESTYLES,
48  THIS,
49  VISUAL_STATE,
50  VIEW_STACK_PROCESSOR,
51  STYLE_ADD_DOUBLE_DOLLAR,
52  $$_VALUE,
53  $$_CHANGE_EVENT,
54  $$_THIS,
55  $$_NEW_VALUE,
56  BUILDER_ATTR_NAME,
57  BUILDER_ATTR_BIND,
58  CUSTOM_DIALOG_CONTROLLER_BUILDER,
59  BIND_DRAG_SET,
60  BIND_POPUP_SET,
61  $$,
62  PROPERTIES_ADD_DOUBLE_DOLLAR,
63  RESOURCE
64} from './pre_define';
65import {
66  INNER_COMPONENT_NAMES,
67  BUILDIN_CONTAINER_COMPONENT,
68  BUILDIN_STYLE_NAMES,
69  CUSTOM_BUILDER_METHOD,
70  GESTURE_ATTRS,
71  GESTURE_TYPE_NAMES,
72  EXTEND_ATTRIBUTE,
73  NO_DEBUG_LINE_COMPONENT,
74  NEEDPOP_COMPONENT,
75  INNER_STYLE_FUNCTION,
76  GLOBAL_STYLE_FUNCTION,
77  COMMON_ATTRS,
78  CUSTOM_BUILDER_PROPERTIES
79} from './component_map';
80import { componentCollection } from './validate_ui_syntax';
81import { processCustomComponent } from './process_custom_component';
82import {
83  LogType,
84  LogInfo,
85  componentInfo,
86  createFunction
87} from './utils';
88import { builderParamObjectCollection } from './process_component_member';
89import { projectConfig } from '../main';
90import { transformLog, contextGlobal } from './process_ui_syntax';
91import { props } from './compile_info';
92
93export function processComponentBuild(node: ts.MethodDeclaration,
94  log: LogInfo[]): ts.MethodDeclaration {
95  let newNode: ts.MethodDeclaration;
96  const renderNode: ts.Identifier = ts.factory.createIdentifier(COMPONENT_RENDER_FUNCTION);
97  if (node.body && node.body.statements && node.body.statements.length &&
98    validateRootNode(node, log)) {
99    newNode = ts.factory.updateMethodDeclaration(node, node.decorators, node.modifiers,
100      node.asteriskToken, renderNode, node.questionToken, node.typeParameters, node.parameters,
101      node.type, processComponentBlock(node.body, false, log));
102  } else {
103    newNode = ts.factory.updateMethodDeclaration(node, node.decorators, node.modifiers,
104      node.asteriskToken, renderNode, node.questionToken, node.typeParameters, node.parameters,
105      node.type, node.body);
106  }
107  return newNode;
108}
109
110export function processComponentBlock(node: ts.Block,
111  isLazy: boolean, log: LogInfo[], isTransition: boolean = false,
112  forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): ts.Block {
113  const newStatements: ts.Statement[] = [];
114  processComponentChild(node, newStatements, log,
115    {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, forEachParameters);
116  if (isLazy) {
117    newStatements.unshift(createRenderingInProgress(true));
118  }
119  if (isTransition) {
120    newStatements.unshift(ts.factory.createExpressionStatement(
121      createFunction(ts.factory.createIdentifier(COMPONENT_TRANSITION_NAME),
122        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)));
123    newStatements.push(ts.factory.createExpressionStatement(
124      createFunction(ts.factory.createIdentifier(COMPONENT_TRANSITION_NAME),
125        ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
126  }
127  if (isLazy) {
128    newStatements.push(createRenderingInProgress(false));
129  }
130  return ts.factory.updateBlock(node, newStatements);
131}
132
133function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean {
134  let isValid: boolean = false;
135  if (node.body.statements.length === 1) {
136    const statement: ts.Statement = node.body.statements[0];
137    if (ts.isIfStatement(statement) || validateFirstNode(statement)) {
138      isValid = true;
139    }
140  } else {
141    isValid = false;
142  }
143  if (!isValid) {
144    log.push({
145      type: LogType.ERROR,
146      message: `There should have a root container component.`,
147      pos: node.body.statements.pos
148    });
149  }
150  return isValid;
151}
152
153function validateFirstNode(node: ts.Statement): boolean {
154  const isEntryComponent: boolean =
155    componentCollection.entryComponent === componentCollection.currentClassName;
156  if (isEntryComponent && !validateContainerComponent(node)) {
157    return false;
158  }
159  return true;
160}
161
162function validateContainerComponent(node: ts.Statement): boolean {
163  if (ts.isExpressionStatement(node) && node.expression &&
164    (ts.isEtsComponentExpression(node.expression) || ts.isCallExpression(node.expression))) {
165    const nameResult: NameResult = { name: null };
166    validateEtsComponentNode(node.expression, nameResult);
167    if (nameResult.name && BUILDIN_CONTAINER_COMPONENT.has(nameResult.name)) {
168      return true;
169    }
170  }
171  return false;
172}
173
174interface supplementType {
175  isAcceleratePreview: boolean,
176  line: number,
177  column: number,
178  fileName: string
179}
180
181let newsupplement: supplementType = {
182  isAcceleratePreview: false,
183  line: 0,
184  column: 0,
185  fileName: ''
186};
187
188type NameResult = {
189  name: string
190}
191
192function validateEtsComponentNode(node: ts.CallExpression | ts.EtsComponentExpression, result?: NameResult) {
193  let childNode: ts.Node = node;
194  result.name = null;
195  while (ts.isCallExpression(childNode) && childNode.expression &&
196    ts.isPropertyAccessExpression(childNode.expression) && childNode.expression.expression) {
197    childNode = childNode.expression.expression;
198  }
199  if (ts.isEtsComponentExpression(childNode)) {
200    if (ts.isIdentifier(childNode.expression)) {
201      result.name = childNode.expression.getText();
202    }
203    return true;
204  } else {
205    return false;
206  }
207}
208
209let sourceNode: ts.SourceFile;
210
211export function processComponentChild(node: ts.Block | ts.SourceFile, newStatements: ts.Statement[],
212  log: LogInfo[], supplement: supplementType = {isAcceleratePreview: false, line: 0, column: 0, fileName: ''},
213  forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): void {
214  if (supplement.isAcceleratePreview) {
215    newsupplement = supplement;
216    const compilerOptions = ts.readConfigFile(
217      path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
218    Object.assign(compilerOptions, {
219      'sourceMap': false
220    });
221    sourceNode = ts.createSourceFile('', node.getText(), ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS, compilerOptions);
222  }
223  if (node.statements.length) {
224    node.statements.forEach((item) => {
225      if (ts.isExpressionStatement(item)) {
226        const name: string = getName(item);
227        switch (getComponentType(item, log, name, forEachParameters)) {
228          case ComponentType.innerComponent:
229            processInnerComponent(item, newStatements, log);
230            break;
231          case ComponentType.customComponent:
232            if (!newsupplement.isAcceleratePreview) {
233              if (item.expression && ts.isEtsComponentExpression(item.expression) && item.expression.body) {
234                if (processExpressionStatementChange(item, item.expression.body, log)) {
235                  item = processExpressionStatementChange(item, item.expression.body, log);
236                }
237              }
238              processCustomComponent(item as ts.ExpressionStatement, newStatements, log);
239            }
240            break;
241          case ComponentType.forEachComponent:
242            processForEachComponent(item, newStatements, log);
243            break;
244          case ComponentType.customBuilderMethod:
245          case ComponentType.builderParamMethod:
246            newStatements.push(item);
247            break;
248        }
249      } else if (ts.isIfStatement(item)) {
250        processIfStatement(item, newStatements, log);
251      } else if (!ts.isBlock(item)) {
252        log.push({
253          type: LogType.ERROR,
254          message: `Only UI component syntax can be written in build method.`,
255          pos: item.getStart()
256        });
257      }
258    });
259  }
260  if (supplement.isAcceleratePreview) {
261    newsupplement = {
262      isAcceleratePreview: false,
263      line: 0,
264      column: 0,
265      fileName: ''
266    };
267  }
268}
269
270function processExpressionStatementChange(node: ts.ExpressionStatement, nextNode: ts.Block,
271  log: LogInfo[]): ts.ExpressionStatement {
272  let name: string;
273  // @ts-ignore
274  if (node.expression.expression && ts.isIdentifier(node.expression.expression)) {
275    name = node.expression.expression.escapedText.toString();
276  }
277  if (builderParamObjectCollection.get(name) &&
278    builderParamObjectCollection.get(name).size === 1) {
279    return processBlockToExpression(node, nextNode, log, name);
280  } else {
281    log.push({
282      type: LogType.ERROR,
283      message: `In the trailing lambda case, '${name}' must have one and only one property decorated with `
284        + `@BuilderParam, and its @BuilderParam expects no parameter.`,
285      pos: node.getStart()
286    });
287  }
288}
289
290function processBlockToExpression(node: ts.ExpressionStatement, nextNode: ts.Block,
291  log: LogInfo[], name: string): ts.ExpressionStatement {
292  const childParam: string = [...builderParamObjectCollection.get(name)].slice(-1)[0];
293  const newBlock: ts.Block = processComponentBlock(nextNode, false, log);
294  const arrowNode: ts.ArrowFunction = ts.factory.createArrowFunction(undefined, undefined,
295    [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), newBlock);
296  const newPropertyAssignment:ts.PropertyAssignment = ts.factory.createPropertyAssignment(
297    ts.factory.createIdentifier(childParam), arrowNode);
298  // @ts-ignore
299  let argumentsArray: ts.ObjectLiteralExpression[] = node.expression.arguments;
300  if (argumentsArray && !argumentsArray.length) {
301    argumentsArray = [ts.factory.createObjectLiteralExpression([newPropertyAssignment], true)];
302  } else {
303    argumentsArray = [ts.factory.createObjectLiteralExpression(
304      // @ts-ignore
305      node.expression.arguments[0].properties.concat([newPropertyAssignment]), true)];
306  }
307  node = ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(
308    // @ts-ignore
309    node.expression, node.expression.expression, node.expression.expression.typeArguments,
310    argumentsArray));
311  return node;
312}
313
314type EtsComponentResult = {
315  etsComponentNode: ts.EtsComponentExpression;
316  hasAttr: boolean;
317}
318function parseEtsComponentExpression(node: ts.ExpressionStatement): EtsComponentResult {
319  let etsComponentNode: ts.EtsComponentExpression;
320  let hasAttr: boolean = false;
321  let temp: any = node.expression;
322  while (temp) {
323    if (ts.isCallExpression(temp) && temp.expression &&
324      ts.isPropertyAccessExpression(temp.expression)) {
325      hasAttr = true;
326    }
327    if (ts.isEtsComponentExpression(temp)) {
328      etsComponentNode = temp;
329      break;
330    }
331    temp = temp.expression;
332  }
333  return { etsComponentNode: etsComponentNode, hasAttr: hasAttr };
334}
335
336function processInnerComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], log: LogInfo[]): void {
337  const res: CreateResult = createComponent(node, COMPONENT_CREATE_FUNCTION);
338  newStatements.push(res.newNode);
339  const nameResult: NameResult = { name: null };
340  validateEtsComponentNode(node.expression as ts.EtsComponentExpression, nameResult);
341  if (projectConfig.isPreview && nameResult.name && !NO_DEBUG_LINE_COMPONENT.has(nameResult.name)) {
342    let posOfNode: ts.LineAndCharacter;
343    let curFileName: string;
344    let line: number = 1;
345    let col: number = 1;
346    if (sourceNode && newsupplement.isAcceleratePreview) {
347      posOfNode = sourceNode.getLineAndCharacterOfPosition(getRealNodePos(node) - 22);
348      curFileName = newsupplement.fileName;
349      if (posOfNode.line === 0) {
350        col = newsupplement.column - 1;
351      }
352      line = newsupplement.line;
353    } else {
354      posOfNode = transformLog.sourceFile.getLineAndCharacterOfPosition(getRealNodePos(node));
355      curFileName = transformLog.sourceFile.fileName.replace(/\.ts$/, '');
356    }
357    const projectPath: string = projectConfig.projectPath;
358    const debugInfo: string =
359      `${path.relative(projectPath, curFileName).replace(/\\+/g, '/')}` +
360      `(${posOfNode.line + line}:${posOfNode.character + col})`;
361    const debugNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(
362      createFunction(ts.factory.createIdentifier(nameResult.name),
363        ts.factory.createIdentifier(COMPONENT_DEBUGLINE_FUNCTION),
364        ts.factory.createNodeArray([ts.factory.createStringLiteral(debugInfo)])));
365    newStatements.push(debugNode);
366  }
367  const etsComponentResult: EtsComponentResult = parseEtsComponentExpression(node);
368  if (PROPERTIES_ADD_DOUBLE_DOLLAR.has(res.identifierNode.getText()) &&
369    etsComponentResult.etsComponentNode.arguments && etsComponentResult.etsComponentNode.arguments.length) {
370    etsComponentResult.etsComponentNode = processDollarEtsComponent(etsComponentResult.etsComponentNode,
371      res.identifierNode.getText());
372  }
373  if (etsComponentResult.etsComponentNode.body && ts.isBlock(etsComponentResult.etsComponentNode.body)) {
374    if (res.isButton) {
375      checkButtonParamHasLabel(etsComponentResult.etsComponentNode, log);
376      if (projectConfig.isPreview) {
377        newStatements.splice(-2, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode);
378      } else {
379        newStatements.splice(-1, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode);
380      }
381    }
382    if (etsComponentResult.hasAttr) {
383      bindComponentAttr(node, res.identifierNode, newStatements, log);
384    }
385    processComponentChild(etsComponentResult.etsComponentNode.body, newStatements, log);
386  } else {
387    bindComponentAttr(node, res.identifierNode, newStatements, log);
388  }
389  if (res.isContainerComponent || res.needPop) {
390    newStatements.push(createComponent(node, COMPONENT_POP_FUNCTION).newNode);
391  }
392}
393
394function getRealNodePos(node: ts.Node): number {
395  // @ts-ignore
396  if (node.pos === -1 && node.expression) {
397    // @ts-ignore
398    return getRealNodePos(node.expression);
399  } else {
400    return node.getStart();
401  }
402}
403
404function processForEachComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
405  log: LogInfo[]): void {
406  const popNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(createFunction(
407    // @ts-ignore
408    node.expression.expression as ts.Identifier,
409    ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null));
410  if (ts.isCallExpression(node.expression)) {
411    const propertyNode: ts.PropertyAccessExpression = ts.factory.createPropertyAccessExpression(
412      node.expression.expression as ts.Identifier,
413      ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
414    );
415    const argumentsArray: ts.Expression[] = Array.from(node.expression.arguments);
416    let arrayObserveredObject: ts.CallExpression;
417    if (argumentsArray.length) {
418      arrayObserveredObject = ts.factory.createCallExpression(
419        ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(FOREACH_OBSERVED_OBJECT),
420          ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [argumentsArray[0]]);
421    }
422    argumentsArray.splice(0, 1, arrayObserveredObject);
423    const newArrowNode: ts.ArrowFunction = processForEachBlock(node.expression, log);
424    if (newArrowNode) {
425      argumentsArray.splice(1, 1, newArrowNode);
426    }
427    node = addForEachId(ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(
428      node.expression, propertyNode, node.expression.typeArguments, argumentsArray)));
429  }
430  newStatements.push(node, popNode);
431}
432
433function addForEachId(node: ts.ExpressionStatement): ts.ExpressionStatement {
434  const forEachComponent: ts.CallExpression = node.expression as ts.CallExpression;
435  return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(
436    forEachComponent, forEachComponent.expression, forEachComponent.typeArguments,
437    [ts.factory.createStringLiteral((++componentInfo.id).toString()), ts.factory.createThis(),
438      ...forEachComponent.arguments]));
439}
440
441function processForEachBlock(node: ts.CallExpression, log: LogInfo[]): ts.ArrowFunction {
442  if (node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) {
443    const isLazy: boolean = node.expression.getText() === COMPONENT_LAZYFOREACH;
444    const arrowNode: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction;
445    const body: ts.ConciseBody = arrowNode.body;
446    if (node.arguments.length > 2 && !ts.isArrowFunction(node.arguments[2])) {
447      log.push({
448        type: LogType.ERROR,
449        message: 'There should be wrapped in curly braces in ForEach.',
450        pos: body.getStart()
451      });
452    } else if (!ts.isBlock(body)) {
453      const statement: ts.Statement = ts.factory.createExpressionStatement(body);
454      const blockNode: ts.Block = ts.factory.createBlock([statement], true);
455      // @ts-ignore
456      statement.parent = blockNode;
457      return ts.factory.updateArrowFunction(
458        arrowNode, arrowNode.modifiers, arrowNode.typeParameters,
459        arrowNode.parameters, arrowNode.type, arrowNode.equalsGreaterThanToken,
460        processComponentBlock(blockNode, isLazy, log, false, arrowNode.parameters));
461    } else {
462      return ts.factory.updateArrowFunction(
463        arrowNode, arrowNode.modifiers, arrowNode.typeParameters,
464        arrowNode.parameters, arrowNode.type, arrowNode.equalsGreaterThanToken,
465        processComponentBlock(body, isLazy, log, false, arrowNode.parameters));
466    }
467  }
468  return null;
469}
470
471function createRenderingInProgress(isTrue: boolean): ts.ExpressionStatement {
472  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
473    ts.factory.createPropertyAccessExpression(
474      ts.factory.createThis(),
475      ts.factory.createIdentifier(IS_RENDERING_IN_PROGRESS)
476    ),
477    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
478    isTrue ? ts.factory.createTrue() : ts.factory.createFalse()
479  ));
480}
481
482function processIfStatement(node: ts.IfStatement, newStatements: ts.Statement[],
483  log: LogInfo[]): void {
484  const ifCreate: ts.ExpressionStatement = createIfCreate();
485  const newIfNode: ts.IfStatement = processInnerIfStatement(node, 0, log);
486  const ifPop: ts.ExpressionStatement = createIfPop();
487  newStatements.push(ifCreate, newIfNode, ifPop);
488}
489
490function processInnerIfStatement(node: ts.IfStatement, id: number, log: LogInfo[]): ts.IfStatement {
491  if (ts.isIdentifier(node.expression) && node.expression.originalKeywordKind === undefined &&
492    !node.expression.escapedText) {
493    log.push({
494      type: LogType.ERROR,
495      message: 'Condition expression cannot be null in if statement.',
496      pos: node.expression.getStart()
497    });
498    node = ts.factory.updateIfStatement(node, ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED),
499      node.thenStatement, node.elseStatement);
500  }
501  const newThenStatement: ts.Statement = processThenStatement(node.thenStatement, id, log);
502  const newElseStatement: ts.Statement = processElseStatement(node.elseStatement, id, log);
503  const newIfNode: ts.IfStatement = ts.factory.updateIfStatement(
504    node, node.expression, newThenStatement, newElseStatement);
505  return newIfNode;
506}
507
508function processThenStatement(thenStatement: ts.Statement, id: number,
509  log: LogInfo[]): ts.Statement {
510  if (ts.isExpressionStatement(thenStatement) && ts.isIdentifier(thenStatement.expression) &&
511    thenStatement.expression.originalKeywordKind === undefined &&
512    !thenStatement.expression.escapedText) {
513    log.push({
514      type: LogType.ERROR,
515      message: 'Then statement cannot be null in if statement.',
516      pos: thenStatement.expression.getStart()
517    });
518  }
519  if (thenStatement) {
520    if (ts.isBlock(thenStatement)) {
521      thenStatement = processIfBlock(thenStatement, id, log);
522    } else if (ts.isIfStatement(thenStatement)) {
523      thenStatement = processInnerIfStatement(thenStatement, 0, log);
524      thenStatement = ts.factory.createBlock(
525        [createIfCreate(), createIfBranchId(id), thenStatement, createIfPop()], true);
526    } else {
527      thenStatement = ts.factory.createBlock([thenStatement], true);
528      thenStatement = processIfBlock(thenStatement as ts.Block, id, log);
529    }
530  }
531  return thenStatement;
532}
533
534function processElseStatement(elseStatement: ts.Statement, id: number,
535  log: LogInfo[]): ts.Statement {
536  if (elseStatement) {
537    if (ts.isBlock(elseStatement)) {
538      elseStatement = processIfBlock(elseStatement, id + 1, log);
539    } else if (ts.isIfStatement(elseStatement)) {
540      elseStatement = processInnerIfStatement(elseStatement, id + 1, log);
541    } else {
542      elseStatement = ts.factory.createBlock([elseStatement], true);
543      elseStatement = processIfBlock(elseStatement as ts.Block, id + 1, log);
544    }
545  }
546  return elseStatement;
547}
548
549function processIfBlock(block: ts.Block, id: number, log: LogInfo[]): ts.Block {
550  return addIfBranchId(id, processComponentBlock(block, false, log));
551}
552
553function addIfBranchId(id: number, container: ts.Block): ts.Block {
554  return ts.factory.updateBlock(container, [createIfBranchId(id), ...container.statements]);
555}
556
557function createIf(): ts.Identifier {
558  return ts.factory.createIdentifier(COMPONENT_IF);
559}
560
561function createIfCreate(): ts.ExpressionStatement {
562  return ts.factory.createExpressionStatement(createFunction(createIf(),
563    ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([])));
564}
565
566function createIfPop(): ts.ExpressionStatement {
567  return ts.factory.createExpressionStatement(createFunction(createIf(),
568    ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null));
569}
570
571function createIfBranchId(id: number): ts.ExpressionStatement {
572  return ts.factory.createExpressionStatement(createFunction(createIf(),
573    ts.factory.createIdentifier(COMPONENT_IF_BRANCH_ID_FUNCTION),
574    ts.factory.createNodeArray([ts.factory.createNumericLiteral(id)])));
575}
576
577interface CreateResult {
578  newNode: ts.ExpressionStatement;
579  identifierNode: ts.Identifier;
580  isContainerComponent: boolean;
581  isButton: boolean;
582  needPop: boolean;
583}
584
585function createComponent(node: ts.ExpressionStatement, type: string): CreateResult {
586  const res: CreateResult = {
587    newNode: node,
588    identifierNode: null,
589    isContainerComponent: false,
590    isButton: false,
591    needPop: false
592  };
593  let identifierNode: ts.Identifier = ts.factory.createIdentifier(type);
594  let temp: any = node.expression;
595  while (temp && !ts.isIdentifier(temp) && temp.expression) {
596    temp = temp.expression;
597  }
598  if (temp && temp.parent && (ts.isCallExpression(temp.parent) ||
599    ts.isEtsComponentExpression(temp.parent)) && ts.isIdentifier(temp)) {
600    if (temp.getText() === COMPONENT_BUTTON && type !== COMPONENT_POP_FUNCTION) {
601      res.isButton = true;
602      identifierNode = type === COMPONENT_CREATE_CHILD_FUNCTION
603        ? ts.factory.createIdentifier(COMPONENT_CREATE_CHILD_FUNCTION)
604        : ts.factory.createIdentifier(COMPONENT_CREATE_LABEL_FUNCTION);
605    }
606    if (NEEDPOP_COMPONENT.has(temp.getText())) {
607      res.needPop = true;
608    }
609    if (BUILDIN_CONTAINER_COMPONENT.has(temp.getText())) {
610      res.isContainerComponent = true;
611    }
612    res.newNode = type === COMPONENT_POP_FUNCTION
613      ? ts.factory.updateExpressionStatement(node,
614        createFunction(temp, identifierNode, null))
615      : ts.factory.updateExpressionStatement(node,
616        createFunction(temp, identifierNode, temp.parent.arguments));
617    res.identifierNode = temp;
618  }
619  return res;
620}
621
622interface AnimationInfo {
623  statement: ts.Statement,
624  kind: boolean
625}
626
627export function bindComponentAttr(node: ts.ExpressionStatement, identifierNode: ts.Identifier,
628  newStatements: ts.Statement[], log: LogInfo[], reverse: boolean = true,
629  isStylesAttr: boolean = false): void {
630  let temp: any = node.expression;
631  const statements: ts.Statement[] = [];
632  const lastStatement: AnimationInfo = { statement: null, kind: false };
633  if (ts.isPropertyAccessExpression(temp)) {
634    log.push({
635      type: LogType.ERROR,
636      message: `'${node.getText()}' does not meet UI component syntax.`,
637      pos: node.getStart()
638    });
639  }
640  while (temp && ts.isCallExpression(temp) && temp.expression) {
641    let flag: boolean = false;
642    if (temp.expression && (validatePropertyAccessExpressionWithCustomBuilder(temp.expression) ||
643      validateIdentifierWithCustomBuilder(temp.expression))) {
644      let propertyName: string = '';
645      if (ts.isIdentifier(temp.expression)) {
646        propertyName = temp.expression.escapedText.toString();
647      } else if (ts.isPropertyAccessExpression(temp.expression)) {
648        propertyName = temp.expression.name.escapedText.toString();
649      }
650      switch (true) {
651        case BIND_POPUP_SET.has(propertyName):
652          temp = processBindPopupBuilder(temp);
653          break;
654        case BIND_DRAG_SET.has(propertyName):
655          temp = processDragStartBuilder(temp);
656          break;
657        default:
658          temp = processCustomBuilderProperty(temp);
659      }
660      flag = true;
661    }
662    if (ts.isPropertyAccessExpression(temp.expression) &&
663      temp.expression.name && ts.isIdentifier(temp.expression.name)) {
664      addComponentAttr(temp, temp.expression.name, lastStatement, statements, identifierNode, log,
665        isStylesAttr);
666      temp = temp.expression.expression;
667      flag = true;
668    } else if (ts.isIdentifier(temp.expression)) {
669      if (!INNER_COMPONENT_NAMES.has(temp.expression.getText()) &&
670        !GESTURE_TYPE_NAMES.has(temp.expression.getText())) {
671        addComponentAttr(temp, temp.expression, lastStatement, statements, identifierNode, log,
672          isStylesAttr);
673      }
674      break;
675    }
676    if (!flag) {
677      temp = temp.expression;
678    }
679  }
680  if (lastStatement.statement && lastStatement.kind) {
681    statements.push(lastStatement.statement);
682  }
683  if (statements.length) {
684    reverse ? newStatements.push(...statements.reverse()) : newStatements.push(...statements);
685  }
686}
687
688function processCustomBuilderProperty(node: ts.CallExpression): ts.CallExpression {
689  const newArguments: ts.Expression[] = [];
690  node.arguments.forEach((argument: ts.Expression | ts.Identifier, index: number) => {
691    if (index === 0 && isBuilderChangeNode(argument)) {
692      newArguments.push(parseBuilderNode(argument));
693    } else {
694      newArguments.push(argument);
695    }
696  });
697  node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments);
698  return node;
699}
700
701function isBuilderChangeNode(argument: ts.Node): boolean {
702  return ts.isPropertyAccessExpression(argument) && argument.name && ts.isIdentifier(argument.name)
703    && CUSTOM_BUILDER_METHOD.has(argument.name.getText()) ||
704    ts.isCallExpression(argument) && argument.expression && argument.expression.name &&
705    ts.isIdentifier(argument.expression.name) &&
706    CUSTOM_BUILDER_METHOD.has(argument.expression.name.getText()) || ts.isIdentifier(argument) &&
707    argument.escapedText && CUSTOM_BUILDER_METHOD.has(argument.escapedText.toString());
708}
709
710function parseBuilderNode(node: ts.Node): ts.ObjectLiteralExpression {
711  if (isPropertyAccessExpressionNode(node)) {
712    return processPropertyBuilder(node as ts.PropertyAccessExpression);
713  } else if (ts.isIdentifier(node) && CUSTOM_BUILDER_METHOD.has(node.escapedText.toString())) {
714    return processIdentifierBuilder(node);
715  } else if (ts.isCallExpression(node)) {
716    return getParsedBuilderAttrArgumentWithParams(node);
717  }
718}
719
720function isPropertyAccessExpressionNode(node: ts.Node): boolean {
721  return ts.isPropertyAccessExpression(node) && node.expression &&
722    node.expression.kind === ts.SyntaxKind.ThisKeyword && node.name && ts.isIdentifier(node.name) &&
723    CUSTOM_BUILDER_METHOD.has(node.name.escapedText.toString());
724}
725
726function processBindPopupBuilder(node: ts.CallExpression): ts.CallExpression {
727  const newArguments: ts.Expression[] = [];
728  node.arguments.forEach((argument: ts.ObjectLiteralExpression, index: number) => {
729    if (index === 1) {
730      // @ts-ignore
731      newArguments.push(processBindPopupBuilderProperty(argument));
732    } else {
733      newArguments.push(argument);
734    }
735  });
736  node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments);
737  return node;
738}
739
740function processDragStartBuilder(node: ts.CallExpression): ts.CallExpression {
741  const newStatements: ts.Statement[] = [];
742  if (isNodeFunction(node)) {
743    // @ts-ignore
744    for (let i = 0; i < node.arguments[0].body.statements.length; i++) {
745      // @ts-ignore
746      const statement: ts.Statement = node.arguments[0].body.statements[i];
747      newStatements.push(checkStatement(statement));
748    }
749    node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ts.factory.updateArrowFunction(
750      // @ts-ignore
751      node.arguments[0], undefined, undefined, node.arguments[0].parameters, node.arguments[0].type,
752      // @ts-ignore
753      node.arguments[0].equalsGreaterThanToken, ts.factory.updateBlock(node.arguments[0].body, newStatements))]);
754  }
755  return node;
756}
757
758function isNodeFunction(node: ts.CallExpression): boolean {
759  return node.arguments && node.arguments.length && ts.isArrowFunction(node.arguments[0]) && node.arguments[0].body &&
760    ts.isBlock(node.arguments[0].body);
761}
762
763function checkStatement(statement: ts.Statement): ts.Statement {
764  if (ts.isReturnStatement(statement)) {
765    if (ts.isObjectLiteralExpression(statement.expression)) {
766      const newProperties: ts.ObjectLiteralElementLike[] = [];
767      for (let j = 0; j < statement.expression.properties.length; j++) {
768        const property: ts.ObjectLiteralElementLike = statement.expression.properties[j];
769        checkProperty(property);
770        newProperties.push(property);
771      }
772      return ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression(newProperties));
773    } else {
774      return ts.factory.updateReturnStatement(statement, parseBuilderNode(statement.expression));
775    }
776  } else {
777    return statement;
778  }
779}
780
781function checkProperty(property: ts.ObjectLiteralElementLike): void {
782  if (isPropertyFunction(property)) {
783    // @ts-ignore
784    property = ts.factory.createPropertyAssignment(property.name, parseBuilderNode(property.initializer));
785  }
786}
787
788function isPropertyFunction(property: ts.ObjectLiteralElementLike): boolean {
789  return ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name) &&
790    property.name.escapedText.toString() === BUILDER_ATTR_NAME;
791}
792
793function processBindPopupBuilderProperty(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression {
794  const newProperties: ts.PropertyAssignment[] = [];
795  node.properties.forEach((property: ts.PropertyAssignment, index: number) => {
796    if (index === 0) {
797      if (property.name && ts.isIdentifier(property.name) &&
798        property.name.escapedText.toString() === CUSTOM_DIALOG_CONTROLLER_BUILDER) {
799        newProperties.push(ts.factory.updatePropertyAssignment(property, property.name,
800          parseBuilderNode(property.initializer)));
801      } else {
802        newProperties.push(property);
803      }
804    } else {
805      newProperties.push(property);
806    }
807  });
808  return ts.factory.updateObjectLiteralExpression(node, newProperties);
809}
810
811function processPropertyBuilder(node: ts.PropertyAccessExpression): ts.ObjectLiteralExpression {
812  return ts.factory.createObjectLiteralExpression([
813    ts.factory.createPropertyAssignment(
814      ts.factory.createIdentifier(BUILDER_ATTR_NAME),
815      ts.factory.createCallExpression(
816        ts.factory.createPropertyAccessExpression(
817          node,
818          ts.factory.createIdentifier(BUILDER_ATTR_BIND)
819        ),
820        undefined,
821        [ts.factory.createThis()]
822      )
823    )
824  ]);
825}
826
827function processIdentifierBuilder(node: ts.Identifier): ts.ObjectLiteralExpression {
828  return ts.factory.createObjectLiteralExpression([
829    ts.factory.createPropertyAssignment(
830      ts.factory.createIdentifier(BUILDER_ATTR_NAME),
831      node
832    )
833  ]);
834}
835
836function getParsedBuilderAttrArgumentWithParams(node: ts.CallExpression):
837  ts.ObjectLiteralExpression {
838  return ts.factory.createObjectLiteralExpression([
839    ts.factory.createPropertyAssignment(
840      ts.factory.createIdentifier(BUILDER_ATTR_NAME),
841      ts.factory.createArrowFunction(
842        undefined,
843        undefined,
844        [],
845        undefined,
846        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
847        ts.factory.createBlock(
848          [ts.factory.createExpressionStatement(node)],
849          true
850        )
851      )
852    )
853  ]);
854}
855
856function validatePropertyAccessExpressionWithCustomBuilder(node: ts.Node): boolean {
857  return ts.isPropertyAccessExpression(node) && node.name &&
858    ts.isIdentifier(node.name) && CUSTOM_BUILDER_PROPERTIES.has(node.name.escapedText.toString());
859}
860
861function validateIdentifierWithCustomBuilder(node: ts.Node): boolean {
862  return ts.isIdentifier(node) && CUSTOM_BUILDER_PROPERTIES.has(node.escapedText.toString());
863}
864
865function createArrowFunctionFor$$($$varExp: ts.Expression): ts.ArrowFunction {
866  return ts.factory.createArrowFunction(
867    undefined, undefined,
868    [ts.factory.createParameterDeclaration(
869      undefined, undefined, undefined,
870      ts.factory.createIdentifier($$_NEW_VALUE),
871      undefined, undefined, undefined
872    )],
873    undefined,
874    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
875    ts.factory.createBlock(
876      [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
877        $$varExp,
878        ts.factory.createToken(ts.SyntaxKind.EqualsToken),
879        ts.factory.createIdentifier($$_NEW_VALUE)
880      ))],
881      false
882    )
883  );
884}
885
886function updateArgumentFor$$(argument: any): ts.Expression {
887  if (ts.isElementAccessExpression(argument)) {
888    return ts.factory.updateElementAccessExpression(
889      argument, updateArgumentFor$$(argument.expression), argument.argumentExpression);
890  } else if (ts.isIdentifier(argument)) {
891    props.push(argument.getText());
892    if (argument.getText() === $$_THIS) {
893      return ts.factory.createThis();
894    } else if (argument.getText().match(/^\$\$(.|\n)+/)) {
895      return ts.factory.createIdentifier(argument.getText().replace(/\$\$/, ''));
896    }
897  } else if (ts.isPropertyAccessExpression(argument)) {
898    return ts.factory.updatePropertyAccessExpression(
899      argument, updateArgumentFor$$(argument.expression), argument.name);
900  }
901}
902
903function addComponentAttr(temp: any, node: ts.Identifier, lastStatement: any,
904  statements: ts.Statement[], identifierNode: ts.Identifier, log: LogInfo[],
905  isStylesAttr: boolean): void {
906  const propName: string = node.getText();
907  if (propName === ATTRIBUTE_ANIMATION) {
908    if (!lastStatement.statement) {
909      if (!(temp.arguments.length === 1 &&
910        temp.arguments[0].kind === ts.SyntaxKind.NullKeyword)) {
911        statements.push(ts.factory.createExpressionStatement(createFunction(
912          ts.factory.createIdentifier(GLOBAL_CONTEXT), node,
913          // @ts-ignore
914          [ts.factory.createNull()])));
915      }
916    } else {
917      statements.push(lastStatement.statement);
918    }
919    lastStatement.statement = ts.factory.createExpressionStatement(createFunction(
920      ts.factory.createIdentifier(GLOBAL_CONTEXT), node, temp.arguments));
921    lastStatement.kind = false;
922  } else if (GESTURE_ATTRS.has(propName)) {
923    parseGesture(temp, propName, statements, log);
924    lastStatement.kind = true;
925  } else if (isExtendFunctionNode(identifierNode, propName)) {
926    if (newsupplement.isAcceleratePreview) {
927      log.push({
928        type: LogType.ERROR,
929        message: `Doesn't support Extend function now`,
930        pos: temp.getStart()
931      });
932    }
933    statements.push(ts.factory.createExpressionStatement(ts.factory.createCallExpression(
934      ts.factory.createIdentifier(`__${identifierNode.escapedText.toString()}__${propName}`),
935      undefined, temp.arguments)));
936    lastStatement.kind = true;
937  } else if (propName === ATTRIBUTE_STATESTYLES) {
938    if (temp.arguments.length === 1 && ts.isObjectLiteralExpression(temp.arguments[0])) {
939      statements.push(createViewStackProcessor(temp, true));
940      traverseStateStylesAttr(temp, statements, identifierNode, log);
941      lastStatement.kind = true;
942    } else {
943      validateStateStyleSyntax(temp, log);
944    }
945  } else if (GLOBAL_STYLE_FUNCTION.has(propName) || INNER_STYLE_FUNCTION.has(propName)) {
946    const styleBlock: ts.Block =
947      INNER_STYLE_FUNCTION.get(propName) || GLOBAL_STYLE_FUNCTION.get(propName);
948    bindComponentAttr(styleBlock.statements[0] as ts.ExpressionStatement, identifierNode,
949      statements, log, false, true);
950    lastStatement.kind = true;
951  } else if (isDoubleDollarToChange(isStylesAttr, identifierNode, propName, temp)) {
952    const argumentsArr: ts.Expression[] = [];
953    classifyArgumentsNum(temp.arguments, argumentsArr, propName, identifierNode);
954    statements.push(ts.factory.createExpressionStatement(
955      createFunction(identifierNode, node, argumentsArr)));
956    lastStatement.kind = true;
957  } else {
958    if (isStylesAttr) {
959      if (!COMMON_ATTRS.has(propName)) {
960        validateStateStyleSyntax(temp, log);
961      }
962    }
963    temp = loopEtscomponent(temp, isStylesAttr);
964    statements.push(ts.factory.createExpressionStatement(
965      createFunction(identifierNode, node, temp.arguments)));
966    lastStatement.kind = true;
967  }
968}
969
970function isDoubleDollarToChange(isStylesAttr: boolean, identifierNode: ts.Identifier,
971  propName: string, temp: any): boolean {
972  return !isStylesAttr &&
973    PROPERTIES_ADD_DOUBLE_DOLLAR.has(identifierNode.escapedText.toString()) &&
974    PROPERTIES_ADD_DOUBLE_DOLLAR.get(identifierNode.escapedText.toString()).has(propName) ||
975    STYLE_ADD_DOUBLE_DOLLAR.has(propName) && temp.arguments.length && temp.arguments[0] ?
976      temp.arguments[0].getText().match(/^\$\$(.|\n)+/) !== null ? true : false
977    : false;
978}
979
980function processDollarEtsComponent(node: ts.EtsComponentExpression, name: string
981  ): ts.EtsComponentExpression {
982  node.arguments.forEach((item: ts.Node, index: number) => {
983    if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) {
984      item.properties.forEach((param: ts.PropertyAssignment, paramIndex: number)=>{
985        if (isHaveDoubleDollar(param, name)) {
986          const varExp: ts.Expression = updateArgumentFor$$(param.initializer);
987          node.arguments[index].properties[paramIndex].initializer = generateObjectFor$$(varExp);
988        }
989      })
990    }
991  })
992  return node;
993}
994
995function isHaveDoubleDollar(param: ts.PropertyAssignment, name: string): boolean {
996  return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) &&
997    PROPERTIES_ADD_DOUBLE_DOLLAR.get(name).has(param.name.getText()) && param.initializer &&
998    param.initializer.getText().startsWith($$);
999}
1000
1001function loopEtscomponent(node: any, isStylesAttr: boolean): ts.Node {
1002  node.arguments.forEach((item: ts.Node, index: number) => {
1003    if (ts.isEtsComponentExpression(item)) {
1004      node.arguments[index] = ts.factory.createCallExpression(
1005        item.expression, undefined, item.arguments);
1006    } else if (ts.isCallExpression(item) || ts.isNewExpression(item)) {
1007      node.arguments[index] = ts.visitEachChild(item,
1008        changeEtsComponentKind, contextGlobal);
1009    }
1010  });
1011  return node;
1012}
1013
1014function changeEtsComponentKind(node: ts.Node): ts.Node {
1015  if (ts.isEtsComponentExpression(node)) {
1016    node.kind = 204;
1017    return node;
1018  }
1019  return ts.visitEachChild(node, changeEtsComponentKind, contextGlobal);
1020}
1021
1022function classifyArgumentsNum(args: any, argumentsArr: ts.Expression[], propName: string,
1023  identifierNode: ts.Identifier): void {
1024  if (STYLE_ADD_DOUBLE_DOLLAR.has(propName) && args.length === 2) {
1025    const varExp: ts.Expression = updateArgumentFor$$(args[0]);
1026    argumentsArr.push(generateObjectFor$$(varExp), args[1]);
1027  } else if (PROPERTIES_ADD_DOUBLE_DOLLAR.has(identifierNode.getText()) && args.length === 1 &&
1028    PROPERTIES_ADD_DOUBLE_DOLLAR.get(identifierNode.getText()).has(propName) ||
1029    STYLE_ADD_DOUBLE_DOLLAR.has(propName) && args.length === 1) {
1030    const varExp: ts.Expression = updateArgumentFor$$(args[0]);
1031    argumentsArr.push(varExp, createArrowFunctionFor$$(varExp));
1032  }
1033}
1034
1035function generateObjectFor$$(varExp: ts.Expression): ts.ObjectLiteralExpression {
1036  return ts.factory.createObjectLiteralExpression(
1037    [
1038      ts.factory.createPropertyAssignment(
1039        ts.factory.createIdentifier($$_VALUE),
1040        varExp
1041      ),
1042      ts.factory.createPropertyAssignment(
1043        ts.factory.createIdentifier($$_CHANGE_EVENT),
1044        createArrowFunctionFor$$(varExp)
1045      )
1046    ],
1047    false
1048  );
1049}
1050
1051function createViewStackProcessor(item: any, endViewStack: boolean): ts.ExpressionStatement {
1052  const argument: ts.StringLiteral[] = [];
1053  if (!endViewStack && item.name) {
1054    argument.push(ts.factory.createStringLiteral(item.name.getText()));
1055  }
1056  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1057    ts.factory.createPropertyAccessExpression(
1058      ts.factory.createIdentifier(VIEW_STACK_PROCESSOR),
1059      ts.factory.createIdentifier(VISUAL_STATE)
1060    ),
1061    undefined,
1062    argument
1063  ));
1064}
1065
1066function traverseStateStylesAttr(temp: any, statements: ts.Statement[],
1067  identifierNode: ts.Identifier, log: LogInfo[]): void {
1068  temp.arguments[0].properties.reverse().forEach((item: ts.PropertyAssignment) => {
1069    if (ts.isPropertyAccessExpression(item.initializer) &&
1070      item.initializer.expression.getText() === THIS &&
1071      INNER_STYLE_FUNCTION.get(item.initializer.name.getText())) {
1072      const name: string = item.initializer.name.getText();
1073      bindComponentAttr(INNER_STYLE_FUNCTION.get(name).statements[0] as ts.ExpressionStatement,
1074        identifierNode, statements, log, false, true);
1075    } else if (ts.isIdentifier(item.initializer) &&
1076      GLOBAL_STYLE_FUNCTION.get(item.initializer.getText())) {
1077      const name: string = item.initializer.getText();
1078      bindComponentAttr(GLOBAL_STYLE_FUNCTION.get(name).statements[0] as ts.ExpressionStatement,
1079        identifierNode, statements, log, false, true);
1080    } else if (ts.isObjectLiteralExpression(item.initializer) &&
1081      item.initializer.properties.length === 1 &&
1082      ts.isPropertyAssignment(item.initializer.properties[0])) {
1083      bindComponentAttr(ts.factory.createExpressionStatement(
1084        item.initializer.properties[0].initializer), identifierNode, statements, log, false, true);
1085    } else {
1086      validateStateStyleSyntax(temp, log);
1087    }
1088    if (item.name) {
1089      statements.push(createViewStackProcessor(item, false));
1090    }
1091  });
1092}
1093
1094function isExtendFunctionNode(identifierNode: ts.Identifier, propName: string): boolean {
1095  if (identifierNode && EXTEND_ATTRIBUTE.has(identifierNode.escapedText.toString())) {
1096    const attributeArray: string[] =
1097      [...EXTEND_ATTRIBUTE.get(identifierNode.escapedText.toString())];
1098    if (attributeArray.includes(propName)) {
1099      return true;
1100    }
1101  }
1102  return false;
1103}
1104
1105const gestureMap: Map<string, string> = new Map([
1106  [PRIORITY_GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_HIGH],
1107  [PARALLEL_GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_PARALLEL],
1108  [GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_LOW]
1109]);
1110
1111function parseGesture(node: ts.CallExpression, propName: string, statements: ts.Statement[],
1112  log: LogInfo[]): void {
1113  statements.push(ts.factory.createExpressionStatement(
1114    createFunction(ts.factory.createIdentifier(COMPONENT_GESTURE),
1115      ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
1116  parseGestureInterface(node, statements, log);
1117  const argumentArr: ts.NodeArray<ts.PropertyAccessExpression> = ts.factory.createNodeArray(
1118    [ts.factory.createPropertyAccessExpression(
1119      ts.factory.createIdentifier(GESTURE_ENUM_KEY),
1120      ts.factory.createIdentifier(gestureMap.get(propName)))
1121    ]
1122  );
1123  if (node.arguments && node.arguments.length > 1 &&
1124    ts.isPropertyAccessExpression(node.arguments[1])) {
1125    // @ts-ignore
1126    argumentArr.push(node.arguments[1]);
1127  }
1128  statements.push(ts.factory.createExpressionStatement(
1129    createFunction(ts.factory.createIdentifier(COMPONENT_GESTURE),
1130      ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), argumentArr)));
1131}
1132
1133function processGestureType(node: ts.CallExpression, statements: ts.Statement[], log: LogInfo[],
1134  reverse: boolean = false): void {
1135  const newStatements: ts.Statement[] = [];
1136  const newNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(node);
1137  let temp: any = node.expression;
1138  while (temp && !ts.isIdentifier(temp) && temp.expression) {
1139    temp = temp.expression;
1140  }
1141  if (temp && temp.parent && ts.isCallExpression(temp.parent) && ts.isIdentifier(temp) &&
1142    GESTURE_TYPE_NAMES.has(temp.escapedText.toString())) {
1143    newStatements.push(ts.factory.createExpressionStatement(
1144      createFunction(temp, ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
1145    if (temp.escapedText.toString() === COMPONENT_GESTURE_GROUP) {
1146      const gestureStatements: ts.Statement[] = [];
1147      parseGestureInterface(temp.parent, gestureStatements, log, true);
1148      newStatements.push(...gestureStatements.reverse());
1149      bindComponentAttr(newNode, temp, newStatements, log, false);
1150      let argumentArr: ts.NodeArray<ts.Expression> = null;
1151      if (temp.parent.arguments && temp.parent.arguments.length) {
1152        // @ts-ignore
1153        argumentArr = ts.factory.createNodeArray([temp.parent.arguments[0]]);
1154      }
1155      newStatements.push(ts.factory.createExpressionStatement(
1156        createFunction(temp, ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), argumentArr)));
1157    } else {
1158      bindComponentAttr(newNode, temp, newStatements, log, false);
1159      newStatements.push(ts.factory.createExpressionStatement(
1160        createFunction(temp, ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), temp.parent.arguments)));
1161    }
1162  }
1163  if (newStatements.length) {
1164    reverse ? statements.push(...newStatements.reverse()) : statements.push(...newStatements);
1165  }
1166}
1167
1168function parseGestureInterface(node: ts.CallExpression, statements: ts.Statement[], log: LogInfo[],
1169  reverse: boolean = false): void {
1170  if (node.arguments && node.arguments.length) {
1171    node.arguments.forEach((item: ts.Node) => {
1172      if (ts.isCallExpression(item)) {
1173        processGestureType(item, statements, log, reverse);
1174      }
1175    });
1176  }
1177}
1178
1179export function getName(node: ts.ExpressionStatement): string {
1180  let temp: any = node.expression;
1181  let name: string;
1182  while (temp) {
1183    if (ts.isIdentifier(temp) && temp.parent && (ts.isCallExpression(temp.parent) ||
1184      ts.isEtsComponentExpression(temp.parent))) {
1185      name = temp.escapedText.toString();
1186      break;
1187    } else if (ts.isPropertyAccessExpression(temp) && temp.name && ts.isIdentifier(temp.name) &&
1188      !BUILDIN_STYLE_NAMES.has(temp.name.escapedText.toString())) {
1189      name = temp.name.escapedText.toString();
1190      break;
1191    }
1192    temp = temp.expression;
1193  }
1194  return name;
1195}
1196
1197export function isAttributeNode(node: ts.ExpressionStatement): boolean {
1198  let temp: any = node.expression;
1199  let name: string;
1200  while (temp) {
1201    if (ts.isCallExpression(temp) && temp.expression && ts.isIdentifier(temp.expression)) {
1202      name = temp.expression.escapedText.toString();
1203      break;
1204    }
1205    temp = temp.expression;
1206  }
1207  return BUILDIN_STYLE_NAMES.has(name);
1208}
1209
1210enum ComponentType {
1211  innerComponent,
1212  customComponent,
1213  forEachComponent,
1214  customBuilderMethod,
1215  builderParamMethod
1216}
1217
1218function isEtsComponent(node: ts.ExpressionStatement): boolean {
1219  let isEtsComponent: boolean = false;
1220  let temp: any = node.expression;
1221  while (temp) {
1222    if (ts.isEtsComponentExpression(temp)) {
1223      isEtsComponent = true;
1224    }
1225    temp = temp.expression;
1226  }
1227  return isEtsComponent;
1228}
1229
1230function isSomeName(forEachParameters: ts.NodeArray<ts.ParameterDeclaration>, name: string): boolean {
1231  return Array.isArray(forEachParameters) &&
1232    forEachParameters.some((item)=>{
1233      return ts.isIdentifier(item.name) ? item.name.escapedText.toString() === name : false;
1234    });
1235}
1236
1237function isParamFunction(node: ts.ExpressionStatement): boolean {
1238  return node.expression && ts.isCallExpression(node.expression) &&
1239    node.expression.expression && ts.isIdentifier(node.expression.expression);
1240}
1241
1242function getComponentType(node: ts.ExpressionStatement, log: LogInfo[],
1243  name: string, forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): ComponentType {
1244  let isBuilderName: boolean = true;
1245  if(forEachParameters && isSomeName(forEachParameters, name) && isParamFunction(node)) {
1246    isBuilderName = false;
1247  }
1248  if (isEtsComponent(node)) {
1249    if (componentCollection.customComponents.has(name)) {
1250      return ComponentType.customComponent;
1251    } else {
1252      return ComponentType.innerComponent;
1253    }
1254  } else if (componentCollection.customComponents.has(name)) {
1255    return ComponentType.customComponent;
1256  } else if (name === COMPONENT_FOREACH || name === COMPONENT_LAZYFOREACH) {
1257    return ComponentType.forEachComponent;
1258  } else if (CUSTOM_BUILDER_METHOD.has(name) && isBuilderName) {
1259    return ComponentType.customBuilderMethod;
1260  } else if (builderParamObjectCollection.get(componentCollection.currentClassName) &&
1261    builderParamObjectCollection.get(componentCollection.currentClassName).has(name)) {
1262    return ComponentType.builderParamMethod;
1263  } else if (!isAttributeNode(node)) {
1264    log.push({
1265      type: LogType.ERROR,
1266      message: `'${node.getText()}' does not meet UI component syntax.`,
1267      pos: node.getStart()
1268    });
1269  }
1270  return null;
1271}
1272
1273export function validateStateStyleSyntax(temp: any, log: LogInfo[]): void {
1274  log.push({
1275    type: LogType.ERROR,
1276    message: `.stateStyles doesn't conform standard.`,
1277    pos: temp.getStart()
1278  });
1279}
1280
1281function checkButtonParamHasLabel(node: ts.EtsComponentExpression, log: LogInfo[]): void {
1282  if (node.arguments && node.arguments.length !== 0) {
1283    for (let i = 0; i < node.arguments.length; i++) {
1284      let argument: ts.Expression = node.arguments[i];
1285      if (ts.isStringLiteral(argument) || (ts.isCallExpression(argument) && ts.isIdentifier(argument.expression) &&
1286        (argument.expression.escapedText.toString() === RESOURCE))) {
1287        log.push({
1288          type: LogType.ERROR,
1289          message: "The Button component with a label parameter can not have any child.",
1290          pos: node.getStart(),
1291        });
1292        return;
1293      }
1294    }
1295  }
1296}
1297