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 ts from 'typescript'; 17import path from 'path'; 18import fs from 'fs'; 19import os from 'os'; 20import { createHash } from 'crypto'; 21import { logger } from './compile_info'; 22import { 23 FAIL 24} from './pre_define'; 25 26export enum LogType { 27 ERROR = 'ERROR', 28 WARN = 'WARN', 29 NOTE = 'NOTE' 30} 31 32const WINDOWS: string = 'Windows_NT'; 33const LINUX: string = 'Linux'; 34const MAC: string = 'Darwin'; 35 36const red: string = '\u001b[31m'; 37const reset: string = '\u001b[39m'; 38 39export interface LogInfo { 40 type: LogType, 41 message: string, 42 pos?: number, 43 line?: number, 44 column?: number, 45 fileName?: string 46} 47 48export class FileLog { 49 private _sourceFile: ts.SourceFile; 50 private _errors: LogInfo[] = []; 51 52 public get sourceFile() { 53 return this._sourceFile; 54 } 55 56 public set sourceFile(newValue: ts.SourceFile) { 57 this._sourceFile = newValue; 58 } 59 60 public get errors() { 61 return this._errors; 62 } 63 64 public set errors(newValue: LogInfo[]) { 65 this._errors = newValue; 66 } 67} 68 69export function emitLogInfo(loader: any, infos: LogInfo[]) { 70 if (infos && infos.length) { 71 infos.forEach((item) => { 72 switch (item.type) { 73 case LogType.ERROR: 74 loader.emitError(getMessage(item.fileName || loader.resourcePath, item)); 75 break; 76 case LogType.WARN: 77 loader.emitWarning(getMessage(loader.resourcePath, item)); 78 break; 79 case LogType.NOTE: 80 loader.emitWarning(getMessage(loader.resourcePath, item)); 81 break; 82 } 83 }); 84 } 85} 86 87export function addLog(type: LogType, message: string, pos: number, log: LogInfo[], 88 sourceFile: ts.SourceFile) { 89 const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(pos); 90 log.push({ 91 type: type, 92 message: message, 93 line: posOfNode.line + 1, 94 column: posOfNode.character + 1, 95 fileName: sourceFile.fileName 96 }); 97} 98 99export function getMessage(fileName: string, info: LogInfo): string { 100 let message: string; 101 if (info.line && info.column) { 102 message = `BUILD${info.type} File: ${fileName}:${info.line}:${info.column}\n ${info.message}`; 103 } else { 104 message = `BUILD${info.type} File: ${fileName}\n ${info.message}`; 105 } 106 return message; 107} 108 109class ComponentInfo { 110 private _id: number = 0; 111 private _componentNames: Set<string> = new Set(['ForEach']); 112 public set id(id: number) { 113 this._id = id; 114 } 115 public get id() { 116 return this._id; 117 } 118 public set componentNames(componentNames: Set<string>) { 119 this._componentNames = componentNames; 120 } 121 public get componentNames() { 122 return this._componentNames; 123 } 124} 125 126export const componentInfo: ComponentInfo = new ComponentInfo(); 127 128export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration | 129 ts.StructDeclaration | ts.ClassDeclaration, decortorName: string): boolean { 130 if (node.decorators && node.decorators.length) { 131 for (let i = 0; i < node.decorators.length; i++) { 132 if (node.decorators[i].getText().replace(/\(.*\)$/, '').trim() === decortorName) { 133 return true; 134 } 135 } 136 } 137 return false; 138} 139 140const STATEMENT_EXPECT: number = 1128; 141const SEMICOLON_EXPECT: number = 1005; 142const STATESTYLES_EXPECT: number = 1003; 143export const IGNORE_ERROR_CODE: number[] = [STATEMENT_EXPECT, SEMICOLON_EXPECT, STATESTYLES_EXPECT]; 144 145export function readFile(dir: string, utFiles: string[]) { 146 try { 147 const files: string[] = fs.readdirSync(dir); 148 files.forEach((element) => { 149 const filePath: string = path.join(dir, element); 150 const status: fs.Stats = fs.statSync(filePath); 151 if (status.isDirectory()) { 152 readFile(filePath, utFiles); 153 } else { 154 utFiles.push(filePath); 155 } 156 }); 157 } catch (e) { 158 console.error(red, 'ArkTS ERROR: ' + e, reset); 159 } 160} 161 162export function createFunction(node: ts.Identifier, attrNode: ts.Identifier, 163 argumentsArr: ts.NodeArray<ts.Expression>): ts.CallExpression { 164 return ts.factory.createCallExpression( 165 ts.factory.createPropertyAccessExpression( 166 node, 167 attrNode 168 ), 169 undefined, 170 argumentsArr && argumentsArr.length ? argumentsArr : [] 171 ); 172} 173 174export function circularFile(inputPath: string, outputPath: string): void { 175 if (!inputPath || !outputPath) { 176 return; 177 } 178 fs.readdir(inputPath, function(err, files) { 179 if (!files) { 180 return; 181 } 182 files.forEach(file => { 183 const inputFile: string = path.resolve(inputPath, file); 184 const outputFile: string = path.resolve(outputPath, file); 185 const fileStat: fs.Stats = fs.statSync(inputFile); 186 if (fileStat.isFile()) { 187 copyFile(inputFile, outputFile); 188 } else { 189 circularFile(inputFile, outputFile); 190 } 191 }); 192 }); 193} 194 195function copyFile(inputFile: string, outputFile: string): void { 196 try { 197 const parent: string = path.join(outputFile, '..'); 198 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 199 mkDir(parent); 200 } 201 if (fs.existsSync(outputFile)) { 202 return; 203 } 204 const readStream: fs.ReadStream = fs.createReadStream(inputFile); 205 const writeStream: fs.WriteStream = fs.createWriteStream(outputFile); 206 readStream.pipe(writeStream); 207 readStream.on('close', function() { 208 writeStream.end(); 209 }); 210 } catch (err) { 211 throw err.message; 212 } 213} 214 215export function mkDir(path_: string): void { 216 const parent: string = path.join(path_, '..'); 217 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 218 mkDir(parent); 219 } 220 fs.mkdirSync(path_); 221} 222 223export function toUnixPath(data: string): string { 224 if (/^win/.test(require('os').platform())) { 225 const fileTmps: string[] = data.split(path.sep); 226 const newData: string = path.posix.join(...fileTmps); 227 return newData; 228 } 229 return data; 230} 231 232export function toHashData(path: string) { 233 const content = fs.readFileSync(path); 234 const hash = createHash('sha256'); 235 hash.update(content); 236 return hash.digest('hex'); 237} 238 239export function writeFileSync(filePath: string, content: string): void { 240 if (!fs.existsSync(filePath)) { 241 const parent: string = path.join(filePath, '..'); 242 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 243 mkDir(parent); 244 } 245 } 246 fs.writeFileSync(filePath, content); 247} 248 249export function parseErrorMessage(message: string): string { 250 const messageArrary: string[] = message.split('\n'); 251 let logContent: string = ''; 252 messageArrary.forEach(element => { 253 if (!(/^at/.test(element.trim()))) { 254 logContent = logContent + element + '\n'; 255 } 256 }); 257 return logContent; 258} 259 260export function isWindows(): boolean { 261 return os.type() === WINDOWS; 262} 263 264export function isLinux(): boolean { 265 return os.type() === LINUX; 266} 267 268export function isMac(): boolean { 269 return os.type() === MAC; 270} 271 272export function maxFilePathLength(): number { 273 if (isWindows()) { 274 return 32766; 275 } else if (isLinux()) { 276 return 4095; 277 } else if (isMac()) { 278 return 1016; 279 } else { 280 return -1; 281 } 282} 283 284export function validateFilePathLength(filePath: string): boolean { 285 if (maxFilePathLength() < 0) { 286 logger.error(red, "Unknown OS platform", reset); 287 process.exitCode = FAIL; 288 return false; 289 } else if (filePath.length > 0 && filePath.length <= maxFilePathLength()) { 290 return true; 291 } else if (filePath.length > maxFilePathLength()) { 292 logger.error(red, `The length of ${filePath} exceeds the limitation of current platform, which is ` + 293 `${maxFilePathLength()}. Please try moving the project folder to avoid deeply nested file path and try again`, 294 reset); 295 process.exitCode = FAIL; 296 return false; 297 } else { 298 logger.error(red, "Validate file path failed", reset); 299 process.exitCode = FAIL; 300 return false; 301 } 302}