befly 2.3.0 → 2.3.2
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/checks/table.js +10 -0
- package/main.js +14 -5
- package/package.json +1 -1
- package/scripts/syncDb.js +5 -2
- package/utils/index.js +13 -0
package/checks/table.js
CHANGED
|
@@ -56,9 +56,19 @@ export const checkTable = async () => {
|
|
|
56
56
|
for (const { file, type } of allTableFiles) {
|
|
57
57
|
totalFiles++;
|
|
58
58
|
const fileName = path.basename(file);
|
|
59
|
+
const fileBaseName = path.basename(file, '.json');
|
|
59
60
|
const fileType = type === 'core' ? '内核' : '项目';
|
|
60
61
|
|
|
61
62
|
try {
|
|
63
|
+
// 1) 文件名小驼峰校验:必须以小写字母开头,后续可包含小写/数字,或多个 [大写+小写/数字] 片段
|
|
64
|
+
// 示例:userTable、testCustomers、common
|
|
65
|
+
const lowerCamelCaseRegex = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
66
|
+
if (!lowerCamelCaseRegex.test(fileBaseName)) {
|
|
67
|
+
Logger.error(`${fileType}表 ${fileName} 文件名必须使用小驼峰命名(例如 testCustomers.json)`);
|
|
68
|
+
// 命名不合规,直接标记为无效文件
|
|
69
|
+
throw new Error('文件命名不符合小驼峰规范');
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
// 读取并解析 JSON 文件
|
|
63
73
|
const table = await Bun.file(file).json();
|
|
64
74
|
let fileValid = true;
|
package/main.js
CHANGED
|
@@ -43,11 +43,20 @@ class Befly {
|
|
|
43
43
|
const singleCheckStart = Bun.nanoseconds();
|
|
44
44
|
|
|
45
45
|
// 导入检查模块
|
|
46
|
-
const
|
|
46
|
+
const checkModule = await import(file);
|
|
47
|
+
|
|
48
|
+
// 仅允许具名导出(以 check 开头)的检查函数
|
|
49
|
+
let checkFn = null;
|
|
50
|
+
for (const [exportName, exportValue] of Object.entries(checkModule)) {
|
|
51
|
+
if (typeof exportValue === 'function' && /^check/i.test(exportName)) {
|
|
52
|
+
checkFn = exportValue;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
47
56
|
|
|
48
|
-
//
|
|
49
|
-
if (typeof
|
|
50
|
-
const checkResult = await
|
|
57
|
+
// 执行检查函数
|
|
58
|
+
if (typeof checkFn === 'function') {
|
|
59
|
+
const checkResult = await checkFn(this.appContext);
|
|
51
60
|
const singleCheckTime = calcPerfTime(singleCheckStart);
|
|
52
61
|
|
|
53
62
|
if (checkResult === true) {
|
|
@@ -59,7 +68,7 @@ class Befly {
|
|
|
59
68
|
}
|
|
60
69
|
} else {
|
|
61
70
|
const singleCheckTime = calcPerfTime(singleCheckStart);
|
|
62
|
-
Logger.warn(`文件 ${fileName}
|
|
71
|
+
Logger.warn(`文件 ${fileName} 未找到可执行的检查函数(必须具名导出以 check 开头的函数),耗时: ${singleCheckTime}`);
|
|
63
72
|
failedChecks++;
|
|
64
73
|
}
|
|
65
74
|
} catch (error) {
|
package/package.json
CHANGED
package/scripts/syncDb.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { Env } from '../config/env.js';
|
|
7
7
|
import { Logger } from '../utils/logger.js';
|
|
8
|
-
import { parseFieldRule, createSqlClient } from '../utils/index.js';
|
|
8
|
+
import { parseFieldRule, createSqlClient, toSnakeTableName } from '../utils/index.js';
|
|
9
9
|
import { __dirtables, getProjectDir } from '../system.js';
|
|
10
10
|
import { checkTable } from '../checks/table.js';
|
|
11
11
|
|
|
@@ -16,6 +16,8 @@ const typeMapping = {
|
|
|
16
16
|
array: 'VARCHAR'
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
// 表名转换函数已移动至 utils/index.js 的 toSnakeTableName
|
|
20
|
+
|
|
19
21
|
// 环境开关读取(支持未在 Env 显式声明的变量,默认值兜底)
|
|
20
22
|
const getFlag = (val, def = 0) => {
|
|
21
23
|
if (val === undefined || val === null || val === '') return !!def;
|
|
@@ -466,7 +468,8 @@ const SyncDb = async () => {
|
|
|
466
468
|
for (const dir of directories) {
|
|
467
469
|
try {
|
|
468
470
|
for await (const file of tablesGlob.scan({ cwd: dir, absolute: true, onlyFiles: true })) {
|
|
469
|
-
const
|
|
471
|
+
const fileBaseName = path.basename(file, '.json');
|
|
472
|
+
const tableName = toSnakeTableName(fileBaseName);
|
|
470
473
|
const tableDefinition = await Bun.file(file).json();
|
|
471
474
|
const result = await exec(client, 'SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?', [Env.MYSQL_DB || 'test', tableName]);
|
|
472
475
|
const exists = result[0].count > 0;
|
package/utils/index.js
CHANGED
|
@@ -93,6 +93,9 @@ export const isType = (value, type) => {
|
|
|
93
93
|
|
|
94
94
|
// 语义类型单独处理,其余走 actualType === expectedType
|
|
95
95
|
switch (expectedType) {
|
|
96
|
+
case 'function':
|
|
97
|
+
// 统一将普通函数、异步函数、生成器函数等都识别为函数
|
|
98
|
+
return typeof value === 'function';
|
|
96
99
|
case 'nan':
|
|
97
100
|
return typeof value === 'number' && Number.isNaN(value);
|
|
98
101
|
case 'empty':
|
|
@@ -294,6 +297,16 @@ export const validateRegex = (value) => {
|
|
|
294
297
|
}
|
|
295
298
|
};
|
|
296
299
|
|
|
300
|
+
// 将 lowerCamelCase 或单词形式转换为下划线风格(snake_case)
|
|
301
|
+
// 例如:userTable -> user_table, testNewFormat -> test_new_format, users -> users, orderV2 -> order_v2
|
|
302
|
+
export const toSnakeTableName = (name) => {
|
|
303
|
+
if (!name) return name;
|
|
304
|
+
return String(name)
|
|
305
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
306
|
+
.replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, '$1_$2')
|
|
307
|
+
.toLowerCase();
|
|
308
|
+
};
|
|
309
|
+
|
|
297
310
|
// 专门用于处理⚡分隔的字段规则
|
|
298
311
|
export const parseFieldRule = (rule) => {
|
|
299
312
|
const allParts = rule.split('⚡');
|