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 { existsSync, readFileSync, writeFileSync } from 'fs'; 17import { readJsonSync } from 'fs-extra'; 18import type { IOptions } from '../configs/IOptions'; 19import { fileExtensions } from '../common/type'; 20import type { PathAndExtension } from '../common/type'; 21import fs from 'fs'; 22import path from 'path'; 23 24export const BUNDLE = '@bundle:'; 25export const NORMALIZE = '@normalized:'; 26 27export class FileUtils { 28 /** 29 * Read file and return content 30 * 31 * @param filePath file path 32 */ 33 public static readFile(filePath: string): string | undefined { 34 if (!existsSync(filePath)) { 35 console.error(`File <${this.getFileName(filePath)}> is not found.`); 36 return undefined; 37 } 38 return readFileSync(filePath, 'utf-8'); 39 } 40 41 /** 42 * Read file and convert to json object. 43 * 44 * @param filePath file path 45 */ 46 public static readFileAsJson(filePath: string): IOptions | undefined { 47 if (!existsSync(filePath)) { 48 console.error(`File <${this.getFileName(filePath)}> is not found.`); 49 return undefined; 50 } 51 52 try { 53 return readJsonSync(filePath); 54 } catch (e) { 55 console.error('json file read error: ' + filePath); 56 return undefined; 57 } 58 } 59 60 /** 61 * Get File Name 62 * 63 * @param filePath file path 64 */ 65 public static getFileName(filePath: string): string | undefined { 66 if (!filePath) { 67 return undefined; 68 } 69 70 const lastSepIndex: number = filePath.lastIndexOf('/'); 71 if (lastSepIndex >= 0) { 72 return filePath.slice(lastSepIndex + 1); 73 } 74 75 return filePath.slice(filePath.lastIndexOf('\\') + 1); 76 } 77 78 /** 79 * Get suffix of a file. 80 * 81 * @param filePath file path 82 */ 83 public static getFileExtension(filePath: string): string | undefined { 84 if (!filePath || !filePath.includes('.')) { 85 return undefined; 86 } 87 88 // get file name 89 let fileName: string = this.getFileName(filePath); 90 if (!fileName.includes('.')) { 91 return undefined; 92 } 93 94 return fileName.slice(fileName.lastIndexOf('.') + 1); 95 } 96 97 public static writeFile(filePath: string, content: string): void { 98 writeFileSync(filePath, content); 99 } 100 101 /** 102 * get prefix of directory 103 * @param dirPath 104 */ 105 public static getPrefix(dirPath: string): string | undefined { 106 if (!dirPath || (!dirPath.includes('/') && !dirPath.includes('\\'))) { 107 return undefined; 108 } 109 110 const sepIndex: number = dirPath.lastIndexOf('/'); 111 if (sepIndex >= 0) { 112 return dirPath.slice(0, sepIndex + 1); 113 } 114 115 return dirPath.slice(0, dirPath.lastIndexOf('\\') + 1); 116 } 117 118 public static getPathWithoutPrefix(filePath: string, prefix: string): string | undefined { 119 if (!filePath.startsWith(prefix)) { 120 return filePath; 121 } 122 123 return filePath.slice(prefix.length); 124 } 125 126 public static splitFilePath(filePath: string): string[] { 127 if (!filePath.includes('\\') && !filePath.includes('\/')) { 128 return [filePath]; 129 } 130 const directories = filePath.split(/[\/\\]/); 131 return directories; 132 } 133 134 /** 135 * split the file path and collect the results into the reserved array 136 */ 137 public static collectPathReservedString(filePath: string, reservedArray: string[]): void { 138 const directories = this.splitFilePath(filePath); 139 directories.forEach(reservedStr => { 140 reservedArray.push(reservedStr); 141 }); 142 } 143 144 static relativePathBegins: string[] = ['./', '../', '.\\', '..\\']; 145 public static isRelativePath(filePath: string): boolean { 146 for (const bebin of this.relativePathBegins) { 147 if (filePath.startsWith(bebin)) { 148 return true; 149 } 150 } 151 return false; 152 } 153 154 public static getFileSuffix(filePath: string): PathAndExtension { 155 for (let ext of fileExtensions) { 156 if (filePath.endsWith(ext)) { 157 const filePathWithoutSuffix: string = filePath.replace(new RegExp(`${ext}$`), ''); 158 return { path: filePathWithoutSuffix, ext: ext }; 159 } 160 } 161 return { path: filePath, ext: '' }; 162 } 163 164 public static isReadableFile(filePath: string): boolean { 165 try { 166 fs.accessSync(filePath, fs.constants.R_OK); 167 } catch (err) { 168 return false; 169 } 170 return true; 171 } 172 173 public static toUnixPath(data: string): string { 174 if (/^win/.test(require('os').platform())) { 175 const fileTmps: string[] = data.split(path.sep); 176 const newData: string = path.posix.join(...fileTmps); 177 return newData; 178 } 179 return data; 180 } 181 182 public static getAbsPathBaseConfigPath(configPath: string, relativePath: string): string { 183 const absPath: string = path.join(path.dirname(configPath), relativePath); 184 return this.toUnixPath(absPath); 185 } 186 187 public static deleteFile(filePath: string): void { 188 if (fs.existsSync(filePath)) { 189 fs.unlinkSync(filePath); 190 } 191 } 192} 193