• 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_NON_DECORATOR,
20  COMPONENT_STATE_DECORATOR,
21  COMPONENT_PROP_DECORATOR,
22  COMPONENT_LINK_DECORATOR,
23  COMPONENT_STORAGE_LINK_DECORATOR,
24  COMPONENT_PROVIDE_DECORATOR,
25  COMPONENT_OBJECT_LINK_DECORATOR,
26  COMPONENT_CREATE_FUNCTION,
27  COMPONENT_POP_FUNCTION,
28  BASE_COMPONENT_NAME,
29  CUSTOM_COMPONENT_EARLIER_CREATE_CHILD,
30  COMPONENT_CONSTRUCTOR_UPDATE_PARAMS,
31  CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID,
32  COMPONENT_CONSTRUCTOR_UNDEFINED,
33  CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION,
34  CUSTOM_COMPONENT_MARK_STATIC_FUNCTION,
35  COMPONENT_COMMON,
36  GENERATE_ID,
37  ELMTID,
38  STARTGETACCESSRECORDINGFOR,
39  STOPGETACCESSRECORDING,
40  BASE_COMPONENT_NAME_PU,
41  OBSERVECOMPONENTCREATION,
42  OBSERVECOMPONENTCREATION2,
43  ISINITIALRENDER,
44  UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID,
45  $$,
46  COMPONENT_RECYCLE,
47  COMPONENT_CREATE_RECYCLE,
48  RECYCLE_NODE,
49  ABOUT_TO_REUSE,
50  COMPONENT_RERENDER_FUNCTION,
51  OBSERVE_RECYCLE_COMPONENT_CREATION,
52  FUNCTION,
53  COMPONENT_IF_UNDEFINED,
54  COMPONENT_PARAMS_LAMBDA_FUNCTION,
55  COMPONENT_PARAMS_FUNCTION,
56  COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION,
57  NAME,
58  COMPONENT_CALL
59} from './pre_define';
60import {
61  stateCollection,
62  linkCollection,
63  propCollection,
64  regularCollection,
65  storagePropCollection,
66  storageLinkCollection,
67  provideCollection,
68  consumeCollection,
69  objectLinkCollection,
70  isStaticViewCollection,
71  builderParamObjectCollection,
72  getLocalStorageCollection,
73  builderParamInitialization,
74  propInitialization
75} from './validate_ui_syntax';
76import {
77  curPropMap,
78  createViewCreate,
79  createCustomComponentNewExpression
80} from './process_component_member';
81import {
82  LogType,
83  LogInfo,
84  componentInfo,
85  storedFileInfo
86} from './utils';
87import {
88  bindComponentAttr,
89  parentConditionalExpression,
90  createComponentCreationStatement,
91  createFunction,
92  ComponentAttrInfo,
93  ifRetakeId,
94  transferBuilderCall,
95  createCollectElmtIdNode,
96  createViewStackProcessorStatement,
97  BuilderParamsResult
98} from './process_component_build';
99import {
100  partialUpdateConfig,
101  projectConfig
102} from '../main';
103import {
104  GLOBAL_CUSTOM_BUILDER_METHOD
105} from './component_map';
106import {
107  createReference,
108  isProperty
109} from './process_component_class';
110
111let decoractorMap: Map<string, Map<string, Set<string>>>;
112
113export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
114  log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false,
115  idName: ts.Expression = undefined, builderParamsResult: BuilderParamsResult = null): void {
116  decoractorMap = new Map(
117    [[COMPONENT_STATE_DECORATOR, stateCollection],
118      [COMPONENT_LINK_DECORATOR, linkCollection],
119      [COMPONENT_PROP_DECORATOR, propCollection],
120      [COMPONENT_NON_DECORATOR, regularCollection],
121      [COMPONENT_PROVIDE_DECORATOR, provideCollection],
122      [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]);
123  const componentNode: ts.CallExpression = getCustomComponentNode(node);
124  if (componentNode) {
125    const isRecycleComponent: boolean = isRecycle(name);
126    const hasChainCall: boolean = componentNode.parent &&
127      ts.isPropertyAccessExpression(componentNode.parent);
128    let ischangeNode: boolean = false;
129    let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression(
130      componentNode, name, isBuilder, isGlobalBuilder);
131    let argumentsArray: ts.PropertyAssignment[];
132    const componentAttrInfo: ComponentAttrInfo = { reuseId: null };
133    if (isHasChild(componentNode)) {
134      // @ts-ignore
135      argumentsArray = componentNode.arguments[0].properties.slice();
136      argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => {
137        if (isToChange(item, name)) {
138          ischangeNode = true;
139          const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment(
140            item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression));
141          argumentsArray.splice(index, 1, propertyAssignmentNode);
142        }
143      });
144      if (ischangeNode) {
145        const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node,
146          ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments,
147            [ts.factory.createObjectLiteralExpression(argumentsArray, true)]));
148        customComponentNewExpression = createCustomComponentNewExpression(
149          newNode.expression as ts.CallExpression, name, isBuilder);
150      }
151    }
152    let judgeIdStart: number;
153    if (partialUpdateConfig.partialUpdateMode && idName) {
154      judgeIdStart = newStatements.length;
155    }
156    let needCommon: boolean = false;
157    if (hasChainCall) {
158      if (partialUpdateConfig.partialUpdateMode) {
159        const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement(
160          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
161            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))];
162        const immutableStatements: ts.Statement[] = [];
163        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode,
164          log, true, false, immutableStatements, false, componentAttrInfo);
165        needCommon = commomComponentNode.length > 1 || immutableStatements.length > 0;
166        if (needCommon) {
167          newStatements.push(createComponentCreationStatement(componentAttributes(COMPONENT_COMMON),
168            commomComponentNode, COMPONENT_COMMON, isGlobalBuilder, false, undefined, immutableStatements,
169            builderParamsResult, isRecycleComponent));
170        }
171      } else {
172        newStatements.push(ts.factory.createExpressionStatement(
173          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
174            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)));
175        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log);
176      }
177    }
178    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
179      newStatements.push(createRecycleComponent(isGlobalBuilder));
180    }
181    addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode,
182      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult);
183    if (hasChainCall && (!partialUpdateConfig.partialUpdateMode || needCommon)) {
184      newStatements.push(ts.factory.createExpressionStatement(
185        createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
186          ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
187    }
188    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
189      newStatements.push(componentAttributes(COMPONENT_RECYCLE));
190    }
191    if (partialUpdateConfig.partialUpdateMode && idName) {
192      newStatements.splice(judgeIdStart, newStatements.length - judgeIdStart,
193        ifRetakeId(newStatements.slice(judgeIdStart), idName));
194    }
195  }
196}
197
198export function isRecycle(componentName: string): boolean {
199  return storedFileInfo.getCurrentArkTsFile().recycleComponents.has(componentName);
200}
201
202function createRecycleComponent(isGlobalBuilder: boolean): ts.Statement {
203  return createComponentCreationStatement(componentAttributes(COMPONENT_RECYCLE),
204    [ts.factory.createExpressionStatement(
205      createFunction(ts.factory.createIdentifier(COMPONENT_RECYCLE),
206        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))
207    ], COMPONENT_RECYCLE, isGlobalBuilder);
208}
209
210function componentAttributes(componentName: string): ts.Statement {
211  return ts.factory.createExpressionStatement(
212    ts.factory.createCallExpression(
213      ts.factory.createPropertyAccessExpression(
214        ts.factory.createIdentifier(componentName),
215        ts.factory.createIdentifier(COMPONENT_POP_FUNCTION)
216      ), undefined, []
217    ));
218}
219
220function isHasChild(node: ts.CallExpression): boolean {
221  return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) &&
222    node.arguments[0].properties && node.arguments[0].properties.length > 0;
223}
224
225function isToChange(item: ts.PropertyAssignment, name: string): boolean {
226  const builderParamName: Set<string> = builderParamObjectCollection.get(name);
227  if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName &&
228    builderParamName.has(item.name.getText()) &&
229    !/\.(bind|call|apply)/.test(item.initializer.getText())) {
230    return true;
231  }
232  return false;
233}
234
235function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ConditionalExpression {
236  let builderBindThis: ts.ExpressionStatement = ts.factory.createExpressionStatement(node);
237  if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
238    GLOBAL_CUSTOM_BUILDER_METHOD.has(node.expression.escapedText.toString())) {
239    builderBindThis = transferBuilderCall(ts.factory.createExpressionStatement(node), node.expression.escapedText.toString());
240  }
241  return changeNodeFromCallToArrowDetermine(node, builderBindThis);
242}
243
244function changeNodeFromCallToArrowDetermine(node: ts.CallExpression, builderBindThis: ts.ExpressionStatement): ts.ConditionalExpression {
245  if (ts.isCallExpression(node)) {
246    return ts.factory.createConditionalExpression(
247      ts.factory.createBinaryExpression(
248        ts.factory.createTypeOfExpression(node),
249        ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
250        ts.factory.createStringLiteral(FUNCTION)
251      ),
252      ts.factory.createToken(ts.SyntaxKind.QuestionToken),
253      node,
254      ts.factory.createToken(ts.SyntaxKind.ColonToken),
255      ts.factory.createArrowFunction(
256        undefined,
257        undefined,
258        [],
259        undefined,
260        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
261        ts.factory.createBlock(
262          [builderBindThis],
263          true
264        )
265      )
266    );
267  }
268}
269
270function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
271  newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression,
272  isBuilder: boolean, isGlobalBuilder: boolean, isRecycleComponent: boolean,
273  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult): void {
274  if (ts.isNewExpression(newNode)) {
275    const propertyArray: ts.ObjectLiteralElementLike[] = [];
276    validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder);
277    addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode,
278      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult);
279  }
280}
281
282function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[],
283  newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[],
284  componentNode: ts.CallExpression, isBuilder: boolean, isGlobalBuilder: boolean,
285  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo,
286  builderParamsResult: BuilderParamsResult): void {
287  if (!partialUpdateConfig.partialUpdateMode) {
288    const id: string = componentInfo.id.toString();
289    newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id,
290      ts.factory.updateExpressionStatement(node, createViewCreate(newNode)),
291      ts.factory.createObjectLiteralExpression(props, true), name));
292  } else {
293    newStatements.push(createCustomComponent(newNode, name, componentNode, isGlobalBuilder, isBuilder,
294      isRecycleComponent, componentAttrInfo, builderParamsResult));
295  }
296}
297
298function createChildElmtId(node: ts.CallExpression, name: string): ts.PropertyAssignment[] {
299  const propsAndObjectLinks: string[] = [];
300  const childParam: ts.PropertyAssignment[] = [];
301  if (propCollection.get(name)) {
302    propsAndObjectLinks.push(...propCollection.get(name));
303  }
304  if (objectLinkCollection.get(name)) {
305    propsAndObjectLinks.push(...objectLinkCollection.get(name));
306  }
307  if (projectConfig.optLazyForEach && storedFileInfo.processLazyForEach && stateCollection.get(name)) {
308    propsAndObjectLinks.push(...stateCollection.get(name));
309  }
310  if (node.arguments[0].properties) {
311    node.arguments[0].properties.forEach(item => {
312      if (ts.isIdentifier(item.name) && propsAndObjectLinks.includes(item.name.escapedText.toString())) {
313        childParam.push(item);
314      }
315    });
316  }
317  return childParam;
318}
319
320function createCustomComponent(newNode: ts.NewExpression, name: string, componentNode: ts.CallExpression,
321  isGlobalBuilder: boolean, isBuilder: boolean, isRecycleComponent: boolean,
322  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult): ts.Block {
323  let componentParameter: ts.ObjectLiteralExpression;
324  if (componentNode.arguments && componentNode.arguments.length) {
325    componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name), true);
326  } else {
327    componentParameter = ts.factory.createObjectLiteralExpression([], false);
328  }
329  const arrowArgArr: ts.ParameterDeclaration[] = [
330    ts.factory.createParameterDeclaration(undefined, undefined,
331      ts.factory.createIdentifier(ELMTID)
332    ),
333    ts.factory.createParameterDeclaration(undefined, undefined,
334      ts.factory.createIdentifier(ISINITIALRENDER)
335    )
336  ];
337  const arrowBolck: ts.Statement[] = [
338    projectConfig.optLazyForEach && storedFileInfo.processLazyForEach ? createCollectElmtIdNode() : undefined,
339    createIfCustomComponent(newNode, componentNode, componentParameter, name, isGlobalBuilder,
340      isBuilder, isRecycleComponent, componentAttrInfo)
341  ];
342  if (isRecycleComponent) {
343    arrowArgArr.push(ts.factory.createParameterDeclaration(
344      undefined, undefined, ts.factory.createIdentifier(RECYCLE_NODE),
345      undefined, undefined, ts.factory.createNull()
346    ));
347  } else if (partialUpdateConfig.optimizeComponent && isGlobalBuilder &&
348    builderParamsResult && builderParamsResult.firstParam) {
349    const paramName: ts.Identifier = builderParamsResult.firstParam.name as ts.Identifier;
350    arrowArgArr.push(ts.factory.createParameterDeclaration(undefined, undefined,
351      paramName, undefined, undefined, ts.factory.createIdentifier(`__${paramName.escapedText.toString()}__`)
352    ));
353  }
354  if (isRecycleComponent || !partialUpdateConfig.optimizeComponent) {
355    arrowBolck.unshift(createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID));
356    arrowBolck.push(createViewStackProcessorStatement(STOPGETACCESSRECORDING));
357  }
358  const observeArgArr: ts.Node[] = [
359    ts.factory.createArrowFunction(undefined, undefined, arrowArgArr, undefined,
360      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
361      ts.factory.createBlock(arrowBolck, true))
362  ];
363  if (isRecycleComponent) {
364    componentAttrInfo.reuseId ? observeArgArr.unshift(componentAttrInfo.reuseId) :
365      observeArgArr.unshift(ts.factory.createStringLiteral(name));
366  } else if (partialUpdateConfig.optimizeComponent) {
367    observeArgArr.push(componentPop(name));
368  }
369  return ts.factory.createBlock(
370    [
371      ts.factory.createExpressionStatement(ts.factory.createCallExpression(
372        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
373          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
374        isRecycleComponent ?
375          ts.factory.createIdentifier(OBSERVE_RECYCLE_COMPONENT_CREATION) :
376          ts.factory.createIdentifier(partialUpdateConfig.optimizeComponent ?
377            OBSERVECOMPONENTCREATION2 : OBSERVECOMPONENTCREATION)
378        ),
379        undefined, observeArgArr as ts.Expression[]))
380    ], true);
381}
382
383function componentPop(name: string): ts.ObjectLiteralExpression {
384  return ts.factory.createObjectLiteralExpression(
385    [ts.factory.createPropertyAssignment(
386      ts.factory.createIdentifier(NAME),
387      ts.factory.createStringLiteral(name)
388    )],
389    false
390  );
391}
392
393export function assignComponentParams(componentNode: ts.CallExpression,
394  isBuilder: boolean = false): ts.VariableStatement {
395  const isParamsLambda: boolean = true;
396  const [keyArray, valueArray]: [ts.Node[], ts.Node[]] = splitComponentParams(componentNode, isBuilder, isParamsLambda);
397  let integrateParams: boolean = false;
398  if (!keyArray.length && componentNode.arguments && componentNode.arguments.length > 0 && componentNode.arguments[0]) {
399    integrateParams = true;
400  }
401  return ts.factory.createVariableStatement(
402    undefined,
403    ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
404      ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION),
405      undefined,
406      undefined,
407      ts.factory.createArrowFunction(
408        undefined,
409        undefined,
410        [],
411        undefined,
412        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
413        ts.factory.createBlock(
414          [ts.factory.createReturnStatement(
415            integrateParams ? componentNode.arguments[0] : ts.factory.createObjectLiteralExpression(
416              reWriteComponentParams(keyArray, valueArray),
417              true
418            )
419          )],
420          true
421        )
422      )
423    )],
424    ts.NodeFlags.Let
425    ));
426}
427
428function reWriteComponentParams(keyArray: ts.Node[], valueArray: ts.Node[]): (ts.PropertyAssignment |
429  ts.ShorthandPropertyAssignment)[] {
430  const returnProperties: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = [];
431  keyArray.forEach((item: ts.Identifier, index: number) => {
432    if (!valueArray[index]) {
433      returnProperties.push(ts.factory.createShorthandPropertyAssignment(
434        item,
435        undefined
436      ));
437    } else {
438      returnProperties.push(ts.factory.createPropertyAssignment(
439        item,
440        valueArray[index] as ts.Identifier
441      ));
442    }
443  });
444  return returnProperties;
445}
446
447function splitComponentParams(componentNode: ts.CallExpression, isBuilder: boolean, isParamsLambda: boolean): [ts.Node[], ts.Node[]] {
448  const keyArray: ts.Node[] = [];
449  const valueArray: ts.Node[] = [];
450  if (componentNode.arguments && componentNode.arguments.length > 0 &&
451    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
452    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
453      const newPropertyItem: ts.PropertyAssignment =
454        isProperty(propertyItem) ? createReference(propertyItem, [], isBuilder, isParamsLambda) : propertyItem;
455      keyArray.push(newPropertyItem.name);
456      valueArray.push(newPropertyItem.initializer);
457    });
458  }
459  return [keyArray, valueArray];
460}
461
462function createIfCustomComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
463  componentParameter: ts.ObjectLiteralExpression, name: string, isGlobalBuilder: boolean, isBuilder: boolean,
464  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo): ts.IfStatement {
465  return ts.factory.createIfStatement(
466    ts.factory.createIdentifier(ISINITIALRENDER),
467    ts.factory.createBlock(
468      [ componentParamDetachment(newNode, isRecycleComponent),
469        isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) :
470          createNewComponent(COMPONENT_CALL),
471        assignComponentParams(componentNode, isBuilder),
472        assignmentFunction(COMPONENT_CALL)
473      ], true),
474    ts.factory.createBlock(
475      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
476        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
477          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
478        ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID)
479        ), undefined,
480        [ts.factory.createIdentifier(ELMTID), componentParameter]))], true)
481  );
482}
483
484export function assignmentFunction(componeParamName: string): ts.ExpressionStatement {
485  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
486    ts.factory.createPropertyAccessExpression(
487      ts.factory.createIdentifier(componeParamName),
488      ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION)
489    ),
490    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
491    ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)
492  ))
493}
494
495function componentParamDetachment(newNode: ts.NewExpression, isRecycleComponent: boolean): ts.VariableStatement {
496  return ts.factory.createVariableStatement(
497    undefined,
498    ts.factory.createVariableDeclarationList(
499      [ts.factory.createVariableDeclaration(
500        ts.factory.createIdentifier(COMPONENT_CALL),
501        undefined,
502        undefined,
503        isRecycleComponent ? ts.factory.createConditionalExpression(
504          ts.factory.createIdentifier(RECYCLE_NODE),
505          ts.factory.createToken(ts.SyntaxKind.QuestionToken),
506          ts.factory.createIdentifier(RECYCLE_NODE),
507          ts.factory.createToken(ts.SyntaxKind.ColonToken),
508          newNode) : newNode
509      )],
510      ts.NodeFlags.Let
511    ));
512}
513
514function createNewComponent(componeParamName: string): ts.Statement {
515  return ts.factory.createExpressionStatement(
516    ts.factory.createCallExpression(
517      ts.factory.createPropertyAccessExpression(
518        ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
519        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
520      ), undefined, [ts.factory.createIdentifier(componeParamName)]));
521}
522
523function createNewRecycleComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
524  name: string, componentAttrInfo: ComponentAttrInfo): ts.Statement {
525  let argNode: ts.Expression[] = [];
526  const componentParam: ts.PropertyAssignment[] = [];
527  if (componentNode.arguments && componentNode.arguments.length > 0 &&
528    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
529    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
530      const newPropertyItem: ts.PropertyAssignment = createReference(propertyItem, [], false, false, true);
531      componentParam.push(newPropertyItem);
532    });
533    argNode = [ts.factory.createObjectLiteralExpression(componentParam, false)];
534  } else {
535    argNode = [ts.factory.createObjectLiteralExpression([], false)];
536  }
537  const recycleNode: ts.CallExpression = ts.factory.createCallExpression(
538    createRecyclePropertyNode(ABOUT_TO_REUSE), undefined, argNode);
539  return ts.factory.createExpressionStatement(
540    ts.factory.createCallExpression(
541      ts.factory.createPropertyAccessExpression(
542        ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
543        ts.factory.createIdentifier(COMPONENT_CREATE_RECYCLE)
544      ), undefined,
545      [
546        ts.factory.createIdentifier(COMPONENT_CALL),
547        ts.factory.createBinaryExpression(
548          ts.factory.createIdentifier(RECYCLE_NODE),
549          ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
550          ts.factory.createNull()
551        ),
552        componentAttrInfo.reuseId ? componentAttrInfo.reuseId as ts.Expression :
553          ts.factory.createStringLiteral(name),
554        ts.factory.createArrowFunction(undefined, undefined, [], undefined,
555          ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
556          ts.factory.createBlock([
557            ts.factory.createIfStatement(
558              ts.factory.createBinaryExpression(
559                ts.factory.createIdentifier(RECYCLE_NODE),
560                ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
561                ts.factory.createBinaryExpression(
562                  ts.factory.createTypeOfExpression(
563                    createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION)),
564                  ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
565                  ts.factory.createStringLiteral(FUNCTION)
566                )),
567              ts.factory.createBlock([
568                ts.factory.createExpressionStatement(ts.factory.createCallExpression(
569                  createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION),
570                  undefined,
571                  []
572                ))
573              ], true),
574              ts.factory.createBlock(
575                [
576                  ts.factory.createIfStatement(ts.factory.createBinaryExpression(
577                    createRecyclePropertyNode(ABOUT_TO_REUSE), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
578                    ts.factory.createBinaryExpression(
579                      ts.factory.createTypeOfExpression(createRecyclePropertyNode(ABOUT_TO_REUSE)),
580                      ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
581                      ts.factory.createStringLiteral(FUNCTION)
582                    )),
583                  ts.factory.createBlock([ts.factory.createExpressionStatement(recycleNode)], true)),
584                  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
585                    createRecyclePropertyNode(COMPONENT_RERENDER_FUNCTION), undefined, []
586                  ))
587                ],
588                true
589              )
590            )], true))
591      ]));
592}
593
594function createRecyclePropertyNode(recycleFunctionName: string): ts.PropertyAccessExpression {
595  return ts.factory.createPropertyAccessExpression(
596    ts.factory.createIdentifier(RECYCLE_NODE), ts.factory.createIdentifier(recycleFunctionName));
597}
598
599function validateCustomComponentPrams(node: ts.CallExpression, name: string,
600  props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void {
601  const curChildProps: Set<string> = new Set([]);
602  const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments;
603  const linkSet: Set<string> = getCollectionSet(name, linkCollection);
604  if (nodeArguments && nodeArguments.length === 1 &&
605    ts.isObjectLiteralExpression(nodeArguments[0])) {
606    const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression;
607    nodeArgument.properties.forEach(item => {
608      if (item.name && ts.isIdentifier(item.name)) {
609        curChildProps.add(item.name.escapedText.toString());
610      }
611      validateStateManagement(item, name, log, isBuilder);
612      if (isNonThisProperty(item, linkSet)) {
613        if (isToChange(item as ts.PropertyAssignment, name)) {
614          item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment,
615            item.name, changeNodeFromCallToArrow(item.initializer));
616        }
617        props.push(item);
618      }
619    });
620  }
621  validateInitDecorator(node, name, curChildProps, log);
622  validateMandatoryToInitViaParam(node, name, curChildProps, log);
623}
624
625function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression {
626  let temp: any = node.expression;
627  let child: any = null;
628  let componentNode: any = null;
629  while (temp) {
630    if (ts.isIdentifier(temp)) {
631      child = temp;
632      break;
633    }
634    temp = temp.expression;
635  }
636  if (child) {
637    let parent = child.parent;
638    while (parent) {
639      if (ts.isExpressionStatement(parent)) {
640        break;
641      }
642      if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) {
643        componentNode = parent;
644        break;
645      }
646      parent = parent.parent;
647    }
648  }
649  return componentNode;
650}
651
652function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> {
653  if (!collection) {
654    return new Set([]);
655  }
656  return collection.get(name) || new Set([]);
657}
658
659function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
660  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
661    propertySet.has(node.name.escapedText.toString())) {
662    return true;
663  }
664  return false;
665}
666
667function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
668  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
669    (node.initializer.escapedText && node.initializer.escapedText.includes('$') ||
670    ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
671    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
672    ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) {
673    return false;
674  }
675  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
676    !propertySet.has(node.name.escapedText.toString())) {
677    return true;
678  }
679  return false;
680}
681
682function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string,
683  log: LogInfo[], isBuilder: boolean): void {
684  validateForbiddenToInitViaParam(node, customComponentName, log);
685  checkFromParentToChild(node, customComponentName, log, isBuilder);
686}
687
688function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string,
689  log: LogInfo[], isBuilder: boolean): void {
690  let propertyName: string;
691  if (ts.isIdentifier(node.name)) {
692    propertyName = node.name.escapedText.toString();
693  }
694  const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName);
695  let parentPropertyName: string;
696  if (curPropertyKind) {
697    if (isInitFromParent(node)) {
698      parentPropertyName =
699        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log);
700      let parentPropertyKind: string = curPropMap.get(parentPropertyName);
701      if (!parentPropertyKind) {
702        parentPropertyKind = COMPONENT_NON_DECORATOR;
703      }
704      if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
705        validateIllegalInitFromParent(
706          node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log);
707      }
708    } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) &&
709      curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) {
710      if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) {
711        validateIllegalInitFromParent(node, propertyName, curPropertyKind,
712          node.initializer.getText(), COMPONENT_NON_DECORATOR, log);
713      }
714    } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer &&
715      (ts.isPropertyAccessExpression(node.initializer) ||
716        ts.isElementAccessExpression(node.initializer) || ts.isIdentifier(node.initializer))) {
717      return;
718    } else {
719      parentPropertyName =
720        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName;
721      const parentPropertyKind = COMPONENT_NON_DECORATOR;
722      if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
723        if (isBuilder && judgeStructAssigned$$(node)) {
724          log.push({
725            type: LogType.WARN,
726            message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` +
727              `${curPropertyKind} property '${propertyName}' by yourself.`,
728            // @ts-ignore
729            pos: node.initializer ? node.initializer.getStart() : node.getStart()
730          });
731        } else {
732          validateIllegalInitFromParent(
733            node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN);
734        }
735      }
736    }
737  }
738}
739
740function judgeStructAssigned$$(node: ts.ObjectLiteralElementLike): boolean {
741  return partialUpdateConfig.partialUpdateMode && node.initializer &&
742    ts.isPropertyAccessExpression(node.initializer) &&
743    node.initializer.expression && ts.isIdentifier(node.initializer.expression) &&
744    node.initializer.expression.escapedText.toString() === $$;
745}
746
747function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean {
748  if (ts.isPropertyAssignment(node) && node.initializer) {
749    if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
750    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
751    ts.isIdentifier(node.initializer.name)) {
752      return true;
753    } else if (ts.isIdentifier(node.initializer) &&
754      matchStartWithDollar(node.initializer.getText())) {
755      return true;
756    }
757  }
758}
759
760function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean {
761  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) &&
762    !matchStartWithDollar(node.initializer.getText())) {
763    return true;
764  }
765}
766
767function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string,
768  log: LogInfo[]): string {
769  const initExpression: ts.Expression = node.initializer;
770  if (!initExpression) {
771    return undefined;
772  }
773  let parentPropertyName: string = initExpression.getText();
774  if (curPropertyKind === COMPONENT_LINK_DECORATOR) {
775    // @ts-ignore
776    const initName: ts.Identifier = initExpression.name || initExpression;
777    if (hasDollar(initExpression)) {
778      parentPropertyName = initName.getText().replace(/^\$/, '');
779    } else {
780      parentPropertyName = initName.getText();
781    }
782  } else {
783    if (hasDollar(initExpression)) {
784      validateNonLinkWithDollar(node, log);
785    } else {
786      // @ts-ignore
787      if (node.initializer && node.initializer.name) {
788        parentPropertyName = node.initializer.name.getText();
789      }
790    }
791  }
792
793  return parentPropertyName;
794}
795
796function isCorrectInitFormParent(parent: string, child: string): boolean {
797  switch (child) {
798    case COMPONENT_STATE_DECORATOR:
799    case COMPONENT_PROP_DECORATOR:
800    case COMPONENT_PROVIDE_DECORATOR:
801      return true;
802    case COMPONENT_NON_DECORATOR:
803      if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR,
804        COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) {
805        return true;
806      }
807      break;
808    case COMPONENT_LINK_DECORATOR:
809    case COMPONENT_OBJECT_LINK_DECORATOR:
810      return ![COMPONENT_NON_DECORATOR].includes(parent);
811  }
812  return false;
813}
814
815function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string {
816  for (const item of decoractorMap.entries()) {
817    if (getCollectionSet(customComponentName, item[1]).has(propertyName)) {
818      return item[0];
819    }
820  }
821}
822
823function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement {
824  return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList(
825    [ts.factory.createVariableDeclaration(ts.factory.createIdentifier(
826      `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode(
827      ts.factory.createIdentifier(name)),
828    ts.factory.createConditionalExpression(
829      ts.factory.createParenthesizedExpression(
830        ts.factory.createBinaryExpression(
831          createConditionParent(isBuilder),
832          ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
833          ts.factory.createPropertyAccessExpression(
834            createConditionParent(isBuilder),
835            ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID)
836          ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken),
837      ts.factory.createAsExpression(ts.factory.createCallExpression(
838        ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder),
839          ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined,
840        [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID),
841          undefined, []) : ts.factory.createStringLiteral(id)]),
842      ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))),
843      ts.factory.createToken(ts.SyntaxKind.ColonToken),
844      ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)))], ts.NodeFlags.Let));
845}
846
847export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression {
848  return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis();
849}
850
851function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement,
852  newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement {
853  const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`;
854  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
855    ts.factory.createIdentifier(viewName),
856    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
857    ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)),
858  ts.factory.createBlock([node], true),
859  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
860    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(
861      viewName), ts.factory.createIdentifier(
862      `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])),
863  isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined,
864  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
865    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`),
866      ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined,
867    [ts.factory.createIdentifier(viewName)]))], true));
868}
869
870function createStaticIf(name: string): ts.IfStatement {
871  return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(
872    ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(
873      ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
874        ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])),
875  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
876    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
877      ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)),
878    undefined, []))], true), undefined);
879}
880
881function hasDollar(initExpression: ts.Expression): boolean {
882  if (ts.isPropertyAccessExpression(initExpression) &&
883    matchStartWithDollar(initExpression.name.getText())) {
884    return true;
885  } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) {
886    return true;
887  } else {
888    return false;
889  }
890}
891
892function matchStartWithDollar(name: string): boolean {
893  return /^\$/.test(name);
894}
895
896function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike,
897  customComponentName: string, log: LogInfo[]): void {
898  const forbiddenToInitViaParamSet: Set<string> = new Set([
899    ...getCollectionSet(customComponentName, storageLinkCollection),
900    ...getCollectionSet(customComponentName, storagePropCollection),
901    ...getCollectionSet(customComponentName, consumeCollection)
902  ]);
903  const localStorageSet: Set<string> = new Set();
904  getLocalStorageCollection(customComponentName, localStorageSet);
905  if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) {
906    log.push({
907      type: LogType.ERROR,
908      message: `Property '${node.name.getText()}' in the custom component '${customComponentName}'` +
909        ` cannot initialize here (forbidden to specify).`,
910      pos: node.name.getStart()
911    });
912  }
913}
914
915function validateMandatoryToInitViaParam(node: ts.CallExpression, customComponentName: string,
916  curChildProps: Set<string>, log: LogInfo[]): void {
917  const mandatoryToInitViaParamSet: Set<string> = new Set([
918    ...getCollectionSet(customComponentName, linkCollection),
919    ...getCollectionSet(customComponentName, objectLinkCollection)]);
920  mandatoryToInitViaParamSet.forEach(item => {
921    if (item && !curChildProps.has(item)) {
922      log.push({
923        type: LogType.ERROR,
924        message: `Property '${item}' in the custom component '${customComponentName}'` +
925          ` is missing (mandatory to specify).`,
926        pos: node.getStart()
927      });
928    }
929  });
930}
931
932function validateInitDecorator(node: ts.CallExpression, customComponentName: string,
933  curChildProps: Set<string>, log: LogInfo[]): void {
934  const mandatoryToInitViaParamSet: Set<string> = new Set([
935    ...getCollectionSet(customComponentName, builderParamObjectCollection),
936    ...getCollectionSet(customComponentName, propCollection)]);
937  const decoratorVariable: Set<string> = new Set([
938    ...(builderParamInitialization.get(customComponentName) || []),
939    ...(propInitialization.get(customComponentName) || [])]);
940  mandatoryToInitViaParamSet.forEach((item: string) => {
941    if (item && !curChildProps.has(item) && decoratorVariable && decoratorVariable.has(item)) {
942      log.push({
943        type: LogType.ERROR,
944        message: `Property '${item}' must be initialized through the component constructor.`,
945        pos: node.getStart()
946      });
947    }
948  });
949}
950
951function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string,
952  curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string,
953  log: LogInfo[], inputType: LogType = undefined): void {
954  let type: LogType = LogType.ERROR;
955  if (inputType) {
956    type = inputType;
957  } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes(
958    parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) {
959    type = LogType.WARN;
960  }
961  log.push({
962    type: type,
963    message: `The ${parentPropertyKind} property '${parentPropertyName}' cannot be assigned to ` +
964      `the ${curPropertyKind} property '${propertyName}'.`,
965    // @ts-ignore
966    pos: node.initializer ? node.initializer.getStart() : node.getStart()
967  });
968}
969
970function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void {
971  log.push({
972    type: LogType.ERROR,
973    message: `Property '${node.name.getText()}' cannot initialize` +
974      ` using '$' to create a reference to a variable.`,
975    pos: node.initializer.getStart()
976  });
977}
978