diff --git a/build-tools/intToNumber.js b/build-tools/intToNumber.js new file mode 100644 index 0000000000000000000000000000000000000000..6de2066658b4157bbb05d676fb1977bcf54a7a25 --- /dev/null +++ b/build-tools/intToNumber.js @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2025 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 ts = require('typescript'); +const fs = require('fs'); +const path = require('path'); +const commander = require('commander'); + +const typeArray = ['float', 'double', 'int']; + +let inputDir = ''; +let outputDir = ''; +let jsDocContent = ''; +let tagDataList = []; + +function main() { + const program = new commander.Command(); + program + .name('intToNumber') + .version('0.0.1'); + program + .option('--path ', 'input path') + .option('--output ', 'output path') + .action((opts) => { + if (!opts.path || !opts.output) { + console.error('Error: Must provide --path and --output parameters'); + process.exit(1); + } + + // 将相对路径解析为绝对路径 + inputDir = path.resolve(opts.path); + outputDir = path.resolve(opts.output); + + // 检查输入目录是否存在 + if (!fs.existsSync(inputDir)) { + console.error(`Error: Input directory does not exist: ${inputDir}`); + process.exit(1); + } + + const apiUtFiles1 = []; + readFile(inputDir, apiUtFiles1); + tsTransform(apiUtFiles1, recursionAstCallback); + const apiUtFiles2 = []; + readFile(outputDir, apiUtFiles2); + tsTransform(apiUtFiles2, recursionAstCallback2); + + const jsDocUtFiles1 = []; + readFile(outputDir, jsDocUtFiles1); + tsTransform(jsDocUtFiles1, parseJSDocCallback); + const jsDocUtFiles2 = []; + readFile(outputDir, jsDocUtFiles2); + tsTransform(jsDocUtFiles2, parseJSDocCallback2); + }); + program.parse(process.argv); +} + +/** + * 读取目录下所有文件 + * @param {string} dir 文件目录 + * @param {Array} utFiles 所有文件 + */ +function readFile(dir, utFiles) { + try { + const files = fs.readdirSync(dir); + files.forEach((element) => { + const filePath = path.join(dir, element); + const status = fs.statSync(filePath); + if (status.isDirectory()) { + readFile(filePath, utFiles); + } else { + utFiles.push(filePath); + } + }); + } catch (e) { + console.error('Error reading files: ' + e.message); + } +} + + +/** + * 遍历所有文件进行处理 + * @param {Array} utFiles 所有文件 + * @param {recursionAstCallback} callback 回调函数 + */ +function tsTransform(utFiles, callback) { + utFiles.forEach((url) => { + let content = fs.readFileSync(url, 'utf-8'); + const apiBaseName = path.basename(url); + tagDataList = []; + if (/\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName)) { + const fileName = processFileName(url); + ts.transpileModule(content, { + compilerOptions: { + target: ts.ScriptTarget.ES2017, + }, + fileName: fileName, + transformers: { before: [callback(url, content)] } + }) + } else { + const outputPath = replaceInputDirWithOutputDir(url, inputDir, outputDir); + ensureDirectoryExists(outputPath); + fs.writeFileSync(outputPath, content); + } + }); +} + +/** + * 统一处理文件名称,修改后缀等 + * @param {string} filePath 文件路径 + * @returns {string} filename 文件名称 + */ +function processFileName(filePath) { + return path + .basename(filePath) + .replace(/\.d\.ts$/g, '.ts') + .replace(/\.d\.ets$/g, '.ets'); +} + +/** + * 每个文件处理签回调函数第一个 + * @callback recursionAstCallback + * @param {string} url 文件路径 + * @returns {Function} + */ +function recursionAstCallback(url) { + return (context) => { + return (sourceFile) => { + node = processVisitEachChild1(context, sourceFile) + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }) + const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile) + const outputPath = replaceInputDirWithOutputDir(url, inputDir, outputDir); + ensureDirectoryExists(outputPath); + fs.writeFileSync(outputPath, result); + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + }; +} + +/** + * 每个文件处理签回调函数第一个 + * @callback recursionAstCallback + * @param {string} url 文件路径 + * @returns {Function} + */ +function recursionAstCallback2(url) { + return (context) => { + return (sourceFile) => { + node = processVisitEachChild2(context, sourceFile) + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }) + const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile) + const outputPath = replaceInputDirWithOutputDir(url, inputDir, outputDir); + ensureDirectoryExists(outputPath); + fs.writeFileSync(outputPath, result); + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + }; +} + +/** + * 每个文件处理签回调函数第一个 + * @callback parseJSDocCallback + * @param {string} url 文件路径 + * @returns {Function} + */ +function parseJSDocCallback(url, content) { + return (context) => { + return (sourceFile) => { + node = parseJSDocVisitEachChild1(context, sourceFile, content) + const outputPath = replaceInputDirWithOutputDir(url, inputDir, outputDir); + ensureDirectoryExists(outputPath); + fs.writeFileSync(outputPath, jsDocContent); + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + }; +} + +/** + * 每个文件处理签回调函数第一个 + * @callback parseJSDocCallback + * @param {string} url 文件路径 + * @returns {Function} + */ +function parseJSDocCallback2(url, content) { + return (context) => { + return (sourceFile) => { + const contents = fs.readFileSync(url, 'utf-8'); + node = parseJSDocVisitEachChild2(context, sourceFile, contents); + const outputPath = replaceInputDirWithOutputDir(url, inputDir, outputDir); + ensureDirectoryExists(outputPath); + fs.writeFileSync(outputPath, jsDocContent); + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + }; +} + +/** + * 遍历处理tsnode节点 + * @param {context} 解下过后的内容 + * @param {node} 解下过后的节点 + * @returns ts.node + */ +function parseJSDocVisitEachChild1(context, node, content) { + return ts.visitEachChild(node, processAllNodes, context) + function processAllNodes(node) { + if (node.jsDoc) { + node.jsDoc.forEach(doc => { + if (!doc.tags) { + return; + } + doc.tags.forEach(tag => { + if (!tag.typeExpression) { + return; + } + const typeExpr = tag.typeExpression; + const newTypeExpr = parseTypeExpression(typeExpr); + applJSDocTransformations(typeExpr.type, newTypeExpr, content, tagDataList); + }); + }); + } + return ts.visitEachChild(node, processAllNodes, context); + } + function parseTypeExpression(typeExpr) { + let newTypeExpr = typeExpr; + if (typeExpr.type.kind === ts.SyntaxKind.JSDocNullableType) { + if (typeExpr.type.type.kind && typeExpr.type.type.kind === ts.SyntaxKind.ParenthesizedType) { + newTypeExpr = parseTypeExpression(typeExpr.type.type.type); + } else { + newTypeExpr = parseTypeExpression(typeExpr.type.type); + } + } else { + newTypeExpr = parseTypeExpression(typeExpr.type); + } + return newTypeExpr; + } + function parseTypeExpression(node) { + if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && typeArray.includes(node.typeName.getText())) { + node = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + } + return ts.visitEachChild(node, parseTypeExpression, context); + } +} + +/** + * + * @param {typeExpr} 原始typeExpr + * @param {newTypeExpr} 新typeExpr + * @param {content} 文本内容 + */ +function applJSDocTransformations(typeExpr, newTypeExpr, content, tagDataList) { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const finalContent = printer.printNode(ts.EmitHint.Unspecified, newTypeExpr); + if (finalContent.includes('number')) { + const data = { + pos: typeExpr.pos, + end: typeExpr.end, + convertedText: finalContent + }; + tagDataList.push(data); + changeContent(content, tagDataList); + } +} + +/** + * + * @param {typeExpr} 原始typeExpr + * @param {newTypeExpr} 新typeExpr + * @param {content} 文本内容 + */ +function changeContent(content, tagDataList) { + tagDataList.sort((a, b) => b.pos - a.pos); + let result = content + for (const data of tagDataList) { + const before = result.substring(0, data.pos); + const after = result.substring(data.end); + result = before + ` ${data.convertedText}` + after + } + jsDocContent = result; +} + +/** + * 遍历处理tsnode节点 + * @param {context} 解下过后的内容 + * @param {node} 解下过后的节点 + * @returns ts.node + */ +function parseJSDocVisitEachChild2(context, node, content) { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + return ts.visitEachChild(node, processAllNodes, context) + function processAllNodes(node) { + if (node.jsDoc) { + node.jsDoc.forEach(doc => { + if (!doc.tags) { + return + } + doc.tags.forEach(tag => { + if (!tag.typeExpression) { + return; + } + const typeExpr = tag.typeExpression; + const newTypeExpr = parseTypeExpression(typeExpr.type) + const finalContent = printer.printNode(ts.EmitHint.Unspecified, newTypeExpr); + const before = content.substring(0, typeExpr.type.pos); + const after = content.substring(typeExpr.type.end); + jsDocContent = before + ` ${finalContent}` + after; + }) + }) + } + return ts.visitEachChild(node, processAllNodes, context); + } + function parseTypeExpression(node) { + if (ts.isUnionTypeNode(node)) { + const types = node.types; + const newTypes1 = []; + let newTypes2 = []; + types.forEach(type => { + if (ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName) && typeArray.includes(type.typeName.getText())) { + newTypes1.push(ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)); + } else { + newTypes1.push(type); + } + }) + + newTypes1.forEach(type => { + newTypes2 = duplicateRemoval(type); + }); + node = ts.factory.updateUnionTypeNode(node, newTypes2); + } + return ts.visitEachChild(node, parseTypeExpression, context); + } + function duplicateRemoval(type) { + let newTypes2 = []; + let hasNumberKeyWorld = false; + if (type.kind === ts.SyntaxKind.NumberKeyword) { + if (!hasNumberKeyWorld) { + newTypes2.push(type); + hasNumberKeyWorld = true; + } + } else { + newTypes2.push(type); + } + return newTypes2; + } +} + +/** + * 遍历处理tsnode节点 + * @param {context} 解下过后的内容 + * @param {node} 解下过后的节点 + * @returns ts.node + */ +function processVisitEachChild1(context, node) { + return ts.visitEachChild(node, processAllNodes, context) + function processAllNodes(node) { + if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && typeArray.includes(node.typeName.getText())) { + node = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + } + if (ts.isStructDeclaration(node)) { + node = processStructDeclaration(node); + } + return ts.visitEachChild(node, processAllNodes, context); + } +} + +/** + * 处理struct子节点 + */ +function processStructDeclaration(node) { + const newMembers = []; + node.members.forEach((member, index) => { + if (index >= 1) { + newMembers.push(member); + } + }); + node = ts.factory.updateStructDeclaration( + node, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + newMembers + ); + return node; +} + + +/** + * 遍历处理tsnode节点去重 + * @param {context} 解下过后的内容 + * @param {node} 解下过后的节点 + * @returns ts.node + */ +function processVisitEachChild2(context, node) { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + return ts.visitEachChild(node, processAllNodes, context) + function processAllNodes(node) { + if (ts.isUnionTypeNode(node)) { + const types = node.types; + const newTypes = []; + const newTypesText = new Set([]); + types.forEach(type => { + const text = printer.printNode(ts.EmitHint.Unspecified, type) + if (!newTypesText.has(text)) { + newTypes.push(type); + newTypesText.add(text); + } + }); + node = ts.factory.updateUnionTypeNode(node, newTypes); + } + if (ts.isStructDeclaration(node)) { + node = processStructDeclaration(node); + } + return ts.visitEachChild(node, processAllNodes, context); + } +} + +/** + * 将输入路径替换为输出路径 + * @param {string} inputFilePath 输入文件的绝对路径 + * @param {string} inputDir 输入目录的绝对路径 + * @param {string} outputDir 输出目录的绝对路径 + * @returns {string} 输出文件的绝对路径 + */ +function replaceInputDirWithOutputDir(inputFilePath, inputDir, outputDir) { + return inputFilePath.replace(inputDir, outputDir); +} + +/** + * 确保目录结构存在,如果不存在则创建 + * @param {string} filePath 文件路径 + */ +function ensureDirectoryExists(filePath) { + try { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } catch (error) { + console.error(`Error creating directory: ${error.message}`); + } +} + +main(); \ No newline at end of file