1/*
2 * Copyright (c) 2024 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 fs from 'fs';
17import path from 'path';
18import JSON5 from 'json5';
19
20import type * as ts from 'typescript';
21
22import { FileUtils } from '../utils/FileUtils';
23import {
24  type ReservedNameInfo,
25  ApiExtractor,
26  containWildcards,
27  getMapFromJson,
28  performancePrinter,
29  PropCollections,
30  renameFileNameModule,
31  separateUniversalReservedItem,
32  wildcardTransformer,
33} from '../ArkObfuscator';
34
35import { isDebug, isFileExist, sortAndDeduplicateStringArr, mergeSet, convertSetToArray } from './utils';
36import { nameCacheMap, yellow, unobfuscationNamesObj } from './CommonObject';
37import { historyUnobfuscatedPropMap } from './Initializer';
38import { LocalVariableCollections, UnobfuscationCollections } from '../utils/CommonCollections';
39import { INameObfuscationOption } from '../configs/INameObfuscationOption';
40import { WhitelistType } from '../utils/TransformUtil';
41
42enum OptionType {
43  NONE,
44  KEEP,
45  KEEP_DTS,
46  KEEP_GLOBAL_NAME,
47  KEEP_PROPERTY_NAME,
48  KEEP_FILE_NAME,
49  KEEP_COMMENTS,
50  DISABLE_OBFUSCATION,
51  ENABLE_PROPERTY_OBFUSCATION,
52  ENABLE_STRING_PROPERTY_OBFUSCATION,
53  ENABLE_TOPLEVEL_OBFUSCATION,
54  ENABLE_FILENAME_OBFUSCATION,
55  ENABLE_EXPORT_OBFUSCATION,
56  COMPACT,
57  REMOVE_LOG,
58  REMOVE_COMMENTS,
59  PRINT_NAMECACHE,
60  PRINT_KEPT_NAMES,
61  APPLY_NAMECACHE,
62}
63export { OptionType as OptionTypeForTest };
64
65type SystemApiContent = {
66  ReservedPropertyNames?: string[];
67  ReservedGlobalNames?: string[];
68  ReservedLocalNames?: string[];
69};
70
71/* ObConfig's properties:
72 *   ruleOptions: {
73 *    enable: boolean
74 *    rules: string[]
75 *   }
76 *   consumerRules: string[]
77 *
78 * ObfuscationConfig's properties:
79 *   selfConfig: ObConfig
80 *   dependencies: { libraries: ObConfig[], hars: string[] }
81 *   sdkApis: string[]
82 *   obfuscationCacheDir: string
83 *   exportRulePath: string
84 */
85class ObOptions {
86  disableObfuscation: boolean = false;
87  enablePropertyObfuscation: boolean = false;
88  enableStringPropertyObfuscation: boolean = false;
89  enableToplevelObfuscation: boolean = false;
90  enableFileNameObfuscation: boolean = false;
91  enableExportObfuscation: boolean = false;
92  printKeptNames: boolean = false;
93  removeComments: boolean = false;
94  compact: boolean = false;
95  removeLog: boolean = false;
96  printNameCache: string = '';
97  printKeptNamesPath: string = '';
98  applyNameCache: string = '';
99
100  merge(other: ObOptions): void {
101    this.disableObfuscation = this.disableObfuscation || other.disableObfuscation;
102    this.enablePropertyObfuscation = this.enablePropertyObfuscation || other.enablePropertyObfuscation;
103    this.enableToplevelObfuscation = this.enableToplevelObfuscation || other.enableToplevelObfuscation;
104    this.enableStringPropertyObfuscation =
105      this.enableStringPropertyObfuscation || other.enableStringPropertyObfuscation;
106    this.removeComments = this.removeComments || other.removeComments;
107    this.compact = this.compact || other.compact;
108    this.removeLog = this.removeLog || other.removeLog;
109    this.enableFileNameObfuscation = this.enableFileNameObfuscation || other.enableFileNameObfuscation;
110    this.enableExportObfuscation = this.enableExportObfuscation || other.enableExportObfuscation;
111    if (other.printNameCache.length > 0) {
112      this.printNameCache = other.printNameCache;
113    }
114    if (other.printKeptNamesPath.length > 0) {
115      this.printKeptNamesPath = other.printKeptNamesPath;
116    }
117    if (other.applyNameCache.length > 0) {
118      this.applyNameCache = other.applyNameCache;
119    }
120  }
121}
122export const ObOptionsForTest = ObOptions;
123
124export class MergedConfig {
125  options: ObOptions = new ObOptions();
126  reservedPropertyNames: string[] = [];
127  reservedGlobalNames: string[] = [];
128  reservedNames: string[] = [];
129  reservedFileNames: string[] = [];
130  keepComments: string[] = [];
131  keepSourceOfPaths: string[] = []; // The file path or folder path configured by the developer.
132  universalReservedPropertyNames: RegExp[] = []; // Support reserved property names contain wildcards.
133  universalReservedGlobalNames: RegExp[] = []; // Support reserved global names contain wildcards.
134  keepUniversalPaths: RegExp[] = []; // Support reserved paths contain wildcards.
135  excludeUniversalPaths: RegExp[] = []; // Support excluded paths contain wildcards.
136  excludePathSet: Set<string> = new Set();
137
138  merge(other: MergedConfig): void {
139    this.options.merge(other.options);
140    this.reservedPropertyNames.push(...other.reservedPropertyNames);
141    this.reservedGlobalNames.push(...other.reservedGlobalNames);
142    this.reservedFileNames.push(...other.reservedFileNames);
143    this.keepComments.push(...other.keepComments);
144    this.keepSourceOfPaths.push(...other.keepSourceOfPaths);
145    this.keepUniversalPaths.push(...other.keepUniversalPaths);
146    this.excludeUniversalPaths.push(...other.excludeUniversalPaths);
147    other.excludePathSet.forEach((excludePath) => {
148      this.excludePathSet.add(excludePath);
149    });
150  }
151
152  sortAndDeduplicate(): void {
153    this.reservedPropertyNames = sortAndDeduplicateStringArr(this.reservedPropertyNames);
154    this.reservedGlobalNames = sortAndDeduplicateStringArr(this.reservedGlobalNames);
155    this.reservedFileNames = sortAndDeduplicateStringArr(this.reservedFileNames);
156    this.keepComments = sortAndDeduplicateStringArr(this.keepComments);
157    this.keepSourceOfPaths = sortAndDeduplicateStringArr(this.keepSourceOfPaths);
158  }
159
160  serializeMergedConfig(): string {
161    let resultStr: string = '';
162    const keys = Object.keys(this.options);
163    for (const key of keys) {
164      // skip the export of some switches.
165      if (this.options[key] === true && ObConfigResolver.exportedSwitchMap.has(String(key))) {
166        resultStr += ObConfigResolver.exportedSwitchMap.get(String(key)) + '\n';
167      }
168    }
169
170    if (this.reservedGlobalNames.length > 0) {
171      resultStr += ObConfigResolver.KEEP_GLOBAL_NAME + '\n';
172      this.reservedGlobalNames.forEach((item) => {
173        resultStr += item + '\n';
174      });
175    }
176    if (this.reservedPropertyNames.length > 0) {
177      resultStr += ObConfigResolver.KEEP_PROPERTY_NAME + '\n';
178      this.reservedPropertyNames.forEach((item) => {
179        resultStr += item + '\n';
180      });
181    }
182    return resultStr;
183  }
184}
185
186export class ObConfigResolver {
187  sourceObConfig: any;
188  logger: any;
189  isHarCompiled: boolean | undefined;
190  isTerser: boolean;
191
192  constructor(projectConfig: any, logger: any, isTerser?: boolean) {
193    this.sourceObConfig = projectConfig.obfuscationOptions;
194    this.logger = logger;
195    this.isHarCompiled = projectConfig.compileHar;
196    this.isTerser = isTerser;
197  }
198
199  public resolveObfuscationConfigs(): MergedConfig {
200    let sourceObConfig = this.sourceObConfig;
201    if (!sourceObConfig) {
202      return new MergedConfig();
203    }
204    let enableObfuscation: boolean = sourceObConfig.selfConfig.ruleOptions.enable;
205
206    let selfConfig: MergedConfig = new MergedConfig();
207    if (enableObfuscation) {
208      this.getSelfConfigs(selfConfig);
209      enableObfuscation = !selfConfig.options.disableObfuscation;
210    } else {
211      selfConfig.options.disableObfuscation = true;
212    }
213
214    let needConsumerConfigs: boolean =
215      this.isHarCompiled &&
216      sourceObConfig.selfConfig.consumerRules &&
217      sourceObConfig.selfConfig.consumerRules.length > 0;
218    let needDependencyConfigs: boolean = enableObfuscation || needConsumerConfigs;
219
220    let dependencyConfigs: MergedConfig = new MergedConfig();
221    const dependencyMaxLength: number = Math.max(
222      sourceObConfig.dependencies.libraries.length,
223      sourceObConfig.dependencies.hars.length,
224    );
225    if (needDependencyConfigs && dependencyMaxLength > 0) {
226      dependencyConfigs = new MergedConfig();
227      this.getDependencyConfigs(sourceObConfig, dependencyConfigs);
228      enableObfuscation = enableObfuscation && !dependencyConfigs.options.disableObfuscation;
229    }
230    const mergedConfigs: MergedConfig = this.getMergedConfigs(selfConfig, dependencyConfigs);
231    UnobfuscationCollections.printKeptName = mergedConfigs.options.printKeptNames;
232    this.handleReservedArray(mergedConfigs);
233
234    let needKeepSystemApi =
235      enableObfuscation &&
236      (mergedConfigs.options.enablePropertyObfuscation ||
237        (mergedConfigs.options.enableExportObfuscation && mergedConfigs.options.enableToplevelObfuscation));
238
239    if (needKeepSystemApi && sourceObConfig.obfuscationCacheDir) {
240      const systemApiCachePath: string = path.join(sourceObConfig.obfuscationCacheDir, 'systemApiCache.json');
241      if (isFileExist(systemApiCachePath)) {
242        this.getSystemApiConfigsByCache(systemApiCachePath);
243      } else {
244        performancePrinter?.iniPrinter?.startEvent('  Scan system api');
245        this.getSystemApiCache(mergedConfigs, systemApiCachePath);
246        performancePrinter?.iniPrinter?.endEvent('  Scan system api');
247      }
248    }
249
250    if (needConsumerConfigs) {
251      let selfConsumerConfig = new MergedConfig();
252      this.getSelfConsumerConfig(selfConsumerConfig);
253      this.genConsumerConfigFiles(sourceObConfig, selfConsumerConfig, dependencyConfigs);
254    }
255    return mergedConfigs;
256  }
257
258  private getSelfConfigs(selfConfigs: MergedConfig): void {
259    if (this.sourceObConfig.selfConfig.ruleOptions.rules) {
260      const configPaths: string[] = this.sourceObConfig.selfConfig.ruleOptions.rules;
261      for (const path of configPaths) {
262        this.getConfigByPath(path, selfConfigs);
263      }
264    }
265  }
266
267  public getSelfConfigsForTest(selfConfigs: MergedConfig): void {
268    return this.getSelfConfigs(selfConfigs);
269  }
270
271  private getConfigByPath(path: string, configs: MergedConfig): void {
272    let fileContent = undefined;
273    try {
274      fileContent = fs.readFileSync(path, 'utf-8');
275    } catch (err) {
276      this.logger.error(`Failed to open ${path}. Error message: ${err}`);
277      throw err;
278    }
279    this.handleConfigContent(fileContent, configs, path);
280  }
281  
282  public getConfigByPathForTest(path: string, configs: MergedConfig): void {
283    return this.getConfigByPath(path, configs);
284  }
285
286  private handleReservedArray(mergedConfigs: MergedConfig): void {
287    if (mergedConfigs.options.enablePropertyObfuscation && mergedConfigs.reservedPropertyNames) {
288      const propertyReservedInfo: ReservedNameInfo = separateUniversalReservedItem(mergedConfigs.reservedPropertyNames);
289      mergedConfigs.universalReservedPropertyNames = propertyReservedInfo.universalReservedArray;
290      mergedConfigs.reservedPropertyNames = propertyReservedInfo.specificReservedArray;
291    }
292
293    if (mergedConfigs.options.enableToplevelObfuscation && mergedConfigs.reservedGlobalNames) {
294      const globalReservedInfo: ReservedNameInfo = separateUniversalReservedItem(mergedConfigs.reservedGlobalNames);
295      mergedConfigs.universalReservedGlobalNames = globalReservedInfo.universalReservedArray;
296      mergedConfigs.reservedGlobalNames = globalReservedInfo.specificReservedArray;
297    }
298  }
299
300  public handleReservedArrayForTest(mergedConfigs: MergedConfig): void {
301    return this.handleReservedArray(mergedConfigs);
302  }
303
304  // obfuscation options
305  static readonly KEEP = '-keep';
306  static readonly KEEP_DTS = '-keep-dts';
307  static readonly KEEP_GLOBAL_NAME = '-keep-global-name';
308  static readonly KEEP_PROPERTY_NAME = '-keep-property-name';
309  static readonly KEEP_FILE_NAME = '-keep-file-name';
310  static readonly KEEP_COMMENTS = '-keep-comments';
311  static readonly DISABLE_OBFUSCATION = '-disable-obfuscation';
312  static readonly ENABLE_PROPERTY_OBFUSCATION = '-enable-property-obfuscation';
313  static readonly ENABLE_STRING_PROPERTY_OBFUSCATION = '-enable-string-property-obfuscation';
314  static readonly ENABLE_TOPLEVEL_OBFUSCATION = '-enable-toplevel-obfuscation';
315  static readonly ENABLE_FILENAME_OBFUSCATION = '-enable-filename-obfuscation';
316  static readonly ENABLE_EXPORT_OBFUSCATION = '-enable-export-obfuscation';
317  static readonly REMOVE_COMMENTS = '-remove-comments';
318  static readonly COMPACT = '-compact';
319  static readonly REMOVE_LOG = '-remove-log';
320  static readonly PRINT_NAMECACHE = '-print-namecache';
321  static readonly PRINT_KEPT_NAMES = '-print-kept-names';
322  static readonly APPLY_NAMECACHE = '-apply-namecache';
323
324  // renameFileName, printNameCache, applyNameCache, removeComments and keepComments won't be reserved in obfuscation.txt file.
325  static exportedSwitchMap: Map<string, string> = new Map([
326    ['disableObfuscation', ObConfigResolver.KEEP_DTS],
327    ['enablePropertyObfuscation', ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION],
328    ['enableStringPropertyObfuscation', ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION],
329    ['enableToplevelObfuscation', ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION],
330    ['compact', ObConfigResolver.COMPACT],
331    ['removeLog', ObConfigResolver.REMOVE_LOG],
332  ]);
333
334  private getTokenType(token: string): OptionType {
335    switch (token) {
336      case ObConfigResolver.KEEP_DTS:
337        return OptionType.KEEP_DTS;
338      case ObConfigResolver.KEEP_GLOBAL_NAME:
339        return OptionType.KEEP_GLOBAL_NAME;
340      case ObConfigResolver.KEEP_PROPERTY_NAME:
341        return OptionType.KEEP_PROPERTY_NAME;
342      case ObConfigResolver.KEEP_FILE_NAME:
343        return OptionType.KEEP_FILE_NAME;
344      case ObConfigResolver.KEEP_COMMENTS:
345        return OptionType.KEEP_COMMENTS;
346      case ObConfigResolver.DISABLE_OBFUSCATION:
347        return OptionType.DISABLE_OBFUSCATION;
348      case ObConfigResolver.ENABLE_PROPERTY_OBFUSCATION:
349        return OptionType.ENABLE_PROPERTY_OBFUSCATION;
350      case ObConfigResolver.ENABLE_STRING_PROPERTY_OBFUSCATION:
351        return OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION;
352      case ObConfigResolver.ENABLE_TOPLEVEL_OBFUSCATION:
353        return OptionType.ENABLE_TOPLEVEL_OBFUSCATION;
354      case ObConfigResolver.ENABLE_FILENAME_OBFUSCATION:
355        return OptionType.ENABLE_FILENAME_OBFUSCATION;
356      case ObConfigResolver.ENABLE_EXPORT_OBFUSCATION:
357        return OptionType.ENABLE_EXPORT_OBFUSCATION;
358      case ObConfigResolver.REMOVE_COMMENTS:
359        return OptionType.REMOVE_COMMENTS;
360      case ObConfigResolver.COMPACT:
361        return OptionType.COMPACT;
362      case ObConfigResolver.REMOVE_LOG:
363        return OptionType.REMOVE_LOG;
364      case ObConfigResolver.PRINT_NAMECACHE:
365        return OptionType.PRINT_NAMECACHE;
366      case ObConfigResolver.PRINT_KEPT_NAMES:
367        return OptionType.PRINT_KEPT_NAMES;
368      case ObConfigResolver.APPLY_NAMECACHE:
369        return OptionType.APPLY_NAMECACHE;
370      case ObConfigResolver.KEEP:
371        return OptionType.KEEP;
372      default:
373        return OptionType.NONE;
374    }
375  }
376
377  public getTokenTypeForTest(token: string): OptionType {
378    return this.getTokenType(token);
379  }
380
381  private handleConfigContent(data: string, configs: MergedConfig, configPath: string): void {
382    data = this.removeComments(data);
383    const tokens = data.split(/[',', '\t', ' ', '\n', '\r\n']/).filter((item) => item !== '');
384    let type: OptionType = OptionType.NONE;
385    let tokenType: OptionType;
386    let dtsFilePaths: string[] = [];
387    let keepConfigs: string[] = [];
388    for (let i = 0; i < tokens.length; i++) {
389      const token = tokens[i];
390      tokenType = this.getTokenType(token);
391      // handle switches cases
392      switch (tokenType) {
393        case OptionType.DISABLE_OBFUSCATION: {
394          configs.options.disableObfuscation = true;
395          continue;
396        }
397        case OptionType.ENABLE_PROPERTY_OBFUSCATION: {
398          configs.options.enablePropertyObfuscation = true;
399          continue;
400        }
401        case OptionType.ENABLE_STRING_PROPERTY_OBFUSCATION: {
402          configs.options.enableStringPropertyObfuscation = true;
403          continue;
404        }
405        case OptionType.ENABLE_TOPLEVEL_OBFUSCATION: {
406          configs.options.enableToplevelObfuscation = true;
407          continue;
408        }
409        case OptionType.REMOVE_COMMENTS: {
410          configs.options.removeComments = true;
411          continue;
412        }
413        case OptionType.ENABLE_FILENAME_OBFUSCATION: {
414          configs.options.enableFileNameObfuscation = true;
415          continue;
416        }
417        case OptionType.ENABLE_EXPORT_OBFUSCATION: {
418          configs.options.enableExportObfuscation = true;
419          continue;
420        }
421        case OptionType.COMPACT: {
422          configs.options.compact = true;
423          continue;
424        }
425        case OptionType.REMOVE_LOG: {
426          configs.options.removeLog = true;
427          continue;
428        }
429        case OptionType.PRINT_KEPT_NAMES: {
430          configs.options.printKeptNames = true;
431          type = tokenType;
432          continue;
433        }
434        case OptionType.KEEP:
435        case OptionType.KEEP_DTS:
436        case OptionType.KEEP_GLOBAL_NAME:
437        case OptionType.KEEP_PROPERTY_NAME:
438        case OptionType.KEEP_FILE_NAME:
439        case OptionType.KEEP_COMMENTS:
440        case OptionType.PRINT_NAMECACHE:
441        case OptionType.APPLY_NAMECACHE:
442          type = tokenType;
443          continue;
444        default: {
445          // fall-through
446        }
447      }
448      // handle 'keep' options and 'namecache' options
449      switch (type) {
450        case OptionType.KEEP: {
451          keepConfigs.push(token);
452          continue;
453        }
454        case OptionType.KEEP_DTS: {
455          dtsFilePaths.push(token);
456          continue;
457        }
458        case OptionType.KEEP_GLOBAL_NAME: {
459          configs.reservedGlobalNames.push(token);
460          continue;
461        }
462        case OptionType.KEEP_PROPERTY_NAME: {
463          configs.reservedPropertyNames.push(token);
464          continue;
465        }
466        case OptionType.KEEP_FILE_NAME: {
467          configs.reservedFileNames.push(token);
468          continue;
469        }
470        case OptionType.KEEP_COMMENTS: {
471          configs.keepComments.push(token);
472          continue;
473        }
474        case OptionType.PRINT_NAMECACHE: {
475          configs.options.printNameCache = this.resolvePath(configPath, token);
476          type = OptionType.NONE;
477          continue;
478        }
479        case OptionType.PRINT_KEPT_NAMES: {
480          configs.options.printKeptNamesPath = this.resolvePath(configPath, token);
481          type = OptionType.NONE;
482          continue;
483        }
484        case OptionType.APPLY_NAMECACHE: {
485          const absNameCachePath: string = this.resolvePath(configPath, token);
486          this.determineNameCachePath(absNameCachePath, configPath);
487          configs.options.applyNameCache = absNameCachePath;
488          type = OptionType.NONE;
489          continue;
490        }
491        default:
492          continue;
493      }
494    }
495
496    this.resolveDts(dtsFilePaths, configs);
497    this.resolveKeepConfig(keepConfigs, configs, configPath);
498  }
499
500  public handleConfigContentForTest(data: string, configs: MergedConfig, configPath: string): void {
501    return this.handleConfigContent(data, configs, configPath);
502  } 
503  // get absolute path
504  private resolvePath(configPath: string, token: string): string {
505    if (path.isAbsolute(token)) {
506      return token;
507    }
508    const configDirectory = path.dirname(configPath);
509    return path.resolve(configDirectory, token);
510  }
511
512  public resolvePathForTest(configPath: string, token: string): string {
513    return this.resolvePath(configPath, token);
514  }
515
516  // get names in .d.ts files and add them into reserved list
517  private resolveDts(dtsFilePaths: string[], configs: MergedConfig): void {
518    ApiExtractor.mPropertySet.clear();
519    dtsFilePaths.forEach((token) => {
520      ApiExtractor.traverseApiFiles(token, ApiExtractor.ApiType.PROJECT);
521    });
522    configs.reservedNames = configs.reservedNames.concat([...ApiExtractor.mPropertySet]);
523    configs.reservedPropertyNames = configs.reservedPropertyNames.concat([...ApiExtractor.mPropertySet]);
524    configs.reservedGlobalNames = configs.reservedGlobalNames.concat([...ApiExtractor.mPropertySet]);
525    ApiExtractor.mPropertySet.clear();
526  }
527
528  public resolveKeepConfig(keepConfigs: string[], configs: MergedConfig, configPath: string): void {
529    for (let keepPath of keepConfigs) {
530      let tempAbsPath: string;
531      const isExclude: boolean = keepPath.startsWith('!');
532      // 1: remove '!'
533      tempAbsPath = FileUtils.getAbsPathBaseConfigPath(configPath, isExclude ? keepPath.substring(1) : keepPath);
534
535      // contains '*', '?'
536      if (containWildcards(tempAbsPath)) {
537        const regexPattern = wildcardTransformer(tempAbsPath, true);
538        const regexOperator = new RegExp(`^${regexPattern}$`);
539        if (isExclude) {
540          // start with '!'
541          configs.excludeUniversalPaths.push(regexOperator);
542        } else {
543          configs.keepUniversalPaths.push(regexOperator);
544        }
545        continue;
546      }
547
548      if (isExclude) {
549        // exclude specific path
550        configs.excludePathSet.add(tempAbsPath);
551        continue;
552      }
553
554      if (!fs.existsSync(tempAbsPath)) {
555        this.logger.warn(yellow + 'ArkTS: The path of obfuscation \'-keep\' configuration does not exist: ' + keepPath);
556        continue;
557      }
558      tempAbsPath = fs.realpathSync(tempAbsPath);
559      configs.keepSourceOfPaths.push(FileUtils.toUnixPath(tempAbsPath));
560    }
561  }
562
563  // the content from '#' to '\n' are comments
564  private removeComments(data: string): string {
565    const commentStart = '#';
566    const commentEnd = '\n';
567    let tmpStr = '';
568    let isInComments = false;
569    for (let i = 0; i < data.length; i++) {
570      if (isInComments) {
571        isInComments = data[i] !== commentEnd;
572      } else if (data[i] !== commentStart) {
573        tmpStr += data[i];
574      } else {
575        isInComments = true;
576      }
577    }
578    return tmpStr;
579  }
580
581  /**
582   * systemConfigs includes the API directorys.
583   * component directory and pre_define.js file path needs to be concatenated
584   * @param systemConfigs
585   */
586  private getSystemApiCache(systemConfigs: MergedConfig, systemApiCachePath: string): void {
587    ApiExtractor.mPropertySet.clear();
588    ApiExtractor.mSystemExportSet.clear();
589
590    interface ArkUIWhitelist {
591      ReservedPropertyNames: string[]
592    }
593    let arkUIWhitelist: ArkUIWhitelist = { ReservedPropertyNames: [] };
594    const sdkApis: string[] = sortAndDeduplicateStringArr(this.sourceObConfig.sdkApis);
595    for (let apiPath of sdkApis) {
596      this.getSdkApiCache(apiPath);
597      const UIPath: string = path.join(apiPath, '../build-tools/ets-loader/lib/pre_define.js');
598      if (fs.existsSync(UIPath)) {
599        this.getUIApiCache(UIPath);
600      }
601      const arkUIWhitelistPath: string = path.join(apiPath, '../build-tools/ets-loader/obfuscateWhiteList.json5');
602      if (fs.existsSync(arkUIWhitelistPath)) {
603        arkUIWhitelist = JSON5.parse(fs.readFileSync(arkUIWhitelistPath, 'utf-8'));
604      }
605    }
606    let systemApiContent: SystemApiContent = {};
607
608    if (systemConfigs.options.enablePropertyObfuscation) {
609      const savedNameAndPropertySet = new Set([...ApiExtractor.mPropertySet, ...arkUIWhitelist.ReservedPropertyNames]);
610      UnobfuscationCollections.reservedSdkApiForProp = savedNameAndPropertySet;
611      UnobfuscationCollections.reservedSdkApiForLocal = new Set(ApiExtractor.mPropertySet);
612      systemApiContent.ReservedPropertyNames = Array.from(savedNameAndPropertySet);
613      systemApiContent.ReservedLocalNames = Array.from(ApiExtractor.mPropertySet);
614    }
615    if (systemConfigs.options.enableToplevelObfuscation && systemConfigs.options.enableExportObfuscation) {
616      const savedExportNamesSet = new Set(ApiExtractor.mSystemExportSet);
617      UnobfuscationCollections.reservedSdkApiForGlobal = savedExportNamesSet;
618      systemApiContent.ReservedGlobalNames = Array.from(savedExportNamesSet);
619    }
620
621    if (!fs.existsSync(path.dirname(systemApiCachePath))) {
622      fs.mkdirSync(path.dirname(systemApiCachePath), { recursive: true });
623    }
624    fs.writeFileSync(systemApiCachePath, JSON.stringify(systemApiContent, null, 2));
625    ApiExtractor.mPropertySet.clear();
626    ApiExtractor.mSystemExportSet.clear();
627  }
628
629  public getSystemApiCacheForTest(systemConfigs: MergedConfig, systemApiCachePath: string): void {
630    return this.getSystemApiCache(systemConfigs, systemApiCachePath);
631  }
632
633  private getSdkApiCache(sdkApiPath: string): void {
634    ApiExtractor.traverseApiFiles(sdkApiPath, ApiExtractor.ApiType.API);
635    const componentPath: string = path.join(sdkApiPath, '../component');
636    if (fs.existsSync(componentPath)) {
637      ApiExtractor.traverseApiFiles(componentPath, ApiExtractor.ApiType.COMPONENT);
638    }
639  }
640
641  private getUIApiCache(uiApiPath: string): void {
642    ApiExtractor.extractStringsFromFile(uiApiPath);
643  }
644
645  private getDependencyConfigs(sourceObConfig: any, dependencyConfigs: MergedConfig): void {
646    for (const lib of sourceObConfig.dependencies.libraries || []) {
647      if (lib.consumerRules && lib.consumerRules.length > 0) {
648        for (const path of lib.consumerRules) {
649          const thisLibConfigs = new MergedConfig();
650          this.getConfigByPath(path, dependencyConfigs);
651          dependencyConfigs.merge(thisLibConfigs);
652        }
653      }
654    }
655
656    if (
657      sourceObConfig.dependencies &&
658      sourceObConfig.dependencies.hars &&
659      sourceObConfig.dependencies.hars.length > 0
660    ) {
661      for (const path of sourceObConfig.dependencies.hars) {
662        const thisHarConfigs = new MergedConfig();
663        this.getConfigByPath(path, dependencyConfigs);
664        dependencyConfigs.merge(thisHarConfigs);
665      }
666    }
667  }
668
669  public getDependencyConfigsForTest(sourceObConfig: any, dependencyConfigs: MergedConfig): void {
670    return this.getDependencyConfigs(sourceObConfig, dependencyConfigs);
671  }
672
673  private getSystemApiConfigsByCache(systemApiCachePath: string): void {
674    let systemApiContent: {
675      ReservedPropertyNames?: string[];
676      ReservedGlobalNames?: string[];
677      ReservedLocalNames?: string[];
678    } = JSON.parse(fs.readFileSync(systemApiCachePath, 'utf-8'));
679    if (systemApiContent.ReservedPropertyNames) {
680      UnobfuscationCollections.reservedSdkApiForProp = new Set(systemApiContent.ReservedPropertyNames);
681    }
682    if (systemApiContent.ReservedGlobalNames) {
683      UnobfuscationCollections.reservedSdkApiForGlobal = new Set(systemApiContent.ReservedGlobalNames);
684    }
685    if (systemApiContent.ReservedLocalNames) {
686      UnobfuscationCollections.reservedSdkApiForLocal = new Set(systemApiContent.ReservedLocalNames);
687    }
688  }
689
690  public getSystemApiConfigsByCacheForTest(systemApiCachePath: string): void {
691    return this.getSystemApiConfigsByCache(systemApiCachePath);
692  }
693
694  private getSelfConsumerConfig(selfConsumerConfig: MergedConfig): void {
695    for (const path of this.sourceObConfig.selfConfig.consumerRules) {
696      this.getConfigByPath(path, selfConsumerConfig);
697    }
698  }
699
700  public getSelfConsumerConfigForTest(selfConsumerConfig: MergedConfig): void {
701    return this.getSelfConsumerConfig(selfConsumerConfig);
702  }
703
704  private getMergedConfigs(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig {
705    if (dependencyConfigs) {
706      selfConfigs.merge(dependencyConfigs);
707    }
708    selfConfigs.sortAndDeduplicate();
709    return selfConfigs;
710  }
711
712  public getMergedConfigsForTest(selfConfigs: MergedConfig, dependencyConfigs: MergedConfig): MergedConfig {
713    return this.getMergedConfigs(selfConfigs, dependencyConfigs);
714  }
715
716  private genConsumerConfigFiles(
717    sourceObConfig: any,
718    selfConsumerConfig: MergedConfig,
719    dependencyConfigs: MergedConfig,
720  ): void {
721    selfConsumerConfig.merge(dependencyConfigs);
722    selfConsumerConfig.sortAndDeduplicate();
723    this.writeConsumerConfigFile(selfConsumerConfig, sourceObConfig.exportRulePath);
724  }
725
726  public genConsumerConfigFilesForTest(
727    sourceObConfig: any,
728    selfConsumerConfig: MergedConfig,
729    dependencyConfigs: MergedConfig,
730  ): void {
731    return this.genConsumerConfigFiles(sourceObConfig, selfConsumerConfig, dependencyConfigs);
732  }
733
734  public writeConsumerConfigFile(selfConsumerConfig: MergedConfig, outpath: string): void {
735    const configContent: string = selfConsumerConfig.serializeMergedConfig();
736    fs.writeFileSync(outpath, configContent);
737  }
738
739  private determineNameCachePath(nameCachePath: string, configPath: string): void {
740    if (!fs.existsSync(nameCachePath)) {
741      throw new Error(`The applied namecache file '${nameCachePath}' configured by '${configPath}' does not exist.`);
742    }
743  }
744}
745
746/**
747 * Collect reserved file name configured in oh-package.json5 and module.json5.
748 * @param ohPackagePath The 'main' and 'types' fileds in oh-package.json5 need to be reserved.
749 * @param projectConfig Several paths or file contents in projectconfig need to be reserved.
750 *   1: module.json's 'srcEntry' field
751 *   2: projectPath: /library/src/main/ets
752 *   3: cachePath: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release
753 *      target reserved path: /library/build/default/cache/default/default@HarCompileArkTs/esmodules/release/src/main/ets
754 *   4: aceModuleBuild/etsFortgz directory: /library/build/default/intermediates/loader_out/etsFortgz
755 *      If compile the hsp module, the declaration file will be written to the 'aceModuleBuild/etsFortgz' directory.
756 * @param modulePathMap packageName of local har package should be reserved as it is a fixed part of ohmUrl.
757 *   example: modulePathMap: { packageName: path }
758 * @returns reservedFileNames
759 */
760export function collectResevedFileNameInIDEConfig(
761  ohPackagePath: string,
762  projectConfig: any,
763  modulePathMap: Object | undefined,
764  entryArray: Array<string> | undefined,
765): string[] {
766  const reservedFileNames: string[] = [];
767  const moduleJsonPath: string = projectConfig.aceModuleJsonPath;
768  const projectPath: string = projectConfig.projectPath;
769  const cachePath: string = projectConfig.cachePath;
770
771  if (entryArray) {
772    entryArray.forEach((element) => {
773      FileUtils.collectPathReservedString(element, reservedFileNames);
774    });
775  }
776
777  if (modulePathMap) {
778    const modulePaths = Object.values(modulePathMap);
779    const moduleNames = Object.keys(modulePathMap);
780    modulePaths.forEach((val) => {
781      FileUtils.collectPathReservedString(val, reservedFileNames);
782    });
783    moduleNames.forEach((val) => {
784      FileUtils.collectPathReservedString(val, reservedFileNames);
785    });
786  }
787  if (fs.existsSync(ohPackagePath)) {
788    const ohPackageContent = JSON5.parse(fs.readFileSync(ohPackagePath, 'utf-8'));
789    ohPackageContent.main && FileUtils.collectPathReservedString(ohPackageContent.main, reservedFileNames);
790    ohPackageContent.types && FileUtils.collectPathReservedString(ohPackageContent.types, reservedFileNames);
791  }
792
793  if (fs.existsSync(moduleJsonPath)) {
794    const moduleJsonContent = JSON5.parse(fs.readFileSync(moduleJsonPath, 'utf-8'));
795    moduleJsonContent.module?.srcEntry &&
796      FileUtils.collectPathReservedString(moduleJsonContent.module?.srcEntry, reservedFileNames);
797  }
798
799  if (projectConfig.compileShared || projectConfig.byteCodeHar) {
800    FileUtils.collectPathReservedString(projectConfig.aceModuleBuild, reservedFileNames);
801    reservedFileNames.push('etsFortgz');
802  }
803
804  FileUtils.collectPathReservedString(projectPath, reservedFileNames);
805  FileUtils.collectPathReservedString(cachePath, reservedFileNames);
806  return reservedFileNames;
807}
808
809export function readNameCache(nameCachePath: string, logger: any): void {
810  try {
811    const fileContent = fs.readFileSync(nameCachePath, 'utf-8');
812    const nameCache: {
813      compileSdkVersion?: string;
814      [key: string]: Object;
815      PropertyCache?: Object;
816      FileNameCache?: Object;
817    } = JSON.parse(fileContent);
818    if (nameCache.PropertyCache) {
819      PropCollections.historyMangledTable = getMapFromJson(nameCache.PropertyCache);
820    }
821    if (nameCache.FileNameCache) {
822      renameFileNameModule.historyFileNameMangledTable = getMapFromJson(nameCache.FileNameCache);
823    }
824
825    const { compileSdkVersion, PropertyCache, FileNameCache, ...rest } = nameCache;
826    Object.keys(rest).forEach((key) => {
827      nameCacheMap.set(key, rest[key]);
828    });
829  } catch (err) {
830    logger.error(`Failed to open ${nameCachePath}. Error message: ${err}`);
831  }
832}
833
834/**
835 * collect the reserved or excluded paths containing wildcards
836 */
837export function handleUniversalPathInObf(mergedObConfig: MergedConfig, allSourceFilePaths: Set<string>): void {
838  if (
839    !mergedObConfig ||
840    (mergedObConfig.keepUniversalPaths.length === 0 && mergedObConfig.excludeUniversalPaths.length === 0)
841  ) {
842    return;
843  }
844  for (const realFilePath of allSourceFilePaths) {
845    let isReserved = false;
846    for (const universalPath of mergedObConfig.keepUniversalPaths) {
847      if (universalPath.test(realFilePath)) {
848        isReserved = true;
849        break;
850      }
851    }
852    for (const excludePath of mergedObConfig.excludeUniversalPaths) {
853      if (excludePath.test(realFilePath)) {
854        isReserved = false;
855        mergedObConfig.excludePathSet.add(realFilePath);
856        break;
857      }
858    }
859    if (isReserved) {
860      mergedObConfig.keepSourceOfPaths.push(realFilePath);
861    }
862  }
863}
864
865export function getArkguardNameCache(
866  enablePropertyObfuscation: boolean,
867  enableFileNameObfuscation: boolean,
868  enableExportObfuscation: boolean,
869  sdkVersion: string,
870  entryPackageInfo: string,
871): string {
872  let writeContent: string = '';
873  let nameCacheCollection: {
874    compileSdkVersion?: string;
875    PropertyCache?: Object;
876    FileNameCache?: Object;
877    entryPackageInfo?: string;
878  } = Object.fromEntries(nameCacheMap.entries());
879  nameCacheCollection.compileSdkVersion = sdkVersion;
880  nameCacheCollection.entryPackageInfo = entryPackageInfo;
881
882  if (enablePropertyObfuscation || enableExportObfuscation) {
883    const mergedPropertyNameCache: Map<string, string> = new Map();
884    fillNameCache(PropCollections.historyMangledTable, mergedPropertyNameCache);
885    fillNameCache(PropCollections.globalMangledTable, mergedPropertyNameCache);
886    nameCacheCollection.PropertyCache = Object.fromEntries(mergedPropertyNameCache);
887  }
888
889  if (enableFileNameObfuscation) {
890    const mergedFileNameCache: Map<string, string> = new Map();
891    fillNameCache(renameFileNameModule.historyFileNameMangledTable, mergedFileNameCache);
892    fillNameCache(renameFileNameModule.globalFileNameMangledTable, mergedFileNameCache);
893    nameCacheCollection.FileNameCache = Object.fromEntries(mergedFileNameCache);
894  }
895
896  writeContent += JSON.stringify(nameCacheCollection, null, 2);
897  return writeContent;
898}
899
900// export fillNameCache function
901export function fillNameCache(table: Map<string, string>, nameCache: Map<string, string>): void {
902  if (table) {
903    for (const [key, value] of table.entries()) {
904      nameCache.set(key, value);
905    }
906  }
907  return;
908}
909
910export function writeObfuscationNameCache(
911  projectConfig: any,
912  entryPackageInfo: string,
913  obfuscationCacheDir?: string,
914  printNameCache?: string,
915): void {
916  if (!projectConfig.arkObfuscator) {
917    return;
918  }
919  let options = projectConfig.obfuscationMergedObConfig.options;
920  let writeContent = getArkguardNameCache(
921    options.enablePropertyObfuscation,
922    options.enableFileNameObfuscation,
923    options.enableExportObfuscation,
924    projectConfig.etsLoaderVersion,
925    entryPackageInfo,
926  );
927  if (obfuscationCacheDir && obfuscationCacheDir.length > 0) {
928    const defaultNameCachePath: string = path.join(obfuscationCacheDir, 'nameCache.json');
929    if (!fs.existsSync(path.dirname(defaultNameCachePath))) {
930      fs.mkdirSync(path.dirname(defaultNameCachePath), { recursive: true });
931    }
932    fs.writeFileSync(defaultNameCachePath, writeContent);
933  }
934  if (printNameCache && printNameCache.length > 0) {
935    fs.writeFileSync(printNameCache, writeContent);
936  }
937}
938
939// Print unobfuscation names, reasons and whitelist, if -print-kept-names is enabled.
940export function writeUnobfuscationContent(projectConfig: any): void {
941  let obfuscationOptions = projectConfig.obfuscationMergedObConfig.options;
942  let unobfuscationOptions = projectConfig.arkObfuscator.mCustomProfiles.mUnobfuscationOption;
943  let nameOptions = projectConfig.arkObfuscator.mCustomProfiles.mNameObfuscation;
944  if (!unobfuscationOptions.mPrintKeptNames) {
945    return;
946  }
947
948  let configPath = unobfuscationOptions.mPrintPath;
949  let printDir = projectConfig.obfuscationOptions.obfuscationCacheDir;
950  let printUnobfPath = path.join(printDir, 'keptNames.json');
951  printUnobfuscationReasons(configPath, printUnobfPath);
952  let printWhitelistPath = path.join(printDir, 'whitelist.json');
953  printWhitelist(obfuscationOptions, nameOptions, printWhitelistPath);
954}
955
956// Merge similar whitelists and output according to whether the corresponding options are enabled.
957export function printWhitelist(obfuscationOptions: ObOptions, nameOptions: INameObfuscationOption, defaultPath: string): void {
958  const enableToplevel = obfuscationOptions.enableToplevelObfuscation;
959  const enableProperty = obfuscationOptions.enablePropertyObfuscation;
960  const enableStringProp = obfuscationOptions.enableStringPropertyObfuscation;
961  const enableExport = obfuscationOptions.enableExportObfuscation;
962  const reservedConfToplevelArrary = nameOptions.mReservedToplevelNames ?? [];
963  const reservedConfPropertyArray = nameOptions.mReservedProperties ?? [];
964  let whitelistObj = {
965    lang: [],
966    conf: [],
967    struct: [],
968    exported: [],
969    strProp: [],
970    enum: []
971  };
972
973  let languareSet: Set<string>;
974  if (enableProperty) {
975    languareSet = mergeSet(UnobfuscationCollections.reservedLangForProperty, LocalVariableCollections.reservedLangForLocal);
976  } else {
977    languareSet = LocalVariableCollections.reservedLangForLocal;
978  }
979  whitelistObj.lang = convertSetToArray(languareSet);
980
981  let structSet: Set<string>;
982  if (enableProperty) {
983    structSet = mergeSet(UnobfuscationCollections.reservedStruct, LocalVariableCollections.reservedStruct);
984  } else {
985    structSet = LocalVariableCollections.reservedStruct;
986  }
987  whitelistObj.struct = convertSetToArray(structSet);
988
989  let exportedSet: Set<string>;
990  if (enableProperty) {
991    exportedSet = UnobfuscationCollections.reservedExportNameAndProp;
992  } else if (enableExport) {
993    exportedSet = UnobfuscationCollections.reservedExportName;
994  }
995  whitelistObj.exported = convertSetToArray(exportedSet);
996
997  let stringSet: Set<string>;
998  if (enableProperty && !enableStringProp) {
999    stringSet = UnobfuscationCollections.reservedStrProp;
1000  }
1001  whitelistObj.strProp = convertSetToArray(stringSet);
1002
1003  whitelistObj.conf = convertSetToArray(LocalVariableCollections.reservedConfig);
1004  const hasPropertyConfig = enableProperty && reservedConfPropertyArray?.length > 0;
1005  const hasTopLevelConfig = enableToplevel && reservedConfToplevelArrary?.length > 0;
1006  if (hasPropertyConfig) {
1007    whitelistObj.conf.push(...reservedConfPropertyArray);
1008    handleUniversalReservedList(nameOptions.mUniversalReservedProperties, whitelistObj.conf);
1009  }
1010  if (hasTopLevelConfig) {
1011    whitelistObj.conf.push(...reservedConfToplevelArrary);
1012    handleUniversalReservedList(nameOptions.mUniversalReservedToplevelNames, whitelistObj.conf);
1013  }
1014
1015  let enumSet: Set<string>;
1016  if (enableProperty) {
1017    enumSet = UnobfuscationCollections.reservedEnum;
1018  } 
1019  whitelistObj.enum = convertSetToArray(enumSet);
1020
1021  let whitelistContent = JSON.stringify(whitelistObj, null, 2); // 2: indentation
1022  if (!fs.existsSync(path.dirname(defaultPath))) {
1023    fs.mkdirSync(path.dirname(defaultPath), { recursive: true });
1024  }
1025  fs.writeFileSync(defaultPath, whitelistContent);
1026}
1027
1028function handleUniversalReservedList(universalList: RegExp[] | undefined, configArray: string[]): void {
1029  if (universalList?.length > 0) {
1030    universalList.forEach((value) => {
1031      const originalString = UnobfuscationCollections.reservedWildcardMap.get(value);
1032      if (originalString) {
1033        configArray.push(originalString);
1034      }
1035    });
1036  }
1037}
1038
1039// Merge KeptReasons and KeptNames and output
1040export function printUnobfuscationReasons(configPath: string, defaultPath: string): void {
1041  let property: Record<string, string[]> = {};
1042  let unobfuscationObj = { keptReasons: {}, keptNames: { property } };
1043  type WhitelistObject = {
1044    [key in WhitelistType]: string;
1045  };
1046  let keptReasons: WhitelistObject = {
1047    sdk: 'same as the system api names',
1048    lang: 'same as the language keywords',
1049    conf: 'same as the user-configured kept name',
1050    struct: 'same as the ArkUI struct property',
1051    strProp: 'same as the string property',
1052    exported: 'same as the exported names and properties',
1053    enum: 'same as the members in the enum'
1054  };
1055  unobfuscationObj.keptReasons = keptReasons;
1056
1057  if (!historyUnobfuscatedPropMap) {
1058    // Full build
1059    UnobfuscationCollections.unobfuscatedPropMap.forEach((value: Set<string>, key: string) => {
1060      let array: string[] = Array.from(value);
1061      unobfuscationObj.keptNames.property[key] = array;
1062    });
1063  } else {
1064    // Incremental build
1065    UnobfuscationCollections.unobfuscatedPropMap.forEach((value: Set<string>, key: string) => {
1066      let array: string[] = Array.from(value);
1067      historyUnobfuscatedPropMap.set(key, array);
1068    });
1069    historyUnobfuscatedPropMap.forEach((value: string[], key: string) => {
1070      unobfuscationObj.keptNames.property[key] = value;
1071    });
1072  }
1073  Object.assign(unobfuscationObj.keptNames, unobfuscationNamesObj);
1074  let unobfuscationContent = JSON.stringify(unobfuscationObj, null, 2);
1075  if (!fs.existsSync(path.dirname(defaultPath))) {
1076    fs.mkdirSync(path.dirname(defaultPath), { recursive: true });
1077  }
1078  fs.writeFileSync(defaultPath, unobfuscationContent);
1079  
1080  if (!fs.existsSync(path.dirname(configPath))) {
1081    fs.mkdirSync(path.dirname(configPath), { recursive: true });
1082  }
1083  if (configPath) {
1084    fs.copyFileSync(defaultPath, configPath);
1085  }
1086}
1087
1088
1089export function generateConsumerObConfigFile(obfuscationOptions: any, logger: any): void {
1090  const projectConfig = { obfuscationOptions, compileHar: true };
1091  const obConfig: ObConfigResolver = new ObConfigResolver(projectConfig, logger);
1092  obConfig.resolveObfuscationConfigs();
1093}
1094
1095export function mangleFilePath(originalPath: string): string {
1096  const mangledFilePath = renameFileNameModule.getMangleCompletePath(originalPath);
1097  return mangledFilePath;
1098}
1099
1100export function enableObfuscatedFilePathConfig(isPackageModules: boolean, projectConfig: any): boolean {
1101  const isDebugMode = isDebug(projectConfig);
1102  const hasObfuscationConfig = projectConfig.obfuscationMergedObConfig;
1103  if (isDebugMode || !hasObfuscationConfig) {
1104    return false;
1105  }
1106  const disableObfuscation = hasObfuscationConfig.options.disableObfuscation;
1107  const enableFileNameObfuscation = hasObfuscationConfig.options.enableFileNameObfuscation;
1108  if (disableObfuscation || !enableFileNameObfuscation) {
1109    return false;
1110  }
1111  return true;
1112}
1113
1114export function handleObfuscatedFilePath(filePath: string, isPackageModules: boolean, projectConfig: Object): string {
1115  if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) {
1116    return filePath;
1117  }
1118  // Do not obfuscate the file path in dir oh_modules.
1119  if (!isPackageModules) {
1120    return mangleFilePath(filePath);
1121  }
1122  // When open the config 'enableFileNameObfuscation', keeping all paths in unix format.
1123  return FileUtils.toUnixPath(filePath);
1124}
1125
1126export function enableObfuscateFileName(isPackageModules: boolean, projectConfig: Object): boolean {
1127  if (!enableObfuscatedFilePathConfig(isPackageModules, projectConfig)) {
1128    return false;
1129  }
1130
1131  // Do not obfuscate the file path in dir oh_modules.
1132  if (!isPackageModules) {
1133    return true;
1134  }
1135  // When open the config 'enableFileNameObfuscation', keeping all paths in unix format.
1136  return false;
1137}
1138
1139/**
1140 * Get the relative path relative to the project based on the file's associated project
1141 */
1142export function getRelativeSourcePath(
1143  filePath: string,
1144  projectRootPath: string,
1145  belongProjectPath: string | undefined,
1146): string {
1147  filePath = FileUtils.toUnixPath(filePath);
1148
1149  if (projectRootPath) {
1150    projectRootPath = FileUtils.toUnixPath(projectRootPath);
1151  }
1152
1153  if (belongProjectPath) {
1154    belongProjectPath = FileUtils.toUnixPath(belongProjectPath);
1155  }
1156
1157  let relativeFilePath: string = filePath;
1158  if (projectRootPath && filePath.startsWith(projectRootPath)) {
1159    relativeFilePath = filePath.replace(projectRootPath + '/', '');
1160  } else if (belongProjectPath) {
1161    relativeFilePath = filePath.replace(belongProjectPath + '/', '');
1162  }
1163
1164  return relativeFilePath;
1165}
1166