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