v-transform 2.3.1 → 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/package.json +1 -1
- package/src/calibrator.js +53 -21
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,14 @@ export async function calibrateTsData(data, name, types, pk) {
|
|
|
101
139
|
)
|
|
102
140
|
}
|
|
103
141
|
|
|
104
|
-
// 🌟 核心修改 2:在返回的 extra 元数据层,加上 isNativeMap 的明确布尔标记。
|
|
105
|
-
// 这可以让下游的 dump.js 接收到它后,通过简单的一行流判断,决定是将 cleanData 渲染为普通 JSON,还是渲染为强大的 new Map(...)
|
|
106
142
|
const extra = {
|
|
107
143
|
typeSign,
|
|
108
144
|
types: sourceText,
|
|
109
|
-
isNativeMap: !Array.isArray(data),
|
|
145
|
+
isNativeMap: !Array.isArray(data),
|
|
110
146
|
}
|
|
111
|
-
|
|
112
147
|
return { data: cleanData, extra }
|
|
113
148
|
}
|
|
114
149
|
|
|
115
|
-
/**
|
|
116
|
-
* 统一的美化报错打印
|
|
117
|
-
*/
|
|
118
150
|
function printZodError(sheetName, positionLabel, issues, rawRowData) {
|
|
119
151
|
console.error(`\n❌ [vTransform 数据校准失败]`)
|
|
120
152
|
console.error(
|