@lark-apaas/devtool-kits 1.1.1-alpha.0 → 1.2.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);
@@ -729,10 +1397,17 @@ function checkForErrors(logs) {
729
1397
  return false;
730
1398
  }
731
1399
  __name(checkForErrors, "checkForErrors");
732
- function injectTemplateData(template, clientBasePath) {
733
- return template.replace("{{.clientBasePath}}", clientBasePath);
1400
+ function injectErrorData(template, errorLogs, clientBasePath) {
1401
+ let logsText = "";
1402
+ if (errorLogs.length > 0) {
1403
+ logsText = errorLogs.join("\n");
1404
+ } else {
1405
+ logsText = "\u672A\u627E\u5230\u76F8\u5173\u9519\u8BEF\u65E5\u5FD7";
1406
+ }
1407
+ return template.replace("{{.errorData.message}}", `\u670D\u52A1\u542F\u52A8\u5F02\u5E38\uFF0C\u8BF7\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u76F8\u5173\u95EE\u9898
1408
+ ${JSON.stringify(logsText)}`).replace("{{.clientBasePath}}", clientBasePath);
734
1409
  }
735
- __name(injectTemplateData, "injectTemplateData");
1410
+ __name(injectErrorData, "injectErrorData");
736
1411
  function handleDevProxyError(err, req, res, options) {
737
1412
  const { logDir = import_node_path3.default.join(process.cwd(), "logs"), maxErrorLogs = 100, logFileName = "server.log", retryTimeout = 5e3, retryInterval = 500, target = `http://localhost:${process.env.SERVER_PORT || 3e3}`, clientBasePath = process.env.CLIENT_BASE_PATH || "/" } = options || {};
738
1413
  const clientBasePathWithoutSlash = normalizeBasePath(clientBasePath);
@@ -744,7 +1419,7 @@ function handleDevProxyError(err, req, res, options) {
744
1419
  (async () => {
745
1420
  try {
746
1421
  const isConnError = isConnectionError(err);
747
- const { hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
1422
+ const { logs: errorLogs, hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
748
1423
  if (isConnError && !hasCompileError) {
749
1424
  console.log("[Proxy Error]: Connection error without compile errors, possibly server restarting...");
750
1425
  try {
@@ -768,7 +1443,7 @@ function handleDevProxyError(err, req, res, options) {
768
1443
  console.log("[Proxy Error]: Compile error or non-connection error, showing error page");
769
1444
  }
770
1445
  const template = getErrorHtmlTemplate();
771
- const html = injectTemplateData(template, clientBasePathWithoutSlash);
1446
+ const html = injectErrorData(template, errorLogs, clientBasePathWithoutSlash);
772
1447
  res.writeHead(200, {
773
1448
  "Content-Type": "text/html; charset=utf-8",
774
1449
  "Cache-Control": "no-cache, no-store, must-revalidate",
@@ -806,7 +1481,7 @@ var import_node_path9 = __toESM(require("path"), 1);
806
1481
  var import_express = __toESM(require("express"), 1);
807
1482
 
808
1483
  // src/middlewares/openapi/controller.ts
809
- var import_promises = __toESM(require("fs/promises"), 1);
1484
+ var import_promises2 = __toESM(require("fs/promises"), 1);
810
1485
  var import_node_crypto = __toESM(require("crypto"), 1);
811
1486
 
812
1487
  // src/middlewares/openapi/services.ts
@@ -918,7 +1593,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
918
1593
  const startTime = Date.now();
919
1594
  const openapiPath = options.openapiPath || import_node_path5.default.resolve(__dirname, "../client/src/api/gen/openapi.json");
920
1595
  const serverDir = options.serverDir || import_node_path5.default.resolve(__dirname, "../server");
921
- const writeFile = options.writeFile !== false;
1596
+ const writeFile2 = options.writeFile !== false;
922
1597
  let openapi;
923
1598
  if (options.openapiData) {
924
1599
  openapi = JSON.parse(JSON.stringify(options.openapiData));
@@ -929,7 +1604,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
929
1604
  const controllerFiles = await findControllerFiles(serverDir);
930
1605
  const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
931
1606
  const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
932
- if (writeFile) {
1607
+ if (writeFile2) {
933
1608
  await import_node_fs5.promises.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
934
1609
  }
935
1610
  const duration = Date.now() - startTime;
@@ -1039,7 +1714,7 @@ function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
1039
1714
  let cache = null;
1040
1715
  return async (_req, res, context) => {
1041
1716
  try {
1042
- const fileBuffer = await import_promises.default.readFile(openapiFilePath, "utf-8");
1717
+ const fileBuffer = await import_promises2.default.readFile(openapiFilePath, "utf-8");
1043
1718
  const currentHash = import_node_crypto.default.createHash("md5").update(fileBuffer).digest("hex");
1044
1719
  if (cache && cache.fileHash === currentHash) {
1045
1720
  return res.json(cache.data);
@@ -1667,8 +2342,8 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
1667
2342
  var import_express3 = __toESM(require("express"), 1);
1668
2343
 
1669
2344
  // src/middlewares/collect-logs/controller.ts
1670
- var import_path = require("path");
1671
- var import_fs = __toESM(require("fs"), 1);
2345
+ var import_path2 = require("path");
2346
+ var import_fs2 = __toESM(require("fs"), 1);
1672
2347
 
1673
2348
  // src/middlewares/collect-logs/utils.ts
1674
2349
  var import_node_path8 = require("path");
@@ -1700,7 +2375,7 @@ __name(serializeError2, "serializeError");
1700
2375
 
1701
2376
  // src/middlewares/collect-logs/controller.ts
1702
2377
  function collectLogsHandler(logDir, fileName) {
1703
- const filePath = (0, import_path.join)(logDir, fileName);
2378
+ const filePath = (0, import_path2.join)(logDir, fileName);
1704
2379
  ensureDir(logDir);
1705
2380
  return async (req, res) => {
1706
2381
  try {
@@ -1714,7 +2389,7 @@ function collectLogsHandler(logDir, fileName) {
1714
2389
  ...logContent,
1715
2390
  server_time: (/* @__PURE__ */ new Date()).toISOString()
1716
2391
  }) + "\n";
1717
- await import_fs.default.promises.appendFile(filePath, logLine);
2392
+ await import_fs2.default.promises.appendFile(filePath, logLine);
1718
2393
  res.json({
1719
2394
  success: true
1720
2395
  });
@@ -1725,7 +2400,7 @@ function collectLogsHandler(logDir, fileName) {
1725
2400
  }
1726
2401
  __name(collectLogsHandler, "collectLogsHandler");
1727
2402
  function collectLogsBatchHandler(logDir, fileName) {
1728
- const filePath = (0, import_path.join)(logDir, fileName);
2403
+ const filePath = (0, import_path2.join)(logDir, fileName);
1729
2404
  ensureDir(logDir);
1730
2405
  return async (req, res) => {
1731
2406
  try {
@@ -1742,7 +2417,7 @@ function collectLogsBatchHandler(logDir, fileName) {
1742
2417
  server_time: (/* @__PURE__ */ new Date()).toISOString()
1743
2418
  }) + "\n");
1744
2419
  }
1745
- await import_fs.default.promises.appendFile(filePath, logLines.join(""));
2420
+ await import_fs2.default.promises.appendFile(filePath, logLines.join(""));
1746
2421
  res.json({
1747
2422
  success: true
1748
2423
  });
@@ -1881,6 +2556,7 @@ __name(registerMiddlewares, "registerMiddlewares");
1881
2556
  createOpenapiMiddleware,
1882
2557
  handleDevProxyError,
1883
2558
  normalizeBasePath,
2559
+ parseAndGenerateNestResourceTemplate,
1884
2560
  postprocessDrizzleSchema,
1885
2561
  registerMiddlewares
1886
2562
  });