• 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';
38
39const filter: any = createFilter(/(?<!\.d)\.(ets|ts|js)$/);
40const allFiles: Set<string> = new Set();
41
42export const appImportModuleCollection: Map<string, Set<string>> = new Map();
43
44export function apiTransform() {
45  const useOSFiles: Set<string> = new Set();
46  return {
47    name: 'apiTransform',
48    load(id: string) {
49      allFiles.add(path.join(id));
50    },
51    transform(code: string, id: string) {
52      const magicString = new MagicString(code);
53      if (filter(id)) {
54        if (projectConfig.compileMode === "esmodule") {
55          code = processSystemApiAndLibso(code, id, useOSFiles);
56        } else {
57          code = processSystemApi(code, id);
58          code = processLibso(code, id, useOSFiles);
59        }
60      }
61      return {
62        code: code,
63        map: magicString.generateMap({ hires: true })
64      };
65    },
66    beforeBuildEnd() {
67      this.share.allComponents = getAllComponentsOrModules(allFiles, 'component_collection.json');
68      this.share.allFiles = allFiles;
69    },
70    buildEnd() {
71      if (projectConfig.isPreview && projectConfig.aceSoPath &&
72        useOSFiles && useOSFiles.size > 0) {
73        writeUseOSFiles(useOSFiles);
74      }
75      if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
76        const allModules: Map<string, Array<string>> = getAllComponentsOrModules(allFiles, 'module_collection.json');
77        writeCollectionFile(projectConfig.cachePath, appImportModuleCollection, allModules, 'module_collection.json');
78      }
79    }
80  };
81}
82
83
84function processSystemApi(content: string, sourcePath: string): string {
85  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
86  const REG_SYSTEM: RegExp =
87    new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]|import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
88  appImportModuleCollection.set(path.join(sourcePath), new Set());
89  return content.replace(REG_SYSTEM, (item, item1, item2, item3, item4, item5, item6) => {
90    const moduleType: string = item2 || item5;
91    const systemKey: string = item3 || item6;
92    const systemValue: string = item1 || item4;
93    const systemModule: string = `${moduleType}.${systemKey}`;
94    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
95    checkModuleExist(systemModule, sourcePath);
96    const externalModuleParam: string = isExtendModuleType(moduleType) &&
97      moduleType !== ARKUI_X_PLUGIN ? `, false, '', '${moduleType}'` : ``;
98    if (NATIVE_MODULE.has(systemModule)) {
99      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NATIVE_MODULE}('${moduleType}.${systemKey}')`;
100    } else if (checkModuleType(moduleType)) {
101      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NAPI}('${systemKey}'${externalModuleParam})`;
102    }
103    return item;
104  });
105}
106
107interface SdkConfig {
108  apiPath: string;
109  prefix: string;
110}
111
112function isExtendModuleType(moduleType: string): boolean {
113  for (let i = 0; i < extendSdkConfigs.length; i++) {
114    const config: SdkConfig = extendSdkConfigs[i];
115    if (config.prefix === `@${moduleType}`) {
116      return true;
117    }
118  }
119  return false;
120}
121
122function checkModuleType(moduleType: string): boolean {
123  for (let i = 0; i < sdkConfigs.length; i++) {
124    const config = sdkConfigs[i];
125    if (config.prefix === `@${moduleType}`) {
126      return true;
127    }
128  }
129  return false;
130}
131
132function checkModuleExist(systemModule: string, sourcePath: string): void {
133  const module: string = `@${systemModule.trim()}.d.ts`;
134  if (/\.js$/.test(sourcePath) && !systemModules.includes(module)) {
135    const message: string =
136      `Cannot find module '${module}' or its corresponding type declarations.`;
137    console.error(`BUILDERROR File: ${sourcePath}\n ${message}`);
138  }
139}
140
141function processLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
142  const REG_LIB_SO: RegExp =
143    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
144  return content.replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
145    useOSFiles.add(sourcePath);
146    const libSoValue: string = item1 || item3;
147    const libSoKey: string = item2 || item4;
148    return projectConfig.bundleName && projectConfig.moduleName
149      ? `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");`
150      : `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true);`;
151  });
152}
153
154// It is rare to use `import xxx = require('module')` for system module and user native library,
155// Here keep tackling with this for compatibility concern.
156function processSystemApiAndLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
157  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
158  const REG_REQUIRE_SYSTEM: RegExp = new RegExp(`import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
159  // Import libso should be recored in useOSFiles.
160  const REG_LIB_SO: RegExp =
161    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
162  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
163  const REG_IMPORT_SYSTEM = new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]`, 'g');
164  appImportModuleCollection.set(path.join(sourcePath), new Set());
165  content.replace(REG_IMPORT_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
166    const moduleType: string = item2 || item5;
167    const systemKey: string = item3 || item6;
168    const systemValue: string = item1 || item4;
169    const systemModule: string = `${moduleType}.${systemKey}`;
170    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
171    return _;
172  });
173  return content.replace(REG_REQUIRE_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
174    const moduleType: string = item2 || item5;
175    const systemKey: string = item3 || item6;
176    const systemValue: string = item1 || item4;
177    const systemModule: string = `${moduleType}.${systemKey}`;
178    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
179    checkModuleExist(systemModule, sourcePath);
180    return `import ${systemValue} from '@${moduleType}.${systemKey}'`;
181  }).replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
182    useOSFiles.add(sourcePath);
183    const libSoValue: string = item1 || item3;
184    const libSoKey: string = item2 || item4;
185    return `import ${libSoValue} from 'lib${libSoKey}.so'`;
186  });
187}