@lark-apaas/devtool-kits 1.1.0 → 1.2.0-alpha.0

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/index.js CHANGED
@@ -514,6 +514,673 @@ function postprocessDrizzleSchema(targetPath) {
514
514
  }
515
515
  __name(postprocessDrizzleSchema, "postprocessDrizzleSchema");
516
516
 
517
+ // src/helpers/gen-nest-resource/utils.ts
518
+ function mapDrizzleTypeToTS(field) {
519
+ const typeMap = {
520
+ // String types
521
+ char: "string",
522
+ varchar: "string",
523
+ text: "string",
524
+ // Numeric types
525
+ smallint: "number",
526
+ integer: "number",
527
+ int: "number",
528
+ bigint: "string",
529
+ serial: "number",
530
+ smallserial: "number",
531
+ bigserial: "string",
532
+ // Decimal types
533
+ decimal: "string",
534
+ numeric: "string",
535
+ real: "number",
536
+ doublePrecision: "number",
537
+ // Boolean
538
+ boolean: "boolean",
539
+ // Date/Time types
540
+ timestamp: "Date",
541
+ timestamptz: "Date",
542
+ date: "Date",
543
+ time: "string",
544
+ timetz: "string",
545
+ interval: "string",
546
+ // UUID
547
+ uuid: "string",
548
+ // JSON types
549
+ json: "any",
550
+ jsonb: "any",
551
+ // Binary
552
+ bytea: "Buffer",
553
+ // Network types
554
+ inet: "string",
555
+ cidr: "string",
556
+ macaddr: "string",
557
+ macaddr8: "string",
558
+ // Geometric types
559
+ point: "{ x: number; y: number }",
560
+ line: "string",
561
+ lseg: "string",
562
+ box: "string",
563
+ path: "string",
564
+ polygon: "string",
565
+ circle: "string",
566
+ // Array types (handled by isArray flag)
567
+ array: "any[]",
568
+ // Custom types
569
+ customType: "any",
570
+ customTimestamptz: "Date",
571
+ userProfile: "string",
572
+ fileAttachment: "FileAttachment",
573
+ // Enum (handled separately)
574
+ pgEnum: "string"
575
+ };
576
+ let baseType = typeMap[field.type] || "any";
577
+ if (field.isArray) {
578
+ baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
579
+ }
580
+ if (field.enumValues && field.enumValues.length > 0) {
581
+ baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
582
+ }
583
+ return baseType;
584
+ }
585
+ __name(mapDrizzleTypeToTS, "mapDrizzleTypeToTS");
586
+ function toPascalCase(str) {
587
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
588
+ }
589
+ __name(toPascalCase, "toPascalCase");
590
+ function toKebabCase(str) {
591
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
592
+ }
593
+ __name(toKebabCase, "toKebabCase");
594
+
595
+ // src/helpers/gen-nest-resource/generator.ts
596
+ function generateDTO(table) {
597
+ const className = toPascalCase(table.variableName);
598
+ let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
599
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
600
+ import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
601
+ import { Type } from 'class-transformer';
602
+ import { FileAttachment } from '../../../database/schema';
603
+
604
+ `;
605
+ dto += `export class Create${className}Dto {
606
+ `;
607
+ for (const field of table.fields) {
608
+ if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
609
+ continue;
610
+ }
611
+ const tsType = mapDrizzleTypeToTS(field);
612
+ const optional = field.nullable || field.hasDefault ? "?" : "";
613
+ const decorators = generateValidationDecorators(field);
614
+ if (decorators) {
615
+ dto += decorators;
616
+ }
617
+ dto += ` ${field.name}${optional}: ${tsType};
618
+
619
+ `;
620
+ }
621
+ dto += "}\n\n";
622
+ dto += `export class Update${className}Dto {
623
+ `;
624
+ for (const field of table.fields) {
625
+ if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
626
+ continue;
627
+ }
628
+ const tsType = mapDrizzleTypeToTS(field);
629
+ const decorators = generateValidationDecorators(field, {
630
+ isUpdate: true
631
+ });
632
+ if (decorators) {
633
+ dto += decorators;
634
+ }
635
+ dto += ` ${field.name}?: ${tsType};
636
+
637
+ `;
638
+ }
639
+ dto += "}\n\n";
640
+ dto += `export class ${className}ResponseDto {
641
+ `;
642
+ for (const field of table.fields) {
643
+ const tsType = mapDrizzleTypeToTS(field);
644
+ const optional = field.nullable ? "?" : "";
645
+ const decorators = generateValidationDecorators(field, {
646
+ isResponse: true
647
+ });
648
+ if (decorators) {
649
+ dto += decorators;
650
+ }
651
+ dto += ` ${field.name}${optional}: ${tsType};
652
+
653
+ `;
654
+ }
655
+ dto += "}\n";
656
+ return dto;
657
+ }
658
+ __name(generateDTO, "generateDTO");
659
+ function generateValidationDecorators(field, { isUpdate = false, isResponse = false } = {}) {
660
+ let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
661
+ if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
662
+ decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
663
+ `;
664
+ if (isResponse) {
665
+ return decorators;
666
+ }
667
+ decorators += " @IsOptional()\n";
668
+ } else {
669
+ decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
670
+ `;
671
+ if (isResponse) {
672
+ return decorators;
673
+ }
674
+ decorators += " @IsDefined()\n";
675
+ }
676
+ switch (field.type) {
677
+ case "varchar":
678
+ case "char":
679
+ case "text":
680
+ decorators += " @IsString()\n";
681
+ if (field.length) {
682
+ decorators += ` @MaxLength(${field.length})
683
+ `;
684
+ }
685
+ break;
686
+ case "integer":
687
+ case "smallint":
688
+ case "serial":
689
+ case "smallserial":
690
+ decorators += " @IsInt()\n";
691
+ break;
692
+ case "decimal":
693
+ case "numeric":
694
+ case "real":
695
+ case "doublePrecision":
696
+ decorators += " @IsNumber()\n";
697
+ break;
698
+ case "boolean":
699
+ decorators += " @IsBoolean()\n";
700
+ break;
701
+ case "uuid":
702
+ decorators += " @IsUUID()\n";
703
+ break;
704
+ case "timestamp":
705
+ case "timestamptz":
706
+ case "date":
707
+ case "customTimestamptz":
708
+ decorators += " @IsDate()\n";
709
+ break;
710
+ case "json":
711
+ case "jsonb":
712
+ decorators += " @IsObject()\n";
713
+ break;
714
+ }
715
+ if (field.isArray) {
716
+ decorators += " @IsArray()\n";
717
+ }
718
+ return decorators;
719
+ }
720
+ __name(generateValidationDecorators, "generateValidationDecorators");
721
+ function generateController(table) {
722
+ const className = toPascalCase(table.variableName);
723
+ const routePath = toKebabCase(table.variableName);
724
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
725
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
726
+ const pkName = pkField ? pkField.name : "id";
727
+ const controller = `
728
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
729
+ import {
730
+ Controller,
731
+ Get,
732
+ Post,
733
+ Put,
734
+ Delete,
735
+ Body,
736
+ Param,
737
+ Query,
738
+ } from '@nestjs/common';
739
+ import {
740
+ ApiTags,
741
+ ApiOperation,
742
+ ApiOkResponse,
743
+ ApiCreatedResponse,
744
+ } from '@nestjs/swagger';
745
+ import {
746
+ Create${className}Dto,
747
+ Update${className}Dto,
748
+ ${className}ResponseDto
749
+ } from './dtos/${routePath}.dto';
750
+ import { ${className}Service } from './${routePath}.service';
751
+
752
+ @ApiTags('${toPascalCase(table.variableName)}')
753
+ @Controller('api/${routePath}')
754
+ export class ${className}Controller {
755
+ constructor(private readonly ${table.variableName}Service: ${className}Service) {}
756
+
757
+ @Post()
758
+ @ApiOperation({
759
+ summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
760
+ description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
761
+ })
762
+ @ApiCreatedResponse({
763
+ description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
764
+ type: ${className}ResponseDto,
765
+ })
766
+ async create(
767
+ @Body() createDto: Create${className}Dto
768
+ ): Promise<${className}ResponseDto> {
769
+ return this.${table.variableName}Service.create(createDto);
770
+ }
771
+
772
+ @ApiOperation({
773
+ summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
774
+ description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
775
+ })
776
+ @ApiOkResponse({
777
+ description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
778
+ type: ${className}ResponseDto,
779
+ })
780
+ @Get(':${pkName}')
781
+ async findOne(
782
+ @Param('${pkName}') ${pkName}: ${pkType}
783
+ ): Promise<${className}ResponseDto> {
784
+ return this.${table.variableName}Service.findOne(${pkName});
785
+ }
786
+
787
+ @ApiOperation({
788
+ summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
789
+ description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
790
+ })
791
+ @ApiOkResponse({
792
+ description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
793
+ type: ${className}ResponseDto,
794
+ })
795
+ @Put(':${pkName}')
796
+ async update(
797
+ @Param('${pkName}') ${pkName}: ${pkType},
798
+ @Body() updateDto: Update${className}Dto
799
+ ): Promise<${className}ResponseDto> {
800
+ return this.${table.variableName}Service.update(${pkName}, updateDto);
801
+ }
802
+
803
+ @ApiOperation({
804
+ summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
805
+ description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
806
+ })
807
+ @ApiOkResponse({
808
+ description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
809
+ })
810
+ @Delete(':${pkName}')
811
+ async remove(
812
+ @Param('${pkName}') ${pkName}: ${pkType}
813
+ ): Promise<void> {
814
+ return this.${table.variableName}Service.remove(${pkName});
815
+ }
816
+ }
817
+ `;
818
+ return controller;
819
+ }
820
+ __name(generateController, "generateController");
821
+ function generateService(table) {
822
+ const className = toPascalCase(table.variableName);
823
+ const routePath = toKebabCase(table.variableName);
824
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
825
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
826
+ const pkName = pkField ? pkField.name : "id";
827
+ const service = `
828
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
829
+ import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
830
+ import { eq } from 'drizzle-orm';
831
+ import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
832
+ import { ${table.variableName} } from '../../database/schema';
833
+ import {
834
+ Create${className}Dto,
835
+ Update${className}Dto,
836
+ ${className}ResponseDto
837
+ } from './dtos/${routePath}.dto';
838
+
839
+ @Injectable()
840
+ export class ${className}Service {
841
+ private readonly logger = new Logger(${className}Service.name);
842
+
843
+ constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
844
+
845
+ async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
846
+ const [result] = await this.db
847
+ .insert(${table.variableName})
848
+ .values(createDto)
849
+ .returning();
850
+
851
+ this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
852
+
853
+ return result;
854
+ }
855
+
856
+ async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
857
+ const { page = 1, limit = 10 } = options || {};
858
+ const offset = (page - 1) * limit;
859
+
860
+ return this.db
861
+ .select()
862
+ .from(${table.variableName})
863
+ .limit(limit)
864
+ .offset(offset);
865
+ }
866
+
867
+ async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
868
+ const [result] = await this.db
869
+ .select()
870
+ .from(${table.variableName})
871
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
872
+ .limit(1);
873
+
874
+ if (!result) {
875
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
876
+ }
877
+
878
+ return result;
879
+ }
880
+
881
+ async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
882
+ const [result] = await this.db
883
+ .update(${table.variableName})
884
+ .set(updateDto)
885
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
886
+ .returning();
887
+
888
+ if (!result) {
889
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
890
+ }
891
+
892
+ return result;
893
+ }
894
+
895
+ async remove(${pkName}: ${pkType}): Promise<void> {
896
+ const result = await this.db
897
+ .delete(${table.variableName})
898
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
899
+ .returning();
900
+
901
+ if (result.length === 0) {
902
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
903
+ }
904
+
905
+ this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
906
+ }
907
+ }
908
+ `;
909
+ return service;
910
+ }
911
+ __name(generateService, "generateService");
912
+ function generateModule(table) {
913
+ const className = toPascalCase(table.variableName);
914
+ const routePath = toKebabCase(table.variableName);
915
+ const module = `
916
+ import { Module } from '@nestjs/common';
917
+ import { ${className}Controller } from './${routePath}.controller';
918
+ import { ${className}Service } from './${routePath}.service';
919
+
920
+ @Module({
921
+ controllers: [${className}Controller],
922
+ providers: [${className}Service],
923
+ })
924
+ export class ${className}Module {}
925
+ `;
926
+ return module;
927
+ }
928
+ __name(generateModule, "generateModule");
929
+
930
+ // src/helpers/gen-nest-resource/schema-parser.ts
931
+ import { Project, Node } from "ts-morph";
932
+ var DrizzleSchemaParser = class DrizzleSchemaParser2 {
933
+ static {
934
+ __name(this, "DrizzleSchemaParser");
935
+ }
936
+ project;
937
+ constructor(projectOptions) {
938
+ this.project = new Project(projectOptions);
939
+ }
940
+ parseSchemaFile(filePath) {
941
+ const sourceFile = this.project.addSourceFileAtPath(filePath);
942
+ const tables = [];
943
+ const variableStatements = sourceFile.getVariableStatements();
944
+ for (const statement of variableStatements) {
945
+ const declarations = statement.getDeclarations();
946
+ for (const declaration of declarations) {
947
+ const initializer = declaration.getInitializer();
948
+ if (initializer && Node.isCallExpression(initializer)) {
949
+ const expression = initializer.getExpression();
950
+ if (expression.getText() === "pgTable") {
951
+ const tableInfo = this.parsePgTable(declaration.getName(), initializer);
952
+ if (tableInfo) {
953
+ tables.push(tableInfo);
954
+ }
955
+ }
956
+ }
957
+ }
958
+ }
959
+ return tables;
960
+ }
961
+ parsePgTable(variableName, callExpr) {
962
+ const args = callExpr.getArguments();
963
+ if (args.length < 2) {
964
+ return null;
965
+ }
966
+ const tableName = args[0].getText().replace(/['"]/g, "");
967
+ const fieldsArg = args[1];
968
+ if (!Node.isObjectLiteralExpression(fieldsArg)) {
969
+ return null;
970
+ }
971
+ const fields = [];
972
+ const properties = fieldsArg.getProperties();
973
+ for (const prop of properties) {
974
+ if (Node.isPropertyAssignment(prop)) {
975
+ const fieldName = prop.getName();
976
+ const initializer = prop.getInitializer();
977
+ const leadingComments = prop.getLeadingCommentRanges();
978
+ let comment;
979
+ if (leadingComments.length > 0) {
980
+ comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
981
+ }
982
+ if (initializer && Node.isCallExpression(initializer)) {
983
+ const fieldInfo = this.parseField(fieldName, initializer, comment);
984
+ fields.push(fieldInfo);
985
+ }
986
+ }
987
+ }
988
+ return {
989
+ tableName,
990
+ variableName,
991
+ fields
992
+ };
993
+ }
994
+ parseField(fieldName, callExpr, comment) {
995
+ const fieldInfo = {
996
+ name: fieldName,
997
+ columnName: fieldName,
998
+ type: "",
999
+ nullable: true,
1000
+ hasDefault: false,
1001
+ notNull: false,
1002
+ isPrimaryKey: false,
1003
+ isUnique: false,
1004
+ isArray: false,
1005
+ comment
1006
+ };
1007
+ this.parseBaseType(callExpr, fieldInfo);
1008
+ this.parseCallChain(callExpr, fieldInfo);
1009
+ return fieldInfo;
1010
+ }
1011
+ parseBaseType(callExpr, fieldInfo) {
1012
+ let current = callExpr;
1013
+ let baseCall = null;
1014
+ while (Node.isCallExpression(current)) {
1015
+ baseCall = current;
1016
+ const expression2 = current.getExpression();
1017
+ if (Node.isPropertyAccessExpression(expression2)) {
1018
+ current = expression2.getExpression();
1019
+ } else {
1020
+ break;
1021
+ }
1022
+ }
1023
+ if (!baseCall) {
1024
+ return;
1025
+ }
1026
+ const expression = baseCall.getExpression();
1027
+ let typeName = "";
1028
+ if (Node.isPropertyAccessExpression(expression)) {
1029
+ typeName = expression.getName();
1030
+ } else {
1031
+ typeName = expression.getText();
1032
+ }
1033
+ fieldInfo.type = typeName;
1034
+ const args = baseCall.getArguments();
1035
+ if (args.length > 0) {
1036
+ const firstArg = args[0];
1037
+ if (Node.isStringLiteral(firstArg)) {
1038
+ fieldInfo.columnName = firstArg.getLiteralText();
1039
+ } else if (Node.isObjectLiteralExpression(firstArg)) {
1040
+ this.parseTypeConfig(firstArg, fieldInfo);
1041
+ } else if (Node.isArrayLiteralExpression(firstArg)) {
1042
+ fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
1043
+ }
1044
+ }
1045
+ if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
1046
+ this.parseTypeConfig(args[1], fieldInfo);
1047
+ }
1048
+ }
1049
+ parseTypeConfig(objLiteral, fieldInfo) {
1050
+ if (!Node.isObjectLiteralExpression(objLiteral)) {
1051
+ return;
1052
+ }
1053
+ const properties = objLiteral.getProperties();
1054
+ for (const prop of properties) {
1055
+ if (Node.isPropertyAssignment(prop)) {
1056
+ const propName = prop.getName();
1057
+ const value = prop.getInitializer()?.getText();
1058
+ switch (propName) {
1059
+ case "length":
1060
+ fieldInfo.length = value ? parseInt(value) : void 0;
1061
+ break;
1062
+ case "precision":
1063
+ fieldInfo.precision = value ? parseInt(value) : void 0;
1064
+ break;
1065
+ case "scale":
1066
+ fieldInfo.scale = value ? parseInt(value) : void 0;
1067
+ break;
1068
+ case "default":
1069
+ fieldInfo.hasDefault = true;
1070
+ fieldInfo.defaultValue = value;
1071
+ break;
1072
+ // 时间精度(用于 timestamp, time 等)
1073
+ case "withTimezone":
1074
+ fieldInfo.withTimezone = value === "true";
1075
+ break;
1076
+ case "mode":
1077
+ fieldInfo.mode = value?.replace(/['"]/g, "");
1078
+ break;
1079
+ default:
1080
+ throw new Error(`Unsupported property: ${propName}`);
1081
+ }
1082
+ }
1083
+ }
1084
+ }
1085
+ parseCallChain(callExpr, fieldInfo) {
1086
+ let current = callExpr;
1087
+ while (Node.isCallExpression(current)) {
1088
+ const expression = current.getExpression();
1089
+ if (Node.isPropertyAccessExpression(expression)) {
1090
+ const methodName = expression.getName();
1091
+ const args = current.getArguments();
1092
+ switch (methodName) {
1093
+ case "notNull":
1094
+ fieldInfo.notNull = true;
1095
+ fieldInfo.nullable = false;
1096
+ break;
1097
+ case "default":
1098
+ fieldInfo.hasDefault = true;
1099
+ if (args.length > 0) {
1100
+ fieldInfo.defaultValue = args[0].getText();
1101
+ }
1102
+ break;
1103
+ case "defaultRandom":
1104
+ fieldInfo.hasDefault = true;
1105
+ fieldInfo.defaultValue = "random";
1106
+ break;
1107
+ case "primaryKey":
1108
+ fieldInfo.isPrimaryKey = true;
1109
+ fieldInfo.notNull = true;
1110
+ fieldInfo.nullable = false;
1111
+ break;
1112
+ case "unique":
1113
+ fieldInfo.isUnique = true;
1114
+ break;
1115
+ case "array":
1116
+ fieldInfo.isArray = true;
1117
+ break;
1118
+ case "references":
1119
+ if (args.length > 0) {
1120
+ const refArg = args[0].getText();
1121
+ const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
1122
+ if (match) {
1123
+ fieldInfo.references = {
1124
+ table: match[1],
1125
+ column: match[2]
1126
+ };
1127
+ }
1128
+ }
1129
+ break;
1130
+ default:
1131
+ throw new Error(`Unsupported method: ${methodName}`);
1132
+ }
1133
+ current = expression.getExpression();
1134
+ } else {
1135
+ break;
1136
+ }
1137
+ }
1138
+ }
1139
+ };
1140
+
1141
+ // src/helpers/gen-nest-resource/index.ts
1142
+ import { join } from "path";
1143
+ import { mkdir, rm, writeFile } from "fs/promises";
1144
+ import { existsSync } from "fs";
1145
+ async function parseAndGenerateNestResourceTemplate(options) {
1146
+ const parser = new DrizzleSchemaParser({
1147
+ tsConfigFilePath: options.tsConfigFilePath
1148
+ });
1149
+ const tables = parser.parseSchemaFile(options.schemaFilePath);
1150
+ for (const table of tables) {
1151
+ console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
1152
+ const routePath = toKebabCase(table.variableName);
1153
+ const moduleDir = join(options.moduleOutputDir, routePath);
1154
+ if (existsSync(moduleDir)) {
1155
+ console.info(`Nest.js \u6A21\u5757 ${routePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
1156
+ continue;
1157
+ }
1158
+ const dto = generateDTO(table);
1159
+ const controller = generateController(table);
1160
+ const service = generateService(table);
1161
+ const moduleFilePath = join(moduleDir, `${routePath}.module.ts`);
1162
+ const module = generateModule(table);
1163
+ try {
1164
+ await mkdir(moduleDir, {
1165
+ recursive: true
1166
+ });
1167
+ await mkdir(join(moduleDir, "dtos"), {
1168
+ recursive: true
1169
+ });
1170
+ await writeFile(join(moduleDir, "dtos", `${routePath}.dto.ts`), dto);
1171
+ await writeFile(join(moduleDir, `${routePath}.controller.ts`), controller);
1172
+ await writeFile(join(moduleDir, `${routePath}.service.ts`), service);
1173
+ await writeFile(moduleFilePath, module);
1174
+ } catch (err) {
1175
+ console.error(`\u751F\u6210 Nest.js ${routePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
1176
+ await rm(moduleDir, {
1177
+ recursive: true
1178
+ });
1179
+ }
1180
+ }
1181
+ }
1182
+ __name(parseAndGenerateNestResourceTemplate, "parseAndGenerateNestResourceTemplate");
1183
+
517
1184
  // src/helpers/proxy-error/index.ts
518
1185
  import fs3 from "fs";
519
1186
  import path3 from "path";
@@ -885,7 +1552,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
885
1552
  const startTime = Date.now();
886
1553
  const openapiPath = options.openapiPath || path5.resolve(__dirname, "../client/src/api/gen/openapi.json");
887
1554
  const serverDir = options.serverDir || path5.resolve(__dirname, "../server");
888
- const writeFile = options.writeFile !== false;
1555
+ const writeFile2 = options.writeFile !== false;
889
1556
  let openapi;
890
1557
  if (options.openapiData) {
891
1558
  openapi = JSON.parse(JSON.stringify(options.openapiData));
@@ -896,7 +1563,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
896
1563
  const controllerFiles = await findControllerFiles(serverDir);
897
1564
  const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
898
1565
  const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
899
- if (writeFile) {
1566
+ if (writeFile2) {
900
1567
  await fs5.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
901
1568
  }
902
1569
  const duration = Date.now() - startTime;
@@ -1079,7 +1746,7 @@ import express2 from "express";
1079
1746
 
1080
1747
  // src/middlewares/dev-logs/utils.ts
1081
1748
  import { promises as fs7 } from "fs";
1082
- import { isAbsolute, join, relative } from "path";
1749
+ import { isAbsolute, join as join2, relative } from "path";
1083
1750
 
1084
1751
  // src/middlewares/dev-logs/helper/path-matcher.ts
1085
1752
  function pathPatternToRegex(pattern) {
@@ -1119,9 +1786,9 @@ __name(normalizePathForMatching, "normalizePathForMatching");
1119
1786
  // src/middlewares/dev-logs/utils.ts
1120
1787
  function resolveLogDir(provided) {
1121
1788
  if (!provided) {
1122
- return join(process.cwd(), "logs");
1789
+ return join2(process.cwd(), "logs");
1123
1790
  }
1124
- return isAbsolute(provided) ? provided : join(process.cwd(), provided);
1791
+ return isAbsolute(provided) ? provided : join2(process.cwd(), provided);
1125
1792
  }
1126
1793
  __name(resolveLogDir, "resolveLogDir");
1127
1794
  function getRelativePath(filePath) {
@@ -1180,7 +1847,7 @@ function resolveLogFilePath(baseDir, fileName) {
1180
1847
  if (segments.some((segment) => segment === "..")) {
1181
1848
  throw new Error("Invalid log file path");
1182
1849
  }
1183
- const resolved = join(baseDir, segments.join("/"));
1850
+ const resolved = join2(baseDir, segments.join("/"));
1184
1851
  const rel = relative(baseDir, resolved);
1185
1852
  if (rel.startsWith("..")) {
1186
1853
  throw new Error("Access to the specified log file is denied");
@@ -1210,7 +1877,7 @@ function serializeError(error) {
1210
1877
  __name(serializeError, "serializeError");
1211
1878
 
1212
1879
  // src/middlewares/dev-logs/controller.ts
1213
- import { join as join2 } from "path";
1880
+ import { join as join3 } from "path";
1214
1881
 
1215
1882
  // src/middlewares/dev-logs/services.ts
1216
1883
  import { createReadStream, promises as fs8 } from "fs";
@@ -1439,7 +2106,7 @@ function handleError(res, error, message = "Failed to read log file") {
1439
2106
  }
1440
2107
  __name(handleError, "handleError");
1441
2108
  function createGetTraceEntriesHandler(logDir) {
1442
- const appLogPath = join2(logDir, "server.log");
2109
+ const appLogPath = join3(logDir, "server.log");
1443
2110
  return async (req, res) => {
1444
2111
  const traceId = (req.params.traceId || "").trim();
1445
2112
  if (!traceId) {
@@ -1466,7 +2133,7 @@ function createGetTraceEntriesHandler(logDir) {
1466
2133
  }
1467
2134
  __name(createGetTraceEntriesHandler, "createGetTraceEntriesHandler");
1468
2135
  function createGetRecentTracesHandler(logDir) {
1469
- const traceLogPath = join2(logDir, "trace.log");
2136
+ const traceLogPath = join3(logDir, "trace.log");
1470
2137
  return async (req, res) => {
1471
2138
  const page = parsePositiveInt(req.query.page, 1);
1472
2139
  const pageSize = parseLimit(req.query.pageSize, 10, 100);
@@ -1634,17 +2301,17 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
1634
2301
  import express3 from "express";
1635
2302
 
1636
2303
  // src/middlewares/collect-logs/controller.ts
1637
- import { join as join4 } from "path";
2304
+ import { join as join5 } from "path";
1638
2305
  import fs10 from "fs";
1639
2306
 
1640
2307
  // src/middlewares/collect-logs/utils.ts
1641
- import { isAbsolute as isAbsolute2, join as join3 } from "path";
2308
+ import { isAbsolute as isAbsolute2, join as join4 } from "path";
1642
2309
  import fs9 from "fs";
1643
2310
  function resolveLogDir2(provided) {
1644
2311
  if (!provided) {
1645
- return join3(process.cwd(), "logs");
2312
+ return join4(process.cwd(), "logs");
1646
2313
  }
1647
- return isAbsolute2(provided) ? provided : join3(process.cwd(), provided);
2314
+ return isAbsolute2(provided) ? provided : join4(process.cwd(), provided);
1648
2315
  }
1649
2316
  __name(resolveLogDir2, "resolveLogDir");
1650
2317
  function ensureDir(dir) {
@@ -1667,7 +2334,7 @@ __name(serializeError2, "serializeError");
1667
2334
 
1668
2335
  // src/middlewares/collect-logs/controller.ts
1669
2336
  function collectLogsHandler(logDir, fileName) {
1670
- const filePath = join4(logDir, fileName);
2337
+ const filePath = join5(logDir, fileName);
1671
2338
  ensureDir(logDir);
1672
2339
  return async (req, res) => {
1673
2340
  try {
@@ -1692,7 +2359,7 @@ function collectLogsHandler(logDir, fileName) {
1692
2359
  }
1693
2360
  __name(collectLogsHandler, "collectLogsHandler");
1694
2361
  function collectLogsBatchHandler(logDir, fileName) {
1695
- const filePath = join4(logDir, fileName);
2362
+ const filePath = join5(logDir, fileName);
1696
2363
  ensureDir(logDir);
1697
2364
  return async (req, res) => {
1698
2365
  try {
@@ -1847,6 +2514,7 @@ export {
1847
2514
  createOpenapiMiddleware,
1848
2515
  handleDevProxyError,
1849
2516
  normalizeBasePath,
2517
+ parseAndGenerateNestResourceTemplate,
1850
2518
  postprocessDrizzleSchema,
1851
2519
  registerMiddlewares
1852
2520
  };