From 8f42803d63a35b5508090759dcfd8dd221f91e83 Mon Sep 17 00:00:00 2001 From: liuyi Date: Sun, 15 Jun 2025 14:13:01 +0800 Subject: [PATCH] pnpm update --- .prettierrc.js | 32 +-- eslint.config.js | 198 ++++++++++++++++++ package.json | 5 + pnpm-lock.yaml | 34 +++ .../content/services/comment.service.ts | 5 +- .../constraints/phone.number.constraint.ts | 1 - src/modules/database/constants.ts | 13 +- .../dtos/paginate-width-trashed.dto.ts | 16 ++ src/modules/restful/dtos/paginate.dto.ts | 30 +++ src/options.ts | 3 +- test/all-case.test.ts | 20 +- test/generate-mock-data.ts | 2 +- 12 files changed, 328 insertions(+), 31 deletions(-) create mode 100644 eslint.config.js create mode 100644 src/modules/restful/dtos/paginate-width-trashed.dto.ts create mode 100644 src/modules/restful/dtos/paginate.dto.ts diff --git a/.prettierrc.js b/.prettierrc.js index 152d1e4..dc2f058 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,17 +1,17 @@ module.exports = { - "singleQuote": true, - "trailingComma": "all", - "printWidth": 100, - "proseWrap": "never", - "endOfLine": "auto", - "semi": true, - "tabWidth": 4, - "overrides": [ - { - "files": ".prettierrc", - "options": { - "parser": "json" - } - } - ] -} \ No newline at end of file + singleQuote: true, + trailingComma: 'all', + printWidth: 100, + proseWrap: 'never', + endOfLine: 'auto', + semi: true, + tabWidth: 4, + overrides: [ + { + files: '.prettierrc', + options: { + parser: 'json', + }, + }, + ], +}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..80eeb82 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,198 @@ +/* eslint-disable import/no-unresolved */ +/* eslint-disable @typescript-eslint/no-require-imports */ +/* eslint-disable import/no-extraneous-dependencies */ +const { FlatCompat } = require('@eslint/eslintrc'); +const js = require('@eslint/js'); +const typescriptEslint = require('@typescript-eslint/eslint-plugin'); +const tsParser = require('@typescript-eslint/parser'); +const { defineConfig, globalIgnores } = require('eslint/config'); + +const jest = require('eslint-plugin-jest'); +const prettier = require('eslint-plugin-prettier'); +const unusedImports = require('eslint-plugin-unused-imports'); +const globals = require('globals'); + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}); + +module.exports = defineConfig([ + { + languageOptions: { + parser: tsParser, + ecmaVersion: 'latest', + sourceType: 'module', + + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + }, + + globals: { + ...globals.node, + ...globals.jest, + }, + }, + + plugins: { + '@typescript-eslint': typescriptEslint, + jest, + prettier, + 'unused-imports': unusedImports, + }, + + extends: compat.extends( + 'airbnb-base', + 'airbnb-typescript/base', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:jest/recommended', + 'prettier', + 'plugin:prettier/recommended', + ), + + rules: { + 'no-console': 0, + 'no-var-requires': 0, + 'no-restricted-syntax': 0, + 'no-continue': 0, + 'no-await-in-loop': 0, + 'no-return-await': 0, + 'no-unused-vars': 0, + 'no-multi-assign': 0, + + 'no-param-reassign': [ + 2, + { + props: false, + }, + ], + + 'import/prefer-default-export': 0, + 'import/no-cycle': 0, + 'import/no-dynamic-require': 0, + 'max-classes-per-file': 0, + 'class-methods-use-this': 0, + 'guard-for-in': 0, + 'no-underscore-dangle': 0, + 'no-plusplus': 0, + 'no-lonely-if': 0, + + 'no-bitwise': [ + 'error', + { + allow: ['~'], + }, + ], + + 'import/no-absolute-path': 0, + 'import/extensions': 0, + 'import/no-named-default': 0, + 'no-restricted-exports': 0, + + 'import/no-extraneous-dependencies': [ + 1, + { + devDependencies: [ + '**/*.test.{ts,js}', + '**/*.spec.{ts,js}', + './test/**.{ts,js}', + './scripts/**/*.{ts,js}', + ], + }, + ], + + 'import/order': [ + 1, + { + pathGroups: [ + { + pattern: '@/**', + group: 'external', + position: 'after', + }, + ], + + alphabetize: { + order: 'asc', + caseInsensitive: false, + }, + + 'newlines-between': 'always-and-inside-groups', + warnOnUnassignedImports: true, + }, + ], + + 'unused-imports/no-unused-imports': 1, + + 'unused-imports/no-unused-vars': [ + 'warn', + { + vars: 'all', + args: 'none', + ignoreRestSiblings: true, + }, + ], + + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/no-empty-interface': 0, + '@typescript-eslint/no-this-alias': 0, + '@typescript-eslint/no-var-requires': 0, + '@typescript-eslint/no-use-before-define': 0, + '@typescript-eslint/explicit-member-accessibility': 0, + '@typescript-eslint/no-non-null-assertion': 0, + '@typescript-eslint/no-unnecessary-type-assertion': 0, + '@typescript-eslint/require-await': 0, + '@typescript-eslint/no-for-in-array': 0, + '@typescript-eslint/interface-name-prefix': 0, + '@typescript-eslint/explicit-function-return-type': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-floating-promises': 0, + '@typescript-eslint/restrict-template-expressions': 0, + '@typescript-eslint/no-unsafe-assignment': 0, + '@typescript-eslint/no-unsafe-return': 0, + '@typescript-eslint/no-unused-expressions': 0, + '@typescript-eslint/no-misused-promises': 0, + '@typescript-eslint/no-unsafe-member-access': 0, + '@typescript-eslint/no-unsafe-call': 0, + '@typescript-eslint/no-unsafe-argument': 0, + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/lines-between-class-members': 0, + '@typescript-eslint/no-throw-literal': 0, + }, + + settings: { + extensions: ['.ts', '.d.ts', '.cts', '.mts', '.js', '.cjs', 'mjs', '.json'], + }, + }, + globalIgnores([ + '**/dist', + '**/back', + '**/node_modules', + '**/pnpm-lock.yaml', + '**/docker', + '**/Dockerfile*', + '**/LICENSE', + '**/yarn-error.log', + '**/.history', + '**/.vscode', + '**/.docusaurus', + '**/.dockerignore', + '**/.DS_Store', + '**/.eslintignore', + '**/.editorconfig', + '**/.gitignore', + '**/.prettierignore', + '**/.eslintcache', + '**/*.lock', + '**/*.svg', + '**/*.md', + '**/*.ejs', + '**/*.html', + '**/*.png', + '**/*.toml', + ]), +]); diff --git a/package.json b/package.json index bbec5ea..e2e4ae7 100644 --- a/package.json +++ b/package.json @@ -46,12 +46,16 @@ "yaml": "^2.8.0" }, "devDependencies": { + "@eslint/compat": "^1.3.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.29.0", "@faker-js/faker": "^9.8.0", "@nestjs/cli": "^11.0.7", "@nestjs/schematics": "^11.0.5", "@nestjs/testing": "^11.1.3", "@swc/cli": "^0.7.7", "@swc/core": "^1.12.1", + "@types/eslint": "^9.6.1", "@types/fs-extra": "^11.0.4", "@types/jest": "29.5.14", "@types/lodash": "^4.17.17", @@ -70,6 +74,7 @@ "eslint-plugin-jest": "^28.13.5", "eslint-plugin-prettier": "^5.4.1", "eslint-plugin-unused-imports": "^4.1.4", + "globals": "^16.2.0", "jest": "30.0.0", "prettier": "^3.5.3", "source-map-support": "^0.5.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7391c87..20ba15d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,15 @@ importers: specifier: ^2.8.0 version: 2.8.0 devDependencies: + '@eslint/compat': + specifier: ^1.3.0 + version: 1.3.0(eslint@9.29.0) + '@eslint/eslintrc': + specifier: ^3.3.1 + version: 3.3.1 + '@eslint/js': + specifier: ^9.29.0 + version: 9.29.0 '@faker-js/faker': specifier: ^9.8.0 version: 9.8.0 @@ -96,6 +105,9 @@ importers: '@swc/core': specifier: ^1.12.1 version: 1.12.1 + '@types/eslint': + specifier: ^9.6.1 + version: 9.6.1 '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -150,6 +162,9 @@ importers: eslint-plugin-unused-imports: specifier: ^4.1.4 version: 4.1.4(@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0) + globals: + specifier: ^16.2.0 + version: 16.2.0 jest: specifier: 30.0.0 version: 30.0.0(@types/node@24.0.1)(ts-node@10.9.2(@swc/core@1.12.1)(@types/node@24.0.1)(typescript@5.8.3)) @@ -403,6 +418,15 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/compat@1.3.0': + resolution: {integrity: sha512-ZBygRBqpDYiIHsN+d1WyHn3TYgzgpzLEcgJUxTATyiInQbKZz6wZb6+ljwdg8xeeOe4v03z6Uh6lELiw0/mVhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true + '@eslint/config-array@0.20.1': resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2533,6 +2557,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@16.2.0: + resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -4548,6 +4576,10 @@ snapshots: '@eslint-community/regexpp@4.12.1': {} + '@eslint/compat@1.3.0(eslint@9.29.0)': + optionalDependencies: + eslint: 9.29.0 + '@eslint/config-array@0.20.1': dependencies: '@eslint/object-schema': 2.1.6 @@ -6968,6 +7000,8 @@ snapshots: globals@14.0.0: {} + globals@16.2.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 diff --git a/src/modules/content/services/comment.service.ts b/src/modules/content/services/comment.service.ts index 831b46a..e69527b 100644 --- a/src/modules/content/services/comment.service.ts +++ b/src/modules/content/services/comment.service.ts @@ -16,7 +16,10 @@ import { treePaginate } from '@/modules/database/utils'; @Injectable() export class CommentService extends BaseService { - constructor(protected repository: CommentRepository, protected postRepository: PostRepository) { + constructor( + protected repository: CommentRepository, + protected postRepository: PostRepository, + ) { super(repository); } diff --git a/src/modules/core/constraints/phone.number.constraint.ts b/src/modules/core/constraints/phone.number.constraint.ts index fb32af9..204c85e 100644 --- a/src/modules/core/constraints/phone.number.constraint.ts +++ b/src/modules/core/constraints/phone.number.constraint.ts @@ -1,5 +1,4 @@ import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; -// eslint-disable-next-line import/no-extraneous-dependencies import { isMobilePhone, IsMobilePhoneOptions, MobilePhoneLocale } from 'validator'; export function isMatchPhone( diff --git a/src/modules/database/constants.ts b/src/modules/database/constants.ts index af1e344..73596cb 100644 --- a/src/modules/database/constants.ts +++ b/src/modules/database/constants.ts @@ -1,11 +1,18 @@ export const CUSTOM_REPOSITORY_METADATA = 'CUSTOM_REPOSITORY_METADATA'; export enum SelectTrashMode { - // ALL: 包含已软删除和未软删除的数据(同时查询正常数据和回收站中的数据) + /** + * ALL: 包含已软删除和未软删除的数据(同时查询正常数据和回收站中的数据) + */ ALL = 'all', - // ONLY: 只包含软删除的数据 (只查询回收站中的数据) + /** + * ONLY: 只包含软删除的数据 (只查询回收站中的数据) + */ ONLY = 'only', - // NONE: 只包含未软删除的数据 (只查询正常数据) + + /** + * NONE: 只包含未软删除的数据 (只查询正常数据) + */ NONE = 'none', } diff --git a/src/modules/restful/dtos/paginate-width-trashed.dto.ts b/src/modules/restful/dtos/paginate-width-trashed.dto.ts new file mode 100644 index 0000000..61b94c4 --- /dev/null +++ b/src/modules/restful/dtos/paginate-width-trashed.dto.ts @@ -0,0 +1,16 @@ +import { IsEnum, IsOptional } from 'class-validator'; + +import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator'; +import { SelectTrashMode } from '@/modules/database/constants'; + +import { PaginateDto } from './paginate.dto'; + +@DtoValidation({ type: 'query' }) +export class PaginateWithTrashedDto extends PaginateDto { + /** + * 根据软删除状态查询 + */ + @IsEnum(SelectTrashMode) + @IsOptional() + trashed?: SelectTrashMode; +} diff --git a/src/modules/restful/dtos/paginate.dto.ts b/src/modules/restful/dtos/paginate.dto.ts new file mode 100644 index 0000000..8adc421 --- /dev/null +++ b/src/modules/restful/dtos/paginate.dto.ts @@ -0,0 +1,30 @@ +import { Transform } from 'class-transformer'; +import { Min, IsNumber, IsOptional } from 'class-validator'; +import { toNumber } from 'lodash'; + +import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator'; +import { PaginateOptions } from '@/modules/database/types'; + +/** + * 分页数据查询验证 + */ +@DtoValidation({ type: 'query' }) +export class PaginateDto implements PaginateOptions { + /** + * 当前页 + */ + @Transform(({ value }) => toNumber(value)) + @Min(1, { message: '当前页必须大于1' }) + @IsNumber() + @IsOptional() + page?: number = 1; + + /** + * 每页数据量 + */ + @Transform(({ value }) => toNumber(value)) + @Min(1, { message: '每页显示数据必须大于1' }) + @IsNumber() + @IsOptional() + limit?: number = 10; +} diff --git a/src/options.ts b/src/options.ts index cb8a913..4dd4de1 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,3 +1,5 @@ +import { join } from 'path'; + import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; @@ -13,7 +15,6 @@ import { MeiliModule } from './modules/meilisearch/meili.module'; import { Restful } from './modules/restful/restful'; import { RestfulModule } from './modules/restful/restful.module'; import { ApiConfig } from './modules/restful/types'; -import { join } from 'path'; export const createOptions: CreateOptions = { config: { factories: configs as any, storage: { enable: true } }, diff --git a/test/all-case.test.ts b/test/all-case.test.ts index ecbca45..671b41f 100644 --- a/test/all-case.test.ts +++ b/test/all-case.test.ts @@ -1,6 +1,6 @@ import { describe } from 'node:test'; -import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; +import { NestFastifyApplication } from '@nestjs/platform-fastify'; import { Test, TestingModule } from '@nestjs/testing'; import { useContainer } from 'class-validator'; @@ -16,8 +16,12 @@ import { } from '@/modules/content/repositories'; import { CoreModule } from '@/modules/core/core.module'; +import { createApp } from '@/modules/core/helpers/app'; +import { App } from '@/modules/core/types'; import { MeiliService } from '@/modules/meilisearch/meili.service'; +import { createOptions } from '@/options'; + import { generateRandomNumber, generateUniqueRandomNumbers } from './generate-mock-data'; import { categoriesData, commentData, INIT_DATA, postData, tagData } from './test-data'; @@ -39,16 +43,16 @@ describe('nest app test', () => { const module: TestingModule = await Test.createTestingModule({ imports: [CoreModule], }).compile(); - app = module.createNestApplication(new FastifyAdapter()); - useContainer(app.select(CoreModule), { fallbackOnErrors: true }); + const appConfig: App = await createApp(createOptions)(); + app = appConfig.container; await app.init(); await app.getHttpAdapter().getInstance().ready(); - categoryRepository = module.get(CategoryRepository); - tagRepository = module.get(TagRepository); - postRepository = module.get(PostRepository); - commentRepository = module.get(CommentRepository); - searchService = module.get(MeiliService); + categoryRepository = app.get(CategoryRepository); + tagRepository = app.get(TagRepository); + postRepository = app.get(PostRepository); + commentRepository = app.get(CommentRepository); + searchService = app.get(MeiliService); datasource = module.get(DataSource); if (!datasource.isInitialized) { await datasource.initialize(); diff --git a/test/generate-mock-data.ts b/test/generate-mock-data.ts index 3439746..88ba235 100644 --- a/test/generate-mock-data.ts +++ b/test/generate-mock-data.ts @@ -1,4 +1,4 @@ -import { fakerEN } from '@faker-js/faker/.'; +import { fakerEN } from '@faker-js/faker'; import { CreateCommentDto } from '@/modules/content/dtos/comment.dto'; import { CreatePostDto } from '@/modules/content/dtos/post.dto';