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