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
16const ts = require('typescript');
17const path = require('path');
18const chai = require('chai');
19const mocha = require('mocha');
20const expect = chai.expect;
21const {
22  processUISyntax,
23  transformLog
24} = require('../lib/process_ui_syntax');
25const {
26  validateUISyntax,
27  preprocessExtend,
28  resetComponentCollection,
29  componentCollection
30} = require('../lib/validate_ui_syntax');
31const {
32  componentInfo,
33  readFile,
34  storedFileInfo
35} = require('../lib/utils');
36const {
37  BUILD_ON,
38  OHOS_PLUGIN,
39  NATIVE_MODULE,
40  SYSTEM_PLUGIN
41} = require('../lib/pre_define');
42const {
43  partialUpdateConfig,
44  projectConfig,
45  resources
46} = require('../main');
47const processStructComponentV2 = require('../lib/process_struct_componentV2');
48
49projectConfig.projectPath = path.resolve(process.cwd());
50
51function expectActual(name, filePath, checkError = false) {
52  transformLog.errors = [];
53  resources.app["media"] = {icon:16777222};
54  resources.app["font"] = {song:16777223};
55  process.env.rawFileResource = './';
56  process.env.compileMode = 'moduleJson';
57  const content = require(filePath);
58  const source = content.source;
59  process.env.compiler = BUILD_ON;
60  componentInfo.id = 0;
61  componentCollection.customComponents.clear();
62  resetComponentCollection();
63  storedFileInfo.setCurrentArkTsFile();
64  const afterProcess = sourceReplace(source);
65  if (checkError) {
66    transformLog.errors.push(...validateUISyntax(source, afterProcess.content, `${name}.ets`, "?entry"));
67  } else {
68    validateUISyntax(source, afterProcess.content, `${name}.ets`);
69  }
70  const compilerOptions = ts.readConfigFile(
71    path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
72  Object.assign(compilerOptions, {
73    'sourceMap': false
74  });
75  const result = ts.transpileModule(afterProcess.content, {
76    compilerOptions: compilerOptions,
77    fileName: `${name}.ets`,
78    transformers: { before: [processUISyntax(null, true)] }
79  });
80  processStructComponentV2.default.resetStructMapInEts();
81  if (checkError) {
82    assertError(name);
83  } else {
84    expect(result.outputText).eql(content.expectResult);
85  }
86}
87
88mocha.describe('compiler', () => {
89  let utPath = path.resolve(__dirname, './ut');
90  if (process.argv.includes('--partialUpdate')) {
91    partialUpdateConfig.partialUpdateMode = true;
92    utPath = path.resolve(__dirname, './utForPartialUpdate');
93  } else if (process.argv.includes('--assertError')) {
94    partialUpdateConfig.partialUpdateMode = true;
95    utPath = path.resolve(__dirname, './utForValidate');
96  }
97  const utFiles = [];
98  readFile(utPath, utFiles);
99  utFiles.forEach((item) => {
100    const fileName = path.basename(item, '.ts');
101    mocha.it(fileName, () => {
102      if (process.argv.includes('--assertError')) {
103        expectActual(fileName, item, true);
104      } else {
105        expectActual(fileName, item);
106      }
107    });
108  });
109});
110
111function sourceReplace(source) {
112  let content = source;
113  const log = [];
114  content = preprocessExtend(content);
115  content = processSystemApi(content);
116  return {
117    content: content,
118    log: log
119  };
120}
121
122function processSystemApi(content) {
123  const REG_SYSTEM =
124    /import\s+(.+)\s+from\s+['"]@(system|ohos)\.(\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"]@(system|ohos)\.(\S+)['"]\s*\)/g;
125  const REG_LIB_SO =
126    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
127  const newContent = content.replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
128    const libSoValue = item1 || item3;
129    const libSoKey = item2 || item4;
130    return `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true);`;
131  }).replace(REG_SYSTEM, (item, item1, item2, item3, item4, item5, item6, item7) => {
132    let moduleType = item2 || item5;
133    let systemKey = item3 || item6;
134    let systemValue = item1 || item4;
135    if (NATIVE_MODULE.has(`${moduleType}.${systemKey}`)) {
136      item = `var ${systemValue} = globalThis.requireNativeModule('${moduleType}.${systemKey}')`;
137    } else if (moduleType === SYSTEM_PLUGIN) {
138      item = `var ${systemValue} = isSystemplugin('${systemKey}', '${SYSTEM_PLUGIN}') ? ` +
139        `globalThis.systemplugin.${systemKey} : globalThis.requireNapi('${systemKey}')`;
140    } else if (moduleType === OHOS_PLUGIN) {
141      item = `var ${systemValue} = globalThis.requireNapi('${systemKey}') || ` +
142        `(isSystemplugin('${systemKey}', '${OHOS_PLUGIN}') ? ` +
143        `globalThis.ohosplugin.${systemKey} : isSystemplugin('${systemKey}', '${SYSTEM_PLUGIN}') ` +
144        `? globalThis.systemplugin.${systemKey} : undefined)`;
145    }
146    return item;
147  });
148  return newContent;
149}
150
151function replaceFunctions(message) {
152  const regex = /\${(.*?)}/g;
153  return message.replace(regex, (match, p1) => {
154    return eval(p1);
155  });
156}
157
158function validateError(logmsg, logtype, message, type) {
159  expect(logmsg).to.be.equal(message);
160  expect(logtype).to.be.equal(type);
161}
162
163function assertError(fileName) {
164  const errorJson = require('./error.json');
165  const errorInfo = errorJson[fileName];
166  if (errorInfo) {
167    if (Array.isArray(errorInfo)) {
168      errorInfo.forEach((item, index) => {
169        validateError(transformLog.errors[index].message, transformLog.errors[index].type, replaceFunctions(item.message), item.type);
170      });
171    } else {
172      validateError(transformLog.errors[0].message, transformLog.errors[0].type, replaceFunctions(errorInfo.message), errorInfo.type);
173    }
174  }
175}
176