• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 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 path from 'path';
17import ts from 'typescript';
18import fs from 'fs';
19import os from 'os';
20import uglifyJS from 'uglify-js';
21
22import {
23  partialUpdateConfig,
24  projectConfig,
25  globalProgram
26} from '../main';
27import { createHash } from 'crypto';
28import type { Hash } from 'crypto';
29import {
30  AUXILIARY,
31  EXTNAME_ETS,
32  EXTNAME_JS,
33  MAIN,
34  FAIL,
35  TEMPORARY,
36  ESMODULE,
37  $$,
38  EXTEND_DECORATORS,
39  COMPONENT_EXTEND_DECORATOR,
40  COMPONENT_ANIMATABLE_EXTEND_DECORATOR,
41  COMPONENT_CONSTRUCTOR_LOCALSTORAGE_TYPE_PU,
42  GET_SHARED,
43  COMPONENT_CONSTRUCTOR_UNDEFINED,
44  USE_SHARED_STORAGE,
45  STORAGE
46} from './pre_define';
47import {
48  ERROR_DESCRIPTION,
49  HvigorLogInfo,
50  HvigorErrorInfo
51} from './hvigor_error_code/hvigor_error_info';
52
53export enum LogType {
54  ERROR = 'ERROR',
55  WARN = 'WARN',
56  NOTE = 'NOTE'
57}
58export const TEMPORARYS: string = 'temporarys';
59export const BUILD: string = 'build';
60export const SRC_MAIN: string = 'src/main';
61
62const red: string = '\u001b[31m';
63const reset: string = '\u001b[39m';
64
65const WINDOWS: string = 'Windows_NT';
66const LINUX: string = 'Linux';
67const MAC: string = 'Darwin';
68const HARMONYOS: string = 'HarmonyOS';
69
70export interface LogInfo extends HvigorLogInfo {
71  type: LogType,
72  message: string,
73  pos?: number,
74  line?: number,
75  column?: number,
76  fileName?: string
77}
78
79export const repeatLog: Map<string, LogInfo> = new Map();
80
81export interface IFileLog {
82  sourceFile: ts.SourceFile | undefined;
83  errors: LogInfo[];
84  cleanUp(): void
85}
86
87export function buildErrorInfoFromLogInfo(info: LogInfo, fileName: string): HvigorErrorInfo {
88  const positionMessage: string = info.line && info.column ? `${fileName}:${info.line}:${info.column}` : fileName;
89  return new HvigorErrorInfo()
90    .setCode(info.code)
91    .setDescription(info.description ?? ERROR_DESCRIPTION)
92    .setCause(info.cause ?? info.message)
93    .setPosition(`File: ${positionMessage}`)
94    .setSolutions(info.solutions ?? []);
95}
96
97export function isHvigorLogInfo(logInfo: LogInfo): boolean {
98  // Hvigor logger requires code as mendatory attribute.
99  // ArkUI warnings or info are not using Hvigor logger to emit.
100  return logInfo.code !== undefined;
101}
102
103export function emitLogInfo(loader: any, infos: LogInfo[], fastBuild: boolean = false,
104  resourcePath: string | null = null, hvigorLogger: any = undefined): void {
105  if (infos && infos.length) {
106    infos.forEach((item) => {
107      hvigorLogger ? emitLogInfoFromHvigorLogger(hvigorLogger, item, loader, fastBuild, resourcePath)
108        : emitLogInfoFromLoader(loader, item, fastBuild, resourcePath);
109    });
110  }
111}
112
113function emitLogInfoFromHvigorLogger(hvigorLogger: any, info: LogInfo, loader: any,
114  fastBuild: boolean, resourcePath: string | null): void {
115  if (!isHvigorLogInfo(info)) {
116    return emitLogInfoFromLoader(loader, info, fastBuild, resourcePath);
117  }
118  const errorInfo: HvigorErrorInfo = buildErrorInfoFromLogInfo(info, info.fileName || resourcePath);
119  switch (info.type) {
120    case LogType.ERROR:
121      hvigorLogger.printError(errorInfo);
122      break;
123    case LogType.WARN:
124      hvigorLogger.printWarn(errorInfo.cause);
125      break;
126    case LogType.NOTE:
127      hvigorLogger.printInfo(errorInfo.cause);
128      break;
129  }
130}
131
132function emitLogInfoFromLoader(loader: any, info: LogInfo, fastBuild: boolean = false,
133  resourcePath: string | null = null): void {
134  const message: string = fastBuild ? getMessage(info.fileName || resourcePath, info, true)
135    : getMessage(info.fileName || loader.resourcePath, info);
136  switch (info.type) {
137    case LogType.ERROR:
138      fastBuild ? loader.error(message) : loader.emitError(message);
139      break;
140    case LogType.WARN:
141      fastBuild ? loader.warn(message) : loader.emitWarning(message);
142      break;
143    case LogType.NOTE:
144      fastBuild ? loader.info(message) : loader.emitWarning(message);
145      break;
146  }
147}
148
149export function addLog(
150  type: LogType,
151  message: string,
152  pos: number,
153  log: LogInfo[],
154  sourceFile: ts.SourceFile,
155  hvigorLogInfo?: HvigorLogInfo
156): void {
157  const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(pos);
158  log.push({
159    type: type,
160    message: message,
161    line: posOfNode.line + 1,
162    column: posOfNode.character + 1,
163    fileName: sourceFile.fileName,
164    ...hvigorLogInfo
165  });
166}
167
168export function getMessage(fileName: string, info: LogInfo, fastBuild: boolean = false): string {
169  let message: string;
170  if (info.line && info.column) {
171    message = `BUILD${info.type} File: ${fileName}:${info.line}:${info.column}\n ${info.message}`;
172  } else {
173    message = `BUILD${info.type} File: ${fileName}\n ${info.message}`;
174  }
175  if (fastBuild) {
176    message = message.replace(/^BUILD/, 'ArkTS:');
177  }
178  return message;
179}
180
181export function getTransformLog(transformLog: IFileLog): LogInfo[] {
182  const sourceFile: ts.SourceFile = transformLog.sourceFile;
183  const logInfos: LogInfo[] = transformLog.errors.map((item) => {
184    if (item.pos || item.pos === 0) {
185      if (!item.column || !item.line) {
186        const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(item.pos);
187        item.line = posOfNode.line + 1;
188        item.column = posOfNode.character + 1;
189      }
190    } else {
191      item.line = item.line || undefined;
192      item.column = item.column || undefined;
193    }
194    if (!item.fileName) {
195      item.fileName = sourceFile.fileName;
196    }
197    return item;
198  });
199  return logInfos;
200}
201
202class ComponentInfo {
203  private _id: number = 0;
204  private _componentNames: Set<string> = new Set(['ForEach']);
205  public set id(id: number) {
206    this._id = id;
207  }
208  public get id(): number {
209    return this._id;
210  }
211  public set componentNames(componentNames: Set<string>) {
212    this._componentNames = componentNames;
213  }
214  public get componentNames(): Set<string> {
215    return this._componentNames;
216  }
217}
218
219export let componentInfo: ComponentInfo = new ComponentInfo();
220
221export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration |
222  ts.StructDeclaration | ts.ClassDeclaration, decortorName: string,
223  customBuilder: ts.Decorator[] = null, log: LogInfo[] = null): boolean {
224  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
225  if (decorators && decorators.length) {
226    const extendResult = {
227      Extend: false,
228      AnimatableExtend: false
229    };
230    for (let i = 0; i < decorators.length; i++) {
231      const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').replace(/\s*/g, '').trim();
232      if (log && EXTEND_DECORATORS.includes(decortorName)) {
233        if (originalDecortor === COMPONENT_EXTEND_DECORATOR) {
234          extendResult.Extend = true;
235        }
236        if (originalDecortor === COMPONENT_ANIMATABLE_EXTEND_DECORATOR) {
237          extendResult.AnimatableExtend = true;
238        }
239      } else {
240        if (originalDecortor === decortorName) {
241          if (customBuilder) {
242            customBuilder.push(...decorators.slice(i + 1), ...decorators.slice(0, i));
243          }
244          return true;
245        }
246      }
247    }
248    if (log && extendResult.Extend && extendResult.AnimatableExtend) {
249      log.push({
250        type: LogType.ERROR,
251        message: `The function can not be decorated by '@Extend' and '@AnimatableExtend' at the same time.`,
252        pos: node.getStart(),
253        code: '10905111'
254      });
255    }
256    return (decortorName === COMPONENT_EXTEND_DECORATOR && extendResult.Extend) ||
257      (decortorName === COMPONENT_ANIMATABLE_EXTEND_DECORATOR && extendResult.AnimatableExtend);
258  }
259  return false;
260}
261
262const STATEMENT_EXPECT: number = 1128;
263const SEMICOLON_EXPECT: number = 1005;
264const STATESTYLES_EXPECT: number = 1003;
265export const IGNORE_ERROR_CODE: number[] = [STATEMENT_EXPECT, SEMICOLON_EXPECT, STATESTYLES_EXPECT];
266
267export function readFile(dir: string, utFiles: string[]): void {
268  try {
269    const files: string[] = fs.readdirSync(dir);
270    files.forEach((element) => {
271      const filePath: string = path.join(dir, element);
272      const status: fs.Stats = fs.statSync(filePath);
273      if (status.isDirectory()) {
274        readFile(filePath, utFiles);
275      } else {
276        utFiles.push(filePath);
277      }
278    });
279  } catch (e) {
280    console.error(red, 'ArkTS ERROR: ' + e, reset);
281  }
282}
283
284export function circularFile(inputPath: string, outputPath: string): void {
285  if (!inputPath || !outputPath) {
286    return;
287  }
288  fs.readdir(inputPath, function (err, files) {
289    if (!files) {
290      return;
291    }
292    files.forEach(file => {
293      const inputFile: string = path.resolve(inputPath, file);
294      const outputFile: string = path.resolve(outputPath, file);
295      const fileStat: fs.Stats = fs.statSync(inputFile);
296      if (fileStat.isFile()) {
297        copyFile(inputFile, outputFile);
298      } else {
299        circularFile(inputFile, outputFile);
300      }
301    });
302  });
303}
304
305function copyFile(inputFile: string, outputFile: string): void {
306  try {
307    const parent: string = path.join(outputFile, '..');
308    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
309      mkDir(parent);
310    }
311    if (fs.existsSync(outputFile)) {
312      return;
313    }
314    const readStream: fs.ReadStream = fs.createReadStream(inputFile);
315    const writeStream: fs.WriteStream = fs.createWriteStream(outputFile);
316    readStream.pipe(writeStream);
317    readStream.on('close', function () {
318      writeStream.end();
319    });
320  } catch (err) {
321    throw err.message;
322  }
323}
324
325export function mkDir(path_: string): void {
326  const parent: string = path.join(path_, '..');
327  if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
328    mkDir(parent);
329  }
330  fs.mkdirSync(path_);
331}
332
333export function toUnixPath(data: string): string {
334  if (/^win/.test(require('os').platform())) {
335    const fileTmps: string[] = data.split(path.sep);
336    const newData: string = path.posix.join(...fileTmps);
337    return newData;
338  }
339  return data;
340}
341
342export function tryToLowerCasePath(filePath: string): string {
343  return toUnixPath(filePath).toLowerCase();
344}
345
346export function toHashData(path: string): string {
347  const content: string = fs.readFileSync(path).toString();
348  const hash: Hash = createHash('sha256');
349  hash.update(content);
350  return hash.digest('hex');
351}
352
353export function writeFileSync(filePath: string, content: string): void {
354  if (!fs.existsSync(filePath)) {
355    const parent: string = path.join(filePath, '..');
356    if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
357      mkDir(parent);
358    }
359  }
360  fs.writeFileSync(filePath, content);
361}
362export function genLoaderOutPathOfHar(filePath: string, cachePath: string, buildPath: string, moduleRootPath: string, projectRootPath): string {
363  filePath = toUnixPath(filePath);
364  buildPath = toUnixPath(buildPath);
365  const cacheRootPath: string = toUnixPath(cachePath);
366  const moduleName = toUnixPath(moduleRootPath).replace(toUnixPath(projectRootPath), '');
367  const relativeFilePath: string = filePath.replace(cacheRootPath, '').replace(moduleName, '');
368  const output: string = path.join(buildPath, relativeFilePath);
369  return output;
370}
371
372export function genTemporaryPath(filePath: string, projectPath: string, buildPath: string, projectConfig: Object,
373  metaInfo: Object, buildInHar: boolean = false): string {
374  filePath = toUnixPath(filePath).replace(/\.[cm]js$/, EXTNAME_JS);
375  projectPath = toUnixPath(projectPath);
376
377  if (process.env.compileTool === 'rollup') {
378    let relativeFilePath: string = '';
379    if (metaInfo) {
380      if (metaInfo.isLocalDependency) {
381        // When buildInHar and compileHar are both True,
382        // this is the path under the PackageHar directory being spliced ​​together.
383        // Here, only the relative path based on moduleRootPath needs to be retained.
384        // eg. moduleA/index.js --> index.js --> PackageHar/index.js
385        // eg. moduleA/src/main/ets/test.js --> src/main/ets/test.js --> PackageHar/src/main/ets/test.js
386        const moduleName: string = buildInHar && projectConfig.compileHar ? '' : metaInfo.moduleName;
387        relativeFilePath = filePath.replace(toUnixPath(metaInfo.belongModulePath), moduleName);
388      } else {
389        relativeFilePath = filePath.replace(toUnixPath(metaInfo.belongProjectPath), '');
390      }
391    } else {
392      relativeFilePath = filePath.replace(toUnixPath(projectConfig.projectRootPath), '');
393    }
394    const output: string = path.join(buildPath, relativeFilePath);
395    return output;
396  }
397
398  if (isPackageModulesFile(filePath, projectConfig)) {
399    const packageDir: string = projectConfig.packageDir;
400    const fakePkgModulesPath: string = toUnixPath(path.join(projectConfig.projectRootPath, packageDir));
401    let output: string = '';
402    if (filePath.indexOf(fakePkgModulesPath) === -1) {
403      const hapPath: string = toUnixPath(projectConfig.projectRootPath);
404      const tempFilePath: string = filePath.replace(hapPath, '');
405      const relativeFilePath: string = tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1);
406      output = path.join(buildPath, buildInHar ? '' : TEMPORARY, packageDir, MAIN, relativeFilePath);
407    } else {
408      output = filePath.replace(fakePkgModulesPath,
409        path.join(buildPath, buildInHar ? '' : TEMPORARY, packageDir, AUXILIARY));
410    }
411    return output;
412  }
413
414  if (filePath.indexOf(projectPath) !== -1) {
415    const relativeFilePath: string = filePath.replace(projectPath, '');
416    const output: string = path.join(buildPath, buildInHar ? '' : TEMPORARY, relativeFilePath);
417    return output;
418  }
419
420  return '';
421}
422
423export function isPackageModulesFile(filePath: string, projectConfig: Object): boolean {
424  filePath = toUnixPath(filePath);
425  const hapPath: string = toUnixPath(projectConfig.projectRootPath);
426  const tempFilePath: string = filePath.replace(hapPath, '');
427  const packageDir: string = projectConfig.packageDir;
428  if (tempFilePath.indexOf(packageDir) !== -1) {
429    const fakePkgModulesPath: string = toUnixPath(path.resolve(projectConfig.projectRootPath, packageDir));
430    if (filePath.indexOf(fakePkgModulesPath) !== -1) {
431      return true;
432    }
433    if (projectConfig.modulePathMap) {
434      for (const key in projectConfig.modulePathMap) {
435        const value: string = projectConfig.modulePathMap[key];
436        const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir));
437        if (filePath.indexOf(fakeModulePkgModulesPath) !== -1) {
438          return true;
439        }
440      }
441    }
442  }
443
444  return false;
445}
446
447export interface GeneratedFileInHar {
448  sourcePath: string;
449  sourceCachePath?: string;
450  obfuscatedSourceCachePath?: string;
451  originalDeclarationCachePath?: string;
452  originalDeclarationContent?: string;
453  obfuscatedDeclarationCachePath?: string;
454}
455
456export const harFilesRecord: Map<string, GeneratedFileInHar> = new Map();
457
458export function generateSourceFilesInHar(sourcePath: string, sourceContent: string, suffix: string,
459  projectConfig: Object, modulePathMap?: Object): void {
460  const belongModuleInfo: Object = getBelongModuleInfo(sourcePath, modulePathMap, projectConfig.projectRootPath);
461  // compileShared: compile shared har of project
462  let jsFilePath: string = genTemporaryPath(sourcePath,
463    projectConfig.compileShared ? projectConfig.projectRootPath : projectConfig.moduleRootPath,
464    projectConfig.compileShared || projectConfig.byteCodeHar ? path.resolve(projectConfig.aceModuleBuild, '../etsFortgz') : projectConfig.cachePath,
465    projectConfig, belongModuleInfo, projectConfig.compileShared);
466  if (!jsFilePath.match(new RegExp(projectConfig.packageDir))) {
467    jsFilePath = jsFilePath.replace(/\.ets$/, suffix).replace(/\.ts$/, suffix);
468    if (projectConfig.obfuscateHarType === 'uglify' && suffix === '.js') {
469      sourceContent = uglifyJS.minify(sourceContent).code;
470    }
471    // collect the declaration files for obfuscation
472    if (projectConfig.compileMode === ESMODULE && (/\.d\.e?ts$/).test(jsFilePath)) {
473      sourcePath = toUnixPath(sourcePath);
474      const genFilesInHar: GeneratedFileInHar = {
475        sourcePath: sourcePath,
476        originalDeclarationCachePath: jsFilePath,
477        originalDeclarationContent: sourceContent
478      };
479      harFilesRecord.set(sourcePath, genFilesInHar);
480      return;
481    } else {
482      mkdirsSync(path.dirname(jsFilePath));
483    }
484    fs.writeFileSync(jsFilePath, sourceContent);
485  }
486}
487
488export function mkdirsSync(dirname: string): boolean {
489  if (fs.existsSync(dirname)) {
490    return true;
491  } else if (mkdirsSync(path.dirname(dirname))) {
492    fs.mkdirSync(dirname);
493    return true;
494  }
495
496  return false;
497}
498
499export function nodeLargeOrEqualTargetVersion(targetVersion: number): boolean {
500  const currentNodeVersion: number = parseInt(process.versions.node.split('.')[0]);
501  if (currentNodeVersion >= targetVersion) {
502    return true;
503  }
504
505  return false;
506}
507
508export function removeDir(dirName: string): void {
509  if (fs.existsSync(dirName)) {
510    if (nodeLargeOrEqualTargetVersion(16)) {
511      fs.rmSync(dirName, { recursive: true });
512    } else {
513      fs.rmdirSync(dirName, { recursive: true });
514    }
515  }
516}
517
518export function parseErrorMessage(message: string): string {
519  const messageArrary: string[] = message.split('\n');
520  let logContent: string = '';
521  messageArrary.forEach(element => {
522    if (!(/^at/.test(element.trim()))) {
523      logContent = logContent + element + '\n';
524    }
525  });
526  return logContent;
527}
528
529export function isWindows(): boolean {
530  return os.type() === WINDOWS;
531}
532
533export function isLinux(): boolean {
534  return os.type() === LINUX;
535}
536
537export function isMac(): boolean {
538  return os.type() === MAC;
539}
540
541export function isHarmonyOs(): boolean {
542  return os.type() === HARMONYOS;
543}
544
545export function maxFilePathLength(): number {
546  if (isWindows()) {
547    return 32766;
548  } else if (isLinux() || isHarmonyOs()) {
549    return 4095;
550  } else if (isMac()) {
551    return 1016;
552  } else {
553    return -1;
554  }
555}
556
557export function validateFilePathLength(filePath: string, logger: Object): boolean {
558  if (maxFilePathLength() < 0) {
559    logger.error(red, 'Unknown OS platform', reset);
560    process.exitCode = FAIL;
561    return false;
562  } else if (filePath.length > 0 && filePath.length <= maxFilePathLength()) {
563    return true;
564  } else if (filePath.length > maxFilePathLength()) {
565    logger.error(red, `The length of ${filePath} exceeds the limitation of current platform, which is ` +
566      `${maxFilePathLength()}. Please try moving the project folder to avoid deeply nested file path and try again`,
567    reset);
568    process.exitCode = FAIL;
569    return false;
570  } else {
571    logger.error(red, 'Validate file path failed', reset);
572    process.exitCode = FAIL;
573    return false;
574  }
575}
576
577export function validateFilePathLengths(filePaths: Array<string>, logger: any): boolean {
578  filePaths.forEach((filePath) => {
579    if (!validateFilePathLength(filePath, logger)) {
580      return false;
581    }
582    return true;
583  });
584  return true;
585}
586
587export function unlinkSync(filePath: string): void {
588  if (fs.existsSync(filePath)) {
589    fs.unlinkSync(filePath);
590  }
591}
592
593export function getExtensionIfUnfullySpecifiedFilepath(filePath: string): string {
594  if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
595    return '';
596  }
597
598  let extension: string = EXTNAME_ETS;
599  if (fs.existsSync(filePath + '.ts') && fs.statSync(filePath + '.ts').isFile()) {
600    extension = '.ts';
601  } else if (fs.existsSync(filePath + '.d.ts') && fs.statSync(filePath + '.d.ts').isFile()) {
602    extension = '.d.ts';
603  } else if (fs.existsSync(filePath + '.d.ets') && fs.statSync(filePath + '.d.ets').isFile()) {
604    extension = '.d.ets';
605  } else if (fs.existsSync(filePath + '.js') && fs.statSync(filePath + '.js').isFile()) {
606    extension = '.js';
607  } else if (fs.existsSync(filePath + '.json') && fs.statSync(filePath + '.json').isFile()) {
608    extension = '.json';
609  }
610
611  return extension;
612}
613
614export function shouldWriteChangedList(watchModifiedFiles: string[],
615  watchRemovedFiles: string[]): boolean {
616  if (projectConfig.compileMode === ESMODULE && process.env.watchMode === 'true' && !projectConfig.isPreview &&
617    projectConfig.changedFileList && (watchRemovedFiles.length + watchModifiedFiles.length)) {
618    if (process.env.compileTool !== 'rollup') {
619      if (!(watchModifiedFiles.length === 1 &&
620        watchModifiedFiles[0] === projectConfig.projectPath && !watchRemovedFiles.length)) {
621        return true;
622      } else {
623        return false;
624      }
625    }
626    return true;
627  }
628  return false;
629}
630
631interface HotReloadIncrementalTime {
632  hotReloadIncrementalStartTime: string;
633  hotReloadIncrementalEndTime: string;
634}
635
636export const hotReloadIncrementalTime: HotReloadIncrementalTime = {
637  hotReloadIncrementalStartTime: '',
638  hotReloadIncrementalEndTime: ''
639};
640
641interface FilesObj {
642  modifiedFiles: string[],
643  removedFiles: string[]
644}
645
646let allModifiedFiles: Set<string> = new Set();
647
648export function getHotReloadFiles(watchModifiedFiles: string[],
649  watchRemovedFiles: string[], hotReloadSupportFiles: Set<string>): FilesObj {
650  hotReloadIncrementalTime.hotReloadIncrementalStartTime = new Date().getTime().toString();
651  watchRemovedFiles = watchRemovedFiles.map(file => path.relative(projectConfig.projectPath, file));
652  allModifiedFiles = new Set([...allModifiedFiles, ...watchModifiedFiles
653    .filter(file => fs.statSync(file).isFile() &&
654      (hotReloadSupportFiles.has(file) || !['.ets', '.ts', '.js'].includes(path.extname(file))))
655    .map(file => path.relative(projectConfig.projectPath, file))]
656    .filter(file => !watchRemovedFiles.includes(file)));
657  return {
658    modifiedFiles: [...allModifiedFiles],
659    removedFiles: [...watchRemovedFiles]
660  };
661}
662
663export function getResolveModules(projectPath: string, faMode: boolean): string[] {
664  if (faMode) {
665    return [
666      path.resolve(projectPath, '../../../../../'),
667      path.resolve(projectPath, '../../../../' + projectConfig.packageDir),
668      path.resolve(projectPath, '../../../../../' + projectConfig.packageDir),
669      path.resolve(projectPath, '../../')
670    ];
671  } else {
672    return [
673      path.resolve(projectPath, '../../../../'),
674      path.resolve(projectPath, '../../../' + projectConfig.packageDir),
675      path.resolve(projectPath, '../../../../' + projectConfig.packageDir),
676      path.resolve(projectPath, '../')
677    ];
678  }
679}
680
681export function writeUseOSFiles(useOSFiles: Set<string>): void {
682  let info: string = '';
683  if (!fs.existsSync(projectConfig.aceSoPath)) {
684    const parent: string = path.resolve(projectConfig.aceSoPath, '..');
685    if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
686      mkDir(parent);
687    }
688  } else {
689    info = fs.readFileSync(projectConfig.aceSoPath, 'utf-8') + '\n';
690  }
691  fs.writeFileSync(projectConfig.aceSoPath, info + Array.from(useOSFiles).join('\n'));
692}
693
694
695export function writeCollectionFile(cachePath: string, appCollection: Map<string, Set<string>>,
696  allComponentsOrModules: Map<string, Array<string>>, fileName: string, allFiles: Set<string> = null,
697  widgetPath: string = undefined): void {
698  for (let key of appCollection.keys()) {
699    if (appCollection.get(key).size === 0) {
700      allComponentsOrModules.delete(key);
701      continue;
702    }
703    if (allFiles && !allFiles.has(key)) {
704      continue;
705    }
706    const newKey: string = projectConfig.projectRootPath ? path.relative(projectConfig.projectRootPath, key) : key;
707    allComponentsOrModules.set(newKey, Array.from(appCollection.get(key)));
708  }
709  const content: string = JSON.stringify(Object.fromEntries(allComponentsOrModules), null, 2);
710  writeFileSync(path.resolve(cachePath, fileName), content);
711  if (widgetPath) {
712    writeFileSync(path.resolve(widgetPath, fileName), content);
713  }
714}
715
716export function getAllComponentsOrModules(allFiles: Set<string>,
717  cacheCollectionFileName: string): Map<string, Array<string>> {
718  const cacheCollectionFilePath: string = path.resolve(projectConfig.cachePath, cacheCollectionFileName);
719  const allComponentsOrModules: Map<string, Array<string>> = new Map();
720  if (!fs.existsSync(cacheCollectionFilePath)) {
721    return allComponentsOrModules;
722  }
723  const lastComponentsOrModules = require(cacheCollectionFilePath);
724  for (let key in lastComponentsOrModules) {
725    if (allFiles.has(key)) {
726      allComponentsOrModules.set(key, lastComponentsOrModules[key]);
727    }
728  }
729  return allComponentsOrModules;
730}
731
732export function getPossibleBuilderTypeParameter(parameters: ts.ParameterDeclaration[]): string[] {
733  const parameterNames: string[] = [];
734  if (!partialUpdateConfig.builderCheck) {
735    if (isDollarParameter(parameters)) {
736      parameters[0].type.members.forEach((member) => {
737        if (member.name && ts.isIdentifier(member.name)) {
738          parameterNames.push(member.name.escapedText.toString());
739        }
740      });
741    } else {
742      parameters.forEach((parameter) => {
743        if (parameter.name && ts.isIdentifier(parameter.name)) {
744          parameterNames.push(parameter.name.escapedText.toString());
745        }
746      });
747    }
748  }
749  return parameterNames;
750}
751
752function isDollarParameter(parameters: ts.ParameterDeclaration[]): boolean {
753  return parameters.length === 1 && parameters[0].name && ts.isIdentifier(parameters[0].name) &&
754    parameters[0].name.escapedText.toString() === $$ && parameters[0].type && ts.isTypeLiteralNode(parameters[0].type) &&
755    parameters[0].type.members && parameters[0].type.members.length > 0;
756}
757
758interface ChildrenCacheFile {
759  fileName: string,
760  mtimeMs: number,
761}
762
763export interface CacheFile {
764  mtimeMs: number,
765  children: Array<ChildrenCacheFile>,
766}
767
768export interface RouterInfo {
769  name: string,
770  buildFunction: string,
771}
772
773// Global Information & Method
774export class ProcessFileInfo {
775  buildStart: boolean = true;
776  wholeFileInfo: { [id: string]: SpecialArkTSFileInfo | TSFileInfo } = {}; // Save ArkTS & TS file's infomation
777  transformedFiles: Set<string> = new Set(); // ArkTS & TS Files which should be transformed in this compilation
778  cachedFiles: string[] = []; // ArkTS & TS Files which should not be transformed in this compilation
779  shouldHaveEntry: string[] = []; // Which file should have @Entry decorator
780  resourceToFile: { [resource: string]: Set<string> } = {}; // Resource is used by which file
781  lastResourceList: Set<string> = new Set();
782  resourceList: Set<string> = new Set(); // Whole project resource
783  shouldInvalidFiles: Set<string> = new Set();
784  resourceTableChanged: boolean = false;
785  currentArkTsFile: SpecialArkTSFileInfo;
786  reUseProgram: boolean = false;
787  resourcesArr: Set<string> = new Set();
788  lastResourcesSet: Set<string> = new Set();
789  transformCacheFiles: { [fileName: string]: CacheFile } = {};
790  processBuilder: boolean = false;
791  processGlobalBuilder: boolean = false;
792  processLocalBuilder: boolean = false;
793  builderLikeCollection: Set<string> = new Set();
794  newTsProgram: ts.Program;
795  changeFiles: string[] = [];
796  isFirstBuild: boolean = true;
797  processForEach: number = 0;
798  processLazyForEach: number = 0;
799  processRepeat: boolean = false;
800  isAsPageImport: boolean = false;
801  overallObjectLinkCollection: Map<string, Set<string>> = new Map();
802  overallLinkCollection: Map<string, Set<string>> = new Map();
803  overallBuilderParamCollection: Map<string, Set<string>> = new Map();
804  lazyForEachInfo: {
805    forEachParameters: ts.ParameterDeclaration,
806    isDependItem: boolean
807  } = {
808      forEachParameters: null,
809      isDependItem: false
810    };
811  routerInfo: Map<string, Array<RouterInfo>> = new Map();
812  hasLocalBuilderInFile: boolean = false;
813
814  addGlobalCacheInfo(resourceListCacheInfo: string[],
815    resourceToFileCacheInfo: { [resource: string]: Set<string> },
816    cacheFile: { [fileName: string]: CacheFile }): void {
817    if (this.buildStart) {
818      for (const element in resourceToFileCacheInfo) {
819        this.resourceToFile[element] = new Set(resourceToFileCacheInfo[element]);
820      }
821      this.lastResourceList = new Set(resourceListCacheInfo);
822    }
823    if (this.resourceTableChanged) {
824      this.compareResourceDiff();
825    }
826    if (cacheFile) {
827      this.transformCacheFiles = cacheFile;
828    }
829  }
830
831  addFileCacheInfo(id: string, fileCacheInfo: fileInfo): void {
832    if (fileCacheInfo && process.env.compileMode === 'moduleJson') {
833      if (Array.isArray(fileCacheInfo.fileToResourceList)) {
834        fileCacheInfo.fileToResourceList = new Set(fileCacheInfo.fileToResourceList);
835      } else {
836        fileCacheInfo.fileToResourceList = new Set();
837      }
838    }
839    if (id.match(/(?<!\.d)\.(ets)$/)) {
840      this.wholeFileInfo[id] = new SpecialArkTSFileInfo(fileCacheInfo);
841    } else if (id.match(/(?<!\.d)\.(ts)$/) && process.env.compileMode === 'moduleJson') {
842      this.wholeFileInfo[id] = new TSFileInfo(fileCacheInfo);
843    }
844  }
845
846  collectTransformedFiles(id: string): void {
847    if (id.match(process.env.compileMode === 'moduleJson' ? /(?<!\.d)\.(ets|ts)$/ : /(?<!\.d)\.(ets)$/)) {
848      this.transformedFiles.add(id);
849    }
850  }
851
852  collectCachedFiles(id: string): void {
853    if (id.match(process.env.compileMode === 'moduleJson' ? /(?<!\.d)\.(ets|ts)$/ : /(?<!\.d)\.(ets)$/)) {
854      this.cachedFiles.push(id);
855    }
856  }
857
858  judgeShouldHaveEntryFiles(entryFileWithoutEntryDecorator: Set<string>): void {
859    this.shouldHaveEntry = Object.values(projectConfig.entryObj as string[]).filter((item) => {
860      return !entryFileWithoutEntryDecorator.has(item.toLowerCase()) && item.match(/(?<!\.d)\.(ets)$/);
861    });
862  }
863
864  saveCacheFileInfo(cache): void {
865    if (process.env.compileMode === 'moduleJson') {
866      const fileCacheInfo: { [id: string]: fileInfo | tsFileInfo } = cache.get('fileCacheInfo') || {};
867      const resourceToFileCacheInfo = cache.get('resourceToFileCacheInfo') || {};
868      for (const i in resourceToFileCacheInfo) {
869        resourceToFileCacheInfo[i] = new Set(resourceToFileCacheInfo[i]);
870      }
871      const resourceToFile: { [resource: string]: Set<string> | string[] } = Object.assign(resourceToFileCacheInfo, this.resourceToFile);
872      for (const id of this.transformedFiles) {
873        fileCacheInfo[id] = this.wholeFileInfo[id].fileInfo;
874        for (const resource of this.wholeFileInfo[id].newFileToResourceList) {
875          if (!(fileCacheInfo[id].fileToResourceList as Set<string>).has(resource)) {
876            this.resourceToFileBecomeSet(resourceToFile, resource, id);
877          }
878        }
879        for (const resource of fileCacheInfo[id].fileToResourceList) {
880          if (!this.wholeFileInfo[id].newFileToResourceList.has(resource)) {
881            (resourceToFile[resource] as Set<string>).delete(id);
882          }
883        }
884        fileCacheInfo[id].fileToResourceList = [...this.wholeFileInfo[id].newFileToResourceList];
885      }
886      for (const id of this.cachedFiles) {
887        fileCacheInfo[id].fileToResourceList = [...fileCacheInfo[id].fileToResourceList];
888      }
889      this.resourceToFile = resourceToFile as { [resource: string]: Set<string> };
890      for (const resource in resourceToFile) {
891        resourceToFile[resource] = [...resourceToFile[resource]];
892      }
893      cache.set('fileCacheInfo', fileCacheInfo);
894      cache.set('resourceListCacheInfo', [...this.resourceList]);
895      cache.set('resourceToFileCacheInfo', resourceToFile);
896    } else {
897      const cacheInfo: { [id: string]: fileInfo } = cache.get('fileCacheInfo') || {};
898      for (const id of this.transformedFiles) {
899        cacheInfo[id] = this.wholeFileInfo[id].fileInfo;
900      }
901      cache.set('fileCacheInfo', cacheInfo);
902    }
903  }
904
905  resourceToFileBecomeSet(resourceToFile: { [resource: string]: Set<string> | string[] }, resource: string, id: string): void {
906    if (!resourceToFile[resource]) {
907      resourceToFile[resource] = new Set();
908    }
909    if (resourceToFile[resource] instanceof Set) {
910      resourceToFile[resource].add(id);
911    } else if (Array.isArray(resourceToFile[resource])) {
912      resourceToFile[resource] = new Set(resourceToFile[resource]);
913      resourceToFile[resource].add(id);
914    } else {
915      return;
916    }
917  }
918
919  updateResourceList(resource: string): void {
920    this.resourceList.add(resource);
921  }
922
923  compareResourceDiff(): void {
924    // delete resource
925    for (const resource of this.lastResourceList) {
926      if (!this.resourceList.has(resource) && this.resourceToFile[resource]) {
927        this.resourceToFile[resource].forEach(file => {
928          this.shouldInvalidFiles.add(file);
929        });
930      }
931    }
932    // create resource
933    for (const resource of this.resourceList) {
934      if (!this.resourceToFile[resource]) {
935        this.resourceToFile[resource] = new Set();
936      }
937      if (!this.lastResourceList.has(resource)) {
938        this.resourceToFile[resource].forEach(file => {
939          this.shouldInvalidFiles.add(file);
940        });
941      }
942    }
943  }
944
945  collectResourceInFile(resource: string, file: string): void {
946    if (this.wholeFileInfo[file]) {
947      this.wholeFileInfo[file].newFileToResourceList.add(resource);
948    }
949  }
950
951  clearCollectedInfo(cache): void {
952    this.buildStart = false;
953    this.resourceTableChanged = false;
954    this.isAsPageImport = false;
955    this.saveCacheFileInfo(cache);
956    this.transformedFiles = new Set();
957    this.cachedFiles = [];
958    this.lastResourceList = new Set([...this.resourceList]);
959    this.shouldInvalidFiles.clear();
960    this.resourcesArr.clear();
961  }
962  setCurrentArkTsFile(): void {
963    this.currentArkTsFile = new SpecialArkTSFileInfo();
964  }
965  getCurrentArkTsFile(): SpecialArkTSFileInfo {
966    return this.currentArkTsFile;
967  }
968}
969
970export let storedFileInfo: ProcessFileInfo = new ProcessFileInfo();
971
972export interface fileInfo extends tsFileInfo {
973  hasEntry: boolean; // Has @Entry decorator or not
974}
975
976export interface tsFileInfo {
977  fileToResourceList: Set<string> | string[]; // How much Resource is used
978}
979
980// Save single TS file information
981class TSFileInfo {
982  fileInfo: tsFileInfo = {
983    fileToResourceList: new Set()
984  };
985  newFileToResourceList: Set<string> = new Set();
986  constructor(cacheInfo: fileInfo, etsFile?: boolean) {
987    if (!etsFile) {
988      this.fileInfo = cacheInfo || this.fileInfo;
989    }
990  }
991}
992
993// Save single ArkTS file information
994class SpecialArkTSFileInfo extends TSFileInfo {
995  fileInfo: fileInfo = {
996    hasEntry: false,
997    fileToResourceList: new Set()
998  };
999  recycleComponents: Set<string> = new Set([]);
1000  reuseComponentsV2: Set<string> = new Set([]);
1001  compFromDETS: Set<string> = new Set();
1002  animatableExtendAttribute: Map<string, Set<string>> = new Map();
1003  pkgName: string;
1004
1005  constructor(cacheInfo?: fileInfo) {
1006    super(cacheInfo, true);
1007    this.fileInfo = cacheInfo || this.fileInfo;
1008  }
1009
1010  get hasEntry(): boolean {
1011    return this.fileInfo.hasEntry;
1012  }
1013  set hasEntry(value: boolean) {
1014    this.fileInfo.hasEntry = value;
1015  }
1016}
1017
1018export function setChecker(): void {
1019  if (globalProgram.program) {
1020    globalProgram.checker = globalProgram.program.getTypeChecker();
1021    globalProgram.strictChecker = globalProgram.program.getLinterTypeChecker();
1022  } else if (globalProgram.watchProgram) {
1023    globalProgram.checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker();
1024  }
1025}
1026export interface ExtendResult {
1027  decoratorName: string;
1028  componentName: string;
1029}
1030
1031export function resourcesRawfile(rawfilePath: string, resourcesArr: Set<string>, resourceName: string = ''): void {
1032  if (fs.existsSync(process.env.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) {
1033    const files: string[] = fs.readdirSync(rawfilePath);
1034    files.forEach((file: string) => {
1035      if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) {
1036        resourcesRawfile(path.join(rawfilePath, file), resourcesArr, resourceName ? resourceName + '/' + file : file);
1037      } else {
1038        if (resourceName) {
1039          resourcesArr.add(resourceName + '/' + file);
1040        } else {
1041          resourcesArr.add(file);
1042        }
1043      }
1044    });
1045  }
1046}
1047
1048export function differenceResourcesRawfile(oldRawfile: Set<string>, newRawfile: Set<string>): boolean {
1049  if (oldRawfile.size !== 0 && oldRawfile.size === newRawfile.size) {
1050    for (const singleRawfiles of oldRawfile.values()) {
1051      if (!newRawfile.has(singleRawfiles)) {
1052        return true;
1053      }
1054    }
1055    return false;
1056  } else if (oldRawfile.size === 0 && oldRawfile.size === newRawfile.size) {
1057    return false;
1058  } else {
1059    return true;
1060  }
1061}
1062
1063export function isString(text: unknown): text is string {
1064  return typeof text === 'string';
1065}
1066
1067function getRollupCacheStoreKey(projectConfig: object): string {
1068  let keyInfo: string[] = [projectConfig.compileSdkVersion, projectConfig.compatibleSdkVersion, projectConfig.runtimeOS,
1069    projectConfig.etsLoaderPath];
1070  return keyInfo.join('#');
1071}
1072
1073function getRollupCacheKey(projectConfig: object): string {
1074  let isWidget: string = projectConfig.widgetCompile ? 'widget' : 'non-widget';
1075  let ohosTestInfo: string = 'non-ohosTest';
1076  if (projectConfig.testFrameworkPar) {
1077    ohosTestInfo = JSON.stringify(projectConfig.testFrameworkPar);
1078  }
1079
1080  let keyInfo: string[] = [projectConfig.entryModuleName, projectConfig.targetName, isWidget, ohosTestInfo,
1081    projectConfig.needCoverageInsert, projectConfig.isOhosTest];
1082  return keyInfo.join('#');
1083}
1084
1085function clearRollupCacheStore(cacheStoreManager: object, currentKey: string): void {
1086  if (!cacheStoreManager) {
1087    return;
1088  }
1089
1090  for (let key of cacheStoreManager.keys()) {
1091    if (key !== currentKey) {
1092      cacheStoreManager.unmount(key);
1093    }
1094  }
1095}
1096
1097export function startTimeStatisticsLocation(startTimeEvent: CompileEvent): void {
1098  if (startTimeEvent) {
1099    startTimeEvent.start();
1100  }
1101}
1102
1103export function stopTimeStatisticsLocation(stopTimeEvent: CompileEvent): void {
1104  if (stopTimeEvent) {
1105    stopTimeEvent.stop();
1106  }
1107}
1108export let resolveModuleNamesTime: CompileEvent;
1109export class CompilationTimeStatistics {
1110  hookEventFactory: HookEventFactoryType;
1111  createProgramTime: CompileEvent;
1112  runArkTSLinterTime: CompileEvent;
1113  diagnosticTime: CompileEvent;
1114  scriptSnapshotTime: CompileEvent;
1115  processImportTime: CompileEvent;
1116  processComponentClassTime: CompileEvent;
1117  validateEtsTime: CompileEvent;
1118  tsProgramEmitTime: CompileEvent;
1119  shouldEmitJsTime: CompileEvent;
1120  transformNodesTime: CompileEvent;
1121  emitTime: CompileEvent;
1122  printNodeTime: CompileEvent;
1123  noSourceFileRebuildProgramTime: CompileEvent;
1124  etsTransformBuildStartTime: CompileEvent;
1125  etsTransformLoadTime: CompileEvent;
1126  processKitImportTime: CompileEvent;
1127  processUISyntaxTime: CompileEvent;
1128  constructor(share, pluginName: string, hookName: string) {
1129    if (share && share.getHookEventFactory) {
1130      if (pluginName === 'etsChecker' && hookName === 'buildStart' && share.getHookEventFactory(pluginName, hookName)) {
1131        this.hookEventFactory = share.getHookEventFactory(pluginName, hookName);
1132        this.createProgramTime = this.hookEventFactory.createEvent('createProgram');
1133        this.runArkTSLinterTime = this.hookEventFactory.createEvent('arkTSLinter');
1134        this.diagnosticTime = this.hookEventFactory.createEvent('diagnostic');
1135        this.scriptSnapshotTime = this.createProgramTime.createSubEvent('scriptSnapshot');
1136        resolveModuleNamesTime = this.hookEventFactory.createEvent('resolveModuleNames');
1137      } else if (pluginName === 'etsTransform' && hookName === 'transform' && share.getHookEventFactory(pluginName, hookName)) {
1138        this.hookEventFactory = share.getHookEventFactory(pluginName, hookName);
1139        this.validateEtsTime = this.hookEventFactory.createEvent('validateEts');
1140        this.tsProgramEmitTime = this.hookEventFactory.createEvent('tsProgramEmit');
1141        this.shouldEmitJsTime = this.hookEventFactory.createEvent('shouldEmitJs');
1142        this.transformNodesTime = this.tsProgramEmitTime.createSubEvent('transformNodes');
1143        this.emitTime = this.tsProgramEmitTime.createSubEvent('emit');
1144        this.printNodeTime = this.hookEventFactory.createEvent('printNode');
1145        this.noSourceFileRebuildProgramTime = this.hookEventFactory.createEvent('noSourceFileRebuildProgram');
1146        this.processKitImportTime = this.tsProgramEmitTime.createSubEvent('processKitImport');
1147        this.processUISyntaxTime = this.tsProgramEmitTime.createSubEvent('processUISyntax');
1148        this.processImportTime = this.processUISyntaxTime.createSubEvent('processImport');
1149        this.processComponentClassTime = this.processUISyntaxTime.createSubEvent('processComponentClass');
1150      } else if (pluginName === 'etsTransform' && hookName === 'buildStart' && share.getHookEventFactory(pluginName, hookName)) {
1151        this.hookEventFactory = share.getHookEventFactory(pluginName, hookName);
1152        this.etsTransformBuildStartTime = this.hookEventFactory.createEvent('etsTransformBuildStart');
1153      } else if (pluginName === 'etsTransform' && hookName === 'load' && share.getHookEventFactory(pluginName, hookName)) {
1154        this.hookEventFactory = share.getHookEventFactory(pluginName, hookName);
1155        this.etsTransformLoadTime = this.hookEventFactory.createEvent('etsTransformLoad');
1156      }
1157    }
1158  }
1159}
1160
1161interface HookEventFactoryType {
1162  createEvent(name: string): CompileEvent | undefined;
1163}
1164
1165type CompileEventState = 'created' | 'beginning' | 'running' | 'failed' | 'success' | 'warn';
1166interface CompileEvent {
1167  start(time?: number): CompileEvent;
1168  stop(state?: CompileEventState, time?: number): void;
1169  startAsyncEvent(time: number): CompileEvent;
1170  stopAsyncEvent(state?: CompileEventState, TIME?: number): void;
1171  createSubEvent(name: string): CompileEvent;
1172}
1173
1174export function resetUtils(): void {
1175  componentInfo = new ComponentInfo();
1176  harFilesRecord.clear();
1177  storedFileInfo = new ProcessFileInfo();
1178}
1179
1180export function getStoredFileInfo(): ProcessFileInfo {
1181  return storedFileInfo;
1182}
1183
1184export class EntryOptionValue {
1185  routeName: ts.Expression;
1186  storage: ts.Expression;
1187  useSharedStorage: ts.Expression;
1188}
1189
1190function sharedNode(): ts.CallExpression {
1191  return ts.factory.createCallExpression(
1192    ts.factory.createPropertyAccessExpression(
1193      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_LOCALSTORAGE_TYPE_PU),
1194      ts.factory.createIdentifier(GET_SHARED)
1195    ),
1196    undefined,
1197    []
1198  );
1199}
1200
1201export function createGetShared(entryOptionValue: EntryOptionValue): ts.ConditionalExpression {
1202  return ts.factory.createConditionalExpression(
1203    entryOptionValue.useSharedStorage,
1204    ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1205    sharedNode(),
1206    ts.factory.createToken(ts.SyntaxKind.ColonToken),
1207    entryOptionValue.storage || ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1208  );
1209}
1210
1211export function createGetSharedForVariable(entryOptionNode: ts.Expression, isShare: boolean = true): ts.ConditionalExpression {
1212  return ts.factory.createConditionalExpression(
1213    ts.factory.createPropertyAccessExpression(
1214      entryOptionNode,
1215      ts.factory.createIdentifier(USE_SHARED_STORAGE)
1216    ),
1217    ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1218    sharedNode(),
1219    ts.factory.createToken(ts.SyntaxKind.ColonToken),
1220    isShare ? ts.factory.createPropertyAccessExpression(
1221      entryOptionNode, ts.factory.createIdentifier(STORAGE)) :
1222      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1223  );
1224}
1225
1226export function judgeUseSharedStorageForExpresion(entryOptionNode: ts.Expression): ts.BinaryExpression {
1227  return ts.factory.createBinaryExpression(
1228    entryOptionNode,
1229    ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1230    ts.factory.createBinaryExpression(
1231      ts.factory.createPropertyAccessExpression(
1232        entryOptionNode,
1233        ts.factory.createIdentifier(USE_SHARED_STORAGE)
1234      ),
1235      ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsToken),
1236      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1237    )
1238  );
1239}
1240
1241export function getRollupCache(rollupShareObject: object, projectConfig: object, key: string): object | undefined {
1242  if (!rollupShareObject) {
1243    return undefined;
1244  }
1245
1246  // Preferentially get cache object from the rollup’s cache interface.
1247  if (rollupShareObject.cache) {
1248    // Only the cache object’s name as the cache key is required.
1249    return rollupShareObject.cache.get(key);
1250  }
1251
1252  // Try to get cache object from the rollup's cacheStoreManager interface.
1253  if (rollupShareObject.cacheStoreManager) {
1254    // The cache under cacheStoreManager is divided into two layers. The key for the first layer of cache is determined
1255    // by the SDK and runtimeOS, accessed through the `mount` interface. The key for the second layer of cache is
1256    // determined by the compilation task and the name of the cache object,
1257    // accessed through `getCache` or `setCache` interface.
1258    const cacheStoreKey: string = getRollupCacheStoreKey(projectConfig);
1259    const cacheServiceKey: string = getRollupCacheKey(projectConfig) + '#' + key;
1260
1261    // Clear the cache if the SDK path or runtimeOS changed
1262    clearRollupCacheStore(rollupShareObject.cacheStoreManager, cacheStoreKey);
1263    return rollupShareObject.cacheStoreManager.mount(cacheStoreKey).getCache(cacheServiceKey);
1264  }
1265
1266  return undefined;
1267}
1268
1269export function setRollupCache(rollupShareObject: object, projectConfig: object, key: string, value: object): void {
1270  if (!rollupShareObject) {
1271    return;
1272  }
1273
1274  // Preferentially set cache object to the rollup’s cache interface.
1275  if (rollupShareObject.cache) {
1276    // Only the cache object’s name as the cache key is required.
1277    rollupShareObject.cache.set(key, value);
1278    return;
1279  }
1280
1281  // Try to set cache object to the rollup's cacheStoreManager interface.
1282  if (rollupShareObject.cacheStoreManager) {
1283    const cacheStoreKey: string = getRollupCacheStoreKey(projectConfig);
1284    const cacheServiceKey: string = getRollupCacheKey(projectConfig) + '#' + key;
1285
1286    rollupShareObject.cacheStoreManager.mount(cacheStoreKey).setCache(cacheServiceKey, value);
1287  }
1288}
1289
1290export function removeDecorator(decorators: readonly ts.Decorator[], decoratorName: string): readonly ts.Decorator[] {
1291  return decorators.filter((item: ts.Node) => {
1292    if (ts.isDecorator(item) && ts.isIdentifier(item.expression) &&
1293      item.expression.escapedText.toString() === decoratorName) {
1294      return false;
1295    }
1296    return true;
1297  });
1298}
1299
1300export function isFileInProject(filePath: string, projectRootPath: string): boolean {
1301  const relativeFilePath: string = toUnixPath(path.relative(toUnixPath(projectRootPath), toUnixPath(filePath)));
1302  // When processing ohmurl, hsp's filePath is consistent with moduleRequest
1303  return fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../');
1304}
1305
1306export function getProjectRootPath(filePath: string, projectConfig: Object, rootPathSet: Object): string {
1307  if (rootPathSet) {
1308    for (const rootPath of rootPathSet) {
1309      if (isFileInProject(filePath, rootPath)) {
1310        return rootPath;
1311      }
1312    }
1313  }
1314  return projectConfig.projectRootPath;
1315}
1316
1317export function getBelongModuleInfo(filePath: string, modulePathMap: Object, projectRootPath: string): Object {
1318  for (const moduleName of Object.keys(modulePathMap)) {
1319    if (toUnixPath(filePath).startsWith(toUnixPath(modulePathMap[moduleName]) + '/')) {
1320      return {
1321        isLocalDependency: true,
1322        moduleName: moduleName,
1323        belongModulePath: modulePathMap[moduleName]
1324      };
1325    }
1326  }
1327  return {
1328    isLocalDependency: false,
1329    moduleName: '',
1330    belongModulePath: projectRootPath
1331  };
1332}