code-quality-lib 0.1.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/LICENSE +21 -0
- package/README.md +385 -0
- package/config/.prettierignore +87 -0
- package/config/.prettierrc +17 -0
- package/config/README.md +32 -0
- package/config/config.json +30 -0
- package/config/eslint.config.mjs +311 -0
- package/config/knip.json +30 -0
- package/config/tsconfig.json +28 -0
- package/index.d.ts +60 -0
- package/index.js +1503 -0
- package/package.json +67 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import typescript from '@typescript-eslint/eslint-plugin'
|
|
3
|
+
import tsParser from '@typescript-eslint/parser'
|
|
4
|
+
import prettier from 'eslint-config-prettier'
|
|
5
|
+
import importPlugin from 'eslint-plugin-import'
|
|
6
|
+
import react from 'eslint-plugin-react'
|
|
7
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
8
|
+
import sonarjs from 'eslint-plugin-sonarjs'
|
|
9
|
+
import unicorn from 'eslint-plugin-unicorn'
|
|
10
|
+
|
|
11
|
+
// Import optional plugins - these may or may not be installed
|
|
12
|
+
let nextPlugin
|
|
13
|
+
let jsxA11yPlugin
|
|
14
|
+
let prettierPlugin
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const nextModule = await import('eslint-plugin-next')
|
|
18
|
+
nextPlugin = nextModule.default || nextModule
|
|
19
|
+
} catch {
|
|
20
|
+
// Next.js plugin not installed, skip Next.js rules
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const jsxA11yModule = await import('eslint-plugin-jsx-a11y')
|
|
25
|
+
jsxA11yPlugin = jsxA11yModule.default || jsxA11yModule
|
|
26
|
+
} catch {
|
|
27
|
+
// jsx-a11y plugin not installed, skip accessibility rules
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const prettierModule = await import('eslint-plugin-prettier')
|
|
32
|
+
prettierPlugin = prettierModule.default || prettierModule
|
|
33
|
+
} catch {
|
|
34
|
+
// eslint-plugin-prettier not installed, skip prettier rules
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const eslintConfig = [
|
|
38
|
+
{
|
|
39
|
+
ignores: [
|
|
40
|
+
'node_modules/',
|
|
41
|
+
'.pnp',
|
|
42
|
+
'.pnp.js',
|
|
43
|
+
'.next/',
|
|
44
|
+
'out/',
|
|
45
|
+
'dist/',
|
|
46
|
+
'build/',
|
|
47
|
+
'.env*',
|
|
48
|
+
'*.log',
|
|
49
|
+
'pids',
|
|
50
|
+
'*.pid',
|
|
51
|
+
'*.seed',
|
|
52
|
+
'*.pid.lock',
|
|
53
|
+
'coverage/',
|
|
54
|
+
'.nyc_output',
|
|
55
|
+
'.cache/',
|
|
56
|
+
'.parcel-cache/',
|
|
57
|
+
'*.tsbuildinfo',
|
|
58
|
+
'.DS_Store',
|
|
59
|
+
'.DS_Store?',
|
|
60
|
+
'._*',
|
|
61
|
+
'.Spotlight-V100',
|
|
62
|
+
'.Trashes',
|
|
63
|
+
'ehthumbs.db',
|
|
64
|
+
'Thumbs.db',
|
|
65
|
+
'.vscode/',
|
|
66
|
+
'.idea/',
|
|
67
|
+
'tmp/',
|
|
68
|
+
'temp/',
|
|
69
|
+
'*.tmp',
|
|
70
|
+
'*.temp',
|
|
71
|
+
'public/',
|
|
72
|
+
'.code-quality/',
|
|
73
|
+
'**/.code-quality/**', // More specific pattern
|
|
74
|
+
'eslint.config.mjs',
|
|
75
|
+
'next.config.ts',
|
|
76
|
+
'.venv/',
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
js.configs.recommended,
|
|
80
|
+
{
|
|
81
|
+
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
82
|
+
languageOptions: {
|
|
83
|
+
parser: tsParser,
|
|
84
|
+
parserOptions: {
|
|
85
|
+
ecmaVersion: 'latest',
|
|
86
|
+
sourceType: 'module',
|
|
87
|
+
ecmaFeatures: {
|
|
88
|
+
jsx: true,
|
|
89
|
+
},
|
|
90
|
+
project: './tsconfig.json',
|
|
91
|
+
tsconfigRootDir: import.meta.dirname,
|
|
92
|
+
},
|
|
93
|
+
globals: {
|
|
94
|
+
console: 'readonly',
|
|
95
|
+
process: 'readonly',
|
|
96
|
+
__dirname: 'readonly',
|
|
97
|
+
module: 'readonly',
|
|
98
|
+
require: 'readonly',
|
|
99
|
+
// Browser globals
|
|
100
|
+
window: 'readonly',
|
|
101
|
+
document: 'readonly',
|
|
102
|
+
fetch: 'readonly',
|
|
103
|
+
setTimeout: 'readonly',
|
|
104
|
+
clearTimeout: 'readonly',
|
|
105
|
+
setInterval: 'readonly',
|
|
106
|
+
clearInterval: 'readonly',
|
|
107
|
+
requestAnimationFrame: 'readonly',
|
|
108
|
+
URL: 'readonly',
|
|
109
|
+
URLSearchParams: 'readonly',
|
|
110
|
+
FormData: 'readonly',
|
|
111
|
+
HeadersInit: 'readonly',
|
|
112
|
+
RequestInit: 'readonly',
|
|
113
|
+
TextEncoder: 'readonly',
|
|
114
|
+
btoa: 'readonly',
|
|
115
|
+
crypto: 'readonly',
|
|
116
|
+
sessionStorage: 'readonly',
|
|
117
|
+
// React globals
|
|
118
|
+
React: 'readonly',
|
|
119
|
+
// DOM types
|
|
120
|
+
HTMLDivElement: 'readonly',
|
|
121
|
+
// Node.js globals
|
|
122
|
+
Buffer: 'readonly',
|
|
123
|
+
// Type globals
|
|
124
|
+
Item: 'readonly',
|
|
125
|
+
Bundle: 'readonly',
|
|
126
|
+
ConstStallCategory: 'readonly',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
settings: {
|
|
130
|
+
'import/resolver': {
|
|
131
|
+
typescript: {
|
|
132
|
+
alwaysTryTypes: true,
|
|
133
|
+
project: './tsconfig.json',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
plugins: {
|
|
138
|
+
'@typescript-eslint': typescript,
|
|
139
|
+
react,
|
|
140
|
+
'react-hooks': reactHooks,
|
|
141
|
+
sonarjs,
|
|
142
|
+
unicorn,
|
|
143
|
+
import: importPlugin,
|
|
144
|
+
...(nextPlugin ? { '@next/next': nextPlugin } : {}),
|
|
145
|
+
...(jsxA11yPlugin ? { 'jsx-a11y': jsxA11yPlugin } : {}),
|
|
146
|
+
...(prettierPlugin ? { prettier: prettierPlugin } : {}),
|
|
147
|
+
},
|
|
148
|
+
rules: {
|
|
149
|
+
// React
|
|
150
|
+
'react/jsx-no-literals': 'off',
|
|
151
|
+
'react/prop-types': 'off',
|
|
152
|
+
'react/self-closing-comp': 'warn',
|
|
153
|
+
'react/jsx-sort-props': [
|
|
154
|
+
'warn',
|
|
155
|
+
{
|
|
156
|
+
callbacksLast: true,
|
|
157
|
+
shorthandFirst: true,
|
|
158
|
+
noSortAlphabetically: false,
|
|
159
|
+
reservedFirst: true,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
'react/jsx-fragments': ['warn', 'syntax'],
|
|
163
|
+
'react/jsx-no-useless-fragment': 'warn',
|
|
164
|
+
'react/jsx-pascal-case': 'warn',
|
|
165
|
+
'react/no-array-index-key': 'warn',
|
|
166
|
+
'react/react-in-jsx-scope': 'off',
|
|
167
|
+
// JSX quotes consistency with Prettier jsxSingleQuote: true
|
|
168
|
+
'jsx-quotes': ['error', 'prefer-single'],
|
|
169
|
+
|
|
170
|
+
// TypeScript
|
|
171
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
172
|
+
'@typescript-eslint/no-require-imports': 'off',
|
|
173
|
+
'@typescript-eslint/no-unused-vars': [
|
|
174
|
+
'warn',
|
|
175
|
+
{
|
|
176
|
+
args: 'after-used',
|
|
177
|
+
ignoreRestSiblings: false,
|
|
178
|
+
argsIgnorePattern: '^_.*$',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
182
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
183
|
+
'@typescript-eslint/no-empty-interface': 'warn',
|
|
184
|
+
'@typescript-eslint/no-non-null-assertion': 'warn',
|
|
185
|
+
'@typescript-eslint/prefer-as-const': 'warn',
|
|
186
|
+
|
|
187
|
+
// Next.js (if available)
|
|
188
|
+
...(nextPlugin
|
|
189
|
+
? {
|
|
190
|
+
'@next/next/no-html-link-for-pages': 'off',
|
|
191
|
+
'@next/next/no-img-element': 'warn',
|
|
192
|
+
'@next/next/no-sync-scripts': 'warn',
|
|
193
|
+
}
|
|
194
|
+
: {}),
|
|
195
|
+
|
|
196
|
+
// Accessibility (if jsx-a11y plugin available)
|
|
197
|
+
...(jsxA11yPlugin
|
|
198
|
+
? {
|
|
199
|
+
'jsx-a11y/alt-text': 'warn',
|
|
200
|
+
'jsx-a11y/interactive-supports-focus': 'off',
|
|
201
|
+
'jsx-a11y/click-events-have-key-events': 'warn',
|
|
202
|
+
'jsx-a11y/no-static-element-interactions': 'warn',
|
|
203
|
+
}
|
|
204
|
+
: {}),
|
|
205
|
+
|
|
206
|
+
// Import ordering
|
|
207
|
+
'import/order': [
|
|
208
|
+
'error',
|
|
209
|
+
{
|
|
210
|
+
groups: [
|
|
211
|
+
'type',
|
|
212
|
+
'builtin',
|
|
213
|
+
'external',
|
|
214
|
+
'internal',
|
|
215
|
+
'parent',
|
|
216
|
+
'sibling',
|
|
217
|
+
'index',
|
|
218
|
+
'object',
|
|
219
|
+
],
|
|
220
|
+
pathGroups: [
|
|
221
|
+
{
|
|
222
|
+
pattern: '~/**',
|
|
223
|
+
group: 'external',
|
|
224
|
+
position: 'after',
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
pathGroupsExcludedImportTypes: ['builtin'],
|
|
228
|
+
'newlines-between': 'always',
|
|
229
|
+
alphabetize: {
|
|
230
|
+
order: 'asc',
|
|
231
|
+
caseInsensitive: true,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
'import/no-duplicates': 'error',
|
|
236
|
+
'import/no-unresolved': 'error',
|
|
237
|
+
'import/no-cycle': 'error',
|
|
238
|
+
|
|
239
|
+
// Code quality
|
|
240
|
+
'no-console': ['error', { allow: ['warn', 'error'] }],
|
|
241
|
+
...(prettierPlugin
|
|
242
|
+
? {
|
|
243
|
+
'prettier/prettier': [
|
|
244
|
+
'error',
|
|
245
|
+
{
|
|
246
|
+
endOfLine: 'auto',
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
}
|
|
250
|
+
: {}),
|
|
251
|
+
'padding-line-between-statements': [
|
|
252
|
+
'warn',
|
|
253
|
+
{ blankLine: 'always', prev: '*', next: 'return' },
|
|
254
|
+
{ blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
|
|
255
|
+
{
|
|
256
|
+
blankLine: 'any',
|
|
257
|
+
prev: ['const', 'let', 'var'],
|
|
258
|
+
next: ['const', 'let', 'var'],
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
|
|
262
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
263
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
264
|
+
'no-param-reassign': 'warn',
|
|
265
|
+
'no-return-await': 'warn',
|
|
266
|
+
'prefer-const': 'warn',
|
|
267
|
+
'no-var': 'error',
|
|
268
|
+
eqeqeq: ['error', 'always'],
|
|
269
|
+
'no-unused-expressions': 'error',
|
|
270
|
+
// Semicolons consistency with Prettier semi: false
|
|
271
|
+
semi: ['error', 'never'],
|
|
272
|
+
|
|
273
|
+
// SonarJS
|
|
274
|
+
'sonarjs/cognitive-complexity': ['error', 15],
|
|
275
|
+
'sonarjs/no-duplicate-string': 'warn',
|
|
276
|
+
|
|
277
|
+
// Unicorn
|
|
278
|
+
'unicorn/no-array-for-each': 'error',
|
|
279
|
+
'unicorn/prefer-node-protocol': 'error',
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
284
|
+
rules: {
|
|
285
|
+
'no-restricted-syntax': [
|
|
286
|
+
'error',
|
|
287
|
+
{
|
|
288
|
+
selector:
|
|
289
|
+
"MemberExpression[object.object.name='process'][object.property.name='env'][property.type='Identifier'][property.name=/^NEXT_PUBLIC_/]",
|
|
290
|
+
message:
|
|
291
|
+
'Do not access process.env.NEXT_PUBLIC_* directly. Use src/constants/environment.ts instead.',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
selector:
|
|
295
|
+
"MemberExpression[object.object.name='process'][object.property.name='env'][property.type='Literal'][property.value=/^NEXT_PUBLIC_/]",
|
|
296
|
+
message:
|
|
297
|
+
'Do not access process.env.NEXT_PUBLIC_* directly. Use src/constants/environment.ts instead.',
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
files: ['src/constants/environment.ts'],
|
|
304
|
+
rules: {
|
|
305
|
+
'no-restricted-syntax': 'off',
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
prettier,
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
export default eslintConfig
|
package/config/knip.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entry": ["index.js"],
|
|
3
|
+
"project": ["*.js"],
|
|
4
|
+
"ignore": [
|
|
5
|
+
"node_modules/**",
|
|
6
|
+
"dist/**",
|
|
7
|
+
"build/**",
|
|
8
|
+
"coverage/**",
|
|
9
|
+
"*.config.js",
|
|
10
|
+
"*.config.ts",
|
|
11
|
+
"test/**",
|
|
12
|
+
"**/*.test.*",
|
|
13
|
+
"**/*.spec.*",
|
|
14
|
+
"examples/**",
|
|
15
|
+
".code-quality/**",
|
|
16
|
+
"**/*.d.ts"
|
|
17
|
+
],
|
|
18
|
+
"ignoreBinaries": ["code-quality"],
|
|
19
|
+
"ignoreDependencies": [
|
|
20
|
+
"eslint-plugin-*",
|
|
21
|
+
"@typescript-eslint/*",
|
|
22
|
+
"@eslint/js",
|
|
23
|
+
"prettier",
|
|
24
|
+
"@types/*",
|
|
25
|
+
"typescript",
|
|
26
|
+
"knip",
|
|
27
|
+
"snyk",
|
|
28
|
+
"dotenv"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noUncheckedIndexedAccess": true,
|
|
9
|
+
"noImplicitOverride": true,
|
|
10
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
11
|
+
"useUnknownInCatchVariables": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "ESNext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true,
|
|
21
|
+
"baseUrl": ".",
|
|
22
|
+
"paths": {
|
|
23
|
+
"@/*": ["src/*"]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"include": ["**/*.ts", "**/*.tsx", "**/*.mts"],
|
|
27
|
+
"exclude": ["node_modules", "dist", "build", "coverage", "examples"]
|
|
28
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface EnvironmentConfig {
|
|
2
|
+
tools: string[]
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface CodeQualityOptions {
|
|
6
|
+
/** Load environment variables from .env file (default: true) */
|
|
7
|
+
loadEnv?: boolean
|
|
8
|
+
/** Use project's own config files instead of bundled configs (default: true) */
|
|
9
|
+
useProjectConfig?: boolean
|
|
10
|
+
/** Array of tool names to run (default: all tools) */
|
|
11
|
+
tools?: string[]
|
|
12
|
+
/** Custom commands for each tool, keyed by tool name */
|
|
13
|
+
commands?: Record<string, string>
|
|
14
|
+
/** Custom descriptions for each tool, keyed by tool name */
|
|
15
|
+
descriptions?: Record<string, string>
|
|
16
|
+
/** Force a specific package manager (auto-detected if not specified) */
|
|
17
|
+
packageManager?: 'bun' | 'pnpm' | 'yarn' | 'npm'
|
|
18
|
+
/** Show detailed error logs in terminal */
|
|
19
|
+
showLogs?: boolean
|
|
20
|
+
/** Set the active environment (default: development) */
|
|
21
|
+
environment?: string
|
|
22
|
+
/** Environment-specific tool configurations */
|
|
23
|
+
environments?: Record<string, EnvironmentConfig>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CommandResult {
|
|
27
|
+
success: boolean
|
|
28
|
+
output: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CheckResult {
|
|
32
|
+
name: string
|
|
33
|
+
description: string
|
|
34
|
+
success: boolean
|
|
35
|
+
output: string
|
|
36
|
+
errors: number
|
|
37
|
+
warnings: number
|
|
38
|
+
errorLines: string[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface QualityCheckResult {
|
|
42
|
+
success: boolean
|
|
43
|
+
message: string
|
|
44
|
+
results: CheckResult[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class CodeQualityChecker {
|
|
48
|
+
options: Required<Omit<CodeQualityOptions, 'showLogs'>>
|
|
49
|
+
|
|
50
|
+
constructor(options?: CodeQualityOptions)
|
|
51
|
+
|
|
52
|
+
/** Execute a single shell command and return the result */
|
|
53
|
+
runCommand(command: string, description: string): CommandResult
|
|
54
|
+
|
|
55
|
+
/** Run all configured quality checks */
|
|
56
|
+
run(options?: { showLogs?: boolean }): Promise<QualityCheckResult>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Convenience function to run quality checks without instantiating the class */
|
|
60
|
+
export function runQualityCheck(options?: CodeQualityOptions): Promise<QualityCheckResult>
|