v-transform 2.3.1 → 2.3.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/package.json +1 -1
- package/src/calibrator.js +54 -25
package/package.json
CHANGED
package/src/calibrator.js
CHANGED
|
@@ -3,7 +3,6 @@ import path from 'node:path'
|
|
|
3
3
|
import { generate } from 'ts-to-zod'
|
|
4
4
|
|
|
5
5
|
export async function calibrateTsData(data, name, types, pk) {
|
|
6
|
-
// 如果没配对应的 .types.ts 文件,直接退回原始数据,类型签名为 any[]
|
|
7
6
|
if (!types || !fs.existsSync(types)) {
|
|
8
7
|
console.warn(
|
|
9
8
|
`⚠️ 提示: 在 Excel 同级目录下未找到 [${name}.types.ts],将退回普通无类型导出。`,
|
|
@@ -12,7 +11,7 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
12
11
|
const extra = {
|
|
13
12
|
typeSign: isArray ? 'any[]' : 'Map<any, any>',
|
|
14
13
|
types: isArray ? `export type ${name.toUpperCase()} = any;` : '',
|
|
15
|
-
isNativeMap: !isArray,
|
|
14
|
+
isNativeMap: !isArray,
|
|
16
15
|
}
|
|
17
16
|
return { data, extra }
|
|
18
17
|
}
|
|
@@ -23,11 +22,58 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
23
22
|
const sourceText = fs.readFileSync(types, 'utf-8')
|
|
24
23
|
const options = { keepOptionalProperties: true }
|
|
25
24
|
const zod = generate({ sourceText, options })
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const tsFileBase64 = Buffer.from(sourceText).toString('base64')
|
|
26
|
+
const configModule = await import(
|
|
27
|
+
`data:text/javascript;base64,${tsFileBase64}`
|
|
28
|
+
)
|
|
29
|
+
const customTransformers = configModule.transformers || {}
|
|
30
|
+
const mergedTransformers = {
|
|
31
|
+
string: val => {
|
|
32
|
+
if (val === null || val === undefined) return val
|
|
33
|
+
return String(val)
|
|
34
|
+
},
|
|
35
|
+
boolean: val => {
|
|
36
|
+
if (val === null || val === undefined) return val
|
|
37
|
+
const strVal = String(val).toLowerCase().trim()
|
|
38
|
+
if (strVal === 'true' || val === true || val === 1) return true
|
|
39
|
+
if (strVal === 'false' || val === false || val === 0) return false
|
|
40
|
+
return Boolean(val)
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
const localZodPath = require.resolve('zod').replace(/\\/g, '/')
|
|
44
|
+
let zodRawCode = zod.getZodSchemasFile()
|
|
45
|
+
zodRawCode = zodRawCode.replace(
|
|
46
|
+
/from\s+['"]zod['"]/g,
|
|
47
|
+
`from "${localZodPath}"`,
|
|
48
|
+
)
|
|
49
|
+
zodRawCode = zodRawCode.replace(
|
|
50
|
+
/z\.string\(\)/g,
|
|
51
|
+
`z.preprocess(globalThis.__MERGED_TRANSFORMERS__.string, z.string())`,
|
|
52
|
+
)
|
|
53
|
+
zodRawCode = zodRawCode.replace(
|
|
54
|
+
/z\.boolean\(\)/g,
|
|
55
|
+
`z.preprocess(globalThis.__MERGED_TRANSFORMERS__.boolean, z.boolean())`,
|
|
56
|
+
)
|
|
57
|
+
Object.keys(customTransformers).forEach(fieldKey => {
|
|
58
|
+
if (fieldKey === 'string' || fieldKey === 'boolean') return
|
|
59
|
+
|
|
60
|
+
const targetRegex = new RegExp(
|
|
61
|
+
`(${fieldKey}:\\s*)(z\\.[a-zA-Z_0-9]+(?:\\([^)]*\\))?)`,
|
|
62
|
+
'g',
|
|
63
|
+
)
|
|
64
|
+
zodRawCode = zodRawCode.replace(
|
|
65
|
+
targetRegex,
|
|
66
|
+
(m, prefix, originalZodType) => {
|
|
67
|
+
mergedTransformers[fieldKey] = customTransformers[fieldKey]
|
|
68
|
+
return `${prefix}z.preprocess(globalThis.__MERGED_TRANSFORMERS__.${fieldKey}, ${originalZodType})`
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
globalThis.__MERGED_TRANSFORMERS__ = mergedTransformers
|
|
29
74
|
const base64Code = Buffer.from(zodRawCode).toString('base64')
|
|
30
75
|
const zodModule = await import(`data:text/javascript;base64,${base64Code}`)
|
|
76
|
+
delete globalThis.__MERGED_TRANSFORMERS__
|
|
31
77
|
const targetSchemaName = `${name}Schema`
|
|
32
78
|
const RowZodValidator = zodModule[targetSchemaName]
|
|
33
79
|
|
|
@@ -57,9 +103,7 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
57
103
|
cleanData.push(parseResult.data)
|
|
58
104
|
}
|
|
59
105
|
console.info(`📦 [Array 模式] 完美对齐数组规范`)
|
|
60
|
-
}
|
|
61
|
-
// 2. Map 模式验证
|
|
62
|
-
else if (typeof data === 'object' && data !== null) {
|
|
106
|
+
} else if (typeof data === 'object' && data !== null) {
|
|
63
107
|
const tempCleanMap = {}
|
|
64
108
|
for (const key in data) {
|
|
65
109
|
const parseResult = RowZodValidator.safeParse(data[key])
|
|
@@ -73,15 +117,9 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
73
117
|
}
|
|
74
118
|
tempCleanMap[key] = parseResult.data
|
|
75
119
|
}
|
|
76
|
-
// ========================================================
|
|
77
|
-
// 🌟 终极修复:利用 Zod 原生验证自适应看穿 readonly 伪装
|
|
78
|
-
// ========================================================
|
|
79
120
|
let keyType = 'string'
|
|
80
121
|
if (pk && RowZodValidator.shape && RowZodValidator.shape[pk]) {
|
|
81
122
|
const validator = RowZodValidator.shape[pk]
|
|
82
|
-
|
|
83
|
-
// 💡 核心黑科技:直接用 1(数字)和 "1"(字符串)去该字段的校验器里试探!
|
|
84
|
-
// 无论它套了多少层 readonly() 还是可选约束,只要它不接受数字 1 校验,就说明它不是 number
|
|
85
123
|
if (
|
|
86
124
|
validator.safeParse(1).success &&
|
|
87
125
|
!validator.safeParse('1').success
|
|
@@ -101,20 +139,11 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
101
139
|
)
|
|
102
140
|
}
|
|
103
141
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const extra = {
|
|
107
|
-
typeSign,
|
|
108
|
-
types: sourceText,
|
|
109
|
-
isNativeMap: !Array.isArray(data), // 如果上游合并出来的不是数组(即Map模式),标记为 true
|
|
110
|
-
}
|
|
111
|
-
|
|
142
|
+
types = sourceText.split(/.*@vt-types-end.*/i)[0]
|
|
143
|
+
const extra = { typeSign, types, isNativeMap: !Array.isArray(data) }
|
|
112
144
|
return { data: cleanData, extra }
|
|
113
145
|
}
|
|
114
146
|
|
|
115
|
-
/**
|
|
116
|
-
* 统一的美化报错打印
|
|
117
|
-
*/
|
|
118
147
|
function printZodError(sheetName, positionLabel, issues, rawRowData) {
|
|
119
148
|
console.error(`\n❌ [vTransform 数据校准失败]`)
|
|
120
149
|
console.error(
|