Skip to content

Commit 67efcb6

Browse files
committed
feat(lint): update lint configuration
1 parent 529f918 commit 67efcb6

File tree

13 files changed

+1159
-171
lines changed

13 files changed

+1159
-171
lines changed

eslint.config.mjs

Lines changed: 121 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import eslint from '@eslint/js';
44
import eslintConfigPrettier from 'eslint-config-prettier';
55
import nodePlugin from 'eslint-plugin-n';
66
import pluginPromise from 'eslint-plugin-promise';
7+
import security from 'eslint-plugin-security';
8+
import sonarjs from 'eslint-plugin-sonarjs';
79
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
10+
import unusedImports from 'eslint-plugin-unused-imports';
811
import globals from 'globals';
912
import path from 'node:path';
1013
import { fileURLToPath } from 'node:url';
@@ -15,16 +18,24 @@ const __dirname = path.dirname(__filename);
1518
const gitignorePath = path.resolve(__dirname, '.gitignore');
1619

1720
export default tseslint.config(
21+
// Base configurations
1822
eslint.configs.recommended,
1923
...tseslint.configs.strict,
2024
...tseslint.configs.stylistic,
25+
26+
// Plugin configurations
2127
nodePlugin.configs['flat/recommended'],
22-
eslintPluginUnicorn.configs['flat/recommended'],
28+
eslintPluginUnicorn.configs['recommended'],
2329
pluginPromise.configs['flat/recommended'],
30+
security.configs['recommended'],
31+
sonarjs.configs['recommended'],
32+
33+
// Global language options
2434
{
35+
name: 'Global Language Options',
2536
languageOptions: {
2637
parser: tseslint.parser,
27-
ecmaVersion: 2022,
38+
ecmaVersion: 2024,
2839
sourceType: 'module',
2940
globals: {
3041
...globals.node,
@@ -34,61 +45,134 @@ export default tseslint.config(
3445
{
3546
name: 'Global Ignores',
3647
ignores: [
37-
...includeIgnoreFile(gitignorePath).ignores,
48+
...(includeIgnoreFile(gitignorePath).ignores ?? []),
3849
'/.cache',
3950
'/.git',
4051
'/.husky',
4152
'/.yarn',
53+
'dist/**',
54+
'build/**',
55+
'coverage/**',
4256
'.dependency-cruiser.js',
4357
'cucumber.setup.js',
4458
'eslint.config.mjs',
59+
'**/*.d.ts',
4560
],
4661
},
4762
{
48-
files: ['**/*.ts'],
63+
plugins: {
64+
'unused-imports': unusedImports,
65+
},
4966
rules: {
50-
'@typescript-eslint/ban-ts-ignore': 'off',
51-
'@typescript-eslint/consistent-type-definitions': 'off',
52-
'@typescript-eslint/explicit-function-return-type': 'off',
53-
'@typescript-eslint/explicit-member-accessibility': 'off',
54-
'@typescript-eslint/explicit-module-boundary-types': 'off',
55-
'@typescript-eslint/indent': 'off',
56-
'@typescript-eslint/member-delimiter-style': 'off',
57-
'@typescript-eslint/no-empty-interface': 'off',
67+
'no-unused-vars': 'off',
68+
'unused-imports/no-unused-imports': 'error',
69+
'unused-imports/no-unused-vars': [
70+
'warn',
71+
{
72+
vars: 'all',
73+
varsIgnorePattern: '^_',
74+
args: 'after-used',
75+
argsIgnorePattern: '^_',
76+
},
77+
],
78+
},
79+
},
80+
{
81+
name: 'Security and Code Quality',
82+
rules: {
83+
// Security rules - important for backend APIs
84+
'security/detect-object-injection': 'off',
85+
'security/detect-non-literal-regexp': 'warn',
86+
'security/detect-unsafe-regex': 'error',
87+
'security/detect-buffer-noassert': 'error',
88+
'security/detect-child-process': 'warn',
89+
'security/detect-disable-mustache-escape': 'error',
90+
'security/detect-eval-with-expression': 'error',
91+
'security/detect-new-buffer': 'error',
92+
'security/detect-no-csrf-before-method-override': 'error',
93+
'security/detect-possible-timing-attacks': 'warn',
94+
'security/detect-pseudoRandomBytes': 'error',
95+
96+
// SonarJS rules - code quality and bug prevention
97+
'sonarjs/cognitive-complexity': ['warn', 15],
98+
'sonarjs/no-duplicate-string': ['warn', { threshold: 3 }],
99+
'sonarjs/no-duplicated-branches': 'error',
100+
'sonarjs/no-identical-functions': 'error',
101+
'sonarjs/no-small-switch': 'error',
102+
'sonarjs/prefer-immediate-return': 'warn',
103+
'sonarjs/prefer-object-literal': 'warn',
104+
'sonarjs/prefer-single-boolean-return': 'warn',
105+
'sonarjs/no-nested-template-literals': 'warn',
106+
'sonarjs/no-redundant-boolean': 'error',
107+
'sonarjs/no-unused-collection': 'error',
108+
'sonarjs/no-useless-catch': 'error',
109+
'sonarjs/todo-tag': 'off',
110+
'sonarjs/no-commented-code': 'off',
111+
'sonarjs/function-return-type': 'off',
112+
'sonarjs/no-empty-test-file': 'off',
113+
'sonarjs/no-alphabetical-sort': 'off',
114+
},
115+
},
116+
{
117+
name: 'TypeScript Rules',
118+
files: ['**/*.ts', '**/*.tsx'],
119+
languageOptions: {
120+
parserOptions: {
121+
project: true,
122+
tsconfigRootDir: __dirname,
123+
},
124+
},
125+
rules: {
126+
'@typescript-eslint/no-unused-vars': 'off',
127+
'no-undef': 'off',
58128
'@typescript-eslint/no-explicit-any': 'off',
59129
'@typescript-eslint/no-floating-promises': 'off',
60-
'@typescript-eslint/no-object-literal-type-assertion': 'off',
61-
'@typescript-eslint/no-unsafe-argument': 'off',
62-
'@typescript-eslint/no-unsafe-assignment': 'off',
63-
'@typescript-eslint/no-unsafe-call': 'off',
64-
'@typescript-eslint/no-unsafe-member-access': 'off',
65-
'@typescript-eslint/no-unsafe-return': 'off',
66-
'@typescript-eslint/no-var-requires': 'off',
130+
'@typescript-eslint/await-thenable': 'error',
131+
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
132+
'@typescript-eslint/prefer-optional-chain': 'warn',
133+
'@typescript-eslint/explicit-function-return-type': 'off',
134+
'@typescript-eslint/explicit-module-boundary-types': 'off',
67135
'@typescript-eslint/require-await': 'off',
68-
'@typescript-eslint/prefer-nullish-coalescing': 'off',
69-
'@typescript-eslint/unbound-method': 'off',
70-
'@typescript-eslint/no-empty-object-type': 'off',
136+
'@typescript-eslint/no-unnecessary-condition': 'off',
71137
'n/no-missing-import': 'off',
72138
'n/no-unpublished-import': 'off',
73-
'import/extensions': 'off',
74-
'import/no-cycle': 'off',
75-
'import/no-extraneous-dependencies': 'off',
76-
'import/order': 'off',
77-
'import/prefer-default-export': 'off',
78-
'no-restricted-exports': 'off',
79-
'no-undef': 'off',
80-
'func-names': 'off',
81-
'no-unused-vars': 'off',
82-
'no-use-before-define': 'off',
83-
'promise/always-return': 'off',
84-
'promise/catch-or-return': 'off',
139+
'n/prefer-global/process': 'error',
140+
'n/prefer-global/buffer': 'error',
141+
'n/prefer-promises/dns': 'error',
142+
'n/prefer-promises/fs': 'error',
143+
'promise/prefer-await-to-then': 'warn',
144+
'promise/prefer-await-to-callbacks': 'warn',
145+
'unicorn/no-array-callback-reference': 'off',
85146
'unicorn/filename-case': 'off',
147+
'unicorn/no-null': 'off',
148+
'unicorn/consistent-function-scoping': 'off',
86149
'unicorn/prefer-module': 'off',
87-
'unicorn/prefer-top-level-await': 'off',
88150
'unicorn/prevent-abbreviations': 'off',
151+
'unicorn/prefer-top-level-await': 'off',
89152
'unicorn/no-array-reduce': 'off',
90-
'unicorn/no-abusive-eslint-disable': 'off',
91-
'unicorn/no-array-callback-reference': 'off',
153+
},
154+
},
155+
{
156+
name: 'Test Files',
157+
files: [
158+
'**/*.test.ts',
159+
'**/*.spec.ts',
160+
'**/test/**/*.ts',
161+
'**/tests/**/*.ts',
162+
],
163+
rules: {
164+
'sonarjs/no-duplicate-string': 'off',
165+
'@typescript-eslint/no-explicit-any': 'off',
166+
'@typescript-eslint/no-non-null-assertion': 'off',
167+
'unicorn/no-null': 'off',
168+
},
169+
},
170+
{
171+
name: 'Configuration Files',
172+
files: ['*.config.{js,ts,mjs}', '.*.{js,ts,mjs}', './scripts/**/*'],
173+
rules: {
174+
'unicorn/prefer-module': 'off',
175+
'@typescript-eslint/no-var-requires': 'off',
92176
},
93177
},
94178
eslintConfigPrettier,

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"@fastify/helmet": "13.0.1",
5656
"@fastify/request-context": "6.2.0",
5757
"@fastify/swagger": "9.5.1",
58-
"@fastify/swagger-ui": "5.2.2",
58+
"@fastify/swagger-ui": "5.2.3",
5959
"@fastify/type-provider-json-schema-to-ts": "5.0.0",
6060
"@fastify/type-provider-typebox": "5.1.0",
6161
"@fastify/under-pressure": "9.0.3",
@@ -101,9 +101,12 @@
101101
"dependency-cruiser": "16.10.2",
102102
"eslint": "9.28.0",
103103
"eslint-config-prettier": "10.1.5",
104-
"eslint-plugin-n": "17.18.0",
104+
"eslint-plugin-n": "17.19.0",
105105
"eslint-plugin-promise": "7.2.1",
106+
"eslint-plugin-security": "3.0.1",
107+
"eslint-plugin-sonarjs": "3.0.2",
106108
"eslint-plugin-unicorn": "59.0.1",
109+
"eslint-plugin-unused-imports": "4.1.4",
107110
"globals": "16.2.0",
108111
"husky": "9.1.7",
109112
"lint-staged": "16.1.0",
@@ -117,7 +120,7 @@
117120
"tsconfig-paths": "4.2.0",
118121
"tsx": "4.19.4",
119122
"typescript": "5.8.3",
120-
"typescript-eslint": "8.33.0"
123+
"typescript-eslint": "8.33.1"
121124
},
122125
"keywords": [
123126
"fastify",

src/modules/user/commands/create-user/create-user.resolver.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ export default async function createUserResolver(
1212
Mutation: {
1313
putUser: async (_, args) => {
1414
try {
15-
const id = await fastify.commandBus.execute<CreateUserCommandResult>(
16-
createUserCommand(args.user || {}),
15+
return await fastify.commandBus.execute<CreateUserCommandResult>(
16+
createUserCommand(args.user ?? {}),
1717
);
18-
return id;
1918
} catch (error) {
2019
if (error instanceof UserAlreadyExistsError) {
2120
throw new ErrorWithProps(error.message, {

src/modules/user/commands/delete-user/delete-user.handler.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ export default function makeDeleteUser({
1616
if (!user) {
1717
throw new NotFoundException();
1818
}
19-
const result = await userRepository.delete(payload.id);
20-
return result;
19+
return await userRepository.delete(payload.id);
2120
},
2221
init() {
2322
commandBus.register(deleteUserCommand.type, this.handler);

src/modules/user/queries/find-users/find-users.resolver.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ export default async function findUsersResolver(fastify: FastifyRouteInstance) {
44
fastify.graphql.defineResolvers({
55
Query: {
66
findUsers: async (_, args) => {
7-
const result = await fastify.queryBus.execute<FindUsersQueryResult>(
8-
findUsersQuery(args || {}),
7+
return await fastify.queryBus.execute<FindUsersQueryResult>(
8+
findUsersQuery(args ?? {}),
99
);
10-
return result;
1110
},
1211
},
1312
});

src/server/di/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export function formatName(fileName: string): string {
22
// Split the filename by delimiters (".", "-")
3-
const parts = fileName.split(/\.|-/);
3+
const parts = fileName.split(/[.-]/);
44

55
// Capitalize the first letter of each word (except the first)
66
const formattedParts = parts.map((part, index) =>

src/server/plugins/error-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const fastifyErrorCodesMap = {
2525
};
2626

2727
async function errorHandlerPlugin(fastify: FastifyInstance) {
28+
// eslint-disable-next-line promise/prefer-await-to-callbacks
2829
fastify.setErrorHandler((error: FastifyError | Error, _, res) => {
2930
// Handle fastify errors
3031
const fastifyError =

src/shared/cqrs/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {
77
} from '@/shared/cqrs/middlewares';
88
import fastifyPlugin from 'fastify-plugin';
99

10-
interface CQRSPluginOption {}
11-
12-
const CQRSPlugin = fastifyPlugin<CQRSPluginOption>(
10+
const CQRSPlugin = fastifyPlugin(
1311
(fastify, _opts, done) => {
1412
if (fastify.queryBus || fastify.commandBus || fastify.eventBus) {
1513
throw new Error('This plugin is already registered');

src/shared/cqrs/middlewares.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export function decorateWithMetadata(
88
) {
99
action.meta = {
1010
...action.meta,
11-
correlationId: action.meta?.correlationId || getRequestId(),
12-
timestamp: action.meta?.timestamp || Date.now(),
11+
correlationId: action.meta?.correlationId ?? getRequestId(),
12+
timestamp: action.meta?.timestamp ?? Date.now(),
1313
};
1414
return handler(action) as Promise<CommandHandler | EventHandler>;
1515
}

src/shared/db/repository.port.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ export interface Paginated<T> {
55
data: T[];
66
}
77

8-
export type OrderBy = { field: string | boolean; param: 'asc' | 'desc' };
8+
export interface OrderBy {
9+
field: string | boolean;
10+
param: 'asc' | 'desc';
11+
}
912

1013
export interface PaginatedQueryParams {
1114
limit: number;

0 commit comments

Comments
 (0)