From 70e1626a00eca804aff74d155792505888825284 Mon Sep 17 00:00:00 2001 From: yulong-nj Date: Mon, 14 Jul 2025 20:24:06 +0800 Subject: [PATCH 1/3] interop sdk Signed-off-by: yulong-nj --- BUILD.gn | 19 + .../component/ets/alphabet_indexer.d.ts | 4 + api/arkui/AlphabetIndexerModifier.d.ts | 1 + build-tools/process_interop.js | 833 ++++++++++++++++++ process_interop.py | 56 ++ 5 files changed, 913 insertions(+) create mode 100644 build-tools/process_interop.js create mode 100644 process_interop.py diff --git a/BUILD.gn b/BUILD.gn index a8e68e0168..8664543db2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -405,4 +405,23 @@ ohos_copy("api_check_plugin") { outputs = [ target_out_dir + "/$target_name" ] module_source_dir = target_out_dir + "/$target_name" module_install_name = "" +} + +action("ohos_ets_process_interop") { + script = "process_interop.py" + + deps = [ + ":ohos_base_split" + ] + + args = [ + "--intermediates-output", + rebase_path("${interface_sdk_path}", root_build_dir), + "--source-root-dir", + rebase_path("//", root_build_dir), + "--node-js", + rebase_path(nodejs, root_build_dir) + ] + + outputs = ["${interface_sdk_path}/ets1.1_interop", "${interface_sdk_path}/ets1.2_interop"] } \ No newline at end of file diff --git a/api/@internal/component/ets/alphabet_indexer.d.ts b/api/@internal/component/ets/alphabet_indexer.d.ts index 1f69d01830..ffaf7997a0 100644 --- a/api/@internal/component/ets/alphabet_indexer.d.ts +++ b/api/@internal/component/ets/alphabet_indexer.d.ts @@ -232,6 +232,7 @@ interface AlphabetIndexerOptions { * @crossplatform * @atomicservice * @since 11 + * @noninterop */ interface AlphabetIndexerInterface { /** @@ -339,6 +340,7 @@ declare type OnAlphabetIndexerRequestPopupDataCallback = (index: number) => Arra * @crossplatform * @atomicservice * @since 11 + * @noninterop */ declare class AlphabetIndexerAttribute extends CommonMethod { /** @@ -1054,6 +1056,7 @@ declare class AlphabetIndexerAttribute extends CommonMethod { diff --git a/build-tools/process_interop.js b/build-tools/process_interop.js new file mode 100644 index 0000000000..82ba7601ee --- /dev/null +++ b/build-tools/process_interop.js @@ -0,0 +1,833 @@ +/* + * 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('typescript'); +const commander = require('commander'); + +let sourceFile = null; +let componentEtsFiles = []; +let componentEtsDeleteFiles = []; +const kitFileNeedDeleteMap = new Map(); +const stmtReplacementMap = new Map(); + + +function start() { + const program = new commander.Command(); + program + .name('noninterop') + .version('0.0.1'); + program + .option('--input ', 'input path') + .option('--output ', 'output path') + .action((opts) => { + outputPath = opts.output; + inputDir = opts.input; + transformFiles(opts.input); + }); + program.parse(process.argv); +} + +function transformFiles(inputDir) { + // 入口 + try { + const utFiles = []; + readFile(inputDir, utFiles); // 读取文件 + tsTransform(utFiles, deleteSystemApi); + } catch (error) { + console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error); + } +} + +function getPureName(name) { + const pureName = path.basename(name).replace('.d.ts', '').replace('.d.ets', '').replace(/_/g, '').toLowerCase(); + return pureName; +} + + +/** + * 判断文件路径对应的文件是否存在 + * @param {string} importPath kit文件import + * @param {string} apiDir 引用接口所在目录 + * @returns {boolean} importPath是否存在 + */ +function hasFileByImportPath(importPath, apiDir) { + let fileDir = path.resolve(apiDir); + if (importPath.startsWith('@arkts')) { + fileDir = path.resolve(inputDir, '../arkts'); + } + return isExistArkUIFile(path.resolve(inputDir, 'arkui', 'component'), importPath) || + isExistImportFile(fileDir, importPath); +} + +function isExistArkUIFile(resolvedPath, importPath) { + const filePath = path.resolve(resolvedPath, importPath); + if ( + filePath.includes(path.resolve(inputDir, '@internal', 'component', 'ets')) || + filePath.includes(path.resolve(inputDir, 'arkui', 'component')) + ) { + const fileName = getPureName(filePath); + return componentEtsFiles.includes(fileName); + } + return isExistImportFile(resolvedPath, importPath); +} + +function isExistImportFile(fileDir, importPath) { + return ['.d.ts', '.d.ets'].some(ext => { + return fs.existsSync(path.resolve(fileDir, `${importPath}${ext}`)); + }); +} + +/** + * 统一处理文件名称,修改后缀等 + * @param {string} filePath 文件路径 + * @returns {string} filename 文件名称 + */ +function processFileName(filePath) { + return path + .basename(filePath) + .replace(/\.d\.ts$/g, '.ts') + .replace(/\.d\.ets$/g, '.ets'); +} + +function processFileNameWithoutExt(filePath) { + return path + .basename(filePath) + .replace(/\.d\.ts$/g, '') + .replace(/\.d\.ets$/g, '') + .replace(/\.ts$/g, '') + .replace(/\.ets$/g, ''); +} + +/** + * 对文件内容进行预处理,把下面的两行处理成符合1.1的语法: + * @Retention({policy: "SOURCE"}) + * export declare @interface State {}; + * + * 转成 + * /**@reserved @Retention({policy: "SOURCE"}) #/ + * export declare const State; + * @param {string} content + */ +function preprocessContent(content) { + stmtReplacementMap.clear(); + let result = content.replace(/^(\s*)(\@Retention\(\{[^\(\)\{\}]*\}\)$)/mg, '$1/**@reserved $2 */'); + const matches = result.match(/(^[^\*]*\s+\@interface\s+.*$)/mg); + if (matches) { + for (const match of matches) { + const transformedStmt = match.replace(/(?<=\s+)\@interface(\s+\w+)\s*\{\}/g, 'const$1'); + result = result.replace(match, transformedStmt); + stmtReplacementMap.set(match, transformedStmt); + } + } + return result; +} + +/** + * 遍历所有文件进行处理 + * @param {Array} utFiles 所有文件 + * @param {deleteSystemApi} callback 回调函数 + */ +function tsTransform(utFiles, callback) { + utFiles.forEach((url) => { + const apiBaseName = path.basename(url); + let content = fs.readFileSync(url, 'utf-8'); // 文件内容 + let isTransformer = /\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName); + if (/\.json/.test(url)) { + isTransformer = false; + } + if (!isTransformer) { + writeFile(url, content); + return; + } + // dts文件处理 + const fileName = processFileName(url); + ts.transpileModule(preprocessContent(content), { + compilerOptions: { + target: ts.ScriptTarget.ES2017, + }, + fileName: fileName, + transformers: { before: [callback(url)] }, + }); + }); +} + +/** + * 读取目录下所有文件 + * @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.log('ETS ERROR: ' + e); + } +} + +function writeFile(url, data, option) { + const newFilePath = path.resolve(outputPath, path.relative(inputDir, url)); + fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => { + if (err) { + console.log(`ERROR FOR CREATE PATH ${err}`); + } else { + if (data === '') { + fs.rmSync(newFilePath); + return; + } + fs.writeFileSync(newFilePath, data, option, (err) => { + if (err) { + console.log(`ERROR FOR CREATE FILE ${err}`); + } + }); + } + }); +} + +const globalModules = new Map(); + +function postProcessContent(content) { + for(const [originalStmt, transformedStmt] of stmtReplacementMap){ + content = content.replace(transformedStmt, originalStmt); + } + return content.replace(/^(\s*)\/\*\*\@reserved (.*) \*\/$/mg, '$1$2'); +} + +/** + * 每个文件处理前回调函数第二个 + * @param {string} url 文件路径 + * @returns {Function} + */ +function formatImportDeclaration(url, copyrightMessage = '', isCopyrightDeleted = false) { + return (context) => { + const allIdentifierSet = new Set([]); + return (node) => { + sourceFile = node; + collectAllIdentifier(node); // 获取所有标识符 + formatValue = formatAllNodes(url, node, allIdentifierSet); // 获取所有节点 + node = formatValue.node; + const referencesMessage = formatValue.referencesMessage; + if (formatValue.isCopyrightDeleted) { + copyrightMessage = formatValue.copyrightMessage; + isCopyrightDeleted = formatValue.isCopyrightDeleted; + } + if (!isEmptyFile(node)) { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); + if (isCopyrightDeleted) { + // 当第一个节点被删除时会同步删除整个文件jsdoc + result = copyrightMessage + '\n' + result; + } + copyrightMessage = node.getFullText().replace(node.getText(), ''); + if (referencesMessage) { + // 将references写入文件 + result = + result.substring(0, copyrightMessage.length) + + '\n' + + referencesMessage + + result.substring(copyrightMessage.length); + } + result = removeNonInteropDoc(result); + writeFile(url, postProcessContent(result)); + } + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + function collectAllIdentifier(node) { + if (ts.isSourceFile(node) && node.statements) { + node.statements.forEach((stat) => { + if (!ts.isImportDeclaration(stat)) { + ts.visitEachChild(stat, collectAllNodes, context); + } + }); + } + } + function collectAllNodes(node) { + if (ts.isIdentifier(node)) { + allIdentifierSet.add(node.escapedText.toString()); + } + return ts.visitEachChild(node, collectAllNodes, context); + } + }; +} + +function formatAllNodes(url, node, allIdentifierSet, copyrightMessage = '', isCopyrightDeleted = false) { + let referencesMessage = ''; + // let currReferencesModule = formatAllNodesReferences(url); + let currReferencesModule = []; + if (ts.isSourceFile(node) && node.statements) { + const newStatements = []; + node.statements.forEach((statement) => { + if (ts.isImportDeclaration(statement)) { + const importInfo = formatAllNodesImportDeclaration( + node, + statement, + url, + currReferencesModule, + allIdentifierSet + ); + if (importInfo.statement) { + newStatements.push(statement); + } else if (importInfo.isCopyrightDeleted) { + copyrightMessage = importInfo.copyrightMessage; + isCopyrightDeleted = importInfo.isCopyrightDeleted; + } + } else if (ts.isStructDeclaration(statement)) { + statement = ts.factory.updateStructDeclaration( + statement, + statement.modifiers, + statement.name, + statement.typeParameters, + statement.heritageClauses, + statement.members.slice(1) + ); + newStatements.push(statement); + } else { + newStatements.push(statement); + } + }); + currReferencesModule.forEach((item) => { + if (item.isUsed) { + referencesMessage += item.reference + '\n'; + } + }); + node = ts.factory.updateSourceFile(node, newStatements); + } + return { node, referencesMessage, copyrightMessage, isCopyrightDeleted }; +} + +function hasCopyright(node) { + return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), '')); +} + + +/** + * 处理Import节点 去除未使用、不存在、References中没有对应模块的导入 + * @param {ts.node} node 当前节点 + * @param {ts.ImportDeclaration} statement 导入节点 + * @param {string} url 文件路径 + * @param {string} url 文件路径 + * @param {Set} allIdentifierSet 该文件的所有Identifier关键字 + * @returns {{statement:ts.ImportDeclaration,copyrightMessage:string,isCopyrightDeleted:boolean}} statement 处理完成的导入节点、copyrightMessage + */ +function formatAllNodesImportDeclaration(node, statement, url, currReferencesModule, allIdentifierSet) { + // 是import节点 import { AsyncCallback } from './@ohos.base'; + const clauseSet = new Set([]); + if (statement.importClause && ts.isImportClause(statement.importClause)) { + // 标识符 + const clauseNode = statement.importClause; + if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) { + // 没有大括号的标识符 + clauseSet.add(clauseNode.name.escapedText.toString()); + } else if ( + clauseNode.namedBindings && + clauseNode.namedBindings.name && + ts.isIdentifier(clauseNode.namedBindings.name) + ) { + // 没有标识符 *号 + clauseSet.add(clauseNode.namedBindings.name.escapedText.toString()); + } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) { + // 有花括号的标识符 + clauseNode.namedBindings.elements.forEach((ele) => { + if (ele.name && ts.isIdentifier(ele.name)) { + clauseSet.add(ele.name.escapedText.toString()); + } + }); + } + } + const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, ''); + const fileDir = path.dirname(url); + let hasImportSpecifierFile = hasFileByImportPath(importSpecifier, fileDir); + let hasImportSpecifierInModules = globalModules.has(importSpecifier); + if ((hasImportSpecifierFile || hasImportSpecifierInModules) && clauseSet.size > 0) { + let currModule = []; + if (hasImportSpecifierInModules) { + let index = globalModules.get(importSpecifier); + currModule = currReferencesModule[index].modules[importSpecifier]; + } + const clasueCheckList = []; + let exsitClauseSet = new Set([]); + for (const clause of clauseSet) { + let flag = allIdentifierSet.has(clause); + if (hasImportSpecifierInModules) { + flag = allIdentifierSet.has(clause) && currModule.includes(clause); + } + if (flag) { + // 标识符使用到了当前import中的引用 + exsitClauseSet.add(clause); + clasueCheckList.push('exist'); + } else { + clasueCheckList.push('non-exist'); + } + } + let hasExsitStatus = false; + let hasNonExsitStatus = false; + clasueCheckList.forEach((ele) => { + if (ele === 'exist') { + hasExsitStatus = true; + } else { + hasNonExsitStatus = true; + } + }); + if (hasExsitStatus) { + // 有使用到的标识符 + if (hasNonExsitStatus) { + // 有没有使用到的标识符 + const newSpecifiers = []; + statement.importClause.namedBindings.elements.forEach((element) => { + if (exsitClauseSet.has(element.name.escapedText.toString())) { + newSpecifiers.push(element); + } + }); + statement.importClause.namedBindings = ts.factory.updateNamedImports( + statement.importClause.namedBindings, + newSpecifiers + ); + } + if (hasImportSpecifierInModules) { + let index = globalModules.get(importSpecifier); + currReferencesModule[index].isUsed = true; + } + return { statement }; + } else if (hasCopyright(statement)) { + return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true }; + } + } else if (hasCopyright(statement)) { + return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true }; + } + return { statement: undefined, copyrightMessage: '', isCopyrightDeleted: false }; +} + +/** + * + * 防止@file和@kit段注释丢失 + * @param {string} fileFullText + * @returns {string} + * + */ +function getFileAndKitComment(fileFullText) { + let fileAndKitComment = ''; + let pattern = /\/\*\*\s*\*\s*@file[\s\S]*?@kit[\s\S]*?\*\//; + let comment = fileFullText.match(pattern); + if (comment) { + fileAndKitComment = comment[0]; + } + return fileAndKitComment; +} + +/** + * 处理最终结果中的noninterop + * @param {string} result + */ +function removeNonInteropDoc(result) { + result.split; + return result.replace(/\/\*\*[\s\S]*?\*\//g, (substring, p1) => { + return /@noninterop/g.test(substring) ? '' : substring; + }); +} + +/** + * 每个文件处理前回调函数第一个 + * @callback deleteSystemApi + * @param {string} url 文件路径 + * @returns {Function} + */ +function deleteSystemApi(url) { + return (context) => { + return (node) => { + const fullText = String(node.getFullText()); + //获取文件头部的注释信息--这里可能会涉及到@file和@kit段注释丢失 + let fileAndKitComment = getFileAndKitComment(fullText); + const copyrightMessage = fullText.replace(node.getText(), '').split(/\/\*\*/)[0] + fileAndKitComment + '\n'; + let kitName = ''; + if (fullText.match(/\@kit (.*)\r?\n/g)) { + kitName = RegExp.$1.replace(/\s/g, ''); + } + sourceFile = node; + const deleteNode = processSourceFile(node, kitName, url); // 处理最外层节点 + node = processVisitEachChild(context, deleteNode.node); + if (!isEmptyFile(node)) { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); + const fileName = processFileName(url); + ts.transpileModule(result, { + compilerOptions: { + target: ts.ScriptTarget.ES2017, + }, + fileName: fileName, + transformers: { before: [formatImportDeclaration(url, copyrightMessage, deleteNode.isCopyrightDeleted)] }, + }); + } + return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); + }; + }; +} + +exports.deleteSystemApi = deleteSystemApi; + +/** + * 遍历每个文件下的所有节点,然后删除节点 + * @param node + * @returns + */ + +/** + * 处理最外层的节点看是否删除 + * @param node 解析过后的节点 + * @param kitName 当前文件kitName + * @returns + */ +function processSourceFile(node, kitName, url) { + let isCopyrightDeleted = false; + const newStatements = []; + const newStatementsWithoutExport = []; + const deleteSystemApiSet = new Set(); + let needDeleteExport = { + fileName: '', + default: '', + exportName: new Set(), + }; + isCopyrightDeleted = addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport); + newStatements.forEach((statement) => { + const names = getExportIdentifierName(statement); + if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && statement.moduleSpecifier.text.startsWith('./arkui/component/')) { + const importPath = statement.moduleSpecifier.text.replace('./arkui/component/', ''); + const isDeleteSystemFile = componentEtsDeleteFiles.includes(getPureName(importPath)); + const hasEtsFile = componentEtsFiles.includes(getPureName(importPath)); + const existFile = isExistImportFile(path.dirname(url), statement.moduleSpecifier.text.toString()); + if (isDeleteSystemFile || !hasEtsFile && !existFile) { + return; + } + } + if (names.length === 0) { + newStatementsWithoutExport.push(statement); + return; + } + if (names.length === 1 && !deleteSystemApiSet.has(names[0])) { + //exports.name = test; + //export default test1 + //export {test1} + newStatementsWithoutExport.push(statement); + return; + } + processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport); + }); + if (needDeleteExport.fileName !== '') { + kitFileNeedDeleteMap.set(needDeleteExport.fileName, needDeleteExport); + } + return { + node: ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile, node.referencedFiles), + isCopyrightDeleted, + }; +} + +function processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport) { + //删除export节点信息 + if (ts.isExportAssignment(statement)) { + //export default abilityAccessCtrl; + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + needDeleteExport.default = statement.expression.escapedText.toString(); + } else if (ts.isExportDeclaration(statement)) { + //export {test1 as test,testa as test2} + let needExport = false; + const newSpecifiers = []; + names.forEach((name, index) => { + const exportSpecifier = statement.exportClause.elements[index]; + if (!deleteSystemApiSet.has(name)) { + //未被删除的节点 + newSpecifiers.push(exportSpecifier); + needExport = true; + } else { + //被删除的节点 + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + needDeleteExport.exportName.add(exportSpecifier.name.escapedText.toString()); + } + }); + if (needExport) { + statement.exportClause = ts.factory.updateNamedExports(statement.exportClause, newSpecifiers); + newStatementsWithoutExport.push(statement); + } + } +} + +function addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport) { + let isCopyrightDeleted = false; + node.statements.forEach((statement, index) => { + if (!isNonInterop(statement)) { + newStatements.push(statement); + return; + } + if (index === 0) { + isCopyrightDeleted = true; + } + if (ts.isVariableStatement(statement)) { + deleteSystemApiSet.add(variableStatementGetEscapedText(statement)); + } else if ( + ts.isModuleDeclaration(statement) || + ts.isInterfaceDeclaration(statement) || + ts.isClassDeclaration(statement) || + ts.isEnumDeclaration(statement) || + ts.isStructDeclaration(statement) || + ts.isTypeAliasDeclaration(statement) + ) { + if (statement && statement.name && statement.name.escapedText) { + deleteSystemApiSet.add(statement.name.escapedText.toString()); + } + setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet); + } else if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) { + setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet); + } + }); + + return isCopyrightDeleted; +} + +function setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet) { + if (ts.isExportAssignment(statement) && deleteSystemApiSet.has(statement.expression.escapedText.toString())) { + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + needDeleteExport.default = statement.expression.escapedText.toString(); + } else if (ts.isExportDeclaration(statement)) { + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + statement.exportClause.elements.forEach((element) => { + const exportName = element.propertyName ? + element.propertyName.escapedText.toString() : + element.name.escapedText.toString(); + if (deleteSystemApiSet.has(exportName)) { + needDeleteExport.exportName.add(element.name.escapedText.toString()); + } + }); + } + //export namespace test {} + const modifiers = statement.modifiers; + if (modifiers === undefined) { + return; + } + const exportFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword); + const defaultFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword); + if (exportFlag && defaultFlag) { + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + needDeleteExport.default = statement.name.escapedText.toString(); + } else if (exportFlag) { + needDeleteExport.fileName = processFileNameWithoutExt(node.fileName); + needDeleteExport.exportName.add(statement.name.escapedText.toString()); + } +} + +/** + * 获取export节点的名字,只获取第一个关键词 + * @param {ts.node} statement + * @returns {Array} + */ +function getExportIdentifierName(statement) { + const names = []; + if (ts.isExpressionStatement(statement)) { + //exports.name = test; + if (ts.isBinaryExpression(statement.expression)) { + names.push(statement.expression.right.escapedText.toString()); + } + } else if (ts.isExportAssignment(statement)) { + //export default test1 + names.push(statement.expression.escapedText.toString()); + } else if (ts.isExportDeclaration(statement)) { + //export {test1} 、export {test1 as test} 、export * from './featureability' + const exportClause = statement.exportClause; + if (exportClause) { + const specifiers = exportClause.elements; + specifiers.forEach((specifier) => { + if (ts.isExportSpecifier(specifier)) { + const name = specifier.propertyName ? specifier.propertyName : specifier.name; + names.push(name.escapedText.toString()); + } + }); + } + } + return names; +} + +/** + * 遍历处理tsnode节点 + * @param context 解析过后的内容 + * @param node 解析过后的节点 + * @returns ts.node + */ +function processVisitEachChild(context, node) { + return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点 + function processAllNodes(node) { + if (ts.isInterfaceDeclaration(node)) { + node = processInterfaceDeclaration(node); + } else if (ts.isClassDeclaration(node)) { + node = processClassDeclaration(node); + } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) { + const newStatements = []; + node.body.statements.forEach((statement) => { + if (!isNonInterop(statement)) { + newStatements.push(statement); + } + }); + const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements); + node = ts.factory.updateModuleDeclaration( + node, + node.modifiers, + node.name, + newModuleBody + ); + } else if (ts.isEnumDeclaration(node)) { + node = processEnumDeclaration(node); + } else if (ts.isStructDeclaration(node)) { + node = processStructDeclaration(node); + } + return ts.visitEachChild(node, processAllNodes, context); + } +} + +/** + * 处理interface子节点 + */ +function processInterfaceDeclaration(node) { + const newMembers = []; + node.members.forEach((member) => { + if (!isNonInterop(member)) { + newMembers.push(member); + } + }); + node = ts.factory.updateInterfaceDeclaration( + node, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + newMembers + ); + return node; +} + +/** + * 处理class子节点 + */ +function processClassDeclaration(node) { + const newMembers = []; + node.members.forEach((member) => { + if (!isNonInterop(member)) { + newMembers.push(member); + } + }); + node = ts.factory.updateClassDeclaration( + node, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + newMembers + ); + return node; +} + +/** + * 处理enum子节点 + */ +function processEnumDeclaration(node) { + const newMembers = []; + node.members.forEach((member) => { + if (!isNonInterop(member)) { + newMembers.push(member); + } + }); + node = ts.factory.updateEnumDeclaration( + node, + node.modifiers, + node.name, + newMembers + ); + return node; +} + +/** + * 处理struct子节点 + */ +function processStructDeclaration(node) { + const newMembers = []; + node.members.forEach((member, index) => { + if (index >= 1 && !isNonInterop(member)) { + newMembers.push(member); + } + }); + node = ts.factory.updateStructDeclaration( + node, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + newMembers + ); + return node; +} + +function variableStatementGetEscapedText(statement) { + let name = ''; + if ( + statement && + statement.declarationList && + statement.declarationList.declarations && + statement.declarationList.declarations.length > 0 && + statement.declarationList.declarations[0].name && + statement.declarationList.declarations[0].name.escapedText + ) { + name = statement.declarationList.declarations[0].name.escapedText.toString(); + } + return name; +} + +function isNonInterop(node) { + const notesContent = node.getFullText().replace(node.getText(), '').replace(/[\s]/g, ''); + const notesArr = notesContent.split(/\/\*\*/); + const notesStr = notesArr[notesArr.length - 1]; + for (const note of notesArr) { + if (note.length !== 0 && /@noninterop/g.test(note)) { + return true; + } + } + return false; +} + +function isEmptyFile(node) { + let isEmpty = true; + if (ts.isSourceFile(node) && node.statements) { + for (let i = 0; i < node.statements.length; i++) { + const statement = node.statements[i]; + if (ts.isImportDeclaration(statement)) { + continue; + } + isEmpty = false; + break; + } + } + const fileName = getPureName(node.fileName.replace('.ts', '').replace('.ets', '')); + if (isEmpty && componentEtsFiles.includes(fileName)) { + componentEtsDeleteFiles.push(fileName); + } + return isEmpty; +} + +let outputPath = ''; +let inputDir = ''; +start(); diff --git a/process_interop.py b/process_interop.py new file mode 100644 index 0000000000..7d42dff1bc --- /dev/null +++ b/process_interop.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# 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. + +import argparse +import sys +import os +import shutil +import subprocess + +INTERFACE_PATH = "interface/sdk-js" +PROCESS_INTEROP = "interface/sdk-js/build-tools/process_interop.js" + +def process_interop(options, sub_input, sub_output): + nodejs = os.path.abspath(options.node_js) + tool = os.path.abspath(os.path.join(options.source_root_dir, PROCESS_INTEROP)) + cwd_dir = os.path.abspath(os.path.join( + options.source_root_dir, INTERFACE_PATH)) + intermediates_output = os.path.abspath(options.intermediates_output) + + input_dir = intermediates_output + sub_input + output_dir = intermediates_output + sub_output + os.makedirs(output_dir, exist_ok=True) + process = subprocess.run([nodejs, tool, "--input", input_dir, + "--output", output_dir], shell=False, + cwd=os.path.abspath(os.path.join( + options.source_root_dir, cwd_dir)), + stdout=subprocess.PIPE) + return process + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--intermediates-output', required=True) + parser.add_argument('--source-root-dir', required=True) + parser.add_argument('--node-js', required=True) + + options = parser.parse_args() + process_interop(options, "/ets1.1interop/api/arkui", "/ets1.1interop/api/arkui") + process_interop(options, "/ets1.2interop/declaration/api/arkui", "/ets1.2interop/declaration/api/arkui") + + +if __name__ == '__main__': + sys.exit(main()) + \ No newline at end of file -- Gitee From 30042f0f54d32ff1fb8699430ccd8c800b9ea416 Mon Sep 17 00:00:00 2001 From: yulong-nj Date: Tue, 15 Jul 2025 10:35:11 +0800 Subject: [PATCH 2/3] add delete_label_noninterop.js Signed-off-by: yulong-nj --- BUILD.gn | 9 +- build-tools/delete_label_noninterop.js | 112 +++++++++++++++++++++++++ delete_arkui_label.py | 46 ++++++++++ process_interop.py | 7 +- 4 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 build-tools/delete_label_noninterop.js create mode 100755 delete_arkui_label.py mode change 100644 => 100755 process_interop.py diff --git a/BUILD.gn b/BUILD.gn index 8664543db2..bf86711c9d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -408,12 +408,11 @@ ohos_copy("api_check_plugin") { } action("ohos_ets_process_interop") { - script = "process_interop.py" - deps = [ - ":ohos_base_split" + ":build_sdk_interop1", + ":build_sdk_interop2" ] - + script = "process_interop.py" args = [ "--intermediates-output", rebase_path("${interface_sdk_path}", root_build_dir), @@ -423,5 +422,5 @@ action("ohos_ets_process_interop") { rebase_path(nodejs, root_build_dir) ] - outputs = ["${interface_sdk_path}/ets1.1_interop", "${interface_sdk_path}/ets1.2_interop"] + outputs = ["${interface_sdk_path}/arkui_dummy_interop"] } \ No newline at end of file diff --git a/build-tools/delete_label_noninterop.js b/build-tools/delete_label_noninterop.js new file mode 100644 index 0000000000..fdac4cb0c0 --- /dev/null +++ b/build-tools/delete_label_noninterop.js @@ -0,0 +1,112 @@ +/* + * 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 commander = require('commander'); + +function start() { + const program = new commander.Command(); + program + .name('deleteLabel') + .version('0.0.1'); + program + .option('--input ', 'input path') + .option('--output ', 'output path') + .action((opts) => { + outputPath = opts.output; + inputDir = opts.input; + console.error('yulong--- inputDir', inputDir); + console.error('yulong--- outputPath', outputPath); + transformFiles(opts.input); + }); + program.parse(process.argv); +} + +function transformFiles(inputDir) { + // 入口 + try { + const utFiles = []; + readFile(inputDir, utFiles); // 读取文件 + tsTransform(utFiles); + } catch (error) { + console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error); + } +} + +/** + * 删除所有 * @noninterop + * @param {string} content + */ +function deleteLabel(content) { + let result = content.replace(/^\s*\*\s*\@noninterop\s*[\r\n]/mg, ''); + return result; +} + +/** + * 遍历所有文件进行处理 + * @param {Array} utFiles 所有文件 + */ +function tsTransform(utFiles) { + utFiles.forEach((url) => { + console.error('yulong--- url', url); + let content = fs.readFileSync(url, 'utf-8'); // 文件内容 + writeFile(url, deleteLabel(content)); + }); +} + +/** + * 读取目录下所有文件 + * @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.log('ETS ERROR: ' + e); + } +} + +function writeFile(url, data, option) { + const newFilePath = path.resolve(outputPath, path.relative(inputDir, url)); + fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => { + if (err) { + console.log(`ERROR FOR CREATE PATH ${err}`); + } else { + if (data === '') { + fs.rmSync(newFilePath); + return; + } + fs.writeFileSync(newFilePath, data, option, (err) => { + if (err) { + console.log(`ERROR FOR CREATE FILE ${err}`); + } + }); + } + }); +} + +let outputPath = ''; +let inputDir = ''; +start(); diff --git a/delete_arkui_label.py b/delete_arkui_label.py new file mode 100755 index 0000000000..8a434e5c02 --- /dev/null +++ b/delete_arkui_label.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# 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. + +import os +import subprocess +import argparse +import sys + +INTEROP_POSTPROCESS_TOOL = "interface/sdk-js/build-tools/delete_label_noninterop.js" + +def delete_label_noninterop(source_root:str, input_dir:str, out_path:str, nodejs:str): + tool = os.path.join(source_root, INTEROP_POSTPROCESS_TOOL) + tool = os.path.abspath(tool) + nodejs = os.path.abspath(nodejs) + print("cmd=>", " ".join([nodejs, tool, "--input", input_dir, "--output", + out_path])) + + p = subprocess.Popen([nodejs, tool, "--input", input_dir, "--output", + out_path], stdout=subprocess.PIPE) + + p.wait() + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--root-build-dir', required=True) + parser.add_argument('--node-js', required=True) + parser.add_argument('--input-interface-sdk', required=True) + parser.add_argument('--output-arkui-interface-sdk', required=True) + options = parser.parse_args() + delete_label_noninterop(options.root_build_dir, os.path.abspath(options.input_interface_sdk), os.path.abspath(options.output_arkui_interface_sdk), options.node_js) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/process_interop.py b/process_interop.py old mode 100644 new mode 100755 index 7d42dff1bc..356f25df1e --- a/process_interop.py +++ b/process_interop.py @@ -47,10 +47,11 @@ def main(): parser.add_argument('--node-js', required=True) options = parser.parse_args() - process_interop(options, "/ets1.1interop/api/arkui", "/ets1.1interop/api/arkui") - process_interop(options, "/ets1.2interop/declaration/api/arkui", "/ets1.2interop/declaration/api/arkui") + process_interop(options, "/ets1.1interop/api", "/ets1.1interop/api") + process_interop(options, "/ets1.1interop/component", "/ets1.1interop/component") + process_interop(options, "/ets1.2interop/declaration/api", "/ets1.2interop/declaration/api") if __name__ == '__main__': sys.exit(main()) - \ No newline at end of file + -- Gitee From 4c9fa8335da640c457c2ace6c7cbfcd91841bb80 Mon Sep 17 00:00:00 2001 From: liuyulong Date: Thu, 24 Jul 2025 16:04:13 +0800 Subject: [PATCH 3/3] fix CodeCheck Signed-off-by: liuyulong --- BUILD.gn | 2 +- build-tools/delete_label_noninterop.js | 6 +- ...interop.js => process_label_noninterop.js} | 387 ++++++++++-------- ..._interop.py => process_label_noninterop.py | 4 +- 4 files changed, 216 insertions(+), 183 deletions(-) rename build-tools/{process_interop.js => process_label_noninterop.js} (76%) rename process_interop.py => process_label_noninterop.py (95%) diff --git a/BUILD.gn b/BUILD.gn index d0d52593c7..eb9b42b203 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -561,7 +561,7 @@ action("ohos_ets_process_interop") { ":build_sdk_interop1", ":build_sdk_interop2" ] - script = "process_interop.py" + script = "process_label_noninterop.py" args = [ "--intermediates-output", rebase_path("${interface_sdk_path}", root_build_dir), diff --git a/build-tools/delete_label_noninterop.js b/build-tools/delete_label_noninterop.js index e2938b1be1..6851811c27 100644 --- a/build-tools/delete_label_noninterop.js +++ b/build-tools/delete_label_noninterop.js @@ -39,7 +39,7 @@ function transformFiles(inputDir) { readFile(inputDir, utFiles); // 读取文件 tsTransform(utFiles); } catch (error) { - console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error); + console.error('DELETE_LABEL_NONINTEROP ERROR: ', error); } } @@ -89,7 +89,7 @@ function writeFile(url, data, option) { const newFilePath = path.resolve(outputPath, path.relative(inputDir, url)); fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => { if (err) { - console.log(`ERROR FOR CREATE PATH ${err}`); + console.error(`ERROR FOR CREATE PATH ${err}`); } else { if (data === '') { fs.rmSync(newFilePath); @@ -97,7 +97,7 @@ function writeFile(url, data, option) { } fs.writeFileSync(newFilePath, data, option, (err) => { if (err) { - console.log(`ERROR FOR CREATE FILE ${err}`); + console.error(`ERROR FOR CREATE FILE ${err}`); } }); } diff --git a/build-tools/process_interop.js b/build-tools/process_label_noninterop.js similarity index 76% rename from build-tools/process_interop.js rename to build-tools/process_label_noninterop.js index 73926cbb9f..f6468f78ce 100644 --- a/build-tools/process_interop.js +++ b/build-tools/process_label_noninterop.js @@ -34,11 +34,11 @@ function start() { program .option('--input ', 'input path') .option('--output ', 'output path') - .option('--export ', 'export flag', false) + .option('--export ', 'export flag', false) .action((opts) => { outputPath = opts.output; inputDir = opts.input; - exportFlag = opts.export; + exportFlag = opts.export === 'true'; transformFiles(opts.input); }); program.parse(process.argv); @@ -48,7 +48,8 @@ function writeArkuiComponentFile() { let data = ''; for (const [file, elements] of exportElementsMap) { if (elements.length > 0) { - data += `export {${elements.join(', ')}} from "../component/${file.replace('.d.ts', '').replace('.d.ets', '')}"\n`; + data += `export {${elements.join(', ')}} from "../component/${file.replace('.d.ts', '') + .replace('.d.ets', '')}"\n`; } } fs.writeFileSync(`${path.resolve(outputPath, '../api')}/@ohos.arkui.component.d.ets`, data, undefined, (err) => { @@ -240,6 +241,51 @@ function postProcessContent(content) { return content.replace(/^(\s*)\/\*\*\@reserved (.*) \*\/$/mg, '$1$2'); } +function outputFile(url, node, sourceFile, referencesMessage, copyrightMessage, isCopyrightDeleted) { + if (isEmptyFile(node)) { + return; + } + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); + if (isCopyrightDeleted) { + // 当第一个节点被删除时会同步删除整个文件jsdoc + result = copyrightMessage + '\n' + result; + } + copyrightMessage = node.getFullText().replace(node.getText(), ''); + if (referencesMessage) { + // 将references写入文件 + result = + result.substring(0, copyrightMessage.length) + + '\n' + + referencesMessage + + result.substring(copyrightMessage.length); + } + result = removeNonInteropDoc(result); + exportElementsMap.set(path.relative(inputDir, url), exportElements); + writeFile(url, postProcessContent(result)); +} + +function collectAllIdentifier(node, context) { + const identifierSet = new Set([]); + if (!ts.isSourceFile(node) || !node.statements) { + return identifierSet; + } + node.statements.forEach((stat) => { + if (!ts.isImportDeclaration(stat)) { + ts.visitEachChild(stat, collectAllNodes, context); + } + }); + + function collectAllNodes(node) { + if (ts.isIdentifier(node)) { + identifierSet.add(node.escapedText.toString()); + } + return ts.visitEachChild(node, collectAllNodes, context); + } + + return identifierSet; +} + /** * 每个文件处理前回调函数第二个 * @param {string} url 文件路径 @@ -247,10 +293,9 @@ function postProcessContent(content) { */ function formatImportDeclaration(url, copyrightMessage = '', isCopyrightDeleted = false) { return (context) => { - const allIdentifierSet = new Set([]); return (node) => { sourceFile = node; - collectAllIdentifier(node); // 获取所有标识符 + const allIdentifierSet = collectAllIdentifier(node, context); // 获取所有标识符 formatValue = formatAllNodes(url, node, allIdentifierSet); // 获取所有节点 node = formatValue.node; const referencesMessage = formatValue.referencesMessage; @@ -258,99 +303,145 @@ function formatImportDeclaration(url, copyrightMessage = '', isCopyrightDeleted copyrightMessage = formatValue.copyrightMessage; isCopyrightDeleted = formatValue.isCopyrightDeleted; } - if (!isEmptyFile(node)) { - const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); - let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); - if (isCopyrightDeleted) { - // 当第一个节点被删除时会同步删除整个文件jsdoc - result = copyrightMessage + '\n' + result; - } - copyrightMessage = node.getFullText().replace(node.getText(), ''); - if (referencesMessage) { - // 将references写入文件 - result = - result.substring(0, copyrightMessage.length) + - '\n' + - referencesMessage + - result.substring(copyrightMessage.length); - } - result = removeNonInteropDoc(result); - exportElementsMap.set(path.relative(inputDir, url), exportElements); - writeFile(url, postProcessContent(result)); - } + outputFile(url, node, sourceFile, referencesMessage, copyrightMessage, isCopyrightDeleted); return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None); }; - - function collectAllIdentifier(node) { - if (ts.isSourceFile(node) && node.statements) { - node.statements.forEach((stat) => { - if (!ts.isImportDeclaration(stat)) { - ts.visitEachChild(stat, collectAllNodes, context); - } - }); - } - } - - function collectAllNodes(node) { - if (ts.isIdentifier(node)) { - allIdentifierSet.add(node.escapedText.toString()); - } - return ts.visitEachChild(node, collectAllNodes, context); - } }; } function formatAllNodes(url, node, allIdentifierSet, copyrightMessage = '', isCopyrightDeleted = false) { let referencesMessage = ''; let currReferencesModule = []; - if (ts.isSourceFile(node) && node.statements) { - const newStatements = []; - node.statements.forEach((statement) => { - if (ts.isImportDeclaration(statement)) { - const importInfo = formatAllNodesImportDeclaration( - node, - statement, - url, - currReferencesModule, - allIdentifierSet - ); - if (importInfo.statement) { - newStatements.push(statement); - } else if (importInfo.isCopyrightDeleted) { - copyrightMessage = importInfo.copyrightMessage; - isCopyrightDeleted = importInfo.isCopyrightDeleted; - } - } else if (ts.isStructDeclaration(statement)) { - statement = ts.factory.updateStructDeclaration( - statement, - statement.modifiers, - statement.name, - statement.typeParameters, - statement.heritageClauses, - statement.members.slice(1) - ); - newStatements.push(statement); - } else { + if (!ts.isSourceFile(node) || !node.statements) { + return { node, referencesMessage, copyrightMessage, isCopyrightDeleted }; + } + const newStatements = []; + node.statements.forEach((statement) => { + if (ts.isImportDeclaration(statement)) { + const importInfo = formatAllNodesImportDeclaration( + node, + statement, + url, + currReferencesModule, + allIdentifierSet + ); + if (importInfo.statement) { newStatements.push(statement); + } else if (importInfo.isCopyrightDeleted) { + copyrightMessage = importInfo.copyrightMessage; + isCopyrightDeleted = importInfo.isCopyrightDeleted; } - }); - currReferencesModule.forEach((item) => { - if (item.isUsed) { - referencesMessage += item.reference + '\n'; + } else if (ts.isStructDeclaration(statement)) { + statement = ts.factory.updateStructDeclaration( + statement, + statement.modifiers, + statement.name, + statement.typeParameters, + statement.heritageClauses, + statement.members.slice(1) + ); + newStatements.push(statement); + } else { + newStatements.push(statement); + } + }); + currReferencesModule.forEach((item) => { + if (item.isUsed) { + referencesMessage += item.reference + '\n'; + } + }); + node = ts.factory.updateSourceFile(node, newStatements); + return { node, referencesMessage, copyrightMessage, isCopyrightDeleted }; +} + +function hasCopyright(node) { + return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), '')); +} + +function getClauseSet(statement) { + // 是import节点 import { AsyncCallback } from './@ohos.base'; + const clauseSet = new Set([]); + if (!statement.importClause || !ts.isImportClause(statement.importClause)) { + return clauseSet; + } + // 标识符 + const clauseNode = statement.importClause; + if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) { + // 没有大括号的标识符 + clauseSet.add(clauseNode.name.escapedText.toString()); + } else if ( + clauseNode.namedBindings && + clauseNode.namedBindings.name && + ts.isIdentifier(clauseNode.namedBindings.name) + ) { + // 没有标识符 *号 + clauseSet.add(clauseNode.namedBindings.name.escapedText.toString()); + } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) { + // 有花括号的标识符 + clauseNode.namedBindings.elements.forEach((ele) => { + if (ele.name && ts.isIdentifier(ele.name)) { + clauseSet.add(ele.name.escapedText.toString()); } }); - node = ts.factory.updateSourceFile(node, newStatements); } - return { - node, - referencesMessage, - copyrightMessage, - isCopyrightDeleted - }; + return clauseSet; } -function hasCopyright(node) { - return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), '')); +function getExsitClauseSet(hasImportSpecifierInModules, importSpecifier, currReferencesModule, clauseSet, + allIdentifierSet) { + let currModule = []; + if (hasImportSpecifierInModules) { + let index = globalModules.get(importSpecifier); + currModule = currReferencesModule[index].modules[importSpecifier]; + } + const clasueCheckList = []; + let exsitClauseSet = new Set([]); + for (const clause of clauseSet) { + let flag = allIdentifierSet.has(clause); + if (hasImportSpecifierInModules) { + flag = allIdentifierSet.has(clause) && currModule.includes(clause); + } + if (flag) { + // 标识符使用到了当前import中的引用 + exsitClauseSet.add(clause); + clasueCheckList.push('exist'); + } else { + clasueCheckList.push('non-exist'); + } + } + let hasExsitStatus = false; + let hasNonExsitStatus = false; + clasueCheckList.forEach((ele) => { + if (ele === 'exist') { + hasExsitStatus = true; + } else { + hasNonExsitStatus = true; + } + }); + return { exsitClauseSet, hasExsitStatus, hasNonExsitStatus }; +} + +function handleUsedImport(hasNonExsitStatus, statement, exsitClauseSet, hasImportSpecifierInModules, + currReferencesModule) { + // 有使用到的标识符 + if (hasNonExsitStatus) { + // 有没有使用到的标识符 + const newSpecifiers = []; + statement.importClause.namedBindings.elements.forEach((element) => { + if (exsitClauseSet.has(element.name.escapedText.toString())) { + newSpecifiers.push(element); + } + }); + statement.importClause.namedBindings = ts.factory.updateNamedImports( + statement.importClause.namedBindings, + newSpecifiers + ); + } + if (hasImportSpecifierInModules) { + let index = globalModules.get(importSpecifier); + currReferencesModule[index].isUsed = true; + } + return { statement }; } @@ -364,91 +455,32 @@ function hasCopyright(node) { * @returns {{statement:ts.ImportDeclaration,copyrightMessage:string,isCopyrightDeleted:boolean}} statement 处理完成的导入节点、copyrightMessage */ function formatAllNodesImportDeclaration(node, statement, url, currReferencesModule, allIdentifierSet) { - // 是import节点 import { AsyncCallback } from './@ohos.base'; - const clauseSet = new Set([]); - if (statement.importClause && ts.isImportClause(statement.importClause)) { - // 标识符 - const clauseNode = statement.importClause; - if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) { - // 没有大括号的标识符 - clauseSet.add(clauseNode.name.escapedText.toString()); - } else if ( - clauseNode.namedBindings && - clauseNode.namedBindings.name && - ts.isIdentifier(clauseNode.namedBindings.name) - ) { - // 没有标识符 *号 - clauseSet.add(clauseNode.namedBindings.name.escapedText.toString()); - } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) { - // 有花括号的标识符 - clauseNode.namedBindings.elements.forEach((ele) => { - if (ele.name && ts.isIdentifier(ele.name)) { - clauseSet.add(ele.name.escapedText.toString()); - } - }); - } - } + const clauseSet = getClauseSet(statement); const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, ''); const fileDir = path.dirname(url); let hasImportSpecifierFile = hasFileByImportPath(importSpecifier, fileDir); let hasImportSpecifierInModules = globalModules.has(importSpecifier); - if ((hasImportSpecifierFile || hasImportSpecifierInModules) && clauseSet.size > 0) { - let currModule = []; - if (hasImportSpecifierInModules) { - let index = globalModules.get(importSpecifier); - currModule = currReferencesModule[index].modules[importSpecifier]; - } - const clasueCheckList = []; - let exsitClauseSet = new Set([]); - for (const clause of clauseSet) { - let flag = allIdentifierSet.has(clause); - if (hasImportSpecifierInModules) { - flag = allIdentifierSet.has(clause) && currModule.includes(clause); - } - if (flag) { - // 标识符使用到了当前import中的引用 - exsitClauseSet.add(clause); - clasueCheckList.push('exist'); - } else { - clasueCheckList.push('non-exist'); - } - } - let hasExsitStatus = false; - let hasNonExsitStatus = false; - clasueCheckList.forEach((ele) => { - if (ele === 'exist') { - hasExsitStatus = true; - } else { - hasNonExsitStatus = true; - } - }); - if (hasExsitStatus) { - // 有使用到的标识符 - if (hasNonExsitStatus) { - // 有没有使用到的标识符 - const newSpecifiers = []; - statement.importClause.namedBindings.elements.forEach((element) => { - if (exsitClauseSet.has(element.name.escapedText.toString())) { - newSpecifiers.push(element); - } - }); - statement.importClause.namedBindings = ts.factory.updateNamedImports( - statement.importClause.namedBindings, - newSpecifiers - ); - } - if (hasImportSpecifierInModules) { - let index = globalModules.get(importSpecifier); - currReferencesModule[index].isUsed = true; - } - return { statement }; - } else if (hasCopyright(statement)) { + if ((!hasImportSpecifierFile && !hasImportSpecifierInModules) || clauseSet.size === 0) { + if (hasCopyright(statement)) { return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true }; + } else { + return { statement: undefined, copyrightMessage: '', isCopyrightDeleted: false }; } + } + const clauseSetValue = + getExsitClauseSet(hasImportSpecifierInModules, importSpecifier, currReferencesModule, clauseSet, + allIdentifierSet); + const hasExsitStatus = clauseSetValue.hasExsitStatus; + const hasNonExsitStatus = clauseSetValue.hasNonExsitStatus; + let exsitClauseSet = clauseSetValue.exsitClauseSet; + if (hasExsitStatus) { + return handleUsedImport(hasNonExsitStatus, statement, exsitClauseSet, hasImportSpecifierInModules, + currReferencesModule); } else if (hasCopyright(statement)) { return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true }; + } else { + return { statement: undefined, copyrightMessage: '', isCopyrightDeleted: false }; } - return { statement: undefined, copyrightMessage: '', isCopyrightDeleted: false }; } /** @@ -685,18 +717,15 @@ function getExportIdentifierName(statement) { } else if (ts.isExportAssignment(statement)) { //export default test1 names.push(statement.expression.escapedText.toString()); - } else if (ts.isExportDeclaration(statement)) { + } else if (ts.isExportDeclaration(statement) && statement.exportClause) { //export {test1} 、export {test1 as test} 、export * from './featureability' - const exportClause = statement.exportClause; - if (exportClause) { - const specifiers = exportClause.elements; - specifiers.forEach((specifier) => { - if (ts.isExportSpecifier(specifier)) { - const name = specifier.propertyName ? specifier.propertyName : specifier.name; - names.push(name.escapedText.toString()); - } - }); - } + const specifiers = statement.exportClause.elements; + specifiers.forEach((specifier) => { + if (ts.isExportSpecifier(specifier)) { + const name = specifier.propertyName ? specifier.propertyName : specifier.name; + names.push(name.escapedText.toString()); + } + }); } return names; } @@ -710,19 +739,23 @@ function getExportIdentifierName(statement) { function processVisitEachChild(context, node) { return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点 + function getNewStatements(node) { + const newStatements = []; + node.body.statements.forEach((statement) => { + if (!isNonInterop(statement)) { + newStatements.push(statement); + } + }); + return newStatements; + } + function processAllNodes(node) { if (ts.isInterfaceDeclaration(node)) { node = processInterfaceDeclaration(node); } else if (ts.isClassDeclaration(node)) { node = processClassDeclaration(node); } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) { - const newStatements = []; - node.body.statements.forEach((statement) => { - if (!isNonInterop(statement)) { - newStatements.push(statement); - } - }); - const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements); + const newModuleBody = ts.factory.updateModuleBlock(node.body, getNewStatements(node)); node = ts.factory.updateModuleDeclaration( node, node.modifiers, diff --git a/process_interop.py b/process_label_noninterop.py similarity index 95% rename from process_interop.py rename to process_label_noninterop.py index 68ea137197..d1a3f95998 100755 --- a/process_interop.py +++ b/process_label_noninterop.py @@ -20,7 +20,7 @@ import shutil import subprocess INTERFACE_PATH = "interface/sdk-js" -PROCESS_INTEROP = "interface/sdk-js/build-tools/process_interop.js" +PROCESS_INTEROP = "interface/sdk-js/build-tools/process_label_noninterop.js" def process_interop(options, sub_input, sub_output, export_flag): @@ -49,7 +49,7 @@ def main(): options = parser.parse_args() process_interop(options, "/ets1.1interop/api", "/ets1.1interop/api", "false") - process_interop(options, "/ets1.1interop/component", "/ets1.1interop/component", "true") + process_interop(options, "/ets1.1interop/component", "/ets1.1interop/component", "false") process_interop(options, "/ets1.2interop/declaration/api", "/ets1.2interop/declaration/api", "false") -- Gitee