diff --git a/build-tools/dts_parser/src/coreImpl/checker/local_entry.ts b/build-tools/dts_parser/src/coreImpl/checker/local_entry.ts index 526d77b86fdcc6b618fa30501cb009b7d93133d1..4d6c96f5f63f2f4da0dd94df4b3a721ed375b882 100644 --- a/build-tools/dts_parser/src/coreImpl/checker/local_entry.ts +++ b/build-tools/dts_parser/src/coreImpl/checker/local_entry.ts @@ -15,8 +15,10 @@ import path from 'path'; import { ApiResultSimpleInfo } from '../../typedef/checker/result_type'; import { Check } from './src/api_check_plugin'; +import { TsSyntaxCheck } from './src/ts_check_plugin'; import { FileUtils } from '../../utils/FileUtils'; import { LogUtil } from '../../utils/logUtil'; +import { GenerateFile } from '../../utils/checkUtils'; /** * local entrance @@ -26,10 +28,12 @@ export class LocalEntry { const mdFilesPath = path.resolve(FileUtils.getBaseDirName(), '../mdFiles.txt'); let result: ApiResultSimpleInfo[] = []; try { - result = Check.scanEntry(mdFilesPath); + result = TsSyntaxCheck.checkAPISyntax(mdFilesPath); + result.concat(Check.scanEntry(mdFilesPath)); } catch (error) { LogUtil.e('API_CHECK_ERROR', error); } finally { + GenerateFile.writeFile(result, '../result.txt', {}); } return result; } diff --git a/build-tools/dts_parser/src/coreImpl/checker/src/compile_info.ts b/build-tools/dts_parser/src/coreImpl/checker/src/compile_info.ts index 80b4f00d09038cfc081599d9df5f8df1110a0469..f67e6ec6968f2e5ec8bdfb92e773da645525d4b3 100644 --- a/build-tools/dts_parser/src/coreImpl/checker/src/compile_info.ts +++ b/build-tools/dts_parser/src/coreImpl/checker/src/compile_info.ts @@ -28,11 +28,11 @@ export class AddErrorLogs { * @param { string } apiName -error message api name * @param { string } apiFullText -error message api text * @param { string } message -error infomation - * @param { ApiResultSimpleInfo[] } checkErrorArr -array for storing error information + * @param { ApiResultSimpleInfo[] } checkErrorInfos -array for storing error information */ static addAPICheckErrorLogs(id: number, level: number, filePath: string, location: string, errorType: string, apiType: string, version: number, apiName: string, apiFullText: string, - message: string, checkErrorArr: ApiResultSimpleInfo[]): void { + message: string, checkErrorInfos: ApiResultSimpleInfo[], checkErrorAllInfos: ApiResultInfo[]): void { const apiChecktSimpleErrorLog: ApiResultSimpleInfo = new ApiResultSimpleInfo(); apiChecktSimpleErrorLog .setID(id) @@ -50,8 +50,10 @@ export class AddErrorLogs { .setVersion(version) .setLevel(level) .setApiName(apiName) - .setApiFullText(apiFullText); - - checkErrorArr.push(apiChecktSimpleErrorLog); + .setApiFullText(apiFullText) + .setBaseName(filePath.substring(filePath.lastIndexOf('/') + 1, filePath.length)); + checkErrorInfos.push(apiChecktSimpleErrorLog); + checkErrorAllInfos.push(apiCheckErrorLog); } -} \ No newline at end of file +} + diff --git a/build-tools/dts_parser/src/coreImpl/checker/src/ts_check_plugin.ts b/build-tools/dts_parser/src/coreImpl/checker/src/ts_check_plugin.ts new file mode 100644 index 0000000000000000000000000000000000000000..8bdac8df35aeff7450da2dd15e7d93539a44e3fa --- /dev/null +++ b/build-tools/dts_parser/src/coreImpl/checker/src/ts_check_plugin.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 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 fs from 'fs'; +import path from 'path'; +import ts from 'typescript'; +import { Check } from './api_check_plugin'; +import { AddErrorLogs } from './compile_info'; +import { ApiResultSimpleInfo, ErrorType, ErrorID, LogType, ErrorLevel, ApiResultInfo } from + '../../../typedef/checker/result_type'; +import { StringConstant } from '../../../utils/Constant'; +import { PosOfNode, CompolerOptions, ObtainFullPath, GenerateFile } from '../../../utils/checkUtils'; + +export class TsSyntaxCheck { + /** + * ts语法检查工具入口 + * @param { string } url -File path for storing critical information. + */ + static checkAPISyntax(url: string): ApiResultSimpleInfo[] { + const tsResult: ApiResultSimpleInfo[] = []; + const tsLocalResult: ApiResultInfo[] = []; + if (fs.existsSync(url)) { + const fullFilesPath: string[] = []; + ObtainFullPath.getFullFiles(path.resolve(__dirname, '../../../../../../api'), fullFilesPath); + const files: Array = Check.getMdFiles(url); + const program: ts.Program = ts.createProgram({ + rootNames: fullFilesPath, + options: CompolerOptions.getCompolerOptions() + }); + //ts存在bug,仅为解决无法绑定sourcefile根节点的问题 + program.getTypeChecker(); + const programSourceFiles: readonly ts.SourceFile[] = program.getSourceFiles(); + const host: ts.CompilerHost = ts.createCompilerHost(CompolerOptions.getCompolerOptions()); + const diagnostics: ts.Diagnostic[] = ts.runArkTSLinter(program, host); + files.forEach((filePath: string, index: number) => { + TsSyntaxCheck.checkAPISyntaxCallback(filePath, program, diagnostics, programSourceFiles, tsResult, tsLocalResult); + console.log(`scaning file in no ${++index}!`); + }); + } + GenerateFile.writeExcelFile(tsLocalResult); + return tsResult; + } + /** + * ts检查主要处理过程 + * @param { string } fileName + * @param { ts.Program } program + * @param { ts.Diagnostic[] } diagnostics + * @param { readonly ts.SourceFile[] } programSourceFiles + * @param { ApiResultSimpleInfo[] } tsResult + * @param { ApiResultInfo[] } checkErrorAllInfos + */ + static checkAPISyntaxCallback(fileName: string, program: ts.Program, diagnostics: ts.Diagnostic[], + programSourceFiles: readonly ts.SourceFile[], tsResult: ApiResultSimpleInfo[], + checkErrorAllInfos: ApiResultInfo[]): void { + const fileContent: string = fs.readFileSync(fileName, StringConstant.UTF8); + const node: ts.Node = ts.createSourceFile(fileName, fileContent, ts.ScriptTarget.ES2017, true); + const fileSuffix: string = fileName.substring(fileName.lastIndexOf('.'), fileName.length); + // tsc诊断日志 + if (fileSuffix === '.ts') { + const targetSourceFile: ts.SourceFile = node.getSourceFile(); + programSourceFiles.forEach(programSourceFile => { + if (programSourceFile.fileName === targetSourceFile.fileName) { + const result: readonly ts.Diagnostic[] = program.getSemanticDiagnostics(programSourceFile); + result.forEach(item => { + AddErrorLogs.addAPICheckErrorLogs(ErrorID.TS_SYNTAX_ERROR_ID, ErrorLevel.MIDDLE, + item.file?.fileName as string, PosOfNode.getPosOfNode(node, item), + ErrorType.TS_SYNTAX_ERROR, LogType.LOG_API, -1, 'NA', 'NA', item.messageText as string, tsResult, + checkErrorAllInfos); + }); + } + }); + } + // ArkTS诊断日志 + if (fileSuffix === '.ets') { + diagnostics.forEach(item => { + if (path.normalize(item.file?.fileName as string) === path.normalize(fileName)) { + AddErrorLogs.addAPICheckErrorLogs(ErrorID.TS_SYNTAX_ERROR_ID, ErrorLevel.MIDDLE, + item.file?.fileName as string, PosOfNode.getPosOfNode(node, item), + ErrorType.TS_SYNTAX_ERROR, LogType.LOG_API, -1, 'NA', 'NA', item.messageText as string, tsResult, + checkErrorAllInfos); + } + }); + } + } + +} \ No newline at end of file diff --git a/build-tools/dts_parser/src/typedef/checker/result_type.ts b/build-tools/dts_parser/src/typedef/checker/result_type.ts index fe518141ec1551e8a38f4619ae00e751275e5c6a..4afcc3237169746c34cc67489b823ea3ea111830 100644 --- a/build-tools/dts_parser/src/typedef/checker/result_type.ts +++ b/build-tools/dts_parser/src/typedef/checker/result_type.ts @@ -47,7 +47,8 @@ export enum ErrorType { PARAMETER_ERRORS = 'wrong parameter', API_PAIR_ERRORS = 'limited api pair errors', ILLEGAL_ANY = 'illegal any', - API_CHANGE_ERRORS = 'api change errors' + API_CHANGE_ERRORS = 'api change errors', + TS_SYNTAX_ERROR = 'TS syntax error' } /** @@ -67,7 +68,8 @@ export enum ErrorID { PARAMETER_ERRORS_ID = 9, API_PAIR_ERRORS_ID = 10, ILLEGAL_ANY_ID = 11, - API_CHANGE_ERRORS_ID = 12 + API_CHANGE_ERRORS_ID = 12, + TS_SYNTAX_ERROR_ID = 13 } /** @@ -211,6 +213,7 @@ export class ApiResultInfo { level: number = -1; apiName: string = ''; apiFullText: string = ''; + baseName: string = ''; setErrorType(errorType: string): ApiResultInfo { this.errorType = errorType; @@ -282,4 +285,12 @@ export class ApiResultInfo { getApiFullText(): string { return this.apiFullText; } + setBaseName(baseName: string): ApiResultInfo { + this.baseName = baseName; + return this; + } + + getBaseName(): string { + return this.baseName; + } } \ No newline at end of file diff --git a/build-tools/dts_parser/src/utils/checkUtils.ts b/build-tools/dts_parser/src/utils/checkUtils.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ce991decda308cac14bbbe1cdb6fa9a920eb22e --- /dev/null +++ b/build-tools/dts_parser/src/utils/checkUtils.ts @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 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 fs, { Stats } from 'fs'; +import { Workbook, Worksheet } from 'exceljs'; +import ts, { LineAndCharacter } from 'typescript'; +import { ApiResultSimpleInfo, ApiResultInfo } from '../typedef/checker/result_type'; + +export class PosOfNode { + /** + * 获取行列信息 + * @param { ts.Node } node + * @param { ts.Diagnostic } diagnostic + */ + static getPosOfNode(node: ts.Node, diagnostic: ts.Diagnostic): string { + const posOfNode: LineAndCharacter = ts.getLineAndCharacterOfPosition(node.getSourceFile(), + diagnostic.start as number); + const location: string = diagnostic.file?.fileName as string + + `(line: ${posOfNode.line + 1}, col: ${posOfNode.character + 1})`; + return location; + } +} +export class CompolerOptions { + /** + * tsconfig配置项设置 + */ + static getCompolerOptions(): ts.CompilerOptions { + const compilerOptions: ts.CompilerOptions = ts.readConfigFile(path.resolve(__dirname, '../../tsconfig.json'), + ts.sys.readFile).config.compilerOptions; + Object.assign(compilerOptions, { + target: 'es2020', + jsx: 'preserve', + incremental: undefined, + declaration: undefined, + declarationMap: undefined, + emitDeclarationOnly: undefined, + outFile: undefined, + composite: undefined, + tsBuildInfoFile: undefined, + noEmit: undefined, + isolatedModules: true, + paths: undefined, + rootDirs: undefined, + types: undefined, + out: undefined, + noLib: undefined, + noResolve: true, + noEmitOnError: undefined, + declarationDir: undefined, + suppressOutputPathCheck: true, + allowNonTsExtensions: true + }); + return compilerOptions; + } +} + +export class GenerateFile { + /** + * 将错误信息输出为txt文件 + * @param { ApiResultSimpleInfo[] } resultData + * @param { string } outputPath + * @param { string } option + */ + static writeFile(resultData: ApiResultSimpleInfo[], outputPath: string, option: object): void { + const STANDARD_INDENT: number = 2; + fs.writeFile(path.resolve(__dirname, outputPath), JSON.stringify(resultData, null, STANDARD_INDENT), option, (err) => { + if (err) { + console.error(`ERROR FOR CREATE FILE:${err}`); + } else { + console.log('API CHECK FINISH!'); + } + }); + } + + /** + * 将错误信息输出为excel文件 + * @param { ApiResultInfo[] } apiCheckArr + */ + static async writeExcelFile(apiCheckArr: ApiResultInfo[]): Promise { + const workbook: Workbook = new Workbook(); + const sheet: Worksheet = workbook.addWorksheet('Js Api', { views: [{ xSplit: 1 }] }); + sheet.getRow(1).values = ['order', 'errorType', 'fileName', 'apiName', 'apiContent', 'type', 'errorInfo', 'version', 'model']; + for (let i = 1; i <= apiCheckArr.length; i++) { + const apiData: ApiResultInfo = apiCheckArr[i - 1]; + sheet.getRow(i + 1).values = [i, apiData.getErrorType(), apiData.getLocation(), apiData.getApiName(), + apiData.getApiFullText(), apiData.getApiType(), apiData.getMessage(), apiData.getVersion(), apiData.getBaseName()]; + } + workbook.xlsx.writeBuffer().then((buffer) => { + fs.writeFile(path.resolve(__dirname, '../coreImpl/checker/Js_Api.xlsx'), buffer, function (err) { + if (err) { + console.error(err); + return; + } + }); + }); + } +} + +export class ObtainFullPath { + /** + * 获取仓库中api文件夹下的所有d.ts和d.ets路径 + * @param { string } dir -api路径 + * @param { string[] } utFiles -存放具体路径的数组 + */ + static getFullFiles(dir: string, utFiles: string[]): void { + try { + const files: string[] = fs.readdirSync(dir); + files.forEach((element) => { + const filePath: string = path.join(dir, element); + const status: Stats = fs.statSync(filePath); + if (status.isDirectory()) { + ObtainFullPath.getFullFiles(filePath, utFiles); + } else { + if (/\.d\.ts/.test(filePath) || /\.d\.ets/.test(filePath)) { + utFiles.push(filePath); + } + } + }); + } catch (e) { + console.error('ETS ERROR: ' + e); + } + } +} \ No newline at end of file diff --git a/build-tools/dts_parser/tsconfig.json b/build-tools/dts_parser/tsconfig.json index 99e3acae4e5f25f533be9287184c524e4cca3d43..3a4da0b8d60c42fdd55fe8b09e5c526113e04d98 100644 --- a/build-tools/dts_parser/tsconfig.json +++ b/build-tools/dts_parser/tsconfig.json @@ -12,7 +12,7 @@ /* Language and Environment */ "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["es2015","es2020"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -96,6 +96,6 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + // "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } \ No newline at end of file