@soulbatical/tetra-dev-toolkit 1.3.2 → 1.4.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/bin/cleanup-repos.sh +23 -0
- package/bin/tetra-setup.js +463 -2
- package/lib/checks/health/conventional-commits.js +164 -0
- package/lib/checks/health/coverage-thresholds.js +199 -0
- package/lib/checks/health/dependency-cruiser.js +113 -0
- package/lib/checks/health/eslint-security.js +172 -0
- package/lib/checks/health/index.js +7 -1
- package/lib/checks/health/prettier.js +162 -0
- package/lib/checks/health/scanner.js +14 -2
- package/lib/checks/health/types.js +1 -1
- package/lib/checks/health/typescript-strict.js +143 -0
- package/package.json +5 -3
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'} HealthCheckType
|
|
8
|
+
* @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'typescript-strict'|'prettier'|'coverage-thresholds'|'eslint-security'|'dependency-cruiser'|'conventional-commits'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'} HealthCheckType
|
|
9
9
|
*
|
|
10
10
|
* @typedef {'ok'|'warning'|'error'} HealthStatus
|
|
11
11
|
*
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check: TypeScript Strictness
|
|
3
|
+
*
|
|
4
|
+
* Checks TypeScript configuration for strict mode and type safety.
|
|
5
|
+
* Score: up to 3 points:
|
|
6
|
+
* +1 for strict: true in tsconfig.json
|
|
7
|
+
* +1 for tsc --noEmit script in package.json (typecheck command)
|
|
8
|
+
* +1 for noImplicitAny + strictNullChecks (if not using strict: true)
|
|
9
|
+
* OR bonus for strict: true + noUncheckedIndexedAccess
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync } from 'fs'
|
|
13
|
+
import { join } from 'path'
|
|
14
|
+
import { createCheck } from './types.js'
|
|
15
|
+
|
|
16
|
+
export async function check(projectPath) {
|
|
17
|
+
const result = createCheck('typescript-strict', 3, {
|
|
18
|
+
tsconfigFound: false,
|
|
19
|
+
strict: false,
|
|
20
|
+
noImplicitAny: false,
|
|
21
|
+
strictNullChecks: false,
|
|
22
|
+
noUncheckedIndexedAccess: false,
|
|
23
|
+
hasTypecheckScript: false,
|
|
24
|
+
typecheckScript: null,
|
|
25
|
+
tsconfigPaths: [],
|
|
26
|
+
message: ''
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Find tsconfig files in root + common sub-packages
|
|
30
|
+
const tsconfigLocations = [
|
|
31
|
+
'tsconfig.json',
|
|
32
|
+
'backend/tsconfig.json',
|
|
33
|
+
'frontend/tsconfig.json',
|
|
34
|
+
'frontend-user/tsconfig.json',
|
|
35
|
+
'src/tsconfig.json',
|
|
36
|
+
'mcp/tsconfig.json'
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
let bestConfig = null
|
|
40
|
+
|
|
41
|
+
for (const loc of tsconfigLocations) {
|
|
42
|
+
const tsconfigPath = join(projectPath, loc)
|
|
43
|
+
if (!existsSync(tsconfigPath)) continue
|
|
44
|
+
|
|
45
|
+
result.details.tsconfigPaths.push(loc)
|
|
46
|
+
result.details.tsconfigFound = true
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Strip JSON comments (// and /* */) before parsing
|
|
50
|
+
const raw = readFileSync(tsconfigPath, 'utf-8')
|
|
51
|
+
const stripped = raw
|
|
52
|
+
.replace(/\/\/.*$/gm, '')
|
|
53
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
54
|
+
.replace(/,\s*([}\]])/g, '$1')
|
|
55
|
+
const tsconfig = JSON.parse(stripped)
|
|
56
|
+
const opts = tsconfig.compilerOptions || {}
|
|
57
|
+
|
|
58
|
+
if (opts.strict && !bestConfig) {
|
|
59
|
+
bestConfig = { path: loc, strict: true, opts }
|
|
60
|
+
} else if (!bestConfig) {
|
|
61
|
+
bestConfig = { path: loc, strict: false, opts }
|
|
62
|
+
}
|
|
63
|
+
} catch { /* invalid tsconfig, skip */ }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!result.details.tsconfigFound) {
|
|
67
|
+
result.status = 'warning'
|
|
68
|
+
result.details.message = 'No tsconfig.json found'
|
|
69
|
+
return result
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (bestConfig) {
|
|
73
|
+
const opts = bestConfig.opts
|
|
74
|
+
|
|
75
|
+
if (opts.strict) {
|
|
76
|
+
result.score += 1
|
|
77
|
+
result.details.strict = true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (opts.noImplicitAny) result.details.noImplicitAny = true
|
|
81
|
+
if (opts.strictNullChecks) result.details.strictNullChecks = true
|
|
82
|
+
if (opts.noUncheckedIndexedAccess) result.details.noUncheckedIndexedAccess = true
|
|
83
|
+
|
|
84
|
+
// Bonus point: strict + extra strictness OR individual flags
|
|
85
|
+
if (opts.strict && opts.noUncheckedIndexedAccess) {
|
|
86
|
+
result.score += 1
|
|
87
|
+
} else if (!opts.strict && opts.noImplicitAny && opts.strictNullChecks) {
|
|
88
|
+
result.score += 1
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check for typecheck script in package.json files
|
|
93
|
+
const packageLocations = [
|
|
94
|
+
'package.json',
|
|
95
|
+
'backend/package.json',
|
|
96
|
+
'frontend/package.json',
|
|
97
|
+
'frontend-user/package.json'
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
for (const loc of packageLocations) {
|
|
101
|
+
const pkgPath = join(projectPath, loc)
|
|
102
|
+
if (!existsSync(pkgPath)) continue
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
106
|
+
const scripts = pkg.scripts || {}
|
|
107
|
+
|
|
108
|
+
// Look for typecheck or tsc --noEmit scripts
|
|
109
|
+
for (const [name, cmd] of Object.entries(scripts)) {
|
|
110
|
+
if (typeof cmd !== 'string') continue
|
|
111
|
+
if (name === 'typecheck' || name === 'type-check' ||
|
|
112
|
+
cmd.includes('tsc --noEmit') || cmd.includes('tsc -noEmit') ||
|
|
113
|
+
(name === 'check' && cmd.includes('tsc'))) {
|
|
114
|
+
result.details.hasTypecheckScript = true
|
|
115
|
+
result.details.typecheckScript = `${loc} → ${name}: ${cmd}`
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (result.details.hasTypecheckScript) break
|
|
120
|
+
} catch { /* ignore */ }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (result.details.hasTypecheckScript) {
|
|
124
|
+
result.score += 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Determine status
|
|
128
|
+
if (result.score === 0) {
|
|
129
|
+
result.status = 'error'
|
|
130
|
+
result.details.message = 'TypeScript not strict and no typecheck script found'
|
|
131
|
+
} else if (result.score < 2) {
|
|
132
|
+
result.status = 'warning'
|
|
133
|
+
result.details.message = result.details.strict
|
|
134
|
+
? 'strict: true but no typecheck script'
|
|
135
|
+
: result.details.hasTypecheckScript
|
|
136
|
+
? 'Typecheck script exists but strict mode disabled'
|
|
137
|
+
: 'Partial TypeScript strictness'
|
|
138
|
+
} else {
|
|
139
|
+
result.details.message = `TypeScript well-configured (${result.details.tsconfigPaths.length} tsconfig(s))`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result
|
|
143
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulbatical/tetra-dev-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "restricted"
|
|
6
6
|
},
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"templates/"
|
|
36
36
|
],
|
|
37
37
|
"scripts": {
|
|
38
|
-
"test": "
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
39
40
|
"lint": "eslint src/ lib/ bin/",
|
|
40
41
|
"build": "echo 'No build step needed'"
|
|
41
42
|
},
|
|
@@ -52,7 +53,8 @@
|
|
|
52
53
|
"yaml": "^2.8.2"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"eslint": "^9.0.0"
|
|
56
|
+
"eslint": "^9.0.0",
|
|
57
|
+
"vitest": "^4.0.18"
|
|
56
58
|
},
|
|
57
59
|
"peerDependencies": {
|
|
58
60
|
"eslint": ">=8.0.0",
|