• 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';
17const path = require('path');
18
19import {
20  INNER_COMPONENT_MEMBER_DECORATORS,
21  COMPONENT_NON_DECORATOR,
22  COMPONENT_STATE_DECORATOR,
23  COMPONENT_PROP_DECORATOR,
24  COMPONENT_LINK_DECORATOR,
25  COMPONENT_STORAGE_PROP_DECORATOR,
26  COMPONENT_STORAGE_LINK_DECORATOR,
27  COMPONENT_PROVIDE_DECORATOR,
28  COMPONENT_CONSUME_DECORATOR,
29  COMPONENT_OBJECT_LINK_DECORATOR,
30  COMPONENT_WATCH_DECORATOR,
31  COMPONENT_OBSERVED_DECORATOR,
32  OBSERVED_PROPERTY_SIMPLE,
33  OBSERVED_PROPERTY_OBJECT,
34  SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
35  SYNCHED_PROPERTY_SIMPLE_TWO_WAY,
36  SYNCHED_PROPERTY_OBJECT_TWO_WAY,
37  SYNCHED_PROPERTY_NESED_OBJECT,
38  CREATE_GET_METHOD,
39  CREATE_SET_METHOD,
40  CREATE_NEWVALUE_IDENTIFIER,
41  CREATE_CONSTRUCTOR_PARAMS,
42  ADD_PROVIDED_VAR,
43  INITIALIZE_CONSUME_FUNCTION,
44  APP_STORAGE,
45  APP_STORAGE_SET_AND_PROP,
46  APP_STORAGE_SET_AND_LINK,
47  COMPONENT_CONSTRUCTOR_UNDEFINED,
48  SET_CONTROLLER_METHOD,
49  SET_CONTROLLER_CTR,
50  SET_CONTROLLER_CTR_TYPE,
51  BASE_COMPONENT_NAME,
52  COMPONENT_CREATE_FUNCTION,
53  COMPONENT_BUILDERPARAM_DECORATOR,
54  COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
55  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR,
56  EXTNAME_ETS,
57  _GENERATE_ID,
58  RMELMTID,
59  PURGEDEPENDENCYONELMTID,
60  BASICDECORATORS,
61  BASE_COMPONENT_NAME_PU,
62  OBSERVED_PROPERTY_SIMPLE_PU,
63  OBSERVED_PROPERTY_OBJECT_PU,
64  SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU,
65  SYNCHED_PROPERTY_OBJECT_TWO_WAY_PU,
66  SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU,
67  SYNCHED_PROPERTY_OBJECT_ONE_WAY_PU,
68  SYNCHED_PROPERTY_NESED_OBJECT_PU,
69  COMPONENT_CUSTOM_DECORATOR,
70  THIS,
71  CREATE_STORAGE_LINK,
72  CREATE_STORAGE_PROP,
73  ELMTID,
74  COMPONENT_CONSTRUCTOR_PARAMS,
75  RESERT,
76  COMPONENT_IF_UNDEFINED,
77  COMPONENT_REQUIRE_DECORATOR,
78  OBSERVED,
79  TRUE,
80  FALSE
81} from './pre_define';
82import {
83  forbiddenUseStateType,
84  BUILDIN_STYLE_NAMES
85} from './component_map';
86import {
87  observedClassCollection,
88  enumCollection,
89  componentCollection,
90  classMethodCollection
91} from './validate_ui_syntax';
92import { updateConstructor } from './process_component_constructor';
93import {
94  LogType,
95  LogInfo,
96  componentInfo,
97  storedFileInfo
98} from './utils';
99import {
100  createReference,
101  isProperty,
102  isRegularProperty
103} from './process_component_class';
104import { transformLog, resourceFileName } from './process_ui_syntax';
105import {
106  globalProgram,
107  projectConfig,
108  partialUpdateConfig
109} from '../main';
110import {
111  parentConditionalExpression,
112  createFunction,
113  getRealNodePos,
114  isWrappedBuilder
115} from './process_component_build';
116import {
117  CUSTOM_BUILDER_METHOD,
118  INNER_CUSTOM_LOCALBUILDER_METHOD
119} from './component_map';
120export type ControllerType = {
121  hasController: boolean;
122  unassignedControllerSet: Set<string>;
123};
124
125export const observedPropertyDecorators: Set<string> =
126  new Set([COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR]);
127
128export const propAndLinkDecorators: Set<string> =
129  new Set([COMPONENT_PROP_DECORATOR, COMPONENT_LINK_DECORATOR]);
130
131export const appStorageDecorators: Set<string> =
132  new Set([COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR,
133    COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_PROP_DECORATOR]);
134
135export const mandatorySpecifyDefaultValueDecorators: Set<string> =
136  new Set([...observedPropertyDecorators, ...appStorageDecorators]);
137
138export const requireCanReleaseMandatoryDecorators: Set<string> =
139  new Set([COMPONENT_PROP_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR,
140    ...observedPropertyDecorators]);
141
142export const forbiddenSpecifyDefaultValueDecorators: Set<string> =
143  new Set([COMPONENT_LINK_DECORATOR, COMPONENT_CONSUME_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]);
144
145export const mandatoryToInitViaParamDecorators: Set<string> =
146  new Set([...propAndLinkDecorators, COMPONENT_OBJECT_LINK_DECORATOR]);
147
148export const setUpdateParamsDecorators: Set<string> =
149  new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR,
150    COMPONENT_BUILDERPARAM_DECORATOR
151  ]);
152
153export const setStateVarsDecorators: Set<string> = new Set([COMPONENT_OBJECT_LINK_DECORATOR]);
154
155export const immutableDecorators: Set<string> =
156  new Set([COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR]);
157
158export const simpleTypes: Set<ts.SyntaxKind> = new Set([ts.SyntaxKind.StringKeyword,
159  ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.EnumDeclaration]);
160
161export const decoratorParamSet: Set<string> = new Set();
162
163export const stateObjectCollection: Set<string> = new Set();
164
165export class UpdateResult {
166  private itemUpdate: boolean = false;
167  private ctorUpdate: boolean = false;
168  private properity: ts.PropertyDeclaration;
169  private ctor: ts.ConstructorDeclaration;
170  private variableGet: ts.GetAccessorDeclaration;
171  private variableSet: ts.SetAccessorDeclaration;
172  private updateParams: ts.Statement;
173  private deleteParams: boolean = false;
174  private controllerSet: ts.MethodDeclaration;
175  private purgeVariableDepStatement: ts.Statement;
176  private decoratorName: string;
177  private stateVarsParams: ts.Statement;
178
179  public setProperity(updateItem: ts.PropertyDeclaration): void {
180    this.itemUpdate = true;
181    this.properity = updateItem;
182  }
183
184  public setCtor(updateCtor: ts.ConstructorDeclaration): void {
185    this.ctorUpdate = true;
186    this.ctor = updateCtor;
187  }
188
189  public setControllerSet(updateControllerSet: ts.MethodDeclaration): void {
190    this.controllerSet = updateControllerSet;
191  }
192
193  public getControllerSet(): ts.MethodDeclaration {
194    return this.controllerSet;
195  }
196
197  public setVariableGet(updateVariableGet: ts.GetAccessorDeclaration): void {
198    this.variableGet = updateVariableGet;
199  }
200
201  public setVariableSet(updateVariableSet: ts.SetAccessorDeclaration): void {
202    this.variableSet = updateVariableSet;
203  }
204
205  public setUpdateParams(updateParams: ts.Statement): void {
206    this.updateParams = updateParams;
207  }
208
209  public setStateVarsParams(stateVarsParams: ts.Statement): void {
210    this.stateVarsParams = stateVarsParams;
211  }
212
213  public setDeleteParams(deleteParams: boolean): void {
214    this.deleteParams = deleteParams;
215  }
216
217  public setPurgeVariableDepStatement(purgeVariableDepStatement: ts.Statement): void {
218    this.purgeVariableDepStatement = purgeVariableDepStatement;
219  }
220
221  public setDecoratorName(decoratorName: string): void {
222    this.decoratorName = decoratorName;
223  }
224
225  public isItemUpdate(): boolean {
226    return this.itemUpdate;
227  }
228
229  public isCtorUpdate(): boolean {
230    return this.ctorUpdate;
231  }
232
233  public getProperity(): ts.PropertyDeclaration {
234    return this.properity;
235  }
236
237  public getCtor(): ts.ConstructorDeclaration {
238    return this.ctor;
239  }
240
241  public getUpdateParams(): ts.Statement {
242    return this.updateParams;
243  }
244
245  public getStateVarsParams(): ts.Statement {
246    return this.stateVarsParams;
247  }
248
249  public getPurgeVariableDepStatement(): ts.Statement {
250    return this.purgeVariableDepStatement;
251  }
252
253  public getVariableGet(): ts.GetAccessorDeclaration {
254    return this.variableGet;
255  }
256
257  public getVariableSet(): ts.SetAccessorDeclaration {
258    return this.variableSet;
259  }
260
261  public getDecoratorName(): string {
262    return this.decoratorName;
263  }
264
265  public isDeleteParams(): boolean {
266    return this.deleteParams;
267  }
268}
269
270export class PropMapManager {
271  static curPropMap: Map<string, string> = new Map();
272  static logInfoMap: Map<string, LogInfo[]> = new Map();
273
274  public static register(identifierName: string, decoratorName: string): void {
275    PropMapManager.curPropMap.set(identifierName, decoratorName);
276
277    if (decoratorName !== COMPONENT_NON_DECORATOR) {
278      PropMapManager.releaseLogs(identifierName, COMPONENT_NON_DECORATOR);
279    }
280  }
281
282  public static find(identifierName: string): string {
283    return PropMapManager.curPropMap.get(identifierName);
284  }
285
286  public static reserveLog(identifierName: string, decoratorName: string, log: LogInfo): void {
287    const key: string = `${identifierName}-${decoratorName}`;
288    const logInfos: LogInfo[] = PropMapManager.logInfoMap.get(key) ?? [];
289    PropMapManager.logInfoMap.set(key, [...logInfos, log]);
290  }
291
292  public static releaseLogs(identifierName: string, decoratorName: string): void {
293    const key: string = `${identifierName}-${decoratorName}`;
294    if (PropMapManager.logInfoMap.has(key)) {
295      PropMapManager.logInfoMap.delete(key);
296    }
297  }
298
299  public static reset(): void {
300    PropMapManager.curPropMap.clear();
301    PropMapManager.logInfoMap.clear();
302  }
303}
304
305export function processMemberVariableDecorators(parentName: ts.Identifier,
306  item: ts.PropertyDeclaration, ctorNode: ts.ConstructorDeclaration, watchMap: Map<string, ts.Node>,
307  checkController: ControllerType, log: LogInfo[], program: ts.Program, context: ts.TransformationContext,
308  hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): UpdateResult {
309  const updateResult: UpdateResult = new UpdateResult();
310  const name: ts.Identifier = item.name as ts.Identifier;
311  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
312  if (isRegularProperty(decorators)) {
313    if (!name.escapedText) {
314      return updateResult;
315    }
316    PropMapManager.register(name.escapedText.toString(), COMPONENT_NON_DECORATOR);
317    updateResult.setProperity(undefined);
318    updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_NON_DECORATOR));
319    updateResult.setCtor(updateConstructor(ctorNode, [], [
320      createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview,
321        interfaceNode)], []));
322    updateResult.setControllerSet(createControllerSet(item, parentName, name, checkController));
323    if (partialUpdateConfig.partialUpdateMode) {
324      updateResult.setDeleteParams(true);
325    }
326  } else if (!item.type) {
327    validatePropertyNonType(name, log);
328    return updateResult;
329  } else if (validateCustomDecorator(decorators, log)) {
330    updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_CUSTOM_DECORATOR));
331  } else {
332    processPropertyNodeDecorator(parentName, item, updateResult, ctorNode, name, watchMap,
333      log, program, context, hasPreview, interfaceNode);
334  }
335  if (decorators && decorators.length && validatePropDecorator(decorators)) {
336    updateResult.setStateVarsParams(createStateVarsBody(name));
337  }
338  return updateResult;
339}
340
341function createStateVarsBody(name: ts.Identifier): ts.ExpressionStatement {
342  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
343    ts.factory.createPropertyAccessExpression(
344      ts.factory.createPropertyAccessExpression(
345        ts.factory.createThis(),
346        ts.factory.createIdentifier('__' + name.escapedText.toString())
347      ),
348      ts.factory.createIdentifier(RESERT)
349    ),
350    undefined,
351    [ts.factory.createPropertyAccessExpression(
352      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARAMS),
353      name
354    )]
355  ));
356}
357
358function createControllerSet(node: ts.PropertyDeclaration, componentName: ts.Identifier,
359  name: ts.Identifier, checkController: ControllerType): ts.MethodDeclaration {
360  if (componentCollection.customDialogs.has(componentName.getText()) && node.type &&
361    node.type.getText() === SET_CONTROLLER_CTR_TYPE) {
362    checkController.unassignedControllerSet.add(name.getText());
363    checkController.hasController = true;
364    return ts.factory.createMethodDeclaration(undefined, undefined,
365      ts.factory.createIdentifier(SET_CONTROLLER_METHOD), undefined, undefined,
366      [ts.factory.createParameterDeclaration(undefined, undefined,
367        ts.factory.createIdentifier(SET_CONTROLLER_CTR), undefined,
368        ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(SET_CONTROLLER_CTR_TYPE),
369          undefined), undefined)], undefined, ts.factory.createBlock(
370        [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
371          ts.factory.createPropertyAccessExpression(ts.factory.createThis(), name),
372          ts.factory.createToken(ts.SyntaxKind.EqualsToken),
373          ts.factory.createIdentifier(SET_CONTROLLER_CTR)))], true));
374  }
375  return undefined;
376}
377
378function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.PropertyDeclaration,
379  updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, name: ts.Identifier,
380  watchMap: Map<string, ts.Node>, log: LogInfo[], program: ts.Program,
381  context: ts.TransformationContext, hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): void {
382  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
383  const propertyDecorators: string[] = [];
384  for (let i = 0; i < decorators.length; i++) {
385    const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
386    const includeWatchAndRequire: boolean =
387      [COMPONENT_WATCH_DECORATOR, COMPONENT_REQUIRE_DECORATOR].includes(decoratorName);
388    if (!includeWatchAndRequire) {
389      PropMapManager.register(name.escapedText.toString(), decoratorName);
390    }
391    checkDecoratorIsDuplicated(decoratorName, decorators[i], log);
392    if (checkDecoratorIsForbidden(node, includeWatchAndRequire, decoratorName, name, log)) {
393      return;
394    }
395    checkDecoratorIsIllegalInEntry(parentName, decoratorName, name, log);
396    if (checkDecoratorIsIllegalInPropertyInit(node, decoratorName, name, log)) {
397      return;
398    }
399    checkDecoratorHasIllegalQuestionToken(node, decoratorName, name, log);
400    if (!isSimpleType(node.type, program) &&
401      decoratorName !== COMPONENT_BUILDERPARAM_DECORATOR) {
402      stateObjectCollection.add(name.escapedText.toString());
403    }
404    if (decoratorName === COMPONENT_WATCH_DECORATOR &&
405      validateWatchDecorator(name, decorators, log)) {
406      processWatch(node, decorators[i], watchMap, log);
407    } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
408      propertyDecorators.push(decoratorName);
409      if (decoratorName !== COMPONENT_REQUIRE_DECORATOR) {
410        processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context,
411          hasPreview, interfaceNode);
412      }
413    }
414  }
415  validatePropertyDecorator(propertyDecorators, name, log);
416}
417
418function checkDecoratorHasIllegalQuestionToken(
419  node: ts.PropertyDeclaration, decoratorName: string, name: ts.Identifier, log: LogInfo[]): void {
420  if (node.questionToken && mandatoryToInitViaParamDecorators.has(decoratorName) &&
421    !(decoratorName === COMPONENT_PROP_DECORATOR && node.initializer)) {
422    validateHasIllegalQuestionToken(name, decoratorName, log);
423  }
424}
425
426function checkDecoratorIsIllegalInPropertyInit(
427  node: ts.PropertyDeclaration, decoratorName: string, name: ts.Identifier, log: LogInfo[]): boolean {
428  if (node.initializer && forbiddenSpecifyDefaultValueDecorators.has(decoratorName)) {
429    validatePropertyDefaultValue(name, decoratorName, log);
430    return true;
431  } else if (!node.initializer && !isRequireCanReleaseMandatoryDecorators(node, decoratorName) &&
432    mandatorySpecifyDefaultValueDecorators.has(decoratorName)) {
433    validatePropertyNonDefaultValue(name, decoratorName, log);
434    return true;
435  }
436  return false;
437}
438
439function checkDecoratorIsIllegalInEntry(
440  parentName: ts.Identifier, decoratorName: string, name: ts.Identifier, log: LogInfo[]): void {
441  if (parentName.getText() === componentCollection.entryComponent &&
442    mandatoryToInitViaParamDecorators.has(decoratorName)) {
443    validateHasIllegalDecoratorInEntry(parentName, name, decoratorName, log);
444  }
445}
446
447function checkDecoratorIsForbidden(
448  node: ts.PropertyDeclaration, includeWatchAndRequire: boolean, decoratorName: string,
449  name: ts.Identifier, log: LogInfo[]): boolean {
450  if (!includeWatchAndRequire && ts.isTypeReferenceNode(node.type) && isForbiddenUseStateType(node.type)) {
451    validateForbiddenUseStateType(name, decoratorName, node.type.typeName.getText(), log);
452    return true;
453  }
454  return false;
455}
456
457function checkDecoratorIsDuplicated(decoratorName: string, decorator: ts.Decorator, log: LogInfo[]): void {
458  if (BUILDIN_STYLE_NAMES.has(decoratorName.replace('@', ''))) {
459    validateDuplicateDecorator(decorator, log);
460  }
461}
462
463function isRequireCanReleaseMandatoryDecorators(node: ts.PropertyDeclaration, decoratorName: string): boolean {
464  if (decoratorName === COMPONENT_REQUIRE_DECORATOR) {
465    return true;
466  }
467
468  const decoratorIsNotMandatory: boolean = ts.getAllDecorators(node).find(
469    (decorator: ts.Decorator) => decorator.getText() === COMPONENT_REQUIRE_DECORATOR) &&
470    requireCanReleaseMandatoryDecorators.has(decoratorName);
471  return decoratorIsNotMandatory;
472}
473
474function validatePropertyDecorator(propertyDecorators: string[], name: ts.Identifier,
475  log: LogInfo[]): void {
476  if (propertyDecorators.length > 1 && !validateRequireDecorator(propertyDecorators)) {
477    validateMultiDecorators(name, log);
478  }
479}
480
481const DECORATOR_LENGTH: number = 2;
482const SUPPORT_REQUIRE_DECORATOR: string[] = [COMPONENT_PROP_DECORATOR,
483  COMPONENT_BUILDERPARAM_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR,
484  COMPONENT_WATCH_DECORATOR
485];
486
487function validateRequireDecorator(propertyDecorators: string[]): boolean {
488  const isSupportRequire: boolean = propertyDecorators.some((item: string) => {
489    return SUPPORT_REQUIRE_DECORATOR.includes(item);
490  });
491  return propertyDecorators.length === DECORATOR_LENGTH &&
492    propertyDecorators.includes(COMPONENT_REQUIRE_DECORATOR) && isSupportRequire;
493}
494
495function processStateDecorators(node: ts.PropertyDeclaration, decorator: string,
496  updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, log: LogInfo[],
497  program: ts.Program, context: ts.TransformationContext, hasPreview:boolean,
498  interfaceNode: ts.InterfaceDeclaration): void {
499  const name: ts.Identifier = node.name as ts.Identifier;
500  updateResult.setProperity(undefined);
501  const updateState: ts.Statement[] = [];
502  const variableInitStatement: ts.Statement =
503    createVariableInitStatement(node, decorator, log, program, context, hasPreview, interfaceNode);
504  if (variableInitStatement) {
505    updateState.push(variableInitStatement);
506  }
507  addAddProvidedVar(node, name, decorator, updateState);
508  updateResult.setCtor(updateConstructor(ctorNode, [], [...updateState], [], false));
509  if (decorator !== COMPONENT_BUILDERPARAM_DECORATOR) {
510    updateResult.setVariableGet(createGetAccessor(name, CREATE_GET_METHOD));
511    updateResult.setDeleteParams(true);
512  }
513  if (!immutableDecorators.has(decorator)) {
514    updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD, node.type));
515  }
516  if (setUpdateParamsDecorators.has(decorator)) {
517    updateResult.setUpdateParams(createUpdateParams(name, decorator, node));
518  }
519  if (projectConfig.optLazyForEach) {
520    setStateVarsDecorators.add(COMPONENT_STATE_DECORATOR);
521  }
522  if (setStateVarsDecorators.has(decorator)) {
523    updateResult.setStateVarsParams(createStateVarsParams(name, decorator));
524  }
525  if (partialUpdateConfig.partialUpdateMode && BASICDECORATORS.has(decorator)) {
526    const variableWithUnderLink: string = '__' + name.escapedText.toString();
527    updateResult.setDecoratorName(decorator);
528    updateResult.setPurgeVariableDepStatement(createPurgeVariableDepStatement(variableWithUnderLink));
529  }
530}
531
532function createPurgeVariableDepStatement(variableWithUnderLink: string): ts.Statement {
533  return ts.factory.createExpressionStatement(
534    ts.factory.createCallExpression(
535      ts.factory.createPropertyAccessExpression(
536        ts.factory.createPropertyAccessExpression(
537          ts.factory.createThis(),
538          ts.factory.createIdentifier(variableWithUnderLink)
539        ),
540        ts.factory.createIdentifier(PURGEDEPENDENCYONELMTID)
541      ),
542      undefined,
543      [ts.factory.createIdentifier(RMELMTID)]
544    )
545  );
546}
547
548function processWatch(node: ts.PropertyDeclaration, decorator: ts.Decorator,
549  watchMap: Map<string, ts.Node>, log: LogInfo[]): void {
550  if (node.name) {
551    const propertyName: string = node.name.getText();
552    if (decorator.expression && ts.isCallExpression(decorator.expression) &&
553      decorator.expression.arguments && decorator.expression.arguments.length === 1) {
554      const currentClassMethod: Set<string> = getClassMethod(node);
555      const argument: ts.Node = decorator.expression.arguments[0];
556      if (ts.isStringLiteral(argument)) {
557        if (currentClassMethod && currentClassMethod.has(argument.text)) {
558          watchMap.set(propertyName, argument);
559        } else {
560          log.push({
561            type: LogType.ERROR,
562            message: `'@Watch' cannot be used with ${argument.getText()}.` +
563            ` Apply it only to parameters that correspond to existing methods.`,
564            pos: argument.getStart(),
565            code: '10905301'
566          });
567        }
568      } else if (ts.isIdentifier(decorator.expression.arguments[0])) {
569        const content: string = decorator.expression.arguments[0].getText();
570        const propertyNode: ts.PropertyAccessExpression = createPropertyAccessExpressionWithThis(content);
571        watchMap.set(propertyName, propertyNode);
572        decoratorParamSet.add(content);
573        validateWatchParam(LogType.WARN, argument.getStart(), log, argument.getText());
574      } else if (ts.isPropertyAccessExpression(decorator.expression.arguments[0])) {
575        watchMap.set(propertyName, decorator.expression.arguments[0]);
576        validateWatchParam(LogType.WARN, argument.getStart(), log, argument.getText());
577      } else {
578        validateWatchParam(LogType.ERROR, argument.getStart(), log, argument.getText());
579      }
580    }
581  }
582}
583
584function getClassMethod(node: ts.PropertyDeclaration): Set<string> {
585  const sourceFile: ts.SourceFile = node.getSourceFile();
586  const filePath: string = sourceFile ? sourceFile.fileName : undefined;
587  if (filePath && classMethodCollection.get(filePath)) {
588    return classMethodCollection.get(filePath).get(node.parent.name.getText());
589  }
590  return new Set();
591}
592
593function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: string,
594  log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean,
595  interfaceNode: ts.InterfaceDeclaration): ts.Statement {
596  const name: ts.Identifier = node.name as ts.Identifier;
597  let type: ts.TypeNode;
598  let updateState: ts.ExpressionStatement;
599  if (node.type) {
600    type = node.type;
601  }
602  switch (decorator) {
603    case COMPONENT_NON_DECORATOR:
604      updateState = updateNormalProperty(node, name, log, context);
605      break;
606    case COMPONENT_STATE_DECORATOR:
607    case COMPONENT_PROVIDE_DECORATOR:
608      updateState = !partialUpdateConfig.partialUpdateMode ?
609        updateObservedProperty(node, name, type, program) : updateObservedPropertyPU(node, name, type, program);
610      break;
611    case COMPONENT_LINK_DECORATOR:
612      wrongDecoratorInPreview(node, COMPONENT_LINK_DECORATOR, hasPreview, log);
613      updateState = !partialUpdateConfig.partialUpdateMode ?
614        updateSynchedPropertyTwoWay(name, type, program) : updateSynchedPropertyTwoWayPU(name, type, program);
615      break;
616    case COMPONENT_PROP_DECORATOR:
617      wrongDecoratorInPreview(node, COMPONENT_PROP_DECORATOR, hasPreview, log);
618      updateState = !partialUpdateConfig.partialUpdateMode ?
619        updateSynchedPropertyOneWay(name, type, decorator, log, program) :
620        updateSynchedPropertyOneWayPU(name, type, decorator, log, program);
621      break;
622    case COMPONENT_STORAGE_PROP_DECORATOR:
623    case COMPONENT_STORAGE_LINK_DECORATOR:
624      updateState = updateStoragePropAndLinkProperty(node, name, decorator);
625      break;
626    case COMPONENT_OBJECT_LINK_DECORATOR:
627      updateState = !partialUpdateConfig.partialUpdateMode ?
628        updateSynchedPropertyNesedObject(name, type, decorator, log) :
629        updateSynchedPropertyNesedObjectPU(name, type, decorator, log);
630      break;
631    case COMPONENT_CONSUME_DECORATOR:
632      wrongDecoratorInPreview(node, COMPONENT_CONSUME_DECORATOR, hasPreview, log);
633      updateState = updateConsumeProperty(node, name);
634      break;
635    case COMPONENT_BUILDERPARAM_DECORATOR:
636      updateState = updateBuilderParamProperty(node, name, log);
637  }
638  const members = interfaceNode.members;
639  members.push(ts.factory.createPropertySignature(undefined, name,
640    ts.factory.createToken(ts.SyntaxKind.QuestionToken), type));
641  interfaceNode = ts.factory.updateInterfaceDeclaration(interfaceNode,
642    ts.getModifiers(interfaceNode), interfaceNode.name, interfaceNode.typeParameters,
643    interfaceNode.heritageClauses, members);
644  return updateState;
645}
646
647function wrongDecoratorInPreview(node: ts.PropertyDeclaration, decorator: string,
648  hasPreview: boolean, log: LogInfo[]): void {
649  if (hasPreview && projectConfig.isPreview) {
650    log.push({
651      type: LogType.WARN,
652      message: `The variable with '${decorator}' in component with '@Preview' may ` +
653        `cause error in component preview mode`,
654      pos: node.getStart()
655    });
656  }
657}
658
659function createUpdateParams(name: ts.Identifier, decorator: string,
660  localInitializationNode: ts.PropertyDeclaration = undefined): ts.Statement {
661  let updateParamsNode: ts.Statement;
662  switch (decorator) {
663    case COMPONENT_NON_DECORATOR:
664    case COMPONENT_STATE_DECORATOR:
665    case COMPONENT_PROVIDE_DECORATOR:
666    case COMPONENT_CUSTOM_DECORATOR:
667      updateParamsNode = createUpdateParamsWithIf(name);
668      break;
669    case COMPONENT_PROP_DECORATOR:
670      if (!partialUpdateConfig.partialUpdateMode) {
671        updateParamsNode = createUpdateParamsWithoutIf(name);
672      } else {
673        if (localInitializationNode && localInitializationNode.initializer) {
674          updateParamsNode = createUpdateParamsWithIf(name, true,
675            localInitializationNode.initializer);
676        }
677      }
678      break;
679    case COMPONENT_BUILDERPARAM_DECORATOR:
680      updateParamsNode = createUpdateParamsWithIf(name);
681      break;
682    case COMPONENT_OBJECT_LINK_DECORATOR:
683      updateParamsNode = createUpdateParamsWithSet(name);
684      break;
685  }
686  return updateParamsNode;
687}
688
689function createStateVarsParams(name: ts.Identifier, decorator: string): ts.Statement {
690  let updateParamsNode: ts.Statement;
691  switch (decorator) {
692    case COMPONENT_OBJECT_LINK_DECORATOR:
693      updateParamsNode = createUpdateParamsWithSet(name);
694      break;
695    case COMPONENT_STATE_DECORATOR:
696      updateParamsNode = createUpdateParamsForState(name);
697      break;
698  }
699  return updateParamsNode;
700}
701
702function createUpdateParamsWithIf(name: ts.Identifier, isProp: boolean = false,
703  initializeNode: ts.Expression = undefined): ts.IfStatement {
704  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
705    ts.factory.createPropertyAccessExpression(
706      ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS),
707      ts.factory.createIdentifier(name.escapedText.toString())),
708    isProp ? ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken) :
709      ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
710    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)),
711  isProp ? ts.factory.createBlock([createUpdateParamsWithSet(name, true, initializeNode)]) :
712    ts.factory.createBlock([
713      createUpdateParamsWithoutIf(name)], true), undefined);
714}
715
716function createUpdateParamsForState(name: ts.Identifier): ts.IfStatement {
717  return ts.factory.createIfStatement(createPropertyAccessExpressionWithParams(name.getText()),
718    ts.factory.createBlock([createUpdateParamsWithSet(name)]));
719}
720
721function createUpdateParamsWithoutIf(name: ts.Identifier): ts.ExpressionStatement {
722  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
723    createPropertyAccessExpressionWithThis(name.getText()),
724    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
725    createPropertyAccessExpressionWithParams(name.getText())));
726}
727
728function createUpdateParamsWithSet(name: ts.Identifier, hasElse: boolean = false,
729  initializeNode: ts.Expression = undefined): ts.ExpressionStatement {
730  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
731    ts.factory.createPropertyAccessExpression(createPropertyAccessExpressionWithThis(`__${name.getText()}`),
732      ts.factory.createIdentifier(CREATE_SET_METHOD)), undefined,
733    [hasElse ? initializeNode : createPropertyAccessExpressionWithParams(name.getText())]));
734}
735
736function updateNormalProperty(node: ts.PropertyDeclaration, name: ts.Identifier,
737  log: LogInfo[], context: ts.TransformationContext): ts.ExpressionStatement {
738  const init: ts.Expression =
739    ts.visitNode(node.initializer, visitDialogController);
740  function visitDialogController(node: ts.Node): ts.Node {
741    if (isProperty(node)) {
742      node = createReference(node as ts.PropertyAssignment, log);
743    }
744    return ts.visitEachChild(node, visitDialogController, context);
745  }
746  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
747    createPropertyAccessExpressionWithThis(name.getText()),
748    ts.factory.createToken(ts.SyntaxKind.EqualsToken), init ||
749    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)));
750}
751
752function updateObservedProperty(item: ts.PropertyDeclaration, name: ts.Identifier,
753  type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement {
754  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
755    createPropertyAccessExpressionWithThis(`__${name.getText()}`),
756    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression(
757      ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE :
758        OBSERVED_PROPERTY_OBJECT), undefined, [item.initializer, ts.factory.createThis(),
759        ts.factory.createStringLiteral(name.escapedText.toString())])));
760}
761
762function updateSynchedPropertyTwoWay(nameIdentifier: ts.Identifier, type: ts.TypeNode,
763  program: ts.Program): ts.ExpressionStatement {
764  const name: string = nameIdentifier.escapedText.toString();
765  const functionName: string = isSimpleType(type, program) ?
766    SYNCHED_PROPERTY_SIMPLE_TWO_WAY : SYNCHED_PROPERTY_OBJECT_TWO_WAY;
767  return createInitExpressionStatementForDecorator(name, functionName,
768    createPropertyAccessExpressionWithParams(name));
769}
770
771function updateSynchedPropertyOneWay(nameIdentifier: ts.Identifier, type: ts.TypeNode,
772  decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement {
773  const name: string = nameIdentifier.escapedText.toString();
774  if (isSimpleType(type, program)) {
775    return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
776      createPropertyAccessExpressionWithParams(name));
777  } else {
778    validateNonSimpleType(nameIdentifier, decoractor, log);
779    return undefined;
780  }
781}
782
783export function findDecoratorIndex(decorators: readonly ts.Decorator[], nameList: string[]): number {
784  return decorators.findIndex((item: ts.Decorator) => {
785    return nameList.includes(item.getText().replace(/\(.*\)$/, '').trim());
786  });
787}
788
789function updateStoragePropAndLinkProperty(node: ts.PropertyDeclaration, name: ts.Identifier,
790  decorator: string): ts.ExpressionStatement {
791  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
792  if (isSingleKey(node)) {
793    let setFuncName: string;
794    let storageFuncName: string;
795    const index: number = findDecoratorIndex(decorators, [decorator]);
796    const storageValue: ts.Expression[] = [
797      decorators[index].expression.arguments[0],
798      node.initializer,
799      ts.factory.createThis(),
800      ts.factory.createStringLiteral(name.getText())
801    ];
802    if (!partialUpdateConfig.partialUpdateMode) {
803      setFuncName = decorator === COMPONENT_STORAGE_PROP_DECORATOR ?
804        APP_STORAGE_SET_AND_PROP : APP_STORAGE_SET_AND_LINK;
805      storageFuncName = APP_STORAGE;
806    } else {
807      setFuncName = decorator === COMPONENT_STORAGE_PROP_DECORATOR ?
808        CREATE_STORAGE_PROP : CREATE_STORAGE_LINK;
809      storageFuncName = THIS;
810      storageValue.splice(2, 1);
811    }
812    return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
813      createPropertyAccessExpressionWithThis(`__${name.getText()}`),
814      ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(
815        ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(storageFuncName),
816          ts.factory.createIdentifier(setFuncName)), undefined, storageValue)));
817  }
818  return undefined;
819}
820
821function getDecoratorKey(node: ts.PropertyDeclaration, isProvided: boolean = false): [string, boolean, ts.Node, boolean] {
822  let key: string;
823  let isStringKey: boolean = false;
824  let isProvidedParamObj: boolean = false;
825  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
826  const index: number = findDecoratorIndex(decorators, [COMPONENT_PROVIDE_DECORATOR, COMPONENT_CONSUME_DECORATOR]);
827  // @ts-ignore
828  let keyNameNode: ts.Node = decorators[index].expression.arguments[0];
829  if (ts.isIdentifier(keyNameNode)) {
830    key = keyNameNode.getText();
831    decoratorParamSet.add(key);
832  } else if (ts.isStringLiteral(keyNameNode)) {
833    key = keyNameNode.text;
834    isStringKey = true;
835  } else if (isProvided && ts.isObjectLiteralExpression(keyNameNode) && keyNameNode.properties.length === 1 &&
836    ts.isPropertyAssignment(keyNameNode.properties[0]) && keyNameNode.properties[0].initializer) {
837    key = keyNameNode.properties[0].initializer.text;
838    keyNameNode = keyNameNode.properties[0].initializer;
839    isProvidedParamObj = true;
840  } else {
841    key = keyNameNode.getText();
842  }
843  return [key, isStringKey, keyNameNode, isProvidedParamObj];
844}
845
846function updateSynchedPropertyNesedObject(nameIdentifier: ts.Identifier,
847  type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement {
848  if (isObservedClassType(type)) {
849    return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT,
850      createPropertyAccessExpressionWithParams(nameIdentifier.getText()));
851  } else {
852    validateNonObservedClassType(nameIdentifier, decoractor, log);
853  }
854  return undefined;
855}
856
857function updateConsumeProperty(node: ts.PropertyDeclaration,
858  nameIdentifier: ts.Identifier): ts.ExpressionStatement {
859  const name: string = nameIdentifier.getText();
860  let propertyOrAliasName: string;
861  const propertyAndStringKey: [string?, boolean?, ts.Node?, boolean?] = [];
862  if (isSingleKey(node, true)) {
863    propertyAndStringKey.push(...getDecoratorKey(node));
864    propertyOrAliasName = propertyAndStringKey[0];
865  } else {
866    propertyOrAliasName = name;
867  }
868  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
869    createPropertyAccessExpressionWithThis(`__${name}`),
870    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(
871      createPropertyAccessExpressionWithThis(INITIALIZE_CONSUME_FUNCTION), undefined, [
872        propertyAndStringKey.length === 0 ? ts.factory.createStringLiteral(propertyOrAliasName) :
873          propertyAndStringKey.length === 4 && propertyAndStringKey[2] as ts.Expression, ts.factory.createStringLiteral(name)])));
874}
875
876function updateBuilderParamProperty(node: ts.PropertyDeclaration,
877  nameIdentifier: ts.Identifier, log: LogInfo[]): ts.ExpressionStatement {
878  const name: string = nameIdentifier.getText();
879  if (judgeBuilderParamAssignedByBuilder(node)) {
880    log.push({
881      type: LogType.ERROR,
882      message: `'@BuilderParam' property can only initialized by '@Builder' function or '@LocalBuilder' method in struct.`,
883      pos: node.getStart(),
884      code: '10905101'
885    });
886  }
887  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
888    createPropertyAccessExpressionWithThis(name), ts.factory.createToken(ts.SyntaxKind.EqualsToken),
889    node.initializer || ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
890  ));
891}
892
893export function judgeBuilderParamAssignedByBuilder(node: ts.PropertyDeclaration): boolean {
894  return node.initializer && !(node.initializer && (ts.isIdentifier(node.initializer) &&
895    CUSTOM_BUILDER_METHOD.has(node.initializer.escapedText.toString()) ||
896    ts.isPropertyAccessExpression(node.initializer) && node.initializer.name &&
897    ts.isIdentifier(node.initializer.name) &&
898    (CUSTOM_BUILDER_METHOD.has(node.initializer.name.escapedText.toString()) ||
899    INNER_CUSTOM_LOCALBUILDER_METHOD.has(node.initializer.name.escapedText.toString())) ||
900    isWrappedBuilder(node.initializer as ts.PropertyAccessExpression)));
901}
902
903export function createViewCreate(node: ts.NewExpression | ts.Identifier): ts.CallExpression {
904  if (partialUpdateConfig.partialUpdateMode) {
905    return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
906      ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node]));
907  }
908  return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME),
909    ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node]));
910}
911
912export function createCustomComponentNewExpression(node: ts.CallExpression, name: string,
913  isBuilder: boolean = false, isGlobalBuilder: boolean = false,
914  isCutomDialog: boolean = false): ts.NewExpression {
915  const newNode: ts.NewExpression = ts.factory.createNewExpression(node.expression,
916    node.typeArguments, node.arguments.length ? node.arguments : []);
917  return addCustomComponentId(newNode, node, name, isBuilder, isGlobalBuilder, isCutomDialog);
918}
919
920function addCustomComponentId(node: ts.NewExpression, oldNode: ts.CallExpression, componentName: string,
921  isBuilder: boolean = false, isGlobalBuilder: boolean = false,
922  isCutomDialog: boolean = false): ts.NewExpression {
923  const posOfNode = transformLog.sourceFile.getLineAndCharacterOfPosition(getRealNodePos(node));
924  const line: number = posOfNode.line + 1;
925  const col: number = posOfNode.character + 1;
926  for (const item of componentCollection.customComponents) {
927    componentInfo.componentNames.add(item);
928  }
929  componentInfo.componentNames.forEach((name: string) => {
930    let argumentsArray: ts.Expression[];
931    if (node.arguments && node.arguments.length) {
932      argumentsArray = Array.from(node.arguments);
933      if (partialUpdateConfig.partialUpdateMode && node.arguments.length === 1) {
934        manageLocalStorageComponents(oldNode, argumentsArray);
935      }
936    }
937    if (componentName === name) {
938      if (!argumentsArray) {
939        argumentsArray = [ts.factory.createObjectLiteralExpression([], true)];
940        if (partialUpdateConfig.partialUpdateMode) {
941          argumentsArray.push(ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED));
942        }
943      }
944      if (!partialUpdateConfig.partialUpdateMode) {
945        ++componentInfo.id;
946        argumentsArray.unshift(isBuilder ? ts.factory.createBinaryExpression(
947          ts.factory.createStringLiteral(path.basename(transformLog.sourceFile.fileName, EXTNAME_ETS) + '_'),
948          ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createIdentifier(_GENERATE_ID)) :
949          ts.factory.createStringLiteral(componentInfo.id.toString()),
950        isBuilder ? parentConditionalExpression() : ts.factory.createThis());
951      } else {
952        argumentsArray.unshift((isGlobalBuilder || storedFileInfo.processLocalBuilder) ? parentConditionalExpression() : ts.factory.createThis());
953        argumentsArray.push(isCutomDialog ? ts.factory.createPrefixUnaryExpression(
954          ts.SyntaxKind.MinusToken,
955          ts.factory.createNumericLiteral('1')) : ts.factory.createIdentifier(ELMTID),
956        createArrowFunctionNode(), componentParamRowAndColumn(line, col));
957      }
958      node =
959        ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray);
960    } else if (argumentsArray) {
961      node =
962        ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray);
963    }
964  });
965  return node;
966}
967
968function manageLocalStorageComponents(node: ts.CallExpression, argumentsArray: ts.Expression[]): void {
969  if (isLocalStorageParameter(node)) {
970    argumentsArray.unshift(ts.factory.createObjectLiteralExpression([], false));
971  } else {
972    argumentsArray.push(ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED));
973  }
974}
975
976export function isLocalStorageParameter(node: ts.CallExpression): boolean {
977  return globalProgram.checker && globalProgram.checker.getResolvedSignature &&
978    globalProgram.checker.getResolvedSignature(node) &&
979    globalProgram.checker.getResolvedSignature(node).parameters &&
980    globalProgram.checker.getResolvedSignature(node).parameters.length === 1 &&
981    globalProgram.checker.getResolvedSignature(node).parameters[0].escapedName === '##storage';
982}
983
984function componentParamRowAndColumn(line: number, col: number): ts.ObjectLiteralExpression {
985  return ts.factory.createObjectLiteralExpression(
986    [
987      ts.factory.createPropertyAssignment(
988        ts.factory.createIdentifier('page'),
989        ts.factory.createStringLiteral(path.relative(process.cwd(), resourceFileName).replace(/\\/g, '/'))
990      ),
991      ts.factory.createPropertyAssignment(
992        ts.factory.createIdentifier('line'),
993        ts.factory.createNumericLiteral(line)
994      ),
995      ts.factory.createPropertyAssignment(
996        ts.factory.createIdentifier('col'),
997        ts.factory.createNumericLiteral(col)
998      )
999    ],
1000    false
1001  );
1002}
1003
1004function createArrowFunctionNode(): ts.ArrowFunction {
1005  return ts.factory.createArrowFunction(
1006    undefined,
1007    undefined,
1008    [],
1009    undefined,
1010    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
1011    ts.factory.createBlock(
1012      [],
1013      false
1014    )
1015  );
1016}
1017
1018function createInitExpressionStatementForDecorator(propertyName: string, functionName: string,
1019  parameterNode: ts.Expression): ts.ExpressionStatement {
1020  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
1021    createPropertyAccessExpressionWithThis(`__${propertyName}`),
1022    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression(
1023      ts.factory.createIdentifier(functionName), undefined, [parameterNode, ts.factory.createThis(),
1024        ts.factory.createStringLiteral(propertyName)])));
1025}
1026
1027function createPropertyAccessExpressionWithParams(propertyName: string): ts.PropertyAccessExpression {
1028  return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS),
1029    ts.factory.createIdentifier(propertyName));
1030}
1031
1032function createPropertyAccessExpressionWithThis(propertyName: string): ts.PropertyAccessExpression {
1033  return ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
1034    ts.factory.createIdentifier(propertyName));
1035}
1036
1037function addAddProvidedVar(node: ts.PropertyDeclaration, name: ts.Identifier,
1038  decoratorName: string, updateState: ts.Statement[]): void {
1039  if (decoratorName === COMPONENT_PROVIDE_DECORATOR) {
1040    let parameterName: string;
1041    const parameterNameAndStringKey: [string?, boolean?, ts.Node?, boolean?] = [];
1042    if (isSingleKey(node, true)) {
1043      parameterNameAndStringKey.push(...getDecoratorKey(node, true));
1044      parameterName = parameterNameAndStringKey[0];
1045      updateState.push(createAddProvidedVar(parameterName, name, parameterNameAndStringKey[1], parameterNameAndStringKey[2],
1046        parameterNameAndStringKey[3]));
1047    }
1048    if (parameterName !== name.getText()) {
1049      updateState.push(createAddProvidedVar(name.getText(), name, true, undefined, parameterNameAndStringKey[3]));
1050    }
1051  }
1052}
1053
1054function createAddProvidedVar(propertyOrAliasName: string,
1055  name: ts.Identifier, isString: boolean, decoratorKeyNode: ts.Node,
1056  isProvidedParamObj: boolean): ts.ExpressionStatement {
1057  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1058    createPropertyAccessExpressionWithThis(ADD_PROVIDED_VAR), undefined, [
1059      isString ? ts.factory.createStringLiteral(propertyOrAliasName) : decoratorKeyNode as ts.Expression,
1060      createPropertyAccessExpressionWithThis(`__${name.getText()}`),
1061      isProvidedParamObj ? ts.factory.createIdentifier(TRUE) : ts.factory.createIdentifier(FALSE)]));
1062}
1063
1064function createGetAccessor(item: ts.Identifier, express: string): ts.GetAccessorDeclaration {
1065  const getAccessorStatement: ts.GetAccessorDeclaration =
1066    ts.factory.createGetAccessorDeclaration(undefined, item, [], undefined,
1067      ts.factory.createBlock([ts.factory.createReturnStatement(
1068        ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
1069          createPropertyAccessExpressionWithThis(`__${item.getText()}`),
1070          ts.factory.createIdentifier(express)), undefined, []))], true));
1071  return getAccessorStatement;
1072}
1073
1074function createSetAccessor(item: ts.Identifier, express: string, type: ts.TypeNode):
1075  ts.SetAccessorDeclaration {
1076  const setAccessorStatement: ts.SetAccessorDeclaration =
1077    ts.factory.createSetAccessorDeclaration(undefined, item,
1078      [ts.factory.createParameterDeclaration(undefined, undefined,
1079        ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, type,
1080        undefined)], ts.factory.createBlock([ts.factory.createExpressionStatement(
1081        ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
1082          createPropertyAccessExpressionWithThis(`__${item.getText()}`),
1083          ts.factory.createIdentifier(express)), undefined,
1084        [ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER)]))], true));
1085  return setAccessorStatement;
1086}
1087
1088function isForbiddenUseStateType(typeNode: ts.TypeNode): boolean {
1089  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) &&
1090    forbiddenUseStateType.has(typeNode.typeName.getText())) {
1091    return true;
1092  }
1093  return false;
1094}
1095
1096export function isSimpleType(typeNode: ts.TypeNode, program: ts.Program, log?: LogInfo[]): boolean {
1097  typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
1098  let checker: ts.TypeChecker;
1099  if (globalProgram.program) {
1100    checker = globalProgram.program.getTypeChecker();
1101  } else if (globalProgram.watchProgram) {
1102    checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker();
1103  } else if (program) {
1104    checker = program.getTypeChecker();
1105  }
1106  return getDeclarationType(typeNode, checker, log);
1107}
1108
1109function getDeclarationType(typeNode: ts.TypeNode, checker: ts.TypeChecker, log: LogInfo[]): boolean {
1110  if (simpleTypes.has(typeNode.kind)) {
1111    return true;
1112  }
1113  if (ts.isTypeReferenceNode(typeNode) && typeNode.typeName && ts.isIdentifier(typeNode.typeName) &&
1114    enumCollection.has(typeNode.typeName.escapedText.toString())) {
1115    return true;
1116  }
1117  if (checker) {
1118    const type: ts.Type = checker.getTypeFromTypeNode(typeNode);
1119    /* Enum */
1120    if (type.flags & (32 | 1024)) {
1121      return true;
1122    }
1123    // @ts-ignore
1124    if (type.types && type.types.length) {
1125      // @ts-ignore
1126      const types = type.types;
1127      let referenceType: boolean = false;
1128      for (let i = 0; i < types.length; i++) {
1129        if (!isBasicType(types[i].flags)) {
1130          referenceType = true;
1131        }
1132      }
1133      if (!referenceType) {
1134        return true;
1135      }
1136    }
1137  }
1138  return false;
1139}
1140
1141export function isBasicType(flags: number): boolean {
1142  if (flags & (4 | /* String */ 8 | /* Number */ 16 | /* Boolean */ 32 | /* Enum */ 64 | /* BigInt */
1143    128 | /* StringLiteral */ 256 | /* NumberLiteral */ 512 /* BooleanLiteral */| 1024 /* EnumLiteral */|
1144    2048 /* BigIntLiteral */)) {
1145    return true;
1146  }
1147  return false;
1148}
1149
1150function isObservedClassType(type: ts.TypeNode): boolean {
1151  if (judgmentTypedeclaration(type) && observedClassCollection.has(type.typeName.escapedText.toString())) {
1152    return true;
1153  } else if (ts.isUnionTypeNode(type) && type.types) {
1154    const types: ts.NodeArray<ts.TypeNode> = type.types;
1155    for (let i = 0; i < types.length; i++) {
1156      if (judgmentTypedeclaration(types[i]) && !observedClassCollection.has(types[i].typeName.escapedText.toString())) {
1157        return false;
1158      }
1159    }
1160    return true;
1161  }
1162  return false;
1163}
1164
1165function judgmentTypedeclaration(type: ts.TypeNode): boolean {
1166  return ts.isTypeReferenceNode(type) && type.typeName && ts.isIdentifier(type.typeName);
1167}
1168
1169export function isSingleKey(node: ts.PropertyDeclaration, isOptionalKey: boolean = false): boolean {
1170  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
1171  const optionalKeyDecorator: Set<string> = new Set(['Provide', 'Consume']);
1172  return decorators.some((item: ts.Decorator) => {
1173    return ts.isCallExpression(item.expression) &&
1174      item.expression.arguments &&
1175      item.expression.arguments.length === 1 &&
1176      ts.isIdentifier(item.expression.expression) &&
1177      (!isOptionalKey || optionalKeyDecorator.has(item.expression.expression.escapedText.toString()));
1178  });
1179}
1180
1181function validateMultiDecorators(name: ts.Identifier, log: LogInfo[]): void {
1182  log.push({
1183    type: LogType.ERROR,
1184    message: `The property '${name.escapedText.toString()}' cannot have multiple state management decorators.`,
1185    pos: name.getStart(),
1186    code: '10905302'
1187  });
1188}
1189
1190function validatePropertyNonDefaultValue(propertyName: ts.Identifier, decorator: string,
1191  log: LogInfo[]): void {
1192  log.push({
1193    type: LogType.ERROR,
1194    message: `The '${decorator}' property '${propertyName.getText()}' must be specified a default value.`,
1195    pos: propertyName.getStart(),
1196    code: '10905303'
1197  });
1198}
1199
1200function validatePropertyDefaultValue(propertyName: ts.Identifier, decorator: string,
1201  log: LogInfo[]): void {
1202  log.push({
1203    type: LogType.ERROR,
1204    message: `The '${decorator}' property cannot be specified a default value.`,
1205    pos: propertyName.getStart(),
1206    code: '10905304',
1207    solutions: ['Please initialize the rules according to the decorator.']
1208  });
1209}
1210
1211function validatePropertyNonType(propertyName: ts.Identifier, log: LogInfo[]): void {
1212  log.push({
1213    type: LogType.ERROR,
1214    message: `The property '${propertyName.getText()}' must specify a type.`,
1215    pos: propertyName.getStart(),
1216    code: '10905305'
1217  });
1218}
1219
1220function validateNonSimpleType(propertyName: ts.Identifier, decorator: string,
1221  log: LogInfo[]): void {
1222  log.push({
1223    type: projectConfig.optLazyForEach ? LogType.WARN : LogType.ERROR,
1224    message: `The type of the ${decorator} property '${propertyName.getText()}' ` +
1225      `can only be string, number or boolean.`,
1226    pos: propertyName.getStart(),
1227    code: '10905306'
1228  });
1229}
1230
1231function validateNonObservedClassType(propertyName: ts.Identifier, decorator: string,
1232  log: LogInfo[]): void {
1233    log.push({
1234      type: projectConfig.optLazyForEach ? LogType.WARN : LogType.ERROR,
1235      message: `'@ObjectLink' cannot be used with this type.` +
1236        ` Apply it only to classes decorated by '@Observed' or initialized using the return value of 'makeV1Observed'.`,
1237      pos: propertyName.getStart(),
1238      code: '10905307'
1239    });
1240  }
1241
1242function validateHasIllegalQuestionToken(propertyName: ts.Identifier, decorator: string,
1243  log: LogInfo[]): void {
1244  log.push({
1245    type: LogType.WARN,
1246    message: `The ${decorator} property '${propertyName.getText()}' cannot be an optional parameter.`,
1247    pos: propertyName.getStart()
1248  });
1249}
1250
1251function validateHasIllegalDecoratorInEntry(parentName: ts.Identifier, propertyName: ts.Identifier,
1252  decorator: string, log: LogInfo[]): void {
1253  log.push({
1254    type: LogType.WARN,
1255    message: `The '@Entry' component '${parentName.getText()}' cannot have the ` +
1256      `'${decorator}' property '${propertyName.getText()}'.`,
1257    pos: propertyName.getStart()
1258  });
1259}
1260
1261function validateForbiddenUseStateType(propertyName: ts.Identifier, decorator: string, type: string,
1262  log: LogInfo[]): void {
1263  log.push({
1264    type: LogType.ERROR,
1265    message: `The '${decorator}' property '${propertyName.getText()}' cannot be a '${type}' object.`,
1266    pos: propertyName.getStart(),
1267    code: '10905308'
1268  });
1269}
1270
1271function validateDuplicateDecorator(decorator: ts.Decorator, log: LogInfo[]): void {
1272  log.push({
1273    type: LogType.ERROR,
1274    message: `The decorator '${decorator.getText()}' cannot have the same name as the built-in ` +
1275      `style attribute '${decorator.getText().replace('@', '')}'.`,
1276    pos: decorator.getStart(),
1277    code: '10905309'
1278  });
1279}
1280
1281function validateWatchDecorator(propertyName: ts.Identifier, decorators: readonly ts.Decorator[],
1282  log: LogInfo[]): boolean {
1283  let isRegular: boolean = false;
1284  if (decorators.length === DECORATOR_LENGTH) {
1285    isRegular = decorators.some((item: ts.Decorator) => {
1286      return item.getText() === COMPONENT_REQUIRE_DECORATOR;
1287    });
1288  }
1289  if (decorators.length === 1 || isRegular) {
1290    log.push({
1291      type: LogType.ERROR,
1292      message: `Regular variable '${propertyName.escapedText.toString()}' can not be decorated with '@Watch'.`,
1293      pos: propertyName.getStart(),
1294      code: '10905310'
1295    });
1296    return false;
1297  }
1298  return true;
1299}
1300
1301function validateWatchParam(type: LogType, pos: number, log: LogInfo[], paramName: string): void {
1302  log.push({
1303    type: type,
1304    message: `'@Watch' cannot be used with '${paramName}'. Apply it only to 'string' parameters.`,
1305    pos,
1306    code: '10905311'
1307  });
1308}
1309
1310function updateObservedPropertyPU(item: ts.PropertyDeclaration, name: ts.Identifier,
1311  type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement {
1312  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
1313    createPropertyAccessExpressionWithThis(`__${name.getText()}`),
1314    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression(
1315      ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE_PU :
1316        OBSERVED_PROPERTY_OBJECT_PU), undefined, [
1317          item.initializer ?? ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED),
1318          ts.factory.createThis(),
1319          ts.factory.createStringLiteral(name.escapedText.toString())
1320        ]
1321      )
1322    )
1323  );
1324}
1325
1326function updateSynchedPropertyTwoWayPU(nameIdentifier: ts.Identifier, type: ts.TypeNode,
1327  program: ts.Program): ts.ExpressionStatement {
1328  const name: string = nameIdentifier.escapedText.toString();
1329  const functionName: string = isSimpleType(type, program) ?
1330    SYNCHED_PROPERTY_SIMPLE_TWO_WAY_PU : SYNCHED_PROPERTY_OBJECT_TWO_WAY_PU;
1331  return createInitExpressionStatementForDecorator(name, functionName,
1332    createPropertyAccessExpressionWithParams(name));
1333}
1334
1335function updateSynchedPropertyOneWayPU(nameIdentifier: ts.Identifier, type: ts.TypeNode,
1336  decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement {
1337  const name: string = nameIdentifier.escapedText.toString();
1338  if (isSimpleType(type, program, log)) {
1339    return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY_PU,
1340      createPropertyAccessExpressionWithParams(name));
1341  } else {
1342    return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_OBJECT_ONE_WAY_PU,
1343      createPropertyAccessExpressionWithParams(name));
1344  }
1345}
1346
1347function updateSynchedPropertyNesedObjectPU(nameIdentifier: ts.Identifier,
1348  type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement {
1349  if (partialUpdateConfig.partialUpdateMode && projectConfig.compileMode === 'esmodule' && checkObjectLinkType(type)) {
1350    return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT_PU,
1351      createPropertyAccessExpressionWithParams(nameIdentifier.getText()));
1352  } else if ((projectConfig.compileMode !== 'esmodule' || !partialUpdateConfig.partialUpdateMode) && isObservedClassType(type)) {
1353    return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT_PU,
1354      createPropertyAccessExpressionWithParams(nameIdentifier.getText()));
1355  } else {
1356    validateNonObservedClassType(nameIdentifier, decoractor, log);
1357    return undefined;
1358  }
1359}
1360
1361function checkObjectLinkType(typeNode: ts.TypeNode): boolean {
1362  if (globalProgram.checker) {
1363    const type: ts.Type = globalProgram.checker.getTypeFromTypeNode(typeNode);
1364    if (typeNode.parent && ts.isPropertyDeclaration(typeNode.parent) && isObserved(type)) {
1365      return true;
1366    }
1367    // @ts-ignore
1368    if (type.types && type.types.length) {
1369      let observedNumber: number = 0;
1370      for (let i = 0; i < type.types.length; i++) {
1371        if (type.types[i].intrinsicName) {
1372          return false;
1373        }
1374        if (isObserved(type.types[i])) {
1375          observedNumber += 1;
1376        }
1377      }
1378      if (observedNumber === type.types.length) {
1379        return true;
1380      } else {
1381        return false;
1382      }
1383    }
1384  }
1385  return false;
1386}
1387
1388function validateCustomDecorator(decorators: readonly ts.Decorator[], log: LogInfo[]): boolean {
1389  let hasInnerDecorator: boolean = false;
1390  let hasCustomDecorator: boolean = false;
1391  let innerDecorator: ts.Decorator;
1392  for (let i = 0; i < decorators.length; i++) {
1393    const decorator: ts.Decorator = decorators[i];
1394    const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim();
1395    if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1396      hasInnerDecorator = true;
1397      innerDecorator = innerDecorator || decorator;
1398    } else {
1399      hasCustomDecorator = true;
1400    }
1401  }
1402  if (hasCustomDecorator && hasInnerDecorator) {
1403    log.push({
1404      type: LogType.ERROR,
1405      message: `The inner decorator '${innerDecorator.getText()}' cannot be used together with custom decorator.`,
1406      pos: innerDecorator.getStart(),
1407      code: '10905312'
1408    });
1409  } else if (!hasInnerDecorator) {
1410    return true;
1411  }
1412  return false;
1413}
1414
1415function validatePropDecorator(decorators: readonly ts.Decorator[]): boolean {
1416  for (let i = 0; i < decorators.length; i++) {
1417    const decorator: ts.Decorator = decorators[i];
1418    const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim();
1419    if (COMPONENT_PROP_DECORATOR === decoratorName) {
1420      return true;
1421    }
1422  }
1423  return false;
1424}
1425
1426function isObserved(type: ts.Type): boolean {
1427  if (type && type.getSymbol() && type.getSymbol().declarations) {
1428    return type.getSymbol().declarations.some((classDeclaration: ts.ClassDeclaration) => {
1429      const decorators: readonly ts.Decorator[] = ts.getAllDecorators(classDeclaration);
1430      if (ts.isClassDeclaration(classDeclaration) && decorators) {
1431        return decorators.some((decorator: ts.Decorator) => {
1432          return ts.isIdentifier(decorator.expression) && decorator.expression.escapedText.toString() === OBSERVED;
1433        });
1434      }
1435      return false;
1436    });
1437  }
1438  return false;
1439}
1440
1441export function resetProcessComponentMember(): void {
1442  decoratorParamSet.clear();
1443}
1444