1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as fs from 'fs';
17import * as path from 'path';
18import { logger } from './compile_info';
19const { projectConfig } = require('../main');
20
21const red: string = '\u001b[31m';
22const reset: string = '\u001b[39m';
23
24const REG_OHM_URL: RegExp = /^@bundle:(\S+)\/(\S+)\/(ets|js)\/(\S+)$/;
25
26export class OHMResolverPlugin {
27  private source: any;
28  private target: any;
29
30  constructor(source = 'resolve', target = 'resolve') {
31    this.source = source;
32    this.target = target;
33  }
34
35  apply(resolver) {
36    const target: any = resolver.ensureHook(this.target);
37    resolver.getHook(this.source).tapAsync('OHMResolverPlugin', (request, resolveContext, callback) => {
38      if (isOhmUrl(request.request)) {
39        const resolvedSourceFile: string = resolveSourceFile(request.request);
40        const obj = Object.assign({}, request, {
41          request: resolvedSourceFile
42        });
43        return resolver.doResolve(target, obj, null, resolveContext, callback);
44      }
45      callback();
46      return undefined;
47    });
48  }
49}
50
51export function isOhmUrl(moduleRequest: string): boolean {
52  return !!/^@(\S+):/.test(moduleRequest);
53}
54
55function addExtension(file: string, srcPath: string): string {
56  let extension: string = '.d.ts';
57  if (fs.existsSync(file + '.ets') && fs.statSync(file + '.ets').isFile()) {
58    extension = '.ets';
59  }
60  if (fs.existsSync(file + '.ts') && fs.statSync(file + '.ts').isFile()) {
61    if (extension !== '.d.ts') {
62      logger.error(red, `ArkTS:ERROR Failed to compile with files with same name ${srcPath} in the same directory`, reset);
63    }
64    extension = '.ts';
65  }
66  if (fs.existsSync(file + '.js') && fs.statSync(file + '.js').isFile()) {
67    if (extension !== '.d.ts') {
68      logger.error(red, `ArkTS:ERROR Failed to compile with files with same name ${srcPath} in the same directory`, reset);
69    }
70    extension = '.js';
71  }
72  return file + extension;
73}
74
75export function resolveSourceFile(ohmUrl: string): string {
76  if (!projectConfig.aceBuildJson) {
77    logger.error(red, `ArkTS:ERROR Failed to resolve OhmUrl because of aceBuildJson not existing `, reset);
78    return ohmUrl;
79  }
80
81  const result: RegExpMatchArray = ohmUrl.match(REG_OHM_URL);
82  const moduleName: string = result[2];
83  const srcKind: string = result[3];
84
85  const modulePath: string = projectConfig.modulePathMap[moduleName];
86  const srcRoot: string = projectConfig.isOhosTest ? 'src/ohosTest' : 'src/main';
87  let file: string = path.join(modulePath, srcRoot, srcKind, result[4]);
88  file = addExtension(file, result[4]);
89
90  if (!fs.existsSync(file) || !fs.statSync(file).isFile()) {
91    if (projectConfig.isOhosTest) {
92      file = path.join(modulePath, 'src/main', srcKind, result[4]);
93      file = addExtension(file, result[4]);
94
95      if (fs.existsSync(file) && fs.statSync(file).isFile()) {
96        return file;
97      }
98    }
99
100    logger.error(red, `ArkTS:ERROR Failed to resolve existed file by this ohm url ${ohmUrl} `, reset);
101  }
102
103  return file;
104}
105