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 cluster from 'cluster'; 17import fs from 'fs'; 18import path from 'path'; 19import os from 'os'; 20import sourceMap from "source-map"; 21 22import { 23 DEBUG, 24 ESMODULE, 25 EXTNAME_ETS, 26 EXTNAME_JS, 27 EXTNAME_TS, 28 EXTNAME_JSON, 29 EXTNAME_CJS, 30 EXTNAME_MJS, 31 TEMPORARY 32} from './common/ark_define'; 33import { 34 nodeLargeOrEqualTargetVersion, 35 genTemporaryPath, 36 mkdirsSync, 37 validateFilePathLength, 38 toUnixPath 39} from '../../utils'; 40import { 41 tryMangleFileNameAndWriteFile, 42 writeObfuscatedSourceCode, 43 cleanUpUtilsObjects, 44 createAndStartEvent, 45 stopEvent 46} from '../../ark_utils'; 47import { AOT_FULL, AOT_PARTIAL, AOT_TYPE } from '../../pre_define'; 48import { newSourceMaps } from './transform'; 49import { hasTsNoCheckOrTsIgnoreFiles, compilingEtsOrTsFiles } from '../../process_kit_import'; 50import { cleanSourceMapObject } from './transform'; 51import { cleanUpKitImportObjects } from '../../process_kit_import'; 52import { cleanModuleMode } from './generate_module_abc'; 53import { ModuleSourceFile } from './module/module_source_file'; 54 55export function needAotCompiler(projectConfig: Object): boolean { 56 return projectConfig.compileMode === ESMODULE && (projectConfig.anBuildMode === AOT_FULL || 57 projectConfig.anBuildMode === AOT_PARTIAL); 58} 59 60export function isAotMode(projectConfig: Object): boolean { 61 return projectConfig.compileMode === ESMODULE && (projectConfig.anBuildMode === AOT_FULL || 62 projectConfig.anBuildMode === AOT_PARTIAL || projectConfig.anBuildMode === AOT_TYPE); 63} 64 65export function isDebug(projectConfig: Object): boolean { 66 return projectConfig.buildMode.toLowerCase() === DEBUG; 67} 68 69export function isMasterOrPrimary() { 70 return ((nodeLargeOrEqualTargetVersion(16) && cluster.isPrimary) || 71 (!nodeLargeOrEqualTargetVersion(16) && cluster.isMaster)); 72} 73 74export function changeFileExtension(file: string, targetExt: string, originExt = ''): string { 75 let currentExt = originExt.length === 0 ? path.extname(file) : originExt; 76 let fileWithoutExt = file.substring(0, file.lastIndexOf(currentExt)); 77 return fileWithoutExt + targetExt; 78} 79 80function removeCacheFile(cacheFilePath: string, ext: string): void { 81 let filePath = toUnixPath(changeFileExtension(cacheFilePath, ext)); 82 if (fs.existsSync(filePath)) { 83 fs.rmSync(filePath); 84 } 85} 86 87export function shouldETSOrTSFileTransformToJS(filePath: string, projectConfig: Object): boolean { 88 const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectConfig.projectRootPath), ''); 89 let cacheFilePath: string = path.join(projectConfig.cachePath, sufStr); 90 91 if (!projectConfig.processTs) { 92 removeCacheFile(cacheFilePath, EXTNAME_TS); 93 return true; 94 } 95 96 if (compilingEtsOrTsFiles.indexOf(filePath) !== -1) { // file involves in compilation 97 const hasTsNoCheckOrTsIgnore = hasTsNoCheckOrTsIgnoreFiles.indexOf(filePath) !== -1; 98 // Remove cacheFile whose extension is different the target file 99 removeCacheFile(cacheFilePath, hasTsNoCheckOrTsIgnore ? EXTNAME_TS : EXTNAME_JS); 100 return hasTsNoCheckOrTsIgnore; 101 } 102 103 cacheFilePath = toUnixPath(changeFileExtension(cacheFilePath, EXTNAME_JS)); 104 return fs.existsSync(cacheFilePath); 105} 106 107export async function writeFileContentToTempDir(id: string, content: string, projectConfig: Object, 108 logger: Object, parentEvent: Object): Promise<void> { 109 if (isCommonJsPluginVirtualFile(id)) { 110 return; 111 } 112 113 if (!isCurrentProjectFiles(id, projectConfig)) { 114 return; 115 } 116 117 let filePath: string; 118 if (projectConfig.compileHar) { 119 // compileShared: compile shared har of project 120 filePath = genTemporaryPath(id, 121 projectConfig.compileShared ? projectConfig.projectRootPath : projectConfig.moduleRootPath, 122 projectConfig.compileShared ? path.resolve(projectConfig.aceModuleBuild, '../etsFortgz'): projectConfig.cachePath, 123 projectConfig, projectConfig.compileShared); 124 } else { 125 filePath = genTemporaryPath(id, projectConfig.projectPath, projectConfig.cachePath, projectConfig); 126 } 127 128 const eventWriteFileContent = createAndStartEvent(parentEvent, 'write file content'); 129 switch (path.extname(id)) { 130 case EXTNAME_ETS: 131 case EXTNAME_TS: 132 case EXTNAME_JS: 133 case EXTNAME_MJS: 134 case EXTNAME_CJS: 135 await writeFileContent(id, filePath, content, projectConfig, logger); 136 break; 137 case EXTNAME_JSON: 138 tryMangleFileNameAndWriteFile(filePath, content, projectConfig, id); 139 break; 140 default: 141 break; 142 } 143 stopEvent(eventWriteFileContent); 144} 145 146async function writeFileContent(sourceFilePath: string, filePath: string, content: string, 147 projectConfig: Object, logger: Object): Promise<void> { 148 if (!isSpecifiedExt(sourceFilePath, EXTNAME_JS)) { 149 filePath = changeFileExtension(filePath, EXTNAME_JS); 150 } 151 152 // In compile har mode, the code needs to be obfuscated and compressed. 153 if (projectConfig.compileHar || !isDebug(projectConfig)) { 154 const relativeSourceFilePath: string = toUnixPath(sourceFilePath).replace(toUnixPath(projectConfig.projectRootPath) 155 + '/', ''); 156 await writeObfuscatedSourceCode(content, filePath, logger, projectConfig, relativeSourceFilePath, newSourceMaps, sourceFilePath); 157 return; 158 } 159 mkdirsSync(path.dirname(filePath)); 160 fs.writeFileSync(filePath, content, 'utf-8'); 161} 162 163export function getEs2abcFileThreadNumber(): number { 164 const fileThreads : number = os.cpus().length < 16 ? os.cpus().length : 16; 165 return fileThreads; 166} 167 168export function isCommonJsPluginVirtualFile(filePath: string): boolean { 169 // rollup uses commonjs plugin to handle commonjs files, 170 // which will automatic generate files like 'jsfile.js?commonjs-exports' 171 return filePath.includes('\x00'); 172} 173 174export function isCurrentProjectFiles(filePath: string, projectConfig: Object): boolean { 175 return filePath.indexOf(projectConfig.projectRootPath) >= 0; 176} 177 178export function genTemporaryModuleCacheDirectoryForBundle(projectConfig: Object): string { 179 const buildDirArr: string[] = projectConfig.aceModuleBuild.split(path.sep); 180 const abilityDir: string = buildDirArr[buildDirArr.length - 1]; 181 const temporaryModuleCacheDirPath: string = path.join(projectConfig.cachePath, TEMPORARY, abilityDir); 182 mkdirsSync(temporaryModuleCacheDirPath); 183 184 return temporaryModuleCacheDirPath; 185} 186 187export function isSpecifiedExt(filePath: string, fileExtendName: string) { 188 return path.extname(filePath) === fileExtendName; 189} 190 191export function genCachePath(tailName: string, projectConfig: Object, logger: Object): string { 192 const pathName: string = projectConfig.cachePath !== undefined ? 193 path.join(projectConfig.cachePath, TEMPORARY, tailName) : path.join(projectConfig.aceModuleBuild, tailName); 194 mkdirsSync(path.dirname(pathName)); 195 196 validateFilePathLength(pathName, logger); 197 return pathName; 198} 199 200export function isTsOrEtsSourceFile(file: string): boolean { 201 return /(?<!\.d)\.[e]?ts$/.test(file); 202} 203 204export function isJsSourceFile(file: string): boolean { 205 return /\.[cm]?js$/.test(file); 206} 207 208export function isJsonSourceFile(file: string): boolean { 209 return /\.json$/.test(file); 210} 211 212export async function updateSourceMap(originMap: sourceMap.RawSourceMap, newMap: sourceMap.RawSourceMap): Promise<any> { 213 if (!originMap) { 214 return newMap; 215 } 216 if (!newMap) { 217 return originMap; 218 } 219 const originConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(originMap); 220 const newConsumer: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(newMap); 221 const newMappingList: sourceMap.MappingItem[] = []; 222 newConsumer.eachMapping((mapping: sourceMap.MappingItem) => { 223 if (mapping.originalLine == null) { 224 return; 225 } 226 const originalPos = 227 originConsumer.originalPositionFor({line: mapping.originalLine, column: mapping.originalColumn}); 228 if (originalPos.source == null) { 229 return; 230 } 231 mapping.originalLine = originalPos.line; 232 mapping.originalColumn = originalPos.column; 233 newMappingList.push(mapping); 234 }); 235 const updatedGenerator: sourceMap.SourceMapGenerator = sourceMap.SourceMapGenerator.fromSourceMap(newConsumer); 236 updatedGenerator['_file'] = originMap.file; 237 updatedGenerator['_mappings']['_array'] = newMappingList; 238 return JSON.parse(updatedGenerator.toString()); 239} 240 241export function cleanUpObjects(): void { 242 cleanSourceMapObject(); 243 cleanUpUtilsObjects(); 244 cleanUpKitImportObjects(); 245 cleanModuleMode(); 246 ModuleSourceFile.cleanUpObjects(); 247} 248 249export const utUtils = { 250 writeFileContent 251}