diff --git a/build-tools/api_check_plugin/code_style_rule.json b/build-tools/api_check_plugin/code_style_rule.json index 99d3bf5aa..193f5b630 100644 --- a/build-tools/api_check_plugin/code_style_rule.json +++ b/build-tools/api_check_plugin/code_style_rule.json @@ -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", diff --git a/build-tools/api_check_plugin/config/config.json b/build-tools/api_check_plugin/config/config.json index fdef2cbd4..c04b76d80 100644 --- a/build-tools/api_check_plugin/config/config.json +++ b/build-tools/api_check_plugin/config/config.json @@ -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 } ] } -} +} \ No newline at end of file diff --git a/build-tools/api_check_plugin/index.js b/build-tools/api_check_plugin/index.js index 71ba39e09..730d0a41e 100644 --- a/build-tools/api_check_plugin/index.js +++ b/build-tools/api_check_plugin/index.js @@ -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); -}; \ No newline at end of file +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`; +} \ No newline at end of file diff --git a/build-tools/api_check_plugin/src/api_check_plugin.js b/build-tools/api_check_plugin/src/api_check_plugin.js index 996dccc5c..ae103ca42 100644 --- a/build-tools/api_check_plugin/src/api_check_plugin.js +++ b/build-tools/api_check_plugin/src/api_check_plugin.js @@ -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) { diff --git a/build-tools/api_check_plugin/src/build.json b/build-tools/api_check_plugin/src/build.json new file mode 100644 index 000000000..af2e0ca04 --- /dev/null +++ b/build-tools/api_check_plugin/src/build.json @@ -0,0 +1,3 @@ +{ + "isBundle": false +} \ No newline at end of file diff --git a/build-tools/api_check_plugin/src/check_hump.js b/build-tools/api_check_plugin/src/check_hump.js index 0fa01d3f3..d89a96a5f 100644 --- a/build-tools/api_check_plugin/src/check_hump.js +++ b/build-tools/api_check_plugin/src/check_hump.js @@ -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 = ''; diff --git a/build-tools/api_check_plugin/src/check_jsdoc_value/check_rest_value.js b/build-tools/api_check_plugin/src/check_jsdoc_value/check_rest_value.js index 2c8d59e9f..7e4e44dd6 100644 --- a/build-tools/api_check_plugin/src/check_jsdoc_value/check_rest_value.js +++ b/build-tools/api_check_plugin/src/check_jsdoc_value/check_rest_value.js @@ -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; diff --git a/build-tools/api_check_plugin/src/check_jsdoc_value/chek_order.js b/build-tools/api_check_plugin/src/check_jsdoc_value/chek_order.js index dd509d4b4..39588c0e8 100644 --- a/build-tools/api_check_plugin/src/check_jsdoc_value/chek_order.js +++ b/build-tools/api_check_plugin/src/check_jsdoc_value/chek_order.js @@ -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) { diff --git a/build-tools/api_check_plugin/src/check_legality.js b/build-tools/api_check_plugin/src/check_legality.js index fa2f71c65..ee162821b 100644 --- a/build-tools/api_check_plugin/src/check_legality.js +++ b/build-tools/api_check_plugin/src/check_legality.js @@ -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) { diff --git a/build-tools/api_check_plugin/src/check_permission.js b/build-tools/api_check_plugin/src/check_permission.js index c381ce7e5..f8168463b 100644 --- a/build-tools/api_check_plugin/src/check_permission.js +++ b/build-tools/api_check_plugin/src/check_permission.js @@ -21,6 +21,13 @@ const { addAPICheckErrorLogs } = require('./compile_info'); const permissionCheckWhitelist = new Set(['@ohos.wifi.d.ts', '@ohos.wifiManager.d.ts']); +/** + * 门禁环境优先使用systemPermissionFile + * 本地环境从指定分支上下载 + * 下载失败则使用默认配置 + * + * @returns Set + */ function getPermissionBank() { const permissionTags = ['ohos.permission.HEALTH_DATA', 'ohos.permission.HEART_RATE', 'ohos.permission.ACCELERATION']; let permissionFileContent; diff --git a/build-tools/api_check_plugin/src/check_spelling.js b/build-tools/api_check_plugin/src/check_spelling.js index fa2e46f88..43df997f3 100644 --- a/build-tools/api_check_plugin/src/check_spelling.js +++ b/build-tools/api_check_plugin/src/check_spelling.js @@ -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) { diff --git a/build-tools/api_check_plugin/src/utils.js b/build-tools/api_check_plugin/src/utils.js index c833680c5..dcacb7c3a 100644 --- a/build-tools/api_check_plugin/src/utils.js +++ b/build-tools/api_check_plugin/src/utils.js @@ -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; diff --git a/build-tools/jsdoc_format_plugin/README.md b/build-tools/jsdoc_format_plugin/README.md index e88c0b80a..d829cbad7 100644 --- a/build-tools/jsdoc_format_plugin/README.md +++ b/build-tools/jsdoc_format_plugin/README.md @@ -51,7 +51,3 @@ Options: 在上述命令成功执行完后,会同时生成一个 .xlsx 报告。可根据报告提示,修改错误。 报告出现在 -i 输入的文件/文件夹的同级目录,命名方式为 文件(夹)名_时间戳.xlsx - -## 约束 - -Node.js version 15.0.0 及以上 diff --git a/build-tools/jsdoc_format_plugin/loader/flavor.js b/build-tools/jsdoc_format_plugin/loader/flavor.js new file mode 100644 index 000000000..eccf4103b --- /dev/null +++ b/build-tools/jsdoc_format_plugin/loader/flavor.js @@ -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; +}; \ No newline at end of file diff --git a/build-tools/jsdoc_format_plugin/package.json b/build-tools/jsdoc_format_plugin/package.json index 741f53e3f..2da9662ac 100644 --- a/build-tools/jsdoc_format_plugin/package.json +++ b/build-tools/jsdoc_format_plugin/package.json @@ -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", diff --git a/build-tools/jsdoc_format_plugin/src/core/apiSplitProcessor.ts b/build-tools/jsdoc_format_plugin/src/core/apiSplitProcessor.ts index a132c58ff..436d9cd13 100644 --- a/build-tools/jsdoc_format_plugin/src/core/apiSplitProcessor.ts +++ b/build-tools/jsdoc_format_plugin/src/core/apiSplitProcessor.ts @@ -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 { if (!context.getOptions().splitUnionTypeApi) { return { code: Code.OK, content: content }; } diff --git a/build-tools/jsdoc_format_plugin/src/core/asynchronousFunctionProcessor.ts b/build-tools/jsdoc_format_plugin/src/core/asynchronousFunctionProcessor.ts index 165442a54..632858908 100644 --- a/build-tools/jsdoc_format_plugin/src/core/asynchronousFunctionProcessor.ts +++ b/build-tools/jsdoc_format_plugin/src/core/asynchronousFunctionProcessor.ts @@ -26,7 +26,7 @@ export class AsynchronousFunctionProcessor implements ISourceCodeProcessor { context?: Context; - process(context: Context, content: string): ProcessResult { + async process(context: Context, content: string): Promise { const sourceParser = context.getSourceParser(content); this.context = context; const sourceFile: ts.SourceFile | undefined = sourceParser.createSourceFile(content); diff --git a/build-tools/jsdoc_format_plugin/src/core/coreImpls.ts b/build-tools/jsdoc_format_plugin/src/core/coreImpls.ts index 253fbeee4..f7d5769c8 100644 --- a/build-tools/jsdoc_format_plugin/src/core/coreImpls.ts +++ b/build-tools/jsdoc_format_plugin/src/core/coreImpls.ts @@ -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 ', `${StringResource.getString(StringResourceId.COMMAND_INPUT_DESCRIPTION)}`) .option('-o, --output ', `${StringResource.getString(StringResourceId.COMMAND_OUT_DESCRIPTION)}`) .option('-l, --logLevel ', `${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 ', `${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 { + await this.writer?.writeResults(this.checkResults, undefined, path); } - writeModifyResults(path: string): void { - this.writer?.writeResults(undefined, this.modifyResults, path); + async writeModifyResults(path: string): Promise { + 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 { + await this.writer?.writeResults(this.checkResults, this.modifyResults, path); } } @@ -995,7 +1000,8 @@ export class ExcelWriter implements LogWriter { }); } - writeResults(checkResults: Array | undefined, modifyResults: Array | undefined, path: string): void { + async writeResults(checkResults: Array | undefined, + modifyResults: Array | undefined, path: string): Promise { 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); } } diff --git a/build-tools/jsdoc_format_plugin/src/core/entry.ts b/build-tools/jsdoc_format_plugin/src/core/entry.ts index abb9f2312..b31187341 100644 --- a/build-tools/jsdoc_format_plugin/src/core/entry.ts +++ b/build-tools/jsdoc_format_plugin/src/core/entry.ts @@ -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 { + 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 { + 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; 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 { 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 { const intpuDir = context.getInputFile(); if (!intpuDir) { return { @@ -157,14 +142,14 @@ export class MultiFileProcessor extends BaseSourceCodeProcessor { }); const errorSet: Array = 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)}` }; } } diff --git a/build-tools/jsdoc_format_plugin/src/core/modificationProcessor.ts b/build-tools/jsdoc_format_plugin/src/core/modificationProcessor.ts index 61e6f54e9..f1f785592 100644 --- a/build-tools/jsdoc_format_plugin/src/core/modificationProcessor.ts +++ b/build-tools/jsdoc_format_plugin/src/core/modificationProcessor.ts @@ -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 { 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 = 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 = 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']; diff --git a/build-tools/jsdoc_format_plugin/src/core/outputProcessor.ts b/build-tools/jsdoc_format_plugin/src/core/outputProcessor.ts index 2162a0171..9d6ee6787 100644 --- a/build-tools/jsdoc_format_plugin/src/core/outputProcessor.ts +++ b/build-tools/jsdoc_format_plugin/src/core/outputProcessor.ts @@ -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 { 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; diff --git a/build-tools/jsdoc_format_plugin/src/core/rawCodeProcessor.ts b/build-tools/jsdoc_format_plugin/src/core/rawCodeProcessor.ts index d434c0a68..d01115b3e 100644 --- a/build-tools/jsdoc_format_plugin/src/core/rawCodeProcessor.ts +++ b/build-tools/jsdoc_format_plugin/src/core/rawCodeProcessor.ts @@ -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 { const sourceParser: sourceParser.SourceCodeParser = context.getSourceParser(content); this.rawSourceCodeInfo = new RawSourceCodeInfoImpl(content); sourceParser.visitEachNodeComment(this, false); diff --git a/build-tools/jsdoc_format_plugin/src/core/typedef.ts b/build-tools/jsdoc_format_plugin/src/core/typedef.ts index 23933018e..24a94b4e3 100644 --- a/build-tools/jsdoc_format_plugin/src/core/typedef.ts +++ b/build-tools/jsdoc_format_plugin/src/core/typedef.ts @@ -30,7 +30,7 @@ export interface ISourceCodeProcessor { * * @param content */ - process(context: Context, content: string): ProcessResult; + process(context: Context, content: string): Promise; } /** @@ -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; /** * 将整改结果报告落盘 * @param path 报告落盘路径 */ - writeModifyResults(path: string): void; + writeModifyResults(path: string): Promise; /** * 将结果报告落盘 * @param path 报告落盘路径 */ - writeAllResults(path: string): void; + writeAllResults(path: string): Promise; /** * 传入writer对象 @@ -693,7 +693,7 @@ export interface LogWriter { * @param modifyResults 整改结果集 * @param path 报告落盘路径 */ - writeResults(checkResults: Array | undefined, modifyResults: Array | undefined, path: string): void; + writeResults(checkResults: Array | undefined, modifyResults: Array | undefined, path: string): Promise; } /** @@ -784,7 +784,7 @@ export namespace sourceParser { * 整改工具的API */ export interface IJSDocModifier { - start(): void; + start(): Promise; } 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复制.' } diff --git a/build-tools/jsdoc_format_plugin/src/main.ts b/build-tools/jsdoc_format_plugin/src/main.ts index 4b85007d1..96e2e9d98 100644 --- a/build-tools/jsdoc_format_plugin/src/main.ts +++ b/build-tools/jsdoc_format_plugin/src/main.ts @@ -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(); \ No newline at end of file diff --git a/build-tools/jsdoc_format_plugin/src/utils/constant.ts b/build-tools/jsdoc_format_plugin/src/utils/constant.ts index de27e96f6..1be51abd3 100644 --- a/build-tools/jsdoc_format_plugin/src/utils/constant.ts +++ b/build-tools/jsdoc_format_plugin/src/utils/constant.ts @@ -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; } \ No newline at end of file diff --git a/build-tools/jsdoc_format_plugin/src/utils/logUtil.ts b/build-tools/jsdoc_format_plugin/src/utils/logUtil.ts index f4a80a30e..5625bcc72 100644 --- a/build-tools/jsdoc_format_plugin/src/utils/logUtil.ts +++ b/build-tools/jsdoc_format_plugin/src/utils/logUtil.ts @@ -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; } diff --git a/build-tools/jsdoc_format_plugin/src/utils/stringUtils.ts b/build-tools/jsdoc_format_plugin/src/utils/stringUtils.ts index 374075741..1a391c820 100644 --- a/build-tools/jsdoc_format_plugin/src/utils/stringUtils.ts +++ b/build-tools/jsdoc_format_plugin/src/utils/stringUtils.ts @@ -30,6 +30,8 @@ const ZH_STRING_MAP: Map = 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 = new Map([ @@ -47,6 +49,8 @@ const EN_STRING_MAP: Map = 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): string { + let newStr = pattern; + for (let index = 0; index < args.length; index++) { + newStr = newStr.replace(`$${index}`, `${args[index]}`); + } + return newStr; + } } export class LogReportStringUtils { diff --git a/build-tools/jsdoc_format_plugin/test/mocha/.mocharc.jsonc b/build-tools/jsdoc_format_plugin/test/mocha/.mocharc.jsonc new file mode 100644 index 000000000..df375ac2a --- /dev/null +++ b/build-tools/jsdoc_format_plugin/test/mocha/.mocharc.jsonc @@ -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" +} \ No newline at end of file diff --git a/build-tools/jsdoc_format_plugin/test/mocha/init.js b/build-tools/jsdoc_format_plugin/test/mocha/init.js new file mode 100644 index 000000000..89b811c8a --- /dev/null +++ b/build-tools/jsdoc_format_plugin/test/mocha/init.js @@ -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" +}); diff --git a/build-tools/jsdoc_format_plugin/test/testCase/runTest.ts b/build-tools/jsdoc_format_plugin/test/testCase/runTest.ts new file mode 100644 index 000000000..41ddd3f6e --- /dev/null +++ b/build-tools/jsdoc_format_plugin/test/testCase/runTest.ts @@ -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); + }); + }); +}); \ No newline at end of file diff --git a/build-tools/jsdoc_format_plugin/webpack.config.js b/build-tools/jsdoc_format_plugin/webpack.config.js index b8951736e..2e34b61d0 100644 --- a/build-tools/jsdoc_format_plugin/webpack.config.js +++ b/build-tools/jsdoc_format_plugin/webpack.config.js @@ -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; -} \ No newline at end of file +}; \ No newline at end of file