@yorha2b-lab/autodev 5.5.1 → 5.6.0

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/bin/autodev.js CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env node
2
- const fs = require('fs')
3
2
  const path = require('path')
4
3
  const chalk = require('chalk')
5
4
  const pkg = require('../package.json')
6
5
  const { program } = require('commander')
6
+ const local = Intl.DateTimeFormat().resolvedOptions().locale.toUpperCase()
7
7
 
8
- const { discover } = require('../src/utils/foundation')
9
- const { local, matrixEffect, bootSequence } = require('../src/utils/ux')
10
-
11
- const dialogs = discover(path.join(__dirname, '../src/dialogs'))
8
+ const { matrixEffect } = require('../src/utils/ux')
9
+ const recruiter = require('../src/kernel/recruiter')
10
+ const dialogs = recruiter(path.join(__dirname, '../src/dialogs'))
12
11
  const dialog = dialogs[local] ?? dialogs['EN-US']
13
12
 
14
13
  program
@@ -22,23 +21,12 @@ program
22
21
  .description(dialog.bunker.initDesc)
23
22
  .action(() => {
24
23
  const battlefield = {
25
- dirs: [],
26
24
  files: [
27
25
  { from: '.env.example', to: '.env', exist: 'envCheck', success: 'envCopy' },
28
26
  { from: 'config.js', to: 'config.js', exist: 'configCheck', success: 'configCopy' }
29
27
  ]
30
28
  }
31
- const installer = {
32
- dirs: dir => fs.mkdirSync(path.join(process.cwd(), dir), { recursive: true }),
33
- files: ({ from, to, exist, success }) => {
34
- const target = path.join(process.cwd(), to)
35
- if (fs.existsSync(target)) {
36
- return console.log(chalk.yellow(dialog.bunker[exist]))
37
- }
38
- fs.copyFileSync(path.resolve(__dirname, `../${from}`), target)
39
- console.log(chalk.green(dialog.bunker[success]))
40
- }
41
- }
29
+ const installer = require('../src/utils/installer')({ root: process.cwd(), dialog })
42
30
  Object.entries(battlefield).forEach(([type, items]) => items.forEach(installer[type]))
43
31
  const bunkerCmd = chalk.yellow(`'bunker': 'autodev watch'`)
44
32
  console.log(chalk.cyan(dialog.bunker.initComplete(bunkerCmd)))
@@ -50,11 +38,10 @@ program
50
38
  .description(dialog.bunker.watchDesc)
51
39
  .action(async () => {
52
40
  const bunker = require('../src/bootstrap')
53
- const result = bunker.init(program.opts(), dialog)
41
+ const result = await bunker.init(program.opts(), dialog, pkg.version, local)
54
42
  if (!result) {
55
43
  return
56
44
  }
57
- await bootSequence(pkg.version)
58
45
  require('../src/commands/watch')()
59
46
  process.on('SIGINT', () => {
60
47
  result.yorha.commander.report(dialog.bunker.systemOffline, 'gray')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "5.5.1",
3
+ "version": "5.6.0",
4
4
  "description": "基于视觉大模型的前端(react+Antd)全自动 CRUD 代码生成器",
5
5
  "bin": {
6
6
  "autodev": "bin/autodev.js"
@@ -32,13 +32,13 @@ const askAI = async ({ model, yorha, dialog, openAI, messages, response_format =
32
32
  }
33
33
  }
34
34
 
35
- module.exports = ({ config, yorha, openAI, dialog, prompts }) => {
35
+ module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
36
36
 
37
37
  const sharp = require('sharp')
38
38
 
39
- const { textModel, visionModel } = config
40
39
  const { api, mock, system, council } = prompts
41
40
  const { UI_DESIGNER, API_DESIGNER, MOCK_DESIGNER } = system
41
+ const { textModel, visionModel } = utils.foundation.getConfig()
42
42
 
43
43
  return {
44
44
  generateMock: async ({ columns, fileName }) => {
@@ -4,8 +4,8 @@ module.exports = () => {
4
4
  const path = require('path')
5
5
  const OpenAI = require('openai')
6
6
 
7
- const pkgEnv = path.resolve(__dirname, '../.env')
8
- const localEnv = path.resolve(process.cwd(), '.env')
7
+ const pkgEnv = path.resolve(__dirname, '../../.env')
8
+ const localEnv = path.resolve(process.cwd(), 'bunker', '.env')
9
9
  require('dotenv').config({ path: fs.existsSync(localEnv) ? localEnv : pkgEnv })
10
10
  const openAI = new OpenAI({ apiKey: process.env.API_KEY, baseURL: process.env.BASE_URL })
11
11
 
@@ -5,9 +5,11 @@ let instance = null
5
5
 
6
6
  module.exports = {
7
7
 
8
- init: ({ template }, dialog) => {
8
+ init: async ({ template }, dialog, version, local) => {
9
9
 
10
- const yorha = require('./yorha')()
10
+ const recruiter = require('../kernel/recruiter')
11
+ const utils = recruiter(path.join(__dirname, '../utils'))
12
+ const yorha = utils.ux.yorha()
11
13
 
12
14
  const generatorPath = path.join(__dirname, `../framework/${template}/generator`)
13
15
  if (!fs.existsSync(generatorPath)) {
@@ -15,27 +17,24 @@ module.exports = {
15
17
  return
16
18
  }
17
19
 
18
- const foundation = require('../utils/foundation')
19
- const config = foundation.getConfig()
20
-
21
20
  const accessPoint = {
21
+ utils,
22
22
  yorha,
23
- config,
24
23
  dialog,
25
24
  template,
26
- foundation,
27
- openAI: require('./openai')(),
28
- core: require('../utils/core'),
29
- menus: foundation.getExistingMenus(),
30
- handlers: foundation.discover(path.join(__dirname, '../handlers')),
31
- handlebars: require('./handlebar')({ config, template, ...foundation }),
32
- prompts: foundation.discover(path.join(__dirname, `../framework/${template}/prompts`)),
25
+ openAI: require('../ai/openai')(),
26
+ engine: require('../kernel/engine')({ utils, template }),
27
+ handlers: recruiter(path.join(__dirname, '../handlers')),
28
+ prompts: recruiter(path.join(__dirname, `../framework/${template}/prompts`)),
33
29
  }
34
30
 
35
- require('./foundation')(accessPoint)
36
- const llm = require('../services/llm')(accessPoint)
31
+ if (version) {
32
+ await utils.ux.bootSequence(version, local)
33
+ }
34
+ require('../kernel/deployment')(accessPoint)
35
+ const llm = require('../ai')(accessPoint)
37
36
  const generator = require(generatorPath)(accessPoint)
38
- const labs = require('./labs')({ llm, ...accessPoint })
37
+ const labs = require('../labs')({ llm, ...accessPoint })
39
38
 
40
39
 
41
40
  instance = { llm, labs, generator, ...accessPoint }
@@ -55,7 +54,7 @@ module.exports = {
55
54
 
56
55
  reboot() {
57
56
  const { template, dialog } = instance
58
- delete require.cache[require.resolve(path.join(process.cwd(), 'config.js'))]
57
+ delete require.cache[require.resolve(path.join(process.cwd(), 'bunker', 'config.js'))]
59
58
  instance.labs?.tower?.close()
60
59
  instance = null
61
60
  return this.init({ template }, dialog)
@@ -5,16 +5,17 @@ module.exports = () => {
5
5
  const chokidar = require('chokidar')
6
6
 
7
7
  const bootstrap = require('../bootstrap')
8
- const ctx = bootstrap.get()
8
+ const queue = require('../kernel/queue')(2)
9
9
 
10
- const queue = ctx.core.createTaskQueue(2)
10
+ const ctx = bootstrap.get()
11
11
  const routes = Object.values(ctx.handlers)
12
+ const config = ctx.utils.foundation.getConfig()
12
13
 
13
14
  queue.onIdle(() => {
14
15
  ctx.yorha.pod042.report(ctx.dialog.pod042.queueEmpty)
15
- const utilsDir = path.join(process.cwd(), ctx.config.utilsDir)
16
+ const utilsDir = path.join(process.cwd(), config.utilsDir)
16
17
  if (!fs.existsSync(utilsDir)) fs.mkdirSync(utilsDir, { recursive: true })
17
- fs.writeFileSync(path.join(utilsDir, 'menus.js'), `export const menus = ${ctx.foundation.contextStringify({ context: ctx.menus, maxLength: 50 })}`)
18
+ fs.writeFileSync(path.join(utilsDir, 'menus.js'), `export const menus = ${ctx.utils.generator.contextStringify({ context: ctx.menus, maxLength: 50 })}`)
18
19
  ctx.yorha.commander.report(ctx.dialog.bunker.systemStandby, 'gray')
19
20
  })
20
21
 
@@ -25,8 +26,8 @@ module.exports = () => {
25
26
  awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
26
27
  }
27
28
 
28
- const fileWatcher = chokidar.watch(['./config.js'], options)
29
- const dirWatcher = chokidar.watch(routes.map(route => `${route.watch}`), options)
29
+ const fileWatcher = chokidar.watch(['./bunker/config.js'], options)
30
+ const dirWatcher = chokidar.watch(routes.map(route => `./bunker/${route.watch}`), options)
30
31
 
31
32
  ctx.yorha.operator6O.report(ctx.dialog.operator6O.call2B)
32
33
 
@@ -1,9 +1,8 @@
1
- module.exports = ({ core, config, handlebars, foundation }) => {
1
+ module.exports = ({ utils, engine }) => {
2
2
 
3
- const { contextStringify } = foundation
4
- const { indexTpl, resourceTpl } = handlebars
5
- const { needMock, responseSuccess } = config
6
- const { cleanCode, generateSmartImports, formatFormItemAndColumns } = core
3
+ const { indexTpl, resourceTpl } = engine
4
+ const { needMock, responseSuccess } = utils.foundation.getConfig()
5
+ const { cleanCode, contextStringify, generateSmartImports, formatFormItemAndColumns } = utils.generator
7
6
 
8
7
  return {
9
8
  index: ({ fileName, pageConfig }) => {
@@ -6,12 +6,13 @@ module.exports = {
6
6
  async handle(filePath, liveResponse = null) {
7
7
 
8
8
  const chalk = require('chalk')
9
- const { llm, core, labs, yorha, dialog, config } = require('../bootstrap').get()
9
+ const { llm, utils, labs, yorha, dialog } = require('../bootstrap').get()
10
10
 
11
- const { pagesDir } = config
12
11
  const { nineS, pod153 } = yorha
13
12
  const { alignResponseFields } = llm
14
- const { cleanCode, unwrapSignal } = core
13
+ const { cleanCode } = utils.generator
14
+ const { unwrapSignal } = utils.semantic
15
+ const { pagesDir } = utils.foundation.getConfig()
15
16
 
16
17
  const startTime = Date.now()
17
18
  const fileName = liveResponse?.fileName ?? path.basename(filePath, path.extname(filePath))
@@ -5,17 +5,18 @@ module.exports = {
5
5
  watch: 'screenShot',
6
6
  async handle(filePath) {
7
7
 
8
- const { llm, menus, yorha, dialog, config, prompts, template, generator, foundation } = require('../bootstrap').get()
8
+ const { llm, utils, yorha, dialog, prompts, template, generator } = require('../bootstrap').get()
9
9
 
10
10
  const { page } = prompts
11
11
  const { nineS, pod042 } = yorha
12
12
  const { index, resource } = generator
13
- const { contextStringify } = foundation
14
13
  const { generateMock, recognizePage } = llm
15
- const { useDemo, pagesDir, needMock } = config
14
+ const { contextStringify } = utils.generator
15
+ const { useDemo, pagesDir, needMock } = utils.foundation.getConfig()
16
16
 
17
17
 
18
18
  const startTime = Date.now()
19
+ const menus = utils.foundation.getExistingMenus(pagesDir)
19
20
  const fileName = path.basename(filePath, path.extname(filePath))
20
21
  const mockDir = path.join(process.cwd(), 'mock')
21
22
  const targetDir = path.join(process.cwd(), pagesDir, fileName)
@@ -5,13 +5,12 @@ module.exports = {
5
5
  async handle(filePath) {
6
6
 
7
7
  const chalk = require('chalk')
8
- const { llm, core, yorha, dialog, prompts, foundation } = require('../bootstrap').get()
8
+ const { llm, utils, yorha, dialog, prompts } = require('../bootstrap').get()
9
9
 
10
10
  const { pod042 } = yorha
11
11
  const { recognizePage } = llm
12
12
  const { part: prompt } = prompts
13
- const { contextStringify } = foundation
14
- const { cleanCode, formatFormItemAndColumns } = core
13
+ const { cleanCode, contextStringify, formatFormItemAndColumns } = utils.generator
15
14
 
16
15
  const startTime = Date.now()
17
16
 
@@ -41,7 +40,7 @@ module.exports = {
41
40
  console.log(chalk.magenta(`\n┌────────────────── [ YoRHa Construction Output ] ─────────────────┐`))
42
41
  console.log(chalk.magenta(`│ Source: ${filePath}`))
43
42
  console.log(chalk.magenta(`│ Protocol: Partial UI Fragment | Status: SUCCESS`))
44
- console.log(chalk.magenta(`├───────────────────────────────────────────────────────────────────┘`))
43
+ console.log(chalk.magenta(`├──────────────────────────────────────────────────────────────────┘`))
45
44
  console.log(chalk.white(finalResult))
46
45
  pod042.success(spinner, dialog.pod042.partialConstruction((endTime - startTime) / 1000))
47
46
  pod042.success(spinner, dialog.pod042.partialRecommendation)
@@ -1,14 +1,14 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
- module.exports = ({ yorha, config, dialog, handlers, template, foundation }) => {
4
+ module.exports = ({ utils, yorha, dialog, handlers, template }) => {
5
5
 
6
- const { copyTemplateDir } = foundation
7
- const { hbsDir, hooksDir, utilsDir, componentsDir } = config
6
+ const { copyTemplateDir } = utils.foundation
7
+ const { hbsDir, hooksDir, utilsDir, componentsDir } = utils.foundation.getConfig()
8
8
 
9
9
  try {
10
- Object.values(handlers).forEach(handler => fs.mkdirSync(path.join(process.cwd(), handler.watch), { recursive: true }))
11
- if (hbsDir === '') {
10
+ Object.values(handlers).forEach(handler => fs.mkdirSync(path.join(process.cwd(), 'bunker', handler.watch), { recursive: true }))
11
+ if (!hbsDir) {
12
12
  copyTemplateDir(template, 'hooks', hooksDir)
13
13
  copyTemplateDir(template, 'utils', utilsDir)
14
14
  copyTemplateDir(template, 'components', componentsDir)
@@ -1,15 +1,15 @@
1
- module.exports = ({ config, template, contextStringify }) => {
1
+ module.exports = ({ utils, template }) => {
2
2
 
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
5
  const Handlebars = require('handlebars')
6
6
 
7
7
  Handlebars.registerHelper('raw', opt => opt.fn())
8
- Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(contextStringify({ context, maxLength })) : '[]')
8
+ Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(utils.generator.contextStringify({ context, maxLength })) : '[]')
9
9
 
10
- const { hbsDir } = config
11
- const tplDir = hbsDir !== '' ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars`)
12
- const partialsDir = hbsDir !== '' ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars/partials`)
10
+ const { hbsDir } = utils.foundation.getConfig()
11
+ const tplDir = !!hbsDir ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars`)
12
+ const partialsDir = !!hbsDir ? path.join(process.cwd(), hbsDir, 'partials') : path.join(__dirname, `../framework/${template}/handlebars/partials`)
13
13
 
14
14
  fs.readdirSync(partialsDir).forEach(file => {
15
15
  if (file.endsWith('.hbs')) {
@@ -0,0 +1,36 @@
1
+ module.exports = (concurrency = 1) => {
2
+
3
+ let running = 0
4
+ let queue = []
5
+ let onIdleCallback = null
6
+
7
+ const next = async () => {
8
+
9
+ if (running >= concurrency || queue.length === 0) return
10
+
11
+ const task = queue.shift()
12
+ running++
13
+
14
+ try {
15
+ await task()
16
+ } catch (err) {
17
+ console.error('任务执行异常:', err)
18
+ } finally {
19
+ running--
20
+ next()
21
+ if (running === 0 && queue.length === 0 && onIdleCallback) {
22
+ onIdleCallback()
23
+ }
24
+ }
25
+ }
26
+
27
+ return {
28
+ add: (task) => {
29
+ queue.push(task)
30
+ next()
31
+ },
32
+ onIdle: (callback) => {
33
+ onIdleCallback = callback
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auto CRUD Copilot - YoRHa Bunker Construction System
3
+ * This file is part of AutoDev.
4
+ * AutoDev is free software: you can redistribute it and/or modify
5
+ * it under the terms of the GNU Affero General Public License as
6
+ * published by the Free Software Foundation, either version 3 of the
7
+ * License, or (at your option) any later version.
8
+ * (c) 2026 [yorha2b-lab]. Glory to Mankind.
9
+ */
10
+
11
+ const fs = require('fs')
12
+ const path = require('path')
13
+
14
+ module.exports = dir => Object.fromEntries(fs.readdirSync(dir).filter(file => file.endsWith('.js')).map(file => [path.basename(file, '.js'), require(path.join(dir, file))]))
@@ -1,13 +1,13 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
- module.exports = async ({ llm, core, yorha, dialog, config, foundation }) => {
4
+ module.exports = async ({ llm, utils, yorha, dialog }) => {
5
5
 
6
6
  const { apiParser } = llm
7
7
  const { pod153, commander } = yorha
8
- const { apiDoc, pagesDir } = config
9
- const { contextStringify } = foundation
10
- const { getLocalScore, getSemanticKeywords } = core
8
+ const { contextStringify } = utils.generator
9
+ const { apiDoc, pagesDir } = utils.foundation.getConfig()
10
+ const { getLocalScore, getSemanticKeywords } = utils.semantic
11
11
 
12
12
  if (!apiDoc) return
13
13
 
@@ -36,7 +36,6 @@ module.exports = async ({ llm, core, yorha, dialog, config, foundation }) => {
36
36
 
37
37
  if (fs.existsSync(indexPath) && fs.existsSync(resourcePath)) {
38
38
  let indexCode = fs.readFileSync(indexPath, 'utf-8')
39
- let resourceCode = fs.readFileSync(resourcePath, 'utf-8')
40
39
 
41
40
  // 💡 锁定待通电锚点
42
41
  if (indexCode.includes('BUNKER_API_ANCHOR')) {
@@ -2,7 +2,7 @@ module.exports = accessPoint => {
2
2
 
3
3
  const labs = {}
4
4
 
5
- const { apiDoc, needMock, proxyTarget, enableAutoAlignment } = accessPoint.config
5
+ const { apiDoc, needMock, proxyTarget, enableAutoAlignment } = accessPoint.utils.foundation.getConfig()
6
6
 
7
7
  if (!needMock && proxyTarget && enableAutoAlignment) {
8
8
  labs.tower = require('../labs/tower')(accessPoint)
package/src/labs/tower.js CHANGED
@@ -3,15 +3,15 @@
3
3
  * @description [地堡 42153 联合波段] 启动流量拦截塔。
4
4
  * 独立运行于后台,监听 42153 端口,物理截获联调过程中的 JSON 信号并触发语义对齐。
5
5
  */
6
- module.exports = ({ core, yorha, dialog, config, handlers }) => {
6
+ module.exports = ({ utils, yorha, dialog, handlers }) => {
7
7
 
8
8
  const http = require('http')
9
9
  const zlib = require('zlib')
10
10
  const httpProxy = require('http-proxy')
11
11
 
12
12
  const { pod153, commander } = yorha
13
- const { unwrapSignal, isQuerySignal } = core
14
- const { routeMap = {}, proxyTarget } = config
13
+ const { unwrapSignal, isQuerySignal } = utils.semantic
14
+ const { routeMap = {}, proxyTarget } = utils.foundation.getConfig()
15
15
 
16
16
  const TOWER_PORT = 42153
17
17
  const hackedRegistry = new Map()
@@ -45,7 +45,7 @@ module.exports = ({ core, yorha, dialog, config, handlers }) => {
45
45
  const urlPath = new URL(referer).pathname
46
46
  const fileName = routeMap?.[urlPath] ?? urlPath.split('/').filter(Boolean).at(-1)
47
47
 
48
- const fingerprint = getJsonFingerprint(unwrapSignal(json)) // 获取数据指纹
48
+ const fingerprint = getJsonFingerprint(coreData) // 获取数据指纹
49
49
  const lastFingerprint = hackedRegistry.get(fileName)
50
50
 
51
51
  // 如果指纹没变,说明字段结构是一样的,无需再次骇入
@@ -1,37 +1,17 @@
1
- /**
2
- * This file is part of AutoDev.
3
- * AutoDev is free software: you can redistribute it and/or modify
4
- * it under the terms of the GNU Affero General Public License as
5
- * published by the Free Software Foundation, either version 3 of the
6
- * License, or (at your option) any later version.
7
- *
8
- */
9
-
10
1
  const fs = require('fs')
11
2
  const path = require('path')
12
- const stringify = require('json-stringify-pretty-compact')
13
3
 
14
- /**
15
- * 获取项目配置信息
16
- * 优先读取项目根目录的 config.js,如果不存在则使用默认配置
17
- * @returns {Object} 合并后的配置对象
18
- */
19
4
  const getConfig = () => {
20
- const localConfigPath = path.join(process.cwd(), 'config.js') // 项目根目录配置
5
+ const localConfigPath = path.join(process.cwd(), 'bunker', 'config.js') // 项目配置
21
6
  const defaultConfigPath = path.resolve(__dirname, '../../config.js') // 默认配置
22
7
  let config = require(defaultConfigPath)
23
8
  if (fs.existsSync(localConfigPath)) {
24
9
  const userConfig = require(localConfigPath)
25
- config = { ...config, ...userConfig } // 合并用户自定义配置
10
+ config = { ...config, ...userConfig }
26
11
  }
27
12
  return config
28
13
  }
29
14
 
30
- /**
31
- * 获取已有的页面菜单配置
32
- * @param {string} dir - 页面目录路径(默认 'src/pages')
33
- * @returns {Array} 菜单数组 [{ label: '页面名', key: '页面名' }]
34
- */
35
15
  const getExistingMenus = (dir = 'src/pages') => {
36
16
  const pagesDir = path.join(process.cwd(), dir)
37
17
  if (!fs.existsSync(pagesDir)) return []
@@ -40,12 +20,6 @@ const getExistingMenus = (dir = 'src/pages') => {
40
20
  .map(file => ({ label: file, key: file }))
41
21
  }
42
22
 
43
- /**
44
- * 复制模板目录到目标项目
45
- * @param {string} template - 框架名称
46
- * @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
47
- * @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
48
- */
49
23
  const copyTemplateDir = (template, templateSubDir, targetSubDir) => {
50
24
  const targetDir = path.join(process.cwd(), targetSubDir)
51
25
  const sourceDir = path.join(__dirname, `../framework/${template}/${templateSubDir}`)
@@ -60,8 +34,8 @@ const copyTemplateDir = (template, templateSubDir, targetSubDir) => {
60
34
  })
61
35
  }
62
36
 
63
- const contextStringify = ({ context, indent = 4, maxLength = 200 }) => stringify.default(context, { indent, maxLength })
64
-
65
- const discover = dir => Object.fromEntries(fs.readdirSync(dir).filter(file => file.endsWith('.js')).map(file => [path.basename(file, '.js'), require(path.join(dir, file))]))
66
-
67
- module.exports = { discover, getConfig, getExistingMenus, copyTemplateDir, contextStringify }
37
+ module.exports = {
38
+ getConfig,
39
+ copyTemplateDir,
40
+ getExistingMenus,
41
+ }
@@ -0,0 +1,98 @@
1
+ const stringify = require('json-stringify-pretty-compact')
2
+
3
+ const cleanCode = str => str
4
+ .replace(/```[a-z]*\n?/gi, '')// 物理超度 Markdown 糖衣(解决```json 报错)
5
+ .replace(/```/g, '')
6
+ .replace(/\\"/g, '"') // 逻辑自愈 处理 AI 错误转义的引号(把 \" 还原回 ")
7
+ .replace(/['"]?_CODE_([\s\S]*?)_CODE_['"]?/g, '$1') // 去掉 _CODE_ 包裹的代码
8
+ .replace(/_CODE_/g, '') // 兜底清理
9
+ .replace(/"(\w+)":/g, '$1:') // 去掉 key 的双引号
10
+ .replace(/"/g, "'") // 双引号全部转单引号
11
+ .replace(/[ \t]+$/gm, '') // 去除每一行行尾的多余空格
12
+ .replace(/\n{3,}/g, '\n\n') // 将3个或以上的换行符压缩成2个换行符
13
+ .replace(/^\s+/, '') // 去掉文件头部的空行
14
+ .trim() + '\n'
15
+
16
+ const formatFormItemAndColumns = ({ pageConfig }) => {
17
+
18
+ const codePresets = {
19
+ money: 'text => moneyRender(text)',
20
+ date: 'text => timeRender({time: text})',
21
+ index: '(_, record, index) => index + 1',
22
+ tag: `text => <Tag color='magenta'>{text}</Tag>`,
23
+ badge: `text => <Badge status='success' text={text} />`,
24
+ enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label ?? text`,
25
+ }
26
+
27
+ const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
28
+
29
+ const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
30
+ const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
31
+ const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
32
+
33
+ const formItems = pageConfig.formItems?.map(item => {
34
+ if (item.type === 'text') {
35
+ delete item.type
36
+ }
37
+ return {
38
+ ...item,
39
+ ...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
40
+ }
41
+ })
42
+
43
+ const processedColumns = columns?.map(col => {
44
+ if (col.type === 'text') {
45
+ delete col.type
46
+ }
47
+ if (['image'].includes(col.type)) {
48
+ return { ...col, renderAction: true }
49
+ }
50
+ if (col.type && codePresets[col.type]) {
51
+ const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
52
+ return { ...col, render: `_CODE_${renderCode}_CODE_` }
53
+ }
54
+ return col
55
+ })
56
+
57
+ return { formItems, dictBlocks, processedColumns }
58
+ }
59
+
60
+ const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
61
+
62
+ const hooksLib = ['useTableQuery']
63
+ const utilsLib = ['timeRender', 'moneyRender']
64
+ const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
65
+ const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
66
+ const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
67
+
68
+ const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
69
+ const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
70
+ const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
71
+ const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
72
+ const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
73
+
74
+ const imports = [
75
+ usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
76
+ usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
77
+ ...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
78
+ ...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
79
+ ...(module === 'index' ? [
80
+ `import { request } from '../../utils/request'`,
81
+ `import { formatQuery } from '../../utils/utils'`,
82
+ `import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
83
+ ] : [
84
+ usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
85
+ ]),
86
+ ].sort((a, b) => a.length - b.length)
87
+
88
+ return imports.filter(Boolean).join('\n')
89
+ }
90
+
91
+ const contextStringify = ({ context, indent = 4, maxLength = 200 }) => stringify.default(context, { indent, maxLength })
92
+
93
+ module.exports = {
94
+ cleanCode,
95
+ contextStringify,
96
+ generateSmartImports,
97
+ formatFormItemAndColumns,
98
+ }
@@ -0,0 +1,15 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const chalk = require('chalk')
4
+
5
+ module.exports = ({ root, dialog }) => ({
6
+ dirs: dir => fs.mkdirSync(path.join(root, dir), { recursive: true }),
7
+ files: ({ from, to, exist, success }) => {
8
+ const target = path.join(root, 'bunker', to)
9
+ if (fs.existsSync(target)) {
10
+ return console.log(chalk.yellow(dialog.bunker[exist]))
11
+ }
12
+ fs.cpSync(path.resolve(__dirname, `../../${from}`), target, { recursive: true })
13
+ console.log(chalk.green(dialog.bunker[success]))
14
+ }
15
+ })
@@ -0,0 +1,58 @@
1
+ const { Segment, useDefault } = require('segmentit')
2
+ const segmentit = useDefault(new Segment())
3
+
4
+ const unwrapSignal = json => {
5
+ if (Array.isArray(json)) return json
6
+ const dataKeys = ['data', 'list', 'items', 'datas', 'rows', 'result', 'payload', 'results', 'dataList']
7
+ for (const key of dataKeys) {
8
+ if (json[key] && Array.isArray(json[key])) return json[key]
9
+ }
10
+ for (const key in json) {
11
+ if (json[key] && typeof json[key] === 'object') {
12
+ const nested = unwrapSignal(json[key])
13
+ if (Array.isArray(nested)) return nested
14
+ }
15
+ }
16
+ return null
17
+ }
18
+
19
+ const getSemanticKeywords = text => {
20
+ const content = Array.isArray(text) ? text.join(' ') : (text || '')
21
+ const result = segmentit.doSegment(content, {
22
+ simple: true, // 开启简易模式,提升吞吐效率
23
+ stripPunctuation: true // 过滤掉所有的标点符号
24
+ })
25
+ return Array.from(new Set(result.filter(word => word.length >= 2 && !/^[0-9]+$/.test(word))))
26
+ }
27
+
28
+ const isQuerySignal = (req, json, coreData) => {
29
+ const url = req.url.toLowerCase()
30
+ const actionKeywords = ['add', 'delete', 'update', 'save', 'remove', 'edit', 'insert', 'create', 'export', 'upload']
31
+ if (actionKeywords.some(key => url.includes(key))) return false
32
+ const hasListData = Array.isArray(coreData) && coreData.length > 0
33
+ const hasPaginationFingerprint = ['total', 'records', 'page', 'size', 'count'].some(key => {
34
+ const k = key.toLowerCase()
35
+ return Object.keys(json).some(rawKey => rawKey.toLowerCase().includes(k))
36
+ })
37
+ return hasListData || hasPaginationFingerprint
38
+ }
39
+
40
+ const getLocalScore = (api, pageKeywords, moduleName) => {
41
+ let score = 0
42
+ const path = api.path.toLowerCase()
43
+ const desc = api.desc.toLowerCase()
44
+ const mod = moduleName.toLowerCase()
45
+ if (path.includes(mod)) score += 50
46
+ pageKeywords.forEach(word => {
47
+ if (desc.includes(word.toLowerCase())) score += 10
48
+ if (path.includes(word.toLowerCase())) score += 5
49
+ })
50
+ return score
51
+ }
52
+
53
+ module.exports = {
54
+ unwrapSignal,
55
+ isQuerySignal,
56
+ getLocalScore,
57
+ getSemanticKeywords,
58
+ }
package/src/utils/ux.js CHANGED
@@ -1,21 +1,45 @@
1
- /**
2
- * (c) 2026 [yorha2b-lab]. Glory to Mankind.
3
- *
4
- */
5
-
1
+ const ora = require('ora')
6
2
  const chalk = require('chalk')
7
3
  const figlet = require('figlet')
8
4
 
9
- const local = Intl.DateTimeFormat().resolvedOptions().locale.toUpperCase()
10
-
11
5
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
12
6
 
13
- /**
14
- * 引导序列
15
- * 模拟系统引导过程中的动画效果
16
- * @returns {void}
17
- */
18
- const bootSequence = async (version) => {
7
+ const yorha = () => {
8
+
9
+ const createMember = ({ name }) => ({
10
+ start(text) {
11
+ const spinner = ora({ text: chalk.cyan(`${name} ${text}\n`) }).start()
12
+ return spinner
13
+ },
14
+ fail(spinner, text) {
15
+ spinner.fail(chalk.red(`${name} ${text}\n`))
16
+ },
17
+ report(text, color = 'yellow') {
18
+ console.log(chalk[color](`${name} ${text}\n`))
19
+ },
20
+ update(spinner, text) {
21
+ spinner.text = chalk.cyan(`${name} ${text}\n`)
22
+ },
23
+ warning(spinner, text) {
24
+ spinner.warn(chalk.yellow(`${name} ${text}\n`))
25
+ },
26
+ success(spinner, text) {
27
+ spinner.succeed(chalk.green(`${name} ${text}\n`))
28
+ },
29
+ })
30
+
31
+ return {
32
+ nineS: createMember({ name: '[YoRHa::9S]' }),
33
+ commander: createMember({ name: '[BUNKER]' }),
34
+ pod042: createMember({ name: '[YoRHa::Pod042]' }),
35
+ pod153: createMember({ name: '[YoRHa::Pod153]' }),
36
+ operator6O: createMember({ name: '[YoRHa::6O]' }),
37
+ }
38
+
39
+ }
40
+
41
+ const bootSequence = async (version, local) => {
42
+
19
43
  const lines = [
20
44
  chalk.cyan(figlet.textSync('AutoDev', { horizontalLayout: 'full' })),
21
45
  chalk.gray('[BUNKER] Booting System...'),
@@ -27,18 +51,13 @@ const bootSequence = async (version) => {
27
51
  chalk.white('[BUNKER] ') + chalk.magenta('Glory to mankind. (人类荣光永存)'),
28
52
  chalk.gray('--------------------------------------------------')
29
53
  ]
54
+
30
55
  for (const line of lines) {
31
56
  console.log(line)
32
57
  await sleep(line.includes('AutoDev') ? 300 : 80)
33
58
  }
34
59
  }
35
60
 
36
- /**
37
- * 矩阵效果
38
- * 模拟数据物理封存过程中的矩阵效果
39
- * @param {number} duration - 持续时间(毫秒)
40
- * @returns {void}
41
- */
42
61
  const matrixEffect = async (duration = 1500, dialog) => {
43
62
 
44
63
  let currentTotal = 0
@@ -109,4 +128,8 @@ const matrixEffect = async (duration = 1500, dialog) => {
109
128
  }, 40)
110
129
  }
111
130
 
112
- module.exports = { local, matrixEffect, bootSequence }
131
+ module.exports = {
132
+ yorha,
133
+ bootSequence,
134
+ matrixEffect
135
+ }
@@ -1,36 +0,0 @@
1
- module.exports = () => {
2
-
3
- const ora = require('ora')
4
- const chalk = require('chalk')
5
-
6
- const createMember = ({ name }) => ({
7
- start(text) {
8
- const spinner = ora({ text: chalk.cyan(`${name} ${text}\n`) }).start()
9
- return spinner
10
- },
11
- fail(spinner, text) {
12
- spinner.fail(chalk.red(`${name} ${text}\n`))
13
- },
14
- report(text, color = 'yellow') {
15
- console.log(chalk[color](`${name} ${text}\n`))
16
- },
17
- update(spinner, text) {
18
- spinner.text = chalk.cyan(`${name} ${text}\n`)
19
- },
20
- warning(spinner, text) {
21
- spinner.warn(chalk.yellow(`${name} ${text}\n`))
22
- },
23
- success(spinner, text) {
24
- spinner.succeed(chalk.green(`${name} ${text}\n`))
25
- },
26
- })
27
-
28
- return {
29
- nineS: createMember({ name: '[YoRHa::9S]' }),
30
- commander: createMember({ name: '[BUNKER]' }),
31
- pod042: createMember({ name: '[YoRHa::Pod042]' }),
32
- pod153: createMember({ name: '[YoRHa::Pod153]' }),
33
- operator6O: createMember({ name: '[YoRHa::6O]' }),
34
- }
35
-
36
- }
package/src/utils/core.js DELETED
@@ -1,262 +0,0 @@
1
- /**
2
- * Auto CRUD Copilot - YoRHa Bunker Construction System
3
- *
4
- */
5
- const { Segment, useDefault } = require('segmentit')
6
- const segmentit = useDefault(new Segment())
7
- /**
8
- * 清理大模型生成的代码
9
- * 大模型输出的标准 JSON 无法携带 render: (text) => <Tag> 等箭头函数。
10
- * 我们在 Prompt 中要求大模型用 _CODE_ 占位符包裹函数字符串,
11
- * 在此处用正则剥离外层的双引号和占位符,将其还原为真正的可执行 JS 代码。
12
- * @param {string} str - 大模型返回的 JSON 字符串
13
- * @returns {string} 清理后的可执行 JS 代码
14
- */
15
- const cleanCode = str => {
16
- return str
17
- // 物理超度 Markdown 糖衣(解决```json 报错)
18
- .replace(/```[a-z]*\n?/gi, '')
19
- .replace(/```/g, '')
20
- // 逻辑自愈 处理 AI 错误转义的引号(把 \" 还原回 ")
21
- .replace(/\\"/g, '"')
22
- .replace(/['"]?_CODE_([\s\S]*?)_CODE_['"]?/g, '$1') // 去掉 _CODE_ 包裹的代码
23
- .replace(/_CODE_/g, '') // 兜底清理
24
- .replace(/"(\w+)":/g, '$1:') // 去掉 key 的双引号
25
- .replace(/"/g, "'") // 双引号全部转单引号
26
- .replace(/[ \t]+$/gm, '') // 去除每一行行尾的多余空格
27
- .replace(/\n{3,}/g, '\n\n') // 将3个或以上的换行符压缩成2个换行符
28
- .replace(/^\s+/, '') // 去掉文件头部的空行
29
- .trim() + '\n'
30
- }
31
-
32
- /**
33
- * @function unwrapSignal
34
- * @description [地堡数据脱水机] 物理扫描 JSON 结构,剥离业务外壳(code/msg/total 等)。
35
- * 目标:精准定位到核心的 Array。
36
- */
37
- const unwrapSignal = json => {
38
- if (Array.isArray(json)) return json
39
-
40
- // 💡 扫描常见的数据仓库 Key
41
- const dataKeys = ['data', 'list', 'items', 'datas', 'rows', 'result', 'payload', 'results', 'dataList']
42
- for (const key of dataKeys) {
43
- if (json[key] && Array.isArray(json[key])) return json[key]
44
- }
45
-
46
- // 💡 递归侦察:处理 data: { list: [...] } 这种二级套娃
47
- for (const key in json) {
48
- if (json[key] && typeof json[key] === 'object') {
49
- const nested = unwrapSignal(json[key])
50
- if (Array.isArray(nested)) return nested
51
- }
52
- }
53
- return null
54
- }
55
-
56
- /**
57
- * @function createTaskQueue
58
- * @description [任务队列] 创建一个并发任务队列,用于处理异步任务。
59
- * @param {number} concurrency - 并发任务数,默认 1。
60
- */
61
- const createTaskQueue = (concurrency = 1) => {
62
-
63
- let running = 0
64
- let queue = []
65
- let onIdleCallback = null
66
-
67
- const next = async () => {
68
-
69
- if (running >= concurrency || queue.length === 0) return
70
-
71
- const task = queue.shift()
72
- running++
73
-
74
- try {
75
- await task()
76
- } catch (err) {
77
- console.error('任务执行异常:', err)
78
- } finally {
79
- running--
80
- next()
81
- if (running === 0 && queue.length === 0 && onIdleCallback) {
82
- onIdleCallback()
83
- }
84
- }
85
- }
86
-
87
- return {
88
- add: (task) => {
89
- queue.push(task)
90
- next()
91
- },
92
- onIdle: (callback) => {
93
- onIdleCallback = callback
94
- }
95
- }
96
- }
97
-
98
- /**
99
- * @function isQuerySignal
100
- * @description [语义雷达]
101
- * 核心逻辑:不纠结请求是怎么发的,只看带回来的货(Response)长什么样。
102
- */
103
- const isQuerySignal = (req, json, coreData) => {
104
-
105
- const url = req.url.toLowerCase()
106
- // 💡 1. 物理红区:只要 URL 包含这些动作,无论返回什么都视为“非列表”
107
- const actionKeywords = ['add', 'delete', 'update', 'save', 'remove', 'edit', 'insert', 'create', 'export', 'upload']
108
- if (actionKeywords.some(key => url.includes(key))) return false
109
- // 💡 2. 物理金标准:脱壳后的核心物资是一个【非空数组】
110
- // 只要带回了一堆长得一样的对象,那它 99.9% 就是列表页
111
- const hasListData = Array.isArray(coreData) && coreData.length > 0
112
- // 💡 3. 语义辅助:如果响应里包含“分页指纹”
113
- // 有时候第一页刚好没数据(coreData 是空数组),但 JSON 里带有 total, page 等字段
114
- const hasPaginationFingerprint = ['total', 'records', 'page', 'size', 'count'].some(key => {
115
- const k = key.toLowerCase()
116
- // 在原始 JSON 的第一层寻找分页相关的 key
117
- return Object.keys(json).some(rawKey => rawKey.toLowerCase().includes(k))
118
- })
119
- // 结论:具备列表特征或者是分页指纹的,判定为查询信号
120
- return hasListData || hasPaginationFingerprint
121
- }
122
-
123
- /**
124
- * @function getSemanticKeywords
125
- * @description [语义精炼厂 2.0] 使用 segmentit 引擎进行物理切片。
126
- */
127
- const getSemanticKeywords = text => {
128
- // 💡 物理脱水:确保输入为纯净字符串
129
- const content = Array.isArray(text) ? text.join(' ') : (text || '')
130
- // 💡 执行分词
131
- const result = segmentit.doSegment(content, {
132
- simple: true, // 开启简易模式,提升吞吐效率
133
- stripPunctuation: true // 物理过滤掉所有的标点符号
134
- })
135
- // 💡 提取词条并执行“基因筛选”
136
- return Array.from(new Set(result.filter(word => word.length >= 2 && !/^[0-9]+$/.test(word))))
137
- }
138
-
139
-
140
- /**
141
- * @function formatFormItemAndColumns
142
- * @description [地堡逻辑转录引擎] 执行核心的语义对齐与代码物理注入协议。
143
- * 该函数负责将 AI 识别出的视觉类型标签(money/date/enum等)转化为标准的 React 渲染逻辑,
144
- * 并自动提取全频道所需的字典引用(Dictionary Blocks)。
145
- *
146
- * @param {Object} params - 构筑参数包
147
- * @param {Object} params.pageConfig - 由 Pod 042 扫描出的原始页面配置对象
148
- *
149
- * @returns {Object} 返回物理装配完成的数据包:
150
- * @returns {Array} .formItems - 已注入 Options 指令的表单零部件清单
151
- * @returns {Array} .processedColumns - 已执行逻辑注入(Render 补丁)的表格列清单
152
- * @returns {Array} .dictBlocks - 全频道去重后的字典(Options)变量名清单
153
- */
154
- const formatFormItemAndColumns = ({ pageConfig }) => {
155
-
156
- const codePresets = {
157
- money: 'text => moneyRender(text)',
158
- date: 'text => timeRender({time: text})',
159
- index: '(_, record, index) => index + 1',
160
- tag: `text => <Tag color='magenta'>{text}</Tag>`,
161
- badge: `text => <Badge status='success' text={text} />`,
162
- enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label ?? text`,
163
- }
164
-
165
- const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
166
-
167
- const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
168
- const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
169
- const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
170
-
171
- const formItems = pageConfig.formItems?.map(item => {
172
- if (item.type === 'text') {
173
- delete item.type
174
- }
175
- return {
176
- ...item,
177
- ...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
178
- }
179
- })
180
-
181
- const processedColumns = columns?.map(col => {
182
- if (col.type === 'text') {
183
- delete col.type
184
- }
185
- if (['image'].includes(col.type)) {
186
- return { ...col, renderAction: true }
187
- }
188
- if (col.type && codePresets[col.type]) {
189
- // 💡 物理注入:根据标签,强行塞入标准化的 JS 代码字符串
190
- const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
191
- return {
192
- ...col,
193
- render: `_CODE_${renderCode}_CODE_` // 重新打标,交给 cleanCode 处理
194
- }
195
- }
196
- return col
197
- })
198
-
199
- return { formItems, dictBlocks, processedColumns }
200
- }
201
-
202
- /**
203
- * @function getLocalScore
204
- * @description [战力评估协议] 计算语义重合度。
205
- */
206
- const getLocalScore = (api, pageKeywords, moduleName) => {
207
- let score = 0
208
- const path = api.path.toLowerCase()
209
- const desc = api.desc.toLowerCase()
210
- const mod = moduleName.toLowerCase()
211
-
212
- // 💡 权重 A:路径直接包含模块名 (暴击加分 +50)
213
- if (path.includes(mod)) score += 50
214
-
215
- // 💡 权重 B:描述包含模块核心词 (中量加分 +10)
216
- pageKeywords.forEach(word => {
217
- if (desc.includes(word.toLowerCase())) score += 10
218
- if (path.includes(word.toLowerCase())) score += 5
219
- })
220
-
221
- return score
222
- }
223
-
224
- /**
225
- * 生成智能导入语句
226
- * 根据代码中实际使用的依赖,自动生成对应的 import 语句
227
- * @param {string} codeStr - 生成的代码字符串
228
- * @param {boolean} hasTabs - 是否包含标签页
229
- * @returns {string} 拼接后的 import 语句
230
- */
231
- const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
232
-
233
- const hooksLib = ['useTableQuery']
234
- const utilsLib = ['timeRender', 'moneyRender']
235
- const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
236
- const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
237
- const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
238
-
239
- const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
240
- const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
241
- const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
242
- const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
243
- const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
244
-
245
- const imports = [
246
- usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
247
- usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
248
- ...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
249
- ...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
250
- ...(module === 'index' ? [
251
- `import { request } from '../../utils/request'`,
252
- `import { formatQuery } from '../../utils/utils'`,
253
- `import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
254
- ] : [
255
- usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
256
- ]),
257
- ].sort((a, b) => a.length - b.length)
258
-
259
- return imports.filter(Boolean).join('\n')
260
- }
261
-
262
- module.exports = { cleanCode, unwrapSignal, isQuerySignal, getLocalScore, createTaskQueue, getSemanticKeywords, generateSmartImports, formatFormItemAndColumns }