Files
developtools_ace-ets2bundle/compiler/src/utils.ts
T
zhangrengao 8d83033839 fix pr issue
Signed-off-by: zhangrengao <zhangrengao1@huawei.com>
Change-Id: I0815b0f0c28aeeebfad97b4fc6ced1e607163512
2022-06-07 14:20:46 +08:00

476 lines
15 KiB
TypeScript

/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path from 'path';
import ts from 'typescript';
import fs from 'fs';
import { projectConfig } from '../main';
import { createHash } from 'crypto';
import { processSystemApi } from './validate_ui_syntax';
import {
NODE_MODULES,
TEMPRARY,
MAIN,
AUXILIARY,
ZERO,
ONE,
EXTNAME_JS,
EXTNAME_TS,
EXTNAME_MJS,
EXTNAME_CJS,
EXTNAME_ABC,
EXTNAME_ETS,
EXTNAME_TS_MAP,
EXTNAME_JS_MAP
} from './pre_define';
export enum LogType {
ERROR = 'ERROR',
WARN = 'WARN',
NOTE = 'NOTE'
}
export const TEMPORARYS: string = 'temporarys';
export const BUILD: string = 'build';
export const SRC_MAIN: string = 'src/main';
const TS_NOCHECK: string = '// @ts-nocheck';
export interface LogInfo {
type: LogType,
message: string,
pos?: number,
line?: number,
column?: number,
fileName?: string
}
export class FileLog {
private _sourceFile: ts.SourceFile;
private _errors: LogInfo[] = [];
public get sourceFile() {
return this._sourceFile;
}
public set sourceFile(newValue: ts.SourceFile) {
this._sourceFile = newValue;
}
public get errors() {
return this._errors;
}
public set errors(newValue: LogInfo[]) {
this._errors = newValue;
}
}
export function emitLogInfo(loader: any, infos: LogInfo[]) {
if (infos && infos.length) {
infos.forEach((item) => {
switch (item.type) {
case LogType.ERROR:
loader.emitError(getMessage(loader.resourcePath, item));
break;
case LogType.WARN:
loader.emitWarning(getMessage(loader.resourcePath, item));
break;
case LogType.NOTE:
loader.emitWarning(getMessage(loader.resourcePath, item));
break;
}
});
}
}
export function addLog(type: LogType, message: string, pos: number, log: LogInfo[],
sourceFile: ts.SourceFile) {
const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(pos);
log.push({
type: type,
message: message,
line: posOfNode.line + 1,
column: posOfNode.character + 1,
fileName: sourceFile.fileName
});
}
export function getMessage(fileName: string, info: LogInfo): string {
let message: string;
if (info.line && info.column) {
message = `BUILD${info.type} File: ${fileName}:${info.line}:${info.column}\n ${info.message}`;
} else {
message = `BUILD${info.type} File: ${fileName}\n ${info.message}`;
}
return message;
}
class ComponentInfo {
private _id: number = 0;
private _componentNames: Set<string> = new Set(['ForEach']);
public set id(id: number) {
this._id = id;
}
public get id() {
return this._id;
}
public set componentNames(componentNames: Set<string>) {
this._componentNames = componentNames;
}
public get componentNames() {
return this._componentNames;
}
}
export const componentInfo: ComponentInfo = new ComponentInfo();
export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration |
ts.StructDeclaration | ts.ClassDeclaration, decortorName: string): boolean {
if (node.decorators && node.decorators.length) {
for (let i = 0; i < node.decorators.length; i++) {
if (node.decorators[i].getText().replace(/\(.*\)$/, '').trim() === decortorName) {
return true;
}
}
}
return false;
}
const STATEMENT_EXPECT: number = 1128;
const SEMICOLON_EXPECT: number = 1005;
const STATESTYLES_EXPECT: number = 1003;
export const IGNORE_ERROR_CODE: number[] = [STATEMENT_EXPECT, SEMICOLON_EXPECT, STATESTYLES_EXPECT];
export function readFile(dir: string, utFiles: string[]) {
try {
const files: string[] = fs.readdirSync(dir);
files.forEach((element) => {
const filePath: string = path.join(dir, element);
const status: fs.Stats = fs.statSync(filePath);
if (status.isDirectory()) {
readFile(filePath, utFiles);
} else {
utFiles.push(filePath);
}
});
} catch (e) {
console.error('ETS ERROR: ' + e);
}
}
export function createFunction(node: ts.Identifier, attrNode: ts.Identifier,
argumentsArr: ts.NodeArray<ts.Expression>): ts.CallExpression {
return ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
node,
attrNode
),
undefined,
argumentsArr && argumentsArr.length ? argumentsArr : []
);
}
export function circularFile(inputPath: string, outputPath: string): void {
if (!inputPath || !outputPath) {
return;
}
fs.readdir(inputPath, function(err, files) {
if (!files) {
return;
}
files.forEach(file => {
const inputFile: string = path.resolve(inputPath, file);
const outputFile: string = path.resolve(outputPath, file);
const fileStat: fs.Stats = fs.statSync(inputFile);
if (fileStat.isFile()) {
copyFile(inputFile, outputFile);
} else {
circularFile(inputFile, outputFile);
}
});
});
}
function copyFile(inputFile: string, outputFile: string): void {
try {
const parent: string = path.join(outputFile, '..');
if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
mkDir(parent);
}
if (fs.existsSync(outputFile)) {
return;
}
const readStream: fs.ReadStream = fs.createReadStream(inputFile);
const writeStream: fs.WriteStream = fs.createWriteStream(outputFile);
readStream.pipe(writeStream);
readStream.on('close', function() {
writeStream.end();
});
} catch (err) {
throw err.message;
}
}
export function mkDir(path_: string): void {
const parent: string = path.join(path_, '..');
if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
mkDir(parent);
}
fs.mkdirSync(path_);
}
export function toUnixPath(data: string): string {
if (/^win/.test(require('os').platform())) {
const fileTmps: string[] = data.split(path.sep);
const newData: string = path.posix.join(...fileTmps);
return newData;
}
return data;
}
export function toHashData(path: string): any {
const content: string = fs.readFileSync(path).toString();
const hash: any = createHash('sha256');
hash.update(content);
return hash.digest('hex');
}
export function writeFileSync(filePath: string, content: string): void {
if (!fs.existsSync(filePath)) {
const parent: string = path.join(filePath, '..');
if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
mkDir(parent);
}
}
fs.writeFileSync(filePath, content);
}
export function genTemporaryPath(filePath: string, projectPath: string, buildPath: string, toTsFile: boolean = true): string {
filePath = toUnixPath(filePath);
if (filePath.endsWith(EXTNAME_MJS)) {
filePath = filePath.replace(/\.mjs$/, EXTNAME_JS);
}
if (filePath.endsWith(EXTNAME_CJS)) {
filePath = filePath.replace(/\.cjs$/, EXTNAME_JS);
}
projectPath = toUnixPath(projectPath);
const hapPath: string = toUnixPath(projectConfig.projectRootPath);
const tempFilePath: string = filePath.replace(hapPath, '');
if (checkNodeModulesFile(filePath, projectPath)) {
const fakeNodeModulesPath: string = toUnixPath(path.resolve(projectConfig.projectRootPath, NODE_MODULES));
const dataTmps: string[] = tempFilePath.split(NODE_MODULES);
let output: string = '';
if (filePath.indexOf(fakeNodeModulesPath) === -1) {
const sufStr: string = dataTmps[dataTmps.length - 1];
output = path.join(buildPath, TEMPRARY, NODE_MODULES, MAIN, sufStr);
} else {
const sufStr: string = dataTmps[dataTmps.length - 1];
output = path.join(buildPath, TEMPRARY, NODE_MODULES, AUXILIARY, sufStr);
}
return output;
}
if (filePath.indexOf(projectPath) !== -1) {
const sufStr: string = filePath.replace(projectPath, '');
const output: string = path.join(buildPath, TEMPRARY, sufStr);
return output;
}
return '';
}
export function genBuildPath(filePath: string, projectPath: string, buildPath: string, toTsFile: boolean = true): string {
filePath = toUnixPath(filePath);
if (filePath.endsWith(EXTNAME_MJS)) {
filePath = filePath.replace(/\.mjs$/, EXTNAME_JS);
}
if (filePath.endsWith(EXTNAME_CJS)) {
filePath = filePath.replace(/\.cjs$/, EXTNAME_JS);
}
projectPath = toUnixPath(projectPath);
const hapPath: string = toUnixPath(projectConfig.projectRootPath);
const tempFilePath: string = filePath.replace(hapPath, '');
if (checkNodeModulesFile(filePath, projectPath)) {
filePath = toUnixPath(filePath);
const fakeNodeModulesPath: string = toUnixPath(path.resolve(projectConfig.projectRootPath, NODE_MODULES));
const dataTmps: string[] = tempFilePath.split(NODE_MODULES);
let output: string = '';
if (filePath.indexOf(fakeNodeModulesPath) === -1) {
const sufStr: string = dataTmps[dataTmps.length - 1];
output = path.join(projectConfig.nodeModulesPath, ZERO, sufStr);
} else {
const sufStr: string = dataTmps[dataTmps.length - 1];
output = path.join(projectConfig.nodeModulesPath, ONE, sufStr);
}
return output;
}
if (filePath.indexOf(projectPath) !== -1) {
const sufStr: string = filePath.replace(projectPath, '');
const output: string = path.join(buildPath, sufStr);
return output;
}
return '';
}
export function checkNodeModulesFile(filePath: string, projectPath: string) {
filePath = toUnixPath(filePath);
projectPath = toUnixPath(projectPath);
const hapPath: string = toUnixPath(projectConfig.projectRootPath);
const tempFilePath: string = filePath.replace(hapPath, '');
if (tempFilePath.indexOf(NODE_MODULES) !== -1) {
const fakeNodeModulesPath: string = toUnixPath(path.resolve(projectConfig.projectRootPath, NODE_MODULES));
if (filePath.indexOf(fakeNodeModulesPath) !== -1) {
return true;
}
if (projectConfig.modulePathMap) {
for (const key in projectConfig.modulePathMap) {
const value: string = projectConfig.modulePathMap[key];
const fakeModuleNodeModulesPath: string = toUnixPath(path.resolve(value, NODE_MODULES));
if (filePath.indexOf(fakeModuleNodeModulesPath) !== -1) {
return true;
}
}
}
}
return false;
}
export function mkdirsSync(dirname: string): boolean {
if (fs.existsSync(dirname)) {
return true;
} else if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
return false;
}
export function writeFileSyncByString(sourcePath: string, sourceCode: string, toTsFile: boolean): void {
const temporaryFile: string = genTemporaryPath(sourcePath, projectConfig.projectPath, process.env.cachePath, toTsFile);
if (temporaryFile.length === 0) {
return;
}
mkdirsSync(path.dirname(temporaryFile));
fs.writeFileSync(temporaryFile, sourceCode);
return;
}
export function writeFileSyncByNode(node: ts.SourceFile, toTsFile: boolean) {
if (toTsFile) {
const newStatements: ts.Node[] = [];
const tsIgnoreNode: ts.Node = ts.factory.createExpressionStatement(ts.factory.createIdentifier(TS_NOCHECK));
newStatements.push(tsIgnoreNode);
if (node.statements && node.statements.length) {
newStatements.push(...node.statements);
}
node = ts.factory.updateSourceFile(node, newStatements);
}
const mixedInfo: {content: string, sourceMapContent: string} = genContentAndSourceMapInfo(node, toTsFile);
let temporaryFile: string = genTemporaryPath(node.fileName, projectConfig.projectPath, process.env.cachePath, toTsFile);
if (temporaryFile.length === 0) {
return;
}
let temporarySourceMapFile: string = '';
if (temporaryFile.endsWith(EXTNAME_ETS)) {
if (toTsFile) {
temporaryFile = temporaryFile.replace(/\.ets$/, EXTNAME_TS);
} else {
temporaryFile = temporaryFile.replace(/\.ets$/, EXTNAME_JS);
}
temporarySourceMapFile = genSourceMapFileName(temporaryFile);
} else {
if (!toTsFile) {
temporaryFile = temporaryFile.replace(/\.ts$/, EXTNAME_JS);
temporarySourceMapFile = genSourceMapFileName(temporaryFile);
}
}
mkdirsSync(path.dirname(temporaryFile));
fs.writeFileSync(temporaryFile, mixedInfo.content);
if (temporarySourceMapFile.length > 0 && projectConfig.buildArkMode === 'debug') {
fs.writeFileSync(temporarySourceMapFile, mixedInfo.sourceMapContent);
}
}
function genContentAndSourceMapInfo(node: ts.SourceFile, toTsFile: boolean): {content: string, sourceMapContent: string} {
const printer: ts.Printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
const options: ts.CompilerOptions = {
sourceMap: true
};
const mapOpions: any = {
sourceMap: true,
inlineSourceMap: false,
inlineSources: false,
sourceRoot: '',
mapRoot: '',
extendedDiagnostics: false
};
const host: ts.CompilerHost = ts.createCompilerHost(options);
const fileName: string = node.fileName;
// @ts-ignore
const sourceMapGenerator: any = ts.createSourceMapGenerator(
host,
// @ts-ignore
ts.getBaseFileName(fileName),
'',
'',
mapOpions
);
// @ts-ignore
const writer: any = ts.createTextWriter(
// @ts-ignore
ts.getNewLineCharacter({newLine: ts.NewLineKind.LineFeed, removeComments: false}));
printer['writeFile'](node, writer, sourceMapGenerator);
const sourceMapJson: any = sourceMapGenerator.toJSON();
sourceMapJson['sources'] = [fileName];
const result: string = writer.getText();
let content: string = result;
content = processSystemApi(content, true);
if (toTsFile) {
content = result.replace(`${TS_NOCHECK};`, TS_NOCHECK);
}
const sourceMapContent: string = JSON.stringify(sourceMapJson);
return {
content: content,
sourceMapContent: sourceMapContent
};
}
export function genAbcFileName(temporaryFile: string): string {
let abcFile: string = temporaryFile;
if (temporaryFile.endsWith(EXTNAME_TS)) {
abcFile = temporaryFile.replace(/\.ts$/, EXTNAME_ABC);
} else {
abcFile = temporaryFile.replace(/\.js$/, EXTNAME_ABC);
}
return abcFile;
}
export function genSourceMapFileName(temporaryFile: string): string {
let abcFile: string = temporaryFile;
if (temporaryFile.endsWith(EXTNAME_TS)) {
abcFile = temporaryFile.replace(/\.ts$/, EXTNAME_TS_MAP);
} else {
abcFile = temporaryFile.replace(/\.js$/, EXTNAME_JS_MAP);
}
return abcFile;
}