• 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  ISINITIALRENDER,
47  UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID,
48  COMPONENT_CUSTOM_DECORATOR,
49  $$
50} from './pre_define';
51import {
52  propertyCollection,
53  stateCollection,
54  linkCollection,
55  propCollection,
56  regularCollection,
57  storagePropCollection,
58  storageLinkCollection,
59  provideCollection,
60  consumeCollection,
61  objectLinkCollection,
62  isStaticViewCollection,
63  builderParamObjectCollection,
64  getLocalStorageCollection,
65  builderParamInitialization
66} from './validate_ui_syntax';
67import {
68  propAndLinkDecorators,
69  curPropMap,
70  createViewCreate,
71  createCustomComponentNewExpression
72} from './process_component_member';
73import {
74  LogType,
75  LogInfo,
76  componentInfo
77} from './utils';
78import {
79  bindComponentAttr,
80  parentConditionalExpression,
81  createComponentCreationStatement,
82  createFunction
83} from './process_component_build';
84import { partialUpdateConfig } from '../main';
85
86let decoractorMap: Map<string, Map<string, Set<string>>>;
87
88export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
89  log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false): void {
90  decoractorMap = new Map(
91    [[COMPONENT_STATE_DECORATOR, stateCollection],
92      [COMPONENT_LINK_DECORATOR, linkCollection],
93      [COMPONENT_PROP_DECORATOR, propCollection],
94      [COMPONENT_NON_DECORATOR, regularCollection],
95      [COMPONENT_PROVIDE_DECORATOR, provideCollection],
96      [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]);
97  const componentNode: ts.CallExpression = getCustomComponentNode(node);
98  if (componentNode) {
99    const hasChainCall: boolean = componentNode.parent &&
100      ts.isPropertyAccessExpression(componentNode.parent);
101    let ischangeNode: boolean = false;
102    let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression(
103      componentNode, name, isBuilder, isGlobalBuilder);
104    let argumentsArray: ts.PropertyAssignment[];
105    if (isHasChild(componentNode)) {
106      // @ts-ignore
107      argumentsArray = componentNode.arguments[0].properties.slice();
108      argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => {
109        if (isToChange(item, name)) {
110          ischangeNode = true;
111          const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment(
112            item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression));
113          argumentsArray.splice(index, 1, propertyAssignmentNode);
114        }
115      });
116      if (ischangeNode) {
117        const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node,
118          ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments,
119            [ts.factory.createObjectLiteralExpression(argumentsArray, true)]));
120        customComponentNewExpression = createCustomComponentNewExpression(
121          newNode.expression as ts.CallExpression, name, isBuilder);
122      }
123    }
124    if (hasChainCall) {
125      if (partialUpdateConfig.partialUpdateMode) {
126        const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement(
127          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
128            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))];
129        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode, log);
130        newStatements.push(createComponentCreationStatement(componentAttributes(), commomComponentNode, isGlobalBuilder));
131      } else {
132        newStatements.push(ts.factory.createExpressionStatement(
133          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
134            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)));
135        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log);
136      }
137    }
138    addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode,
139      isBuilder, isGlobalBuilder);
140    if (hasChainCall) {
141      newStatements.push(ts.factory.createExpressionStatement(
142        createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
143          ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
144    }
145  }
146}
147
148function componentAttributes(): ts.Statement {
149  return ts.factory.createExpressionStatement(
150    ts.factory.createCallExpression(
151      ts.factory.createPropertyAccessExpression(
152        ts.factory.createIdentifier(COMPONENT_COMMON),
153        ts.factory.createIdentifier(COMPONENT_POP_FUNCTION)
154      ), undefined, []
155    ));
156}
157
158function isHasChild(node: ts.CallExpression): boolean {
159  return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) &&
160    node.arguments[0].properties && node.arguments[0].properties.length > 0;
161}
162
163function isToChange(item: ts.PropertyAssignment, name: string): boolean {
164  const builderParamName: Set<string> = builderParamObjectCollection.get(name);
165  if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName &&
166    builderParamName.has(item.name.getText()) &&
167    !/\.(bind|call|apply)/.test(item.initializer.getText())) {
168    return true;
169  }
170  return false;
171}
172
173function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ArrowFunction {
174  return ts.factory.createArrowFunction(undefined, undefined, [], undefined,
175    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
176    ts.factory.createBlock([ts.factory.createExpressionStatement(node)], true));
177}
178
179function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
180  newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression,
181  isBuilder: boolean = false, isGlobalBuilder: boolean = false): void {
182  if (ts.isNewExpression(newNode)) {
183    const propertyArray: ts.ObjectLiteralElementLike[] = [];
184    validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder);
185    addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode, isBuilder, isGlobalBuilder);
186  }
187}
188
189function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[],
190  newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[],
191  componentNode: ts.CallExpression, isBuilder: boolean = false, isGlobalBuilder: boolean = false): void {
192  if (!partialUpdateConfig.partialUpdateMode) {
193    const id: string = componentInfo.id.toString();
194    newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id,
195      ts.factory.updateExpressionStatement(node, createViewCreate(newNode)),
196      ts.factory.createObjectLiteralExpression(props, true), name));
197  } else {
198    newStatements.push(createCustomComponent(node, newNode, name, componentNode, isGlobalBuilder));
199  }
200}
201
202function createChildElmtId(node: ts.CallExpression, name: string): ts.PropertyAssignment[] {
203  const propsAndObjectLinks: string[] = [];
204  const childParam: ts.PropertyAssignment[] = [];
205  if (propCollection.get(name)) {
206    propsAndObjectLinks.push(...propCollection.get(name));
207  }
208  if (objectLinkCollection.get(name)) {
209    propsAndObjectLinks.push(...objectLinkCollection.get(name));
210  }
211  if (node.arguments[0].properties) {
212    node.arguments[0].properties.forEach(item => {
213      if (ts.isIdentifier(item.name) && propsAndObjectLinks.includes(item.name.escapedText.toString())) {
214        childParam.push(item);
215      }
216    });
217  }
218  return childParam;
219}
220
221function createCustomComponent(node: ts.ExpressionStatement, newNode: ts.NewExpression, name: string,
222  componentNode: ts.CallExpression, isGlobalBuilder: boolean = false): ts.Block {
223  let componentParameter: ts.ObjectLiteralExpression;
224  if (componentNode.arguments && componentNode.arguments.length) {
225    componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name), true);
226  } else {
227    componentParameter = ts.factory.createObjectLiteralExpression([], false);
228  }
229  return ts.factory.createBlock(
230    [
231      ts.factory.createExpressionStatement(ts.factory.createCallExpression(
232        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
233          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
234        ts.factory.createIdentifier(OBSERVECOMPONENTCREATION)
235        ), undefined,
236        [ts.factory.createArrowFunction(undefined, undefined,
237          [
238            ts.factory.createParameterDeclaration(undefined, undefined, undefined,
239              ts.factory.createIdentifier(ELMTID)
240            ),
241            ts.factory.createParameterDeclaration(undefined, undefined, undefined,
242              ts.factory.createIdentifier(ISINITIALRENDER)
243            )
244          ], undefined,
245          ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
246          ts.factory.createBlock(
247            [
248              ts.factory.createExpressionStatement(
249                ts.factory.createCallExpression(
250                  ts.factory.createPropertyAccessExpression(
251                    ts.factory.createIdentifier(VIEWSTACKPROCESSOR),
252                    ts.factory.createIdentifier(STARTGETACCESSRECORDINGFOR)
253                  ), undefined,
254                  [ts.factory.createIdentifier(ELMTID)]
255                )),
256              createIfCustomComponent(newNode, componentParameter, isGlobalBuilder),
257              ts.factory.createExpressionStatement(ts.factory.createCallExpression(
258                ts.factory.createPropertyAccessExpression(
259                  ts.factory.createIdentifier(VIEWSTACKPROCESSOR),
260                  ts.factory.createIdentifier(STOPGETACCESSRECORDING)
261                ),
262                undefined,
263                []
264              ))
265            ], true))]))
266    ], true);
267}
268
269function createIfCustomComponent(newNode: ts.NewExpression, componentParameter: ts.ObjectLiteralExpression,
270  isGlobalBuilder: boolean = false): ts.IfStatement {
271  return ts.factory.createIfStatement(
272    ts.factory.createIdentifier(ISINITIALRENDER),
273    ts.factory.createBlock(
274      [
275        ts.factory.createExpressionStatement(
276          ts.factory.createCallExpression(
277            ts.factory.createPropertyAccessExpression(
278              ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
279              ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
280            ), undefined, [newNode]))
281      ], true),
282    ts.factory.createBlock(
283      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
284        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
285          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
286        ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID)
287        ), undefined,
288        [ts.factory.createIdentifier(ELMTID), componentParameter]))], true)
289  );
290}
291
292function validateCustomComponentPrams(node: ts.CallExpression, name: string,
293  props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void {
294  const curChildProps: Set<string> = new Set([]);
295  const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments;
296  const propertySet: Set<string> = getCollectionSet(name, propertyCollection);
297  const linkSet: Set<string> = getCollectionSet(name, linkCollection);
298  if (nodeArguments && nodeArguments.length === 1 &&
299    ts.isObjectLiteralExpression(nodeArguments[0])) {
300    const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression;
301    nodeArgument.properties.forEach(item => {
302      if (item.name && ts.isIdentifier(item.name)) {
303        curChildProps.add(item.name.escapedText.toString());
304      }
305      validateStateManagement(item, name, log, isBuilder);
306      if (isNonThisProperty(item, linkSet)) {
307        if (isToChange(item as ts.PropertyAssignment, name)) {
308          item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment,
309            item.name, changeNodeFromCallToArrow(item.initializer));
310        }
311        props.push(item);
312      }
313    });
314  }
315  validateInitDecorator(node, name, curChildProps, log);
316}
317
318function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression {
319  let temp: any = node.expression;
320  let child: any = null;
321  let componentNode: any = null;
322  while (temp) {
323    if (ts.isIdentifier(temp)) {
324      child = temp;
325      break;
326    }
327    temp = temp.expression;
328  }
329  if (child) {
330    let parent = child.parent;
331    while (parent) {
332      if (ts.isExpressionStatement(parent)) {
333        break;
334      }
335      if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) {
336        componentNode = parent;
337        break;
338      }
339      parent = parent.parent;
340    }
341  }
342  return componentNode;
343}
344
345function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> {
346  if (!collection) {
347    return new Set([]);
348  }
349  return collection.get(name) || new Set([]);
350}
351
352function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
353  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
354    propertySet.has(node.name.escapedText.toString())) {
355    return true;
356  }
357  return false;
358}
359
360function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
361  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
362    (node.initializer.escapedText && node.initializer.escapedText.includes('$') ||
363    ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
364    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
365    ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) {
366    return false;
367  }
368  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
369    !propertySet.has(node.name.escapedText.toString())) {
370    return true;
371  }
372  return false;
373}
374
375function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string,
376  log: LogInfo[], isBuilder: boolean): void {
377  validateForbiddenToInitViaParam(node, customComponentName, log);
378  checkFromParentToChild(node, customComponentName, log, isBuilder);
379}
380
381function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string,
382  log: LogInfo[], isBuilder: boolean): void {
383  let propertyName: string;
384  if (ts.isIdentifier(node.name)) {
385    propertyName = node.name.escapedText.toString();
386  }
387  const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName);
388  let parentPropertyName: string;
389  if (curPropertyKind) {
390    if (isInitFromParent(node)) {
391      parentPropertyName =
392        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log);
393      let parentPropertyKind: string = curPropMap.get(parentPropertyName);
394      if (!parentPropertyKind) {
395        parentPropertyKind = COMPONENT_NON_DECORATOR;
396      }
397      if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
398        validateIllegalInitFromParent(
399          node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log);
400      }
401    } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) &&
402      curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) {
403      if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) {
404        validateIllegalInitFromParent(node, propertyName, curPropertyKind,
405          node.initializer.getText(), COMPONENT_NON_DECORATOR, log);
406      }
407    } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer &&
408      (ts.isPropertyAccessExpression(node.initializer) ||
409        ts.isElementAccessExpression(node.initializer))) {
410      return;
411    } else {
412      parentPropertyName =
413        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName;
414      const parentPropertyKind = COMPONENT_NON_DECORATOR;
415      if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
416        if (isBuilder && judgeStructAssigned$$(node)) {
417          log.push({
418            type: LogType.WARN,
419            message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` +
420              `${curPropertyKind} property '${propertyName}' by yourself.`,
421            // @ts-ignore
422            pos: node.initializer ? node.initializer.getStart() : node.getStart()
423          });
424        } else {
425          validateIllegalInitFromParent(
426            node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN);
427        }
428      }
429    }
430  }
431}
432
433function judgeStructAssigned$$(node: ts.ObjectLiteralElementLike): boolean {
434  return partialUpdateConfig.partialUpdateMode && node.initializer &&
435    ts.isPropertyAccessExpression(node.initializer) &&
436    node.initializer.expression && ts.isIdentifier(node.initializer.expression) &&
437    node.initializer.expression.escapedText.toString() === $$;
438}
439
440function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean {
441  if (ts.isPropertyAssignment(node) && node.initializer) {
442    if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
443    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
444    ts.isIdentifier(node.initializer.name)) {
445      return true;
446    } else if (ts.isIdentifier(node.initializer) &&
447      matchStartWithDollar(node.initializer.getText())) {
448      return true;
449    }
450  }
451}
452
453function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean {
454  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) &&
455    !matchStartWithDollar(node.initializer.getText())) {
456    return true;
457  }
458}
459
460function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string,
461  log: LogInfo[]): string {
462  const initExpression: ts.Expression = node.initializer;
463  if (!initExpression) {
464    return undefined;
465  }
466  let parentPropertyName: string = initExpression.getText();
467  if (curPropertyKind === COMPONENT_LINK_DECORATOR) {
468    // @ts-ignore
469    const initName: ts.Identifier = initExpression.name || initExpression;
470    if (hasDollar(initExpression)) {
471      parentPropertyName = initName.getText().replace(/^\$/, '');
472    } else {
473      parentPropertyName = initName.getText();
474    }
475  } else {
476    if (hasDollar(initExpression)) {
477      validateNonLinkWithDollar(node, log);
478    } else {
479      // @ts-ignore
480      if (node.initializer && node.initializer.name) {
481        parentPropertyName = node.initializer.name.getText();
482      }
483    }
484  }
485
486  return parentPropertyName;
487}
488
489function isCorrectInitFormParent(parent: string, child: string): boolean {
490  switch (child) {
491    case COMPONENT_STATE_DECORATOR:
492    case COMPONENT_PROP_DECORATOR:
493    case COMPONENT_PROVIDE_DECORATOR:
494      return true;
495    case COMPONENT_NON_DECORATOR:
496      if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR,
497        COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR].includes(parent)) {
498        return true;
499      }
500      break;
501    case COMPONENT_LINK_DECORATOR:
502      return ![COMPONENT_NON_DECORATOR].includes(parent);
503  }
504  return false;
505}
506
507function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string {
508  for (const item of decoractorMap.entries()) {
509    if (getCollectionSet(customComponentName, item[1]).has(propertyName)) {
510      return item[0];
511    }
512  }
513}
514
515function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement {
516  return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList(
517    [ts.factory.createVariableDeclaration(ts.factory.createIdentifier(
518      `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode(
519      ts.factory.createIdentifier(name)),
520    ts.factory.createConditionalExpression(
521      ts.factory.createParenthesizedExpression(
522        ts.factory.createBinaryExpression(
523          createConditionParent(isBuilder),
524          ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
525          ts.factory.createPropertyAccessExpression(
526            createConditionParent(isBuilder),
527            ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID)
528          ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken),
529      ts.factory.createAsExpression(ts.factory.createCallExpression(
530        ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder),
531          ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined,
532        [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID),
533          undefined, []) : ts.factory.createStringLiteral(id)]),
534      ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))),
535      ts.factory.createToken(ts.SyntaxKind.ColonToken),
536      ts.factory.createIdentifier('undefined')))], ts.NodeFlags.Let));
537}
538
539export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression {
540  return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis();
541}
542
543function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement,
544  newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement {
545  const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`;
546  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
547    ts.factory.createIdentifier(viewName),
548    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
549    ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)),
550  ts.factory.createBlock([node], true),
551  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
552    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(
553      viewName), ts.factory.createIdentifier(
554      `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])),
555  isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined,
556  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
557    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`),
558      ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined,
559    [ts.factory.createIdentifier(viewName)]))], true));
560}
561
562function createStaticIf(name: string): ts.IfStatement {
563  return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(
564    ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(
565      ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
566        ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])),
567  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
568    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
569      ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)),
570    undefined, []))], true), undefined);
571}
572
573function hasDollar(initExpression: ts.Expression): boolean {
574  if (ts.isPropertyAccessExpression(initExpression) &&
575    matchStartWithDollar(initExpression.name.getText())) {
576    return true;
577  } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) {
578    return true;
579  } else {
580    return false;
581  }
582}
583
584function matchStartWithDollar(name: string): boolean {
585  return /^\$/.test(name);
586}
587
588function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike,
589  customComponentName: string, log: LogInfo[]): void {
590  const forbiddenToInitViaParamSet: Set<string> = new Set([
591    ...getCollectionSet(customComponentName, storageLinkCollection),
592    ...getCollectionSet(customComponentName, storagePropCollection),
593    ...getCollectionSet(customComponentName, consumeCollection)
594  ]);
595  const localStorageSet: Set<string> = new Set();
596  getLocalStorageCollection(customComponentName, localStorageSet);
597  if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) {
598    log.push({
599      type: localStorageSet.has(node.name.getText()) ? LogType.WARN : LogType.ERROR,
600      message: `Property '${node.name.getText()}' in the custom component '${customComponentName}'` +
601        ` cannot initialize here (forbidden to specify).`,
602      pos: node.name.getStart()
603    });
604  }
605}
606
607function validateNonExistentProperty(node: ts.ObjectLiteralElementLike,
608  customComponentName: string, log: LogInfo[]): void {
609  log.push({
610    type: LogType.ERROR,
611    message: `Property '${node.name.escapedText.toString()}' does not exist on type '${customComponentName}'.`,
612    pos: node.name.getStart()
613  });
614}
615
616function validateMandatoryToAssignmentViaParam(node: ts.CallExpression, customComponentName: string,
617  curChildProps: Set<string>, log: LogInfo[]): void {
618  if (builderParamObjectCollection.get(customComponentName) &&
619    builderParamObjectCollection.get(customComponentName).size) {
620    builderParamObjectCollection.get(customComponentName).forEach((item) => {
621      if (!curChildProps.has(item)) {
622        log.push({
623          type: LogType.ERROR,
624          message: `The property decorated with @BuilderParam '${item}' must be assigned a value .`,
625          pos: node.getStart()
626        });
627      }
628    });
629  }
630}
631
632function validateMandatoryToInitViaParam(node: ts.ExpressionStatement, customComponentName: string,
633  curChildProps: Set<string>, log: LogInfo[]): void {
634  const mandatoryToInitViaParamSet: Set<string> = new Set([
635    ...getCollectionSet(customComponentName, propCollection),
636    ...getCollectionSet(customComponentName, linkCollection),
637    ...getCollectionSet(customComponentName, objectLinkCollection)]);
638  mandatoryToInitViaParamSet.forEach(item => {
639    if (!curChildProps.has(item)) {
640      log.push({
641        type: LogType.ERROR,
642        message: `Property '${item}' in the custom component '${customComponentName}'` +
643          ` is missing (mandatory to specify).`,
644        pos: node.getStart()
645      });
646    }
647  });
648}
649
650function validateInitDecorator(node: ts.CallExpression, customComponentName: string,
651  curChildProps: Set<string>, log: LogInfo[]): void {
652  const mandatoryToInitViaParamSet: Set<string> = new Set([
653    ...getCollectionSet(customComponentName, builderParamObjectCollection)]);
654  const decoratorVariable: Set<string> = builderParamInitialization.get(customComponentName);
655  mandatoryToInitViaParamSet.forEach((item: string) => {
656    if (item && !curChildProps.has(item) && decoratorVariable && !decoratorVariable.has(item)) {
657      log.push({
658        type: LogType.ERROR,
659        message: `Property '${item}' in the custom component '${customComponentName}'` +
660        ` is missing assignment or initialization.`,
661        pos: node.getStart()
662      });
663    }
664  });
665}
666
667function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string,
668  curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string,
669  log: LogInfo[], inputType: LogType = undefined): void {
670  let type: LogType = LogType.ERROR;
671  if (inputType) {
672    type = inputType;
673  } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes(
674    parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) {
675    type = LogType.WARN;
676  }
677  log.push({
678    type: type,
679    message: `The ${parentPropertyKind} property '${parentPropertyName}' cannot be assigned to ` +
680      `the ${curPropertyKind} property '${propertyName}'.`,
681    // @ts-ignore
682    pos: node.initializer ? node.initializer.getStart() : node.getStart()
683  });
684}
685
686function validateLinkWithoutDollar(node: ts.PropertyAssignment, log: LogInfo[]): void {
687  log.push({
688    type: LogType.ERROR,
689    message: `The @Link property '${node.name.getText()}' should initialize` +
690      ` using '$' to create a reference to a @State or @Link variable.`,
691    pos: node.initializer.getStart()
692  });
693}
694
695function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void {
696  log.push({
697    type: LogType.ERROR,
698    message: `Property '${node.name.getText()}' cannot initialize` +
699      ` using '$' to create a reference to a variable.`,
700    pos: node.initializer.getStart()
701  });
702}
703