@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/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();