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}