tz-clean 1.0.0 → 2.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/.dependency-cruiser.cjs +94 -0
- package/.github/workflows/ci.yml +42 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.husky/pre-push +3 -0
- package/.tz-clean-report.html +413 -0
- package/README-DEV.md +67 -0
- package/README-USER.md +25 -3
- package/__tests__/cli.test.js +8 -0
- package/commitlint.config.js +1 -0
- package/cspell.json +33 -0
- package/eslint.config.mjs +26 -9
- package/index.js +248 -20
- package/knip.json +5 -0
- package/package.json +34 -5
- package/report-template.html +357 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** @type {import('dependency-cruiser').IConfiguration} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
forbidden: [
|
|
4
|
+
{
|
|
5
|
+
comment: 'This dependency is part of a circular relationship.',
|
|
6
|
+
from: {},
|
|
7
|
+
name: 'no-circular',
|
|
8
|
+
severity: 'error',
|
|
9
|
+
to: { circular: true },
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
comment: "This is an orphan module - it's likely not used.",
|
|
13
|
+
from: { orphan: true },
|
|
14
|
+
name: 'no-orphans',
|
|
15
|
+
severity: 'info',
|
|
16
|
+
to: {},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
comment: 'A module depends on a node core module that has been deprecated.',
|
|
20
|
+
from: {},
|
|
21
|
+
name: 'no-deprecated-core',
|
|
22
|
+
severity: 'warn',
|
|
23
|
+
to: {
|
|
24
|
+
dependencyTypes: ['core'],
|
|
25
|
+
path: [
|
|
26
|
+
'^v8/tools/codemap$',
|
|
27
|
+
'^v8/tools/consarray$',
|
|
28
|
+
'^v8/tools/csvparser$',
|
|
29
|
+
'^v8/tools/logreader$',
|
|
30
|
+
'^v8/tools/profile_view$',
|
|
31
|
+
'^v8/tools/profile$',
|
|
32
|
+
'^v8/tools/SourceMap$',
|
|
33
|
+
'^v8/tools/splaytree$',
|
|
34
|
+
'^v8/tools/tickprocessor-driver$',
|
|
35
|
+
'^v8/tools/tickprocessor$',
|
|
36
|
+
'^node-inspect/lib/_inspect$',
|
|
37
|
+
'^node-inspect/lib/internal/inspect_client$',
|
|
38
|
+
'^node-inspect/lib/internal/inspect_repl$',
|
|
39
|
+
'^async_hooks$',
|
|
40
|
+
'^assert$',
|
|
41
|
+
'^punycode$',
|
|
42
|
+
'^domain$',
|
|
43
|
+
'^constants$',
|
|
44
|
+
'^sys$',
|
|
45
|
+
'^_linklist$',
|
|
46
|
+
'^_stream_wrap$',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
comment: 'This module depends on a module that cannot be found.',
|
|
52
|
+
from: {},
|
|
53
|
+
name: 'not-to-unresolvable',
|
|
54
|
+
severity: 'error',
|
|
55
|
+
to: { couldNotResolve: true },
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
comment:
|
|
59
|
+
'Likely this module depends on an external npm package that occurs more than once in your package.json i.e. bot as a devDependencies and in dependencies.',
|
|
60
|
+
from: {},
|
|
61
|
+
name: 'no-duplicate-dep-types',
|
|
62
|
+
severity: 'warn',
|
|
63
|
+
to: { dependencyTypesNot: ['type-only'], moreThanOneDependencyType: true },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
comment:
|
|
67
|
+
"This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. If there's something in a spec that's of use to other modules, it doesn't belong in that spec. Please move it to a safe utility module elsewhere.",
|
|
68
|
+
from: {},
|
|
69
|
+
name: 'not-to-spec',
|
|
70
|
+
severity: 'error',
|
|
71
|
+
to: { path: '\\.(spec|test)\\.(js|mjs|cjs|ts|ls|coffee|litcoffee|coffee\\.md)$' },
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
comment:
|
|
75
|
+
'This module depends on an npm package from the "devDependencies" section of your package.json. It looks like something that ships to production, though.',
|
|
76
|
+
from: { path: '^(src|index\\.js)' },
|
|
77
|
+
name: 'not-to-dev-dep',
|
|
78
|
+
severity: 'error',
|
|
79
|
+
to: { dependencyTypes: ['npm-dev'] },
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
options: {
|
|
83
|
+
doNotFollow: { path: 'node_modules' },
|
|
84
|
+
enhancedResolveOptions: {
|
|
85
|
+
conditionNames: ['import', 'require', 'node', 'default'],
|
|
86
|
+
exportsFields: ['exports'],
|
|
87
|
+
},
|
|
88
|
+
reporterOptions: {
|
|
89
|
+
archi: { collapsePattern: '^(packages|src|lib|app|bin|test(s?)|__tests__|__mocks__)/[^/]+' },
|
|
90
|
+
dot: { collapsePattern: 'node_modules/[^/]+' },
|
|
91
|
+
},
|
|
92
|
+
tsPreCompilationDeps: true,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ['main']
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ['main']
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [18.x, 20.x, 22.x]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: ${{ matrix.node-version }}
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Run Typecheck
|
|
30
|
+
run: npm run typecheck
|
|
31
|
+
|
|
32
|
+
- name: Run Linter
|
|
33
|
+
run: npm run lint
|
|
34
|
+
|
|
35
|
+
- name: Run Architecture Validation (Dependency Cruiser)
|
|
36
|
+
run: npm run cruise
|
|
37
|
+
|
|
38
|
+
- name: Run Code Spell Checker
|
|
39
|
+
run: npm run spellcheck
|
|
40
|
+
|
|
41
|
+
- name: Run Tests
|
|
42
|
+
run: npm run test
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx --no-install commitlint --edit "$1"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/.husky/pre-push
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>tz-clean Analysis Report</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg-color: #0f172a;
|
|
10
|
+
--card-bg: #1e293b;
|
|
11
|
+
--text-main: #f8fafc;
|
|
12
|
+
--text-muted: #94a3b8;
|
|
13
|
+
--accent: #3b82f6;
|
|
14
|
+
--error: #ef4444;
|
|
15
|
+
--warning: #f59e0b;
|
|
16
|
+
--success: #10b981;
|
|
17
|
+
--border: #334155;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body {
|
|
27
|
+
font-family:
|
|
28
|
+
'Inter',
|
|
29
|
+
-apple-system,
|
|
30
|
+
BlinkMacSystemFont,
|
|
31
|
+
'Segoe UI',
|
|
32
|
+
Roboto,
|
|
33
|
+
Helvetica,
|
|
34
|
+
Arial,
|
|
35
|
+
sans-serif;
|
|
36
|
+
background-color: var(--bg-color);
|
|
37
|
+
color: var(--text-main);
|
|
38
|
+
line-height: 1.6;
|
|
39
|
+
padding: 2rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.container {
|
|
43
|
+
max-width: 1000px;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
header {
|
|
48
|
+
text-align: center;
|
|
49
|
+
margin-bottom: 2rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
h1 {
|
|
53
|
+
font-size: 2.5rem;
|
|
54
|
+
font-weight: 700;
|
|
55
|
+
background: linear-gradient(to right, #60a5fa, #3b82f6);
|
|
56
|
+
-webkit-background-clip: text;
|
|
57
|
+
background-clip: text;
|
|
58
|
+
-webkit-text-fill-color: transparent;
|
|
59
|
+
margin-bottom: 0.5rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dashboard-grid {
|
|
63
|
+
display: grid;
|
|
64
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
65
|
+
gap: 1.5rem;
|
|
66
|
+
margin-bottom: 2.5rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.stat-card {
|
|
70
|
+
background-color: var(--card-bg);
|
|
71
|
+
border: 1px solid var(--border);
|
|
72
|
+
border-radius: 12px;
|
|
73
|
+
padding: 1.5rem;
|
|
74
|
+
text-align: center;
|
|
75
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
76
|
+
transition:
|
|
77
|
+
transform 0.2s ease,
|
|
78
|
+
box-shadow 0.2s ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.stat-card:hover {
|
|
82
|
+
transform: translateY(-5px);
|
|
83
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.stat-title {
|
|
87
|
+
font-size: 1rem;
|
|
88
|
+
color: var(--text-muted);
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
letter-spacing: 1px;
|
|
91
|
+
}
|
|
92
|
+
.stat-value {
|
|
93
|
+
font-size: 3rem;
|
|
94
|
+
font-weight: 800;
|
|
95
|
+
margin-top: 0.5rem;
|
|
96
|
+
}
|
|
97
|
+
.stat-value.error {
|
|
98
|
+
color: var(--error);
|
|
99
|
+
}
|
|
100
|
+
.stat-value.warning {
|
|
101
|
+
color: var(--warning);
|
|
102
|
+
}
|
|
103
|
+
.stat-value.success {
|
|
104
|
+
color: var(--success);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.file-list {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: 1rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.file-card {
|
|
114
|
+
background-color: var(--card-bg);
|
|
115
|
+
border: 1px solid var(--border);
|
|
116
|
+
border-radius: 12px;
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.file-header {
|
|
121
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
122
|
+
padding: 1rem 1.5rem;
|
|
123
|
+
display: flex;
|
|
124
|
+
justify-content: space-between;
|
|
125
|
+
align-items: center;
|
|
126
|
+
border-bottom: 1px solid var(--border);
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.file-path {
|
|
131
|
+
font-family: monospace;
|
|
132
|
+
font-size: 0.95rem;
|
|
133
|
+
color: #e2e8f0;
|
|
134
|
+
word-break: break-all;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.badges {
|
|
138
|
+
display: flex;
|
|
139
|
+
gap: 0.5rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.badge {
|
|
143
|
+
padding: 0.25rem 0.75rem;
|
|
144
|
+
border-radius: 9999px;
|
|
145
|
+
font-size: 0.75rem;
|
|
146
|
+
font-weight: 700;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.badge.error {
|
|
150
|
+
background-color: rgba(239, 68, 68, 0.2);
|
|
151
|
+
color: #fca5a5;
|
|
152
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
153
|
+
}
|
|
154
|
+
.badge.warning {
|
|
155
|
+
background-color: rgba(245, 158, 11, 0.2);
|
|
156
|
+
color: #fcd34d;
|
|
157
|
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.issue-list {
|
|
161
|
+
list-style: none;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.issue-item {
|
|
165
|
+
padding: 1rem 1.5rem;
|
|
166
|
+
border-bottom: 1px solid rgba(51, 65, 85, 0.5);
|
|
167
|
+
display: flex;
|
|
168
|
+
gap: 1rem;
|
|
169
|
+
align-items: flex-start;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.issue-item:last-child {
|
|
173
|
+
border-bottom: none;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.issue-line {
|
|
177
|
+
font-family: monospace;
|
|
178
|
+
background-color: #0f172a;
|
|
179
|
+
padding: 0.25rem 0.5rem;
|
|
180
|
+
border-radius: 4px;
|
|
181
|
+
color: var(--text-muted);
|
|
182
|
+
font-size: 0.85rem;
|
|
183
|
+
min-width: 60px;
|
|
184
|
+
text-align: center;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.issue-content {
|
|
188
|
+
flex: 1;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.issue-message {
|
|
192
|
+
font-size: 0.95rem;
|
|
193
|
+
margin-bottom: 0.25rem;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.issue-rule {
|
|
197
|
+
font-size: 0.8rem;
|
|
198
|
+
color: var(--text-muted);
|
|
199
|
+
font-family: monospace;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.empty-state {
|
|
203
|
+
text-align: center;
|
|
204
|
+
padding: 4rem 2rem;
|
|
205
|
+
background-color: var(--card-bg);
|
|
206
|
+
border-radius: 12px;
|
|
207
|
+
border: 1px dashed var(--border);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.empty-icon {
|
|
211
|
+
font-size: 4rem;
|
|
212
|
+
margin-bottom: 1rem;
|
|
213
|
+
}
|
|
214
|
+
</style>
|
|
215
|
+
</head>
|
|
216
|
+
<body>
|
|
217
|
+
<div class="container">
|
|
218
|
+
<header>
|
|
219
|
+
<h1>tz-clean Analysis Report</h1>
|
|
220
|
+
<p style="color: var(--text-muted)" id="timestamp"></p>
|
|
221
|
+
</header>
|
|
222
|
+
|
|
223
|
+
<div class="dashboard-grid">
|
|
224
|
+
<div class="stat-card">
|
|
225
|
+
<div class="stat-title">Files Analyzed</div>
|
|
226
|
+
<div class="stat-value" id="stat-files">0</div>
|
|
227
|
+
</div>
|
|
228
|
+
<div class="stat-card">
|
|
229
|
+
<div class="stat-title">Errors</div>
|
|
230
|
+
<div class="stat-value error" id="stat-errors">0</div>
|
|
231
|
+
</div>
|
|
232
|
+
<div class="stat-card">
|
|
233
|
+
<div class="stat-title">Warnings</div>
|
|
234
|
+
<div class="stat-value warning" id="stat-warnings">0</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<div id="fixed-results" class="file-list" style="margin-bottom: 2rem; display: none">
|
|
239
|
+
<h2 style="margin-bottom: 1rem; color: var(--text-main)">✨ Auto-Fixed (Formatted)</h2>
|
|
240
|
+
<div
|
|
241
|
+
id="fixed-files-list"
|
|
242
|
+
style="
|
|
243
|
+
background: var(--card-bg);
|
|
244
|
+
padding: 1rem 1.5rem;
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
border: 1px solid var(--border);
|
|
247
|
+
"></div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<div id="results" class="file-list">
|
|
251
|
+
<!-- Generated content goes here -->
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<script>
|
|
256
|
+
// Injected by CLI
|
|
257
|
+
const REPORT_DATA = [
|
|
258
|
+
{
|
|
259
|
+
filePath: 'C:\\Users\\Admin\\Downloads\\clean-code\\.dependency-cruiser.cjs',
|
|
260
|
+
messages: [],
|
|
261
|
+
suppressedMessages: [],
|
|
262
|
+
errorCount: 0,
|
|
263
|
+
fatalErrorCount: 0,
|
|
264
|
+
warningCount: 0,
|
|
265
|
+
fixableErrorCount: 0,
|
|
266
|
+
fixableWarningCount: 0,
|
|
267
|
+
usedDeprecatedRules: [],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
filePath: 'C:\\Users\\Admin\\Downloads\\clean-code\\__tests__\\cli.test.js',
|
|
271
|
+
messages: [],
|
|
272
|
+
suppressedMessages: [],
|
|
273
|
+
errorCount: 0,
|
|
274
|
+
fatalErrorCount: 0,
|
|
275
|
+
warningCount: 0,
|
|
276
|
+
fixableErrorCount: 0,
|
|
277
|
+
fixableWarningCount: 0,
|
|
278
|
+
usedDeprecatedRules: [],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
filePath: 'C:\\Users\\Admin\\Downloads\\clean-code\\commitlint.config.js',
|
|
282
|
+
messages: [],
|
|
283
|
+
suppressedMessages: [],
|
|
284
|
+
errorCount: 0,
|
|
285
|
+
fatalErrorCount: 0,
|
|
286
|
+
warningCount: 0,
|
|
287
|
+
fixableErrorCount: 0,
|
|
288
|
+
fixableWarningCount: 0,
|
|
289
|
+
usedDeprecatedRules: [],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
filePath: 'C:\\Users\\Admin\\Downloads\\clean-code\\eslint.config.mjs',
|
|
293
|
+
messages: [],
|
|
294
|
+
suppressedMessages: [],
|
|
295
|
+
errorCount: 0,
|
|
296
|
+
fatalErrorCount: 0,
|
|
297
|
+
warningCount: 0,
|
|
298
|
+
fixableErrorCount: 0,
|
|
299
|
+
fixableWarningCount: 0,
|
|
300
|
+
usedDeprecatedRules: [],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
filePath: 'C:\\Users\\Admin\\Downloads\\clean-code\\index.js',
|
|
304
|
+
messages: [],
|
|
305
|
+
suppressedMessages: [],
|
|
306
|
+
errorCount: 0,
|
|
307
|
+
fatalErrorCount: 0,
|
|
308
|
+
warningCount: 0,
|
|
309
|
+
fixableErrorCount: 0,
|
|
310
|
+
fixableWarningCount: 0,
|
|
311
|
+
usedDeprecatedRules: [],
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
const FIXED_FILES = [];
|
|
315
|
+
|
|
316
|
+
document.getElementById('timestamp').innerText = new Date().toLocaleString();
|
|
317
|
+
|
|
318
|
+
let totalErrors = 0;
|
|
319
|
+
let totalWarnings = 0;
|
|
320
|
+
let filesWithIssues = 0;
|
|
321
|
+
|
|
322
|
+
if (FIXED_FILES && FIXED_FILES.length > 0) {
|
|
323
|
+
document.getElementById('fixed-results').style.display = 'block';
|
|
324
|
+
const fixedList = document.getElementById('fixed-files-list');
|
|
325
|
+
let fixedHtml = '<ul style="list-style: none; padding: 0;">';
|
|
326
|
+
FIXED_FILES.forEach((file) => {
|
|
327
|
+
fixedHtml += `<li style="margin-bottom: 0.5rem; color: var(--success); font-family: monospace;">✔️ ${file}</li>`;
|
|
328
|
+
});
|
|
329
|
+
fixedHtml += '</ul>';
|
|
330
|
+
fixedList.innerHTML = fixedHtml;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const resultsContainer = document.getElementById('results');
|
|
334
|
+
|
|
335
|
+
if (!REPORT_DATA || REPORT_DATA.length === 0) {
|
|
336
|
+
resultsContainer.innerHTML = `
|
|
337
|
+
<div class="empty-state">
|
|
338
|
+
<div class="empty-icon">🎉</div>
|
|
339
|
+
<h2>Perfect Code!</h2>
|
|
340
|
+
<p style="color: var(--text-muted); margin-top: 0.5rem">No issues found. Your code is clean.</p>
|
|
341
|
+
</div>
|
|
342
|
+
`;
|
|
343
|
+
} else {
|
|
344
|
+
const problematicFiles = REPORT_DATA.filter((file) => file.errorCount > 0 || file.warningCount > 0);
|
|
345
|
+
|
|
346
|
+
if (problematicFiles.length === 0) {
|
|
347
|
+
resultsContainer.innerHTML = `
|
|
348
|
+
<div class="empty-state">
|
|
349
|
+
<div class="empty-icon">🎉</div>
|
|
350
|
+
<h2>Perfect Code!</h2>
|
|
351
|
+
<p style="color: var(--text-muted); margin-top: 0.5rem">No issues found. Your code is clean.</p>
|
|
352
|
+
</div>
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
problematicFiles.forEach((file) => {
|
|
357
|
+
totalErrors += file.errorCount;
|
|
358
|
+
totalWarnings += file.warningCount;
|
|
359
|
+
filesWithIssues++;
|
|
360
|
+
|
|
361
|
+
const relativePath = file.filePath.replace(window.location.origin, '').split('\\').pop().split('/').pop();
|
|
362
|
+
|
|
363
|
+
const fileCard = document.createElement('div');
|
|
364
|
+
fileCard.className = 'file-card';
|
|
365
|
+
|
|
366
|
+
let badgesHtml = '';
|
|
367
|
+
if (file.errorCount > 0) badgesHtml += `<span class="badge error">${file.errorCount} Errors</span>`;
|
|
368
|
+
if (file.warningCount > 0) badgesHtml += `<span class="badge warning">${file.warningCount} Warnings</span>`;
|
|
369
|
+
|
|
370
|
+
let messagesHtml = '<ul class="issue-list">';
|
|
371
|
+
file.messages.forEach((msg) => {
|
|
372
|
+
const typeColor = msg.severity === 2 ? 'var(--error)' : 'var(--warning)';
|
|
373
|
+
messagesHtml += `
|
|
374
|
+
<li class="issue-item">
|
|
375
|
+
<div class="issue-line">L${msg.line}:${msg.column}</div>
|
|
376
|
+
<div class="issue-content">
|
|
377
|
+
<div class="issue-message" style="color: ${typeColor}">${msg.message}</div>
|
|
378
|
+
<div class="issue-rule">${msg.ruleId || 'syntax'}</div>
|
|
379
|
+
</div>
|
|
380
|
+
</li>
|
|
381
|
+
`;
|
|
382
|
+
});
|
|
383
|
+
messagesHtml += '</ul>';
|
|
384
|
+
|
|
385
|
+
fileCard.innerHTML = `
|
|
386
|
+
<div class="file-header">
|
|
387
|
+
<div class="file-path">${file.filePath}</div>
|
|
388
|
+
<div class="badges">${badgesHtml}</div>
|
|
389
|
+
</div>
|
|
390
|
+
${messagesHtml}
|
|
391
|
+
`;
|
|
392
|
+
resultsContainer.appendChild(fileCard);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
document.getElementById('stat-files').innerText = REPORT_DATA ? REPORT_DATA.length : 0;
|
|
397
|
+
|
|
398
|
+
const errEl = document.getElementById('stat-errors');
|
|
399
|
+
errEl.innerText = totalErrors;
|
|
400
|
+
if (totalErrors === 0) {
|
|
401
|
+
errEl.classList.remove('error');
|
|
402
|
+
errEl.classList.add('success');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const warnEl = document.getElementById('stat-warnings');
|
|
406
|
+
warnEl.innerText = totalWarnings;
|
|
407
|
+
if (totalWarnings === 0) {
|
|
408
|
+
warnEl.classList.remove('warning');
|
|
409
|
+
warnEl.classList.add('success');
|
|
410
|
+
}
|
|
411
|
+
</script>
|
|
412
|
+
</body>
|
|
413
|
+
</html>
|
package/README-DEV.md
CHANGED
|
@@ -54,3 +54,70 @@ If this happens, use a **Scoped Package**:
|
|
|
54
54
|
2. Run `npm publish --access public`.
|
|
55
55
|
3. The install command will become `npm install -g @your-username/tz-clean`.
|
|
56
56
|
4. The executable command in the terminal will remain `tz-clean`, as defined in the `"bin"` property of `package.json`.
|
|
57
|
+
|
|
58
|
+
## Future Development / Roadmap
|
|
59
|
+
|
|
60
|
+
To further improve code quality and the development workflow, the following features are planned for future integration into this tool or as recommendations for target projects:
|
|
61
|
+
|
|
62
|
+
1. **Git Hooks & `lint-staged` (Husky)**: Automating `tz-clean` to run only on staged files before every `git commit`, preventing dirty code from entering the repository.
|
|
63
|
+
2. **Testing Framework (Vitest / Jest)**: Adding unit and integration tests to ensure the CLI itself and target applications function correctly.
|
|
64
|
+
3. **Commitlint**: Enforcing _Conventional Commits_ to standardize git history and enable automated `CHANGELOG.md` generation.
|
|
65
|
+
4. **CI/CD Pipelines (GitHub Actions)**: Setting up automated workflows to run linting, formatting, and tests on every Push or Pull Request.
|
|
66
|
+
5. **CSpell**: Integrating a code spelling checker to catch typos in variables, comments, and strings.
|
|
67
|
+
6. **Security & Dependency Audit**: Incorporating automated security checks (like `npm audit` or Dependabot) to keep dependencies secure and up-to-date.
|
|
68
|
+
|
|
69
|
+
### Advanced Static Code Analysis (Code Smells & Architecture)
|
|
70
|
+
|
|
71
|
+
Untuk mendeteksi _spaghetti code_, fungsi berulang, dan pelanggaran prinsip _clean code_ (seperti SOLID), fitur berikut dapat ditambahkan di masa depan:
|
|
72
|
+
|
|
73
|
+
1. **SonarJS (`eslint-plugin-sonarjs`)**: Mendeteksi kode yang terlalu kompleks (_spaghetti code_), _bug_ tersembunyi, dan logika yang tidak efisien berdasarkan standar SonarQube.
|
|
74
|
+
2. **Knip**: Alat khusus untuk mendeteksi _unused files_, _unused exports_, dan _unused dependencies_. Sangat ampuh untuk membersihkan _boilerplate_ dan definisi yang tidak pernah dipakai.
|
|
75
|
+
3. **Copy/Paste Detector (`jscpd`)**: Secara programatis mendeteksi blok kode atau fungsi yang diduplikasi/di- _copy-paste_ di berbagai file (mencegah _un-reusable function_).
|
|
76
|
+
4. **Cyclomatic Complexity Limits**: Menerapkan batasan kompleksitas maksimum pada fungsi (via ESLint `complexity` rule) untuk memastikan penerapan _Single Responsibility Principle_ (SRP) dari SOLID.
|
|
77
|
+
|
|
78
|
+
### The "Ultimate Perfection" (Architecture & Reliability)
|
|
79
|
+
|
|
80
|
+
Untuk mencapai level "sempurna" secara rekayasa perangkat lunak (Software Engineering), beberapa lapisan validasi tingkat lanjut ini dapat diterapkan:
|
|
81
|
+
|
|
82
|
+
1. **Dependency Cruiser**: Mengamankan arsitektur proyek (seperti _Clean Architecture_). Mencegah pelanggaran aturan dependensi (contoh: _UI layer_ dilarang _import_ langsung dari _Database layer_) dan mendeteksi dependensi melingkar (_circular dependencies_).
|
|
83
|
+
2. **Mutation Testing (Stryker)**: "Menguji unit test Anda". Tool ini secara sengaja menyuntikkan _bug_ kecil ke dalam kode (mutasi). Jika _unit test_ Anda tetap berstatus "Lulus" (Pass) padahal ada _bug_, berarti test Anda lemah. Ini menjamin kualitas _test suite_ yang absolut.
|
|
84
|
+
3. **Bundle Size Limit (`size-limit`)**: Terutama untuk aplikasi Frontend atau Library, ini akan membatalkan _commit/build_ jika perubahan kode membuat ukuran aplikasi melebihi batas performa kilobyte yang diizinkan.
|
|
85
|
+
4. **Documentation Enforcement (`eslint-plugin-jsdoc`)**: Memaksa agar setiap _function_, parameter, dan tipe data memiliki komentar penjelasan (JSDoc/TSDoc) yang standar, sehingga kode 100% terdokumentasi dengan baik.
|
|
86
|
+
|
|
87
|
+
### Micro-level Formatting & Strict Conventions
|
|
88
|
+
|
|
89
|
+
Untuk mencapai keteraturan tingkat mikroskopis (penamaan, pengurutan, dan struktur file), fitur-fitur ini sangat direkomendasikan:
|
|
90
|
+
|
|
91
|
+
1. **Strict Naming Conventions (`@typescript-eslint/naming-convention`)**: Memaksa aturan penamaan yang mutlak. Contoh: _Class_ wajib `PascalCase`, variabel `camelCase`, konstanta `UPPER_CASE`. Juga dapat digunakan untuk melarang penggunaan nama variabel yang tidak jelas seperti `data`, `temp`, `info`, atau `val`.
|
|
92
|
+
2. **Absolute Sorting (`eslint-plugin-perfectionist` / `simple-import-sort`)**: Alat ini akan secara otomatis (via `--fix`) mengurutkan semua baris kode Anda sesuai abjad atau logika:
|
|
93
|
+
- Mengurutkan dan mengelompokkan `import` di bagian atas file.
|
|
94
|
+
- Mengurutkan _keys_ (kunci) di dalam suatu _Object_ atau _Interface_.
|
|
95
|
+
- Mengurutkan urutan _method/function_ di dalam sebuah _Class_ (misal: _constructor_ di atas, fungsi _public_ di tengah, _private_ di bawah).
|
|
96
|
+
3. **File Structure & Boundaries (`eslint-plugin-project-structure`)**: Memaksa konsistensi struktur folder proyek. Misalnya, memaksa agar semua file test (`*.test.js`) wajib berada di folder `__tests__`, atau komponen _frontend_ dilarang berada di luar folder `components/`. Ini membuat proses _refactoring_ file menjadi sangat tertib.
|
|
97
|
+
|
|
98
|
+
### Dependency Management & Security Monitoring
|
|
99
|
+
|
|
100
|
+
Untuk memastikan paket (_packages/libraries_) yang Anda gunakan selalu stabil, aman dari _hacker_ (bebas _vulnerability_), dan mutakhir (_up-to-date_):
|
|
101
|
+
|
|
102
|
+
1. **`npm-check-updates` (ncu)**: Alat CLI interaktif untuk mengecek secara spesifik mana saja _library_ di `package.json` yang sudah usang (_outdated_), lalu membantu Anda meng-upgrade versinya ke versi stabil terbaru tanpa merusak proyek.
|
|
103
|
+
2. **Dependabot / Renovate Bot**: Bot otomatis (biasanya di GitHub/GitLab) yang bekerja 24/7 di latar belakang. Jika ada versi _library_ baru atau jika ada _bug_ keamanan yang baru ditemukan pada versi yang Anda gunakan, bot ini akan langsung membuatkan _Pull Request_ otomatis untuk memperbaruinya.
|
|
104
|
+
3. **Snyk**: Standar industri tingkat _Enterprise_ untuk keamanan _open-source_. Snyk akan memindai seluruh rantai _dependencies_ Anda (bahkan _library_ di dalam _library_), menemukan kerentanan (_vulnerabilities_), dan mendeteksi apakah lisensi _library_ tersebut legal untuk digunakan.
|
|
105
|
+
4. **`npm audit`**: Fitur bawaan NPM yang akan memberikan peringatan (dan solusi `npm audit fix`) apabila terdapat _library_ yang terjangkit laporan cacat keamanan (CVE - _Common Vulnerabilities and Exposures_).
|
|
106
|
+
|
|
107
|
+
### UI Dashboards & Visual Reports
|
|
108
|
+
|
|
109
|
+
Jika Anda ingin alat CLI Anda tidak hanya mencetak teks di terminal, tetapi juga menghasilkan laporan visual berbasis web (UI) yang indah mengenai kesehatan kode:
|
|
110
|
+
|
|
111
|
+
1. **Plato (Code Complexity Dashboard)**: Tool ini membaca kode JavaScript/TypeScript Anda dan menghasilkan laporan HTML statis yang indah. Anda bisa melihat grafik _maintainability_ (skor kemudahan _maintenance_), ukuran file, dan kompleksitas per file dalam bentuk grafik interaktif.
|
|
112
|
+
2. **SonarQube / SonarCloud Dashboard**: Jika Anda menghubungkan proyek Anda ke SonarCloud, Anda akan mendapatkan sebuah _Dashboard UI_ profesional yang melacak _Technical Debt_ (Berapa hari/jam yang dibutuhkan untuk memperbaiki semua kode buruk), jumlah _code smells_, dan tingkat keandalan kode dari waktu ke waktu.
|
|
113
|
+
3. **Istanbul / NYC (Test Coverage UI)**: Menghasilkan sebuah _website_ HTML mini di komputer pengguna yang menampilkan secara persis baris kode mana saja yang berwarna merah (belum dites) dan baris mana yang berwarna hijau (sudah dilewati oleh _Unit Test_).
|
|
114
|
+
4. **Webpack Bundle Analyzer / Statoscope**: Menghasilkan UI berupa peta interaktif (TreeMap) yang memperlihatkan _library_ mana saja yang menyedot kapasitas memori aplikasi Anda paling besar, sehingga memudahkan pengguna untuk memangkas _library_ yang berat.
|
|
115
|
+
|
|
116
|
+
### Custom UI Reporting Architecture (No 3rd Party UI Modules)
|
|
117
|
+
|
|
118
|
+
Jika Anda ingin membangun UI analisis kode 100% buatan sendiri (tanpa modul pihak ketiga seperti Plato/SonarQube), CLI `tz-clean` dapat merakit laporannya sendiri dengan arsitektur berikut:
|
|
119
|
+
|
|
120
|
+
1. **Data Extraction (Node.js API)**: CLI mengeksekusi ESLint API dan TypeScript Compiler API secara internal (_programmatically_), lalu menangkap _output error/warnings/complexity_ dalam format JSON asli, bukan teks string ke terminal.
|
|
121
|
+
2. **Template Injector**: Anda membuat template `index.html`, `style.css`, dan `app.js` mentah di dalam _source code_ CLI Anda.
|
|
122
|
+
3. **HTML Generation**: CLI akan membaca file JSON hasil analisis, lalu "menyuntikkannya" (inject) ke dalam tag `<script> const reportData = {...} </script>` di dalam template HTML buatan Anda.
|
|
123
|
+
4. **Auto-Launch Browser**: CLI kemudian menyimpan `report.html` tersebut di folder sementara (sementara/lokal) pengguna, lalu menggunakan modul Node.js (misal: eksekusi command `start` di Windows atau `open` di Mac) untuk otomatis membuka _file_ HTML tersebut di browser bawaan pengguna.
|
package/README-USER.md
CHANGED
|
@@ -25,10 +25,32 @@ Navigate to any project directory where you want to clean your code and simply r
|
|
|
25
25
|
tz-clean
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Skipping Specific Tools
|
|
29
|
+
|
|
30
|
+
You can selectively disable any of the integrated tools by passing the corresponding flag:
|
|
31
|
+
|
|
32
|
+
- `--no-prettier`: Disables auto-formatting with Prettier.
|
|
33
|
+
- `--no-eslint`: Disables linting and auto-fixing with ESLint.
|
|
34
|
+
- `--no-typecheck`: Disables TypeScript validation.
|
|
35
|
+
|
|
36
|
+
**Example:**
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
tz-clean --no-prettier --no-typecheck
|
|
40
|
+
```
|
|
41
|
+
|
|
28
42
|
### What it does:
|
|
29
43
|
|
|
30
|
-
The tool will sequentially run the following tasks on your project:
|
|
44
|
+
The tool will sequentially run the following tasks on your project (unless disabled):
|
|
31
45
|
|
|
32
46
|
1. **Prettier**: Formats your code to ensure consistent styling.
|
|
33
|
-
2. **ESLint**: Lints your code and automatically fixes fixable issues
|
|
34
|
-
3. **TypeScript Validation**: Runs `tsc --noEmit` to check for type errors
|
|
47
|
+
2. **ESLint**: Lints your code and automatically fixes fixable issues.
|
|
48
|
+
3. **TypeScript Validation**: Runs `tsc --noEmit` to check for type errors.
|
|
49
|
+
|
|
50
|
+
### Viewing the Report
|
|
51
|
+
|
|
52
|
+
After analysis, you will be prompted to choose how you want to view the results:
|
|
53
|
+
|
|
54
|
+
1. **Terminal Summary**: A quick overview printed directly in your terminal, including a list of auto-fixed files and detailed error logs.
|
|
55
|
+
2. **UI Dashboard (Browser)**: Generates a beautiful HTML report and automatically opens it in your default web browser.
|
|
56
|
+
3. **UI Dashboard (Hosted)**: Spawns a local server on port `8080` to host the HTML report, and opens `http://localhost:8080` in your browser.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default { extends: ['@commitlint/config-conventional'] };
|