mirror of
https://gitee.com/openharmony/interface_sdk-js
synced 2024-11-27 01:11:35 +00:00
JSDoc整改工具功能优化
Signed-off-by: yangbo_404 <yangbo198@huawei.com> Change-Id: Iaefa2270ba31a978e49c2cbed459dd7416bdda4b
This commit is contained in:
parent
89b3c878c7
commit
d103ef4a5b
@ -6,7 +6,6 @@
|
||||
"default",
|
||||
"deprecated",
|
||||
"enum",
|
||||
"errorcode",
|
||||
"example",
|
||||
"extends",
|
||||
"famodelonly",
|
||||
@ -41,18 +40,12 @@
|
||||
"borrows",
|
||||
"class",
|
||||
"classdesc",
|
||||
"constant",
|
||||
"constructs",
|
||||
"copyright",
|
||||
"default",
|
||||
"deprecated",
|
||||
"enum",
|
||||
"event",
|
||||
"example",
|
||||
"exports",
|
||||
"external",
|
||||
"file",
|
||||
"fires",
|
||||
"function",
|
||||
"generator",
|
||||
"global",
|
||||
@ -62,7 +55,6 @@
|
||||
"inheritdoc",
|
||||
"inner",
|
||||
"instance",
|
||||
"interface",
|
||||
"lends",
|
||||
"license",
|
||||
"listens",
|
||||
@ -72,26 +64,17 @@
|
||||
"mixin",
|
||||
"modifies",
|
||||
"module",
|
||||
"namespace",
|
||||
"package",
|
||||
"param",
|
||||
"private",
|
||||
"property",
|
||||
"protected",
|
||||
"public",
|
||||
"readonly",
|
||||
"requires",
|
||||
"returns",
|
||||
"see",
|
||||
"since",
|
||||
"static",
|
||||
"summary",
|
||||
"this",
|
||||
"todo",
|
||||
"throws",
|
||||
"tutorial",
|
||||
"type",
|
||||
"typedef",
|
||||
"variation",
|
||||
"version",
|
||||
"yields",
|
||||
|
@ -1256,6 +1256,15 @@
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.ENTERPRISE_SET_ACCOUNT_POLICY",
|
||||
"grantMode": "system_grant",
|
||||
"availableLevel": "system_basic",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.NFC_TAG",
|
||||
"grantMode": "system_grant",
|
||||
@ -1574,6 +1583,24 @@
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.READ_APP_PUSH_DATA",
|
||||
"grantMode": "system_grant",
|
||||
"availableLevel": "system_basic",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"provisionEnable": false,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.WRITE_APP_PUSH_DATA",
|
||||
"grantMode": "system_grant",
|
||||
"availableLevel": "system_basic",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"provisionEnable": false,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.MANAGE_AUDIO_CONFIG",
|
||||
"grantMode": "system_grant",
|
||||
@ -1669,7 +1696,34 @@
|
||||
"deprecated": "",
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.PRINT",
|
||||
"grantMode": "system_grant",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"availableLevel": "normal",
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.MANAGE_PRINT_JOB",
|
||||
"grantMode": "system_grant",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"availableLevel": "system_basic",
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.CHANGE_OVERLAY_ENABLED_STATE",
|
||||
"grantMode": "system_grant",
|
||||
"availableLevel": "system_basic",
|
||||
"since": 10,
|
||||
"deprecated": "",
|
||||
"provisionEnable": true,
|
||||
"distributedSceneEnable": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,79 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const checkLegality = require('./src/check_legality');
|
||||
exports.checkJSDoc = function (node, sourceFile) {
|
||||
return checkLegality.checkJsDocOfCurrentNode(node, sourceFile, undefined);
|
||||
};
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const urlPrefix = 'https://gitee.com/openharmony/utils_system_resources/raw/';
|
||||
const urlSuffix = '/systemres/main/config.json';
|
||||
|
||||
exports.checkJSDoc = function (node, sourceFile, fileName) {
|
||||
const checkLegality = require('./src/check_legality');
|
||||
return checkLegality.checkJsDocOfCurrentNode(node, sourceFile, undefined, fileName);
|
||||
};
|
||||
|
||||
exports.initEnv = function (version) {
|
||||
const { checkOption } = require('./src/utils');
|
||||
return new Promise((resolve, reject) => {
|
||||
const permissionName = getPermissionName(version);
|
||||
const permissionConfig = getLocalPermissionConfig(permissionName);
|
||||
if (permissionConfig) {
|
||||
checkOption.permissionContent = permissionConfig;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const workingBranch = version ? version : 'mas' + 'ter';
|
||||
const url = `${urlPrefix}${workingBranch}${urlSuffix}`;
|
||||
updatePermissionConfig(url, (content) => {
|
||||
if (content) {
|
||||
checkOption.permissionContent = content;
|
||||
savePermissionConfig(content, permissionName);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updatePermissionConfig(url, callback) {
|
||||
let requestText = undefined;
|
||||
const https = require('https');
|
||||
const request = https.get(url, { timeout: 2000 }, (res) => {
|
||||
res.on('data', (chunk) => {
|
||||
if (typeof chunk === 'string') {
|
||||
requestText = chunk;
|
||||
} else {
|
||||
const dataStr = new TextDecoder().decode(chunk);
|
||||
requestText = requestText ? (requestText + dataStr) : dataStr;
|
||||
}
|
||||
});
|
||||
}).on('error', () => {
|
||||
console.warn('use the default permission list.');
|
||||
}).on('close', () => {
|
||||
callback(requestText);
|
||||
}).on('timeout', () => {
|
||||
request.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
function getLocalPermissionConfig(name) {
|
||||
const localPermissionFile = path.resolve(__dirname, name);
|
||||
if (fs.existsSync(localPermissionFile)) {
|
||||
const content = fs.readFileSync(localPermissionFile);
|
||||
try {
|
||||
JSON.parse(content);
|
||||
return content;
|
||||
} catch (err) {
|
||||
console.warn(`parse error ${localPermissionFile}`);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function savePermissionConfig(content, name) {
|
||||
const localPermissionFile = path.resolve(__dirname, name);
|
||||
fs.writeFileSync(localPermissionFile, content);
|
||||
console.log(`update permission configuration to ${localPermissionFile}`);
|
||||
}
|
||||
|
||||
function getPermissionName(version) {
|
||||
return `permissions_${version}.config.json`;
|
||||
}
|
@ -15,13 +15,13 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const ts = require(path.resolve(__dirname, '../node_modules/typescript'));
|
||||
const { checkAPIDecorators } = require('./check_decorator');
|
||||
const { checkSpelling } = require('./check_spelling');
|
||||
const { checkPermission } = require('./check_permission');
|
||||
const { checkSyscap } = require('./check_syscap');
|
||||
const { checkDeprecated } = require('./check_deprecated');
|
||||
const { hasAPINote, ApiCheckResult } = require('./utils');
|
||||
const { hasAPINote, ApiCheckResult, requireTypescriptModule } = require('./utils');
|
||||
const ts = requireTypescriptModule();
|
||||
let result = require('../check_result.json');
|
||||
|
||||
function checkAPICodeStyle(url) {
|
||||
|
3
build-tools/api_check_plugin/src/build.json
Normal file
3
build-tools/api_check_plugin/src/build.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"isBundle": false
|
||||
}
|
@ -14,10 +14,10 @@
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const ts = require(path.resolve(__dirname, '../node_modules/typescript'));
|
||||
const { ErrorType, ErrorLevel } = require('./utils');
|
||||
const { ErrorType, ErrorLevel, requireTypescriptModule } = require('./utils');
|
||||
const rules = require('../code_style_rule.json');
|
||||
const { addAPICheckErrorLogs } = require('./compile_info');
|
||||
const ts = requireTypescriptModule();
|
||||
|
||||
function checkAPINameOfHump(node, sourcefile, fileName) {
|
||||
let errorInfo = '';
|
||||
|
@ -12,11 +12,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const ts = require(path.resolve(__dirname, '../../node_modules/typescript'));
|
||||
const rules = require('../../code_style_rule.json');
|
||||
const { ErrorLevel, FileType, ErrorType, commentNodeWhiteList } = require('../../src/utils');
|
||||
const { ErrorLevel, FileType, ErrorType, commentNodeWhiteList, requireTypescriptModule } = require('../../src/utils');
|
||||
const { addAPICheckErrorLogs } = require('../compile_info');
|
||||
const { getPermissionBank } = require('../check_permission');
|
||||
const ts = requireTypescriptModule();
|
||||
|
||||
|
||||
function checkExtendsValue(tag, node, sourcefile, fileName, index) {
|
||||
@ -29,15 +29,9 @@ function checkExtendsValue(tag, node, sourcefile, fileName, index) {
|
||||
// 获取api中的extends信息,校验标签合法性及值规范
|
||||
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
||||
const apiValue = node.heritageClauses ? node.heritageClauses[0].types[0].expression.escapedText : '';
|
||||
|
||||
if (apiValue.length === 0) {
|
||||
if (tagValue !== apiValue) {
|
||||
extendsResult.checkResult = false,
|
||||
extendsResult.errorInfo = 'should delete @extends; ';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, extendsResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
} else if (tagValue !== apiValue) {
|
||||
extendsResult.checkResult = false,
|
||||
extendsResult.errorInfo = ` '@${tagValue}' should change to '${apiValue}'; `;
|
||||
extendsResult.errorInfo = 'extends标签值错误, 请检查标签值是否与继承类名保持一致.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, extendsResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -58,7 +52,7 @@ function checkEnumValue(tag, node, sourcefile, fileName, index) {
|
||||
// 获取api中的enum信息,校验标签合法性及值规范
|
||||
if (tagProblems > 0 || enumValues.indexOf(tagValue) === -1) {
|
||||
enumResult.checkResult = false;
|
||||
enumResult.errorInfo = '@enum value is wrong; ';
|
||||
enumResult.errorInfo = 'enum标签类型错误, 请检查标签类型是否为string或number.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, enumResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -71,10 +65,11 @@ function checkSinceValue(tag, node, sourcefile, fileName, index) {
|
||||
checkResult: true,
|
||||
errorInfo: '',
|
||||
};
|
||||
const tagValue = parseInt(tag.name);
|
||||
if (isNaN(tagValue)) {
|
||||
const tagValue = tag.name;
|
||||
const checkNumber = /^\d+$/.test(tagValue);
|
||||
if (!checkNumber && commentNodeWhiteList.includes(node.kind)) {
|
||||
sinceResult.checkResult = false;
|
||||
sinceResult.errorInfo = `@since value '${tag.name}' is wrong; `;
|
||||
sinceResult.errorInfo = 'since标签值错误, 请检查标签值是否为数值.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, sinceResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -93,12 +88,12 @@ function checkReturnsValue(tag, node, sourcefile, fileName, index) {
|
||||
const apiReturnsValue = node.type?.getText();
|
||||
if (voidArr.indexOf(apiReturnsValue) !== -1 || apiReturnsValue === undefined) {
|
||||
returnsResult.checkResult = false;
|
||||
returnsResult.errorInfo = 'should delete @returns; ';
|
||||
returnsResult.errorInfo = 'returns标签使用错误, 返回类型为void时不应该使用returns标签.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, returnsResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
} else if (tagValue !== apiReturnsValue) {
|
||||
returnsResult.checkResult = false;
|
||||
returnsResult.errorInfo = `@returns value '${tagValue}' is wrong; `;
|
||||
returnsResult.errorInfo = 'returns标签类型错误, 请检查标签类型是否与返回类型一致.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, returnsResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -119,22 +114,23 @@ function checkParamValue(tag, node, sourcefile, fileName, index) {
|
||||
if (apiParamInfos[index]) {
|
||||
const apiName = apiParamInfos[index].name.escapedText;
|
||||
const apiType = apiParamInfos[index].type?.getText();
|
||||
let errorInfo = '';
|
||||
if (apiType !== tagTypeValue) {
|
||||
paramResult.checkResult = false;
|
||||
errorInfo += `第[${index + 1}]个param标签类型错误, 请检查是否与第[${index + 1}]个参数类型保持一致.`;
|
||||
}
|
||||
if (apiName !== tagNameValue) {
|
||||
paramResult.checkResult = false;
|
||||
paramResult.errorInfo = `@param name '${tagNameValue}' is wrong; `;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, paramResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
} else if (apiType !== tagTypeValue) {
|
||||
paramResult.checkResult = false;
|
||||
paramResult.errorInfo = `@param type '${tagTypeValue}' is wrong; `;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, paramResult.errorInfo, FileType.JSDOC,
|
||||
if (errorInfo !== '') {
|
||||
errorInfo += '\n';
|
||||
}
|
||||
errorInfo += `第[${index + 1}]个param标签值错误, 请检查是否与第[${index + 1}]个参数名保持一致.`;
|
||||
}
|
||||
if (!paramResult.checkResult) {
|
||||
paramResult.errorInfo = errorInfo;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
} else {
|
||||
paramResult.checkResult = false;
|
||||
paramResult.errorInfo = '@param counts is wrong; ';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, paramResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
}
|
||||
return paramResult;
|
||||
@ -148,32 +144,109 @@ function checkThrowsValue(tag, node, sourcefile, fileName, index) {
|
||||
};
|
||||
const tagNameValue = tag.name;
|
||||
const tagTypeValue = tag.type;
|
||||
|
||||
let errorInfo = '';
|
||||
if (tagTypeValue !== 'BusinessError') {
|
||||
throwsResult.checkResult = false;
|
||||
throwsResult.errorInfo = `@throws type value '${tagTypeValue}' is wrong; `;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, throwsResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
} else if (isNaN(tagNameValue)) {
|
||||
errorInfo += `第[${index + 1}]个throws标签类型错误, 请填写BusinessError.`;
|
||||
}
|
||||
if (isNaN(tagNameValue)) {
|
||||
if (errorInfo !== '') {
|
||||
errorInfo += '\n';
|
||||
}
|
||||
throwsResult.checkResult = false;
|
||||
throwsResult.errorInfo = `@throws name value '${tag.name}' is wrong; `;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, throwsResult.errorInfo, FileType.JSDOC,
|
||||
errorInfo += `第[${index + 1}]个throws标签类型错误, 请检查标签值是否为数值.`;
|
||||
}
|
||||
if (!throwsResult.checkResult) {
|
||||
throwsResult.errorInfo = errorInfo;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
return throwsResult;
|
||||
}
|
||||
exports.checkThrowsValue = checkThrowsValue;
|
||||
|
||||
// #校验功能待补全
|
||||
/**
|
||||
* 判断是否为arkui的api文件
|
||||
*/
|
||||
function isArkUIApiFile(fileName) {
|
||||
if (fileName.indexOf("component\\ets\\") >= 0 || fileName.indexOf("component/ets/") >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 1.引用不同文件的api接口
|
||||
* xxx.xxx#xxx
|
||||
*
|
||||
* 2.引用不同文件的模块接口
|
||||
* xxx.xxx
|
||||
*
|
||||
* 3.引用不同文件的api事件接口
|
||||
* xxx.xxx#event:xxx
|
||||
*/
|
||||
function checkModule(moduleValue) {
|
||||
return /^[A-Za-z]+\b(\.[A-Za-z]+\b)*$/.test(moduleValue) ||
|
||||
/^[A-Za-z]+\b(\.[A-Za-z]+\b)*\#[A-Za-z]+\b$/.test(moduleValue) ||
|
||||
/^[A-Za-z]+\b(\.[A-Za-z]+\b)*\#event:[A-Za-z]+\b$/.test(moduleValue);
|
||||
}
|
||||
|
||||
function splitUseinsteadValue(useinsteadValue) {
|
||||
if (!useinsteadValue || useinsteadValue === '') {
|
||||
return undefined;
|
||||
}
|
||||
const splitResult = {
|
||||
checkResult: true,
|
||||
errorInfo: ''
|
||||
}
|
||||
// 拆分字符串
|
||||
const splitArray = useinsteadValue.split(/\//g);
|
||||
if (splitArray.length === 1) {
|
||||
// 同一文件
|
||||
if (!checkModule(splitArray[0])) {
|
||||
splitResult.checkResult = false;
|
||||
}
|
||||
} else if (splitArray.length === 2) {
|
||||
// 不同文件
|
||||
const fileNameArray = splitArray[0].split('.');
|
||||
if (fileNameArray.length === 1) {
|
||||
// arkui
|
||||
if (!/^[A-Za-z]+\b$/.test(fileNameArray[0]) || !checkModule(splitArray[1])) {
|
||||
splitResult.checkResult = false;
|
||||
}
|
||||
} else {
|
||||
// 非arkui
|
||||
let checkFileName = true;
|
||||
for (let i = 0; i < fileNameArray.length; i++) {
|
||||
if (fileNameArray[0] !== 'ohos' || !/^[A-Za-z]+\b$/.test(fileNameArray[i])) {
|
||||
checkFileName = false;
|
||||
}
|
||||
}
|
||||
if (!checkFileName || !checkModule(splitArray[1])) {
|
||||
splitResult.checkResult = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 格式错误
|
||||
splitResult.checkResult = false;
|
||||
}
|
||||
if (!splitResult.checkResult) {
|
||||
splitResult.errorInfo = 'useinstead标签值错误, 请检查使用方法.';
|
||||
}
|
||||
return splitResult;
|
||||
}
|
||||
|
||||
// 精确校验功能待补全
|
||||
function checkUseinsteadValue(tag, node, sourcefile, fileName, index) {
|
||||
const tagNameValue = tag.name;
|
||||
let useinsteadResult = {
|
||||
checkResult: true,
|
||||
errorInfo: '',
|
||||
};
|
||||
const tagNameValue = tag.name;
|
||||
if (tagNameValue.indexOf('ohos') === -1 || tagNameValue.indexOf('/') === -1) {
|
||||
useinsteadResult.checkResult = false;
|
||||
useinsteadResult.errorInfo = `@useinstead value '${tagNameValue}' is wrong; `;
|
||||
const result = splitUseinsteadValue(tagNameValue, fileName);
|
||||
if (result && !result.checkResult) {
|
||||
useinsteadResult = result;
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, useinsteadResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -191,7 +264,7 @@ function checkTypeValue(tag, node, sourcefile, fileName, index) {
|
||||
const apiTypeValue = node.type?.getText();
|
||||
if (apiTypeValue !== tagTypeValue) {
|
||||
typeResult.checkResult = false;
|
||||
typeResult.errorInfo = `@type value '${tagTypeValue}' is wrong; `;
|
||||
typeResult.errorInfo = 'type标签类型错误, 请检查类型是否与属性类型一致.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, typeResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -207,7 +280,7 @@ function checkDefaultValue(tag, node, sourcefile, fileName, index) {
|
||||
};
|
||||
if (commentNodeWhiteList.includes(node.kind) && tag.name.length === 0 && tag.type.length === 0) {
|
||||
defaultResult.checkResult = false;
|
||||
defaultResult.errorInfo = 'should add @default value; ';
|
||||
defaultResult.errorInfo = 'default标签值错误, 请补充默认值.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.WRONG_ORDER, defaultResult.errorInfo, FileType.JSDOC,
|
||||
ErrorLevel.LOW);
|
||||
}
|
||||
@ -226,18 +299,9 @@ function checkPermissionTag(tag, node, sourcefile, fileName, index) {
|
||||
const tagValue = tag.name + tag.description;
|
||||
const permissionArr = tagValue.replace(/ /g, '').replace(/(or|and|\(|\))/g, '$').split('$');
|
||||
permissionArr.forEach(permissionStr => {
|
||||
if (permissionStr !== '') {
|
||||
if (!permissionRuleSet.has(permissionStr) && permissionStr !== 'N/A') {
|
||||
hasPermissionError = true;
|
||||
if (errorInfo !== '') {
|
||||
errorInfo += `,${permissionStr}`;
|
||||
} else {
|
||||
errorInfo += permissionStr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((permissionStr !== '' && !permissionRuleSet.has(permissionStr) && permissionStr !== 'N/A') || permissionStr === '') {
|
||||
hasPermissionError = true;
|
||||
errorInfo = 'permission value is null';
|
||||
errorInfo = 'permission标签值书写错误, 请检查权限字段是否已配置或者更新配置文件.';
|
||||
}
|
||||
});
|
||||
if (hasPermissionError) {
|
||||
@ -257,9 +321,10 @@ function checkDeprecatedTag(tag, node, sourcefile, fileName, index) {
|
||||
};
|
||||
const tagValue1 = tag.name;
|
||||
const tagValue2 = tag.description;
|
||||
if (tagValue1 !== 'since' || isNaN(parseFloat(tagValue2))) {
|
||||
const checkNumber = /^\d+$/.test(tagValue2);
|
||||
if ((tagValue1 !== 'since' || !checkNumber) && commentNodeWhiteList.includes(node.kind)) {
|
||||
deprecatedResult.checkResult = false;
|
||||
deprecatedResult.errorInfo = '@deprecated value is wrong';
|
||||
deprecatedResult.errorInfo = 'deprecated标签值错误, 请检查使用方法.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.UNKNOW_PERMISSION, deprecatedResult.errorInfo,
|
||||
FileType.API, ErrorLevel.LOW);
|
||||
}
|
||||
@ -283,7 +348,7 @@ function checkSyscapTag(tag, node, sourcefile, fileName, index) {
|
||||
}
|
||||
if (!syscapRuleSet.has(tagValue)) {
|
||||
syscapResult.checkResult = false;
|
||||
syscapResult.errorInfo = '@syscap value is wrong';
|
||||
syscapResult.errorInfo = 'syscap标签值错误, 请检查syscap字段是否已配置.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.UNKNOW_PERMISSION, syscapResult.errorInfo,
|
||||
FileType.API, ErrorLevel.LOW);
|
||||
}
|
||||
@ -301,7 +366,7 @@ function checkNamespaceTag(tag, node, sourcefile, fileName) {
|
||||
let apiValue = node.name?.escapedText;
|
||||
if (apiValue !== undefined && tagValue !== apiValue) {
|
||||
namespaceResult.checkResult = false;
|
||||
namespaceResult.errorInfo = '@namespace value is wrong';
|
||||
namespaceResult.errorInfo = 'namespace标签值错误, 请检查是否与namespace名称保持一致.';
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.UNKNOW_PERMISSION, namespaceResult.errorInfo,
|
||||
FileType.API, ErrorLevel.LOW);
|
||||
}
|
||||
@ -310,6 +375,29 @@ function checkNamespaceTag(tag, node, sourcefile, fileName) {
|
||||
}
|
||||
exports.checkNamespaceTag = checkNamespaceTag;
|
||||
|
||||
function checkInterfaceTypedefTag(tag, node, sourcefile, fileName) {
|
||||
let interfaceResult = {
|
||||
checkResult: true,
|
||||
errorInfo: '',
|
||||
};
|
||||
const tagValue = tag.name;
|
||||
if (commentNodeWhiteList.includes(node.kind)) {
|
||||
let apiValue = node.name?.escapedText;
|
||||
if (apiValue !== undefined && tagValue !== apiValue) {
|
||||
interfaceResult.checkResult = false;
|
||||
if (tag.tag === 'interface') {
|
||||
interfaceResult.errorInfo = 'interface标签值错误, 请检查是否与interface名称保持一致.';
|
||||
} else if (tag.tag === 'typedef') {
|
||||
interfaceResult.errorInfo = 'typedef标签值错误, 请检查是否与interface名称保持一致.';
|
||||
}
|
||||
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.UNKNOW_PERMISSION, interfaceResult.errorInfo,
|
||||
FileType.API, ErrorLevel.LOW);
|
||||
}
|
||||
}
|
||||
return interfaceResult;
|
||||
}
|
||||
exports.checkInterfaceTypedefTag = checkInterfaceTypedefTag;
|
||||
|
||||
const JsDocValueChecker = {
|
||||
'extends': checkExtendsValue,
|
||||
'enum': checkEnumValue,
|
||||
@ -323,6 +411,8 @@ const JsDocValueChecker = {
|
||||
'permission': checkPermissionTag,
|
||||
'deprecated': checkDeprecatedTag,
|
||||
'syscap': checkSyscapTag,
|
||||
'namespace': checkNamespaceTag
|
||||
'namespace': checkNamespaceTag,
|
||||
'interface': checkInterfaceTypedefTag,
|
||||
'typedef': checkInterfaceTypedefTag
|
||||
};
|
||||
exports.JsDocValueChecker = JsDocValueChecker;
|
||||
|
@ -13,15 +13,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
const parse = require('comment-parser');
|
||||
const { getAPINote, ErrorLevel, FileType, ErrorType } = require('../utils');
|
||||
const { getAPINote, ErrorLevel, FileType, ErrorType, tagsArrayOfOrder } = require('../utils');
|
||||
const { addAPICheckErrorLogs } = require('../compile_info');
|
||||
|
||||
const labelOrderArray = [
|
||||
'namespace', 'extends', 'typedef', 'interface', 'permission', 'enum', 'constant', 'type', 'param', 'default',
|
||||
'returns', 'readonly', 'throws', 'static', 'fires', 'syscap', 'systemapi', 'famodelonly', 'FAModelOnly',
|
||||
'stagemodelonly', 'StageModelOnly', 'crossplatform', 'since', 'deprecated', 'useinstead', 'form', 'example'
|
||||
];
|
||||
|
||||
/**
|
||||
* 判断标签排列是否为升序
|
||||
*/
|
||||
@ -30,8 +24,8 @@ function isAscendingOrder(tags) {
|
||||
tags.forEach((tag, index) => {
|
||||
if (index + 1 < tags.length) {
|
||||
// 获取前后两个tag下标
|
||||
const firstIndex = labelOrderArray.indexOf(tag.tag);
|
||||
const secondIndex = labelOrderArray.indexOf(tags[index + 1].tag);
|
||||
const firstIndex = tagsArrayOfOrder.indexOf(tag.tag);
|
||||
const secondIndex = tagsArrayOfOrder.indexOf(tags[index + 1].tag);
|
||||
|
||||
// 非自定义标签在前或数组降序时报错
|
||||
if ((firstIndex === -1 && secondIndex !== -1) || firstIndex > secondIndex) {
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { parseJsDoc, commentNodeWhiteList } = require('./utils');
|
||||
const ts = require(path.resolve(__dirname, "../node_modules/typescript"));
|
||||
const { parseJsDoc, commentNodeWhiteList, requireTypescriptModule, tagsArrayOfOrder } = require('./utils');
|
||||
const { checkApiOrder } = require('./check_jsdoc_value/chek_order');
|
||||
const ts = requireTypescriptModule();
|
||||
|
||||
// 标签合法性校验
|
||||
function checkJsDocLegality(node, sourcefile, checkInfoMap) {
|
||||
@ -95,7 +95,7 @@ function checkJsDocLegality(node, sourcefile, checkInfoMap) {
|
||||
}
|
||||
);
|
||||
// typedef/interface
|
||||
legalityCheck(node, sourcefile, [ts.SyntaxKind.InterfaceDeclaration], ['interface'], true, checkInfoMap,
|
||||
legalityCheck(node, sourcefile, [ts.SyntaxKind.InterfaceDeclaration], ['interface', 'typedef'], true, checkInfoMap,
|
||||
(currentNode, checkResult) => {
|
||||
return true;
|
||||
}
|
||||
@ -154,8 +154,6 @@ function legalityCheck(node, sourcefile, legalKinds, tagsName, isRequire, checkI
|
||||
} else if (tag.tag === 'deprecated') {
|
||||
useinsteadResultObj.hasDeprecated = true;
|
||||
}
|
||||
} if (tagName === 'interface' && (tag.tag === tagName || tag.tag === 'typedef')) {
|
||||
checkResult = true;
|
||||
} else if (tag.tag === tagName) {
|
||||
checkResult = true;
|
||||
}
|
||||
@ -179,9 +177,14 @@ function legalityCheck(node, sourcefile, legalKinds, tagsName, isRequire, checkI
|
||||
(tagName === 'param' && paramTagNum > parameterNum)) && extraCheckCallback(node, checkResult)) {
|
||||
// 报错
|
||||
// console.log(`${sourcefile.fileName}, ${node.getText()} should not has @${tagName}`);
|
||||
|
||||
let errorInfo = `不允许使用[${tagName}]标签, 请检查标签使用方法.`;
|
||||
if (tagName === 'param') {
|
||||
errorInfo = `第[${parameterNum + 1}]个[${tagName}]标签多余, 请检查是否应该删除标签.`;
|
||||
}
|
||||
checkInfoMap[index].illegalTags.push({
|
||||
checkResult: false,
|
||||
errorInfo: `api第[${index + 1}]段JSDoc不允许使用[${tagName}]标签, 请检查标签使用方法`,
|
||||
errorInfo: errorInfo,
|
||||
index: index
|
||||
});
|
||||
}
|
||||
@ -190,30 +193,65 @@ function legalityCheck(node, sourcefile, legalKinds, tagsName, isRequire, checkI
|
||||
return checkInfoMap;
|
||||
}
|
||||
|
||||
function checkJsDocOfCurrentNode(node, sourcefile, permissionConfigPath) {
|
||||
// 标签重复性检查
|
||||
function checkTagsQuantity(comment, index) {
|
||||
const multipleTags = ['throws', 'param']
|
||||
const tagCountObj = {};
|
||||
const checkResult = [];
|
||||
comment.tags.forEach(tag => {
|
||||
if (!tagCountObj[tag.tag]) {
|
||||
tagCountObj[tag.tag] = 0;
|
||||
}
|
||||
tagCountObj[tag.tag] = tagCountObj[tag.tag] + 1;
|
||||
});
|
||||
for (const tagName in tagCountObj) {
|
||||
if (tagCountObj[tagName] > 1 && multipleTags.indexOf(tagName) < 0) {
|
||||
checkResult.push({
|
||||
checkResult: false,
|
||||
errorInfo: `[${tagName}]标签不允许重复使用, 请删除多余标签.`,
|
||||
index: index
|
||||
});
|
||||
}
|
||||
}
|
||||
// interface/typedef互斥校验
|
||||
if (tagCountObj['interface'] > 0 & tagCountObj['typedef'] > 0) {
|
||||
checkResult.push({
|
||||
checkResult: false,
|
||||
errorInfo: 'interface标签与typedef标签不允许同时使用, 请确认接口类型.',
|
||||
index: index
|
||||
});
|
||||
}
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
function checkJsDocOfCurrentNode(node, sourcefile, permissionConfigPath, fileName) {
|
||||
let { permissionFile } = require('./utils');
|
||||
if (permissionConfigPath && fs.existsSync(permissionConfigPath)) {
|
||||
permissionFile = permissionConfigPath;
|
||||
}
|
||||
const checkInfoMap = checkJsDocLegality(node, sourcefile, {});
|
||||
const checkInfoArray = [];
|
||||
const checkOrderResult = checkApiOrder(node, sourcefile, sourcefile.fileName);
|
||||
const checkOrderResult = checkApiOrder(node, sourcefile, fileName);
|
||||
checkOrderResult.forEach((result, index) => {
|
||||
checkInfoMap[index.toString()].orderResult = result;
|
||||
});
|
||||
const comments = parseJsDoc(node);
|
||||
comments.forEach((comment, index) => {
|
||||
const errorLogs = [];
|
||||
let errorLogs = [];
|
||||
let paramIndex = 0;
|
||||
let throwsIndex = 0;
|
||||
// 值检验
|
||||
comment.tags.forEach(tag => {
|
||||
const { JsDocValueChecker } = require('./check_jsdoc_value/check_rest_value');
|
||||
const checker = JsDocValueChecker[tag.tag];
|
||||
if (checker) {
|
||||
let valueCheckResult;
|
||||
if (tag.tag === 'param') {
|
||||
valueCheckResult = checker(tag, node, sourcefile, sourcefile.fileName, paramIndex);
|
||||
valueCheckResult = checker(tag, node, sourcefile, fileName, paramIndex++);
|
||||
} else if (tag.tag === 'throws') {
|
||||
valueCheckResult = checker(tag, node, sourcefile, fileName, throwsIndex++);
|
||||
} else {
|
||||
valueCheckResult = checker(tag, node, sourcefile, sourcefile.fileName);
|
||||
valueCheckResult = checker(tag, node, sourcefile, fileName);
|
||||
}
|
||||
if (!valueCheckResult.checkResult) {
|
||||
valueCheckResult.index = index;
|
||||
@ -222,6 +260,9 @@ function checkJsDocOfCurrentNode(node, sourcefile, permissionConfigPath) {
|
||||
}
|
||||
}
|
||||
});
|
||||
// 标签数量校验
|
||||
const quantityCheckResult = checkTagsQuantity(comment, index);
|
||||
errorLogs = errorLogs.concat(quantityCheckResult);
|
||||
checkInfoMap[index.toString()].illegalTags = checkInfoMap[index.toString()].illegalTags.concat(errorLogs);
|
||||
});
|
||||
for (const key in checkInfoMap) {
|
||||
|
@ -21,6 +21,13 @@ const { addAPICheckErrorLogs } = require('./compile_info');
|
||||
|
||||
const permissionCheckWhitelist = new Set(['@ohos.wifi.d.ts', '@ohos.wifiManager.d.ts']);
|
||||
|
||||
/**
|
||||
* 门禁环境优先使用systemPermissionFile
|
||||
* 本地环境从指定分支上下载
|
||||
* 下载失败则使用默认配置
|
||||
*
|
||||
* @returns Set<string>
|
||||
*/
|
||||
function getPermissionBank() {
|
||||
const permissionTags = ['ohos.permission.HEALTH_DATA', 'ohos.permission.HEART_RATE', 'ohos.permission.ACCELERATION'];
|
||||
let permissionFileContent;
|
||||
|
@ -15,8 +15,7 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ts = require(path.resolve(__dirname, '../node_modules/typescript'));
|
||||
const { hasAPINote, getAPINote, overwriteIndexOf, ErrorType, ErrorLevel, FileType } = require('./utils');
|
||||
const { hasAPINote, getAPINote, overwriteIndexOf, ErrorType, ErrorLevel, FileType, requireTypescriptModule } = require('./utils');
|
||||
const { addAPICheckErrorLogs } = require('./compile_info');
|
||||
const rules = require('../code_style_rule.json');
|
||||
const dictionariesContent = fs.readFileSync(path.resolve(__dirname, '../plugin/dictionaries.txt'), 'utf-8');
|
||||
@ -25,7 +24,8 @@ const dictionariesSupplementaryContent = fs.readFileSync(path.resolve(__dirname,
|
||||
'../plugin/dictionaries_supplementary.txt'), 'utf-8');
|
||||
const dictionariesSupplementaryArr = dictionariesSupplementaryContent.split(/[(\r\n)\r\n]+/g);
|
||||
const dictionariesSet = new Set([...dictionariesArr, ...dictionariesSupplementaryArr, ...rules.decorators.customDoc,
|
||||
...rules.decorators.jsDoc]);
|
||||
...rules.decorators.jsDoc]);
|
||||
const ts = requireTypescriptModule();
|
||||
|
||||
function checkSpelling(node, sourcefile, fileName) {
|
||||
if (ts.isIdentifier(node) && node.escapedText) {
|
||||
|
@ -16,7 +16,23 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ExcelJS = require('exceljs');
|
||||
const cm = require('comment-parser');
|
||||
const ts = require(path.resolve(__dirname, '../node_modules/typescript'));
|
||||
function requireTypescriptModule() {
|
||||
const buildOption = require('./build.json');
|
||||
if (buildOption.isBundle) {
|
||||
return require('typescript');
|
||||
}
|
||||
const tsPathArray = [
|
||||
path.resolve(__dirname, "../node_modules/typescript"),
|
||||
path.resolve(__dirname, "../../node_modules/typescript")
|
||||
];
|
||||
if (fs.existsSync(tsPathArray[0])) {
|
||||
return require(tsPathArray[0]);
|
||||
} else if (fs.existsSync(tsPathArray[1])) {
|
||||
return require(tsPathArray[1]);
|
||||
}
|
||||
}
|
||||
exports.requireTypescriptModule = requireTypescriptModule;
|
||||
const ts = requireTypescriptModule();
|
||||
|
||||
const commentNodeWhiteList = [
|
||||
ts.SyntaxKind.PropertySignature, ts.SyntaxKind.CallSignature, ts.SyntaxKind.MethodSignature,
|
||||
@ -28,6 +44,13 @@ const commentNodeWhiteList = [
|
||||
];
|
||||
exports.commentNodeWhiteList = commentNodeWhiteList;
|
||||
|
||||
const tagsArrayOfOrder = [
|
||||
'namespace', 'extends', 'typedef', 'interface', 'permission', 'enum', 'constant', 'type', 'param', 'default',
|
||||
'returns', 'readonly', 'throws', 'static', 'fires', 'syscap', 'systemapi', 'famodelonly', 'FAModelOnly',
|
||||
'stagemodelonly', 'StageModelOnly', 'crossplatform', 'since', 'deprecated', 'useinstead', 'test', 'form', 'example'
|
||||
];
|
||||
exports.tagsArrayOfOrder = tagsArrayOfOrder;
|
||||
|
||||
function getAPINote(node) {
|
||||
const apiLength = node.getText().length;
|
||||
const apiFullLength = node.getFullText().length;
|
||||
|
@ -51,7 +51,3 @@ Options:
|
||||
在上述命令成功执行完后,会同时生成一个 .xlsx 报告。可根据报告提示,修改错误。
|
||||
|
||||
报告出现在 -i 输入的文件/文件夹的同级目录,命名方式为 文件(夹)名_时间戳.xlsx
|
||||
|
||||
## 约束
|
||||
|
||||
Node.js version 15.0.0 及以上
|
||||
|
26
build-tools/jsdoc_format_plugin/loader/flavor.js
Normal file
26
build-tools/jsdoc_format_plugin/loader/flavor.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** 修改 build.json, 同一份代码编译不同版本 */
|
||||
module.exports = function (content, map, meta) {
|
||||
const buildConfig = JSON.parse(content);
|
||||
if (buildConfig.hasOwnProperty('isBundle') && buildConfig.isBundle === false) {
|
||||
if (process.env.npm_config_bundle) {
|
||||
buildConfig.isBundle = process.env.npm_config_bundle;
|
||||
return JSON.stringify(buildConfig);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
};
|
@ -4,8 +4,11 @@
|
||||
"description": "",
|
||||
"main": "",
|
||||
"scripts": {
|
||||
"build": "webpack --mode=production",
|
||||
"format": "ts-node src/main.ts -i ../../api/ -o result/"
|
||||
"testAll": "npm install && mocha --config test/mocha/.mocharc.jsonc",
|
||||
"testOnly": "nyc mocha --config test/mocha/.mocharc.jsonc --grep",
|
||||
"pack": "webpack --mode=production",
|
||||
"build": "npm run pack --bundle",
|
||||
"formatSDK": "ts-node src/main.ts -i ../../api/ -o formatedSDK/"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@ -30,6 +33,7 @@
|
||||
"json-loader": "^0.5.7",
|
||||
"mocha": "^7.1.1",
|
||||
"mochawesome": "^7.1.3",
|
||||
"nyc": "^15.1.0",
|
||||
"terser-webpack-plugin": "^5.3.1",
|
||||
"ts-loader": "^9.2.8",
|
||||
"ts-node": "^10.9.1",
|
||||
|
@ -16,7 +16,7 @@
|
||||
import ts from 'typescript';
|
||||
import { Code } from '../utils/constant';
|
||||
import {
|
||||
comment, Context, ISourceCodeProcessor, LogReporter, ModifyLogResult, ErrorInfo,
|
||||
comment, Context, ISourceCodeProcessor, ModifyLogResult, ErrorInfo,
|
||||
ProcessResult, sourceParser, JSDocModifyType, MethodNodeType, ApiSplitProcessorInterface
|
||||
} from './typedef';
|
||||
import { CommentHelper, LogResult } from './coreImpls';
|
||||
@ -26,7 +26,7 @@ export class ApiSplitProcessor implements ISourceCodeProcessor, sourceParser.ITr
|
||||
|
||||
context?: Context;
|
||||
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
if (!context.getOptions().splitUnionTypeApi) {
|
||||
return { code: Code.OK, content: content };
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ export class AsynchronousFunctionProcessor implements ISourceCodeProcessor {
|
||||
|
||||
context?: Context;
|
||||
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
const sourceParser = context.getSourceParser(content);
|
||||
this.context = context;
|
||||
const sourceFile: ts.SourceFile | undefined = sourceParser.createSourceFile(content);
|
||||
|
@ -112,14 +112,13 @@ export class OutputFileHelper {
|
||||
|
||||
// 获取报告输出路径
|
||||
static getLogReportFilePath(inputParam: InputParameter): string {
|
||||
const fileTimeStamp = FileUtils.getFileTimeStamp();
|
||||
const fileName = path.basename(inputParam.inputFilePath, '.d.ts');
|
||||
if (inputParam.outputFilePath) {
|
||||
const dirName = path.dirname(inputParam.outputFilePath);
|
||||
return path.join(dirName, `${fileName}_${fileTimeStamp}.xlsx`);
|
||||
return path.join(dirName, `${fileName}.xlsx`);
|
||||
} else {
|
||||
const dirName = path.dirname(inputParam.inputFilePath);
|
||||
return path.join(dirName, `${fileName}_${fileTimeStamp}.xlsx`);
|
||||
return path.join(dirName, `${fileName}.xlsx`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,11 +218,11 @@ export class SourceCodeParserImpl extends sourceParser.SourceCodeParser {
|
||||
const hasComment: boolean = currentCommentNode.commentInfos ? currentCommentNode.commentInfos.length > 0 : false;
|
||||
const { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
||||
if (thiz.shouldNotifyCallback(node, hasComment, onlyVisitHasComment)) {
|
||||
LogUtil.d('SourceCodeParserImpl', `kind: ${node.kind}, line: ${line}, ${JSON.stringify(currentCommentNode.commentInfos)}`);
|
||||
LogUtil.d('SourceCodeParserImpl', `kind: ${node.kind}, line: ${line + 1}, ${JSON.stringify(currentCommentNode.commentInfos)}`);
|
||||
callback.onVisitNode(currentCommentNode);
|
||||
} else {
|
||||
LogUtil.d('SourceCodeParserImpl',
|
||||
`skip, kind: ${node.kind}, line: ${line}, character: ${character}, commnet size: ${currentCommentNode.commentInfos?.length}`);
|
||||
`skip, [ ${node.getText()} ] kind: ${node.kind}, line: ${line + 1}, character: ${character}, comment size: ${currentCommentNode.commentInfos?.length}`);
|
||||
}
|
||||
if (thiz.shouldForEachChildren(node)) {
|
||||
node.forEachChild((child) => {
|
||||
@ -798,6 +797,7 @@ export class InputParameter {
|
||||
outputFilePath: string | undefined;
|
||||
logLevel: string = '';
|
||||
splitUnionTypeApi: boolean = false;
|
||||
branch: string = 'master';
|
||||
options: Options = new Options();
|
||||
|
||||
parse() {
|
||||
@ -806,16 +806,20 @@ export class InputParameter {
|
||||
.name('jsdoc-tool')
|
||||
.description('CLI to format d.ts')
|
||||
.version('0.1.0')
|
||||
.allowUnknownOption(true)
|
||||
.requiredOption('-i, --input <path>', `${StringResource.getString(StringResourceId.COMMAND_INPUT_DESCRIPTION)}`)
|
||||
.option('-o, --output <path>', `${StringResource.getString(StringResourceId.COMMAND_OUT_DESCRIPTION)}`)
|
||||
.option('-l, --logLevel <INFO,WARN,DEBUG,ERR>', `${StringResource.getString(StringResourceId.COMMAND_LOGLEVEL_DESCRIPTION)}`, 'INFO')
|
||||
.option('-s, --split', `${StringResource.getString(StringResourceId.COMMAND_SPLIT_API)}`, false);
|
||||
.option('-s, --split', `${StringResource.getString(StringResourceId.COMMAND_SPLIT_API)}`, false)
|
||||
.option('-b, --branch <string>', `${StringResource.getString(StringResourceId.COMMAND_BRANCH)}`, 'master');
|
||||
|
||||
program.parse();
|
||||
const options = program.opts();
|
||||
this.inputFilePath = options.input;
|
||||
this.outputFilePath = options.output;
|
||||
this.logLevel = options.logLevel;
|
||||
this.splitUnionTypeApi = options.split;
|
||||
this.branch = options.branch;
|
||||
this.checkInput();
|
||||
}
|
||||
|
||||
@ -859,6 +863,7 @@ export class InputParameter {
|
||||
}
|
||||
}
|
||||
this.options.splitUnionTypeApi = this.splitUnionTypeApi;
|
||||
this.options.workingBranch = this.branch;
|
||||
}
|
||||
|
||||
private checkFileExists(filePath: string) {
|
||||
@ -953,16 +958,16 @@ export class LogReporterImpl implements LogReporter {
|
||||
return this.modifyResultMap;
|
||||
}
|
||||
|
||||
writeCheckResults(path: string): void {
|
||||
this.writer?.writeResults(this.checkResults, undefined, path);
|
||||
async writeCheckResults(path: string): Promise<void> {
|
||||
await this.writer?.writeResults(this.checkResults, undefined, path);
|
||||
}
|
||||
|
||||
writeModifyResults(path: string): void {
|
||||
this.writer?.writeResults(undefined, this.modifyResults, path);
|
||||
async writeModifyResults(path: string): Promise<void> {
|
||||
await this.writer?.writeResults(undefined, this.modifyResults, path);
|
||||
}
|
||||
|
||||
writeAllResults(path: string): void {
|
||||
this.writer?.writeResults(this.checkResults, this.modifyResults, path);
|
||||
async writeAllResults(path: string): Promise<void> {
|
||||
await this.writer?.writeResults(this.checkResults, this.modifyResults, path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -995,7 +1000,8 @@ export class ExcelWriter implements LogWriter {
|
||||
});
|
||||
}
|
||||
|
||||
writeResults(checkResults: Array<CheckLogResult> | undefined, modifyResults: Array<ModifyLogResult> | undefined, path: string): void {
|
||||
async writeResults(checkResults: Array<CheckLogResult> | undefined,
|
||||
modifyResults: Array<ModifyLogResult> | undefined, path: string): Promise<void> {
|
||||
if (checkResults) {
|
||||
const checkResultsSheet: excelJs.Worksheet = this.workBook.addWorksheet('待确认报告');
|
||||
checkResultsSheet.columns = this.checkResultsColumns;
|
||||
@ -1006,7 +1012,7 @@ export class ExcelWriter implements LogWriter {
|
||||
modifyResultsSheet.columns = this.modifyResultsColumns;
|
||||
modifyResultsSheet.addRows(modifyResults);
|
||||
}
|
||||
this.workBook.xlsx.writeFile(path);
|
||||
await this.workBook.xlsx.writeFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,27 +30,26 @@ import { Context, IJSDocModifier, ISourceCodeProcessor, LogReporter, ProcessResu
|
||||
*/
|
||||
export class JSDocModifierImpl implements IJSDocModifier {
|
||||
tag: string = 'jsdoc-tool';
|
||||
start(): void {
|
||||
const inputParameter = new InputParameter();
|
||||
try {
|
||||
inputParameter.parse();
|
||||
} catch (error) {
|
||||
LogUtil.e(this.tag, error);
|
||||
return;
|
||||
}
|
||||
this.startInternal(inputParameter);
|
||||
async start(): Promise<void> {
|
||||
await this.startInternal();
|
||||
}
|
||||
|
||||
startInternal(inputParameter: InputParameter) {
|
||||
LogUtil.logLevel = LogLevelUtil.get(inputParameter.logLevel);
|
||||
const sourceProcessor: ISourceCodeProcessor = this.getSourceProcessor(inputParameter);
|
||||
const baseContext: Context = this.getBaseContext(inputParameter);
|
||||
LogUtil.i(this.tag, StringResource.getString(StringResourceId.START_MESSAGE));
|
||||
const result: ProcessResult = sourceProcessor.process(baseContext, '');
|
||||
if (result.code !== Code.OK) {
|
||||
LogUtil.e(this.tag, result.content);
|
||||
} else {
|
||||
LogUtil.i(this.tag, result.content);
|
||||
async startInternal(): Promise<void> {
|
||||
try {
|
||||
const inputParameter = new InputParameter();
|
||||
inputParameter.parse();
|
||||
LogUtil.logLevel = LogLevelUtil.get(inputParameter.logLevel);
|
||||
const sourceProcessor: ISourceCodeProcessor = this.getSourceProcessor(inputParameter);
|
||||
const baseContext: Context = this.getBaseContext(inputParameter);
|
||||
LogUtil.i(this.tag, StringResource.getString(StringResourceId.START_MESSAGE));
|
||||
const result: ProcessResult = await sourceProcessor.process(baseContext, '');
|
||||
if (result.code !== Code.OK) {
|
||||
LogUtil.e(this.tag, result.content);
|
||||
} else {
|
||||
LogUtil.i(this.tag, result.content);
|
||||
}
|
||||
} catch (error) {
|
||||
LogUtil.e(this.tag, error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,20 +64,6 @@ export class JSDocModifierImpl implements IJSDocModifier {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class JSDOcModifierTestEntry extends JSDocModifierImpl {
|
||||
|
||||
runTest(inputFile: string, outputFile: string) {
|
||||
const inputParameter = new InputParameter();
|
||||
inputParameter.inputFilePath = inputFile;
|
||||
inputParameter.outputFilePath = outputFile;
|
||||
inputParameter.splitUnionTypeApi = true;
|
||||
inputParameter.getOptions().splitUnionTypeApi = true;
|
||||
this.startInternal(inputParameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class BaseSourceCodeProcessor implements ISourceCodeProcessor {
|
||||
inputParam: InputParameter;
|
||||
|
||||
@ -86,7 +71,7 @@ abstract class BaseSourceCodeProcessor implements ISourceCodeProcessor {
|
||||
this.inputParam = inputParam;
|
||||
}
|
||||
|
||||
abstract process(context: Context, code: string): ProcessResult;
|
||||
abstract process(context: Context, code: string): Promise<ProcessResult>;
|
||||
|
||||
buildProcessorContext(parentContext: Context, inputFile: string): Context {
|
||||
return new ContextImpl(inputFile,
|
||||
@ -100,7 +85,7 @@ abstract class BaseSourceCodeProcessor implements ISourceCodeProcessor {
|
||||
*/
|
||||
export class SingleFileProcessor extends BaseSourceCodeProcessor {
|
||||
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
const inputFilePath = context.getInputFile();
|
||||
if (!inputFilePath) {
|
||||
return {
|
||||
@ -118,20 +103,20 @@ export class SingleFileProcessor extends BaseSourceCodeProcessor {
|
||||
|
||||
let preResult = {
|
||||
code: Code.OK,
|
||||
content: rawCodeStr!
|
||||
content: rawCodeStr ? rawCodeStr : ''
|
||||
};
|
||||
const newContext = this.buildProcessorContext(context, context.getInputFile());
|
||||
const logReporter: LogReporter = context.getLogReporter();
|
||||
newContext.setLogReporter(logReporter);
|
||||
for (let processor of processorRegistry) {
|
||||
preResult = processor.process(newContext, preResult.content);
|
||||
preResult = await processor.process(newContext, preResult.content);
|
||||
if (preResult.code === Code.ERROR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 报告落盘
|
||||
const reportFilePath: string = OutputFileHelper.getLogReportFilePath(this.inputParam);
|
||||
context.getLogReporter().writeAllResults(reportFilePath);
|
||||
await context.getLogReporter().writeAllResults(reportFilePath);
|
||||
LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}`);
|
||||
if (preResult.code === Code.OK) {
|
||||
preResult.content = `new d.ts file is ${newContext.getOutputFile()}`;
|
||||
@ -144,7 +129,7 @@ export class SingleFileProcessor extends BaseSourceCodeProcessor {
|
||||
* 处理文件夹。
|
||||
*/
|
||||
export class MultiFileProcessor extends BaseSourceCodeProcessor {
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
const intpuDir = context.getInputFile();
|
||||
if (!intpuDir) {
|
||||
return {
|
||||
@ -157,14 +142,14 @@ export class MultiFileProcessor extends BaseSourceCodeProcessor {
|
||||
});
|
||||
const errorSet: Array<ProcessResult> = new Array();
|
||||
const logReporter: LogReporter = context.getLogReporter();
|
||||
allSourceFiles.forEach((childFile) => {
|
||||
for (const childFile of allSourceFiles) {
|
||||
const rawCodeStr = FileUtils.readFileContent(childFile);
|
||||
if (StringUtils.isEmpty(rawCodeStr)) {
|
||||
errorSet.push({
|
||||
code: Code.ERROR,
|
||||
content: `${childFile}: ${StringResource.getString(StringResourceId.INPUT_FILE_CONTENT_EMPTY)}`
|
||||
});
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
const newContext = this.buildProcessorContext(context, childFile);
|
||||
newContext.setLogReporter(logReporter);
|
||||
@ -174,21 +159,21 @@ export class MultiFileProcessor extends BaseSourceCodeProcessor {
|
||||
};
|
||||
|
||||
for (let processor of processorRegistry) {
|
||||
preValue = processor.process(newContext, preValue.content);
|
||||
preValue = await processor.process(newContext, preValue.content);
|
||||
if (preValue.code !== Code.OK) {
|
||||
errorSet.push(preValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 报告落盘
|
||||
const reportFilePath: string = OutputFileHelper.getLogReportFilePath(this.inputParam);
|
||||
context.getLogReporter().writeAllResults(reportFilePath);
|
||||
await context.getLogReporter().writeAllResults(reportFilePath);
|
||||
LogUtil.i('jsdoc-tool', `the report file is in ${reportFilePath}`);
|
||||
return {
|
||||
code: errorSet.length > 0 ? Code.ERROR : Code.OK,
|
||||
content: errorSet.length > 0 ? JSON.stringify(errorSet)
|
||||
: `new d.ts file is in ${OutputFileHelper.getMultiOutputDir(this.inputParam)}`
|
||||
content: errorSet.length > 0 ? JSON.stringify(errorSet) :
|
||||
`new d.ts file is in ${OutputFileHelper.getMultiOutputDir(this.inputParam)}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -32,11 +32,12 @@ export class CommentModificationProcessor implements ISourceCodeProcessor {
|
||||
context?: Context;
|
||||
rawSourceCodeInfo?: rawInfo.RawSourceCodeInfo;
|
||||
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
this.context = context;
|
||||
const newParser = context.getSourceParser(content);
|
||||
this.logReporter = context.getLogReporter();
|
||||
this.rawSourceCodeInfo = context.getRawSourceCodeInfo();
|
||||
await apiChecker.initEnv(context.getOptions().workingBranch);
|
||||
const newSourceFile = newParser.visitEachNodeComment(this, false);
|
||||
return {
|
||||
code: Code.OK,
|
||||
@ -48,7 +49,7 @@ export class CommentModificationProcessor implements ISourceCodeProcessor {
|
||||
if (node.astNode) {
|
||||
const curNode: ts.Node = node.astNode;
|
||||
// 获取诊断信息
|
||||
const checkResults = apiChecker.checkJSDoc(node.astNode, node.astNode?.getSourceFile());
|
||||
const checkResults = apiChecker.checkJSDoc(node.astNode, node.astNode?.getSourceFile(), this.context?.getInputFile());
|
||||
const newCommentIndexs: number[] = [];
|
||||
const newCommentInfos: comment.CommentInfo[] = node.commentInfos ? [...node.commentInfos] : [];
|
||||
// 获取需要整改的JSDoc数组
|
||||
@ -95,10 +96,17 @@ export class CommentModificationProcessor implements ISourceCodeProcessor {
|
||||
const modifyResult: boolean = JSDocModificationManager.addTagFrommParentNode(node, commentInfo, tagName,
|
||||
this.context);
|
||||
if (modifyResult) {
|
||||
const modifyLogResult: ModifyLogResult = LogResult.createModifyResult(curNode, newCommentInfos,
|
||||
JSDocModificationManager.createErrorInfo(ErrorInfo.COMPLETE_INHERIT_TAG_INFORMATION, [`${jsdocNumber}`, `${tagName}`]),
|
||||
this.context, apiName, JSDocModifyType.MISSING_TAG_COMPLETION);
|
||||
this.logReporter?.addModifyResult(modifyLogResult);
|
||||
if (tagName === 'permission') {
|
||||
const checkLogResult: CheckLogResult = LogResult.createCheckResult(curNode, newCommentInfos,
|
||||
JSDocModificationManager.createErrorInfo(ErrorInfo.COMPLETE_INHERIT_PERMISSION_TAG_ERROR, [`${jsdocNumber}`, `${tagName}`]),
|
||||
this.context, apiName, JSDocCheckErrorType.INCOMPLETE_TAG);
|
||||
this.logReporter?.addCheckResult(checkLogResult);
|
||||
} else {
|
||||
const modifyLogResult: ModifyLogResult = LogResult.createModifyResult(curNode, newCommentInfos,
|
||||
JSDocModificationManager.createErrorInfo(ErrorInfo.COMPLETE_INHERIT_TAG_INFORMATION, [`${jsdocNumber}`, `${tagName}`]),
|
||||
this.context, apiName, JSDocModifyType.MISSING_TAG_COMPLETION);
|
||||
this.logReporter?.addModifyResult(modifyLogResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -120,11 +128,13 @@ export class CommentModificationProcessor implements ISourceCodeProcessor {
|
||||
}
|
||||
if (!modifier || !modifyResult) {
|
||||
let modifyErrorInfo: string = ErrorInfo.COMPLETE_TAG_ERROR;
|
||||
let insteadInfo: string[] = [`${jsdocNumber}`, `${tagName}`];
|
||||
if (tagName === 'interface') {
|
||||
modifyErrorInfo = ErrorInfo.COMPLETE_INTERFACE_TAG_ERROR;
|
||||
insteadInfo = [`${jsdocNumber}`];
|
||||
}
|
||||
const checkLogResult: CheckLogResult = LogResult.createCheckResult(curNode, newCommentInfos,
|
||||
JSDocModificationManager.createErrorInfo(modifyErrorInfo, [`${jsdocNumber}`, `${tagName}`]),
|
||||
JSDocModificationManager.createErrorInfo(modifyErrorInfo, insteadInfo),
|
||||
this.context, apiName, JSDocCheckErrorType.INCOMPLETE_TAG);
|
||||
this.logReporter?.addCheckResult(checkLogResult);
|
||||
}
|
||||
@ -241,7 +251,9 @@ class JSDocModificationManager {
|
||||
if (node.parentNode) {
|
||||
const pTag: comment.CommentTag | undefined = getParentTag(node.parentNode, tagName);
|
||||
if (pTag) {
|
||||
commentInfo.commentTags.push(pTag);
|
||||
if (tagName !== 'permission') {
|
||||
commentInfo.commentTags.push(pTag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -298,7 +310,7 @@ class JSDocModificationManager {
|
||||
commentInfo.commentTags.push(newCommentTag);
|
||||
// 提示description缺失信息
|
||||
const checkLogResult: CheckLogResult = LogResult.createCheckResult(node.astNode, commentInfos,
|
||||
JSDocModificationManager.createErrorInfo(ErrorInfo.PARAM_FORAMT_DESCRIPTION_ERROR, [`${i + 1}`]), context, apiName,
|
||||
JSDocModificationManager.createErrorInfo(ErrorInfo.PARAM_FORAMT_DESCRIPTION_ERROR, [`${curIndex + 1}`]), context, apiName,
|
||||
JSDocCheckErrorType.PARAM_DESCRIPTION_WARNING);
|
||||
context?.getLogReporter().addCheckResult(checkLogResult);
|
||||
}
|
||||
@ -364,7 +376,6 @@ class JSDocModificationManager {
|
||||
const jsDocModifier: Map<string, JsDocModificationInterface> = new Map([
|
||||
['constant', JSDocModificationManager.addTagWithoutValue],
|
||||
['deprecated', JSDocModificationManager.addTagFrommParentNode],
|
||||
['enum', JSDocModificationManager.addTagWithValue],
|
||||
['extends', JSDocModificationManager.addTagWithValue],
|
||||
['famodelonly', JSDocModificationManager.addTagFrommParentNode],
|
||||
['namespace', JSDocModificationManager.addTagWithValue],
|
||||
@ -382,10 +393,10 @@ const jsDocModifier: Map<string, JsDocModificationInterface> = new Map([
|
||||
const JSDOC_ORDER_TAGS_ARRAY = [
|
||||
'namespace', 'extends', 'typedef', 'interface', 'permission', 'enum', 'constant', 'type', 'param', 'default',
|
||||
'returns', 'readonly', 'throws', 'static', 'fires', 'syscap', 'systemapi', 'famodelonly', 'FAModelOnly',
|
||||
'stagemodelonly', 'StageModelOnly', 'crossplatform', 'since', 'deprecated', 'useinstead', 'form', 'example'
|
||||
'stagemodelonly', 'StageModelOnly', 'crossplatform', 'since', 'deprecated', 'useinstead', 'test', 'form', 'example'
|
||||
];
|
||||
|
||||
/**
|
||||
* 继承标签白名单
|
||||
*/
|
||||
const INHERIT_TAGS_ARRAY = ['deprecated', 'famodelonly', 'stagemodelonly', 'systemapi', 'test'];
|
||||
const INHERIT_TAGS_ARRAY = ['deprecated', 'famodelonly', 'stagemodelonly', 'systemapi', 'test', 'permission'];
|
||||
|
@ -22,7 +22,7 @@ import { CommentHelper } from './coreImpls';
|
||||
import { comment, Context, ISourceCodeProcessor, ProcessResult, sourceParser } from './typedef';
|
||||
|
||||
export class OutputProcessor implements ISourceCodeProcessor {
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
try {
|
||||
let outputContent = content;
|
||||
const formater = new Formatter(content);
|
||||
@ -36,7 +36,7 @@ export class OutputProcessor implements ISourceCodeProcessor {
|
||||
|
||||
FileUtils.writeStringToFile(outputContent, context.getOutputFile());
|
||||
return { code: Code.OK, content: outputContent };
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
LogUtil.e('OutputProcessor', `error: ${context.getInputFile()}, ${error}`);
|
||||
return { code: Code.OK, content: content };
|
||||
}
|
||||
@ -69,7 +69,7 @@ class Formatter implements sourceParser.INodeVisitorCallback {
|
||||
class NumberLiteralCaseRule {
|
||||
upperCase: boolean;
|
||||
sourceParser: sourceParser.SourceCodeParser;
|
||||
content: string|undefined;
|
||||
content: string | undefined;
|
||||
|
||||
constructor(upperCase: boolean, sourceParser: sourceParser.SourceCodeParser) {
|
||||
this.upperCase = upperCase;
|
||||
|
@ -5,7 +5,7 @@ import { comment, Context, ISourceCodeProcessor, ProcessResult, sourceParser } f
|
||||
|
||||
export class RawSourceCodeProcessor implements ISourceCodeProcessor, sourceParser.INodeVisitorCallback {
|
||||
rawSourceCodeInfo?: RawSourceCodeInfoImpl;
|
||||
process(context: Context, content: string): ProcessResult {
|
||||
async process(context: Context, content: string): Promise<ProcessResult> {
|
||||
const sourceParser: sourceParser.SourceCodeParser = context.getSourceParser(content);
|
||||
this.rawSourceCodeInfo = new RawSourceCodeInfoImpl(content);
|
||||
sourceParser.visitEachNodeComment(this, false);
|
||||
|
@ -30,7 +30,7 @@ export interface ISourceCodeProcessor {
|
||||
*
|
||||
* @param content
|
||||
*/
|
||||
process(context: Context, content: string): ProcessResult;
|
||||
process(context: Context, content: string): Promise<ProcessResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,8 +101,8 @@ export class Options {
|
||||
commentOptions = {
|
||||
emptyLineUnderDescrition: true
|
||||
};
|
||||
|
||||
splitUnionTypeApi: boolean = false;
|
||||
workingBranch: string = 'master';
|
||||
}
|
||||
|
||||
|
||||
@ -616,19 +616,19 @@ export interface LogReporter {
|
||||
* 将校验结果报告落盘
|
||||
* @param path 报告落盘路径
|
||||
*/
|
||||
writeCheckResults(path: string): void;
|
||||
writeCheckResults(path: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* 将整改结果报告落盘
|
||||
* @param path 报告落盘路径
|
||||
*/
|
||||
writeModifyResults(path: string): void;
|
||||
writeModifyResults(path: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* 将结果报告落盘
|
||||
* @param path 报告落盘路径
|
||||
*/
|
||||
writeAllResults(path: string): void;
|
||||
writeAllResults(path: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* 传入writer对象
|
||||
@ -693,7 +693,7 @@ export interface LogWriter {
|
||||
* @param modifyResults 整改结果集
|
||||
* @param path 报告落盘路径
|
||||
*/
|
||||
writeResults(checkResults: Array<CheckLogResult> | undefined, modifyResults: Array<ModifyLogResult> | undefined, path: string): void;
|
||||
writeResults(checkResults: Array<CheckLogResult> | undefined, modifyResults: Array<ModifyLogResult> | undefined, path: string): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -784,7 +784,7 @@ export namespace sourceParser {
|
||||
* 整改工具的API
|
||||
*/
|
||||
export interface IJSDocModifier {
|
||||
start(): void;
|
||||
start(): Promise<void>;
|
||||
}
|
||||
|
||||
interface OrderResultInfo {
|
||||
@ -819,11 +819,12 @@ export enum ErrorInfo {
|
||||
PARAM_FORAMT_DESCRIPTION_ERROR = '请自行添加第[$$]个参数的描述信息.',
|
||||
COMPLETE_TAG_INFORMATION = '补全第[$$]段JSDoc的@$$标签.',
|
||||
COMPLETE_INHERIT_TAG_INFORMATION = '第[$$]段JSDoc从父类继承@$$标签.',
|
||||
COMPLETE_TAG_ERROR = '第[$$]段JSDoc缺少@$$标签, 请自行确认修改.',
|
||||
COMPLETE_INTERFACE_TAG_ERROR = '第[$$]段JSDoc缺少@$$标签, 请自行确认补全@interface或者@type标签.',
|
||||
COMPLETE_INHERIT_PERMISSION_TAG_ERROR = '第[$$]段JSDoc父类存在@$$标签, 请自行确认并补全相同标签.',
|
||||
COMPLETE_TAG_ERROR = '第[$$]段JSDoc缺少@$$标签, 请自行确认并修改.',
|
||||
COMPLETE_INTERFACE_TAG_ERROR = '第[$$]段JSDoc缺少@interface或@typedef标签, 请自行确认并补全@interface或@typedef标签.',
|
||||
MODIFY_TAG_ORDER_INFORMATION = '第[$$]段JSDoc标签顺序调整.',
|
||||
JSDOC_FORMAT_ERROR = 'JSDoc格式错误, 请检查.',
|
||||
JSDOC_ILLEGAL_ERROR = '第[$$]段JSDoc校验失败: ',
|
||||
JSDOC_ILLEGAL_ERROR = '第[$$]段JSDoc校验失败: \n',
|
||||
EVENT_SUBSCRIPTION_SPLITTION = '对事件订阅函数[$$]进行了拆分.',
|
||||
AYYNCHRONOUS_FUNCTION_JSDOC_COPY = '对异步函数[$$]进行了JSDoc复制.'
|
||||
}
|
||||
|
@ -15,10 +15,39 @@
|
||||
|
||||
import { JSDocModifierImpl } from './core/entry';
|
||||
import { IJSDocModifier } from './core/typedef';
|
||||
import { ConstantValue, StringResourceId } from './utils/constant';
|
||||
import { StringResource, StringUtils } from './utils/stringUtils';
|
||||
|
||||
function main() {
|
||||
checkEnvVersion();
|
||||
const jsDocModifier: IJSDocModifier = new JSDocModifierImpl();
|
||||
jsDocModifier.start();
|
||||
}
|
||||
|
||||
function checkEnvVersion(): void {
|
||||
const version = process.version;
|
||||
const versionRegExp = /^v(\d+)\.(\d+)\.(\d+).*/;
|
||||
const matchArray = version.match(versionRegExp);
|
||||
const requiredVersions = [ConstantValue.MAJOR_V, ConstantValue.MINOR_V, ConstantValue.PATCH_V];
|
||||
let showVersionWarning = true;
|
||||
if (matchArray && matchArray.length === 4) {
|
||||
for (let index = 0; index < 3; index++) {
|
||||
const curV = Number(matchArray[index + 1]);
|
||||
const requiredV = requiredVersions[index];
|
||||
if (curV > requiredV || curV < requiredV) {
|
||||
showVersionWarning = curV > requiredV;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showVersionWarning) {
|
||||
return;
|
||||
}
|
||||
let hintMessage = StringResource.getString(StringResourceId.VERSION_HINT);
|
||||
hintMessage = StringUtils.formatString(hintMessage, requiredVersions);
|
||||
console.warn('jsdoc-tool:', hintMessage);
|
||||
}
|
||||
|
||||
main();
|
@ -39,7 +39,9 @@ export enum StringResourceId {
|
||||
OUTPUT_MUST_DIR,
|
||||
OUTPUT_SAME_WITH_INPUT,
|
||||
OUTPUT_SUBDIR_INPUT,
|
||||
START_MESSAGE
|
||||
START_MESSAGE,
|
||||
COMMAND_BRANCH,
|
||||
VERSION_HINT
|
||||
}
|
||||
|
||||
export enum Instruct {
|
||||
@ -56,4 +58,19 @@ export class ConstantValue {
|
||||
* d.ts文件后缀名
|
||||
*/
|
||||
static DTS_EXTENSION = '.d.ts';
|
||||
|
||||
/**
|
||||
* nodejs 最低主版本号
|
||||
*/
|
||||
static MAJOR_V = 15;
|
||||
|
||||
/**
|
||||
* nodejs 最低子版本号
|
||||
*/
|
||||
static MINOR_V = 0;
|
||||
|
||||
/**
|
||||
* nodejs 最低修正版本号
|
||||
*/
|
||||
static PATCH_V = 0;
|
||||
}
|
@ -14,15 +14,15 @@
|
||||
*/
|
||||
|
||||
export enum LogLevel {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERR = 3
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERR
|
||||
}
|
||||
|
||||
export class LogLevelUtil {
|
||||
static get(level: string): LogLevel {
|
||||
for (let v = LogLevel.INFO; v <= LogLevel.ERR; v++) {
|
||||
for (let v = LogLevel.DEBUG; v <= LogLevel.ERR; v++) {
|
||||
if (level === LogLevel[v]) {
|
||||
return v;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ const ZH_STRING_MAP: Map<number, string> = new Map([
|
||||
[StringResourceId.OUTPUT_SAME_WITH_INPUT, '输出文件路径与输入文件路径相同'],
|
||||
[StringResourceId.OUTPUT_SUBDIR_INPUT, '输出目录不能是输入目录的子目录'],
|
||||
[StringResourceId.START_MESSAGE, '正在处理, 请稍后 ...'],
|
||||
[StringResourceId.COMMAND_BRANCH, 'OpenHarmony 分支名'],
|
||||
[StringResourceId.VERSION_HINT, '告警, 需要nodejs $0.$1.$2+']
|
||||
]);
|
||||
|
||||
const EN_STRING_MAP: Map<number, string> = new Map([
|
||||
@ -47,6 +49,8 @@ const EN_STRING_MAP: Map<number, string> = new Map([
|
||||
[StringResourceId.OUTPUT_SAME_WITH_INPUT, 'the output file path is same as the input file path'],
|
||||
[StringResourceId.OUTPUT_SUBDIR_INPUT, 'the output directory cannot be a subdirectory of the input directory'],
|
||||
[StringResourceId.START_MESSAGE, 'Processing please wait ...'],
|
||||
[StringResourceId.COMMAND_BRANCH, 'OpenHarmony branch name'],
|
||||
[StringResourceId.VERSION_HINT, 'warning, nodejs version $0.$1.$2+ is required']
|
||||
]);
|
||||
|
||||
export class StringResource {
|
||||
@ -61,13 +65,21 @@ export class StringUtils {
|
||||
return str === undefined || str.length === 0;
|
||||
}
|
||||
|
||||
static hasSubstring(str: string, sub: string|RegExp): boolean {
|
||||
static hasSubstring(str: string, sub: string | RegExp): boolean {
|
||||
return str.search(sub) !== -1;
|
||||
}
|
||||
|
||||
static replaceAt(src: string, index: number, replacement: string) {
|
||||
return src.substring(0, index) + replacement + src.substring(index + replacement.length);
|
||||
}
|
||||
|
||||
static formatString(pattern: string, args: Array<any>): string {
|
||||
let newStr = pattern;
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
newStr = newStr.replace(`$${index}`, `${args[index]}`);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
}
|
||||
|
||||
export class LogReportStringUtils {
|
||||
|
24
build-tools/jsdoc_format_plugin/test/mocha/.mocharc.jsonc
Normal file
24
build-tools/jsdoc_format_plugin/test/mocha/.mocharc.jsonc
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
{
|
||||
"require": [
|
||||
"test/mocha/init.js"
|
||||
],
|
||||
"spec": [
|
||||
"test/testCase/*.ts"
|
||||
],
|
||||
"reporter": "mochawesome"
|
||||
}
|
18
build-tools/jsdoc_format_plugin/test/mocha/init.js
Normal file
18
build-tools/jsdoc_format_plugin/test/mocha/init.js
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require("ts-node").register({
|
||||
project: "tsconfig.json"
|
||||
});
|
121
build-tools/jsdoc_format_plugin/test/testCase/runTest.ts
Normal file
121
build-tools/jsdoc_format_plugin/test/testCase/runTest.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { expect } from 'chai';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { JSDocModifierImpl } from '../../src/core/entry';
|
||||
|
||||
describe('testSingleFile', function () {
|
||||
const testFileDir = path.join(__dirname, '..', '/ut/');
|
||||
const outFileDir = path.join(__dirname, '..', '/output/testSingleFile/');
|
||||
const expectFileDir = path.join(__dirname, '..', '/expect/');
|
||||
const testFileNames = fs.readdirSync(testFileDir);
|
||||
const argLen = process.argv.length;
|
||||
testFileNames.forEach((testFileName) => {
|
||||
const testFilePath = path.join(testFileDir, testFileName);
|
||||
const outputFilePath = path.join(outFileDir, testFileName);
|
||||
const expectFilePath = path.join(expectFileDir, testFileName);
|
||||
it('testFile#' + testFilePath, async function () {
|
||||
if (fs.existsSync(outputFilePath)) {
|
||||
fs.rmSync(outputFilePath);
|
||||
}
|
||||
const inputParams = [];
|
||||
inputParams.push('-s');
|
||||
inputParams.push('-i');
|
||||
inputParams.push(testFilePath);
|
||||
inputParams.push('-o');
|
||||
inputParams.push(outputFilePath);
|
||||
process.argv.splice(argLen, inputParams.length, ...inputParams);
|
||||
const testEntry = new JSDocModifierImpl();
|
||||
await testEntry.start();
|
||||
|
||||
const outputFileContent: string = fs.readFileSync(outputFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
expect(outputFileContent).eql(expectFileContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('testMultiFiles', function () {
|
||||
const testFileDir = path.join(__dirname, '..', '/ut/');
|
||||
const outFileDir = path.join(__dirname, '..', '/output/testMultiFiles/');
|
||||
const expectFileDir = path.join(__dirname, '..', '/expect/');
|
||||
|
||||
before(async function () {
|
||||
this.timeout(10000);
|
||||
const inputParams = [];
|
||||
inputParams.push('-s');
|
||||
inputParams.push('-i');
|
||||
inputParams.push(testFileDir);
|
||||
inputParams.push('-o');
|
||||
inputParams.push(outFileDir);
|
||||
process.argv.splice(process.argv.length, 0, ...inputParams);
|
||||
const testEntry = new JSDocModifierImpl();
|
||||
await testEntry.start();
|
||||
});
|
||||
|
||||
const testFileNames = fs.readdirSync(testFileDir);
|
||||
testFileNames.forEach((testFileName) => {
|
||||
const testFilePath = path.join(testFileDir, testFileName);
|
||||
const outputFilePath = path.join(outFileDir, testFileName);
|
||||
const expectFilePath = path.join(expectFileDir, testFileName);
|
||||
it('testDir#' + testFilePath, function () {
|
||||
const outputFileContent: string = fs.readFileSync(outputFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
expect(outputFileContent).eql(expectFileContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('testBundleSingleFile', function () {
|
||||
const testFileDir = path.join(__dirname, '..', '/ut/');
|
||||
const outFileDir = path.join(__dirname, '..', '/output/testBundleSingleFile/');
|
||||
const expectFileDir = path.join(__dirname, '..', '/expect/');
|
||||
const testFileNames = fs.readdirSync(testFileDir);
|
||||
const nodeExecute = process.execPath;
|
||||
const { execFileSync } = require('child_process');
|
||||
testFileNames.forEach((testFileName) => {
|
||||
const testFilePath = path.join(testFileDir, testFileName);
|
||||
const outputFilePath = path.join(outFileDir, testFileName);
|
||||
const expectFilePath = path.join(expectFileDir, testFileName);
|
||||
it('bundle_file#' + testFileName, function () {
|
||||
if (fs.existsSync(outputFilePath)) {
|
||||
fs.rmSync(outputFilePath);
|
||||
}
|
||||
execFileSync(nodeExecute, [
|
||||
'build/bundle.js', '-s',
|
||||
'-i', `${testFilePath}`,
|
||||
'-o', `${outputFilePath}`
|
||||
]);
|
||||
const outputFileContent: string = fs.readFileSync(outputFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
expect(outputFileContent).eql(expectFileContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('testBundleMultiFiles', function () {
|
||||
const testFileDir = path.join(__dirname, '..', '/ut/');
|
||||
const outFileDir = path.join(__dirname, '..', '/output/testBundleMultiFiles/');
|
||||
const expectFileDir = path.join(__dirname, '..', '/expect/');
|
||||
const nodeExecute = process.execPath;
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
before(function () {
|
||||
this.timeout(10000);
|
||||
execFileSync(nodeExecute, [
|
||||
'build/bundle.js', '-s',
|
||||
'-i', `${testFileDir}`,
|
||||
'-o', `${outFileDir}`
|
||||
]);
|
||||
});
|
||||
|
||||
const testFileNames = fs.readdirSync(testFileDir);
|
||||
testFileNames.forEach((testFileName) => {
|
||||
const outputFilePath = path.join(outFileDir, testFileName);
|
||||
const expectFilePath = path.join(expectFileDir, testFileName);
|
||||
it('bundle_dir#' + testFileName, function () {
|
||||
const outputFileContent: string = fs.readFileSync(outputFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
const expectFileContent: string = fs.readFileSync(expectFilePath, 'utf-8').replace(/\r\n/g, '\n');
|
||||
expect(outputFileContent).eql(expectFileContent);
|
||||
});
|
||||
});
|
||||
});
|
@ -13,10 +13,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const webpack = require("webpack");
|
||||
const packageInfo = require('./package.json')
|
||||
const path = require('path');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const packageInfo = require('./package.json');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const config = {
|
||||
@ -39,12 +39,11 @@ module.exports = (env, argv) => {
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
loader: 'json-loader',
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
/test/
|
||||
test: /build\.json$/,
|
||||
use: [
|
||||
{
|
||||
loader: path.resolve(__dirname, 'loader/flavor.js')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -67,6 +66,6 @@ module.exports = (env, argv) => {
|
||||
entryOnly: true
|
||||
})
|
||||
]
|
||||
}
|
||||
};
|
||||
return config;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user