• 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 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}