• 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  regularInitialization,
76  stateInitialization,
77  provideInitialization,
78  privateCollection,
79  componentCollection
80} from './validate_ui_syntax';
81import {
82  curPropMap,
83  createViewCreate,
84  createCustomComponentNewExpression,
85  isLocalStorageParameter,
86  isBasicType
87} from './process_component_member';
88import {
89  LogType,
90  LogInfo,
91  componentInfo,
92  storedFileInfo
93} from './utils';
94import {
95  bindComponentAttr,
96  parentConditionalExpression,
97  createComponentCreationStatement,
98  createFunction,
99  ComponentAttrInfo,
100  ifRetakeId,
101  transferBuilderCall,
102  createCollectElmtIdNode,
103  createViewStackProcessorStatement,
104  BuilderParamsResult
105} from './process_component_build';
106import {
107  partialUpdateConfig,
108  projectConfig,
109  globalProgram
110} from '../main';
111import {
112  GLOBAL_CUSTOM_BUILDER_METHOD
113} from './component_map';
114import {
115  createReference,
116  isProperty
117} from './process_component_class';
118import processStructComponentV2, { StructInfo, ParamDecoratorInfo } from './process_struct_componentV2';
119import constantDefine from './constant_define';
120import createAstNodeUtils from './create_ast_node_utils';
121
122let decoractorMap: Map<string, Map<string, Set<string>>>;
123
124export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
125  log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false,
126  idName: ts.Expression = undefined, builderParamsResult: BuilderParamsResult = null): void {
127  decoractorMap = new Map(
128    [[COMPONENT_STATE_DECORATOR, stateCollection],
129      [COMPONENT_LINK_DECORATOR, linkCollection],
130      [COMPONENT_PROP_DECORATOR, propCollection],
131      [COMPONENT_NON_DECORATOR, regularCollection],
132      [COMPONENT_PROVIDE_DECORATOR, provideCollection],
133      [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]);
134  const componentNode: ts.CallExpression = getCustomComponentNode(node);
135  if (componentNode) {
136    const isRecycleComponent: boolean = isRecycle(name);
137    const hasChainCall: boolean = componentNode.parent &&
138      ts.isPropertyAccessExpression(componentNode.parent);
139    let ischangeNode: boolean = false;
140    let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression(
141      componentNode, name, isBuilder, isGlobalBuilder);
142    let argumentsArray: ts.PropertyAssignment[];
143    const componentAttrInfo: ComponentAttrInfo = { reuseId: null, hasIdAttr: false, attrCount: 0 };
144    if (isHasChild(componentNode)) {
145      // @ts-ignore
146      argumentsArray = componentNode.arguments[0].properties.slice();
147      argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => {
148        if (isToChange(item, name)) {
149          ischangeNode = true;
150          const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment(
151            item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression));
152          argumentsArray.splice(index, 1, propertyAssignmentNode);
153        }
154      });
155      if (ischangeNode) {
156        const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node,
157          ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments,
158            [ts.factory.createObjectLiteralExpression(argumentsArray, true)]));
159        customComponentNewExpression = createCustomComponentNewExpression(
160          newNode.expression as ts.CallExpression, name, isBuilder);
161      }
162    }
163    let judgeIdStart: number;
164    if (partialUpdateConfig.partialUpdateMode && idName) {
165      judgeIdStart = newStatements.length;
166    }
167    let needCommon: boolean = false;
168    if (hasChainCall) {
169      if (partialUpdateConfig.partialUpdateMode) {
170        const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement(
171          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
172            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))];
173        const immutableStatements: ts.Statement[] = [];
174        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode,
175          log, true, false, immutableStatements, false, componentAttrInfo);
176        needCommon = commomComponentNode.length > 1 || immutableStatements.length > 0;
177        if (componentAttrInfo.hasIdAttr && componentAttrInfo.attrCount === 1) {
178          commomComponentNode[0] = createCommonIdAttrNode();
179        }
180        if (needCommon) {
181          newStatements.push(createComponentCreationStatement(componentAttributes(COMPONENT_COMMON),
182            commomComponentNode, COMPONENT_COMMON, isGlobalBuilder, false, undefined, immutableStatements,
183            builderParamsResult, isRecycleComponent));
184        }
185      } else {
186        newStatements.push(ts.factory.createExpressionStatement(
187          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
188            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)));
189        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log);
190      }
191    }
192    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
193      newStatements.push(createRecycleComponent(isGlobalBuilder));
194    }
195    addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode,
196      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult);
197    if (hasChainCall && (!partialUpdateConfig.partialUpdateMode || needCommon)) {
198      newStatements.push(ts.factory.createExpressionStatement(
199        createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
200          ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
201    }
202    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
203      newStatements.push(componentAttributes(COMPONENT_RECYCLE));
204    }
205    if (partialUpdateConfig.partialUpdateMode && idName) {
206      newStatements.splice(judgeIdStart, newStatements.length - judgeIdStart,
207        ifRetakeId(newStatements.slice(judgeIdStart), idName));
208    }
209  }
210}
211
212function createCommonIdAttrNode(): ts.ExpressionStatement {
213  return ts.factory.createExpressionStatement(
214    createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
215      ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION),
216      // @ts-ignore
217      [ts.factory.createTrue()]));
218}
219
220export function isRecycle(componentName: string): boolean {
221  return storedFileInfo.getCurrentArkTsFile().recycleComponents.has(componentName);
222}
223
224function createRecycleComponent(isGlobalBuilder: boolean): ts.Statement {
225  return createComponentCreationStatement(componentAttributes(COMPONENT_RECYCLE),
226    [ts.factory.createExpressionStatement(
227      createFunction(ts.factory.createIdentifier(COMPONENT_RECYCLE),
228        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))
229    ], COMPONENT_RECYCLE, isGlobalBuilder);
230}
231
232function componentAttributes(componentName: string): ts.Statement {
233  return ts.factory.createExpressionStatement(
234    ts.factory.createCallExpression(
235      ts.factory.createPropertyAccessExpression(
236        ts.factory.createIdentifier(componentName),
237        ts.factory.createIdentifier(COMPONENT_POP_FUNCTION)
238      ), undefined, []
239    ));
240}
241
242function isHasChild(node: ts.CallExpression): boolean {
243  return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) &&
244    node.arguments[0].properties && node.arguments[0].properties.length > 0;
245}
246
247function isToChange(item: ts.PropertyAssignment, name: string): boolean {
248  const builderParamName: Set<string> = builderParamObjectCollection.get(name);
249  if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName &&
250    builderParamName.has(item.name.getText()) &&
251    !/\.(bind|call|apply)/.test(item.initializer.getText())) {
252    return true;
253  }
254  return false;
255}
256
257function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ConditionalExpression {
258  let builderBindThis: ts.ExpressionStatement = ts.factory.createExpressionStatement(node);
259  if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
260    GLOBAL_CUSTOM_BUILDER_METHOD.has(node.expression.escapedText.toString())) {
261    builderBindThis = transferBuilderCall(ts.factory.createExpressionStatement(node), node.expression.escapedText.toString());
262  }
263  return changeNodeFromCallToArrowDetermine(node, builderBindThis);
264}
265
266function changeNodeFromCallToArrowDetermine(node: ts.CallExpression, builderBindThis: ts.ExpressionStatement): ts.ConditionalExpression {
267  if (ts.isCallExpression(node)) {
268    return ts.factory.createConditionalExpression(
269      ts.factory.createBinaryExpression(
270        ts.factory.createTypeOfExpression(node),
271        ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
272        ts.factory.createStringLiteral(FUNCTION)
273      ),
274      ts.factory.createToken(ts.SyntaxKind.QuestionToken),
275      node,
276      ts.factory.createToken(ts.SyntaxKind.ColonToken),
277      ts.factory.createArrowFunction(
278        undefined,
279        undefined,
280        [],
281        undefined,
282        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
283        ts.factory.createBlock(
284          [builderBindThis],
285          true
286        )
287      )
288    );
289  }
290  return undefined;
291}
292
293function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
294  newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression,
295  isBuilder: boolean, isGlobalBuilder: boolean, isRecycleComponent: boolean,
296  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult): void {
297  if (ts.isNewExpression(newNode)) {
298    const propertyArray: ts.ObjectLiteralElementLike[] = [];
299    validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder);
300    addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode,
301      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult, log);
302  }
303}
304
305function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[],
306  newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[],
307  componentNode: ts.CallExpression, isBuilder: boolean, isGlobalBuilder: boolean,
308  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo,
309  builderParamsResult: BuilderParamsResult, log: LogInfo[]): void {
310  if (!partialUpdateConfig.partialUpdateMode) {
311    const id: string = componentInfo.id.toString();
312    newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id,
313      ts.factory.updateExpressionStatement(node, createViewCreate(newNode)),
314      ts.factory.createObjectLiteralExpression(props, true), name));
315  } else {
316    newStatements.push(createCustomComponent(newNode, name, componentNode, isGlobalBuilder, isBuilder,
317      isRecycleComponent, componentAttrInfo, builderParamsResult, log));
318  }
319}
320
321function createChildElmtId(node: ts.CallExpression, name: string, log: LogInfo[]): ts.PropertyAssignment[] {
322  const childParam: ts.PropertyAssignment[] = [];
323  const propsAndObjectLinks: string[] = [];
324  if (propCollection.get(name)) {
325    propsAndObjectLinks.push(...propCollection.get(name));
326  }
327  if (objectLinkCollection.get(name)) {
328    propsAndObjectLinks.push(...objectLinkCollection.get(name));
329  }
330  if (projectConfig.optLazyForEach && storedFileInfo.processLazyForEach && stateCollection.get(name)) {
331    propsAndObjectLinks.push(...stateCollection.get(name));
332  }
333  parseChildProperties(name, node, childParam, propsAndObjectLinks, log);
334  return childParam;
335}
336
337class ChildAndParentComponentInfo {
338  childStructInfo: StructInfo;
339  parentStructInfo: StructInfo;
340  paramDecoratorMap: Map<string, ParamDecoratorInfo>;
341  updatePropsDecoratorsV2: string[];
342  propsAndObjectLinks: string[];
343  childName: string;
344  forbiddenInitPropsV2: string[];
345  updatePropsForV1Parent: string[];
346  updatePropsForV2Parent: string[];
347  constructor(childName: string, childNode: ts.CallExpression, propsAndObjectLinks: string[]) {
348    this.childName = childName;
349    this.propsAndObjectLinks = propsAndObjectLinks;
350    this.childStructInfo = processStructComponentV2.getAliasStructInfo(childNode) ||
351      processStructComponentV2.getOrCreateStructInfo(childName);
352    this.paramDecoratorMap = this.childStructInfo.paramDecoratorMap;
353    this.updatePropsDecoratorsV2 = [...this.childStructInfo.eventDecoratorSet, ...this.paramDecoratorMap.keys()];
354    this.parentStructInfo = componentCollection.currentClassName ?
355      processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) :
356      new StructInfo();
357    this.forbiddenInitPropsV2 = [...this.childStructInfo.localDecoratorSet,
358      ...this.childStructInfo.providerDecoratorSet, ...this.childStructInfo.consumerDecoratorSet,
359      ...this.childStructInfo.regularSet];
360    this.updatePropsForV1Parent = getUpdatePropsForV1Parent();
361    this.updatePropsForV2Parent = [...this.parentStructInfo.localDecoratorSet,
362      ...this.parentStructInfo.paramDecoratorMap.keys(), ...this.parentStructInfo.providerDecoratorSet,
363      ...this.parentStructInfo.consumerDecoratorSet];
364  }
365}
366
367function getUpdatePropsForV1Parent(): string[] {
368  const propertiesMapArr: Array<Map<string, Set<string>>> = [
369    stateCollection, linkCollection, propCollection,
370    provideCollection, consumeCollection, objectLinkCollection,
371    storagePropCollection, storageLinkCollection
372  ];
373  const updatePropsForParent: string[] = [];
374  if (componentCollection.currentClassName) {
375    const localStorageSet: Set<string> = new Set();
376    getLocalStorageCollection(componentCollection.currentClassName, localStorageSet);
377    updatePropsForParent.push(...localStorageSet);
378    propertiesMapArr.forEach((item: Map<string, Set<string>>) => {
379      const value: Set<string> = item.get(componentCollection.currentClassName);
380      if (value) {
381        updatePropsForParent.push(...value);
382      }
383    });
384  }
385  return updatePropsForParent;
386}
387
388function parseChildProperties(childName: string, node: ts.CallExpression,
389  childParam: ts.PropertyAssignment[], propsAndObjectLinks: string[], log: LogInfo[]): void {
390  const childAndParentComponentInfo: ChildAndParentComponentInfo =
391    new ChildAndParentComponentInfo(childName, node, propsAndObjectLinks);
392  if (node.arguments[0].properties) {
393    node.arguments[0].properties.forEach((item: ts.PropertyAssignment) => {
394      if (ts.isIdentifier(item.name)) {
395        const itemName: string = item.name.escapedText.toString();
396        validateChildProperty(item, itemName, childParam, log, childAndParentComponentInfo);
397      }
398    });
399  }
400}
401
402function validateChildProperty(item: ts.PropertyAssignment, itemName: string,
403  childParam: ts.PropertyAssignment[], log: LogInfo[], info: ChildAndParentComponentInfo): void {
404  if (info.childStructInfo.isComponentV2) {
405    if (info.forbiddenInitPropsV2.includes(itemName)) {
406      log.push({
407        type: LogType.ERROR,
408        message: `Property '${itemName}' in the custom component '${info.childName}'` +
409          ` cannot be initialized here (forbidden to specify).`,
410        pos: item.getStart()
411      });
412      return;
413    }
414    if (info.paramDecoratorMap.has(itemName)) {
415      childParam.push(item);
416    }
417    if (isForbiddenAssignToComponentV2(item, itemName, info)) {
418      log.push({
419        type: LogType.ERROR,
420        message: `Property '${itemName}' in the @ComponentV2 component '${info.childName}' are not allowed to be assigned values here.`,
421        pos: item.getStart()
422      });
423    }
424  } else {
425    if (info.propsAndObjectLinks.includes(itemName)) {
426      childParam.push(item);
427    }
428    if (isForbiddenAssignToComponentV1(item, itemName, info)) {
429      log.push({
430        type: LogType.ERROR,
431        message: `Property '${itemName}' in the @Component component '${info.childName}' are not allowed to be assigned values here.`,
432        pos: item.getStart()
433      });
434    }
435  }
436}
437
438function isForbiddenAssignToComponentV1(item: ts.PropertyAssignment, itemName: string,
439  info: ChildAndParentComponentInfo): boolean {
440  if (info.parentStructInfo.isComponentV2 && info.childStructInfo.updatePropsDecoratorsV1.includes(itemName) &&
441    isObervedProperty(item.initializer, info, false) && globalProgram.checker) {
442    const type: ts.Type = globalProgram.checker.getTypeAtLocation(item.initializer);
443    return isForbiddenTypeToComponentV1(type);
444  }
445  return false;
446}
447
448function isForbiddenTypeToComponentV1(type: ts.Type): boolean {
449  // @ts-ignore
450  if (type.types && type.types.length) {
451    // @ts-ignore
452    return !type.types.some((item: ts.Type) => {
453      return !isForbiddenTypeToComponentV1(item);
454    });
455  }
456  const allowedTypes: string[] = ['Set', 'Map', 'Date', 'Array'];
457  const name: string = type?.getSymbol()?.getName();
458  if (name && allowedTypes.includes(name)) {
459    return true;
460  }
461  return false;
462}
463
464function isForbiddenAssignToComponentV2(item: ts.PropertyAssignment, itemName: string,
465  info: ChildAndParentComponentInfo): boolean {
466  if (!info.parentStructInfo.isComponentV2 && info.updatePropsDecoratorsV2.includes(itemName) &&
467    isObervedProperty(item.initializer, info) && globalProgram.strictChecker) {
468    const type: ts.Type = globalProgram.strictChecker.getTypeAtLocation(item.initializer);
469    return !isAllowedTypeToComponentV2(type);
470  }
471  return false;
472}
473
474function isObervedProperty(value: ts.Expression, info: ChildAndParentComponentInfo,
475  isV1Parent: boolean = true): boolean {
476  if (value && ts.isPropertyAccessExpression(value) && value.expression.kind === ts.SyntaxKind.ThisKeyword &&
477    ts.isIdentifier(value.name)) {
478    const propertyName: string = value.name.escapedText.toString();
479    return isV1Parent ? info.updatePropsForV1Parent.includes(propertyName) :
480      info.updatePropsForV2Parent.includes(propertyName);
481  }
482  return false;
483}
484
485function isAllowedTypeToComponentV2(type: ts.Type): boolean {
486  if (type) {
487    // @ts-ignore
488    if (type.types && type.types.length) {
489      // @ts-ignore
490      return type.types.some((item: ts.Type) => {
491        return isAllowedTypeToComponentV2(item);
492      });
493    }
494    // string|number|boolean|enum|null|undefined
495    if (isAllowedTypeForBasic(type.flags)) {
496      return true;
497    }
498  }
499  return false;
500}
501
502function isAllowedTypeForBasic(flags: ts.TypeFlags): boolean {
503  if (isBasicType(flags) || (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined))) {
504    return true;
505  }
506  return false;
507}
508
509function validateInitParam(childName: string, curChildProps: Set<string>,
510  node: ts.CallExpression, log: LogInfo[], parentStructInfo: StructInfo): void {
511  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(node) ||
512    processStructComponentV2.getOrCreateStructInfo(childName);
513  const paramDecoratorMap: Map<string, ParamDecoratorInfo> = childStructInfo.paramDecoratorMap;
514  if (childStructInfo.isComponentV2) {
515    const needInitParam: string[] = [];
516    for (const item of paramDecoratorMap) {
517      if (item[1].hasRequire) {
518        needInitParam.push(item[0]);
519      }
520    }
521    needInitParam.forEach((paramName: string) => {
522      if (!curChildProps.has(paramName)) {
523        log.push({
524          type: LogType.ERROR,
525          message: `Property '${paramName}' must be initialized through the component constructor.`,
526          pos: node.getStart()
527        });
528      }
529    });
530  } else if (parentStructInfo.isComponentV2 && childStructInfo.linkDecoratorsV1.length) {
531    log.push({
532      type: LogType.ERROR,
533      message: 'The @ComponentV2 struct must not contain any @Component with an @Link decorated variable',
534      pos: node.getStart()
535    });
536  }
537}
538
539function createCustomComponent(newNode: ts.NewExpression, name: string, componentNode: ts.CallExpression,
540  isGlobalBuilder: boolean, isBuilder: boolean, isRecycleComponent: boolean,
541  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult, log: LogInfo[]): ts.Block {
542  let componentParameter: ts.ObjectLiteralExpression;
543  if (componentNode.arguments && componentNode.arguments.length) {
544    componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name, log), true);
545  } else {
546    componentParameter = ts.factory.createObjectLiteralExpression([], false);
547  }
548  const arrowArgArr: ts.ParameterDeclaration[] = [
549    ts.factory.createParameterDeclaration(undefined, undefined,
550      ts.factory.createIdentifier(ELMTID)
551    ),
552    ts.factory.createParameterDeclaration(undefined, undefined,
553      ts.factory.createIdentifier(ISINITIALRENDER)
554    )
555  ];
556  const arrowBolck: ts.Statement[] = [
557    projectConfig.optLazyForEach && storedFileInfo.processLazyForEach ? createCollectElmtIdNode() : undefined,
558    createIfCustomComponent(newNode, componentNode, componentParameter, name, isGlobalBuilder,
559      isBuilder, isRecycleComponent, componentAttrInfo, log)
560  ];
561  if (isRecycleComponent) {
562    arrowArgArr.push(ts.factory.createParameterDeclaration(
563      undefined, undefined, ts.factory.createIdentifier(RECYCLE_NODE),
564      undefined, undefined, ts.factory.createNull()
565    ));
566  } else if (partialUpdateConfig.optimizeComponent && isGlobalBuilder &&
567    builderParamsResult && builderParamsResult.firstParam) {
568    const paramName: ts.Identifier = builderParamsResult.firstParam.name as ts.Identifier;
569    arrowArgArr.push(ts.factory.createParameterDeclaration(undefined, undefined,
570      paramName, undefined, undefined, ts.factory.createIdentifier(`__${paramName.escapedText.toString()}__`)
571    ));
572  }
573  if (isRecycleComponent || !partialUpdateConfig.optimizeComponent) {
574    arrowBolck.unshift(createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID));
575    arrowBolck.push(createViewStackProcessorStatement(STOPGETACCESSRECORDING));
576  }
577  const observeArgArr: ts.Node[] = [
578    ts.factory.createArrowFunction(undefined, undefined, arrowArgArr, undefined,
579      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
580      ts.factory.createBlock(arrowBolck, true))
581  ];
582  if (isRecycleComponent) {
583    componentAttrInfo.reuseId ? observeArgArr.unshift(componentAttrInfo.reuseId) :
584      observeArgArr.unshift(ts.factory.createStringLiteral(name));
585  } else if (partialUpdateConfig.optimizeComponent) {
586    observeArgArr.push(componentPop(name));
587  }
588  return ts.factory.createBlock(
589    [
590      ts.factory.createExpressionStatement(ts.factory.createCallExpression(
591        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
592          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
593        isRecycleComponent ?
594          ts.factory.createIdentifier(OBSERVE_RECYCLE_COMPONENT_CREATION) :
595          ts.factory.createIdentifier(partialUpdateConfig.optimizeComponent ?
596            OBSERVECOMPONENTCREATION2 : OBSERVECOMPONENTCREATION)
597        ),
598        undefined, observeArgArr as ts.Expression[]))
599    ], true);
600}
601
602function componentPop(name: string): ts.ObjectLiteralExpression {
603  return ts.factory.createObjectLiteralExpression(
604    [ts.factory.createPropertyAssignment(
605      ts.factory.createIdentifier(NAME),
606      ts.factory.createStringLiteral(name)
607    )],
608    false
609  );
610}
611
612export function assignComponentParams(componentNode: ts.CallExpression,
613  isBuilder: boolean = false): ts.VariableStatement {
614  const isParamsLambda: boolean = true;
615  const [keyArray, valueArray]: [ts.Node[], ts.Node[]] = splitComponentParams(componentNode, isBuilder, isParamsLambda);
616  let integrateParams: boolean = false;
617  if (!keyArray.length && componentNode.arguments && componentNode.arguments.length > 0 && componentNode.arguments[0]) {
618    integrateParams = true;
619  }
620  return ts.factory.createVariableStatement(
621    undefined,
622    ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
623      ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION),
624      undefined,
625      undefined,
626      ts.factory.createArrowFunction(
627        undefined,
628        undefined,
629        [],
630        undefined,
631        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
632        ts.factory.createBlock(
633          [ts.factory.createReturnStatement(
634            integrateParams ? paramsLambdaCallBack(componentNode) : ts.factory.createObjectLiteralExpression(
635              reWriteComponentParams(keyArray, valueArray),
636              true
637            )
638          )],
639          true
640        )
641      )
642    )],
643    ts.NodeFlags.Let
644    ));
645}
646
647function paramsLambdaCallBack(componentNode: ts.CallExpression): ts.Expression {
648  if (partialUpdateConfig.partialUpdateMode && componentNode.arguments.length === 1 &&
649    isLocalStorageParameter(componentNode)) {
650    return ts.factory.createObjectLiteralExpression([], true);
651  } else {
652    return componentNode.arguments[0];
653  }
654}
655
656function reWriteComponentParams(keyArray: ts.Node[], valueArray: ts.Node[]): (ts.PropertyAssignment |
657  ts.ShorthandPropertyAssignment)[] {
658  const returnProperties: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = [];
659  keyArray.forEach((item: ts.Identifier, index: number) => {
660    if (!valueArray[index]) {
661      returnProperties.push(ts.factory.createShorthandPropertyAssignment(
662        item,
663        undefined
664      ));
665    } else {
666      returnProperties.push(ts.factory.createPropertyAssignment(
667        item,
668        valueArray[index] as ts.Identifier
669      ));
670    }
671  });
672  return returnProperties;
673}
674
675function splitComponentParams(componentNode: ts.CallExpression, isBuilder: boolean, isParamsLambda: boolean): [ts.Node[], ts.Node[]] {
676  const keyArray: ts.Node[] = [];
677  const valueArray: ts.Node[] = [];
678  if (componentNode.arguments && componentNode.arguments.length > 0 &&
679    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
680    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
681      const newPropertyItem: ts.PropertyAssignment =
682        isProperty(propertyItem) ? createReference(propertyItem, [], isBuilder, isParamsLambda) : propertyItem;
683      keyArray.push(newPropertyItem.name);
684      valueArray.push(newPropertyItem.initializer);
685    });
686  }
687  return [keyArray, valueArray];
688}
689
690function createIfCustomComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
691  componentParameter: ts.ObjectLiteralExpression, name: string, isGlobalBuilder: boolean, isBuilder: boolean,
692  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo, log: LogInfo[]): ts.IfStatement {
693  return ts.factory.createIfStatement(
694    ts.factory.createIdentifier(ISINITIALRENDER),
695    ts.factory.createBlock(
696      [componentParamDetachment(newNode, isRecycleComponent, name, log, componentNode),
697        isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) :
698          createNewComponent(COMPONENT_CALL, name, componentNode),
699        assignComponentParams(componentNode, isBuilder),
700        assignmentFunction(COMPONENT_CALL)
701      ], true),
702    ts.factory.createBlock(
703      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
704        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
705          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
706        ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID)
707        ), undefined,
708        [ts.factory.createIdentifier(ELMTID), componentParameter]))], true)
709  );
710}
711
712export function assignmentFunction(componeParamName: string): ts.ExpressionStatement {
713  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
714    ts.factory.createPropertyAccessExpression(
715      ts.factory.createIdentifier(componeParamName),
716      ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION)
717    ),
718    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
719    ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)
720  ));
721}
722
723function traverseChildComponentArgs(childParam: ts.Expression[], name: string, log: LogInfo[],
724  componentNode: ts.CallExpression): ts.Expression[] {
725  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) ||
726    processStructComponentV2.getOrCreateStructInfo(name);
727  if (!childStructInfo.isComponentV2) {
728    return childParam;
729  }
730  const objectLiteralIndex: number = 2;
731  if (childParam.length > objectLiteralIndex && ts.isObjectLiteralExpression(childParam[1]) &&
732    childParam[1].properties) {
733    const newProperties: ts.PropertyAssignment[] = [];
734    childParam[1].properties.forEach((item: ts.PropertyAssignment) => {
735      if (item.name && ts.isIdentifier(item.name)) {
736        const itemName: string = item.name.escapedText.toString();
737        updatePropertyAssignment(newProperties, itemName, item, childStructInfo, log);
738      }
739    });
740    if (newProperties.length) {
741      return getNewArgsForCustomComponent(childParam, newProperties);
742    }
743  }
744  return childParam;
745}
746
747function getNewArgsForCustomComponent(childParam: ts.Expression[],
748  newProperties: ts.PropertyAssignment[]): ts.Expression[] {
749  const newArr: ts.Expression[] = [];
750  const newObjectLiteralNode: ts.ObjectLiteralExpression =
751    ts.factory.updateObjectLiteralExpression(childParam[1], [...childParam[1].properties, ...newProperties]);
752  childParam.forEach((item: ts.Expression, index: number) => {
753    if (index === 1) {
754      newArr.push(newObjectLiteralNode);
755    } else {
756      newArr.push(item);
757    }
758  });
759  return newArr;
760}
761
762function updatePropertyAssignment(newProperties: ts.PropertyAssignment[],
763  itemName: string, item: ts.PropertyAssignment, childStructInfo: StructInfo, log: LogInfo[]): void {
764  if (isDoubleNonNullExpression(item.initializer)) {
765    if (isLeftHandExpression(item.initializer.expression.expression)) {
766      const result: Record<string, boolean> = { hasQuestionToken: false };
767      traverseExpressionNode(item.initializer.expression.expression, result);
768      if (result.hasQuestionToken) {
769        log.push({
770          type: LogType.ERROR,
771          message: `The optional character can not be used in the initial value of property '${itemName}'.`,
772          pos: item.getStart()
773        });
774        return;
775      }
776      if (childStructInfo.paramDecoratorMap.has(itemName) &&
777        childStructInfo.eventDecoratorSet.has('$' + itemName)) {
778        newProperties.push(createUpdateTwoWayNode(itemName, item.initializer.expression.expression));
779        return;
780      }
781      log.push({
782        type: LogType.ERROR,
783        message: 'When the two-way binding syntax is used, ' +
784          `the variable '${itemName}' must be decorated with @Param, ` +
785          `and the @Event variable '$` + `${itemName}' ` + `must be defined in the ${childStructInfo.structName}.`,
786        pos: item.getStart()
787      });
788      return;
789    }
790    log.push({
791      type: LogType.ERROR,
792      message: 'When the two-way binding syntax is used, ' +
793        `the initial value of property '${itemName}' must be a variable.`,
794      pos: item.getStart()
795    });
796    return;
797  }
798}
799
800function createUpdateTwoWayNode(itemName: string, leftHandExpression: ts.Expression): ts.PropertyAssignment {
801  return ts.factory.createPropertyAssignment(
802    ts.factory.createIdentifier('$' + itemName),
803    ts.factory.createArrowFunction(undefined, undefined,
804      [createAstNodeUtils.createParameterDeclaration('value')], undefined,
805      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
806      ts.factory.createBlock([
807        ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
808          leftHandExpression, ts.factory.createToken(ts.SyntaxKind.EqualsToken),
809          ts.factory.createIdentifier('value')
810        ))
811      ], false)
812    )
813  );
814}
815
816function isDoubleNonNullExpression(node: ts.Expression): boolean {
817  return node && ts.isNonNullExpression(node) && ts.isNonNullExpression(node.expression);
818}
819
820function isLeftHandExpression(node: ts.Expression): boolean {
821  return node && (ts.isIdentifier(node) || ts.isPropertyAccessExpression(node));
822}
823
824function traverseExpressionNode(node: ts.Node, result: Record<string, boolean>): void {
825  if (ts.isOptionalChain(node) && !ts.isNonNullExpression(node) && node.questionDotToken) {
826    result.hasQuestionToken = true;
827  }
828  if (!result.hasQuestionToken) {
829    node.getChildren().forEach((item: ts.Node) => traverseExpressionNode(item, result));
830  }
831}
832
833function componentParamDetachment(newNode: ts.NewExpression, isRecycleComponent: boolean,
834  name: string, log: LogInfo[], componentNode: ts.CallExpression): ts.VariableStatement {
835  const paramsArray: ts.Expression[] = newNode.arguments.length ? newNode.arguments : [];
836  const updateNewNode = ts.factory.updateNewExpression(newNode, newNode.expression,
837    newNode.typeArguments, traverseChildComponentArgs(paramsArray, name, log, componentNode));
838  return ts.factory.createVariableStatement(
839    undefined,
840    ts.factory.createVariableDeclarationList(
841      [ts.factory.createVariableDeclaration(
842        ts.factory.createIdentifier(COMPONENT_CALL),
843        undefined,
844        undefined,
845        isRecycleComponent ? ts.factory.createConditionalExpression(
846          ts.factory.createIdentifier(RECYCLE_NODE),
847          ts.factory.createToken(ts.SyntaxKind.QuestionToken),
848          ts.factory.createIdentifier(RECYCLE_NODE),
849          ts.factory.createToken(ts.SyntaxKind.ColonToken),
850          newNode) : updateNewNode
851      )],
852      ts.NodeFlags.Let
853    ));
854}
855
856function createNewComponent(componeParamName: string, name: string,
857  componentNode: ts.CallExpression): ts.Statement {
858  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) ||
859    processStructComponentV2.getOrCreateStructInfo(name);
860  return ts.factory.createExpressionStatement(
861    ts.factory.createCallExpression(
862      ts.factory.createPropertyAccessExpression(
863        ts.factory.createIdentifier(
864          childStructInfo.isComponentV2 ? constantDefine.STRUCT_PARENT : BASE_COMPONENT_NAME_PU),
865        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
866      ), undefined, [ts.factory.createIdentifier(componeParamName)]));
867}
868
869function createNewRecycleComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
870  name: string, componentAttrInfo: ComponentAttrInfo): ts.Statement {
871  let argNode: ts.Expression[] = [];
872  const componentParam: ts.PropertyAssignment[] = [];
873  if (componentNode.arguments && componentNode.arguments.length > 0 &&
874    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
875    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
876      const newPropertyItem: ts.PropertyAssignment = isProperty(propertyItem) ?
877        createReference(propertyItem, [], false, false, true) : propertyItem;
878      componentParam.push(newPropertyItem);
879    });
880    argNode = [ts.factory.createObjectLiteralExpression(componentParam, false)];
881  } else {
882    argNode = [ts.factory.createObjectLiteralExpression([], false)];
883  }
884  const recycleNode: ts.CallExpression = ts.factory.createCallExpression(
885    createRecyclePropertyNode(ABOUT_TO_REUSE), undefined, argNode);
886  return ts.factory.createExpressionStatement(
887    ts.factory.createCallExpression(
888      ts.factory.createPropertyAccessExpression(
889        ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
890        ts.factory.createIdentifier(COMPONENT_CREATE_RECYCLE)
891      ), undefined,
892      [
893        ts.factory.createIdentifier(COMPONENT_CALL),
894        ts.factory.createBinaryExpression(
895          ts.factory.createIdentifier(RECYCLE_NODE),
896          ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
897          ts.factory.createNull()
898        ),
899        componentAttrInfo.reuseId ? componentAttrInfo.reuseId as ts.Expression :
900          ts.factory.createStringLiteral(name),
901        ts.factory.createArrowFunction(undefined, undefined, [], undefined,
902          ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
903          ts.factory.createBlock([
904            ts.factory.createIfStatement(
905              ts.factory.createBinaryExpression(
906                ts.factory.createIdentifier(RECYCLE_NODE),
907                ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
908                ts.factory.createBinaryExpression(
909                  ts.factory.createTypeOfExpression(
910                    createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION)),
911                  ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
912                  ts.factory.createStringLiteral(FUNCTION)
913                )),
914              ts.factory.createBlock([
915                ts.factory.createExpressionStatement(ts.factory.createCallExpression(
916                  createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION),
917                  undefined,
918                  []
919                ))
920              ], true),
921              ts.factory.createBlock(
922                [
923                  ts.factory.createIfStatement(ts.factory.createBinaryExpression(
924                    createRecyclePropertyNode(ABOUT_TO_REUSE), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
925                    ts.factory.createBinaryExpression(
926                      ts.factory.createTypeOfExpression(createRecyclePropertyNode(ABOUT_TO_REUSE)),
927                      ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
928                      ts.factory.createStringLiteral(FUNCTION)
929                    )),
930                  ts.factory.createBlock([ts.factory.createExpressionStatement(recycleNode)], true)),
931                  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
932                    createRecyclePropertyNode(COMPONENT_RERENDER_FUNCTION), undefined, []
933                  ))
934                ],
935                true
936              )
937            )], true))
938      ]));
939}
940
941function createRecyclePropertyNode(recycleFunctionName: string): ts.PropertyAccessExpression {
942  return ts.factory.createPropertyAccessExpression(
943    ts.factory.createIdentifier(RECYCLE_NODE), ts.factory.createIdentifier(recycleFunctionName));
944}
945
946function validateCustomComponentPrams(node: ts.CallExpression, name: string,
947  props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void {
948  const curChildProps: Set<string> = new Set([]);
949  const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments;
950  const linkSet: Set<string> = getCollectionSet(name, linkCollection);
951  if (nodeArguments && nodeArguments.length >= 1 &&
952    ts.isObjectLiteralExpression(nodeArguments[0])) {
953    const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression;
954    nodeArgument.properties.forEach(item => {
955      if (item.name && ts.isIdentifier(item.name)) {
956        curChildProps.add(item.name.escapedText.toString());
957      }
958      validateStateManagement(item, name, log, isBuilder);
959      if (isNonThisProperty(item, linkSet)) {
960        if (isToChange(item as ts.PropertyAssignment, name)) {
961          item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment,
962            item.name, changeNodeFromCallToArrow(item.initializer));
963        }
964        props.push(item);
965      }
966    });
967  }
968  const parentStructInfo: StructInfo = componentCollection.currentClassName ?
969    processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) :
970    new StructInfo();
971  validateInitDecorator(node, name, curChildProps, log);
972  validateMandatoryToInitViaParam(node, name, curChildProps, log, parentStructInfo);
973  validateInitParam(name, curChildProps, node, log, parentStructInfo);
974}
975
976function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression {
977  let temp: any = node.expression;
978  let child: any = null;
979  let componentNode: any = null;
980  while (temp) {
981    if (ts.isIdentifier(temp)) {
982      child = temp;
983      break;
984    }
985    temp = temp.expression;
986  }
987  if (child) {
988    let parent = child.parent;
989    while (parent) {
990      if (ts.isExpressionStatement(parent)) {
991        break;
992      }
993      if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) {
994        componentNode = parent;
995        break;
996      }
997      parent = parent.parent;
998    }
999  }
1000  return componentNode;
1001}
1002
1003function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> {
1004  if (!collection) {
1005    return new Set([]);
1006  }
1007  return collection.get(name) || new Set([]);
1008}
1009
1010function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
1011  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
1012    propertySet.has(node.name.escapedText.toString())) {
1013    return true;
1014  }
1015  return false;
1016}
1017
1018function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
1019  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
1020    (node.initializer.escapedText && node.initializer.escapedText.includes('$') ||
1021    ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
1022    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
1023    ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) {
1024    return false;
1025  }
1026  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
1027    !propertySet.has(node.name.escapedText.toString())) {
1028    return true;
1029  }
1030  return false;
1031}
1032
1033function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string,
1034  log: LogInfo[], isBuilder: boolean): void {
1035  validateForbiddenToInitViaParam(node, customComponentName, log);
1036  if (componentCollection.currentClassName) {
1037    const parentStructInfo: StructInfo =
1038      processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName);
1039    if (parentStructInfo.isComponentV2) {
1040      return;
1041    }
1042  }
1043  checkFromParentToChild(node, customComponentName, log, isBuilder);
1044}
1045
1046function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string,
1047  log: LogInfo[], isBuilder: boolean): void {
1048  let propertyName: string;
1049  if (ts.isIdentifier(node.name)) {
1050    propertyName = node.name.escapedText.toString();
1051  }
1052  const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName);
1053  let parentPropertyName: string;
1054  if (curPropertyKind) {
1055    if (isInitFromParent(node)) {
1056      parentPropertyName =
1057        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log);
1058      let parentPropertyKind: string = curPropMap.get(parentPropertyName);
1059      if (!parentPropertyKind) {
1060        parentPropertyKind = COMPONENT_NON_DECORATOR;
1061      }
1062      if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
1063        validateIllegalInitFromParent(
1064          node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log);
1065      }
1066    } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) &&
1067      curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) {
1068      if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) {
1069        validateIllegalInitFromParent(node, propertyName, curPropertyKind,
1070          node.initializer.getText(), COMPONENT_NON_DECORATOR, log);
1071      }
1072    } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer &&
1073      (ts.isPropertyAccessExpression(node.initializer) ||
1074        ts.isElementAccessExpression(node.initializer) || ts.isIdentifier(node.initializer))) {
1075      return;
1076    } else {
1077      parentPropertyName =
1078        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName;
1079      const parentPropertyKind = COMPONENT_NON_DECORATOR;
1080      if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
1081        if (isBuilder && judgeStructAssignedDollar(node)) {
1082          log.push({
1083            type: LogType.WARN,
1084            message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` +
1085              `${curPropertyKind} property '${propertyName}' by yourself.`,
1086            // @ts-ignore
1087            pos: node.initializer ? node.initializer.getStart() : node.getStart()
1088          });
1089        } else {
1090          validateIllegalInitFromParent(
1091            node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN);
1092        }
1093      }
1094    }
1095  }
1096}
1097
1098function judgeStructAssignedDollar(node: ts.ObjectLiteralElementLike): boolean {
1099  return partialUpdateConfig.partialUpdateMode && node.initializer &&
1100    ts.isPropertyAccessExpression(node.initializer) &&
1101    node.initializer.expression && ts.isIdentifier(node.initializer.expression) &&
1102    node.initializer.expression.escapedText.toString() === $$;
1103}
1104
1105function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean {
1106  if (ts.isPropertyAssignment(node) && node.initializer) {
1107    if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
1108    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
1109    ts.isIdentifier(node.initializer.name)) {
1110      return true;
1111    } else if (ts.isIdentifier(node.initializer) &&
1112      matchStartWithDollar(node.initializer.getText())) {
1113      return true;
1114    }
1115  }
1116  return false;
1117}
1118
1119function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean {
1120  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) &&
1121    !matchStartWithDollar(node.initializer.getText())) {
1122    return true;
1123  }
1124  return false;
1125}
1126
1127function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string,
1128  log: LogInfo[]): string {
1129  const initExpression: ts.Expression = node.initializer;
1130  if (!initExpression) {
1131    return undefined;
1132  }
1133  let parentPropertyName: string = initExpression.getText();
1134  if (curPropertyKind === COMPONENT_LINK_DECORATOR) {
1135    // @ts-ignore
1136    const initName: ts.Identifier = initExpression.name || initExpression;
1137    if (hasDollar(initExpression)) {
1138      parentPropertyName = initName.getText().replace(/^\$/, '');
1139    } else {
1140      parentPropertyName = initName.getText();
1141    }
1142  } else {
1143    if (hasDollar(initExpression)) {
1144      validateNonLinkWithDollar(node, log);
1145    } else {
1146      // @ts-ignore
1147      if (node.initializer && node.initializer.name) {
1148        parentPropertyName = node.initializer.name.getText();
1149      }
1150    }
1151  }
1152
1153  return parentPropertyName;
1154}
1155
1156function isCorrectInitFormParent(parent: string, child: string): boolean {
1157  switch (child) {
1158    case COMPONENT_STATE_DECORATOR:
1159    case COMPONENT_PROP_DECORATOR:
1160    case COMPONENT_PROVIDE_DECORATOR:
1161      return true;
1162    case COMPONENT_NON_DECORATOR:
1163      if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR,
1164        COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) {
1165        return true;
1166      }
1167      break;
1168    case COMPONENT_LINK_DECORATOR:
1169    case COMPONENT_OBJECT_LINK_DECORATOR:
1170      return ![COMPONENT_NON_DECORATOR].includes(parent);
1171  }
1172  return false;
1173}
1174
1175function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string {
1176  for (const item of decoractorMap.entries()) {
1177    if (getCollectionSet(customComponentName, item[1]).has(propertyName)) {
1178      return item[0];
1179    }
1180  }
1181  return undefined;
1182}
1183
1184function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement {
1185  return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList(
1186    [ts.factory.createVariableDeclaration(ts.factory.createIdentifier(
1187      `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode(
1188      ts.factory.createIdentifier(name)),
1189    ts.factory.createConditionalExpression(
1190      ts.factory.createParenthesizedExpression(
1191        ts.factory.createBinaryExpression(
1192          createConditionParent(isBuilder),
1193          ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1194          ts.factory.createPropertyAccessExpression(
1195            createConditionParent(isBuilder),
1196            ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID)
1197          ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1198      ts.factory.createAsExpression(ts.factory.createCallExpression(
1199        ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder),
1200          ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined,
1201        [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID),
1202          undefined, []) : ts.factory.createStringLiteral(id)]),
1203      ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))),
1204      ts.factory.createToken(ts.SyntaxKind.ColonToken),
1205      ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)))], ts.NodeFlags.Let));
1206}
1207
1208export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression {
1209  return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis();
1210}
1211
1212function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement,
1213  newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement {
1214  const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`;
1215  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
1216    ts.factory.createIdentifier(viewName),
1217    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
1218    ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)),
1219  ts.factory.createBlock([node], true),
1220  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1221    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(
1222      viewName), ts.factory.createIdentifier(
1223      `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])),
1224  isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined,
1225  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1226    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`),
1227      ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined,
1228    [ts.factory.createIdentifier(viewName)]))], true));
1229}
1230
1231function createStaticIf(name: string): ts.IfStatement {
1232  return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(
1233    ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(
1234      ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
1235        ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])),
1236  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1237    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
1238      ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)),
1239    undefined, []))], true), undefined);
1240}
1241
1242function hasDollar(initExpression: ts.Expression): boolean {
1243  if (ts.isPropertyAccessExpression(initExpression) &&
1244    matchStartWithDollar(initExpression.name.getText())) {
1245    return true;
1246  } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) {
1247    return true;
1248  } else {
1249    return false;
1250  }
1251}
1252
1253function matchStartWithDollar(name: string): boolean {
1254  return /^\$/.test(name);
1255}
1256
1257function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike,
1258  customComponentName: string, log: LogInfo[]): void {
1259  const forbiddenToInitViaParamSet: Set<string> = new Set([
1260    ...getCollectionSet(customComponentName, storageLinkCollection),
1261    ...getCollectionSet(customComponentName, storagePropCollection),
1262    ...getCollectionSet(customComponentName, consumeCollection)
1263  ]);
1264  const localStorageSet: Set<string> = new Set();
1265  getLocalStorageCollection(customComponentName, localStorageSet);
1266  if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) {
1267    log.push({
1268      type: LogType.ERROR,
1269      message: `Property '${node.name.getText()}' in the custom component '${customComponentName}'` +
1270        ` cannot be initialized here (forbidden to specify).`,
1271      pos: node.name.getStart()
1272    });
1273  }
1274}
1275
1276function validateMandatoryToInitViaParam(node: ts.CallExpression, customComponentName: string,
1277  curChildProps: Set<string>, log: LogInfo[], parentStructInfo: StructInfo): void {
1278  let mandatoryToInitViaParamSet: Set<string>;
1279  if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' && node.expression) {
1280    const overAll: string[] = [
1281      ...getCollectionSet(node.expression.getText(), storedFileInfo.overallObjectLinkCollection)];
1282    if (!parentStructInfo.isComponentV2) {
1283      overAll.unshift(...getCollectionSet(node.expression.getText(), storedFileInfo.overallLinkCollection));
1284    }
1285    mandatoryToInitViaParamSet = new Set(overAll);
1286    customComponentName = node.expression.getText();
1287  } else {
1288    const arr: string[] = [...getCollectionSet(customComponentName, objectLinkCollection)];
1289    if (!parentStructInfo.isComponentV2) {
1290      arr.unshift(...getCollectionSet(customComponentName, linkCollection));
1291    }
1292    mandatoryToInitViaParamSet = new Set(arr);
1293  }
1294  mandatoryToInitViaParamSet.forEach(item => {
1295    if (item && !curChildProps.has(item)) {
1296      log.push({
1297        type: LogType.ERROR,
1298        message: `Property '${item}' in the custom component '${customComponentName}'` +
1299          ` is missing (mandatory to specify).`,
1300        pos: node.getStart()
1301      });
1302    }
1303  });
1304}
1305
1306function validateInitDecorator(node: ts.CallExpression, customComponentName: string,
1307  curChildProps: Set<string>, log: LogInfo[]): void {
1308  const mandatoryToInitViaParamSet: Set<string> = new Set([
1309    ...getCollectionSet(customComponentName, builderParamObjectCollection),
1310    ...getCollectionSet(customComponentName, propCollection),
1311    ...getCollectionSet(customComponentName, regularCollection),
1312    ...getCollectionSet(customComponentName, stateCollection),
1313    ...getCollectionSet(customComponentName, provideCollection)
1314  ]);
1315  const decoratorVariable: Set<string> = new Set([
1316    ...(builderParamInitialization.get(customComponentName) || []),
1317    ...(propInitialization.get(customComponentName) || []),
1318    ...(regularInitialization.get(customComponentName) || []),
1319    ...(stateInitialization.get(customComponentName) || []),
1320    ...(provideInitialization.get(customComponentName) || [])
1321  ]);
1322  mandatoryToInitViaParamSet.forEach((item: string) => {
1323    if (item && !curChildProps.has(item) && decoratorVariable && decoratorVariable.has(item)) {
1324      log.push({
1325        type: LogType.ERROR,
1326        message: `Property '${item}' must be initialized through the component constructor.`,
1327        pos: node.getStart()
1328      });
1329    }
1330  });
1331  const privateParamSet: Set<string> = privateCollection.get(customComponentName) || new Set([]);
1332  curChildProps.forEach((item: string) => {
1333    if (privateParamSet.has(item)) {
1334      log.push({
1335        type: LogType.WARN,
1336        message: `Property '${item}' is private and can not be initialized through the component constructor.`,
1337        pos: node.getStart()
1338      });
1339    }
1340  });
1341}
1342
1343function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string,
1344  curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string,
1345  log: LogInfo[], inputType: LogType = undefined): void {
1346  let type: LogType = LogType.ERROR;
1347  if (inputType) {
1348    type = inputType;
1349  } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes(
1350    parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) {
1351    type = LogType.WARN;
1352  }
1353  log.push({
1354    type: type,
1355    message: `The ${parentPropertyKind} property '${parentPropertyName}' cannot be assigned to ` +
1356      `the ${curPropertyKind} property '${propertyName}'.`,
1357    // @ts-ignore
1358    pos: node.initializer ? node.initializer.getStart() : node.getStart()
1359  });
1360}
1361
1362function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void {
1363  log.push({
1364    type: LogType.ERROR,
1365    message: `Property '${node.name.getText()}' cannot initialize` +
1366      ` using '$' to create a reference to a variable.`,
1367    pos: node.initializer.getStart()
1368  });
1369}
1370