107ac75b1Sopenharmony_ci/*
207ac75b1Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
307ac75b1Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
407ac75b1Sopenharmony_ci * you may not use this file except in compliance with the License.
507ac75b1Sopenharmony_ci * You may obtain a copy of the License at
607ac75b1Sopenharmony_ci *
707ac75b1Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
807ac75b1Sopenharmony_ci *
907ac75b1Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1007ac75b1Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1107ac75b1Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1207ac75b1Sopenharmony_ci * See the License for the specific language governing permissions and
1307ac75b1Sopenharmony_ci * limitations under the License.
1407ac75b1Sopenharmony_ci */
1507ac75b1Sopenharmony_ci
1607ac75b1Sopenharmony_ciimport MagicString from 'magic-string';
1707ac75b1Sopenharmony_ciimport { createFilter } from '@rollup/pluginutils';
1807ac75b1Sopenharmony_ciimport path from 'path';
1907ac75b1Sopenharmony_ciimport {
2007ac75b1Sopenharmony_ci  NATIVE_MODULE,
2107ac75b1Sopenharmony_ci  ARKUI_X_PLUGIN,
2207ac75b1Sopenharmony_ci  GLOBAL_THIS_REQUIRE_NATIVE_MODULE,
2307ac75b1Sopenharmony_ci  GLOBAL_THIS_REQUIRE_NAPI
2407ac75b1Sopenharmony_ci} from '../../pre_define';
2507ac75b1Sopenharmony_ciimport {
2607ac75b1Sopenharmony_ci  systemModules,
2707ac75b1Sopenharmony_ci  projectConfig,
2807ac75b1Sopenharmony_ci  sdkConfigs,
2907ac75b1Sopenharmony_ci  sdkConfigPrefix,
3007ac75b1Sopenharmony_ci  extendSdkConfigs
3107ac75b1Sopenharmony_ci} from '../../../main';
3207ac75b1Sopenharmony_ci
3307ac75b1Sopenharmony_ciimport {
3407ac75b1Sopenharmony_ci  writeUseOSFiles,
3507ac75b1Sopenharmony_ci  writeCollectionFile,
3607ac75b1Sopenharmony_ci  getAllComponentsOrModules
3707ac75b1Sopenharmony_ci} from '../../utils';
3807ac75b1Sopenharmony_ciimport { hasTsNoCheckOrTsIgnoreFiles } from '../ark_compiler/utils';
3907ac75b1Sopenharmony_ci
4007ac75b1Sopenharmony_ciconst filter: any = createFilter(/(?<!\.d)\.(ets|ts|js)$/);
4107ac75b1Sopenharmony_ciconst allFiles: Set<string> = new Set();
4207ac75b1Sopenharmony_ci
4307ac75b1Sopenharmony_ciexport const appImportModuleCollection: Map<string, Set<string>> = new Map();
4407ac75b1Sopenharmony_ciexport const kitModules: Map<string, Map<string, Set<string>>> = new Map();
4507ac75b1Sopenharmony_ci
4607ac75b1Sopenharmony_ciexport function apiTransform() {
4707ac75b1Sopenharmony_ci  const useOSFiles: Set<string> = new Set();
4807ac75b1Sopenharmony_ci  let hiresStatus: boolean = true;
4907ac75b1Sopenharmony_ci  return {
5007ac75b1Sopenharmony_ci    name: 'apiTransform',
5107ac75b1Sopenharmony_ci    load(id: string) {
5207ac75b1Sopenharmony_ci      allFiles.add(path.join(id));
5307ac75b1Sopenharmony_ci    },
5407ac75b1Sopenharmony_ci    transform(code: string, id: string) {
5507ac75b1Sopenharmony_ci      const magicString = new MagicString(code);
5607ac75b1Sopenharmony_ci      if (filter(id)) {
5707ac75b1Sopenharmony_ci        if (projectConfig.compileMode === 'esmodule') {
5807ac75b1Sopenharmony_ci          code = processSystemApiAndLibso(code, id, useOSFiles);
5907ac75b1Sopenharmony_ci          hiresStatus = this.share.projectConfig.needCompleteSourcesMap || hasTsNoCheckOrTsIgnoreFiles.includes(id) ?
6007ac75b1Sopenharmony_ci            true :
6107ac75b1Sopenharmony_ci            false;
6207ac75b1Sopenharmony_ci        } else {
6307ac75b1Sopenharmony_ci          code = processSystemApi(code, id);
6407ac75b1Sopenharmony_ci          code = processLibso(code, id, useOSFiles);
6507ac75b1Sopenharmony_ci          hiresStatus = true;
6607ac75b1Sopenharmony_ci        }
6707ac75b1Sopenharmony_ci      }
6807ac75b1Sopenharmony_ci      return {
6907ac75b1Sopenharmony_ci        code: code,
7007ac75b1Sopenharmony_ci        map: magicString.generateMap({ hires: hiresStatus })
7107ac75b1Sopenharmony_ci      };
7207ac75b1Sopenharmony_ci    },
7307ac75b1Sopenharmony_ci    beforeBuildEnd() {
7407ac75b1Sopenharmony_ci      this.share.allComponents = getAllComponentsOrModules(allFiles, 'component_collection.json');
7507ac75b1Sopenharmony_ci      this.share.allFiles = allFiles;
7607ac75b1Sopenharmony_ci    },
7707ac75b1Sopenharmony_ci    buildEnd() {
7807ac75b1Sopenharmony_ci      if (projectConfig.isPreview && projectConfig.aceSoPath &&
7907ac75b1Sopenharmony_ci        useOSFiles && useOSFiles.size > 0) {
8007ac75b1Sopenharmony_ci        writeUseOSFiles(useOSFiles);
8107ac75b1Sopenharmony_ci      }
8207ac75b1Sopenharmony_ci      if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
8307ac75b1Sopenharmony_ci        replaceKitModules();
8407ac75b1Sopenharmony_ci        const allModules: Map<string, Array<string>> = getAllComponentsOrModules(allFiles, 'module_collection.json');
8507ac75b1Sopenharmony_ci        writeCollectionFile(projectConfig.cachePath, appImportModuleCollection, allModules, 'module_collection.json');
8607ac75b1Sopenharmony_ci      }
8707ac75b1Sopenharmony_ci    },
8807ac75b1Sopenharmony_ci    cleanUp(): void {
8907ac75b1Sopenharmony_ci      allFiles.clear();
9007ac75b1Sopenharmony_ci      appImportModuleCollection.clear();
9107ac75b1Sopenharmony_ci      useOSFiles.clear();
9207ac75b1Sopenharmony_ci      kitModules.clear();
9307ac75b1Sopenharmony_ci    }
9407ac75b1Sopenharmony_ci  };
9507ac75b1Sopenharmony_ci}
9607ac75b1Sopenharmony_ci
9707ac75b1Sopenharmony_ci
9807ac75b1Sopenharmony_cifunction processSystemApi(content: string, sourcePath: string): string {
9907ac75b1Sopenharmony_ci  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
10007ac75b1Sopenharmony_ci  const REG_SYSTEM: RegExp =
10107ac75b1Sopenharmony_ci    new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]|import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
10207ac75b1Sopenharmony_ci  appImportModuleCollection.set(path.join(sourcePath), new Set());
10307ac75b1Sopenharmony_ci  return content.replace(REG_SYSTEM, (item, item1, item2, item3, item4, item5, item6) => {
10407ac75b1Sopenharmony_ci    const moduleType: string = item2 || item5;
10507ac75b1Sopenharmony_ci    const systemKey: string = item3 || item6;
10607ac75b1Sopenharmony_ci    const systemValue: string = item1 || item4;
10707ac75b1Sopenharmony_ci    const systemModule: string = `${moduleType}.${systemKey}`;
10807ac75b1Sopenharmony_ci    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
10907ac75b1Sopenharmony_ci    checkModuleExist(systemModule, sourcePath);
11007ac75b1Sopenharmony_ci    const externalModuleParam: string = isExtendModuleType(moduleType) &&
11107ac75b1Sopenharmony_ci      moduleType !== ARKUI_X_PLUGIN ? `, false, '', '${moduleType}'` : ``;
11207ac75b1Sopenharmony_ci    if (NATIVE_MODULE.has(systemModule)) {
11307ac75b1Sopenharmony_ci      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NATIVE_MODULE}('${moduleType}.${systemKey}')`;
11407ac75b1Sopenharmony_ci    } else if (checkModuleType(moduleType)) {
11507ac75b1Sopenharmony_ci      item = `var ${systemValue} = ${GLOBAL_THIS_REQUIRE_NAPI}('${systemKey}'${externalModuleParam})`;
11607ac75b1Sopenharmony_ci    }
11707ac75b1Sopenharmony_ci    return item;
11807ac75b1Sopenharmony_ci  });
11907ac75b1Sopenharmony_ci}
12007ac75b1Sopenharmony_ci
12107ac75b1Sopenharmony_ciinterface SdkConfig {
12207ac75b1Sopenharmony_ci  apiPath: string;
12307ac75b1Sopenharmony_ci  prefix: string;
12407ac75b1Sopenharmony_ci}
12507ac75b1Sopenharmony_ci
12607ac75b1Sopenharmony_cifunction isExtendModuleType(moduleType: string): boolean {
12707ac75b1Sopenharmony_ci  for (let i = 0; i < extendSdkConfigs.length; i++) {
12807ac75b1Sopenharmony_ci    const config: SdkConfig = extendSdkConfigs[i];
12907ac75b1Sopenharmony_ci    if (config.prefix === `@${moduleType}`) {
13007ac75b1Sopenharmony_ci      return true;
13107ac75b1Sopenharmony_ci    }
13207ac75b1Sopenharmony_ci  }
13307ac75b1Sopenharmony_ci  return false;
13407ac75b1Sopenharmony_ci}
13507ac75b1Sopenharmony_ci
13607ac75b1Sopenharmony_cifunction checkModuleType(moduleType: string): boolean {
13707ac75b1Sopenharmony_ci  for (let i = 0; i < sdkConfigs.length; i++) {
13807ac75b1Sopenharmony_ci    const config = sdkConfigs[i];
13907ac75b1Sopenharmony_ci    if (config.prefix === `@${moduleType}`) {
14007ac75b1Sopenharmony_ci      return true;
14107ac75b1Sopenharmony_ci    }
14207ac75b1Sopenharmony_ci  }
14307ac75b1Sopenharmony_ci  return false;
14407ac75b1Sopenharmony_ci}
14507ac75b1Sopenharmony_ci
14607ac75b1Sopenharmony_cifunction checkModuleExist(systemModule: string, sourcePath: string): void {
14707ac75b1Sopenharmony_ci  const module: string = `@${systemModule.trim()}.d.ts`;
14807ac75b1Sopenharmony_ci  if (/\.js$/.test(sourcePath) && !systemModules.includes(module)) {
14907ac75b1Sopenharmony_ci    const message: string =
15007ac75b1Sopenharmony_ci      `Cannot find module '${module}' or its corresponding type declarations.`;
15107ac75b1Sopenharmony_ci    console.error(`BUILDERROR File: ${sourcePath}\n ${message}`);
15207ac75b1Sopenharmony_ci  }
15307ac75b1Sopenharmony_ci}
15407ac75b1Sopenharmony_ci
15507ac75b1Sopenharmony_cifunction processLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
15607ac75b1Sopenharmony_ci  const REG_LIB_SO: RegExp =
15707ac75b1Sopenharmony_ci    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
15807ac75b1Sopenharmony_ci  return content.replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
15907ac75b1Sopenharmony_ci    useOSFiles.add(sourcePath);
16007ac75b1Sopenharmony_ci    const libSoValue: string = item1 || item3;
16107ac75b1Sopenharmony_ci    const libSoKey: string = item2 || item4;
16207ac75b1Sopenharmony_ci    return projectConfig.bundleName && projectConfig.moduleName
16307ac75b1Sopenharmony_ci      ? `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");`
16407ac75b1Sopenharmony_ci      : `var ${libSoValue} = globalThis.requireNapi("${libSoKey}", true);`;
16507ac75b1Sopenharmony_ci  });
16607ac75b1Sopenharmony_ci}
16707ac75b1Sopenharmony_ci
16807ac75b1Sopenharmony_ci// It is rare to use `import xxx = require('module')` for system module and user native library,
16907ac75b1Sopenharmony_ci// Here keep tackling with this for compatibility concern.
17007ac75b1Sopenharmony_cifunction processSystemApiAndLibso(content: string, sourcePath: string, useOSFiles: Set<string>): string {
17107ac75b1Sopenharmony_ci  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
17207ac75b1Sopenharmony_ci  const REG_REQUIRE_SYSTEM: RegExp = new RegExp(`import\\s+(.+)\\s*=\\s*require\\(\\s*['"]@(${sdkConfigPrefix})\\.(\\S+)['"]\\s*\\)`, 'g');
17307ac75b1Sopenharmony_ci  // Import libso should be recored in useOSFiles.
17407ac75b1Sopenharmony_ci  const REG_LIB_SO: RegExp =
17507ac75b1Sopenharmony_ci    /import\s+(.+)\s+from\s+['"]lib(\S+)\.so['"]|import\s+(.+)\s*=\s*require\(\s*['"]lib(\S+)\.so['"]\s*\)/g;
17607ac75b1Sopenharmony_ci  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
17707ac75b1Sopenharmony_ci  const REG_IMPORT_SYSTEM = new RegExp(`import\\s+(.+)\\s+from\\s+['"]@(${sdkConfigPrefix})\\.(\\S+)['"]`, 'g');
17807ac75b1Sopenharmony_ci  appImportModuleCollection.set(path.join(sourcePath), new Set());
17907ac75b1Sopenharmony_ci  content.replace(REG_IMPORT_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
18007ac75b1Sopenharmony_ci    const moduleType: string = item2 || item5;
18107ac75b1Sopenharmony_ci    const systemKey: string = item3 || item6;
18207ac75b1Sopenharmony_ci    const systemValue: string = item1 || item4;
18307ac75b1Sopenharmony_ci    const systemModule: string = `${moduleType}.${systemKey}`;
18407ac75b1Sopenharmony_ci    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
18507ac75b1Sopenharmony_ci    return _;
18607ac75b1Sopenharmony_ci  });
18707ac75b1Sopenharmony_ci  return content.replace(REG_REQUIRE_SYSTEM, (_, item1, item2, item3, item4, item5, item6) => {
18807ac75b1Sopenharmony_ci    const moduleType: string = item2 || item5;
18907ac75b1Sopenharmony_ci    const systemKey: string = item3 || item6;
19007ac75b1Sopenharmony_ci    const systemValue: string = item1 || item4;
19107ac75b1Sopenharmony_ci    const systemModule: string = `${moduleType}.${systemKey}`;
19207ac75b1Sopenharmony_ci    appImportModuleCollection.get(path.join(sourcePath)).add(systemModule);
19307ac75b1Sopenharmony_ci    checkModuleExist(systemModule, sourcePath);
19407ac75b1Sopenharmony_ci    return `import ${systemValue} from '@${moduleType}.${systemKey}'`;
19507ac75b1Sopenharmony_ci  }).replace(REG_LIB_SO, (_, item1, item2, item3, item4) => {
19607ac75b1Sopenharmony_ci    useOSFiles.add(sourcePath);
19707ac75b1Sopenharmony_ci    const libSoValue: string = item1 || item3;
19807ac75b1Sopenharmony_ci    const libSoKey: string = item2 || item4;
19907ac75b1Sopenharmony_ci    return `import ${libSoValue} from 'lib${libSoKey}.so'`;
20007ac75b1Sopenharmony_ci  });
20107ac75b1Sopenharmony_ci}
20207ac75b1Sopenharmony_ci
20307ac75b1Sopenharmony_ciexport function collectKitModules(fileName: string, key: string, value: string): void {
20407ac75b1Sopenharmony_ci  key = key.replace('@', '');
20507ac75b1Sopenharmony_ci  value = value.replace('@', '');
20607ac75b1Sopenharmony_ci  const currentValue: Map<string, Set<string>> = kitModules.get(fileName);
20707ac75b1Sopenharmony_ci  if (currentValue) {
20807ac75b1Sopenharmony_ci    const currentModuleName: Set<string> = currentValue.get(key);
20907ac75b1Sopenharmony_ci    if (currentModuleName && !currentModuleName.has(value)) {
21007ac75b1Sopenharmony_ci      currentModuleName.add(value);
21107ac75b1Sopenharmony_ci      currentValue.set(key, currentModuleName);
21207ac75b1Sopenharmony_ci    } else if (!currentModuleName) {
21307ac75b1Sopenharmony_ci      const moduleSet: Set<string> = new Set();
21407ac75b1Sopenharmony_ci      currentValue.set(key, moduleSet.add(value));
21507ac75b1Sopenharmony_ci    }
21607ac75b1Sopenharmony_ci  } else {
21707ac75b1Sopenharmony_ci    const moduleMap: Map<string, Set<string>> = new Map();
21807ac75b1Sopenharmony_ci    const moduleSet: Set<string> = new Set();
21907ac75b1Sopenharmony_ci    moduleMap.set(key, moduleSet.add(value));
22007ac75b1Sopenharmony_ci    kitModules.set(fileName, moduleMap);
22107ac75b1Sopenharmony_ci  }
22207ac75b1Sopenharmony_ci}
22307ac75b1Sopenharmony_ci
22407ac75b1Sopenharmony_cifunction compareKitModules(value: Set<string>, kitKey: string, kitValue: Map<string, Set<string>>): void {
22507ac75b1Sopenharmony_ci  const realModules: Set<string> = new Set();
22607ac75b1Sopenharmony_ci  value.forEach((element: string) => {
22707ac75b1Sopenharmony_ci    if (kitValue.get(element)) {
22807ac75b1Sopenharmony_ci      kitValue.get(element).forEach((kitModuleRequest: string) => {
22907ac75b1Sopenharmony_ci        realModules.add(kitModuleRequest.replace(/\.d\.e?ts$/, ''));
23007ac75b1Sopenharmony_ci      });
23107ac75b1Sopenharmony_ci    } else {
23207ac75b1Sopenharmony_ci      realModules.add(element);
23307ac75b1Sopenharmony_ci    }
23407ac75b1Sopenharmony_ci  });
23507ac75b1Sopenharmony_ci  appImportModuleCollection.set(kitKey, realModules);
23607ac75b1Sopenharmony_ci}
23707ac75b1Sopenharmony_ci
23807ac75b1Sopenharmony_cifunction replaceKitModules(): void {
23907ac75b1Sopenharmony_ci  appImportModuleCollection.forEach((value: Set<string>, key: string) => {
24007ac75b1Sopenharmony_ci    kitModules.forEach((kitValue: Map<string, Set<string>>, kitKey: string) => {
24107ac75b1Sopenharmony_ci      if (key === kitKey && value && value.size > 0) {
24207ac75b1Sopenharmony_ci        compareKitModules(value, kitKey, kitValue);
24307ac75b1Sopenharmony_ci      }
24407ac75b1Sopenharmony_ci    });
24507ac75b1Sopenharmony_ci  });
24607ac75b1Sopenharmony_ci}
247