UNPKG

codec-ai

Version:

Codec AI - 智能代码生成和编程助手,支持多语言代码生成、项目脚手架、代码转换和优化

319 lines (295 loc) 7.74 kB
import fs from 'fs-extra'; import path from 'path'; export class ProjectScaffolder { projectTemplates = { web: { 'react-ts': (name) => ({ 'package.json': `{ "name": "${name}", "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "@vitejs/plugin-react": "^4.0.0", "typescript": "^5.0.0", "vite": "^4.4.0" } }`, 'vite.config.ts': `import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 3000 } })`, 'tsconfig.json': `{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] }`, 'tsconfig.node.json': `{ "compilerOptions": { "composite": true, "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"] }`, 'src/main.tsx': `import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, )`, 'src/App.tsx': `import { useState } from 'react' import './App.css' function App() { const [count, setCount] = useState(0) return ( <div className="App"> <h1>${name}</h1> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> </div> ) } export default App`, 'src/index.css': `body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; }`, 'src/App.css': `.App { text-align: center; } button { font-size: 1.5em; }`, 'index.html': `<!doctype html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>${name}</title> </head> <body> <div id="root"></div> <script type="module" src="/src/main.tsx"></script> </body> </html>` }), 'vue-ts': (name) => ({ 'package.json': `{ "name": "${name}", "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc && vite build", "preview": "vite preview" }, "dependencies": { "vue": "^3.3.0" }, "devDependencies": { "@vitejs/plugin-vue": "^4.0.0", "typescript": "^5.0.0", "vue-tsc": "^1.0.0", "vite": "^4.4.0" } }`, 'vite.config.ts': `import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], })`, 'tsconfig.json': `{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve" }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] }`, 'src/main.ts': `import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')`, 'src/App.vue': `<template> <div id="app"> <h1>{{ message }}</h1> <button @click="count++">count is: {{ count }}</button> </div> </template> <script setup lang="ts"> import { ref } from 'vue' const message = ref('${name}') const count = ref(0) </script> <style> #app { text-align: center; } </style>`, 'index.html': `<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>${name}</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.ts"></script> </body> </html>` }) }, cli: { 'typescript': (name) => ({ 'package.json': `{ "name": "${name}", "version": "1.0.0", "description": "命令行工具", "type": "module", "bin": { "${name}": "./dist/cli.js" }, "scripts": { "build": "tsc", "dev": "ts-node src/cli.ts", "start": "node dist/cli.js" }, "dependencies": { "commander": "^11.1.0", "chalk": "^5.3.0", "inquirer": "^9.2.11" }, "devDependencies": { "@types/node": "^20.8.0", "typescript": "^5.2.2", "ts-node": "^10.9.1" } }`, 'tsconfig.json': `{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "node", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true } }`, 'src/cli.ts': `#!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; const program = new Command(); program .name('${name}') .description('命令行工具') .version('1.0.0'); program .command('hello') .description('打招呼') .action(() => { console.log(chalk.green('👋 你好!')); }); program.parse();`, 'README.md': `# ${name} 命令行工具 ## 安装 \`\`\`bash npm install \`\`\` ## 使用 \`\`\`bash npm run dev \`\`\` ## 构建 \`\`\`bash npm run build \`\`\` ` }) } }; async createProject(options) { const { name, type, language, framework = 'none' } = options; const projectPath = path.join(process.cwd(), name); // 检查项目是否已存在 if (await fs.pathExists(projectPath)) { throw new Error(`项目目录 ${name} 已存在`); } // 获取模板 const templateKey = framework === 'none' ? language : `${language}-${framework}`; const typeTemplates = this.projectTemplates[type]; const template = typeTemplates?.[templateKey]; if (!template) { throw new Error(`不支持的项目类型: ${type} 语言: ${language} 框架: ${framework}`); } // 创建项目目录 await fs.ensureDir(projectPath); // 生成模板文件 const templateFiles = template(name); const createdFiles = []; for (const [filePath, content] of Object.entries(templateFiles)) { const fullPath = path.join(projectPath, filePath); await fs.ensureDir(path.dirname(fullPath)); await fs.writeFile(fullPath, content, 'utf8'); createdFiles.push(filePath); } return { projectPath, files: createdFiles }; } } //# sourceMappingURL=scaffolder.js.map