-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathdts.ts
179 lines (159 loc) · 6.39 KB
/
dts.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* 生成 TS 类型定义文件相关方法
*/
import { compile, type JSONSchema } from 'json-schema-to-typescript'
import { deburr, has, set, trim, upperFirst } from 'lodash'
import { uuidv4 } from './tools'
/**
* 生成数据模型的 TS 类型定义文件
*/
export async function generateDataModelDTS(
dataModelList: { name: string; title: string; schema: JSONSchema }[]
): Promise<string> {
const dtsList = await Promise.all(
dataModelList.map(async (item) => {
let dts = await _handleOne(item.name, item.schema)
return {
name: item.name,
title: item.title,
dts
}
})
)
const result = `
import { DataModelMethods } from "@cloudbase/wx-cloud-client-sdk";
${dtsList.map((item) => item.dts).join('\n')}
interface IModels {
${dtsList
.map((item: any) => {
return `
/**
* 数据模型:${item.title}
*/
${_toValidFieldName(item.name)}: DataModelMethods<${getModelInterfaceName(item.name)}>;`
})
.join('\n')}
}
declare module "@cloudbase/wx-cloud-client-sdk" {
interface OrmClient extends IModels {}
}
declare global {
interface WxCloud {
models: IModels;
}
}`
return result
async function _handleOne(name: string, schema: JSONSchema): Promise<string> {
if (!schema?.properties) return `interface ${getModelInterfaceName(name)} {}`
// 删除系统字段
Object.keys(schema.properties).forEach((key) => {
if (schema.properties[key]['x-system']) {
delete schema.properties[key]
}
})
// 旧关联关系
Object.keys(schema.properties).forEach((key) => {
const field = schema.properties[key]
if (['related', 'father-son'].includes(field.format)) {
schema.properties[`@${key}`] = {
type: 'object',
description: `关联${field.title}对象`,
properties: {
v1: {
type: 'object',
properties: {
record: {
type: 'string',
format: field.format,
'x-parent': {
parentDataSourceName: field['x-parent'].parentDataSourceName
}
}
}
}
}
}
// 清空,因为关联关系转移到上面的字段了
schema.properties[key].format = ''
}
})
// title 字段合并进 description 字段作为注释信息。同时删除 title 字段,避免生成的 dts 不符合预期
schema = JSON.parse(
JSON.stringify(schema, (_: string, value: any) => {
if (has(value, 'title') && !has(value, 'title.title')) {
set(value, 'description', value['title'] + '\n' + value['description'])
delete value['title']
}
return value
})
)
const dts = await _compile(name, schema)
return dts
}
async function _compile(name: string, jsonschema) {
try {
let dts = await compile(jsonschema, getModelInterfaceName(name), {
additionalProperties: false,
bannerComment: '',
format: true,
unknownAny: false,
customName(_schema) {
const format = _schema.format
let name = ''
if (['one-one', 'many-one', 'related', 'father-son'].includes(format)) {
// 1 或 n : 1
name = getModelInterfaceName(_schema?.['x-parent']?.parentDataSourceName)
}
if (['one-many', 'many-many'].includes(format)) {
// 1 或 n : n: 增加 ARRAY_TYPE_ 来标识这是个数组类型
name = `ARRAY_TYPE_${getModelInterfaceName(
_schema?.['x-parent']?.parentDataSourceName
)}`
}
if (name) {
// 这里的处理是为了当多个字段关联同个数据模型时,在生成 dts 时,会生成两个不同的类型(比如 type, type1)
name = `${name}_TAIL${uuidv4()}_END_`
}
return name || undefined
}
})
dts = dts
.replace(/export interface/g, 'interface')
.replace(/ARRAY_TYPE_(.*);/g, '$1[]')
.replace(/_TAIL.*?_END_/g, '')
.replace(/[\s\S]*?(?=interface)/, '') // 删除掉 interface 上面的内容
return dts
} catch (e) {
console.error('_compile error:', e)
return ''
}
}
function _toValidFieldName(name: string) {
// 替换无效字符为下划线
let result = name.replace(/[^a-zA-Z0-9_$]/g, '_')
// 确保变量名不以数字开头
if (/^[0-9]/.test(result)) {
result = '_' + result
}
return result
}
function getModelInterfaceName(name: string) {
if (!name) return ''
return upperFirst(
// remove accents, umlauts, ... by their basic latin letters
deburr(`IModal_${name}`)
// replace chars which are not valid for typescript identifiers with whitespace
.replace(/(^\s*[^a-zA-Z_$])|([^a-zA-Z_$\d])/g, ' ')
// uppercase leading underscores followed by lowercase
.replace(/^_[a-z]/g, (match) => match.toUpperCase())
// remove non-leading underscores followed by lowercase (convert snake_case)
.replace(/_[a-z]/g, (match) => match.substr(1, match.length).toUpperCase())
// uppercase letters after digits, dollars
.replace(/([\d$]+[a-zA-Z])/g, (match) => match.toUpperCase())
// uppercase first letter after whitespace
.replace(/\s+([a-zA-Z])/g, (match) => trim(match.toUpperCase()))
// remove remaining whitespace
.replace(/\s/g, '')
)
}
}