Files
developtools_ace-ets2bundle/compiler/src/utils.ts
T
zhangrengao d32c0b6206 fix bugs of bundleLess
Signed-off-by: zhangrengao <zhangrengao1@huawei.com>
Change-Id: I254424e9b5ffa0ceeac44ac63a83b8b91194a3d1
2022-06-07 14:20:46 +08:00

451 lines
14 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';
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) {
const content = fs.readFileSync(path);
const hash = 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("mjs")) {
filePath = filePath.replace(/\.mjs$/, '.js')
}
if (filePath.endsWith("cjs")) {
filePath = filePath.replace(/\.cjs$/, '.js')
}
projectPath = toUnixPath(projectPath);
let hapPath = toUnixPath(projectConfig.projectRootPath);
let tempFilePath = filePath.replace(hapPath, "");
if (checkNodeModulesFile(filePath, projectPath)) {
let fakeNodeModulesPath = toUnixPath(path.resolve(projectConfig.projectRootPath, "node_modules"));
const dataTmps = tempFilePath.split('node_modules');
let output:string = "";
if (filePath.indexOf(fakeNodeModulesPath) === -1) {
const sufStr = dataTmps[dataTmps.length-1];
output = path.join(buildPath, "temprary", 'node_modules', 'main', sufStr);
} else {
const sufStr = dataTmps[dataTmps.length-1];
output = path.join(buildPath, "temprary", 'node_modules', 'auxiliary', sufStr);
}
return output;
}
if (filePath.indexOf(projectPath) !== -1) {
const sufStr = 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("mjs")) {
filePath = filePath.replace(/\.mjs$/, '.js')
}
if (filePath.endsWith("cjs")) {
filePath = filePath.replace(/\.cjs$/, '.js')
}
projectPath = toUnixPath(projectPath);
let hapPath = toUnixPath(projectConfig.projectRootPath);
let tempFilePath = filePath.replace(hapPath, "");
if (checkNodeModulesFile(filePath, projectPath)) {
filePath = toUnixPath(filePath);
let fakeNodeModulesPath = toUnixPath(path.resolve(projectConfig.projectRootPath, "node_modules"));
const dataTmps = tempFilePath.split('node_modules');
let output:string = "";
if (filePath.indexOf(fakeNodeModulesPath) === -1) {
const sufStr = dataTmps[dataTmps.length-1];
output = path.join(projectConfig.nodeModulesPath, '0', sufStr);
} else {
const sufStr = dataTmps[dataTmps.length-1];
output = path.join(projectConfig.nodeModulesPath, '1', sufStr);
}
return output;
}
if (filePath.indexOf(projectPath) !== -1) {
const sufStr = 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);
let hapPath = toUnixPath(projectConfig.projectRootPath);
let tempFilePath = filePath.replace(hapPath, "");
if (tempFilePath.indexOf('node_modules') !== -1) {
let fakeNodeModulesPath = toUnixPath(path.resolve(projectConfig.projectRootPath, "node_modules"));
if (filePath.indexOf(fakeNodeModulesPath) !== -1) {
return true;
}
if (projectConfig.modulePathMap) {
for (let key in projectConfig.modulePathMap) {
let value = projectConfig.modulePathMap[key];
let fakeModuleNodeModulesPath = 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) {
let temporaryFile: string = genTemporaryPath(sourcePath, projectConfig.projectPath, process.env.cachePath, toTsFile);
if (temporaryFile.length === 0) {
return ;
}
mkdirsSync(path.dirname(temporaryFile));
fs.writeFileSync(temporaryFile, sourceCode);
}
export function writeFileSyncByNode(node: ts.SourceFile, toTsFile: boolean) {
if (toTsFile) {
const newStatements = [];
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 printer: ts.Printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
let options : ts.CompilerOptions = {
sourceMap : true
};
let mapOpions = {
sourceMap : true,
inlineSourceMap : false,
inlineSources : false,
sourceRoot : "",
mapRoot : "",
extendedDiagnostics : false
}
let host = ts.createCompilerHost(options);
let fileName = node.fileName;
// @ts-ignore
let sourceMapGenerator = ts.createSourceMapGenerator(
host,
// @ts-ignore
ts.getBaseFileName(fileName),
"",
"",
mapOpions
);
// @ts-ignore
let writer = ts.createTextWriter(
// @ts-ignore
ts.getNewLineCharacter({newLine : ts.NewLineKind.LineFeed, removeComments : false}));
printer["writeFile"](node, writer, sourceMapGenerator);
let sourceMapJson = 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 = JSON.stringify(sourceMapJson);
let temporaryFile: string = genTemporaryPath(node.fileName, projectConfig.projectPath, process.env.cachePath, toTsFile);
if (temporaryFile.length === 0) {
return ;
}
let temporarySourceMapFile: string = "";
if (temporaryFile.endsWith("ets")) {
if (toTsFile) {
temporaryFile = temporaryFile.replace(/\.ets$/, '.ts');
} else {
temporaryFile = temporaryFile.replace(/\.ets$/, '.js');
}
temporarySourceMapFile = genSourceMapFileName(temporaryFile);
} else {
if (!toTsFile) {
temporaryFile = temporaryFile.replace(/\.ts$/, '.js');
temporarySourceMapFile = genSourceMapFileName(temporaryFile);
}
}
mkdirsSync(path.dirname(temporaryFile));
fs.writeFileSync(temporaryFile, content);
if (temporarySourceMapFile.length > 0 && projectConfig.buildArkMode === "debug") {
fs.writeFileSync(temporarySourceMapFile, sourceMapContent);
}
}
export function genAbcFileName(temporaryFile: string): string {
let abcFile: string = temporaryFile;
if (temporaryFile.endsWith('ts')) {
abcFile = temporaryFile.replace(/\.ts$/, '.abc');
} else {
abcFile = temporaryFile.replace(/\.js$/, '.abc');
}
return abcFile;
}
export function genSourceMapFileName(temporaryFile: string): string {
let abcFile: string = temporaryFile;
if (temporaryFile.endsWith('ts')) {
abcFile = temporaryFile.replace(/\.ts$/, '.map');
} else {
abcFile = temporaryFile.replace(/\.js$/, '.map');
}
return abcFile;
}