@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.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";
@@ -689,10 +1356,17 @@ function checkForErrors(logs) {
689
1356
  return false;
690
1357
  }
691
1358
  __name(checkForErrors, "checkForErrors");
692
- function injectTemplateData(template, clientBasePath) {
693
- return template.replace("{{.clientBasePath}}", clientBasePath);
1359
+ function injectErrorData(template, errorLogs, clientBasePath) {
1360
+ let logsText = "";
1361
+ if (errorLogs.length > 0) {
1362
+ logsText = errorLogs.join("\n");
1363
+ } else {
1364
+ logsText = "\u672A\u627E\u5230\u76F8\u5173\u9519\u8BEF\u65E5\u5FD7";
1365
+ }
1366
+ return template.replace("{{.errorData.message}}", `\u670D\u52A1\u542F\u52A8\u5F02\u5E38\uFF0C\u8BF7\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u76F8\u5173\u95EE\u9898
1367
+ ${JSON.stringify(logsText)}`).replace("{{.clientBasePath}}", clientBasePath);
694
1368
  }
695
- __name(injectTemplateData, "injectTemplateData");
1369
+ __name(injectErrorData, "injectErrorData");
696
1370
  function handleDevProxyError(err, req, res, options) {
697
1371
  const { logDir = path3.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 || {};
698
1372
  const clientBasePathWithoutSlash = normalizeBasePath(clientBasePath);
@@ -704,7 +1378,7 @@ function handleDevProxyError(err, req, res, options) {
704
1378
  (async () => {
705
1379
  try {
706
1380
  const isConnError = isConnectionError(err);
707
- const { hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
1381
+ const { logs: errorLogs, hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
708
1382
  if (isConnError && !hasCompileError) {
709
1383
  console.log("[Proxy Error]: Connection error without compile errors, possibly server restarting...");
710
1384
  try {
@@ -728,7 +1402,7 @@ function handleDevProxyError(err, req, res, options) {
728
1402
  console.log("[Proxy Error]: Compile error or non-connection error, showing error page");
729
1403
  }
730
1404
  const template = getErrorHtmlTemplate();
731
- const html = injectTemplateData(template, clientBasePathWithoutSlash);
1405
+ const html = injectErrorData(template, errorLogs, clientBasePathWithoutSlash);
732
1406
  res.writeHead(200, {
733
1407
  "Content-Type": "text/html; charset=utf-8",
734
1408
  "Cache-Control": "no-cache, no-store, must-revalidate",
@@ -878,7 +1552,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
878
1552
  const startTime = Date.now();
879
1553
  const openapiPath = options.openapiPath || path5.resolve(__dirname, "../client/src/api/gen/openapi.json");
880
1554
  const serverDir = options.serverDir || path5.resolve(__dirname, "../server");
881
- const writeFile = options.writeFile !== false;
1555
+ const writeFile2 = options.writeFile !== false;
882
1556
  let openapi;
883
1557
  if (options.openapiData) {
884
1558
  openapi = JSON.parse(JSON.stringify(options.openapiData));
@@ -889,7 +1563,7 @@ async function enhanceOpenApiWithSourceInfo(options = {}) {
889
1563
  const controllerFiles = await findControllerFiles(serverDir);
890
1564
  const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
891
1565
  const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
892
- if (writeFile) {
1566
+ if (writeFile2) {
893
1567
  await fs5.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
894
1568
  }
895
1569
  const duration = Date.now() - startTime;
@@ -1072,7 +1746,7 @@ import express2 from "express";
1072
1746
 
1073
1747
  // src/middlewares/dev-logs/utils.ts
1074
1748
  import { promises as fs7 } from "fs";
1075
- import { isAbsolute, join, relative } from "path";
1749
+ import { isAbsolute, join as join2, relative } from "path";
1076
1750
 
1077
1751
  // src/middlewares/dev-logs/helper/path-matcher.ts
1078
1752
  function pathPatternToRegex(pattern) {
@@ -1112,9 +1786,9 @@ __name(normalizePathForMatching, "normalizePathForMatching");
1112
1786
  // src/middlewares/dev-logs/utils.ts
1113
1787
  function resolveLogDir(provided) {
1114
1788
  if (!provided) {
1115
- return join(process.cwd(), "logs");
1789
+ return join2(process.cwd(), "logs");
1116
1790
  }
1117
- return isAbsolute(provided) ? provided : join(process.cwd(), provided);
1791
+ return isAbsolute(provided) ? provided : join2(process.cwd(), provided);
1118
1792
  }
1119
1793
  __name(resolveLogDir, "resolveLogDir");
1120
1794
  function getRelativePath(filePath) {
@@ -1173,7 +1847,7 @@ function resolveLogFilePath(baseDir, fileName) {
1173
1847
  if (segments.some((segment) => segment === "..")) {
1174
1848
  throw new Error("Invalid log file path");
1175
1849
  }
1176
- const resolved = join(baseDir, segments.join("/"));
1850
+ const resolved = join2(baseDir, segments.join("/"));
1177
1851
  const rel = relative(baseDir, resolved);
1178
1852
  if (rel.startsWith("..")) {
1179
1853
  throw new Error("Access to the specified log file is denied");
@@ -1203,7 +1877,7 @@ function serializeError(error) {
1203
1877
  __name(serializeError, "serializeError");
1204
1878
 
1205
1879
  // src/middlewares/dev-logs/controller.ts
1206
- import { join as join2 } from "path";
1880
+ import { join as join3 } from "path";
1207
1881
 
1208
1882
  // src/middlewares/dev-logs/services.ts
1209
1883
  import { createReadStream, promises as fs8 } from "fs";
@@ -1432,7 +2106,7 @@ function handleError(res, error, message = "Failed to read log file") {
1432
2106
  }
1433
2107
  __name(handleError, "handleError");
1434
2108
  function createGetTraceEntriesHandler(logDir) {
1435
- const appLogPath = join2(logDir, "server.log");
2109
+ const appLogPath = join3(logDir, "server.log");
1436
2110
  return async (req, res) => {
1437
2111
  const traceId = (req.params.traceId || "").trim();
1438
2112
  if (!traceId) {
@@ -1459,7 +2133,7 @@ function createGetTraceEntriesHandler(logDir) {
1459
2133
  }
1460
2134
  __name(createGetTraceEntriesHandler, "createGetTraceEntriesHandler");
1461
2135
  function createGetRecentTracesHandler(logDir) {
1462
- const traceLogPath = join2(logDir, "trace.log");
2136
+ const traceLogPath = join3(logDir, "trace.log");
1463
2137
  return async (req, res) => {
1464
2138
  const page = parsePositiveInt(req.query.page, 1);
1465
2139
  const pageSize = parseLimit(req.query.pageSize, 10, 100);
@@ -1627,17 +2301,17 @@ __name(createDevLogsMiddleware, "createDevLogsMiddleware");
1627
2301
  import express3 from "express";
1628
2302
 
1629
2303
  // src/middlewares/collect-logs/controller.ts
1630
- import { join as join4 } from "path";
2304
+ import { join as join5 } from "path";
1631
2305
  import fs10 from "fs";
1632
2306
 
1633
2307
  // src/middlewares/collect-logs/utils.ts
1634
- import { isAbsolute as isAbsolute2, join as join3 } from "path";
2308
+ import { isAbsolute as isAbsolute2, join as join4 } from "path";
1635
2309
  import fs9 from "fs";
1636
2310
  function resolveLogDir2(provided) {
1637
2311
  if (!provided) {
1638
- return join3(process.cwd(), "logs");
2312
+ return join4(process.cwd(), "logs");
1639
2313
  }
1640
- return isAbsolute2(provided) ? provided : join3(process.cwd(), provided);
2314
+ return isAbsolute2(provided) ? provided : join4(process.cwd(), provided);
1641
2315
  }
1642
2316
  __name(resolveLogDir2, "resolveLogDir");
1643
2317
  function ensureDir(dir) {
@@ -1660,7 +2334,7 @@ __name(serializeError2, "serializeError");
1660
2334
 
1661
2335
  // src/middlewares/collect-logs/controller.ts
1662
2336
  function collectLogsHandler(logDir, fileName) {
1663
- const filePath = join4(logDir, fileName);
2337
+ const filePath = join5(logDir, fileName);
1664
2338
  ensureDir(logDir);
1665
2339
  return async (req, res) => {
1666
2340
  try {
@@ -1685,7 +2359,7 @@ function collectLogsHandler(logDir, fileName) {
1685
2359
  }
1686
2360
  __name(collectLogsHandler, "collectLogsHandler");
1687
2361
  function collectLogsBatchHandler(logDir, fileName) {
1688
- const filePath = join4(logDir, fileName);
2362
+ const filePath = join5(logDir, fileName);
1689
2363
  ensureDir(logDir);
1690
2364
  return async (req, res) => {
1691
2365
  try {
@@ -1840,6 +2514,7 @@ export {
1840
2514
  createOpenapiMiddleware,
1841
2515
  handleDevProxyError,
1842
2516
  normalizeBasePath,
2517
+ parseAndGenerateNestResourceTemplate,
1843
2518
  postprocessDrizzleSchema,
1844
2519
  registerMiddlewares
1845
2520
  };