当前位置: 首页 > news >正文

NodeJS根据UE5.7插件源码文件夹,生成markdown项目代码文档

JS代码

// generate-ue-plugin-doc.js
/*** npm install iconv-lite jschardet* 如果需要处理非 UTF-8 编码的文件,可以在 safeReadFile 中使用 iconv-lite 和 jschardet 来检测和转换编码。* node generate-ue-plugin-doc.js <插件根目录>*/
const fs = require('fs');
const path = require('path');
const iconv = require('iconv-lite');
const jschardet = require('jschardet');// ========== 工具函数 ==========// 判断是否为文本文件(可安全读取)
function isTextFile(filePath) {const textExts = ['.ini', '.json', '.xml', '.txt', '.csv', '.md', '.cs', '.h', '.cpp', '.uplugin'];const ext = path.extname(filePath).toLowerCase();return textExts.includes(ext);
}// 判断是否为二进制资源(如 uasset)
function isBinaryAsset(filePath) {const binaryExts = ['.uasset', '.umap', '.uexp', '.ubulk'];const ext = path.extname(filePath).toLowerCase();return binaryExts.includes(ext);
}function generateTree(dir) {const rootName = path.basename(dir);const lines = [`${rootName}/`];function walk(currentPath, prefix = '') {let items;try {items = fs.readdirSync(currentPath);} catch {return; // 跳过无法读取的目录}// 排序:目录在前,文件在后,同类型按名称排序items.sort((a, b) => {const aPath = path.join(currentPath, a);const bPath = path.join(currentPath, b);const aStat = fs.statSync(aPath);const bStat = fs.statSync(bPath);if (aStat.isDirectory() && !bStat.isDirectory()) return -1;if (!aStat.isDirectory() && bStat.isDirectory()) return 1;return a.localeCompare(b);});for (let i = 0; i < items.length; i++) {const item = items[i];const itemPath = path.join(currentPath, item);const isLast = i === items.length - 1;// 直接显示所有项(无过滤)lines.push(prefix + (isLast ? '└── ' : '├── ') + item);// 如果是目录,递归进入if (fs.statSync(itemPath).isDirectory()) {const nextPrefix = prefix + (isLast ? '    ' : '│   ');walk(itemPath, nextPrefix);}}}walk(dir);return lines.join('\n');
}// 递归收集文件(按扩展名过滤或全收)
function collectFilesRecursively(dir, filterExt = null) {if (!fs.existsSync(dir)) return [];const results = [];const items = fs.readdirSync(dir).sort();for (const item of items) {const fullPath = path.join(dir, item);const stat = fs.statSync(fullPath);const relPath = path.relative(dir, fullPath).replace(/\\/g, '/');if (stat.isDirectory()) {results.push(...collectFilesRecursively(fullPath, filterExt));} else {if (!filterExt || item.toLowerCase().endsWith(filterExt.toLowerCase())) {results.push({label: relPath,path: fullPath});}}}return results;
}// 安全读取文本内容(二进制文件返回特殊标记)
async function safeReadFile(filePath) {if (isBinaryAsset(filePath)) {return '[Binary file - content not displayed]';}if (!isTextFile(filePath)) {return '[Unsupported file type - skipped content]';}try {await fs.promises.access(filePath);const buffer = await fs.promises.readFile(filePath); // 读为 Buffer// 如果文件为空if (buffer.length === 0) return '';// 尝试检测编码(fallback 到 utf8)const detected = jschardet.detect(buffer);let encoding = detected.encoding;// 限制只处理常见编码,避免误判if (!['UTF-8', 'GB2312', 'GBK', 'GB18030'].includes(encoding)) {encoding = 'UTF-8';}// 特别处理:Windows 记事本保存的 ANSI 文件通常被识别为 'windows-1252',但中文环境下其实是 GBKif (encoding === 'windows-1252' && buffer.some(b => b > 127)) {encoding = 'GBK'; // 中文系统下更可能是 GBK}// 解码let content;try {content = iconv.decode(buffer, encoding);} catch (e) {// 如果解码失败,回退到 UTF-8(容忍部分错误)content = iconv.decode(buffer, 'UTF-8', { stripBOM: true });}return content.trim() === '' ? '' : content;} catch (err) {console.warn(`Failed to read file ${filePath}:`, err.message);return null;}
}// 获取代码块语言标识
function getLangByExt(ext) {const map = {'.h': 'cpp','.cpp': 'cpp','.cs': 'csharp','.uplugin': 'json','.ini': 'ini','.json': 'json','.xml': 'xml','.txt': 'plaintext','.md': 'markdown'};return map[ext.toLowerCase()] || 'plaintext';
}// ========== 主逻辑 ==========async function main(pluginDir) {const pluginName = path.basename(pluginDir);// ====== 1. 插件结构 ======let markdown = `# UE5.7插件:${pluginName}\n\n`;markdown += `## 插件结构\n\n`;markdown += '```txt\n';markdown += generateTree(pluginDir);markdown += '\n```\n\n';// ====== 2. 插件代码 ======markdown += `## 插件代码\n\n`;const allFiles = [];// --- 基础文件 ---allFiles.push({ label: `${pluginName}.uplugin`, path: path.join(pluginDir, `${pluginName}.uplugin`) },{ label: `${pluginName}.Build.cs`, path: path.join(pluginDir, 'Source', pluginName, `${pluginName}.Build.cs`) });// --- Public/*.h ---const publicDir = path.join(pluginDir, 'Source', pluginName, 'Public');allFiles.push(...collectFilesRecursively(publicDir).filter(f => f.path.endsWith('.h')));// --- Private/*.cpp ---const privateDir = path.join(pluginDir, 'Source', pluginName, 'Private');allFiles.push(...collectFilesRecursively(privateDir).filter(f => f.path.endsWith('.cpp')));// --- Config/*.ini ---const configDir = path.join(pluginDir, 'Config');if (fs.existsSync(configDir)) {const configFiles = collectFilesRecursively(configDir);allFiles.push(...configFiles.map(f => ({label: `Config/${f.label}`,path: f.path})));}// --- Content/* (all files) ---const contentDir = path.join(pluginDir, 'Content');if (fs.existsSync(contentDir)) {const contentFiles = collectFilesRecursively(contentDir);allFiles.push(...contentFiles.map(f => ({label: `Content/${f.label}`,path: f.path})));}// 排序:按 label 字母序allFiles.sort((a, b) => a.label.localeCompare(b.label));// 生成 Markdownfor (const file of allFiles) {const ext = path.extname(file.path);const lang = getLangByExt(ext);markdown += `### ${file.label}\n\n`;markdown += `\`\`\`${lang}\n`;const content = await safeReadFile(file.path);if (content !== null) {markdown += content;} else {markdown += '// 文件不存在';}markdown += '\n```\n\n';}// 输出const outputPath = path.join(pluginDir, `${pluginName}_DOC.md`);await fs.promises.writeFile(outputPath, markdown, 'utf8');console.log(`✅ 文档已生成: ${outputPath}`);
}// ========== 启动 ==========
const pluginPath = process.argv[2];
if (!pluginPath) {console.error('用法: node generate-ue-plugin-doc.js <插件根目录>');process.exit(1);
}main(path.resolve(pluginPath)).catch(console.error);

效果图

屏幕截图 2026-03-04 173042

http://www.jsqmd.com/news/436135/

相关文章:

  • AI 开发闭环了:从写代码到控制浏览器,中间不需要你 CLAUDE CODE × CHROME / WORKFLOW
  • 2026广州十大天珠品牌实力厂家排行榜:聚焦全屋健康,基于环保性能与市场口碑的权威推荐榜单 - 十大品牌榜
  • 2026能源管理系统厂商评分榜:十大厂商量化排名与推荐指数 - 华Sir1
  • javascript中的splice vs slice用法
  • 深度学习--卷积神经网络(上) - 教程
  • 智能守护新选择:无源智能锁市场全景与优选厂家指南 - 品牌评测官
  • 2026机器视觉系统厂商实力排名:技术、市场与应用全面盘点 - 华Sir1
  • 知到智慧树视频课件课程下载工具,如何在电脑端下载知到智慧树视频课程课件资料PDF,PPT到本地?
  • 2026年北京即时修护面膜专业选型指南:快速补水/玻尿酸/B5修护/舒缓泛红面膜供应商推荐 - 品牌推荐官
  • 2026年广州问题性肌肤管理品牌优选指南 五大品质品牌企业参考 - 十大品牌榜
  • 2026设备管理系统厂商价值排行榜:十大主流厂商评分与推荐 - 华Sir1
  • 斯百德会展|专业活动策划公司的核心能力,藏在大型学术会议前期策划里 - 麦麦唛
  • 杭州绗亿科技|多平台验货数据实时看,决策快人一步 - 搭贝
  • 2026年 ARO/GRACO涂胶机厂家推荐排行榜:汽车玻璃/新能源电机/锂电涂胶机等精密涂胶设备专业实力解析 - 品牌企业推荐师(官方)
  • powershell 临时全局sockt5 代理
  • 2026年全国电力管哪家好?技术先进产能充足服务有保障 适配各类电力工程 - 深度智识库
  • 2026年广州孕期护理品牌优选指南 十大品质品牌企业参考 - 十大品牌榜
  • 2026年市场可靠的AI搜索企业选哪家,视频矩阵/信息流广告代运营/信息流广告/抖音广告代运营,AI搜索企业推荐排行榜单 - 品牌推荐师
  • 2026年土豆粉机设备选型推荐:天华机械小型/商用土豆粉机原理与报价参考 - 品牌推荐官
  • 2026别错过!千笔ai写作,口碑爆棚的AI论文软件
  • Tita项目集使用小技巧:关联项目统筹管理,进度追踪更高效
  • 2026广州最新母婴护理机构推荐:专业定制服务,守护新生家庭安心时光 - 十大品牌榜
  • 论文阅读“VLA-IN-THE-LOOP: ONLINE POLICY CORRECTION WITH WORLD MODELS FOR ROBUST ROBOTIC GRASPING“
  • 微博热搜榜单实时监控爬虫:从原理到落地,一文掌握热点自动抓取
  • 2026年T-BOX品牌实力排行白皮书:谁在定义车联终端的“硬核标准”? - 华Sir1
  • 学长亲荐 10个AI论文网站:MBA毕业论文写作必备工具测评与推荐
  • 为什么很多APP的动态贴纸会卡顿?问题可能出在美颜SDK
  • 赋能高端制造,解锁表面革新——等离子处理机行业全景与优质品牌解析 - 品牌推荐大师
  • 2026年3月英语录音转文字网站推荐,智能识别与品牌保障口碑之选 - 品牌鉴赏师
  • 研究生收藏!全网爆红的降AIGC网站 —— 千笔·专业降AI率智能体