@qvaroo/configs 1.0.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/eslint/node.js ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @qvaroo/configs - Node.js ESLint Configuration
3
+ *
4
+ * Extended configuration for Node.js/Express/NestJS backend projects.
5
+ * Includes all base rules plus Node-specific best practices.
6
+ */
7
+
8
+ const baseConfig = require('./index');
9
+
10
+ /** @type {import('eslint').Linter.Config} */
11
+ module.exports = {
12
+ ...baseConfig,
13
+
14
+ env: {
15
+ node: true,
16
+ es2022: true,
17
+ },
18
+
19
+ plugins: [
20
+ ...baseConfig.plugins,
21
+ 'n', // eslint-plugin-n (Node.js)
22
+ 'security',
23
+ ],
24
+
25
+ extends: [
26
+ ...baseConfig.extends.filter(ext => ext !== 'prettier'),
27
+ 'plugin:n/recommended',
28
+ 'plugin:security/recommended',
29
+ 'prettier',
30
+ ],
31
+
32
+ rules: {
33
+ ...baseConfig.rules,
34
+
35
+ // ═══════════════════════════════════════════════════════════════════════
36
+ // NODE.JS SPECIFIC RULES
37
+ // ═══════════════════════════════════════════════════════════════════════
38
+
39
+ // Allow console in Node.js (for logging)
40
+ 'no-console': 'off',
41
+
42
+ // Node.js best practices
43
+ 'n/no-missing-import': 'error',
44
+ 'n/no-missing-require': 'error',
45
+ 'n/no-unpublished-import': 'off', // Dev dependencies in configs
46
+ 'n/no-unpublished-require': 'off',
47
+ 'n/no-unsupported-features/es-syntax': [
48
+ 'error',
49
+ { ignores: ['modules'] },
50
+ ],
51
+ 'n/no-extraneous-import': 'error',
52
+ 'n/no-deprecated-api': 'error',
53
+ 'n/prefer-global/buffer': ['error', 'always'],
54
+ 'n/prefer-global/console': ['error', 'always'],
55
+ 'n/prefer-global/process': ['error', 'always'],
56
+ 'n/prefer-promises/dns': 'error',
57
+ 'n/prefer-promises/fs': 'error',
58
+
59
+ // ═══════════════════════════════════════════════════════════════════════
60
+ // SECURITY RULES - Prevent common vulnerabilities
61
+ // ═══════════════════════════════════════════════════════════════════════
62
+
63
+ 'security/detect-buffer-noassert': 'error',
64
+ 'security/detect-child-process': 'warn',
65
+ 'security/detect-disable-mustache-escape': 'error',
66
+ 'security/detect-eval-with-expression': 'error',
67
+ 'security/detect-new-buffer': 'error',
68
+ 'security/detect-no-csrf-before-method-override': 'error',
69
+ 'security/detect-non-literal-fs-filename': 'warn',
70
+ 'security/detect-non-literal-regexp': 'warn',
71
+ 'security/detect-non-literal-require': 'warn',
72
+ 'security/detect-object-injection': 'warn',
73
+ 'security/detect-possible-timing-attacks': 'warn',
74
+ 'security/detect-pseudoRandomBytes': 'error',
75
+ 'security/detect-unsafe-regex': 'error',
76
+
77
+ // ═══════════════════════════════════════════════════════════════════════
78
+ // ASYNC/AWAIT - Strict handling for backend
79
+ // ═══════════════════════════════════════════════════════════════════════
80
+
81
+ '@typescript-eslint/no-floating-promises': 'error',
82
+ '@typescript-eslint/promise-function-async': 'error',
83
+ 'require-atomic-updates': 'error',
84
+
85
+ // ═══════════════════════════════════════════════════════════════════════
86
+ // ERROR HANDLING - Backend specific
87
+ // ═══════════════════════════════════════════════════════════════════════
88
+
89
+ 'no-throw-literal': 'error',
90
+ 'prefer-promise-reject-errors': 'error',
91
+
92
+ // ═══════════════════════════════════════════════════════════════════════
93
+ // IMPORT/EXPORT - CommonJS & ESM support
94
+ // ═══════════════════════════════════════════════════════════════════════
95
+
96
+ 'unicorn/prefer-module': 'off', // Allow CommonJS in Node
97
+ '@typescript-eslint/no-require-imports': 'off', // Allow require()
98
+ },
99
+
100
+ overrides: [
101
+ ...baseConfig.overrides,
102
+ // Controller files - allow larger methods for route handlers
103
+ {
104
+ files: ['**/*.controller.ts', '**/controllers/**/*.ts'],
105
+ rules: {
106
+ 'max-lines-per-function': ['error', { max: 60 }],
107
+ },
108
+ },
109
+ // Migration files
110
+ {
111
+ files: ['**/migrations/**/*.ts'],
112
+ rules: {
113
+ '@typescript-eslint/naming-convention': 'off',
114
+ 'max-lines-per-function': 'off',
115
+ },
116
+ },
117
+ // Seed files
118
+ {
119
+ files: ['**/seeds/**/*.ts', '**/seeders/**/*.ts'],
120
+ rules: {
121
+ 'sonarjs/no-duplicate-string': 'off',
122
+ 'no-magic-numbers': 'off',
123
+ },
124
+ },
125
+ ],
126
+ };
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @qvaroo/configs - React ESLint Configuration
3
+ *
4
+ * Extended configuration for React/Next.js projects.
5
+ * Includes all base rules plus React-specific best practices.
6
+ */
7
+
8
+ const baseConfig = require('./index');
9
+
10
+ /** @type {import('eslint').Linter.Config} */
11
+ module.exports = {
12
+ ...baseConfig,
13
+
14
+ parserOptions: {
15
+ ...baseConfig.parserOptions,
16
+ ecmaFeatures: {
17
+ jsx: true,
18
+ },
19
+ },
20
+
21
+ plugins: [
22
+ ...baseConfig.plugins,
23
+ 'react',
24
+ 'react-hooks',
25
+ 'jsx-a11y',
26
+ ],
27
+
28
+ extends: [
29
+ ...baseConfig.extends.filter(ext => ext !== 'prettier'),
30
+ 'plugin:react/recommended',
31
+ 'plugin:react/jsx-runtime',
32
+ 'plugin:react-hooks/recommended',
33
+ 'plugin:jsx-a11y/strict',
34
+ 'prettier',
35
+ ],
36
+
37
+ settings: {
38
+ ...baseConfig.settings,
39
+ react: {
40
+ version: 'detect',
41
+ },
42
+ },
43
+
44
+ rules: {
45
+ ...baseConfig.rules,
46
+
47
+ // ═══════════════════════════════════════════════════════════════════════
48
+ // REACT SPECIFIC RULES
49
+ // ═══════════════════════════════════════════════════════════════════════
50
+
51
+ // Component naming - PascalCase enforced
52
+ 'react/jsx-pascal-case': ['error', { allowAllCaps: false }],
53
+
54
+ // Hooks rules - strict enforcement
55
+ 'react-hooks/rules-of-hooks': 'error',
56
+ 'react-hooks/exhaustive-deps': 'error',
57
+
58
+ // JSX best practices
59
+ 'react/jsx-no-useless-fragment': 'error',
60
+ 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
61
+ 'react/jsx-boolean-value': ['error', 'never'],
62
+ 'react/self-closing-comp': 'error',
63
+ 'react/no-array-index-key': 'error',
64
+ 'react/no-danger': 'error',
65
+ 'react/no-deprecated': 'error',
66
+ 'react/no-direct-mutation-state': 'error',
67
+ 'react/no-find-dom-node': 'error',
68
+ 'react/no-is-mounted': 'error',
69
+ 'react/no-string-refs': 'error',
70
+ 'react/no-unescaped-entities': 'error',
71
+ 'react/no-unknown-property': 'error',
72
+
73
+ // Props and state
74
+ 'react/prop-types': 'off', // Using TypeScript
75
+ 'react/require-default-props': 'off', // Using TypeScript
76
+ 'react/jsx-props-no-spreading': [
77
+ 'error',
78
+ {
79
+ html: 'enforce',
80
+ custom: 'ignore',
81
+ explicitSpread: 'ignore',
82
+ },
83
+ ],
84
+
85
+ // Performance
86
+ 'react/jsx-no-bind': [
87
+ 'error',
88
+ {
89
+ ignoreDOMComponents: false,
90
+ ignoreRefs: true,
91
+ allowArrowFunctions: true,
92
+ allowFunctions: false,
93
+ allowBind: false,
94
+ },
95
+ ],
96
+
97
+ // Accessibility
98
+ 'jsx-a11y/alt-text': 'error',
99
+ 'jsx-a11y/anchor-has-content': 'error',
100
+ 'jsx-a11y/anchor-is-valid': 'error',
101
+ 'jsx-a11y/aria-props': 'error',
102
+ 'jsx-a11y/aria-role': 'error',
103
+ 'jsx-a11y/aria-unsupported-elements': 'error',
104
+ 'jsx-a11y/click-events-have-key-events': 'error',
105
+ 'jsx-a11y/heading-has-content': 'error',
106
+ 'jsx-a11y/html-has-lang': 'error',
107
+ 'jsx-a11y/iframe-has-title': 'error',
108
+ 'jsx-a11y/img-redundant-alt': 'error',
109
+ 'jsx-a11y/interactive-supports-focus': 'error',
110
+ 'jsx-a11y/label-has-associated-control': 'error',
111
+ 'jsx-a11y/lang': 'error',
112
+ 'jsx-a11y/media-has-caption': 'error',
113
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
114
+ 'jsx-a11y/no-access-key': 'error',
115
+ 'jsx-a11y/no-autofocus': 'error',
116
+ 'jsx-a11y/no-distracting-elements': 'error',
117
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
118
+ 'jsx-a11y/no-noninteractive-element-interactions': 'error',
119
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
120
+ 'jsx-a11y/no-redundant-roles': 'error',
121
+ 'jsx-a11y/no-static-element-interactions': 'error',
122
+ 'jsx-a11y/role-has-required-aria-props': 'error',
123
+ 'jsx-a11y/role-supports-aria-props': 'error',
124
+ 'jsx-a11y/scope': 'error',
125
+ 'jsx-a11y/tabindex-no-positive': 'error',
126
+
127
+ // Allow relaxed naming for React components
128
+ '@typescript-eslint/naming-convention': [
129
+ 'error',
130
+ // Keep all base rules
131
+ ...require('./rules/naming-conventions')['@typescript-eslint/naming-convention'].slice(1),
132
+
133
+ // Add exception for React functional components
134
+ {
135
+ selector: 'variable',
136
+ modifiers: ['const', 'exported'],
137
+ format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
138
+ },
139
+ ],
140
+ },
141
+
142
+ overrides: [
143
+ ...baseConfig.overrides,
144
+ // Next.js pages/app router files
145
+ {
146
+ files: ['**/pages/**/*.tsx', '**/app/**/*.tsx'],
147
+ rules: {
148
+ 'import/no-default-export': 'off',
149
+ },
150
+ },
151
+ // Component files
152
+ {
153
+ files: ['**/*.tsx'],
154
+ rules: {
155
+ 'max-lines-per-function': ['error', { max: 80 }], // Allow larger components
156
+ },
157
+ },
158
+ ],
159
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Architecture Rules
3
+ *
4
+ * Enforces folder structure and separation of concerns:
5
+ * - /api, /components, /services, /events, /animations, /styles
6
+ * - Presentation-only views: No business logic or API calls
7
+ * - Boundary enforcement between layers
8
+ */
9
+
10
+ module.exports = {
11
+ // ═══════════════════════════════════════════════════════════════════════
12
+ // BOUNDARY RULES - Folder separation enforcement
13
+ // ═══════════════════════════════════════════════════════════════════════
14
+
15
+ 'boundaries/element-types': [
16
+ 'error',
17
+ {
18
+ default: 'disallow',
19
+ rules: [
20
+ // Components can import from: styles, types, utils, hooks, components, animations
21
+ {
22
+ from: 'components',
23
+ allow: ['components', 'styles', 'types', 'utils', 'hooks', 'animations', 'constants'],
24
+ },
25
+
26
+ // Views can import from: components, styles, types, hooks (NO services, NO api)
27
+ {
28
+ from: 'views',
29
+ allow: ['components', 'styles', 'types', 'hooks', 'animations', 'constants'],
30
+ disallow: ['api', 'services'], // Presentation-only views
31
+ },
32
+
33
+ // Services can import from: api, types, utils, constants
34
+ {
35
+ from: 'services',
36
+ allow: ['api', 'types', 'utils', 'constants', 'services'],
37
+ },
38
+
39
+ // API layer can only import from: types, utils, constants
40
+ {
41
+ from: 'api',
42
+ allow: ['types', 'utils', 'constants'],
43
+ },
44
+
45
+ // Hooks can import from: services, api, types, utils
46
+ {
47
+ from: 'hooks',
48
+ allow: ['services', 'api', 'types', 'utils', 'constants', 'hooks'],
49
+ },
50
+
51
+ // Events can import from: types, utils
52
+ {
53
+ from: 'events',
54
+ allow: ['types', 'utils', 'constants'],
55
+ },
56
+
57
+ // Animations can import from: types, utils, styles
58
+ {
59
+ from: 'animations',
60
+ allow: ['types', 'utils', 'styles', 'constants'],
61
+ },
62
+
63
+ // Styles: standalone, no business imports
64
+ {
65
+ from: 'styles',
66
+ allow: ['styles', 'constants'],
67
+ },
68
+
69
+ // Utils: standalone, no circular dependencies
70
+ {
71
+ from: 'utils',
72
+ allow: ['types', 'constants'],
73
+ },
74
+
75
+ // Types: standalone
76
+ {
77
+ from: 'types',
78
+ allow: ['types'],
79
+ },
80
+
81
+ // Constants: standalone
82
+ {
83
+ from: 'constants',
84
+ allow: ['types'],
85
+ },
86
+ ],
87
+ },
88
+ ],
89
+
90
+ // ═══════════════════════════════════════════════════════════════════════
91
+ // IMPORT BOUNDARIES - Prevent circular dependencies
92
+ // ═══════════════════════════════════════════════════════════════════════
93
+
94
+ 'import/no-cycle': ['error', { maxDepth: 3 }],
95
+
96
+ 'import/no-restricted-paths': [
97
+ 'error',
98
+ {
99
+ zones: [
100
+ // Views cannot import directly from API
101
+ {
102
+ target: './src/views',
103
+ from: './src/api',
104
+ message: 'Views must not import from API directly. Use hooks or services instead.',
105
+ },
106
+ // Views cannot import directly from Services
107
+ {
108
+ target: './src/views',
109
+ from: './src/services',
110
+ message: 'Views must not import from services directly. Use hooks instead.',
111
+ },
112
+ // Components cannot import from views (prevent circular)
113
+ {
114
+ target: './src/components',
115
+ from: './src/views',
116
+ message: 'Components cannot import from views. This creates circular dependencies.',
117
+ },
118
+ // Styles cannot import business logic
119
+ {
120
+ target: './src/styles',
121
+ from: './src/api',
122
+ message: 'Styles must not contain business logic or API dependencies.',
123
+ },
124
+ {
125
+ target: './src/styles',
126
+ from: './src/services',
127
+ message: 'Styles must not contain business logic or service dependencies.',
128
+ },
129
+ ],
130
+ },
131
+ ],
132
+
133
+ // ═══════════════════════════════════════════════════════════════════════
134
+ // FILE NAMING PATTERNS
135
+ // ═══════════════════════════════════════════════════════════════════════
136
+
137
+ // Enforce consistent file naming
138
+ 'unicorn/filename-case': [
139
+ 'error',
140
+ {
141
+ cases: {
142
+ kebabCase: true, // Regular files: my-component.ts
143
+ pascalCase: true, // React components: MyComponent.tsx
144
+ },
145
+ ignore: [
146
+ '^README\\.md$',
147
+ '^CHANGELOG\\.md$',
148
+ '^LICENSE$',
149
+ ],
150
+ },
151
+ ],
152
+ };
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Code Quality Rules
3
+ *
4
+ * Enforces strict quality limits:
5
+ * - Method length: max 40 lines → error
6
+ * - Class length: max 300 lines → error
7
+ * - Max nested if statements: 2 → error
8
+ * - No magic strings / hard-coded values → error
9
+ * - No empty catch blocks → error
10
+ * - Guard clauses enforcement
11
+ */
12
+
13
+ module.exports = {
14
+ // ═══════════════════════════════════════════════════════════════════════
15
+ // METHOD & CLASS LENGTH LIMITS
16
+ // ═══════════════════════════════════════════════════════════════════════
17
+
18
+ // Maximum 40 lines per function/method
19
+ 'max-lines-per-function': [
20
+ 'error',
21
+ {
22
+ max: 40,
23
+ skipBlankLines: true,
24
+ skipComments: true,
25
+ IIFEs: true,
26
+ },
27
+ ],
28
+
29
+ // Maximum 300 lines per file (proxy for class length)
30
+ 'max-lines': [
31
+ 'error',
32
+ {
33
+ max: 300,
34
+ skipBlankLines: true,
35
+ skipComments: true,
36
+ },
37
+ ],
38
+
39
+ // ═══════════════════════════════════════════════════════════════════════
40
+ // NESTING DEPTH - Maximum 2 nested if statements
41
+ // ═══════════════════════════════════════════════════════════════════════
42
+
43
+ 'max-depth': ['error', { max: 2 }],
44
+
45
+ // ═══════════════════════════════════════════════════════════════════════
46
+ // COMPLEXITY LIMITS - Encourage simpler code
47
+ // ═══════════════════════════════════════════════════════════════════════
48
+
49
+ // Cyclomatic complexity limit
50
+ complexity: ['error', { max: 10 }],
51
+
52
+ // Maximum statements per function
53
+ 'max-statements': ['error', { max: 20 }],
54
+
55
+ // Maximum parameters
56
+ 'max-params': ['error', { max: 4 }],
57
+
58
+ // ═══════════════════════════════════════════════════════════════════════
59
+ // MAGIC VALUES - No hard-coded strings/numbers
60
+ // ═══════════════════════════════════════════════════════════════════════
61
+
62
+ // No magic numbers (must use named constants)
63
+ 'no-magic-numbers': [
64
+ 'error',
65
+ {
66
+ ignore: [-1, 0, 1, 2, 100], // Common acceptable values
67
+ ignoreArrayIndexes: true,
68
+ ignoreDefaultValues: true,
69
+ enforceConst: true,
70
+ detectObjects: false,
71
+ },
72
+ ],
73
+
74
+ // SonarJS: Enforce no duplicate strings (magic strings)
75
+ 'sonarjs/no-duplicate-string': [
76
+ 'error',
77
+ {
78
+ threshold: 3,
79
+ },
80
+ ],
81
+
82
+ // ═══════════════════════════════════════════════════════════════════════
83
+ // GUARD CLAUSES - Early returns encouraged
84
+ // ═══════════════════════════════════════════════════════════════════════
85
+
86
+ // Prefer early returns (guard clauses)
87
+ 'no-else-return': [
88
+ 'error',
89
+ {
90
+ allowElseIf: false,
91
+ },
92
+ ],
93
+
94
+ // ═══════════════════════════════════════════════════════════════════════
95
+ // COGNITIVE COMPLEXITY - Readability enforcement
96
+ // ═══════════════════════════════════════════════════════════════════════
97
+
98
+ 'sonarjs/cognitive-complexity': ['error', 15],
99
+
100
+ // ═══════════════════════════════════════════════════════════════════════
101
+ // CODE DUPLICATION - DRY principle
102
+ // ═══════════════════════════════════════════════════════════════════════
103
+
104
+ 'sonarjs/no-identical-functions': 'error',
105
+ 'sonarjs/no-all-duplicated-branches': 'error',
106
+ 'sonarjs/no-duplicated-branches': 'error',
107
+
108
+ // ═══════════════════════════════════════════════════════════════════════
109
+ // ADDITIONAL QUALITY RULES
110
+ // ═══════════════════════════════════════════════════════════════════════
111
+
112
+ // No nested ternaries
113
+ 'no-nested-ternary': 'error',
114
+
115
+ // Require default case in switch
116
+ 'default-case': 'error',
117
+ 'default-case-last': 'error',
118
+
119
+ // No fallthrough in switch (must be explicit)
120
+ 'no-fallthrough': ['error', { commentPattern: 'falls?\\s?through' }],
121
+
122
+ // Enforce consistent return
123
+ 'consistent-return': 'error',
124
+
125
+ // No unreachable code
126
+ 'no-unreachable': 'error',
127
+ 'no-unreachable-loop': 'error',
128
+
129
+ // ═══════════════════════════════════════════════════════════════════════
130
+ // ERROR HANDLING - Strict enforcement
131
+ // ═══════════════════════════════════════════════════════════════════════
132
+
133
+ // No empty blocks (including catch)
134
+ 'no-empty': ['error', { allowEmptyCatch: false }],
135
+
136
+ // Prefer throwing Error objects
137
+ '@typescript-eslint/only-throw-error': 'error',
138
+
139
+ // Require await in async functions
140
+ 'require-await': 'off', // Handled by TypeScript
141
+ '@typescript-eslint/require-await': 'error',
142
+ };