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