1/*
2 * Copyright (c) 2023 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';
17import { projectConfig } from '../../../main';
18import { DECORATOR_SUFFIX } from '../../pre_define';
19
20export function disableMockDecorator(node: ts.Decorator): boolean {
21  if (!shouldDisableMockDecorator()) {
22    return false;
23  }
24
25  let parent: ts.Node = node.parent;
26  switch (parent.kind) {
27    case ts.SyntaxKind.Parameter: {
28      ts.factory.updateParameterDeclaration(<ts.ParameterDeclaration>parent,
29        ts.getModifiers((<ts.ParameterDeclaration>parent)),
30        (<ts.ParameterDeclaration>parent).dotDotDotToken,
31        (<ts.ParameterDeclaration>parent).name,
32        (<ts.ParameterDeclaration>parent).questionToken,
33        (<ts.ParameterDeclaration>parent).type,
34        (<ts.ParameterDeclaration>parent).initializer);
35      break;
36    }
37    case ts.SyntaxKind.MethodDeclaration: {
38      ts.factory.updateMethodDeclaration(<ts.MethodDeclaration>parent,
39        ts.getModifiers((<ts.MethodDeclaration>parent)),
40        (<ts.MethodDeclaration>parent).asteriskToken,
41        (<ts.MethodDeclaration>parent).name,
42        (<ts.MethodDeclaration>parent).questionToken,
43        (<ts.MethodDeclaration>parent).typeParameters,
44        (<ts.MethodDeclaration>parent).parameters,
45        (<ts.MethodDeclaration>parent).type,
46        (<ts.MethodDeclaration>parent).body);
47      break;
48    }
49    case ts.SyntaxKind.Constructor: {
50      ts.factory.updateConstructorDeclaration(<ts.ConstructorDeclaration>parent,
51        ts.getModifiers((<ts.ConstructorDeclaration>parent)),
52        (<ts.ConstructorDeclaration>parent).parameters,
53        (<ts.ConstructorDeclaration>parent).body);
54      break;
55    }
56    case ts.SyntaxKind.GetAccessor: {
57      ts.factory.updateGetAccessorDeclaration(<ts.GetAccessorDeclaration>parent,
58        ts.getModifiers((<ts.GetAccessorDeclaration>parent)),
59        (<ts.GetAccessorDeclaration>parent).name,
60        (<ts.GetAccessorDeclaration>parent).parameters,
61        (<ts.GetAccessorDeclaration>parent).type,
62        (<ts.GetAccessorDeclaration>parent).body);
63      break;
64    }
65    case ts.SyntaxKind.SetAccessor: {
66      ts.factory.updateSetAccessorDeclaration(<ts.SetAccessorDeclaration>parent,
67        ts.getModifiers((<ts.SetAccessorDeclaration>parent)),
68        (<ts.SetAccessorDeclaration>parent).name,
69        (<ts.SetAccessorDeclaration>parent).parameters,
70        (<ts.SetAccessorDeclaration>parent).body);
71      break;
72    }
73    case ts.SyntaxKind.PropertyDeclaration: {
74      if ((<ts.PropertyDeclaration>parent).questionToken) {
75        ts.factory.updatePropertyDeclaration(<ts.PropertyDeclaration>parent,
76          ts.getModifiers((<ts.PropertyDeclaration>parent)),
77          (<ts.PropertyDeclaration>parent).name,
78          (<ts.PropertyDeclaration>parent).questionToken,
79          (<ts.PropertyDeclaration>parent).type,
80          (<ts.PropertyDeclaration>parent).initializer);
81      } else if ((<ts.PropertyDeclaration>parent).exclamationToken) {
82        ts.factory.updatePropertyDeclaration(<ts.PropertyDeclaration>parent,
83          ts.getModifiers((<ts.PropertyDeclaration>parent)),
84          (<ts.PropertyDeclaration>parent).name,
85          (<ts.PropertyDeclaration>parent).exclamationToken,
86          (<ts.PropertyDeclaration>parent).type,
87          (<ts.PropertyDeclaration>parent).initializer);
88      } else {
89        ts.factory.updatePropertyDeclaration(<ts.PropertyDeclaration>parent,
90          ts.getModifiers((<ts.PropertyDeclaration>parent)),
91          (<ts.PropertyDeclaration>parent).name,
92          undefined,
93          (<ts.PropertyDeclaration>parent).type,
94          (<ts.PropertyDeclaration>parent).initializer);
95      }
96      break;
97    }
98    case ts.SyntaxKind.ClassDeclaration: {
99      ts.factory.updateClassDeclaration(<ts.ClassDeclaration>parent,
100        ts.getModifiers((<ts.ClassDeclaration>parent)),
101        (<ts.ClassDeclaration>parent).name,
102        (<ts.ClassDeclaration>parent).typeParameters,
103        (<ts.ClassDeclaration>parent).heritageClauses,
104        (<ts.ClassDeclaration>parent).members);
105      break;
106    }
107    default: {
108      break;
109    }
110  }
111  return true;
112}
113
114function removeMockDecorator(decs: readonly ts.Decorator[]): ts.Decorator[] {
115  let res: ts.Decorator[];
116  for (let dec of decs) {
117    if (!isMockDecorator(dec)) {
118      res.push(dec);
119    }
120  }
121
122  return res;
123}
124
125export function isMockDecorator(dec: ts.Decorator): boolean {
126  let decObj = dec.expression;
127  if (!ts.isIdentifier(decObj)) {
128    return false;
129  }
130
131  if (projectConfig.mockParams) {
132    let mockDecorator: string = projectConfig.mockParams.decorator.replace(DECORATOR_SUFFIX, '');
133    return ((<ts.Identifier>decObj).escapedText.toString() === mockDecorator);
134  }
135
136  return false;
137}
138
139function shouldDisableMockDecorator(): boolean {
140  // mock decorator only takes effect under preview mode, should be removed otherswise
141  if (projectConfig.isPreview) {
142    return false;
143  }
144
145  // mockParams = {
146  //   "decorator": "name of mock decorator",
147  //   "packageName": "name of mock package",
148  //   "etsSourceRootPath": "path of ets source root",
149  //   "mockConfigPath": "path of mock configuration file"
150  // }
151  return (projectConfig.mockParams && projectConfig.mockParams.decorator && projectConfig.mockParams.packageName) ?
152    true : false;
153}
154
155export const ORIGIN_EXTENTION: string = '.origin';