• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 MagicString from 'magic-string';
17import { createFilter } from '@rollup/pluginutils';
18import path from 'path';
19import {
20  NATIVE_MODULE,
21  ARKUI_X_PLUGIN,
22  GLOBAL_THIS_REQUIRE_NATIVE_MODULE,
23  GLOBAL_THIS_REQUIRE_NAPI
24} from '../../pre_define';
25import {
26  systemModules,
27  projectConfig,
28  sdkConfigs,
29  sdkConfigPrefix,
30  extendSdkConfigs
31} from '../../../main';
32
33import {
34  writeUseOSFiles,
35  writeCollectionFile,
36  getAllComponentsOrModules
37} from '../../utils';
38import { hasTsNoCheckOrTsIgnoreFiles } from "../../process_kit_import";
39
40const filter: any = createFilter(/(?<!\.d)\.(ets|ts|js)$/);
41const allFiles: Set<string> = new Set();
42
43export const appImportModuleCollection: Map<string, Set<string>> = new Map();
44export const kitModules: Map<string, Map<string, Set<string>>> = new Map();
45
46export function apiTransform() {
47  const useOSFiles: Set<string> = new Set();
48  let hiresStatus: boolean = true;
49  return {
50    name: 'apiTransform',
51    load(id: string) {
52      allFiles.add(path.join(id));
53    },
54    transform(code: string, id: string) {
55      const magicString = new MagicString(code);
56      if (filter(id)) {
57        if (projectConfig.compileMode === "esmodule") {
58          code = processSystemApiAndLibso(code, id, useOSFiles);
59          hiresStatus = this.share.projectConfig.needCompleteSourcesMap || hasTsNoCheckOrTsIgnoreFiles.includes(id) ?
60            true :
61            false;
62        } else {
63          code = processSystemApi(code, id);
64          code = processLibso(code, id, useOSFiles);
65          hiresStatus = true;
66        }
67      }
68      return {
69        code: code,
70        map: magicString.generateMap({ hires: hiresStatus })
71      };
72    },
73    beforeBuildEnd() {
74      this.share.allComponents = getAllComponentsOrModules(allFiles, 'component_collection.json');
75      this.share.allFiles = allFiles;
76    },
77    buildEnd() {
78      if (projectConfig.isPreview && projectConfig.aceSoPath &&
79        useOSFiles && useOSFiles.size > 0) {
80        writeUseOSFiles(useOSFiles);
81      }
82      if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
83        replaceKitModules();
84        const allModules: Map<string, Array<string>> = getAllComponentsOrModules(allFiles, 'module_collection.json');
85        writeCollectionFile(projectConfig.cachePath, appImportModuleCollection, allModules, 'module_collection.json');
86      }
87    },
88    cleanUp(): void {
89      allFiles.clear();
90      appImportModuleCollection.clear();
91      useOSFiles.clear();
92      kitModules.clear();
93    }
94  };
95}
96
97
98function processSystemApi(content: string, sourcePath: string): string {
99  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
100  const REG_SYSTEM: RegExp =
101    new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]|import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
102  appImportModuleCollection.set(path.join(sourcePath), new Set());
103  return content.replace(REG_SYSTEM, (item, item1, item2, item3, item4, item5, item6) => {
104    const moduleType: string = item2 || item5;
105    const systemKey: string = item3 || item6;
106    const systemValue: string = item1 || item4;
107    const systemModule: string = `${moduleType}.${systemKey}`;
108    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
109    checkModuleExist(systemModule, sourcePath);
110    const externalModuleParam: string = isExtendModuleType(moduleType) &&
111      moduleType !== ARKUI_X_PLUGIN ? `, false, '', '${moduleType}'` : ``;
112    if (NATIVE_MODULE.has(systemModule)) {
113      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NATIVE_MODULE}('${moduleType}.${systemKey}')`;
114    } else if (checkModuleType(moduleType)) {
115      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NAPI}('${systemKey}'${externalModuleParam})`;
116    }
117    return item;
118  });
119}
120
121interface SdkConfig {
122  apiPath: string;
123  prefix: string;
124}
125
126function isExtendModuleType(moduleType: string): boolean {
127  for (let i = 0; i < extendSdkConfigs.length; i++) {
128    const config: SdkConfig = extendSdkConfigs[i];
129    if (config.prefix === `@${moduleType}`) {
130      return true;
131    }
132  }
133  return false;
134}
135
136function checkModuleType(moduleType: string): boolean {
137  for (let i = 0; i < sdkConfigs.length; i++) {
138    const config = sdkConfigs[i];
139    if (config.prefix === `@${moduleType}`) {
140      return true;
141    }
142  }
143  return false;
144}
145
146function checkModuleExist(systemModule: string, sourcePath: string): void {
147  const module: string = `@${systemModule.trim()}.d.ts`;
148  if (/\.js$/.test(sourcePath) && !systemModules.includes(module)) {
149    const message: string =
150      `Cannot find module '${module}' or its corresponding type declarations.`;
151    console.error(`BUILDERROR File: ${sourcePath}\n ${message}`);
152  }
153}
154
155function processLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
156  const REG_LIB_SO: RegExp =
157    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
158  return content.replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
159    useOSFiles.add(sourcePath);
160    const libSoValue: string = item1 || item3;
161    const libSoKey: string = item2 || item4;
162    return projectConfig.bundleName && projectConfig.moduleName
163      ? `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");`
164      : `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true);`;
165  });
166}
167
168// It is rare to use `import xxx = require('module')` for system module and user native library,
169// Here keep tackling with this for compatibility concern.
170function processSystemApiAndLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
171  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
172  const REG_REQUIRE_SYSTEM: RegExp = new RegExp(`import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
173  // Import libso should be recored in useOSFiles.
174  const REG_LIB_SO: RegExp =
175    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
176  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
177  const REG_IMPORT_SYSTEM = new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]`, 'g');
178  appImportModuleCollection.set(path.join(sourcePath), new Set());
179  content.replace(REG_IMPORT_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
180    const moduleType: string = item2 || item5;
181    const systemKey: string = item3 || item6;
182    const systemValue: string = item1 || item4;
183    const systemModule: string = `${moduleType}.${systemKey}`;
184    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
185    return _;
186  });
187  return content.replace(REG_REQUIRE_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
188    const moduleType: string = item2 || item5;
189    const systemKey: string = item3 || item6;
190    const systemValue: string = item1 || item4;
191    const systemModule: string = `${moduleType}.${systemKey}`;
192    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
193    checkModuleExist(systemModule, sourcePath);
194    return `import ${systemValue} from '@${moduleType}.${systemKey}'`;
195  }).replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
196    useOSFiles.add(sourcePath);
197    const libSoValue: string = item1 || item3;
198    const libSoKey: string = item2 || item4;
199    return `import ${libSoValue} from 'lib${libSoKey}.so'`;
200  });
201}
202
203export function collectKitModules(fileName: string, key: string, value: string): void {
204  key = key.replace('@', '');
205  value = value.replace('@', '');
206  const currentValue: Map<string, Set<string>> = kitModules.get(fileName);
207  if (currentValue) {
208    const currentModuleName: Set<string> = currentValue.get(key);
209    if (currentModuleName && !currentModuleName.has(value)) {
210      currentModuleName.add(value);
211      currentValue.set(key, currentModuleName);
212    } else if (!currentModuleName) {
213      const moduleSet: Set<string> = new Set();
214      currentValue.set(key, moduleSet.add(value));
215    }
216  } else {
217    const moduleMap: Map<string, Set<string>> = new Map();
218    const moduleSet: Set<string> = new Set();
219    moduleMap.set(key, moduleSet.add(value));
220    kitModules.set(fileName, moduleMap);
221  }
222}
223
224function compareKitModules(value: Set<string>, kitKey: string, kitValue: Map<string, Set<string>>): void {
225  const realModules: Set<string> = new Set();
226  value.forEach((element: string) => {
227    if (kitValue.get(element)) {
228      kitValue.get(element).forEach((kitModuleRequest: string) => {
229        realModules.add(kitModuleRequest.replace(/\.d\.e?ts$/, ''));
230      });
231    } else {
232      realModules.add(element);
233    }
234  });
235  appImportModuleCollection.set(kitKey, realModules);
236}
237
238function replaceKitModules(): void {
239  appImportModuleCollection.forEach((value: Set<string>, key: string) => {
240    kitModules.forEach((kitValue: Map<string, Set<string>>, kitKey: string) => {
241      if (key === kitKey && value && value.size > 0) {
242        compareKitModules(value, kitKey, kitValue);
243      }
244    });
245  });
246}
247