mirror of
https://gitee.com/openharmony/interface_sdk-js
synced 2025-03-03 06:48:54 +00:00
add jsdoc_format_plugin
Signed-off-by: yangbo_404 <yangbo198@huawei.com> Change-Id: Idf42f5b77eb4ccb20045a128d19ebd2119b9e771
This commit is contained in:
parent
e26cef7d8a
commit
4aa7e5f55b
14
build-tools/jsdoc_format_plugin/entry.js
Normal file
14
build-tools/jsdoc_format_plugin/entry.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
26
build-tools/jsdoc_format_plugin/jsdoc_rules.js
Normal file
26
build-tools/jsdoc_format_plugin/jsdoc_rules.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 decoratorSort = [
|
||||
['description'],
|
||||
['@namespace', '@extends', '@typedef', '@interface', '@permission', '@enum', '@constant'],
|
||||
['@type', '@param', '@default'],
|
||||
['@returns'],
|
||||
['@readonly', '@throws'],
|
||||
['@static'],
|
||||
['@fires'],
|
||||
['@syscap', '@systemapi', '@famodeonly', '@stagemodeonly', '@crossplatform', '@since', '@deprecated', '@useinstead', '@example']
|
||||
]
|
||||
exports.DECORATOR_SORT_LIST = decoratorSort;
|
17
build-tools/jsdoc_format_plugin/package.json
Normal file
17
build-tools/jsdoc_format_plugin/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "jsdoc_format_plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "collect.js",
|
||||
"scripts": {
|
||||
"test": "cd test && node test.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"exceljs": "^4.3.0",
|
||||
"fs": "^0.0.1-security",
|
||||
"path": "^0.12.7",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
2
build-tools/jsdoc_format_plugin/plugin/api_file_path.txt
Normal file
2
build-tools/jsdoc_format_plugin/plugin/api_file_path.txt
Normal file
@ -0,0 +1,2 @@
|
||||
# api absolute path
|
||||
Z:\root\interface\sdk-js\api\xxx.d.ts
|
14
build-tools/jsdoc_format_plugin/src/format_logs.js
Normal file
14
build-tools/jsdoc_format_plugin/src/format_logs.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
157
build-tools/jsdoc_format_plugin/src/jsdoc_format_plugin.js
Normal file
157
build-tools/jsdoc_format_plugin/src/jsdoc_format_plugin.js
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 ts = require('typescript');
|
||||
const { overwriteApiFile, getApiFiles, tsTransform, hasAPINote, getApiInfo, JSDOC_WRITELIST_SET, getAPINote,
|
||||
hasCopyright, getParentApiInfo, TS_KEYWORD_SET } = require('./utils');
|
||||
|
||||
const formatedNodes = new Set([]);
|
||||
let copyrightMessage = "";
|
||||
|
||||
function formatApiFile(url) {
|
||||
return (context) => {
|
||||
return (node) => {
|
||||
sourceFile = node;
|
||||
const fileName = url.replace(/\.d\.ts$/, ".new.d.ts");
|
||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
||||
const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
||||
overwriteApiFile(fileName, result, {});
|
||||
tsTransform([fileName], formatJsDoc);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkJsDoc(node) {
|
||||
if (hasAPINote(node)) {
|
||||
if (!getApiInfo(node).version) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function createNewJsdoc(node, docContent) {
|
||||
// step1. get api information
|
||||
let hasCR = false;
|
||||
if (hasCopyright(docContent)) {
|
||||
contents = docContent.split(/\*\//);
|
||||
hasCR = true;
|
||||
if (hasCopyright(contents[0])) {
|
||||
copyrightMessage = contents[0] + "*/";
|
||||
}
|
||||
docContent = docContent.replace(copyrightMessage, "");
|
||||
}
|
||||
// 根据* @拆分jsDoc
|
||||
const docs = docContent.split(/ *\* *\@/);
|
||||
|
||||
let docInfo = {};
|
||||
docs.forEach((doc, docsIndex) => {
|
||||
const lineInfos = doc.split(/[(\r\n)\r\n]+/);
|
||||
let decoratorContent = "";
|
||||
lineInfos.forEach((lineInfo) => {
|
||||
const content = lineInfo.replace(/( *\/\*\* *)|( *\*\/ *)|( *\* *)/g, "");
|
||||
if (content !== "") {
|
||||
decoratorContent += `${content} `;
|
||||
}
|
||||
});
|
||||
decoratorContent = decoratorContent.trim();
|
||||
if (docsIndex === 0) {
|
||||
docInfo["description"] = decoratorContent;
|
||||
// 待补充全量报错场景
|
||||
if (ts.isConstructorDeclaration(node)) {
|
||||
docInfo["apiName"] = "constructor";
|
||||
} else {
|
||||
if (!node.name) {
|
||||
console.log(node.getFullText())
|
||||
}
|
||||
docInfo["apiName"] = node.name.escapedText.toString();
|
||||
}
|
||||
} else {
|
||||
const decoratorName = decoratorContent.match(/^[a-z]+\b/)[0];
|
||||
if (JSDOC_WRITELIST_SET.has(decoratorName)) {
|
||||
if (!docInfo[decoratorName]) {
|
||||
docInfo[decoratorName] = [decoratorContent.replace(`${decoratorName} `, "")];
|
||||
} else {
|
||||
docInfo[decoratorName] = docInfo[decoratorName].push(decoratorContent.replace(`${decoratorName} `, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
docInfo = getParentApiInfo(node, docInfo);
|
||||
// 缩进空格
|
||||
const indentations = docContent.match(/(?<=\n) *(?=$)/g)[0];
|
||||
|
||||
// step2. create jsdoc
|
||||
let newNodeFullText = "";
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
const docSystemapi = docInfo.systemapi && docInfo.systemapi[0] ? `${indentations + " * "}@systemapi\n` : "";
|
||||
const docModel = docInfo.model && docInfo.model[0] ? `${indentations + " * "}@${docInfo.model[0]}\n` : "";
|
||||
const docCrossplatform = docInfo.crossplatform && docInfo.crossplatform[0] ?
|
||||
`${indentations + " * "}@crossplatform\n` : "";
|
||||
const docDeprecated = docInfo.deprecated && docInfo.deprecated[0] ?
|
||||
`${indentations + " * "}@deprecated ${docInfo.deprecated[0]}\n` : "";
|
||||
const docUseinstead = docInfo.useinstead && docInfo.useinstead[0] ?
|
||||
`${indentations + " * "}@useinstead ${docInfo.useinstead[0]}\n` : "";
|
||||
const docPermission = docInfo.permission && docInfo.permission[0] ?
|
||||
`${indentations + " * "}@useinstead ${docInfo.permission[0]}\n` : "";
|
||||
|
||||
newNodeFullText = `${indentations}/**\n` +
|
||||
`${indentations + " * "}${docInfo.description}\n` +
|
||||
`${indentations + " * "}@namespace ${docInfo.namespace ? docInfo.namespace : docInfo.apiName}\n` +
|
||||
docPermission +
|
||||
`${indentations + " * "}@syscap ${docInfo.syscap}\n` +
|
||||
docSystemapi + docModel + docCrossplatform +
|
||||
`${indentations + " * "}@since ${docInfo.since}\n` +
|
||||
docDeprecated + docUseinstead +
|
||||
`${indentations + " */"}\n${indentations}`;
|
||||
if (hasCR) {
|
||||
newNodeFullText = copyrightMessage + "\n" + newNodeFullText + node.getText();
|
||||
}
|
||||
}
|
||||
return newNodeFullText;
|
||||
}
|
||||
|
||||
function formatJsDoc(fileName) {
|
||||
return (context) => {
|
||||
return (node) => {
|
||||
sourceFile = node;
|
||||
node = ts.visitEachChild(node, processAllNodes, context);
|
||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
||||
const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
||||
overwriteApiFile(fileName, result, {});
|
||||
return node;
|
||||
}
|
||||
function processAllNodes(node) {
|
||||
if (hasAPINote(node) && !formatedNodes.has(fileName + node.pos + node.end) && !TS_KEYWORD_SET.has(node.kind)) {
|
||||
formatedNodes.add(fileName + node.pos + node.end);
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
const result = createNewJsdoc(node, getAPINote(node));
|
||||
console.log(result);
|
||||
}
|
||||
}
|
||||
return ts.visitEachChild(node, processAllNodes, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main(apiDir) {
|
||||
const apiFiles = getApiFiles(apiDir);
|
||||
tsTransform(apiFiles, formatApiFile);
|
||||
}
|
||||
|
||||
const testDir = path.resolve(__dirname, "../plugin/api_file_path.txt");
|
||||
main(testDir);
|
223
build-tools/jsdoc_format_plugin/src/utils.js
Normal file
223
build-tools/jsdoc_format_plugin/src/utils.js
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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');
|
||||
const ts = require('typescript');
|
||||
|
||||
/**
|
||||
* Recursively query the api directory.
|
||||
* @param {String} dir dir api directory
|
||||
* @param {Array} apiFiles api files array
|
||||
*/
|
||||
function readDir(dir, apiFiles) {
|
||||
const files = fs.readFileSync(dir);
|
||||
files.forEach(element => {
|
||||
const filePath = path.join(dir, element);
|
||||
const status = fs.statSync(filePath);
|
||||
if (status.isDirectory()) {
|
||||
readDir(filePath, apiFiles);
|
||||
} else if (status.isFile() && /\.d\.ts$/.test(filePath)) {
|
||||
apiFiles.push(filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get api file array.
|
||||
* @param {String} url api file(.d.ts) | api config file(.txt) | api file directory
|
||||
* @returns api file array
|
||||
*/
|
||||
function getApiFiles(url) {
|
||||
const apiFiles = [];
|
||||
const urlStat = fs.statSync(url);
|
||||
if (urlStat.isFile() && path.extname(url) === ".txt") {
|
||||
const content = fs.readFileSync(url, "utf-8");
|
||||
const apiFilesConfig = content.split(/[(\r\n)\r\n]+/);
|
||||
apiFilesConfig.forEach(file => {
|
||||
if (fs.existsSync(file)) {
|
||||
apiFiles.push(file);
|
||||
} else {
|
||||
console.error(`FORMAT_ERROR: Api file of [${file}] is not found!`);
|
||||
}
|
||||
});
|
||||
} else if (urlStat.isFile() && /\.d\.ts$/.test(url)) {
|
||||
apiFiles.push(url);
|
||||
} else if (url.isDirectory()) {
|
||||
readDir(url, apiFiles);
|
||||
}
|
||||
return apiFiles;
|
||||
}
|
||||
exports.getApiFiles = getApiFiles;
|
||||
|
||||
/**
|
||||
* Tsc compile engine.
|
||||
* @param {Array} apiFiles api file array
|
||||
* @param {Function} callback pretreatment function
|
||||
*/
|
||||
function tsTransform(apiFiles, callback) {
|
||||
apiFiles.forEach(url => {
|
||||
const content = fs.readFileSync(url, "utf-8");
|
||||
const fileName = path.basename(url).replace(/\.d\.ts$/, "ts");
|
||||
ts.transpileModule(content, {
|
||||
compilerOptions: {
|
||||
"target": ts.ScriptTarget.ES2017
|
||||
},
|
||||
fileName: fileName,
|
||||
transformers: { before: [callback(url)] }
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.tsTransform = tsTransform;
|
||||
|
||||
/**
|
||||
* Create generated api file.
|
||||
* @param {String} url generated api file path
|
||||
* @param {String} content content generated api file content
|
||||
* @param {Object} option option of writeFile(fs)
|
||||
*/
|
||||
function overwriteApiFile(url, content, option) {
|
||||
fs.writeFileSync(url, content, option);
|
||||
}
|
||||
exports.overwriteApiFile = overwriteApiFile;
|
||||
|
||||
/**
|
||||
* Get api note.
|
||||
* @param {tsNode} node current node
|
||||
* @returns api note
|
||||
*/
|
||||
function getAPINote(node) {
|
||||
const apiLength = node.getText().length;
|
||||
const apiFullLength = node.getFullText().length
|
||||
return node.getFullText().substring(0, apiFullLength - apiLength);
|
||||
}
|
||||
exports.getAPINote = getAPINote;
|
||||
|
||||
/**
|
||||
* Judge whether the api note exists.
|
||||
* @param {tsNode} node current node
|
||||
* @returns boolean
|
||||
*/
|
||||
function hasAPINote(node) {
|
||||
const apiNote = getAPINote(node).replace(/[\s]/g, "");
|
||||
if (apiNote && apiNote.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.hasAPINote = hasAPINote;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {tsNode} node current node
|
||||
* @returns boolean
|
||||
*/
|
||||
function hasCopyright(content) {
|
||||
return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(content);
|
||||
}
|
||||
exports.hasCopyright = hasCopyright;
|
||||
|
||||
/**
|
||||
* Get api information.
|
||||
* @param {tsNode} node current node
|
||||
* @returns api information
|
||||
*/
|
||||
function getApiInfo(node) {
|
||||
const notesStr = getAPINote(node)
|
||||
let apiInfo = {};
|
||||
if (notesStr !== "") {
|
||||
if (/\@systemapi/g.test(notesStr)) {
|
||||
apiInfo.systemapi = true;
|
||||
}
|
||||
if (/\@since\s*(\d+)/g.test(notesStr)) {
|
||||
notesStr.replace(/\@since\s*(\d+)/g, (versionInfo) => {
|
||||
apiInfo.since = versionInfo.replace(/\@since/g, '').trim();
|
||||
})
|
||||
}
|
||||
if (/\@deprecated.*since\s*(\d+)/g.test(notesStr)) {
|
||||
notesStr.replace(/\@deprecated.*since\s*(\d+)/g,
|
||||
versionInfo => {
|
||||
apiInfo.deprecated = versionInfo.replace(
|
||||
/\@deprecated.*since\s*/g, '').trim();
|
||||
})
|
||||
}
|
||||
if (/\@famodelonly/g.test(notesStr)) {
|
||||
apiInfo.model = "famodelonly";
|
||||
} else if (/\@stagemodelonly/g.test(notesStr)) {
|
||||
apiInfo.model = "stagemodelonly";
|
||||
}
|
||||
if (/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) {
|
||||
notesStr.replace(/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g, sysCapInfo => {
|
||||
apiInfo.syscap = sysCapInfo.replace(/\@syscap/g, '').trim();
|
||||
})
|
||||
}
|
||||
if (/\@permission\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) {
|
||||
notesStr.replace(/\@permission\s*((\w|\.|\/|\{|\@|\}|\s)+)/g,
|
||||
permissionInfo => {
|
||||
apiInfo.permission =
|
||||
permissionInfo.replace(/\@permission/g, '').trim();
|
||||
})
|
||||
}
|
||||
}
|
||||
return apiInfo;
|
||||
}
|
||||
exports.getApiInfo = getApiInfo;
|
||||
|
||||
function getParentApiInfo(node, apiInfo) {
|
||||
if (!apiInfo.since) {
|
||||
if (getApiInfo(node).since) {
|
||||
apiInfo.since = [getApiInfo(node).since];
|
||||
} else if (node.parent) {
|
||||
apiInfo.since = [getParentApiInfo(node.parent, apiInfo).since];
|
||||
} else {
|
||||
apiInfo.since = ["NA"];
|
||||
}
|
||||
}
|
||||
if (!apiInfo.model) {
|
||||
if (getApiInfo(node).model) {
|
||||
apiInfo.model = [getApiInfo(node).model];
|
||||
} else if (node.parent) {
|
||||
apiInfo.model = [getParentApiInfo(node.parent, apiInfo).model];
|
||||
}
|
||||
}
|
||||
if (!apiInfo.systemapi) {
|
||||
if (getApiInfo(node).systemapi) {
|
||||
apiInfo.model = [getApiInfo(node).systemapi];
|
||||
} else if (node.parent) {
|
||||
apiInfo.model = [getParentApiInfo(node.parent, apiInfo).systemapi];
|
||||
} else {
|
||||
apiInfo.systemapi = [false];
|
||||
}
|
||||
}
|
||||
if (!apiInfo.syscap) {
|
||||
if (getApiInfo(node).syscap) {
|
||||
apiInfo.model = [getApiInfo(node).syscap];
|
||||
} else if (node.parent) {
|
||||
apiInfo.model = [getParentApiInfo(node.parent, apiInfo).syscap];
|
||||
} else {
|
||||
apiInfo.systemapi = ["NA"];
|
||||
}
|
||||
}
|
||||
return apiInfo;
|
||||
}
|
||||
exports.getParentApiInfo = getParentApiInfo;
|
||||
|
||||
const JSDOC_WRITELIST_SET = new Set(["constant", "crossplatform", "default", "deprecated", "enum", "example", "extends",
|
||||
"famodeonly", "fires", "interface", "namespace", "param", "permission", "readonly", "returns", "since", "stagemodeonly",
|
||||
"static", "syscap", "systemapi", "type", "typedef", "throws", "test", "useinstead"]);
|
||||
exports.JSDOC_WRITELIST_SET = JSDOC_WRITELIST_SET;
|
||||
|
||||
const TS_KEYWORD_SET = new Set([ts.SyntaxKind.DeclareKeyword]);
|
||||
exports.TS_KEYWORD_SET = TS_KEYWORD_SET;
|
Loading…
x
Reference in New Issue
Block a user