From 2564e3dae3997158e62d0fa9e21be88f33a463fe Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 22 Jan 2025 16:08:50 +0100 Subject: [PATCH] add lint rules, fix lint --- config/.eslintignore | 4 + config/.eslintrc.js | 214 +++++++++++++++++++++++++++++++++++++ config/src/commonSchema.ts | 26 ++--- 3 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 config/.eslintignore create mode 100644 config/.eslintrc.js diff --git a/config/.eslintignore b/config/.eslintignore new file mode 100644 index 000000000..1ae86fe5e --- /dev/null +++ b/config/.eslintignore @@ -0,0 +1,4 @@ +node_modules +**/*.min.js +build +coverage \ No newline at end of file diff --git a/config/.eslintrc.js b/config/.eslintrc.js new file mode 100644 index 000000000..4be107982 --- /dev/null +++ b/config/.eslintrc.js @@ -0,0 +1,214 @@ +// eslint-disable-next-line import/no-commonjs, import/unambiguous +module.exports = { + root: true, + env: { + node: true, + }, + parser: '@typescript-eslint/parser', + plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'import', 'n', 'promise'], + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:security/recommended', + 'plugin:@eslint-community/eslint-comments/recommended', + ], + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + project: ['./tsconfig.json', '**/tsconfig.json'], + }, + node: true, + }, + // the parser cannot handle the split sodium import + 'import/ignore': ['sodium-native'], + }, + rules: { + 'no-console': 'error', + camelcase: ['error', { allow: ['crypto_*', 'randombytes_random'] }], + 'no-debugger': 'error', + 'prettier/prettier': [ + 'error', + { + htmlWhitespaceSensitivity: 'ignore', + }, + ], + // import + 'import/export': 'error', + // 'import/no-deprecated': 'error', + 'import/no-empty-named-blocks': 'error', + 'import/no-extraneous-dependencies': 'error', + 'import/no-mutable-exports': 'error', + 'import/no-unused-modules': 'error', + 'import/no-named-as-default': 'error', + 'import/no-named-as-default-member': 'error', + 'import/no-amd': 'error', + 'import/no-commonjs': 'error', + 'import/no-import-module-exports': 'error', + 'import/no-nodejs-modules': 'off', + 'import/unambiguous': 'error', + 'import/default': 'error', + 'import/named': 'error', + 'import/namespace': 'error', + 'import/no-absolute-path': 'error', + 'import/no-cycle': 'error', + 'import/no-dynamic-require': 'error', + 'import/no-internal-modules': 'off', + 'import/no-relative-packages': 'error', + 'import/no-relative-parent-imports': [ + 'error', + { ignore: ['@/*', 'random-bigint', 'sodium-native'] }, + ], + 'import/no-self-import': 'error', + 'import/no-unresolved': 'error', + 'import/no-useless-path-segments': 'error', + 'import/no-webpack-loader-syntax': 'error', + 'import/consistent-type-specifier-style': 'error', + 'import/exports-last': 'off', + 'import/extensions': 'error', + 'import/first': 'error', + 'import/group-exports': 'off', + 'import/newline-after-import': 'error', + 'import/no-anonymous-default-export': 'error', + 'import/no-default-export': 'error', + 'import/no-duplicates': 'error', + 'import/no-named-default': 'error', + 'import/no-namespace': 'error', + 'import/no-unassigned-import': 'error', + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroups: [ + { + pattern: '@?*/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/**', + group: 'external', + position: 'after', + }, + ], + alphabetize: { + order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, + caseInsensitive: true /* ignore case. Options: [true, false] */, + }, + distinctGroup: true, + }, + ], + 'import/prefer-default-export': 'off', + // n + 'n/handle-callback-err': 'error', + 'n/no-callback-literal': 'error', + 'n/no-exports-assign': 'error', + 'n/no-extraneous-import': 'error', + 'n/no-extraneous-require': 'error', + 'n/no-hide-core-modules': 'error', + 'n/no-missing-import': 'off', // not compatible with typescript + 'n/no-missing-require': 'error', + 'n/no-new-require': 'error', + 'n/no-path-concat': 'error', + 'n/no-process-exit': 'error', + 'n/no-unpublished-bin': 'error', + 'n/no-unpublished-import': 'off', // TODO need to exclude seeds + 'n/no-unpublished-require': 'error', + 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], + 'n/no-unsupported-features/es-builtins': 'error', + 'n/no-unsupported-features/es-syntax': 'error', + 'n/no-unsupported-features/node-builtins': 'error', + 'n/process-exit-as-throw': 'error', + 'n/shebang': 'error', + 'n/callback-return': 'error', + 'n/exports-style': 'error', + 'n/file-extension-in-import': 'off', + 'n/global-require': 'error', + 'n/no-mixed-requires': 'error', + 'n/no-process-env': 'error', + 'n/no-restricted-import': 'error', + 'n/no-restricted-require': 'error', + 'n/no-sync': 'error', + 'n/prefer-global/buffer': 'error', + 'n/prefer-global/console': 'error', + 'n/prefer-global/process': 'error', + 'n/prefer-global/text-decoder': 'error', + 'n/prefer-global/text-encoder': 'error', + 'n/prefer-global/url': 'error', + 'n/prefer-global/url-search-params': 'error', + 'n/prefer-promises/dns': 'error', + 'n/prefer-promises/fs': 'error', + // promise + 'promise/catch-or-return': 'error', + 'promise/no-return-wrap': 'error', + 'promise/param-names': 'error', + 'promise/always-return': 'error', + 'promise/no-native': 'off', + 'promise/no-nesting': 'warn', + 'promise/no-promise-in-callback': 'warn', + 'promise/no-callback-in-promise': 'warn', + 'promise/avoid-new': 'warn', + 'promise/no-new-statics': 'error', + 'promise/no-return-in-finally': 'warn', + 'promise/valid-params': 'warn', + 'promise/prefer-await-to-callbacks': 'error', + 'promise/no-multiple-resolved': 'error', + // eslint comments + '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], + '@eslint-community/eslint-comments/no-restricted-disable': 'error', + '@eslint-community/eslint-comments/no-use': 'off', + '@eslint-community/eslint-comments/require-description': 'off', + }, + overrides: [ + // only for ts files + { + files: ['*.ts', '*.tsx'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:@typescript-eslint/strict', + 'plugin:type-graphql/recommended', + ], + rules: { + // allow explicitly defined dangling promises + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], + 'no-void': ['error', { allowAsStatement: true }], + // ignore prefer-regexp-exec rule to allow string.match(regex) + '@typescript-eslint/prefer-regexp-exec': 'off', + // this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486 + 'import/unambiguous': 'off', + // this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable + '@typescript-eslint/no-unnecessary-condition': 'off', + }, + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json', '**/tsconfig.json'], + // this is to properly reference the referenced project database without requirement of compiling it + // eslint-disable-next-line camelcase + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, + }, + }, + { + files: ['*.test.ts'], + plugins: ['jest'], + env: { + jest: true, + }, + rules: { + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/valid-expect': 'error', + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + }, + }, + ], +} diff --git a/config/src/commonSchema.ts b/config/src/commonSchema.ts index 8a8b59413..ca98aa87a 100644 --- a/config/src/commonSchema.ts +++ b/config/src/commonSchema.ts @@ -2,7 +2,7 @@ import Joi from 'joi' export const browserUrls = Joi.array() .items(Joi.string().uri()) - .custom((value, helpers) => { + .custom((value: Joi.string, helpers: Joi.helpers) => { const protocol = new URL(value[0]).protocol for (const url of value) { if (new URL(url).protocol !== protocol) { @@ -28,7 +28,7 @@ export const DB_VERSION = Joi.string() export const COMMUNITY_URL = Joi.string() .uri({ scheme: ['http', 'https'] }) - .custom((value, helpers) => { + .custom((value: Joi.string, helpers: Joi.helpers) => { if (value.endsWith('/')) { return helpers.error('any.invalid', { message: 'URL should not end with a slash (/)' }) } @@ -124,19 +124,19 @@ export const LOG4JS_CONFIG = Joi.string() .required() export const LOGIN_APP_SECRET = Joi.string() - .pattern(/^[a-fA-F0-9]+$/) - .message('need to be valid hex') - .default('21ffbbc616fe') - .description('App secret for salt component for libsodium crypto_pwhash') - .required() + .pattern(/^[a-fA-F0-9]+$/) + .message('need to be valid hex') + .default('21ffbbc616fe') + .description('App secret for salt component for libsodium crypto_pwhash') + .required() export const LOGIN_SERVER_KEY = Joi.string() - .pattern(/^[a-fA-F0-9]+$/) - .length(32) - .message('need to be valid hex and 32 character') - .default('a51ef8ac7ef1abf162fb7a65261acd7a') - .description('Server key for password hashing as additional salt for libsodium crypto_shorthash_keygen') - .required() + .pattern(/^[a-fA-F0-9]+$/) + .length(32) + .message('need to be valid hex and 32 character') + .default('a51ef8ac7ef1abf162fb7a65261acd7a') + .description('Server key for password hashing as additional salt for libsodium crypto_shorthash_keygen') + .required() export const TYPEORM_LOGGING_RELATIVE_PATH = Joi.string() .pattern(/^[a-zA-Z0-9-_\.]+\.log$/)