@yorha2b-lab/autodev 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/bin/autodev.js +13 -1
- package/package.json +1 -1
- package/src/commands/watch-api.js +4 -1
- package/src/commands/watch-page.js +2 -2
- package/src/prompts/mock.js +1 -1
- package/src/services/llm.js +2 -1
- package/src/utils/utils.js +26 -17
- package/templates/react/components/MyBaseForm.js +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
基于视觉大模型的前端(Umi+Antd)全自动 CRUD 代码生成器 🚀
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
<video src="https://github.com/user-attachments/assets/6d42fce1-c815-482b-8c6a-4a3b9415ba47.mp4" controls="controls" muted="muted" style="max-width: 100%; display: block; margin: 20px auto;">
|
|
11
|
+
</video>
|
|
11
12
|
|
|
12
13
|
拒绝重复劳动!为什么你需要 Auto CRUD Copilot?
|
|
13
14
|
告别手写 Table/Form:截图即代码,提升你的开发效率,告别烦人的复制粘贴。
|
|
@@ -170,4 +171,14 @@ A: 工具本身开源免费,但调用的 AI 模型(如 GPT-4v, Qwen-VL)可
|
|
|
170
171
|
|
|
171
172
|
---
|
|
172
173
|
|
|
173
|
-
|
|
174
|
+
## ⚖️ Disclaimer
|
|
175
|
+
|
|
176
|
+
Auto CRUD Copilot is a **fan-made, non-commercial, open-source tool**.
|
|
177
|
+
|
|
178
|
+
- The themes, names (YoRHa, 2B, etc.), and catchphrases included in this project are inspired by **NieR:Automata**, which is a trademark and copyright of **Square Enix Co., Ltd. / PlatinumGames Inc.**
|
|
179
|
+
- This project is not affiliated with, endorsed by, or representative of Square Enix in any way.
|
|
180
|
+
- Please support the original masterpiece: [NieR:Automata Official Site](https://nierautomata.square-enix-games.com/).
|
|
181
|
+
|
|
182
|
+
**Glory to Mankind.**
|
|
183
|
+
|
|
184
|
+
⭐ 如果这个项目对你有帮助,请给个Star支持一下!
|
package/bin/autodev.js
CHANGED
|
@@ -14,7 +14,7 @@ const localEnv = path.resolve(process.cwd(), '.env')
|
|
|
14
14
|
const pkgEnv = path.resolve(__dirname, '../.env')
|
|
15
15
|
|
|
16
16
|
require("dotenv").config({
|
|
17
|
-
|
|
17
|
+
path: fs.existsSync(localEnv) ? localEnv : pkgEnv
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const { program } = require('commander')
|
|
@@ -26,6 +26,18 @@ program.version('1.0.0').description('AI驱动的前端CRUD代码生成器')
|
|
|
26
26
|
|
|
27
27
|
program.option('-t, --template <type>', '指定前端框架模板', 'react')
|
|
28
28
|
|
|
29
|
+
program
|
|
30
|
+
.command('init')
|
|
31
|
+
.description('在当前目录初始化 .env 和 config.js 配置文件')
|
|
32
|
+
.action(() => {
|
|
33
|
+
const tplConfig = path.resolve(__dirname, '../config.js')
|
|
34
|
+
const tplEnv = path.resolve(__dirname, '../.env.example')
|
|
35
|
+
fs.copyFileSync(tplConfig, path.join(process.cwd(), 'config.js'))
|
|
36
|
+
fs.copyFileSync(tplEnv, path.join(process.cwd(), '.env'))
|
|
37
|
+
console.log('✅ 已生成 config.js')
|
|
38
|
+
console.log('✅ 已生成 .env')
|
|
39
|
+
})
|
|
40
|
+
|
|
29
41
|
program
|
|
30
42
|
.command('watch:page')
|
|
31
43
|
.description('监听截图,自动生成完整的增删改查页面')
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const chokidar = require('chokidar')
|
|
4
|
-
const
|
|
4
|
+
const { getConfig } = require('../utils/utils.js')
|
|
5
5
|
const { alignSwaggerFields } = require('../services/llm.js')
|
|
6
6
|
|
|
7
7
|
const watchApi = () => {
|
|
8
|
+
|
|
9
|
+
const config = getConfig()
|
|
10
|
+
|
|
8
11
|
const watcher = chokidar.watch('./swagger', {
|
|
9
12
|
persistent: true,
|
|
10
13
|
ignored: /(^|[\/\\])\../,
|
|
@@ -3,15 +3,15 @@ const ora = require('ora')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const chokidar = require('chokidar')
|
|
5
5
|
const Handlebars = require('handlebars')
|
|
6
|
-
const config = require('../../config.js')
|
|
7
6
|
const pagePrompt = require('../prompts/watch-page.js')
|
|
8
7
|
const { createTaskQueue } = require('../core/task-queue')
|
|
9
8
|
const stringify = require('json-stringify-pretty-compact')
|
|
10
9
|
const { recognizePage, generateMock } = require('../services/llm.js')
|
|
11
|
-
const { copyHooks, copyComponents, getExistingMenus } = require('../utils/utils.js')
|
|
10
|
+
const { getConfig, copyHooks, copyComponents, getExistingMenus } = require('../utils/utils.js')
|
|
12
11
|
|
|
13
12
|
const watchPage = options => {
|
|
14
13
|
|
|
14
|
+
const config = getConfig()
|
|
15
15
|
const compilerPath = path.join(__dirname, `../core/${options.template}-compiler.js`)
|
|
16
16
|
|
|
17
17
|
if (!fs.existsSync(compilerPath)) {
|
package/src/prompts/mock.js
CHANGED
package/src/services/llm.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const JSON5 = require('json5')
|
|
2
2
|
const sharp = require('sharp')
|
|
3
3
|
const OpenAI = require('openai')
|
|
4
|
-
const config = require('../../config.js')
|
|
5
4
|
const mockPrompt = require('../prompts/mock.js')
|
|
5
|
+
const { getConfig } = require('../utils/utils.js')
|
|
6
6
|
const apiPrompt = require('../prompts/watch-api.js')
|
|
7
7
|
const { UI_DESIGNER, API_DESIGNER, MOCK_DESIGNER } = require('../prompts/system.js')
|
|
8
8
|
|
|
9
|
+
const config = getConfig()
|
|
9
10
|
const openai = new OpenAI({ apiKey: process.env.API_KEY, baseURL: process.env.BASE_URL })
|
|
10
11
|
|
|
11
12
|
const askAI = async (model, messages, retryCount = 0) => {
|
package/src/utils/utils.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
-
const config = require('../../config.js')
|
|
4
3
|
|
|
4
|
+
const getConfig = () => {
|
|
5
|
+
const localConfigPath = path.join(process.cwd(), 'config.js')
|
|
6
|
+
const defaultConfigPath = path.resolve(__dirname, '../../config.js')
|
|
7
|
+
let config = require(defaultConfigPath)
|
|
8
|
+
if (fs.existsSync(localConfigPath)) {
|
|
9
|
+
const userConfig = require(localConfigPath)
|
|
10
|
+
config = { ...config, ...userConfig }
|
|
11
|
+
}
|
|
12
|
+
return config
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* 大模型输出的标准 JSON 无法携带 render: (text) => <Tag> 等箭头函数。
|
|
@@ -20,21 +29,7 @@ const cleanCode = str => {
|
|
|
20
29
|
.trim() + '\n'
|
|
21
30
|
}
|
|
22
31
|
|
|
23
|
-
const
|
|
24
|
-
const targetDir = path.join(process.cwd(), config.hooksDir)
|
|
25
|
-
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
|
|
26
|
-
const hookDir = path.join(__dirname, `../../templates/${options.template}/hooks`)
|
|
27
|
-
fs.readdirSync(hookDir).forEach(file => fs.copyFileSync(path.join(hookDir, file), path.join(targetDir, file)))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const copyComponents = options => {
|
|
31
|
-
const targetDir = path.join(process.cwd(), config.componentsDir)
|
|
32
|
-
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
|
|
33
|
-
const componentsDir = path.join(__dirname, `../../templates/${options.template}/components`)
|
|
34
|
-
fs.readdirSync(componentsDir).forEach(file => fs.copyFileSync(path.join(componentsDir, file), path.join(targetDir, file)))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const getExistingMenus = (dir = config.pagesDir) => {
|
|
32
|
+
const getExistingMenus = (dir = 'src/pages') => {
|
|
38
33
|
const pagesDir = path.join(process.cwd(), dir)
|
|
39
34
|
if (!fs.existsSync(pagesDir)) return []
|
|
40
35
|
return fs.readdirSync(pagesDir)
|
|
@@ -66,4 +61,18 @@ const generateSmartImports = (codeStr, hasTabs) => {
|
|
|
66
61
|
return imports.filter(Boolean).join('\n')
|
|
67
62
|
}
|
|
68
63
|
|
|
69
|
-
|
|
64
|
+
const copyHooks = (options, hooksDir = 'src/hooks') => {
|
|
65
|
+
const targetDir = path.join(process.cwd(), hooksDir)
|
|
66
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
|
|
67
|
+
const hookDir = path.join(__dirname, `../../templates/${options.template}/hooks`)
|
|
68
|
+
fs.readdirSync(hookDir).forEach(file => fs.copyFileSync(path.join(hookDir, file), path.join(targetDir, file)))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const copyComponents = (options, componentsDir = 'src/components') => {
|
|
72
|
+
const targetDir = path.join(process.cwd(), componentsDir)
|
|
73
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
|
|
74
|
+
const componentsDirectory = path.join(__dirname, `../../templates/${options.template}/components`)
|
|
75
|
+
fs.readdirSync(componentsDirectory).forEach(file => fs.copyFileSync(path.join(componentsDirectory, file), path.join(targetDir, file)))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { getConfig, copyHooks, cleanCode, copyComponents, getExistingMenus, generateSmartImports }
|
|
@@ -3,7 +3,7 @@ import { formNode } from './index'
|
|
|
3
3
|
|
|
4
4
|
export const MyBaseForm = ({ item, form }) => {
|
|
5
5
|
|
|
6
|
-
const renderFormContent = item => {
|
|
6
|
+
const renderFormContent = ({ item }) => {
|
|
7
7
|
|
|
8
8
|
if (!item.name) {
|
|
9
9
|
return typeof item.render === 'function' ? item.render(item) : (item.value ?? '-')
|