@tsslint/cli 1.3.5 → 1.3.6
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/index.js +107 -71
- package/package.json +4 -4
package/index.js
CHANGED
|
@@ -6,12 +6,12 @@ const core = require("@tsslint/core");
|
|
|
6
6
|
const cache = require("./lib/cache");
|
|
7
7
|
const glob = require("glob");
|
|
8
8
|
const fs = require("fs");
|
|
9
|
-
const
|
|
10
|
-
const purple = '\x1b[35m';
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
9
|
+
const _reset = '\x1b[0m';
|
|
10
|
+
const purple = (s) => '\x1b[35m' + s + _reset;
|
|
11
|
+
const darkGray = (s) => '\x1b[90m' + s + _reset;
|
|
12
|
+
const lightRed = (s) => '\x1b[91m' + s + _reset;
|
|
13
|
+
const lightGreen = (s) => '\x1b[92m' + s + _reset;
|
|
14
|
+
const lightYellow = (s) => '\x1b[93m' + s + _reset;
|
|
15
15
|
(async () => {
|
|
16
16
|
let hasError = false;
|
|
17
17
|
let projectVersion = 0;
|
|
@@ -54,8 +54,16 @@ const reset = '\x1b[0m';
|
|
|
54
54
|
const languageService = ts.createLanguageService(languageServiceHost);
|
|
55
55
|
if (process.argv.includes('--project')) {
|
|
56
56
|
const projectIndex = process.argv.indexOf('--project');
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
let tsconfig = process.argv[projectIndex + 1];
|
|
58
|
+
if (tsconfig.startsWith('-') || !tsconfig) {
|
|
59
|
+
clack.log.error(lightRed(`Missing argument for --project.`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (!tsconfig.startsWith('.')) {
|
|
63
|
+
tsconfig = `./${tsconfig}`;
|
|
64
|
+
}
|
|
65
|
+
await projectWorker(tsconfig);
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
68
|
else if (process.argv.includes('--projects')) {
|
|
61
69
|
const projectsIndex = process.argv.indexOf('--projects');
|
|
@@ -74,20 +82,21 @@ const reset = '\x1b[0m';
|
|
|
74
82
|
}
|
|
75
83
|
}
|
|
76
84
|
else {
|
|
77
|
-
await
|
|
85
|
+
const tsconfig = await askTSConfig();
|
|
86
|
+
await projectWorker(tsconfig);
|
|
78
87
|
}
|
|
79
88
|
process.exit(hasError ? 1 : 0);
|
|
80
89
|
async function projectWorker(tsconfigOption) {
|
|
81
|
-
const tsconfig =
|
|
82
|
-
clack.intro(`${purple
|
|
90
|
+
const tsconfig = require.resolve(tsconfigOption, { paths: [process.cwd()] });
|
|
91
|
+
clack.intro(`${purple('[project]')} ${path.relative(process.cwd(), tsconfig)}`);
|
|
83
92
|
parsed = parseCommonLine(tsconfig);
|
|
84
93
|
if (!parsed.fileNames.length) {
|
|
85
|
-
clack.outro(
|
|
94
|
+
clack.outro(lightYellow('No included files.'));
|
|
86
95
|
return;
|
|
87
96
|
}
|
|
88
97
|
const configFile = ts.findConfigFile(path.dirname(tsconfig), ts.sys.fileExists, 'tsslint.config.ts');
|
|
89
98
|
if (!configFile) {
|
|
90
|
-
clack.outro(
|
|
99
|
+
clack.outro(lightYellow('No tsslint.config.ts found.'));
|
|
91
100
|
return;
|
|
92
101
|
}
|
|
93
102
|
if (!configs.has(configFile)) {
|
|
@@ -103,9 +112,6 @@ const reset = '\x1b[0m';
|
|
|
103
112
|
if (!tsslintConfig) {
|
|
104
113
|
return;
|
|
105
114
|
}
|
|
106
|
-
if (!parsed.fileNames) {
|
|
107
|
-
throw new Error('No input files found in tsconfig!');
|
|
108
|
-
}
|
|
109
115
|
projectVersion++;
|
|
110
116
|
typeRootsVersion++;
|
|
111
117
|
const lintCache = process.argv.includes('--force')
|
|
@@ -119,19 +125,21 @@ const reset = '\x1b[0m';
|
|
|
119
125
|
tsconfig: ts.server.toNormalizedPath(tsconfig),
|
|
120
126
|
};
|
|
121
127
|
const linter = core.createLinter(projectContext, tsslintConfig, 'cli', clack);
|
|
122
|
-
|
|
128
|
+
let lintSpinner = clack.spinner();
|
|
123
129
|
let hasFix = false;
|
|
130
|
+
let excluded = 0;
|
|
124
131
|
let passed = 0;
|
|
125
132
|
let errors = 0;
|
|
126
133
|
let warnings = 0;
|
|
127
134
|
let cached = 0;
|
|
128
135
|
let t = Date.now();
|
|
129
|
-
lintSpinner.start();
|
|
136
|
+
lintSpinner.start(darkGray(`[1/${parsed.fileNames.length}] ${path.relative(process.cwd(), parsed.fileNames[0])}`));
|
|
137
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
130
138
|
for (let i = 0; i < parsed.fileNames.length; i++) {
|
|
131
139
|
const fileName = parsed.fileNames[i];
|
|
132
140
|
if (Date.now() - t > 100) {
|
|
133
141
|
t = Date.now();
|
|
134
|
-
lintSpinner.message(
|
|
142
|
+
lintSpinner.message(darkGray(`[${i + 1}/${parsed.fileNames.length}] ${path.relative(process.cwd(), fileName)}`));
|
|
135
143
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
136
144
|
}
|
|
137
145
|
const fileMtime = fs.statSync(fileName).mtimeMs;
|
|
@@ -151,6 +159,7 @@ const reset = '\x1b[0m';
|
|
|
151
159
|
else {
|
|
152
160
|
lintCache[fileName] = fileCache = [fileMtime, {}, [], [], {}];
|
|
153
161
|
}
|
|
162
|
+
let diagnostics;
|
|
154
163
|
if (process.argv.includes('--fix')) {
|
|
155
164
|
let retry = 3;
|
|
156
165
|
let shouldRetry = true;
|
|
@@ -164,7 +173,7 @@ const reset = '\x1b[0m';
|
|
|
164
173
|
fileCache[2].length = 0;
|
|
165
174
|
fileCache[3].length = 0;
|
|
166
175
|
}
|
|
167
|
-
|
|
176
|
+
diagnostics = linter.lint(fileName, fileCache);
|
|
168
177
|
const fixes = linter.getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics, fileCache);
|
|
169
178
|
const textChanges = core.combineCodeFixes(fileName, fixes);
|
|
170
179
|
if (textChanges.length) {
|
|
@@ -180,9 +189,16 @@ const reset = '\x1b[0m';
|
|
|
180
189
|
ts.sys.writeFile(fileName, newSnapshot.getText(0, newSnapshot.getLength()));
|
|
181
190
|
fileCache[0] = fs.statSync(fileName).mtimeMs;
|
|
182
191
|
}
|
|
192
|
+
if (shouldRetry) {
|
|
193
|
+
diagnostics = linter.lint(fileName, fileCache);
|
|
194
|
+
}
|
|
183
195
|
}
|
|
184
196
|
else {
|
|
185
|
-
|
|
197
|
+
diagnostics = linter.lint(fileName, fileCache);
|
|
198
|
+
}
|
|
199
|
+
if (diagnostics.length) {
|
|
200
|
+
hasFix ||= linter.hasCodeFixes(fileName);
|
|
201
|
+
hasError ||= diagnostics.some(diagnostic => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
186
202
|
for (const diagnostic of diagnostics) {
|
|
187
203
|
if (diagnostic.category === ts.DiagnosticCategory.Suggestion) {
|
|
188
204
|
continue;
|
|
@@ -192,73 +208,93 @@ const reset = '\x1b[0m';
|
|
|
192
208
|
getCanonicalFileName: ts.sys.useCaseSensitiveFileNames ? x => x : x => x.toLowerCase(),
|
|
193
209
|
getNewLine: () => ts.sys.newLine,
|
|
194
210
|
});
|
|
195
|
-
output = output.replace(`TS${diagnostic.code}`,
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
211
|
+
output = output.replace(`TS${diagnostic.code}`, String(diagnostic.code));
|
|
212
|
+
if (lintSpinner) {
|
|
213
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
214
|
+
errors++;
|
|
215
|
+
lintSpinner.stop(output, 1);
|
|
216
|
+
}
|
|
217
|
+
else if (diagnostic.category === ts.DiagnosticCategory.Warning) {
|
|
218
|
+
warnings++;
|
|
219
|
+
lintSpinner.stop(output, 2);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
lintSpinner.stop(output);
|
|
223
|
+
}
|
|
224
|
+
lintSpinner = undefined;
|
|
203
225
|
}
|
|
204
226
|
else {
|
|
205
|
-
|
|
227
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
228
|
+
errors++;
|
|
229
|
+
clack.log.error(output);
|
|
230
|
+
}
|
|
231
|
+
else if (diagnostic.category === ts.DiagnosticCategory.Warning) {
|
|
232
|
+
warnings++;
|
|
233
|
+
clack.log.warning(output);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
clack.log.info(output);
|
|
237
|
+
}
|
|
206
238
|
}
|
|
207
239
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
240
|
+
}
|
|
241
|
+
else if (!Object.keys(linter.getRules(fileName, fileCache)).length) {
|
|
242
|
+
excluded++;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
passed++;
|
|
246
|
+
}
|
|
247
|
+
if (!lintSpinner) {
|
|
248
|
+
lintSpinner = clack.spinner();
|
|
249
|
+
lintSpinner.start(darkGray(`[${i + 1}/${parsed.fileNames.length}] ${path.relative(process.cwd(), parsed.fileNames[i])}`));
|
|
250
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
215
251
|
}
|
|
216
252
|
}
|
|
217
253
|
if (cached) {
|
|
218
|
-
lintSpinner.stop(
|
|
254
|
+
lintSpinner.stop(darkGray(`Processed ${parsed.fileNames.length} files with cache. (Use --force to ignore cache.)`));
|
|
219
255
|
}
|
|
220
256
|
else {
|
|
221
|
-
lintSpinner.stop(
|
|
222
|
-
}
|
|
223
|
-
if (hasFix) {
|
|
224
|
-
clack.log.message(`${gray}Use --fix to apply fixes.${reset}`);
|
|
257
|
+
lintSpinner.stop(darkGray(`Processed ${parsed.fileNames.length} files.`));
|
|
225
258
|
}
|
|
226
259
|
const data = [
|
|
227
|
-
[passed, 'passed',
|
|
228
|
-
[errors, 'errors',
|
|
229
|
-
[warnings, 'warnings',
|
|
260
|
+
[passed, 'passed', lightGreen],
|
|
261
|
+
[errors, 'errors', lightRed],
|
|
262
|
+
[warnings, 'warnings', lightYellow],
|
|
263
|
+
[excluded, 'excluded', darkGray],
|
|
230
264
|
];
|
|
231
|
-
|
|
265
|
+
let summary = data
|
|
232
266
|
.filter(([count]) => count)
|
|
233
|
-
.map(([count, label, color]) => `${
|
|
234
|
-
.join(
|
|
267
|
+
.map(([count, label, color]) => color(`${count} ${label}`))
|
|
268
|
+
.join(darkGray(' | '));
|
|
269
|
+
if (hasFix) {
|
|
270
|
+
summary += darkGray(` (Use --fix to apply automatic fixes.)`);
|
|
271
|
+
}
|
|
272
|
+
else if (errors || warnings) {
|
|
273
|
+
summary += darkGray(` (No fixes available.)`);
|
|
274
|
+
}
|
|
235
275
|
clack.outro(summary);
|
|
236
276
|
cache.saveCache(configFile, lintCache, ts.sys.createHash);
|
|
237
277
|
}
|
|
238
|
-
async function
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
shortTsconfig = `./${shortTsconfig}`;
|
|
244
|
-
}
|
|
245
|
-
tsconfig = await clack.text({
|
|
246
|
-
message: 'Select the tsconfig project. (Use --project or --projects to skip this prompt.)',
|
|
247
|
-
placeholder: shortTsconfig ? `${shortTsconfig} (${parseCommonLine(tsconfig).fileNames.length} files)` : 'No tsconfig.json/jsconfig.json found, please enter the path to the tsconfig.json/jsconfig.json file.',
|
|
248
|
-
defaultValue: shortTsconfig,
|
|
249
|
-
validate(value) {
|
|
250
|
-
value ||= shortTsconfig;
|
|
251
|
-
try {
|
|
252
|
-
require.resolve(value, { paths: [process.cwd()] });
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
return `File not found!`;
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
});
|
|
278
|
+
async function askTSConfig() {
|
|
279
|
+
const presetConfig = ts.findConfigFile(process.cwd(), ts.sys.fileExists);
|
|
280
|
+
let shortTsconfig = presetConfig ? path.relative(process.cwd(), presetConfig) : undefined;
|
|
281
|
+
if (!shortTsconfig?.startsWith('.')) {
|
|
282
|
+
shortTsconfig = `./${shortTsconfig}`;
|
|
259
283
|
}
|
|
260
|
-
|
|
261
|
-
|
|
284
|
+
return await clack.text({
|
|
285
|
+
message: 'Select the project. (Use --project or --projects to skip this prompt.)',
|
|
286
|
+
placeholder: shortTsconfig ? `${shortTsconfig} (${parseCommonLine(presetConfig).fileNames.length} files)` : 'No tsconfig.json/jsconfig.json found, please enter the path to the tsconfig.json/jsconfig.json file.',
|
|
287
|
+
defaultValue: shortTsconfig,
|
|
288
|
+
validate(value) {
|
|
289
|
+
value ||= shortTsconfig;
|
|
290
|
+
try {
|
|
291
|
+
require.resolve(value, { paths: [process.cwd()] });
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return 'No such file.';
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
});
|
|
262
298
|
}
|
|
263
299
|
function parseCommonLine(tsconfig) {
|
|
264
300
|
const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsslint/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tsslint": "./bin/tsslint.js"
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@clack/prompts": "^0.8.2",
|
|
19
|
-
"@tsslint/config": "1.3.
|
|
20
|
-
"@tsslint/core": "1.3.
|
|
19
|
+
"@tsslint/config": "1.3.6",
|
|
20
|
+
"@tsslint/core": "1.3.6",
|
|
21
21
|
"glob": "^10.4.1"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"typescript": "*"
|
|
25
25
|
},
|
|
26
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "d153aa87c92803b4c20fef1f5cf2799f1a599099"
|
|
27
27
|
}
|