From 18074f6723b9a86197ac8b8636501f181eabf860 Mon Sep 17 00:00:00 2001 From: yangbo Date: Thu, 25 Aug 2022 20:30:16 +0800 Subject: [PATCH] add api_check_plugin Signed-off-by: yangbo Change-Id: I34d79a05a2dbb08dbba7b927dd71d4eab148cc99 --- .../api_check_plugin/check_result.json | 4 + .../api_check_plugin/code_style_rule.json | 101 ++++++++++++++++++ build-tools/api_check_plugin/entry.js | 41 +++++++ build-tools/api_check_plugin/package.json | 18 ++++ .../api_check_plugin/src/api_check_plugin.js | 74 +++++++++++++ .../api_check_plugin/src/check_decorator.js | 70 ++++++++++++ .../api_check_plugin/src/compile_info.js | 16 +++ build-tools/api_check_plugin/src/utils.js | 56 ++++++++++ 8 files changed, 380 insertions(+) create mode 100644 build-tools/api_check_plugin/check_result.json create mode 100644 build-tools/api_check_plugin/code_style_rule.json create mode 100644 build-tools/api_check_plugin/entry.js create mode 100644 build-tools/api_check_plugin/package.json create mode 100644 build-tools/api_check_plugin/src/api_check_plugin.js create mode 100644 build-tools/api_check_plugin/src/check_decorator.js create mode 100644 build-tools/api_check_plugin/src/compile_info.js create mode 100644 build-tools/api_check_plugin/src/utils.js diff --git a/build-tools/api_check_plugin/check_result.json b/build-tools/api_check_plugin/check_result.json new file mode 100644 index 0000000000..52613c01aa --- /dev/null +++ b/build-tools/api_check_plugin/check_result.json @@ -0,0 +1,4 @@ +{ + "apiFiles":[], + "scanResult":[] +} \ No newline at end of file diff --git a/build-tools/api_check_plugin/code_style_rule.json b/build-tools/api_check_plugin/code_style_rule.json new file mode 100644 index 0000000000..43f3672795 --- /dev/null +++ b/build-tools/api_check_plugin/code_style_rule.json @@ -0,0 +1,101 @@ +{ + "decorators": { + "customDoc": [ + "constant", + "default", + "deprecated", + "enum", + "errornumber", + "example", + "extends", + "famodelonly", + "fires", + "interface", + "namespace", + "param", + "permission", + "readonly", + "returns", + "since", + "stagemodelonly", + "static", + "syscap", + "systemapi", + "type", + "typedef", + "throws", + "test", + "useinstead" + ], + "jsDoc": [ + "abstract", + "access", + "alias", + "async", + "augments", + "author", + "borrows", + "class", + "classdesc", + "constant", + "constructs", + "copyright", + "default", + "deprecated", + "enum", + "event", + "example", + "exports", + "external", + "file", + "fires", + "function", + "generator", + "global", + "hideconstructor", + "ignore", + "implements", + "inheritdoc", + "inner", + "instance", + "interface", + "lends", + "license", + "listens", + "member", + "memberof", + "mixes", + "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", + "also", + "description", + "kind", + "name", + "undocumented" + ] + } +} \ No newline at end of file diff --git a/build-tools/api_check_plugin/entry.js b/build-tools/api_check_plugin/entry.js new file mode 100644 index 0000000000..b1be8c322c --- /dev/null +++ b/build-tools/api_check_plugin/entry.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +const path = require("path"); + +function checkEntry(url) { + let result = "API CHECK FAILED!"; + try { + __dirname = "../../interface/sdk-js/build-tools/api_check_plugin"; + const execSync = require("child_process").execSync; + execSync("cd ../../interface/sdk-js/build-tools/api_check_plugin && npm install"); + const { scanEntry } = require(path.resolve(__dirname, "./src/api_check_plugin")); + result = scanEntry(url); + const { removeDir } = require(path.resolve(__dirname, "./src/utils")); + removeDir(path.resolve(__dirname, "node_modules")); + } catch (error) { + // catch error + } + return result; +} + +// function checkEntryLocalText(url) { +// let execSync = require("child_process").execSync; +// execSync("npm install"); +// const { test } = require("./src/api_check_plugin"); +// console.log("entry", test(url)) +// } + +// checkEntryLocalText("XXXX") diff --git a/build-tools/api_check_plugin/package.json b/build-tools/api_check_plugin/package.json new file mode 100644 index 0000000000..595ff2274c --- /dev/null +++ b/build-tools/api_check_plugin/package.json @@ -0,0 +1,18 @@ +{ + "name": "test", + "version": "1.0.0", + "description": "", + "main": "collect.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "check": "node ./src/api_check_plugin.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "path": "^0.12.7", + "typescript": "^4.7.4" + } + } + \ 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 new file mode 100644 index 0000000000..e865a4b3b9 --- /dev/null +++ b/build-tools/api_check_plugin/src/api_check_plugin.js @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +const path = require("path"); +const fs = require("fs"); +const ts = require(path.resolve(__dirname, "../node_modules/typescript")); +const { checkAPIDecorators } = require("./check_decorator"); +const { hasAPINote } = require("./utils"); +let result = require("../check_result.json"); + +function checkAPICodeStyle(url) { + if (fs.existsSync(url)) { + const mdApiFiles = getMdFiles(url); + tsTransform(mdApiFiles, checkAPICodeStyleCallback); + } +} + +function getMdFiles(url) { + const content = fs.readFileSync(url, "utf-8"); + const mdFiles = content.split("\r\n"); + return mdFiles; +} + +function tsTransform(uFiles, callback) { + uFiles.forEach(filePath => { + if (/\.d\.ts/.test(filePath)) { + const content = fs.readFileSync(filePath, "utf-8"); + const fileName = path.basename(filePath).replace(/.d.ts/g, ".ts"); + ts.transpileModule(content, { + compilerOptions: { + "target": ts.ScriptTarget.ES2017 + }, + fileName: fileName, + transformers: { before: [callback(filePath)] } + }) + } + }) +} + +function checkAPICodeStyleCallback(fileName) { + return (context) => { + return (node) => { + checkAllNode(node, node, fileName); + return node; + } + } +} + +function checkAllNode(node, sourcefile, fileName) { + // 校验装饰器 + if (hasAPINote(node)) { + checkAPIDecorators(node, sourcefile, fileName); + } + node.getChildren().forEach((item) => checkAllNode(item, sourcefile, fileName)); +} + +function scanEntry(url) { + // 入口 + checkAPICodeStyle(url); + return JSON.stringify(result.scanResult); +} +exports.scanEntry = scanEntry; diff --git a/build-tools/api_check_plugin/src/check_decorator.js b/build-tools/api_check_plugin/src/check_decorator.js new file mode 100644 index 0000000000..24f3e94493 --- /dev/null +++ b/build-tools/api_check_plugin/src/check_decorator.js @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +const rules = require("../code_style_rule.json"); +const result = require("../check_result.json"); +const { getAPINote } = require("./utils"); + +// 收集装饰器错误节点信息,防止重复收集 +const API_ERROR_DECORATOR_POS = new Set([]); + +function checkAPIDecorators(node, sourcefile, fileName) { + const apiNote = getAPINote(node); + if (API_ERROR_DECORATOR_POS.has(node.pos)) { + return; + } + + const regex = /\*\s*\@[A-Za-z0-9]+\b/g; + const matchResult = apiNote.match(regex); + console.log(matchResult) + let hasCodeStyleError = false; + let errorInfo = ""; + if (matchResult) { + matchResult.forEach(decorator => { + const docTags = [...rules.decorators["customDoc"], ...rules.decorators["jsDoc"]]; + const decoratorRuleSet = new Set(docTags); + const apiDecorator = decorator.replace(/^\*\s*\@/, ""); + if (!decoratorRuleSet.has(apiDecorator)) { + hasCodeStyleError = true; + if (errorInfo !== "") { + errorInfo += `,${apiDecorator}`; + } else { + errorInfo += apiDecorator; + } + } + }); + + if (hasCodeStyleError) { + API_ERROR_DECORATOR_POS.add(node.pos); + const checkFailFileNameSet = new Set(result.apiFiles); + if (!checkFailFileNameSet.has(fileName)) { + result.apiFiles.push(fileName); + } + const posOfNode = sourcefile.getLineAndCharacterOfPosition(node.pos); + const errorMessage = { + "error_type": "unknow decorator", + "file": fileName, + "column": posOfNode.character + 1, + "line": posOfNode.line + 1, + "error_info": errorInfo + }; + const scanResultSet = new Set(result.scanResult); + scanResultSet.add(errorMessage); + result.scanResult = [...scanResultSet]; + } + } +} + +exports.checkAPIDecorators = checkAPIDecorators; \ No newline at end of file diff --git a/build-tools/api_check_plugin/src/compile_info.js b/build-tools/api_check_plugin/src/compile_info.js new file mode 100644 index 0000000000..75d15eaeab --- /dev/null +++ b/build-tools/api_check_plugin/src/compile_info.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +// 优化输出结构 \ No newline at end of file diff --git a/build-tools/api_check_plugin/src/utils.js b/build-tools/api_check_plugin/src/utils.js new file mode 100644 index 0000000000..4e553c038b --- /dev/null +++ b/build-tools/api_check_plugin/src/utils.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2022 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. + */ +const fs = require("fs"); +const path = require("path"); + +function getAPINote(node) { + return node.getFullText().replace(node.getText(), ""); +} +exports.getAPINote = getAPINote; + +function hasAPINote(node) { + const apiNote = getAPINote(node).replace(/[\s]/g, ""); + if (apiNote && apiNote.length !== 0) { + return true; + } + return false; +} +exports.hasAPINote = hasAPINote; + +function removeDir(url) { + let statObj = fs.statSync(url); + if (statObj.isDirectory()) { + let dirs = fs.readdirSync(url); + dirs = dirs.map(dir => path.join(url, dir)); + for (let i = 0; i < dirs.length; i++) { + removeDir(dirs[i]); + } + fs.rmdirSync(url); + } else { + fs.unlinkSync(url); + } +} +exports.removeDir = removeDir; + +function writeResultFile(resultData, outputPath, option) { + fs.writeFile(path.resolve(__dirname, outputPath), JSON.stringify(resultData, null, 2), option, err => { + if (err) { + console.error(`ERROR FOR CREATE FILE:${err}`); + } else { + console.log('API CHECK FINISH!'); + } + }) +} +exports.writeResultFile = writeResultFile; -- Gitee