• 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 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}