#!/usr/bin/env node /** * AI Studio 项目预处理器(最小化处理模式) * * 只做 100% 有把握的操作: * 1. 完整复制项目 * 2. 分析项目结构 * 3. 生成任务文档 * * 不做任何代码修改,全部留给 AI 处理 */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const TARGET_TYPE_TO_SRC_DIR = { prototypes: 'src/prototypes', components: 'src/components', themes: 'src/themes', }; const THEME_SPLIT_SKILL_DOCS = [ '/skills/axure-prototype-workflow/theme-generation.md', '/skills/axure-prototype-workflow/doc-generation.md', '/skills/axure-prototype-workflow/data-generation.md', '/skills/web-page-workflow/theme-generation.md', '/skills/web-page-workflow/doc-generation.md', '/skills/web-page-workflow/data-generation.md', ]; const CONFIG = { projectRoot: path.resolve(__dirname, '..'), tempDir: path.resolve(__dirname, '../temp'), }; function log(message, type = 'info') { const prefix = { info: '✓', warn: '⚠', error: '✗', progress: '⏳' }[type] || 'ℹ'; console.log(`${prefix} ${message}`); } function ensureDir(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } } function sanitizeName(rawName) { return String(rawName || '') .replace(/[^a-z0-9-]/gi, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, '') .toLowerCase(); } function getTargetInfo(targetType, outputName) { const srcDir = TARGET_TYPE_TO_SRC_DIR[targetType]; const outputBaseDir = path.resolve(CONFIG.projectRoot, srcDir); const outputDir = path.join(outputBaseDir, outputName); const relativeOutputDir = `${srcDir}/${outputName}`; if (targetType === 'themes') { return { targetType, srcDir, outputBaseDir, outputDir, relativeOutputDir, tasksFileName: '.ai-studio-theme-tasks.md', analysisFileName: '.ai-studio-theme-analysis.json', checkPath: `/themes/${outputName}`, label: '主题', }; } return { targetType, srcDir, outputBaseDir, outputDir, relativeOutputDir, tasksFileName: '.ai-studio-tasks.md', analysisFileName: '.ai-studio-analysis.json', checkPath: `/${targetType}/${outputName}`, label: targetType === 'components' ? '组件' : '页面', }; } // 递归查找所有 .tsx/.ts 文件 function findFiles(dir, extensions = ['.tsx', '.ts']) { const results = []; if (!fs.existsSync(dir)) return results; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { // 跳过 node_modules if (entry.name === 'node_modules') continue; results.push(...findFiles(fullPath, extensions)); } else { const ext = path.extname(entry.name); if (extensions.includes(ext)) { results.push(fullPath); } } } return results; } function copyDirectory(src, dest) { if (!fs.existsSync(src)) return 0; ensureDir(dest); const entries = fs.readdirSync(src, { withFileTypes: true }); let count = 0; for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { if (entry.name === 'node_modules') continue; count += copyDirectory(srcPath, destPath); } else { fs.copyFileSync(srcPath, destPath); count++; } } return count; } console.log('AI Studio Converter - Minimal Processing Mode\n'); function analyzeProject(targetDir) { const analysis = { files: [], components: [], dependencies: {}, structure: {}, indexHtml: null, viteConfig: null }; const files = findFiles(targetDir, ['.tsx', '.ts']); files.forEach(file => { const relativePath = path.relative(targetDir, file); const content = fs.readFileSync(file, 'utf8'); const fileName = path.basename(file); const fileInfo = { path: relativePath, isAppTsx: fileName === 'App.tsx', isIndexTsx: fileName === 'index.tsx', imports: [] }; const importMatches = content.matchAll(/import\s+.*from\s+['"]([^'"]+)['"]/g); for (const match of importMatches) { fileInfo.imports.push(match[1]); } analysis.files.push(fileInfo); if (relativePath.startsWith('components/')) { analysis.components.push(relativePath); } }); const indexHtmlPath = path.join(targetDir, 'index.html'); if (fs.existsSync(indexHtmlPath)) { const htmlContent = fs.readFileSync(indexHtmlPath, 'utf8'); const importMapMatch = htmlContent.match(/