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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/calibrator.js +54 -25
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "vt": "src/index.js"
5
5
  },
6
6
  "type": "module",
7
- "version": "2.3.1",
7
+ "version": "2.3.3",
8
8
  "main": "index.js",
9
9
  "scripts": {
10
10
  "clean": "npx rimraf test",
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, // 👈 即使降级也需要同步对齐是否为 Map 的标记
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 zodRawCode = zod
27
- .getZodSchemasFile()
28
- .replace(/z\.boolean\(\)/g, 'z.coerce.boolean()')
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
- // 🌟 核心修改 2:在返回的 extra 元数据层,加上 isNativeMap 的明确布尔标记。
105
- // 这可以让下游的 dump.js 接收到它后,通过简单的一行流判断,决定是将 cleanData 渲染为普通 JSON,还是渲染为强大的 new Map(...)
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(