diff --git a/arkui_transformer.py b/arkui_transformer.py index 0d00f1b74d9927acec37aaab6dc2df0be6e4567d..0f6919fd6e0a4127f2e6bfaa83d7606070e19c7e 100755 --- a/arkui_transformer.py +++ b/arkui_transformer.py @@ -30,6 +30,10 @@ def compile_package(options): nodejs = os.path.abspath(options.node_js) input_dir = os.path.abspath(options.input) output = os.path.abspath(options.output) + config_path = '' + if (len(options.config_path) > 0): + config_path = os.path.abspath(options.config_path) + custom_env = { 'PATH': f"{os.path.dirname(os.path.abspath(options.node_js))}:{os.environ.get('PATH')}", 'NODE_HOME': os.path.dirname(os.path.abspath(options.node_js)), @@ -38,7 +42,10 @@ def compile_package(options): process = subprocess.run([npm, "run", "compile:arkui"], env=custom_env, cwd=tool_path, shell=False) if os.path.exists(package_path): - p = subprocess.run([nodejs, package_path, "--input-dir", input_dir, "--target-dir", output], cwd=tool_path, shell=False) + if (len(config_path) > 0): + p = subprocess.run([nodejs, package_path, "--input-dir", input_dir, "--target-dir", output, "--config-path", config_path], cwd=tool_path, shell=False) + else: + p = subprocess.run([nodejs, package_path, "--input-dir", input_dir, "--target-dir", output], cwd=tool_path, shell=False) else: print("arkui_transformer: tool path does not exist") @@ -49,6 +56,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--input', required=True) parser.add_argument('--output', required=True) + parser.add_argument('--config_path', required=False, default='') parser.add_argument('--source_root_dir', required=True) parser.add_argument('--npm-path', required=True) parser.add_argument('--node-js', required=True) diff --git a/build-tools/delete_systemapi_plugin.js b/build-tools/delete_systemapi_plugin.js index 82a3eb989a5102b1103f882faa8972c67afa36db..cdc2a9656b6616f0ea48b057935b7ef94b6d0ea1 100644 --- a/build-tools/delete_systemapi_plugin.js +++ b/build-tools/delete_systemapi_plugin.js @@ -22,7 +22,7 @@ let lastNoteStr = ''; let lastNodeName = ''; let etsType = 'ets'; let componentEtsFiles = []; -let componentEtsDeleteFiles = []; +let componentEtsDeleteFiles = ['plugincomponent', 'uiextensioncomponent']; const referencesMap = new Map(); const referencesModuleMap = new Map(); const kitFileNeedDeleteMap = new Map(); @@ -41,6 +41,45 @@ const PATT = { REFERENCEURL_RIGHTSDK: /(..\/)(\S*)build-tools\/ets-loader\/declarations\/(\S*)/g, REFERENCEURL_SDK: /(..\/)(\S*)component\/(\S*)/g, }; +const METHOD_KIND = [ + ts.SyntaxKind.MethodDeclaration, + ts.SyntaxKind.FunctionDeclaration, + ts.SyntaxKind.MethodSignature, + ts.SyntaxKind.Constructor +]; + +const regJSDOCStr = `\\s*\\/\\*\\*(?:(?!\\/\\*\\*)[\\s\\S])*?`; + +const STATIC_API_DELETE_RULES = new Map([[ + 'arkui/component/common.static.d.ets', [ + `${regJSDOCStr}declare enum TransitionHierarchyStrategy.*ADAPTIVE = 1.*?\\n\\}.*?\\n`, + `${regJSDOCStr}declare interface PixelMapMock.*release\\(\\): void.*?\\n\\}.*?\\n`, + `${regJSDOCStr}declare interface PointLightStyle.*bloom\\?: number.*?\\n\\}.*?\\n`, + `${regJSDOCStr}declare interface LightSource.*color\\?: ResourceColor.*?\\n\\}.*?\\n` + ]], [ + 'arkui/component/embeddedComponent.static.d.ets', [ + `${regJSDOCStr}@memo.*\\): EmbeddedComponentAttribute` + ]], [ + 'arkui/component/list.static.d.ets', [ + `${regJSDOCStr}export declare enum ChainEdgeEffect.*STRETCH.*?\\n\\}.*?\\n`, + `${regJSDOCStr}export declare interface ChainAnimationOptions.*damping\\?: number.*?\\n\\}.*?\\n` + ]], [ + 'arkui/component/pluginComponent.static.d.ets', []], [ + 'arkui/component/richEditor.static.d.ets', []], [ + 'arkui/component/textInput.static.d.ets', []], [ + 'arkui/component/uiExtensionComponent.static.d.ets', []], [ + 'arkui/component/xcomponent.static.d.ets', []]]); + +const STATIC_IMPORT_DELETE_RULES = new Map([[ + 'arkui/component/common.static.d.ets', [{ + reg: /(import\s*\{.*)IlluminatedType(,\s)?(.*from.*enums)/g, replaceValue: '$1$3' + }]], [ + 'arkui/component/pluginComponent.static.d.ets', [{ + reg: /.*/sg, replaceValue: '' + }]], [ + 'arkui/component/uiExtensionComponent.static.d.ets', [{ + reg: /.*/sg, replaceValue: '' + }]]]); function start() { const program = new commander.Command(); @@ -87,7 +126,14 @@ function collectComponentEtsFiles() { } function getPureName(name) { - return path.basename(name).replace('.d.ts', '').replace('.d.ets', '').replace(/_/g, '').toLowerCase(); + return path + .basename(name) + .replace('.static.ets', '') + .replace('.static.d.ets', '') + .replace('.d.ts', '') + .replace('.d.ets', '') + .replace(/_/g, '') + .toLowerCase(); } /** @@ -274,7 +320,7 @@ function isExistArkUIFile(resolvedPath, importPath) { } function isExistImportFile(fileDir, importPath) { - return ['.d.ts', '.d.ets'].some(ext => { + return ['.d.ts', '.d.ets', '.static.d.ets'].some(ext => { return fs.existsSync(path.resolve(fileDir, `${importPath}${ext}`)); }); } @@ -310,12 +356,16 @@ function isArkTsSpecialSyntax(content) { */ function tsTransform(utFiles, callback) { utFiles.forEach((url) => { + const relativePath = path.relative(inputDir, url); + if (STATIC_API_DELETE_RULES.has(relativePath.replace(/\\/g, '/'))) { + const content = processStaticFile(url); + writeFile(url, content); + return; + } const apiBaseName = path.basename(url); let content = fs.readFileSync(url, 'utf-8'); // 文件内容 if (isArkTsSpecialSyntax(content)) { - if (!/\@systemapi/.test(content)) { - writeFile(url, content); - } + writeFile(url, content); return; } let isTransformer = /\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName); @@ -327,6 +377,7 @@ function tsTransform(utFiles, callback) { isTransformer = false; } } + if (!isTransformer) { writeFile(url, content); return; @@ -350,6 +401,32 @@ function tsTransform(utFiles, callback) { }); }); } + +/** + * + * @param {string} url + * @returns + */ +function processStaticFile(url) { + const relativePath = path.relative(inputDir, url).replace(/\\/g, '/'); + let content = fs.readFileSync(url, 'utf-8'); // 文件内容 + const regNodeList = STATIC_API_DELETE_RULES.get(relativePath); + regNodeList.forEach(regRule => { + const regSystemApi = new RegExp(regRule, 'sg'); + content = content.replace(regSystemApi, '\n'); + }); + const replaceSignNode = new RegExp( + '\\s*\\/\\*\\*(?:(?!\\/\\*\\*)[\\s\\S])*?@systemapi[\\s\\S]*?\\*\\/.*?\\r?\\n.*?\\r?\\n', + 'g' + ); + content = content.replace(replaceSignNode, '\n'); + const regImportList = STATIC_IMPORT_DELETE_RULES.get(relativePath) || []; + regImportList.forEach(regNode => { + content = content.replace(regNode.reg, regNode.replaceValue); + }); + return content; +} + /** * 切换references或者references中path的格式 * @param {string} references references或者references中的path @@ -437,13 +514,16 @@ function readFile(dir, utFiles) { } function writeFile(url, data, option) { - const newFilePath = path.resolve(outputPath, path.relative(inputDir.replace('api', ''), url)); + const urlPath = url.replace(/\.static\.d\.ets$/, '.d.ets'); + const urlDirName = path.dirname(inputDir); + const relativePath = path.relative(urlDirName, urlPath); + const newFilePath = path.resolve(outputPath, relativePath); fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => { if (err) { console.log(`ERROR FOR CREATE PATH ${err}`); } else { if (data === '') { - fs.rmSync(newFilePath); + fs.rmSync(newFilePath, { force: true }); return; } fs.writeFileSync(newFilePath, data, option, (err) => { @@ -457,6 +537,59 @@ function writeFile(url, data, option) { const globalModules = new Map(); +/** + * 遍历处理overload节点 + * @param context 解析过后的内容 + * @param node 解析过后的节点 + * @returns ts.node + */ +function visitEachChild(context, node) { + return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点 + function processAllNodes(node) { + if (ts.isOverloadDeclaration(node)) { + node = processInterfaceDeclaration(node); + } + return ts.visitEachChild(node, processAllNodes, context); + } + function processInterfaceDeclaration(overloadNode) { + // 获取方法类型兄弟节点列表 + const parentNode = overloadNode.parent; + const brotherNodes = []; + const brotherFuntionNames = new Set([]); + if (ts.isSourceFile(parentNode) || ts.isModuleBlock(parentNode)) { + brotherNodes.push(...parentNode.statements); + } else if (ts.isInterfaceDeclaration(parentNode) || ts.isClassDeclaration(parentNode)) { + brotherNodes.push(...parentNode.members); + } + if (brotherNodes.length === 0) { + return undefined; + } + brotherNodes.forEach(brotherNode => { + if (METHOD_KIND.includes(brotherNode.kind) && brotherNode.name && ts.isIdentifier(brotherNode.name) && + !brotherFuntionNames.has(brotherNode.name.escapedText.toString())) { + brotherFuntionNames.add(brotherNode.name.escapedText.toString()); + } + }); + + // 更新overload节点 + const overloadChildren = overloadNode.members; + if (overloadChildren.length === 0) { + return undefined; + } + const newChildren = []; + overloadChildren.forEach(overloadChild => { + if (overloadChild.name && ts.isIdentifier(overloadChild.name) && + brotherFuntionNames.has(overloadChild.name.escapedText.toString())) { + newChildren.push(overloadChild); + } + }); + if (newChildren.length === 0) { + return undefined; + } + return ts.factory.updateOverloadDeclaration(overloadNode, overloadNode.modifier, overloadNode.name, newChildren); + } +} + /** * 每个文件处理前回调函数第二个 * @param {string} url 文件路径 @@ -469,7 +602,7 @@ function formatImportDeclaration(url, copyrightMessage = '', isCopyrightDeleted sourceFile = node; collectAllIdentifier(node); // 获取所有标识符 formatValue = formatAllNodes(url, node, allIdentifierSet); // 获取所有节点 - node = formatValue.node; + node = visitEachChild(context, formatValue.node); const referencesMessage = formatValue.referencesMessage; if (formatValue.isCopyrightDeleted) { copyrightMessage = formatValue.copyrightMessage; @@ -1177,7 +1310,7 @@ function isEmptyFile(node) { break; } } - const fileName = getPureName(node.fileName.replace('.ts', '').replace('.ets', '')); + const fileName = getPureName(node.fileName.replace('.ts', '').replace('.static.ets', '').replace('.ets', '')); if (isEmpty && componentEtsFiles.includes(fileName)) { componentEtsDeleteFiles.push(fileName); } diff --git a/build-tools/handleApiFiles.js b/build-tools/handleApiFiles.js index 2a5edae07c3102c3f694f94c0519cb75f1cc2a71..c5b47681cd99d0f19d541e231b8e7ba1ad7732ee 100755 --- a/build-tools/handleApiFiles.js +++ b/build-tools/handleApiFiles.js @@ -49,6 +49,10 @@ function isTsFile(path) { return path.endsWith('d.ts'); } +function isStaticFile(path) { + return path.endsWith('static.d.ets'); +} + function hasEtsFile(path) { // 为StateManagement.d.ts设定白名单,在1.2打包的时候在Linux上有大小写不同的重名,碰到直接返回true if (path.includes('StateManagement.d.ts')) { @@ -63,6 +67,16 @@ function hasTsFile(path) { return fs.existsSync(path.replace(/\.d\.[e]?ts$/g, '.d.ts')); } +function hasStaticFile(path) { + // 为StateManagement.d.ts设定白名单,在1.2打包的时候在Linux上有大小写不同的重名,碰到直接返回true + if (path.includes('StateManagement.d.ts')) { + console.log('StateManagement.d.ts is in white list, return true. path = ', path); + return true; + } else { + return fs.existsSync(path.replace(/\.d\.[e]?ts$/g, '.static.d.ets')); + } +} + /** * 配置参数 */ @@ -75,10 +89,11 @@ function start() { .option('--path ', 'path name') .option('--type ', 'handle type') .option('--output ', 'output path') + .option('--isPublic ', 'is Public') .option('--create-keep-file ', 'create keep file', 'false') .action((opts) => { dirType = opts.type; - handleApiFiles(opts.path, opts.type, opts.output, opts.createKeepFile); + handleApiFiles(opts.path, opts.type, opts.output, opts.isPublic, opts.createKeepFile); }); program.parse(process.argv); } @@ -88,7 +103,7 @@ function start() { * @param {*} rootPath * @param {*} type */ -function handleApiFiles(rootPath, type, output, createKeepFile) { +function handleApiFiles(rootPath, type, output, isPublic, createKeepFile) { const allApiFilePathSet = new Set(); const fileNames = fs.readdirSync(rootPath); const apiRootPath = rootPath.replace(/\\/g, '/'); @@ -108,7 +123,7 @@ function handleApiFiles(rootPath, type, output, createKeepFile) { for (const apiRelativePath of allApiFilePathSet) { try { - handleApiFileByType(apiRelativePath, rootPath, type, output); + handleApiFileByType(apiRelativePath, rootPath, type, output, isPublic); } catch (error) { console.log('error===>', error); } @@ -140,24 +155,76 @@ function handleApiFiles(rootPath, type, output, createKeepFile) { * @param {*} type * @returns */ -function handleApiFileByType(apiRelativePath, rootPath, type, output) { +function handleApiFileByType(apiRelativePath, rootPath, type, output, isPublic) { const fullPath = path.join(rootPath, apiRelativePath); const isEndWithEts = isEtsFile(apiRelativePath); const isEndWithTs = isTsFile(apiRelativePath); + const isEndWithStatic = isStaticFile(apiRelativePath); const outputPath = output ? path.join(output, apiRelativePath) : fullPath; const fileContent = fs.readFileSync(fullPath, 'utf-8'); + if (isEndWithStatic) { + if (type === 'ets2') { + if (isPublic === 'true') { + writeFile(outputPath, fileContent); + return; + } else { + writeFile(outputPath.replace(/\.static\.d\.ets$/, '.d.ets'), fileContent); + return; + } + } else { + return; + } + } + if (!isEndWithEts && !isEndWithTs) { writeFile(outputPath, fileContent); return; } - if (type === 'ets2' && !(hasEtsFile(fullPath) && isEndWithTs)) { + const isCorrectHandleFullpath = isHandleFullPath(fullPath, apiRelativePath, type); + if (type === 'ets2' && isCorrectHandleFullpath) { handleFileInSecondType(apiRelativePath, fullPath, type, output); - } else if (type === 'ets' && !(hasTsFile(fullPath) && isEndWithEts)) { + } else if (type === 'ets' && isCorrectHandleFullpath) { handleFileInFirstType(apiRelativePath, fullPath, type, output); } } +/** + * 判断当前的文件路径是否符合当前打包场景,过滤同名文件 + * @param {*} fullPath + * @param {*} apiRelativePath + * @param {*} type + * @returns + */ +function isHandleFullPath(fullPath, apiRelativePath, type) { + // 当前文件为.ts结尾文件 + if (isTsFile(apiRelativePath)) { + if (type === 'ets') { + return true; + } + if (!(hasEtsFile(fullPath)) && !(hasStaticFile(fullPath))) { + return true; + } + } + // 当前文件为.ets结尾文件 + if (isEtsFile(apiRelativePath)) { + if (!(hasTsFile(fullPath)) && !(hasStaticFile(fullPath))) { + return true; + } + if (hasTsFile(fullPath) && !(hasStaticFile(fullPath)) && type === 'ets2') { + return true; + } + if (hasStaticFile(fullPath) && !(hasTsFile(fullPath)) && type === 'ets') { + return true; + } + } + // 当前文件为.static结尾文件 + if (isStaticFile(apiRelativePath) && type === 'ets2') { + return true; + } + return false; +} + /** * 处理文件过滤 if arkts 1.1|1.2|1.1&1.2 定义 * @@ -360,6 +427,7 @@ function handleFileInSecondType(apiRelativePath, fullPath, type, output) { // 有1.2标签的文件,删除标记 if (secondRegx.test(sourceFile.getFullText())) { let newFileContent = deleteUnsportedTag(fileContent); + newFileContent = getFileContent(newFileContent, fullPath); writeFile(outputPath, deleteArktsTag(newFileContent)); return; } @@ -385,6 +453,7 @@ function handleFileInSecondType(apiRelativePath, fullPath, type, output) { // 有1.2标签的文件,删除标记 if (secondRegx.test(firstJsdocText)) { let newFileContent = deleteUnsportedTag(fileContent); + newFileContent = getFileContent(newFileContent, fullPath); writeFile(outputPath, deleteArktsTag(newFileContent)); return; } @@ -430,6 +499,7 @@ function handlehasTagFile(sourceFile, outputPath) { } // 保留最后一段注释 newContent = saveLatestJsDoc(newContent); + newContent = getFileContent(newContent, outputPath); newContent = deleteUnsportedTag(newContent); writeFile(outputPath, deleteArktsTag(newContent)); } @@ -460,11 +530,75 @@ function handleNoTagFileInSecondType(sourceFile, outputPath, fullPath) { } // 保留最后一段注释 newContent = saveLatestJsDoc(newContent); + newContent = getFileContent(newContent, outputPath); newContent = deleteArktsTag(newContent); newContent = deleteUnsportedTag(newContent); writeFile(outputPath, newContent); } +/** + * 获取删除overload节点后的文件内容 + * @param {*} sourceFile + * @returns + */ +function getFileContent(newContent, filePath) { + const regex = /^overload\s+.+$/; + if (isArkTsSpecialSyntax(newContent) || !regex.test(newContent)) { + return newContent; + } + const sourceFile = ts.createSourceFile(path.basename(filePath), newContent, ts.ScriptTarget.ES2017, true); + const printer = ts.createPrinter(); + const result = ts.transform(sourceFile, [deleteOverLoadJsDoc]); + const output = printer.printFile(result.transformed[0]); + return output; +} + +function isArkTsSpecialSyntax(content) { + return /\@memo|(? { + const visitNode = (node) => { + if (ts.isStructDeclaration(node)) { + return processStructDeclaration(node); + } + if (ts.isOverloadDeclaration(node)) { + return ts.factory.createOverloadDeclaration(node.modifiers, node.name, node.members); + } + return ts.visitEachChild(node, visitNode, context); + }; + const newSourceFile = ts.visitNode(sourceFile, visitNode); + return newSourceFile; + }; +} + +/** + * 处理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; +} + /** * 没有arkts标签,但有if arkts 1.2和1.1&1.2的情况 * @param {*} sourceFile @@ -473,7 +607,7 @@ function handleNoTagFileInSecondType(sourceFile, outputPath, fullPath) { */ function saveApiByArktsDefinition(sourceFile, fileContent, outputPath) { const regx = /\/\*\*\* if arkts (1.1&)?1.2 \*\/\s*([\s\S]*?)\s*\/\*\*\* endif \*\//g; - const regex = /\/\*\r?\n\s*\*\s*Copyright[\s\S]*?limitations under the License\.\r?\n\s*\*\//g; + const regex = /\/\*\r?\n\s*\*\s*Copyright[\s\S]*?\*\//g; const copyrightMessage = fileContent.match(regex)[0]; const firstNode = sourceFile.statements.find(statement => { return !ts.isExpressionStatement(statement); diff --git a/build-tools/package-lock.json b/build-tools/package-lock.json index aeb4e8fbe8e7636c036cdbe87190606ee5d8813e..f16cbabe3387f8b8ffcdc5b9337ecfc89e820c97 100644 --- a/build-tools/package-lock.json +++ b/build-tools/package-lock.json @@ -39,12 +39,9 @@ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, "typescript": { - "version": "npm:ohos-typescript@4.9.5-r5", - "resolved": "https://repo.huaweicloud.com/repository/npm/ohos-typescript/-/ohos-typescript-4.9.5-r5.tgz", - "integrity": "sha512-jaavgk6VJBs/Dwllj6y97+b4ve3CZPjl7uWAOqTsGM+2uaA6XIxxVgZZJdo811bccKhDkjvuqkUcHfSDidV8gw==", - "requires": { - "json5": "2.2.3" - } + "version": "npm:ohos-typescript@4.9.5-r9", + "resolved": "https://repo.huaweicloud.com/repository/npm/ohos-typescript/-/ohos-typescript-4.9.5-r9.tgz", + "integrity": "sha512-Hs7i6MKxHi15TjKK8qGiA75ejf0ggfF0L4F2GJ9XKLARu1hM5QQA395X+RwFYCjW76dqvf8s/CbhtiHNtackHg==" }, "util": { "version": "0.10.4", diff --git a/build-tools/package.json b/build-tools/package.json index 130a687306e991ba3933e28048350d2ba6514342..a28fb3e7d735dff61dfd5a4966ea258d25dd2627 100644 --- a/build-tools/package.json +++ b/build-tools/package.json @@ -13,6 +13,6 @@ "commander": "^13.1.0", "fs": "^0.0.1-security", "path": "^0.12.7", - "typescript": "npm:ohos-typescript@4.9.5-r5" + "typescript": "npm:ohos-typescript@4.9.5-r9" } }