@chihqiang/sql-quicktype 0.0.1
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/LICENSE +201 -0
- package/README.md +451 -0
- package/dist/cli.js +1302 -0
- package/dist/index.d.mts +263 -0
- package/dist/index.d.ts +263 -0
- package/dist/index.global.js +117 -0
- package/dist/index.js +1216 -0
- package/dist/index.mjs +1175 -0
- package/package.json +82 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
|
|
33
|
+
// src/generator/TypeScriptGenerator.ts
|
|
34
|
+
var TypeScriptGenerator_exports = {};
|
|
35
|
+
__export(TypeScriptGenerator_exports, {
|
|
36
|
+
TypeScriptGenerator: () => TypeScriptGenerator
|
|
37
|
+
});
|
|
38
|
+
var TypeScriptGenerator;
|
|
39
|
+
var init_TypeScriptGenerator = __esm({
|
|
40
|
+
"src/generator/TypeScriptGenerator.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
init_generator();
|
|
43
|
+
TypeScriptGenerator = class extends AGenerator {
|
|
44
|
+
constructor(options = { language: "typescript" }) {
|
|
45
|
+
super();
|
|
46
|
+
this.options = options;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 格式化字段名称(使用驼峰命名)
|
|
50
|
+
*/
|
|
51
|
+
formatFieldName(name) {
|
|
52
|
+
return name.split("_").map((part, index) => {
|
|
53
|
+
if (index === 0) {
|
|
54
|
+
return part.toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
|
57
|
+
}).join("");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 生成整个数据库模式的类型定义
|
|
61
|
+
*/
|
|
62
|
+
generateDatabase(database) {
|
|
63
|
+
let result = `// Database: ${database.name}
|
|
64
|
+
`;
|
|
65
|
+
result += `// Dialect: ${database.dialect}
|
|
66
|
+
|
|
67
|
+
`;
|
|
68
|
+
for (const table of database.tables) {
|
|
69
|
+
result += this.generateTable(table);
|
|
70
|
+
result += "\n";
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 生成单个表的类型定义
|
|
76
|
+
*/
|
|
77
|
+
generateTable(table) {
|
|
78
|
+
let result = `// ${table.name} \u8868\u7ED3\u6784
|
|
79
|
+
`;
|
|
80
|
+
result += `export interface ${this.formatTypeName(table.name)} {
|
|
81
|
+
`;
|
|
82
|
+
for (const column of table.columns) {
|
|
83
|
+
result += this.generateColumn(column);
|
|
84
|
+
}
|
|
85
|
+
result += "}\n";
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 生成列的类型定义
|
|
90
|
+
*/
|
|
91
|
+
generateColumn(column) {
|
|
92
|
+
const fieldName = this.formatFieldName(column.name);
|
|
93
|
+
const typeName = this.mapSQLType(column.type, column.name);
|
|
94
|
+
const optional = column.nullable ? "?" : "";
|
|
95
|
+
let result = ` ${fieldName}${optional}: ${typeName}`;
|
|
96
|
+
if (column.comment && this.options.generateComments !== false) {
|
|
97
|
+
result += `; // ${column.comment}`;
|
|
98
|
+
}
|
|
99
|
+
result += "\n";
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 映射 SQL 类型到 TypeScript 类型
|
|
104
|
+
*/
|
|
105
|
+
mapSQLType(type, columnName) {
|
|
106
|
+
switch (type.kind) {
|
|
107
|
+
case "int":
|
|
108
|
+
case "bigint":
|
|
109
|
+
return "number";
|
|
110
|
+
case "float":
|
|
111
|
+
case "decimal":
|
|
112
|
+
return "number";
|
|
113
|
+
case "varchar":
|
|
114
|
+
case "text":
|
|
115
|
+
return "string";
|
|
116
|
+
case "boolean":
|
|
117
|
+
return "boolean";
|
|
118
|
+
case "date":
|
|
119
|
+
case "datetime":
|
|
120
|
+
return "Date";
|
|
121
|
+
case "json":
|
|
122
|
+
return "unknown";
|
|
123
|
+
case "enum":
|
|
124
|
+
if (type.values && type.values.length > 0) {
|
|
125
|
+
const enumValues = type.values.map((v) => `'${v}'`).join(" | ");
|
|
126
|
+
if (columnName) {
|
|
127
|
+
const fieldName = this.formatFieldName(columnName);
|
|
128
|
+
return `${fieldName}: ${enumValues}`;
|
|
129
|
+
}
|
|
130
|
+
return enumValues;
|
|
131
|
+
}
|
|
132
|
+
return "string";
|
|
133
|
+
default:
|
|
134
|
+
return "string";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// src/generator/GolangGenerator.ts
|
|
142
|
+
var GolangGenerator_exports = {};
|
|
143
|
+
__export(GolangGenerator_exports, {
|
|
144
|
+
GolangGenerator: () => GolangGenerator
|
|
145
|
+
});
|
|
146
|
+
var GolangGenerator;
|
|
147
|
+
var init_GolangGenerator = __esm({
|
|
148
|
+
"src/generator/GolangGenerator.ts"() {
|
|
149
|
+
"use strict";
|
|
150
|
+
init_generator();
|
|
151
|
+
GolangGenerator = class extends AGenerator {
|
|
152
|
+
constructor(options = { language: "go" }) {
|
|
153
|
+
super();
|
|
154
|
+
this.options = options;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 生成整个数据库模式的类型定义
|
|
158
|
+
*/
|
|
159
|
+
generateDatabase(database) {
|
|
160
|
+
let result = `// Database: ${database.name}
|
|
161
|
+
`;
|
|
162
|
+
result += `// Dialect: ${database.dialect}
|
|
163
|
+
|
|
164
|
+
`;
|
|
165
|
+
if (this.options.namespace) {
|
|
166
|
+
result += `package ${this.options.namespace}
|
|
167
|
+
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
if (this.needsTimeImport(database)) {
|
|
171
|
+
result += `import "time"
|
|
172
|
+
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
for (const table of database.tables) {
|
|
176
|
+
result += this.generateTable(table);
|
|
177
|
+
result += "\n";
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 生成单个表的类型定义
|
|
183
|
+
*/
|
|
184
|
+
generateTable(table) {
|
|
185
|
+
let result = `// ${table.name} \u8868\u7ED3\u6784
|
|
186
|
+
`;
|
|
187
|
+
result += `type ${this.formatTypeName(table.name)} struct {
|
|
188
|
+
`;
|
|
189
|
+
for (const column of table.columns) {
|
|
190
|
+
result += this.generateColumn(column);
|
|
191
|
+
}
|
|
192
|
+
result += "}\n";
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* 生成列的类型定义
|
|
197
|
+
*/
|
|
198
|
+
generateColumn(column) {
|
|
199
|
+
const fieldName = this.formatFieldName(column.name);
|
|
200
|
+
const typeName = this.mapSQLType(column.type);
|
|
201
|
+
const tag = this.generateGoTag(column);
|
|
202
|
+
let result = ` ${fieldName} ${typeName} ${tag}`;
|
|
203
|
+
if (column.comment && this.options.generateComments !== false) {
|
|
204
|
+
result += ` // ${column.comment}`;
|
|
205
|
+
}
|
|
206
|
+
result += "\n";
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* 映射 SQL 类型到 Go 类型
|
|
211
|
+
*/
|
|
212
|
+
mapSQLType(type) {
|
|
213
|
+
switch (type.kind) {
|
|
214
|
+
case "int":
|
|
215
|
+
return "int";
|
|
216
|
+
case "bigint":
|
|
217
|
+
return "int64";
|
|
218
|
+
case "float":
|
|
219
|
+
return "float64";
|
|
220
|
+
case "decimal":
|
|
221
|
+
return "float64";
|
|
222
|
+
case "varchar":
|
|
223
|
+
case "text":
|
|
224
|
+
return "string";
|
|
225
|
+
case "boolean":
|
|
226
|
+
return "bool";
|
|
227
|
+
case "date":
|
|
228
|
+
case "datetime":
|
|
229
|
+
return "time.Time";
|
|
230
|
+
case "json":
|
|
231
|
+
return "interface{}";
|
|
232
|
+
case "enum":
|
|
233
|
+
return "string";
|
|
234
|
+
default:
|
|
235
|
+
return "string";
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 生成 Go 结构体标签
|
|
240
|
+
*/
|
|
241
|
+
generateGoTag(column) {
|
|
242
|
+
const tags = [];
|
|
243
|
+
tags.push(`json:"${column.name}"`);
|
|
244
|
+
tags.push(`db:"${column.name}"`);
|
|
245
|
+
return `\`${tags.join(" ")}\``;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// src/generator/GormGenerator.ts
|
|
252
|
+
var GormGenerator_exports = {};
|
|
253
|
+
__export(GormGenerator_exports, {
|
|
254
|
+
GormGenerator: () => GormGenerator
|
|
255
|
+
});
|
|
256
|
+
var GormGenerator;
|
|
257
|
+
var init_GormGenerator = __esm({
|
|
258
|
+
"src/generator/GormGenerator.ts"() {
|
|
259
|
+
"use strict";
|
|
260
|
+
init_generator();
|
|
261
|
+
GormGenerator = class extends AGenerator {
|
|
262
|
+
constructor(options = { language: "gorm" }) {
|
|
263
|
+
super();
|
|
264
|
+
this.options = options;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 生成整个数据库模式的类型定义
|
|
268
|
+
*/
|
|
269
|
+
generateDatabase(database) {
|
|
270
|
+
let result = `// Database: ${database.name}
|
|
271
|
+
`;
|
|
272
|
+
result += `// Dialect: ${database.dialect}
|
|
273
|
+
|
|
274
|
+
`;
|
|
275
|
+
if (this.options.namespace) {
|
|
276
|
+
result += `package ${this.options.namespace}
|
|
277
|
+
|
|
278
|
+
`;
|
|
279
|
+
}
|
|
280
|
+
if (this.needsTimeImport(database)) {
|
|
281
|
+
result += `import "time"
|
|
282
|
+
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
for (const table of database.tables) {
|
|
286
|
+
result += this.generateTable(table);
|
|
287
|
+
result += "\n";
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* 生成单个表的类型定义
|
|
293
|
+
*/
|
|
294
|
+
generateTable(table) {
|
|
295
|
+
let result = `// ${table.name} \u8868\u7ED3\u6784
|
|
296
|
+
`;
|
|
297
|
+
result += `type ${this.formatTypeName(table.name)} struct {
|
|
298
|
+
`;
|
|
299
|
+
for (const column of table.columns) {
|
|
300
|
+
result += this.generateColumn(column);
|
|
301
|
+
}
|
|
302
|
+
result += "}\n";
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* 生成列的类型定义
|
|
307
|
+
*/
|
|
308
|
+
generateColumn(column) {
|
|
309
|
+
const fieldName = this.formatFieldName(column.name);
|
|
310
|
+
const typeName = this.mapSQLType(column.type);
|
|
311
|
+
const tag = this.generateGormTag(column);
|
|
312
|
+
let result = ` ${fieldName} ${typeName} ${tag}`;
|
|
313
|
+
if (column.comment && this.options.generateComments !== false) {
|
|
314
|
+
result += ` // ${column.comment}`;
|
|
315
|
+
}
|
|
316
|
+
result += "\n";
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 映射 SQL 类型到 GORM 类型
|
|
321
|
+
*/
|
|
322
|
+
mapSQLType(type) {
|
|
323
|
+
switch (type.kind) {
|
|
324
|
+
case "int":
|
|
325
|
+
return "int";
|
|
326
|
+
case "bigint":
|
|
327
|
+
return "int64";
|
|
328
|
+
case "float":
|
|
329
|
+
return "float64";
|
|
330
|
+
case "decimal":
|
|
331
|
+
return "float64";
|
|
332
|
+
case "varchar":
|
|
333
|
+
case "text":
|
|
334
|
+
return "string";
|
|
335
|
+
case "boolean":
|
|
336
|
+
return "bool";
|
|
337
|
+
case "date":
|
|
338
|
+
case "datetime":
|
|
339
|
+
return "time.Time";
|
|
340
|
+
case "json":
|
|
341
|
+
return "interface{}";
|
|
342
|
+
case "enum":
|
|
343
|
+
return "string";
|
|
344
|
+
default:
|
|
345
|
+
return "string";
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 生成 GORM 结构体标签
|
|
350
|
+
*/
|
|
351
|
+
generateGormTag(column) {
|
|
352
|
+
const tags = [];
|
|
353
|
+
tags.push(`column:${column.name}`);
|
|
354
|
+
let typeTag = "type:";
|
|
355
|
+
switch (column.type.kind) {
|
|
356
|
+
case "int":
|
|
357
|
+
typeTag += "int";
|
|
358
|
+
if (column.type.length) {
|
|
359
|
+
typeTag += `(${column.type.length})`;
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
case "bigint":
|
|
363
|
+
typeTag += "bigint";
|
|
364
|
+
if (column.type.length) {
|
|
365
|
+
typeTag += `(${column.type.length})`;
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
case "float":
|
|
369
|
+
typeTag += "float";
|
|
370
|
+
if (column.type.precision) {
|
|
371
|
+
typeTag += `(${column.type.precision}`;
|
|
372
|
+
if (column.type.scale) {
|
|
373
|
+
typeTag += `,${column.type.scale}`;
|
|
374
|
+
}
|
|
375
|
+
typeTag += ")";
|
|
376
|
+
}
|
|
377
|
+
break;
|
|
378
|
+
case "decimal":
|
|
379
|
+
typeTag += "decimal";
|
|
380
|
+
if (column.type.precision) {
|
|
381
|
+
typeTag += `(${column.type.precision}`;
|
|
382
|
+
if (column.type.scale) {
|
|
383
|
+
typeTag += `,${column.type.scale}`;
|
|
384
|
+
}
|
|
385
|
+
typeTag += ")";
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
case "varchar":
|
|
389
|
+
typeTag += "varchar";
|
|
390
|
+
if (column.type.length) {
|
|
391
|
+
typeTag += `(${column.type.length})`;
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
case "text":
|
|
395
|
+
typeTag += "text";
|
|
396
|
+
break;
|
|
397
|
+
case "boolean":
|
|
398
|
+
typeTag += "bool";
|
|
399
|
+
break;
|
|
400
|
+
case "date":
|
|
401
|
+
typeTag += "date";
|
|
402
|
+
break;
|
|
403
|
+
case "datetime":
|
|
404
|
+
typeTag += "datetime";
|
|
405
|
+
break;
|
|
406
|
+
case "json":
|
|
407
|
+
typeTag += "json";
|
|
408
|
+
break;
|
|
409
|
+
case "enum":
|
|
410
|
+
typeTag += "enum";
|
|
411
|
+
break;
|
|
412
|
+
default:
|
|
413
|
+
typeTag += "string";
|
|
414
|
+
}
|
|
415
|
+
tags.push(typeTag);
|
|
416
|
+
if (column.primaryKey) {
|
|
417
|
+
tags.push("primaryKey");
|
|
418
|
+
}
|
|
419
|
+
if (column.unique) {
|
|
420
|
+
tags.push("unique");
|
|
421
|
+
}
|
|
422
|
+
if (!column.nullable) {
|
|
423
|
+
tags.push("not null");
|
|
424
|
+
}
|
|
425
|
+
if (column.default) {
|
|
426
|
+
tags.push(`default:${column.default}`);
|
|
427
|
+
}
|
|
428
|
+
if (column.generated) {
|
|
429
|
+
tags.push("autoIncrement");
|
|
430
|
+
}
|
|
431
|
+
if (column.comment) {
|
|
432
|
+
tags.push(`comment:${column.comment}`);
|
|
433
|
+
}
|
|
434
|
+
return `\`gorm:"${tags.join(";")}" json:"${column.name}"\``;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// src/generator/XormGenerator.ts
|
|
441
|
+
var XormGenerator_exports = {};
|
|
442
|
+
__export(XormGenerator_exports, {
|
|
443
|
+
XormGenerator: () => XormGenerator
|
|
444
|
+
});
|
|
445
|
+
var XormGenerator;
|
|
446
|
+
var init_XormGenerator = __esm({
|
|
447
|
+
"src/generator/XormGenerator.ts"() {
|
|
448
|
+
"use strict";
|
|
449
|
+
init_generator();
|
|
450
|
+
XormGenerator = class extends AGenerator {
|
|
451
|
+
constructor(options = { language: "xorm" }) {
|
|
452
|
+
super();
|
|
453
|
+
this.options = options;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* 生成整个数据库模式的类型定义
|
|
457
|
+
*/
|
|
458
|
+
generateDatabase(database) {
|
|
459
|
+
let result = `// Database: ${database.name}
|
|
460
|
+
`;
|
|
461
|
+
result += `// Dialect: ${database.dialect}
|
|
462
|
+
|
|
463
|
+
`;
|
|
464
|
+
if (this.options.namespace) {
|
|
465
|
+
result += `package ${this.options.namespace}
|
|
466
|
+
|
|
467
|
+
`;
|
|
468
|
+
}
|
|
469
|
+
if (this.needsTimeImport(database)) {
|
|
470
|
+
result += `import "time"
|
|
471
|
+
|
|
472
|
+
`;
|
|
473
|
+
}
|
|
474
|
+
for (const table of database.tables) {
|
|
475
|
+
result += this.generateTable(table);
|
|
476
|
+
result += "\n";
|
|
477
|
+
}
|
|
478
|
+
return result;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* 生成单个表的类型定义
|
|
482
|
+
*/
|
|
483
|
+
generateTable(table) {
|
|
484
|
+
let result = `// ${table.name} \u8868\u7ED3\u6784
|
|
485
|
+
`;
|
|
486
|
+
result += `type ${this.formatTypeName(table.name)} struct {
|
|
487
|
+
`;
|
|
488
|
+
for (const column of table.columns) {
|
|
489
|
+
result += this.generateColumn(column);
|
|
490
|
+
}
|
|
491
|
+
result += "}\n";
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* 生成列的类型定义
|
|
496
|
+
*/
|
|
497
|
+
generateColumn(column) {
|
|
498
|
+
const fieldName = this.formatFieldName(column.name);
|
|
499
|
+
const typeName = this.mapSQLType(column.type);
|
|
500
|
+
const tag = this.generateXormTag(column);
|
|
501
|
+
let result = ` ${fieldName} ${typeName} ${tag}`;
|
|
502
|
+
if (column.comment && this.options.generateComments !== false) {
|
|
503
|
+
result += ` // ${column.comment}`;
|
|
504
|
+
}
|
|
505
|
+
result += "\n";
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* 映射 SQL 类型到 XORM 类型
|
|
510
|
+
*/
|
|
511
|
+
mapSQLType(type) {
|
|
512
|
+
switch (type.kind) {
|
|
513
|
+
case "int":
|
|
514
|
+
return "int";
|
|
515
|
+
case "bigint":
|
|
516
|
+
return "int64";
|
|
517
|
+
case "float":
|
|
518
|
+
return "float64";
|
|
519
|
+
case "decimal":
|
|
520
|
+
return "float64";
|
|
521
|
+
case "varchar":
|
|
522
|
+
case "text":
|
|
523
|
+
return "string";
|
|
524
|
+
case "boolean":
|
|
525
|
+
return "bool";
|
|
526
|
+
case "date":
|
|
527
|
+
case "datetime":
|
|
528
|
+
return "time.Time";
|
|
529
|
+
case "json":
|
|
530
|
+
return "interface{}";
|
|
531
|
+
case "enum":
|
|
532
|
+
return "string";
|
|
533
|
+
default:
|
|
534
|
+
return "string";
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* 生成 XORM 结构体标签
|
|
539
|
+
*/
|
|
540
|
+
generateXormTag(column) {
|
|
541
|
+
const tags = [];
|
|
542
|
+
tags.push(`${column.name}`);
|
|
543
|
+
let typeTag = "";
|
|
544
|
+
switch (column.type.kind) {
|
|
545
|
+
case "int":
|
|
546
|
+
typeTag = "int";
|
|
547
|
+
if (column.type.length) {
|
|
548
|
+
typeTag += `(${column.type.length})`;
|
|
549
|
+
}
|
|
550
|
+
break;
|
|
551
|
+
case "bigint":
|
|
552
|
+
typeTag = "bigint";
|
|
553
|
+
if (column.type.length) {
|
|
554
|
+
typeTag += `(${column.type.length})`;
|
|
555
|
+
}
|
|
556
|
+
break;
|
|
557
|
+
case "float":
|
|
558
|
+
typeTag = "float";
|
|
559
|
+
if (column.type.precision) {
|
|
560
|
+
typeTag += `(${column.type.precision}`;
|
|
561
|
+
if (column.type.scale) {
|
|
562
|
+
typeTag += `,${column.type.scale}`;
|
|
563
|
+
}
|
|
564
|
+
typeTag += ")";
|
|
565
|
+
}
|
|
566
|
+
break;
|
|
567
|
+
case "decimal":
|
|
568
|
+
typeTag = "decimal";
|
|
569
|
+
if (column.type.precision) {
|
|
570
|
+
typeTag += `(${column.type.precision}`;
|
|
571
|
+
if (column.type.scale) {
|
|
572
|
+
typeTag += `,${column.type.scale}`;
|
|
573
|
+
}
|
|
574
|
+
typeTag += ")";
|
|
575
|
+
}
|
|
576
|
+
break;
|
|
577
|
+
case "varchar":
|
|
578
|
+
typeTag = "varchar";
|
|
579
|
+
if (column.type.length) {
|
|
580
|
+
typeTag += `(${column.type.length})`;
|
|
581
|
+
}
|
|
582
|
+
break;
|
|
583
|
+
case "text":
|
|
584
|
+
typeTag = "text";
|
|
585
|
+
break;
|
|
586
|
+
case "boolean":
|
|
587
|
+
typeTag = "bool";
|
|
588
|
+
break;
|
|
589
|
+
case "date":
|
|
590
|
+
typeTag = "date";
|
|
591
|
+
break;
|
|
592
|
+
case "datetime":
|
|
593
|
+
typeTag = "datetime";
|
|
594
|
+
break;
|
|
595
|
+
case "json":
|
|
596
|
+
typeTag = "json";
|
|
597
|
+
break;
|
|
598
|
+
case "enum":
|
|
599
|
+
typeTag = "enum";
|
|
600
|
+
break;
|
|
601
|
+
default:
|
|
602
|
+
typeTag = "string";
|
|
603
|
+
}
|
|
604
|
+
if (typeTag) {
|
|
605
|
+
tags.push(typeTag);
|
|
606
|
+
}
|
|
607
|
+
if (column.primaryKey) {
|
|
608
|
+
tags.push("pk");
|
|
609
|
+
}
|
|
610
|
+
if (column.unique) {
|
|
611
|
+
tags.push("unique");
|
|
612
|
+
}
|
|
613
|
+
if (!column.nullable) {
|
|
614
|
+
tags.push("notnull");
|
|
615
|
+
}
|
|
616
|
+
if (column.default) {
|
|
617
|
+
tags.push(`default(${column.default})`);
|
|
618
|
+
}
|
|
619
|
+
if (column.generated) {
|
|
620
|
+
tags.push("autoincr");
|
|
621
|
+
}
|
|
622
|
+
if (column.comment) {
|
|
623
|
+
tags.push(`comment(${column.comment})`);
|
|
624
|
+
}
|
|
625
|
+
return `\`xorm:"${tags.join(" ")}" json:"${column.name}"\``;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// src/generator/generator.ts
|
|
632
|
+
var AGenerator, GeneratorFactory;
|
|
633
|
+
var init_generator = __esm({
|
|
634
|
+
"src/generator/generator.ts"() {
|
|
635
|
+
"use strict";
|
|
636
|
+
AGenerator = class {
|
|
637
|
+
/**
|
|
638
|
+
* 格式化类型名称(如驼峰命名、帕斯卡命名等)
|
|
639
|
+
*/
|
|
640
|
+
formatTypeName(name) {
|
|
641
|
+
return name.split("_").map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* 格式化字段名称
|
|
645
|
+
*/
|
|
646
|
+
formatFieldName(name) {
|
|
647
|
+
return name.split("_").map((part) => {
|
|
648
|
+
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
|
649
|
+
}).join("");
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* 生成默认值
|
|
653
|
+
*/
|
|
654
|
+
generateDefaultValue(column) {
|
|
655
|
+
if (!column.default) {
|
|
656
|
+
return "";
|
|
657
|
+
}
|
|
658
|
+
return column.default;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* 检查是否需要导入 time 包
|
|
662
|
+
*/
|
|
663
|
+
needsTimeImport(database) {
|
|
664
|
+
for (const table of database.tables) {
|
|
665
|
+
for (const column of table.columns) {
|
|
666
|
+
if (column.type.kind === "date" || column.type.kind === "datetime") {
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
GeneratorFactory = class {
|
|
675
|
+
/**
|
|
676
|
+
* 创建语言生成器实例
|
|
677
|
+
* @param language 目标语言
|
|
678
|
+
* @param options 生成器配置选项
|
|
679
|
+
* @returns 语言生成器实例
|
|
680
|
+
*/
|
|
681
|
+
static async createGenerator(language, options = { language }) {
|
|
682
|
+
switch (language) {
|
|
683
|
+
case "typescript":
|
|
684
|
+
const { TypeScriptGenerator: TypeScriptGenerator2 } = await Promise.resolve().then(() => (init_TypeScriptGenerator(), TypeScriptGenerator_exports));
|
|
685
|
+
return new TypeScriptGenerator2(options);
|
|
686
|
+
case "go":
|
|
687
|
+
const { GolangGenerator: GolangGenerator2 } = await Promise.resolve().then(() => (init_GolangGenerator(), GolangGenerator_exports));
|
|
688
|
+
return new GolangGenerator2(options);
|
|
689
|
+
case "gorm":
|
|
690
|
+
const { GormGenerator: GormGenerator2 } = await Promise.resolve().then(() => (init_GormGenerator(), GormGenerator_exports));
|
|
691
|
+
return new GormGenerator2(options);
|
|
692
|
+
case "xorm":
|
|
693
|
+
const { XormGenerator: XormGenerator2 } = await Promise.resolve().then(() => (init_XormGenerator(), XormGenerator_exports));
|
|
694
|
+
return new XormGenerator2(options);
|
|
695
|
+
default:
|
|
696
|
+
throw new Error(`Unsupported language: ${language}`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// src/cli.ts
|
|
704
|
+
var import_commander = require("commander");
|
|
705
|
+
|
|
706
|
+
// src/sql-parser.ts
|
|
707
|
+
var import_node_sql_parser = require("node-sql-parser");
|
|
708
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
709
|
+
function getParser(dialect) {
|
|
710
|
+
if (!parserCache.has(dialect)) {
|
|
711
|
+
parserCache.set(dialect, new import_node_sql_parser.Parser());
|
|
712
|
+
}
|
|
713
|
+
return parserCache.get(dialect);
|
|
714
|
+
}
|
|
715
|
+
function parseSQL(sql, options = { dialect: "mysql" }) {
|
|
716
|
+
if (!sql || typeof sql !== "string") {
|
|
717
|
+
throw new Error("SQL string is required and must be a string");
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
const parser = getParser(options.dialect);
|
|
721
|
+
const processedSql = sql.trim().replace(/\s+/g, " ").replace(/;\s*;/g, ";");
|
|
722
|
+
const ast = parser.astify(processedSql, { database: options.dialect });
|
|
723
|
+
if (!ast) {
|
|
724
|
+
throw new Error("Failed to parse SQL: AST is null or undefined");
|
|
725
|
+
}
|
|
726
|
+
const astArray = Array.isArray(ast) ? ast : [ast];
|
|
727
|
+
const sqlParser = new SQLParser(options);
|
|
728
|
+
return sqlParser.parseDatabase(astArray);
|
|
729
|
+
} catch (error) {
|
|
730
|
+
if (error instanceof Error) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
`SQL parsing error: ${error.message}
|
|
733
|
+
Dialect: ${options.dialect}
|
|
734
|
+
SQL length: ${sql.length} characters
|
|
735
|
+
First 200 characters: ${sql.substring(0, 200)}${sql.length > 200 ? "..." : ""}`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
throw new Error(
|
|
739
|
+
`SQL parsing error: ${String(error)}
|
|
740
|
+
Dialect: ${options.dialect}
|
|
741
|
+
SQL length: ${sql.length} characters`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
var SQLParser = class {
|
|
746
|
+
constructor(options = { dialect: "mysql" }) {
|
|
747
|
+
/**
|
|
748
|
+
* 默认类型解析器
|
|
749
|
+
* 提供基本的 SQL 类型到 SQLType 的映射逻辑
|
|
750
|
+
*/
|
|
751
|
+
this.defaultTypeResolver = {
|
|
752
|
+
resolve: (def) => {
|
|
753
|
+
if (!def || !def.dataType) {
|
|
754
|
+
const textType = { kind: "text" };
|
|
755
|
+
return textType;
|
|
756
|
+
}
|
|
757
|
+
const dt = def.dataType.toLowerCase();
|
|
758
|
+
switch (dt) {
|
|
759
|
+
case "int":
|
|
760
|
+
case "integer":
|
|
761
|
+
case "smallint":
|
|
762
|
+
case "mediumint":
|
|
763
|
+
case "year":
|
|
764
|
+
const intType = {
|
|
765
|
+
kind: "int",
|
|
766
|
+
length: Array.isArray(def.length) ? def.length[0] : def.length
|
|
767
|
+
};
|
|
768
|
+
return intType;
|
|
769
|
+
case "bigint":
|
|
770
|
+
const bigIntType = {
|
|
771
|
+
kind: "bigint",
|
|
772
|
+
length: Array.isArray(def.length) ? def.length[0] : def.length
|
|
773
|
+
};
|
|
774
|
+
return bigIntType;
|
|
775
|
+
case "float":
|
|
776
|
+
case "double":
|
|
777
|
+
const floatType = {
|
|
778
|
+
kind: "float",
|
|
779
|
+
precision: Array.isArray(def.length) ? def.length[0] : def.length,
|
|
780
|
+
scale: Array.isArray(def.length) && def.length[1] ? def.length[1] : def.scale
|
|
781
|
+
};
|
|
782
|
+
return floatType;
|
|
783
|
+
case "decimal":
|
|
784
|
+
const decimalType = {
|
|
785
|
+
kind: "decimal",
|
|
786
|
+
precision: Array.isArray(def.length) ? def.length[0] : def.length,
|
|
787
|
+
scale: Array.isArray(def.length) && def.length[1] ? def.length[1] : def.scale
|
|
788
|
+
};
|
|
789
|
+
return decimalType;
|
|
790
|
+
case "varchar":
|
|
791
|
+
const varcharType = {
|
|
792
|
+
kind: "varchar",
|
|
793
|
+
length: Array.isArray(def.length) ? def.length[0] : def.length
|
|
794
|
+
};
|
|
795
|
+
return varcharType;
|
|
796
|
+
case "text":
|
|
797
|
+
case "longtext":
|
|
798
|
+
case "blob":
|
|
799
|
+
case "time":
|
|
800
|
+
const textType = { kind: "text" };
|
|
801
|
+
return textType;
|
|
802
|
+
case "boolean":
|
|
803
|
+
const booleanType = { kind: "boolean" };
|
|
804
|
+
return booleanType;
|
|
805
|
+
case "tinyint":
|
|
806
|
+
if (def.length && (Array.isArray(def.length) ? def.length[0] === 1 : def.length === 1)) {
|
|
807
|
+
const booleanType2 = { kind: "boolean" };
|
|
808
|
+
return booleanType2;
|
|
809
|
+
}
|
|
810
|
+
const tinyIntType = { kind: "int" };
|
|
811
|
+
return tinyIntType;
|
|
812
|
+
case "date":
|
|
813
|
+
const dateType = { kind: "date" };
|
|
814
|
+
return dateType;
|
|
815
|
+
case "datetime":
|
|
816
|
+
case "timestamp":
|
|
817
|
+
const dateTimeType = { kind: "datetime" };
|
|
818
|
+
return dateTimeType;
|
|
819
|
+
case "json":
|
|
820
|
+
const jsonType = { kind: "json" };
|
|
821
|
+
return jsonType;
|
|
822
|
+
case "enum":
|
|
823
|
+
if (def.expr && def.expr.value) {
|
|
824
|
+
const values = def.expr.value.map(
|
|
825
|
+
(v) => {
|
|
826
|
+
let value;
|
|
827
|
+
if (v.value) {
|
|
828
|
+
value = v.value;
|
|
829
|
+
} else if (v.raw) {
|
|
830
|
+
value = v.raw;
|
|
831
|
+
} else {
|
|
832
|
+
value = String(v);
|
|
833
|
+
}
|
|
834
|
+
return value.replace(/^['"]|['"]$/g, "");
|
|
835
|
+
}
|
|
836
|
+
);
|
|
837
|
+
const enumType = {
|
|
838
|
+
kind: "enum",
|
|
839
|
+
values
|
|
840
|
+
};
|
|
841
|
+
return enumType;
|
|
842
|
+
}
|
|
843
|
+
const emptyEnumType = {
|
|
844
|
+
kind: "enum",
|
|
845
|
+
values: []
|
|
846
|
+
};
|
|
847
|
+
return emptyEnumType;
|
|
848
|
+
/**
|
|
849
|
+
* 未识别类型默认降级为 text,避免解析失败
|
|
850
|
+
* 在严格模式下,遇到未识别的类型会抛出错误
|
|
851
|
+
*/
|
|
852
|
+
default:
|
|
853
|
+
if (this.options.strictMode) {
|
|
854
|
+
throw new Error(`Unsupported SQL type: ${dt}`);
|
|
855
|
+
}
|
|
856
|
+
const defaultTextType = { kind: "text" };
|
|
857
|
+
return defaultTextType;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
this.options = {
|
|
862
|
+
strictMode: false,
|
|
863
|
+
ignoreComments: false,
|
|
864
|
+
parseForeignKeys: true,
|
|
865
|
+
parseIndexes: true,
|
|
866
|
+
typeResolvers: [],
|
|
867
|
+
...options
|
|
868
|
+
};
|
|
869
|
+
this.parser = new import_node_sql_parser.Parser();
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* 遍历 AST,提取所有 CREATE TABLE
|
|
873
|
+
*/
|
|
874
|
+
parseDatabase(ast, dbName = this.options.dbName || "db") {
|
|
875
|
+
const db = {
|
|
876
|
+
name: dbName,
|
|
877
|
+
dialect: this.options.dialect,
|
|
878
|
+
tables: []
|
|
879
|
+
};
|
|
880
|
+
for (const node of ast) {
|
|
881
|
+
if (this.isCreateTable(node)) {
|
|
882
|
+
db.tables.push(this.parseTable(node));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
db.tablesMap = {};
|
|
886
|
+
for (const table of db.tables) {
|
|
887
|
+
if (table.name) {
|
|
888
|
+
db.tablesMap[table.name] = table;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return db;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* 类型守卫:判断是否为 CREATE TABLE 语句
|
|
895
|
+
*/
|
|
896
|
+
isCreateTable(node) {
|
|
897
|
+
return node.type === "create" && node.keyword === "table";
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* 解析单表 AST -> TableSchema
|
|
901
|
+
*/
|
|
902
|
+
parseTable(node) {
|
|
903
|
+
var _a, _b, _c, _d;
|
|
904
|
+
const tableName = (_b = (_a = node.table) == null ? void 0 : _a[0]) == null ? void 0 : _b.table;
|
|
905
|
+
if (!tableName) {
|
|
906
|
+
throw new Error("Table name is required in CREATE TABLE statement");
|
|
907
|
+
}
|
|
908
|
+
const comment = (_d = (_c = node.table_options) == null ? void 0 : _c.find(
|
|
909
|
+
(o) => o.keyword === "comment"
|
|
910
|
+
)) == null ? void 0 : _d.value;
|
|
911
|
+
const table = {
|
|
912
|
+
name: tableName,
|
|
913
|
+
comment: comment == null ? void 0 : comment.replace(/'/g, ""),
|
|
914
|
+
columns: [],
|
|
915
|
+
primaryKeys: [],
|
|
916
|
+
indexes: []
|
|
917
|
+
};
|
|
918
|
+
for (const def of node.create_definitions || []) {
|
|
919
|
+
try {
|
|
920
|
+
switch (def.resource) {
|
|
921
|
+
case "column":
|
|
922
|
+
const col = this.parseColumn(def);
|
|
923
|
+
table.columns.push(col);
|
|
924
|
+
if (col.primaryKey) {
|
|
925
|
+
table.primaryKeys.push(col.name);
|
|
926
|
+
}
|
|
927
|
+
break;
|
|
928
|
+
case "constraint":
|
|
929
|
+
this.parseTableConstraint(def, table);
|
|
930
|
+
break;
|
|
931
|
+
case "index":
|
|
932
|
+
this.parseIndex(def, table);
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
} catch (error) {
|
|
936
|
+
throw new Error(
|
|
937
|
+
`Error parsing table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return table;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* 解析列定义 AST -> ColumnSchema
|
|
945
|
+
*
|
|
946
|
+
* 注意:字段级 primary/unique 与表级定义会叠加
|
|
947
|
+
*/
|
|
948
|
+
parseColumn(def) {
|
|
949
|
+
var _a, _b, _c;
|
|
950
|
+
const columnName = (_a = def.column) == null ? void 0 : _a.column;
|
|
951
|
+
if (!columnName) {
|
|
952
|
+
throw new Error("Column name is required in column definition");
|
|
953
|
+
}
|
|
954
|
+
if (!def.definition) {
|
|
955
|
+
throw new Error(`Column ${columnName} missing definition`);
|
|
956
|
+
}
|
|
957
|
+
return {
|
|
958
|
+
name: columnName,
|
|
959
|
+
/**
|
|
960
|
+
* 抽象 SQL 类型映射
|
|
961
|
+
*/
|
|
962
|
+
type: this.mapSQLType(def.definition),
|
|
963
|
+
/**
|
|
964
|
+
* node-sql-parser 中:
|
|
965
|
+
* nullable 是一个对象,当它存在且 type 是 "not null" 时,表示 NOT NULL
|
|
966
|
+
*/
|
|
967
|
+
nullable: !(def.nullable && def.nullable.type === "not null"),
|
|
968
|
+
primaryKey: !!def.primary_key,
|
|
969
|
+
unique: !!def.unique,
|
|
970
|
+
/**
|
|
971
|
+
* 默认值需要序列化为 SQL 字符串
|
|
972
|
+
*/
|
|
973
|
+
default: def.default_val ? this.parseDefault(def.default_val) : void 0,
|
|
974
|
+
comment: (_c = (_b = def.comment) == null ? void 0 : _b.value) == null ? void 0 : _c.value,
|
|
975
|
+
unsigned: !!def.definition.unsigned,
|
|
976
|
+
generated: !!def.definition.generated
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* 解析表级 PRIMARY KEY / UNIQUE / FOREIGN KEY
|
|
981
|
+
*/
|
|
982
|
+
parseTableConstraint(def, table) {
|
|
983
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
984
|
+
const type = (_a = def.constraint_type) == null ? void 0 : _a.toUpperCase();
|
|
985
|
+
const columns = ((_b = def.definition) == null ? void 0 : _b.map((c) => c.column)) || [];
|
|
986
|
+
if (type === "PRIMARY KEY") {
|
|
987
|
+
table.primaryKeys.push(...columns);
|
|
988
|
+
columns.forEach((name) => {
|
|
989
|
+
const col = table.columns.find((c) => c.name === name);
|
|
990
|
+
if (col) col.primaryKey = true;
|
|
991
|
+
});
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if ((type === "UNIQUE" || type === "UNIQUE KEY") && this.options.parseIndexes) {
|
|
995
|
+
const idx = {
|
|
996
|
+
name: def.index || `unique_${columns.join("_")}`,
|
|
997
|
+
columns,
|
|
998
|
+
unique: true
|
|
999
|
+
};
|
|
1000
|
+
table.indexes.push(idx);
|
|
1001
|
+
columns.forEach((name) => {
|
|
1002
|
+
const col = table.columns.find((c) => c.name === name);
|
|
1003
|
+
if (col) col.unique = true;
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
if (type === "FOREIGN KEY" && this.options.parseForeignKeys) {
|
|
1007
|
+
let referencedTable;
|
|
1008
|
+
let referencedColumns = [];
|
|
1009
|
+
if ("reference" in def && def.reference) {
|
|
1010
|
+
referencedTable = (_d = (_c = def.reference.table) == null ? void 0 : _c[0]) == null ? void 0 : _d.table;
|
|
1011
|
+
referencedColumns = ((_e = def.reference.definition) == null ? void 0 : _e.map((c) => c.column)) || [];
|
|
1012
|
+
} else if ("table" in def && def.table) {
|
|
1013
|
+
referencedTable = (_g = (_f = def.table) == null ? void 0 : _f[0]) == null ? void 0 : _g.table;
|
|
1014
|
+
referencedColumns = ((_h = def.definition) == null ? void 0 : _h.map((c) => c.column)) || [];
|
|
1015
|
+
}
|
|
1016
|
+
if (referencedTable && referencedColumns.length > 0) {
|
|
1017
|
+
if (!table.foreignKeys) {
|
|
1018
|
+
table.foreignKeys = [];
|
|
1019
|
+
}
|
|
1020
|
+
table.foreignKeys.push({
|
|
1021
|
+
name: def.index,
|
|
1022
|
+
columns,
|
|
1023
|
+
referencedTable,
|
|
1024
|
+
referencedColumns,
|
|
1025
|
+
onDelete: def.on_delete,
|
|
1026
|
+
onUpdate: def.on_update
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* 解析普通索引
|
|
1033
|
+
*/
|
|
1034
|
+
parseIndex(def, table) {
|
|
1035
|
+
var _a;
|
|
1036
|
+
if (this.options.parseIndexes) {
|
|
1037
|
+
table.indexes.push({
|
|
1038
|
+
name: def.index,
|
|
1039
|
+
columns: ((_a = def.definition) == null ? void 0 : _a.map((c) => c.column)) || [],
|
|
1040
|
+
unique: !!def.unique,
|
|
1041
|
+
type: def.index_type
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* SQL AST 类型 -> SQLType(跨方言抽象)
|
|
1047
|
+
*/
|
|
1048
|
+
mapSQLType(def) {
|
|
1049
|
+
const allResolvers = [
|
|
1050
|
+
...this.options.typeResolvers,
|
|
1051
|
+
this.defaultTypeResolver
|
|
1052
|
+
];
|
|
1053
|
+
for (const resolver of allResolvers) {
|
|
1054
|
+
const result = resolver.resolve(def);
|
|
1055
|
+
if (result) {
|
|
1056
|
+
return result;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const textType = { kind: "text" };
|
|
1060
|
+
return textType;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* 默认值 AST -> SQL 字符串
|
|
1064
|
+
*
|
|
1065
|
+
* 使用 sqlify 确保函数/表达式被正确序列化
|
|
1066
|
+
*/
|
|
1067
|
+
parseDefault(def) {
|
|
1068
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1069
|
+
if (((_a = def.value) == null ? void 0 : _a.type) === "null" || def.value === null) {
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
try {
|
|
1073
|
+
return this.parser.sqlify(def.value);
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
if (((_b = def.value) == null ? void 0 : _b.type) === "function") {
|
|
1076
|
+
if ((_e = (_d = (_c = def.value.name) == null ? void 0 : _c.name) == null ? void 0 : _d[0]) == null ? void 0 : _e.value) {
|
|
1077
|
+
return def.value.name.name[0].value;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return String((_g = (_f = def.value) == null ? void 0 : _f.value) != null ? _g : def.value);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// src/cli/generate.ts
|
|
1086
|
+
init_generator();
|
|
1087
|
+
var fs = __toESM(require("fs"));
|
|
1088
|
+
var path = __toESM(require("path"));
|
|
1089
|
+
var LANGUAGE_EXTENSIONS = {
|
|
1090
|
+
typescript: ".ts",
|
|
1091
|
+
go: ".go",
|
|
1092
|
+
gorm: ".go",
|
|
1093
|
+
xorm: ".go"
|
|
1094
|
+
};
|
|
1095
|
+
var VALID_LANGUAGES = ["go", "typescript", "gorm", "xorm"];
|
|
1096
|
+
var VALID_MODES = ["single", "multi"];
|
|
1097
|
+
var DEFAULT_NAMESPACE = "models";
|
|
1098
|
+
var LANGUAGES_REQUIRING_NAMESPACE = ["go", "gorm", "xorm"];
|
|
1099
|
+
function addCommonGenerateOptions(command) {
|
|
1100
|
+
return command.option("-o, --output <dir>", "Output directory", "./output").option(
|
|
1101
|
+
"-l, --language <lang>",
|
|
1102
|
+
"Target language (gorm, typescript, xorm)",
|
|
1103
|
+
"typescript"
|
|
1104
|
+
).option(
|
|
1105
|
+
"-n, --namespace <namespace>",
|
|
1106
|
+
`Namespace/package name (default: ${DEFAULT_NAMESPACE} for go/gorm/xorm)`
|
|
1107
|
+
).option(
|
|
1108
|
+
"-x, --dialect <dialect>",
|
|
1109
|
+
"SQL dialect (mysql, postgres, sqlite, sqlserver)",
|
|
1110
|
+
"mysql"
|
|
1111
|
+
).option("--db-name <name>", "Database name", "my_database");
|
|
1112
|
+
}
|
|
1113
|
+
async function generateCode(sql, options) {
|
|
1114
|
+
if (!VALID_LANGUAGES.includes(options.language)) {
|
|
1115
|
+
console.error(
|
|
1116
|
+
`Error: Invalid language: ${options.language}. Valid options: ${VALID_LANGUAGES.join(", ")}`
|
|
1117
|
+
);
|
|
1118
|
+
process.exit(1);
|
|
1119
|
+
}
|
|
1120
|
+
if (!VALID_MODES.includes(options.mode)) {
|
|
1121
|
+
console.error(
|
|
1122
|
+
`Error: Invalid mode: ${options.mode}. Valid options: ${VALID_MODES.join(", ")}`
|
|
1123
|
+
);
|
|
1124
|
+
process.exit(1);
|
|
1125
|
+
}
|
|
1126
|
+
const needsNamespace = LANGUAGES_REQUIRING_NAMESPACE.includes(
|
|
1127
|
+
options.language
|
|
1128
|
+
);
|
|
1129
|
+
if (needsNamespace && !options.namespace) {
|
|
1130
|
+
options.namespace = DEFAULT_NAMESPACE;
|
|
1131
|
+
}
|
|
1132
|
+
const dbSchema = parseSQL(sql, {
|
|
1133
|
+
dialect: options.dialect,
|
|
1134
|
+
dbName: options.dbName
|
|
1135
|
+
});
|
|
1136
|
+
console.log(`Parsed ${dbSchema.tables.length} table(s)`);
|
|
1137
|
+
const generatorOptions = {
|
|
1138
|
+
language: options.language,
|
|
1139
|
+
namespace: options.namespace,
|
|
1140
|
+
generateComments: true
|
|
1141
|
+
};
|
|
1142
|
+
let generator;
|
|
1143
|
+
try {
|
|
1144
|
+
generator = await GeneratorFactory.createGenerator(
|
|
1145
|
+
options.language,
|
|
1146
|
+
generatorOptions
|
|
1147
|
+
);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
console.error(`Error: ${error.message}`);
|
|
1150
|
+
process.exit(1);
|
|
1151
|
+
}
|
|
1152
|
+
const outputDir = path.resolve(options.output);
|
|
1153
|
+
if (!fs.existsSync(outputDir)) {
|
|
1154
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1155
|
+
}
|
|
1156
|
+
const fileExtension = LANGUAGE_EXTENSIONS[options.language] || ".go";
|
|
1157
|
+
if (options.mode === "single") {
|
|
1158
|
+
const content = generator.generateDatabase(dbSchema);
|
|
1159
|
+
const outputFile = path.join(outputDir, `models${fileExtension}`);
|
|
1160
|
+
fs.writeFileSync(outputFile, content, "utf-8");
|
|
1161
|
+
console.log(`Generated: ${outputFile}`);
|
|
1162
|
+
} else {
|
|
1163
|
+
for (const table of dbSchema.tables) {
|
|
1164
|
+
const content = generator.generateTable(table);
|
|
1165
|
+
const fileName = `${table.name.toLowerCase()}${fileExtension}`;
|
|
1166
|
+
const outputFile = path.join(outputDir, fileName);
|
|
1167
|
+
let fileContent = "";
|
|
1168
|
+
if (generatorOptions.namespace && needsNamespace) {
|
|
1169
|
+
fileContent += `package ${generatorOptions.namespace}
|
|
1170
|
+
|
|
1171
|
+
`;
|
|
1172
|
+
}
|
|
1173
|
+
fileContent += content;
|
|
1174
|
+
fs.writeFileSync(outputFile, fileContent, "utf-8");
|
|
1175
|
+
console.log(`Generated: ${outputFile}`);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
console.log("Done!");
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/cli/generate-sql.ts
|
|
1182
|
+
async function commandGenerateSqlString(program2) {
|
|
1183
|
+
const command = program2.command("sql").description("Generate code from SQL string").requiredOption("-s, --sql <sql>", "SQL string is required").option(
|
|
1184
|
+
"-m, --mode <mode>",
|
|
1185
|
+
"Output mode: single (one file) or multi (one file per table)",
|
|
1186
|
+
"single"
|
|
1187
|
+
);
|
|
1188
|
+
addCommonGenerateOptions(command);
|
|
1189
|
+
command.action(async (options) => {
|
|
1190
|
+
try {
|
|
1191
|
+
console.log("Using provided SQL string");
|
|
1192
|
+
await generateCode(options.sql, {
|
|
1193
|
+
output: options.output,
|
|
1194
|
+
language: options.language,
|
|
1195
|
+
mode: options.mode,
|
|
1196
|
+
namespace: options.namespace,
|
|
1197
|
+
dialect: options.dialect,
|
|
1198
|
+
dbName: options.dbName
|
|
1199
|
+
});
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
console.error("Error:", error);
|
|
1202
|
+
process.exit(1);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// src/cli/generate-db.ts
|
|
1208
|
+
var import_promise = __toESM(require("mysql2/promise"));
|
|
1209
|
+
async function commandGenerateDb(program2) {
|
|
1210
|
+
const command = program2.command("db").description("Generate code from database connection").option("-h, --host <host>", "MySQL database host", "127.0.0.1").option(
|
|
1211
|
+
"-P, --port <port>",
|
|
1212
|
+
"MySQL database port",
|
|
1213
|
+
(value) => parseInt(value),
|
|
1214
|
+
3306
|
|
1215
|
+
).option("-u, --user <user>", "MySQL database username", "root").option("-p, --password <password>", "MySQL database password", "").option("-d, --database <database>", "MySQL database name", "test");
|
|
1216
|
+
addCommonGenerateOptions(command);
|
|
1217
|
+
command.action(async (options) => {
|
|
1218
|
+
let connection = null;
|
|
1219
|
+
try {
|
|
1220
|
+
const connectionOptions = {
|
|
1221
|
+
host: options.host,
|
|
1222
|
+
port: options.port,
|
|
1223
|
+
user: options.user,
|
|
1224
|
+
password: options.password,
|
|
1225
|
+
database: options.database
|
|
1226
|
+
};
|
|
1227
|
+
console.log(
|
|
1228
|
+
`Connecting to ${connectionOptions.host}:${connectionOptions.port}/${connectionOptions.database}`
|
|
1229
|
+
);
|
|
1230
|
+
connection = await import_promise.default.createConnection(connectionOptions);
|
|
1231
|
+
console.log("Connected to database successfully");
|
|
1232
|
+
const [tablesResult] = await connection.execute("SHOW TABLES");
|
|
1233
|
+
const tables = tablesResult.map(
|
|
1234
|
+
(row) => Object.values(row)[0]
|
|
1235
|
+
);
|
|
1236
|
+
console.log(`Found ${tables.length} tables: ${tables.join(", ")}`);
|
|
1237
|
+
const needsNamespace = LANGUAGES_REQUIRING_NAMESPACE.includes(
|
|
1238
|
+
options.language
|
|
1239
|
+
);
|
|
1240
|
+
if (needsNamespace && !options.namespace) {
|
|
1241
|
+
options.namespace = DEFAULT_NAMESPACE;
|
|
1242
|
+
}
|
|
1243
|
+
let successCount = 0;
|
|
1244
|
+
let failCount = 0;
|
|
1245
|
+
const failedTables = [];
|
|
1246
|
+
for (const tableName of tables) {
|
|
1247
|
+
console.log(`Processing table: ${tableName}`);
|
|
1248
|
+
try {
|
|
1249
|
+
const [result] = await connection.execute(
|
|
1250
|
+
`SHOW CREATE TABLE \`${tableName}\``
|
|
1251
|
+
);
|
|
1252
|
+
const row = result[0];
|
|
1253
|
+
const createTableSql = row["Create Table"];
|
|
1254
|
+
await generateCode(createTableSql, {
|
|
1255
|
+
output: options.output,
|
|
1256
|
+
language: options.language,
|
|
1257
|
+
mode: "multi",
|
|
1258
|
+
namespace: options.namespace,
|
|
1259
|
+
dialect: options.dialect,
|
|
1260
|
+
dbName: options.dbName || options.database
|
|
1261
|
+
});
|
|
1262
|
+
successCount++;
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
console.error(
|
|
1265
|
+
`Failed to process table '${tableName}':`,
|
|
1266
|
+
error.message
|
|
1267
|
+
);
|
|
1268
|
+
failCount++;
|
|
1269
|
+
failedTables.push(tableName);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
console.log(
|
|
1273
|
+
`
|
|
1274
|
+
Summary: ${successCount} tables succeeded, ${failCount} tables failed`
|
|
1275
|
+
);
|
|
1276
|
+
if (failedTables.length > 0) {
|
|
1277
|
+
console.log(`Failed tables: ${failedTables.join(", ")}`);
|
|
1278
|
+
}
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
console.error("Error:", error);
|
|
1281
|
+
process.exit(1);
|
|
1282
|
+
} finally {
|
|
1283
|
+
if (connection) {
|
|
1284
|
+
try {
|
|
1285
|
+
await connection.end();
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
console.error("Error closing database connection:", error);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// package.json
|
|
1295
|
+
var version = "0.0.1";
|
|
1296
|
+
|
|
1297
|
+
// src/cli.ts
|
|
1298
|
+
var program = new import_commander.Command();
|
|
1299
|
+
program.name("sql-quicktype").description("Generate code from SQL schema definitions").version(version);
|
|
1300
|
+
commandGenerateSqlString(program);
|
|
1301
|
+
commandGenerateDb(program);
|
|
1302
|
+
program.parse();
|