@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.cjs CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  createOpenapiMiddleware: () => createOpenapiMiddleware,
37
37
  handleDevProxyError: () => handleDevProxyError,
38
38
  normalizeBasePath: () => normalizeBasePath,
39
+ parseAndGenerateNestResourceTemplate: () => parseAndGenerateNestResourceTemplate,
39
40
  postprocessDrizzleSchema: () => postprocessDrizzleSchema,
40
41
  registerMiddlewares: () => registerMiddlewares
41
42
  });
@@ -554,6 +555,673 @@ function postprocessDrizzleSchema(targetPath) {
554
555
  }
555
556
  __name(postprocessDrizzleSchema, "postprocessDrizzleSchema");
556
557
 
558
+ // src/helpers/gen-nest-resource/utils.ts
559
+ function mapDrizzleTypeToTS(field) {
560
+ const typeMap = {
561
+ // String types
562
+ char: "string",
563
+ varchar: "string",
564
+ text: "string",
565
+ // Numeric types
566
+ smallint: "number",
567
+ integer: "number",
568
+ int: "number",
569
+ bigint: "string",
570
+ serial: "number",
571
+ smallserial: "number",
572
+ bigserial: "string",
573
+ // Decimal types
574
+ decimal: "string",
575
+ numeric: "string",
576
+ real: "number",
577
+ doublePrecision: "number",
578
+ // Boolean
579
+ boolean: "boolean",
580
+ // Date/Time types
581
+ timestamp: "Date",
582
+ timestamptz: "Date",
583
+ date: "Date",
584
+ time: "string",
585
+ timetz: "string",
586
+ interval: "string",
587
+ // UUID
588
+ uuid: "string",
589
+ // JSON types
590
+ json: "any",
591
+ jsonb: "any",
592
+ // Binary
593
+ bytea: "Buffer",
594
+ // Network types
595
+ inet: "string",
596
+ cidr: "string",
597
+ macaddr: "string",
598
+ macaddr8: "string",
599
+ // Geometric types
600
+ point: "{ x: number; y: number }",
601
+ line: "string",
602
+ lseg: "string",
603
+ box: "string",
604
+ path: "string",
605
+ polygon: "string",
606
+ circle: "string",
607
+ // Array types (handled by isArray flag)
608
+ array: "any[]",
609
+ // Custom types
610
+ customType: "any",
611
+ customTimestamptz: "Date",
612
+ userProfile: "string",
613
+ fileAttachment: "FileAttachment",
614
+ // Enum (handled separately)
615
+ pgEnum: "string"
616
+ };
617
+ let baseType = typeMap[field.type] || "any";
618
+ if (field.isArray) {
619
+ baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
620
+ }
621
+ if (field.enumValues && field.enumValues.length > 0) {
622
+ baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
623
+ }
624
+ return baseType;
625
+ }
626
+ __name(mapDrizzleTypeToTS, "mapDrizzleTypeToTS");
627
+ function toPascalCase(str) {
628
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
629
+ }
630
+ __name(toPascalCase, "toPascalCase");
631
+ function toKebabCase(str) {
632
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
633
+ }
634
+ __name(toKebabCase, "toKebabCase");
635
+
636
+ // src/helpers/gen-nest-resource/generator.ts
637
+ function generateDTO(table) {
638
+ const className = toPascalCase(table.variableName);
639
+ let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
640
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
641
+ import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
642
+ import { Type } from 'class-transformer';
643
+ import { FileAttachment } from '../../../database/schema';
644
+
645
+ `;
646
+ dto += `export class Create${className}Dto {
647
+ `;
648
+ for (const field of table.fields) {
649
+ if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
650
+ continue;
651
+ }
652
+ const tsType = mapDrizzleTypeToTS(field);
653
+ const optional = field.nullable || field.hasDefault ? "?" : "";
654
+ const decorators = generateValidationDecorators(field);
655
+ if (decorators) {
656
+ dto += decorators;
657
+ }
658
+ dto += ` ${field.name}${optional}: ${tsType};
659
+
660
+ `;
661
+ }
662
+ dto += "}\n\n";
663
+ dto += `export class Update${className}Dto {
664
+ `;
665
+ for (const field of table.fields) {
666
+ if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
667
+ continue;
668
+ }
669
+ const tsType = mapDrizzleTypeToTS(field);
670
+ const decorators = generateValidationDecorators(field, {
671
+ isUpdate: true
672
+ });
673
+ if (decorators) {
674
+ dto += decorators;
675
+ }
676
+ dto += ` ${field.name}?: ${tsType};
677
+
678
+ `;
679
+ }
680
+ dto += "}\n\n";
681
+ dto += `export class ${className}ResponseDto {
682
+ `;
683
+ for (const field of table.fields) {
684
+ const tsType = mapDrizzleTypeToTS(field);
685
+ const optional = field.nullable ? "?" : "";
686
+ const decorators = generateValidationDecorators(field, {
687
+ isResponse: true
688
+ });
689
+ if (decorators) {
690
+ dto += decorators;
691
+ }
692
+ dto += ` ${field.name}${optional}: ${tsType};
693
+
694
+ `;
695
+ }
696
+ dto += "}\n";
697
+ return dto;
698
+ }
699
+ __name(generateDTO, "generateDTO");
700
+ function generateValidationDecorators(field, { isUpdate = false, isResponse = false } = {}) {
701
+ let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
702
+ if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
703
+ decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
704
+ `;
705
+ if (isResponse) {
706
+ return decorators;
707
+ }
708
+ decorators += " @IsOptional()\n";
709
+ } else {
710
+ decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
711
+ `;
712
+ if (isResponse) {
713
+ return decorators;
714
+ }
715
+ decorators += " @IsDefined()\n";
716
+ }
717
+ switch (field.type) {
718
+ case "varchar":
719
+ case "char":
720
+ case "text":
721
+ decorators += " @IsString()\n";
722
+ if (field.length) {
723
+ decorators += ` @MaxLength(${field.length})
724
+ `;
725
+ }
726
+ break;
727
+ case "integer":
728
+ case "smallint":
729
+ case "serial":
730
+ case "smallserial":
731
+ decorators += " @IsInt()\n";
732
+ break;
733
+ case "decimal":
734
+ case "numeric":
735
+ case "real":
736
+ case "doublePrecision":
737
+ decorators += " @IsNumber()\n";
738
+ break;
739
+ case "boolean":
740
+ decorators += " @IsBoolean()\n";
741
+ break;
742
+ case "uuid":
743
+ decorators += " @IsUUID()\n";
744
+ break;
745
+ case "timestamp":
746
+ case "timestamptz":
747
+ case "date":
748
+ case "customTimestamptz":
749
+ decorators += " @IsDate()\n";
750
+ break;
751
+ case "json":
752
+ case "jsonb":
753
+ decorators += " @IsObject()\n";
754
+ break;
755
+ }
756
+ if (field.isArray) {
757
+ decorators += " @IsArray()\n";
758
+ }
759
+ return decorators;
760
+ }
761
+ __name(generateValidationDecorators, "generateValidationDecorators");
762
+ function generateController(table) {
763
+ const className = toPascalCase(table.variableName);
764
+ const routePath = toKebabCase(table.variableName);
765
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
766
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
767
+ const pkName = pkField ? pkField.name : "id";
768
+ const controller = `
769
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
770
+ import {
771
+ Controller,
772
+ Get,
773
+ Post,
774
+ Put,
775
+ Delete,
776
+ Body,
777
+ Param,
778
+ Query,
779
+ } from '@nestjs/common';
780
+ import {
781
+ ApiTags,
782
+ ApiOperation,
783
+ ApiOkResponse,
784
+ ApiCreatedResponse,
785
+ } from '@nestjs/swagger';
786
+ import {
787
+ Create${className}Dto,
788
+ Update${className}Dto,
789
+ ${className}ResponseDto
790
+ } from './dtos/${routePath}.dto';
791
+ import { ${className}Service } from './${routePath}.service';
792
+
793
+ @ApiTags('${toPascalCase(table.variableName)}')
794
+ @Controller('api/${routePath}')
795
+ export class ${className}Controller {
796
+ constructor(private readonly ${table.variableName}Service: ${className}Service) {}
797
+
798
+ @Post()
799
+ @ApiOperation({
800
+ summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
801
+ description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
802
+ })
803
+ @ApiCreatedResponse({
804
+ description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
805
+ type: ${className}ResponseDto,
806
+ })
807
+ async create(
808
+ @Body() createDto: Create${className}Dto
809
+ ): Promise<${className}ResponseDto> {
810
+ return this.${table.variableName}Service.create(createDto);
811
+ }
812
+
813
+ @ApiOperation({
814
+ summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
815
+ description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
816
+ })
817
+ @ApiOkResponse({
818
+ description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
819
+ type: ${className}ResponseDto,
820
+ })
821
+ @Get(':${pkName}')
822
+ async findOne(
823
+ @Param('${pkName}') ${pkName}: ${pkType}
824
+ ): Promise<${className}ResponseDto> {
825
+ return this.${table.variableName}Service.findOne(${pkName});
826
+ }
827
+
828
+ @ApiOperation({
829
+ summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
830
+ description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
831
+ })
832
+ @ApiOkResponse({
833
+ description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
834
+ type: ${className}ResponseDto,
835
+ })
836
+ @Put(':${pkName}')
837
+ async update(
838
+ @Param('${pkName}') ${pkName}: ${pkType},
839
+ @Body() updateDto: Update${className}Dto
840
+ ): Promise<${className}ResponseDto> {
841
+ return this.${table.variableName}Service.update(${pkName}, updateDto);
842
+ }
843
+
844
+ @ApiOperation({
845
+ summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
846
+ description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
847
+ })
848
+ @ApiOkResponse({
849
+ description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
850
+ })
851
+ @Delete(':${pkName}')
852
+ async remove(
853
+ @Param('${pkName}') ${pkName}: ${pkType}
854
+ ): Promise<void> {
855
+ return this.${table.variableName}Service.remove(${pkName});
856
+ }
857
+ }
858
+ `;
859
+ return controller;
860
+ }
861
+ __name(generateController, "generateController");
862
+ function generateService(table) {
863
+ const className = toPascalCase(table.variableName);
864
+ const routePath = toKebabCase(table.variableName);
865
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
866
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
867
+ const pkName = pkField ? pkField.name : "id";
868
+ const service = `
869
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
870
+ import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
871
+ import { eq } from 'drizzle-orm';
872
+ import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
873
+ import { ${table.variableName} } from '../../database/schema';
874
+ import {
875
+ Create${className}Dto,
876
+ Update${className}Dto,
877
+ ${className}ResponseDto
878
+ } from './dtos/${routePath}.dto';
879
+
880
+ @Injectable()
881
+ export class ${className}Service {
882
+ private readonly logger = new Logger(${className}Service.name);
883
+
884
+ constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
885
+
886
+ async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
887
+ const [result] = await this.db
888
+ .insert(${table.variableName})
889
+ .values(createDto)
890
+ .returning();
891
+
892
+ this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
893
+
894
+ return result;
895
+ }
896
+
897
+ async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
898
+ const { page = 1, limit = 10 } = options || {};
899
+ const offset = (page - 1) * limit;
900
+
901
+ return this.db
902
+ .select()
903
+ .from(${table.variableName})
904
+ .limit(limit)
905
+ .offset(offset);
906
+ }
907
+
908
+ async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
909
+ const [result] = await this.db
910
+ .select()
911
+ .from(${table.variableName})
912
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
913
+ .limit(1);
914
+
915
+ if (!result) {
916
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
917
+ }
918
+
919
+ return result;
920
+ }
921
+
922
+ async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
923
+ const [result] = await this.db
924
+ .update(${table.variableName})
925
+ .set(updateDto)
926
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
927
+ .returning();
928
+
929
+ if (!result) {
930
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
931
+ }
932
+
933
+ return result;
934
+ }
935
+
936
+ async remove(${pkName}: ${pkType}): Promise<void> {
937
+ const result = await this.db
938
+ .delete(${table.variableName})
939
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
940
+ .returning();
941
+
942
+ if (result.length === 0) {
943
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
944
+ }
945
+
946
+ this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
947
+ }
948
+ }
949
+ `;
950
+ return service;
951
+ }
952
+ __name(generateService, "generateService");
953
+ function generateModule(table) {
954
+ const className = toPascalCase(table.variableName);
955
+ const routePath = toKebabCase(table.variableName);
956
+ const module2 = `
957
+ import { Module } from '@nestjs/common';
958
+ import { ${className}Controller } from './${routePath}.controller';
959
+ import { ${className}Service } from './${routePath}.service';
960
+
961
+ @Module({
962
+ controllers: [${className}Controller],
963
+ providers: [${className}Service],
964
+ })
965
+ export class ${className}Module {}
966
+ `;
967
+ return module2;
968
+ }
969
+ __name(generateModule, "generateModule");
970
+
971
+ // src/helpers/gen-nest-resource/schema-parser.ts
972
+ var import_ts_morph = require("ts-morph");
973
+ var DrizzleSchemaParser = class DrizzleSchemaParser2 {
974
+ static {
975
+ __name(this, "DrizzleSchemaParser");
976
+ }
977
+ project;
978
+ constructor(projectOptions) {
979
+ this.project = new import_ts_morph.Project(projectOptions);
980
+ }
981
+ parseSchemaFile(filePath) {
982
+ const sourceFile = this.project.addSourceFileAtPath(filePath);
983
+ const tables = [];
984
+ const variableStatements = sourceFile.getVariableStatements();
985
+ for (const statement of variableStatements) {
986
+ const declarations = statement.getDeclarations();
987
+ for (const declaration of declarations) {
988
+ const initializer = declaration.getInitializer();
989
+ if (initializer && import_ts_morph.Node.isCallExpression(initializer)) {
990
+ const expression = initializer.getExpression();
991
+ if (expression.getText() === "pgTable") {
992
+ const tableInfo = this.parsePgTable(declaration.getName(), initializer);
993
+ if (tableInfo) {
994
+ tables.push(tableInfo);
995
+ }
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ return tables;
1001
+ }
1002
+ parsePgTable(variableName, callExpr) {
1003
+ const args = callExpr.getArguments();
1004
+ if (args.length < 2) {
1005
+ return null;
1006
+ }
1007
+ const tableName = args[0].getText().replace(/['"]/g, "");
1008
+ const fieldsArg = args[1];
1009
+ if (!import_ts_morph.Node.isObjectLiteralExpression(fieldsArg)) {
1010
+ return null;
1011
+ }
1012
+ const fields = [];
1013
+ const properties = fieldsArg.getProperties();
1014
+ for (const prop of properties) {
1015
+ if (import_ts_morph.Node.isPropertyAssignment(prop)) {
1016
+ const fieldName = prop.getName();
1017
+ const initializer = prop.getInitializer();
1018
+ const leadingComments = prop.getLeadingCommentRanges();
1019
+ let comment;
1020
+ if (leadingComments.length > 0) {
1021
+ comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
1022
+ }
1023
+ if (initializer && import_ts_morph.Node.isCallExpression(initializer)) {
1024
+ const fieldInfo = this.parseField(fieldName, initializer, comment);
1025
+ fields.push(fieldInfo);
1026
+ }
1027
+ }
1028
+ }
1029
+ return {
1030
+ tableName,
1031
+ variableName,
1032
+ fields
1033
+ };
1034
+ }
1035
+ parseField(fieldName, callExpr, comment) {
1036
+ const fieldInfo = {
1037
+ name: fieldName,
1038
+ columnName: fieldName,
1039
+ type: "",
1040
+ nullable: true,
1041
+ hasDefault: false,
1042
+ notNull: false,
1043
+ isPrimaryKey: false,
1044
+ isUnique: false,
1045
+ isArray: false,
1046
+ comment
1047
+ };
1048
+ this.parseBaseType(callExpr, fieldInfo);
1049
+ this.parseCallChain(callExpr, fieldInfo);
1050
+ return fieldInfo;
1051
+ }
1052
+ parseBaseType(callExpr, fieldInfo) {
1053
+ let current = callExpr;
1054
+ let baseCall = null;
1055
+ while (import_ts_morph.Node.isCallExpression(current)) {
1056
+ baseCall = current;
1057
+ const expression2 = current.getExpression();
1058
+ if (import_ts_morph.Node.isPropertyAccessExpression(expression2)) {
1059
+ current = expression2.getExpression();
1060
+ } else {
1061
+ break;
1062
+ }
1063
+ }
1064
+ if (!baseCall) {
1065
+ return;
1066
+ }
1067
+ const expression = baseCall.getExpression();
1068
+ let typeName = "";
1069
+ if (import_ts_morph.Node.isPropertyAccessExpression(expression)) {
1070
+ typeName = expression.getName();
1071
+ } else {
1072
+ typeName = expression.getText();
1073
+ }
1074
+ fieldInfo.type = typeName;
1075
+ const args = baseCall.getArguments();
1076
+ if (args.length > 0) {
1077
+ const firstArg = args[0];
1078
+ if (import_ts_morph.Node.isStringLiteral(firstArg)) {
1079
+ fieldInfo.columnName = firstArg.getLiteralText();
1080
+ } else if (import_ts_morph.Node.isObjectLiteralExpression(firstArg)) {
1081
+ this.parseTypeConfig(firstArg, fieldInfo);
1082
+ } else if (import_ts_morph.Node.isArrayLiteralExpression(firstArg)) {
1083
+ fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
1084
+ }
1085
+ }
1086
+ if (args.length > 1 && import_ts_morph.Node.isObjectLiteralExpression(args[1])) {
1087
+ this.parseTypeConfig(args[1], fieldInfo);
1088
+ }
1089
+ }
1090
+ parseTypeConfig(objLiteral, fieldInfo) {
1091
+ if (!import_ts_morph.Node.isObjectLiteralExpression(objLiteral)) {
1092
+ return;
1093
+ }
1094
+ const properties = objLiteral.getProperties();
1095
+ for (const prop of properties) {
1096
+ if (import_ts_morph.Node.isPropertyAssignment(prop)) {
1097
+ const propName = prop.getName();
1098
+ const value = prop.getInitializer()?.getText();
1099
+ switch (propName) {
1100
+ case "length":
1101
+ fieldInfo.length = value ? parseInt(value) : void 0;
1102
+ break;
1103
+ case "precision":
1104
+ fieldInfo.precision = value ? parseInt(value) : void 0;
1105
+ break;
1106
+ case "scale":
1107
+ fieldInfo.scale = value ? parseInt(value) : void 0;
1108
+ break;
1109
+ case "default":
1110
+ fieldInfo.hasDefault = true;
1111
+ fieldInfo.defaultValue = value;
1112
+ break;
1113
+ // 时间精度(用于 timestamp, time 等)
1114
+ case "withTimezone":
1115
+ fieldInfo.withTimezone = value === "true";
1116
+ break;
1117
+ case "mode":
1118
+ fieldInfo.mode = value?.replace(/['"]/g, "");
1119
+ break;
1120
+ default:
1121
+ throw new Error(`Unsupported property: ${propName}`);
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ parseCallChain(callExpr, fieldInfo) {
1127
+ let current = callExpr;
1128
+ while (import_ts_morph.Node.isCallExpression(current)) {
1129
+ const expression = current.getExpression();
1130
+ if (import_ts_morph.Node.isPropertyAccessExpression(expression)) {
1131
+ const methodName = expression.getName();
1132
+ const args = current.getArguments();
1133
+ switch (methodName) {
1134
+ case "notNull":
1135
+ fieldInfo.notNull = true;
1136
+ fieldInfo.nullable = false;
1137
+ break;
1138
+ case "default":
1139
+ fieldInfo.hasDefault = true;
1140
+ if (args.length > 0) {
1141
+ fieldInfo.defaultValue = args[0].getText();
1142
+ }
1143
+ break;
1144
+ case "defaultRandom":
1145
+ fieldInfo.hasDefault = true;
1146
+ fieldInfo.defaultValue = "random";
1147
+ break;
1148
+ case "primaryKey":
1149
+ fieldInfo.isPrimaryKey = true;
1150
+ fieldInfo.notNull = true;
1151
+ fieldInfo.nullable = false;
1152
+ break;
1153
+ case "unique":
1154
+ fieldInfo.isUnique = true;
1155
+ break;
1156
+ case "array":
1157
+ fieldInfo.isArray = true;
1158
+ break;
1159
+ case "references":
1160
+ if (args.length > 0) {
1161
+ const refArg = args[0].getText();
1162
+ const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
1163
+ if (match) {
1164
+ fieldInfo.references = {
1165
+ table: match[1],
1166
+ column: match[2]
1167
+ };
1168
+ }
1169
+ }
1170
+ break;
1171
+ default:
1172
+ throw new Error(`Unsupported method: ${methodName}`);
1173
+ }
1174
+ current = expression.getExpression();
1175
+ } else {
1176
+ break;
1177
+ }
1178
+ }
1179
+ }
1180
+ };
1181
+
1182
+ // src/helpers/gen-nest-resource/index.ts
1183
+ var import_path = require("path");
1184
+ var import_promises = require("fs/promises");
1185
+ var import_fs = require("fs");
1186
+ async function parseAndGenerateNestResourceTemplate(options) {
1187
+ const parser = new DrizzleSchemaParser({
1188
+ tsConfigFilePath: options.tsConfigFilePath
1189
+ });
1190
+ const tables = parser.parseSchemaFile(options.schemaFilePath);
1191
+ for (const table of tables) {
1192
+ console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
1193
+ const routePath = toKebabCase(table.variableName);
1194
+ const moduleDir = (0, import_path.join)(options.moduleOutputDir, routePath);
1195
+ if ((0, import_fs.existsSync)(moduleDir)) {
1196
+ console.info(`Nest.js \u6A21\u5757 ${routePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
1197
+ continue;
1198
+ }
1199
+ const dto = generateDTO(table);
1200
+ const controller = generateController(table);
1201
+ const service = generateService(table);
1202
+ const moduleFilePath = (0, import_path.join)(moduleDir, `${routePath}.module.ts`);
1203
+ const module2 = generateModule(table);
1204
+ try {
1205
+ await (0, import_promises.mkdir)(moduleDir, {
1206
+ recursive: true
1207
+ });
1208
+ await (0, import_promises.mkdir)((0, import_path.join)(moduleDir, "dtos"), {
1209
+ recursive: true
1210
+ });
1211
+ await (0, import_promises.writeFile)((0, import_path.join)(moduleDir, "dtos", `${routePath}.dto.ts`), dto);
1212
+ await (0, import_promises.writeFile)((0, import_path.join)(moduleDir, `${routePath}.controller.ts`), controller);
1213
+ await (0, import_promises.writeFile)((0, import_path.join)(moduleDir, `${routePath}.service.ts`), service);
1214
+ await (0, import_promises.writeFile)(moduleFilePath, module2);
1215
+ } catch (err) {
1216
+ console.error(`\u751F\u6210 Nest.js ${routePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
1217
+ await (0, import_promises.rm)(moduleDir, {
1218
+ recursive: true
1219
+ });
1220
+ }
1221
+ }
1222
+ }
1223
+ __name(parseAndGenerateNestResourceTemplate, "parseAndGenerateNestResourceTemplate");
1224
+
557
1225
  // src/helpers/proxy-error/index.ts
558
1226
  var import_node_fs3 = __toESM(require("fs"), 1);
559
1227
  var import_node_path3 = __toESM(require("path"), 1);
@@ -813,7 +1481,7 @@ var import_node_path9 = __toESM(require("path"), 1);
813
1481
  var import_express = __toESM(require("express"), 1);
814
1482
 
815
1483
  // src/middlewares/openapi/controller.ts
816
- var import_promises = __toESM(require("fs/promises"), 1);
1484
+ var import_promises2 = __toESM(require("fs/promises"), 1);
817
1485
  var import_node_crypto = __toESM(require("crypto"), 1);
818
1486
 
819
1487
  // src/middlewares/openapi/services.ts
@@ -925,7 +1593,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
925
1593
  const startTime = Date.now();
926
1594
  const openapiPath = options.openapiPath || import_node_path5.default.resolve(__dirname, "../client/src/api/gen/openapi.json");
927
1595
  const serverDir = options.serverDir || import_node_path5.default.resolve(__dirname, "../server");
928
- const writeFile = options.writeFile !== false;
1596
+ const writeFile2 = options.writeFile !== false;
929
1597
  let openapi;
930
1598
  if (options.openapiData) {
931
1599
  openapi = JSON.parse(JSON.stringify(options.openapiData));
@@ -936,7 +1604,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
936
1604
  const controllerFiles = await findControllerFiles(serverDir);
937
1605
  const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
938
1606
  const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
939
- if (writeFile) {
1607
+ if (writeFile2) {
940
1608
  await import_node_fs5.promises.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
941
1609
  }
942
1610
  const duration = Date.now() - startTime;
@@ -1046,7 +1714,7 @@ function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
1046
1714
  let cache = null;
1047
1715
  return async (_req, res, context) => {
1048
1716
  try {
1049
- const fileBuffer = await import_promises.default.readFile(openapiFilePath, "utf-8");
1717
+ const fileBuffer = await import_promises2.default.readFile(openapiFilePath, "utf-8");
1050
1718
  const currentHash = import_node_crypto.default.createHash("md5").update(fileBuffer).digest("hex");
1051
1719
  if (cache && cache.fileHash === currentHash) {
1052
1720
  return res.json(cache.data);
@@ -1674,8 +2342,8 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
1674
2342
  var import_express3 = __toESM(require("express"), 1);
1675
2343
 
1676
2344
  // src/middlewares/collect-logs/controller.ts
1677
- var import_path = require("path");
1678
- var import_fs = __toESM(require("fs"), 1);
2345
+ var import_path2 = require("path");
2346
+ var import_fs2 = __toESM(require("fs"), 1);
1679
2347
 
1680
2348
  // src/middlewares/collect-logs/utils.ts
1681
2349
  var import_node_path8 = require("path");
@@ -1707,7 +2375,7 @@ __name(serializeError2, "serializeError");
1707
2375
 
1708
2376
  // src/middlewares/collect-logs/controller.ts
1709
2377
  function collectLogsHandler(logDir, fileName) {
1710
- const filePath = (0, import_path.join)(logDir, fileName);
2378
+ const filePath = (0, import_path2.join)(logDir, fileName);
1711
2379
  ensureDir(logDir);
1712
2380
  return async (req, res) => {
1713
2381
  try {
@@ -1721,7 +2389,7 @@ function collectLogsHandler(logDir, fileName) {
1721
2389
  ...logContent,
1722
2390
  server_time: (/* @__PURE__ */ new Date()).toISOString()
1723
2391
  }) + "\n";
1724
- await import_fs.default.promises.appendFile(filePath, logLine);
2392
+ await import_fs2.default.promises.appendFile(filePath, logLine);
1725
2393
  res.json({
1726
2394
  success: true
1727
2395
  });
@@ -1732,7 +2400,7 @@ function collectLogsHandler(logDir, fileName) {
1732
2400
  }
1733
2401
  __name(collectLogsHandler, "collectLogsHandler");
1734
2402
  function collectLogsBatchHandler(logDir, fileName) {
1735
- const filePath = (0, import_path.join)(logDir, fileName);
2403
+ const filePath = (0, import_path2.join)(logDir, fileName);
1736
2404
  ensureDir(logDir);
1737
2405
  return async (req, res) => {
1738
2406
  try {
@@ -1749,7 +2417,7 @@ function collectLogsBatchHandler(logDir, fileName) {
1749
2417
  server_time: (/* @__PURE__ */ new Date()).toISOString()
1750
2418
  }) + "\n");
1751
2419
  }
1752
- await import_fs.default.promises.appendFile(filePath, logLines.join(""));
2420
+ await import_fs2.default.promises.appendFile(filePath, logLines.join(""));
1753
2421
  res.json({
1754
2422
  success: true
1755
2423
  });
@@ -1888,6 +2556,7 @@ __name(registerMiddlewares, "registerMiddlewares");
1888
2556
  createOpenapiMiddleware,
1889
2557
  handleDevProxyError,
1890
2558
  normalizeBasePath,
2559
+ parseAndGenerateNestResourceTemplate,
1891
2560
  postprocessDrizzleSchema,
1892
2561
  registerMiddlewares
1893
2562
  });