@tsslint/cli 1.4.5 → 1.5.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/index.js +133 -92
- package/lib/formatting.d.ts +9 -0
- package/lib/formatting.js +60 -0
- package/lib/worker.d.ts +19 -9
- package/lib/worker.js +159 -62
- package/package.json +6 -5
package/index.js
CHANGED
|
@@ -9,8 +9,10 @@ const glob = require("glob");
|
|
|
9
9
|
const fs = require("fs");
|
|
10
10
|
const os = require("os");
|
|
11
11
|
const languagePlugins = require("./lib/languagePlugins.js");
|
|
12
|
+
const formatting_js_1 = require("./lib/formatting.js");
|
|
12
13
|
const _reset = '\x1b[0m';
|
|
13
14
|
const purple = (s) => '\x1b[35m' + s + _reset;
|
|
15
|
+
const cyan = (s) => '\x1b[36m' + s + _reset;
|
|
14
16
|
const darkGray = (s) => '\x1b[90m' + s + _reset;
|
|
15
17
|
const lightRed = (s) => '\x1b[91m' + s + _reset;
|
|
16
18
|
const lightGreen = (s) => '\x1b[92m' + s + _reset;
|
|
@@ -31,20 +33,14 @@ if (process.argv.includes('--threads')) {
|
|
|
31
33
|
threads = Math.min(os.availableParallelism(), Number(threadsArg));
|
|
32
34
|
}
|
|
33
35
|
class Project {
|
|
34
|
-
constructor(
|
|
36
|
+
constructor(tsconfig, languages) {
|
|
37
|
+
this.tsconfig = tsconfig;
|
|
35
38
|
this.languages = languages;
|
|
36
39
|
this.workers = [];
|
|
37
40
|
this.fileNames = [];
|
|
38
41
|
this.options = {};
|
|
39
42
|
this.currentFileIndex = 0;
|
|
40
43
|
this.cache = {};
|
|
41
|
-
try {
|
|
42
|
-
this.tsconfig = require.resolve(tsconfigOption, { paths: [process.cwd()] });
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
console.error(lightRed(`No such file: ${tsconfigOption}`));
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
44
|
}
|
|
49
45
|
async init(
|
|
50
46
|
// @ts-expect-error
|
|
@@ -88,6 +84,8 @@ class Project {
|
|
|
88
84
|
const builtConfigs = new Map();
|
|
89
85
|
const clack = await import('@clack/prompts');
|
|
90
86
|
const processFiles = new Set();
|
|
87
|
+
const tsconfigAndLanguages = new Map();
|
|
88
|
+
const formattingSettings = getFormattingSettings();
|
|
91
89
|
let projects = [];
|
|
92
90
|
let spinner = clack.spinner();
|
|
93
91
|
let lastSpinnerUpdate = Date.now();
|
|
@@ -99,15 +97,16 @@ class Project {
|
|
|
99
97
|
let errors = 0;
|
|
100
98
|
let warnings = 0;
|
|
101
99
|
let cached = 0;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
if (![
|
|
101
|
+
'--project',
|
|
102
|
+
'--projects',
|
|
103
|
+
'--vue-project',
|
|
104
|
+
'--vue-projects',
|
|
105
|
+
'--mdx-project',
|
|
106
|
+
'--mdx-projects',
|
|
107
|
+
'--astro-project',
|
|
108
|
+
'--astro-projects',
|
|
109
|
+
].some(flag => process.argv.includes(flag))) {
|
|
111
110
|
const languages = await clack.multiselect({
|
|
112
111
|
required: false,
|
|
113
112
|
message: 'Select frameworks (optional)',
|
|
@@ -154,21 +153,11 @@ class Project {
|
|
|
154
153
|
}
|
|
155
154
|
let command = 'tsslint';
|
|
156
155
|
if (!languages.length) {
|
|
157
|
-
|
|
158
|
-
command += ' --project ' + selectedTsconfigs[0];
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
command += ' --projects ' + selectedTsconfigs.join(' ');
|
|
162
|
-
}
|
|
156
|
+
command += ' --project ' + selectedTsconfigs.join(' ');
|
|
163
157
|
}
|
|
164
158
|
else {
|
|
165
159
|
for (const language of languages) {
|
|
166
|
-
|
|
167
|
-
command += ` --${language}-project ` + selectedTsconfigs[0];
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
command += ` --${language}-projects ` + selectedTsconfigs.join(' ');
|
|
171
|
-
}
|
|
160
|
+
command += ` --${language}-project ` + selectedTsconfigs.join(' ');
|
|
172
161
|
}
|
|
173
162
|
}
|
|
174
163
|
clack.log.info(`Running: ${purple(command)}`);
|
|
@@ -180,76 +169,60 @@ class Project {
|
|
|
180
169
|
}
|
|
181
170
|
}
|
|
182
171
|
else {
|
|
172
|
+
const projectsFlag = process.argv.find(arg => arg.endsWith('-projects'));
|
|
173
|
+
if (projectsFlag) {
|
|
174
|
+
clack.log.warn(lightYellow(`Please use ${projectsFlag.slice(0, -1)} instead of ${projectsFlag} starting from version 1.5.0.`));
|
|
175
|
+
}
|
|
183
176
|
const options = [
|
|
184
177
|
{
|
|
185
|
-
|
|
186
|
-
projectsFlag: '--projects',
|
|
178
|
+
projectFlags: ['--project', '--projects'],
|
|
187
179
|
language: undefined,
|
|
188
180
|
},
|
|
189
181
|
{
|
|
190
|
-
|
|
191
|
-
projectsFlag: '--vue-projects',
|
|
182
|
+
projectFlags: ['--vue-project', '--vue-projects'],
|
|
192
183
|
language: 'vue',
|
|
193
184
|
},
|
|
194
185
|
{
|
|
195
|
-
|
|
186
|
+
projectFlags: ['--mdx-project', '--mdx-projects'],
|
|
196
187
|
projectsFlag: '--mdx-projects',
|
|
197
188
|
language: 'mdx',
|
|
198
189
|
},
|
|
199
190
|
{
|
|
200
|
-
|
|
201
|
-
projectsFlag: '--astro-projects',
|
|
191
|
+
projectFlags: ['--astro-project', '--astro-projects'],
|
|
202
192
|
language: 'astro',
|
|
203
193
|
},
|
|
204
194
|
];
|
|
205
|
-
for (const {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
if (!tsconfigAndLanguages.has(tsconfig)) {
|
|
217
|
-
tsconfigAndLanguages.set(tsconfig, []);
|
|
195
|
+
for (const { projectFlags, language } of options) {
|
|
196
|
+
const projectFlag = projectFlags.find(flag => process.argv.includes(flag));
|
|
197
|
+
if (!projectFlag) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
let foundArg = false;
|
|
201
|
+
const projectsIndex = process.argv.indexOf(projectFlag);
|
|
202
|
+
for (let i = projectsIndex + 1; i < process.argv.length; i++) {
|
|
203
|
+
if (process.argv[i].startsWith('-')) {
|
|
204
|
+
break;
|
|
218
205
|
}
|
|
219
|
-
|
|
220
|
-
|
|
206
|
+
foundArg = true;
|
|
207
|
+
const searchGlob = process.argv[i];
|
|
208
|
+
const tsconfigs = glob.sync(searchGlob);
|
|
209
|
+
if (!tsconfigs.length) {
|
|
210
|
+
clack.log.error(lightRed(`No projects found for ${projectFlag} ${searchGlob}.`));
|
|
211
|
+
process.exit(1);
|
|
221
212
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
for (let i = projectsIndex + 1; i < process.argv.length; i++) {
|
|
227
|
-
if (process.argv[i].startsWith('-')) {
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
foundArg = true;
|
|
231
|
-
const searchGlob = process.argv[i];
|
|
232
|
-
const tsconfigs = glob.sync(searchGlob);
|
|
233
|
-
if (!tsconfigs.length) {
|
|
234
|
-
clack.log.error(lightRed(`No projects found for ${projectsFlag} ${searchGlob}.`));
|
|
235
|
-
process.exit(1);
|
|
213
|
+
for (let tsconfig of tsconfigs) {
|
|
214
|
+
tsconfig = resolvePath(tsconfig);
|
|
215
|
+
if (!tsconfigAndLanguages.has(tsconfig)) {
|
|
216
|
+
tsconfigAndLanguages.set(tsconfig, []);
|
|
236
217
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
tsconfig = `./${tsconfig}`;
|
|
240
|
-
}
|
|
241
|
-
if (!tsconfigAndLanguages.has(tsconfig)) {
|
|
242
|
-
tsconfigAndLanguages.set(tsconfig, []);
|
|
243
|
-
}
|
|
244
|
-
if (language) {
|
|
245
|
-
tsconfigAndLanguages.get(tsconfig).push(language);
|
|
246
|
-
}
|
|
218
|
+
if (language) {
|
|
219
|
+
tsconfigAndLanguages.get(tsconfig).push(language);
|
|
247
220
|
}
|
|
248
221
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
222
|
+
}
|
|
223
|
+
if (!foundArg) {
|
|
224
|
+
clack.log.error(lightRed(`Missing argument for ${projectFlag}.`));
|
|
225
|
+
process.exit(1);
|
|
253
226
|
}
|
|
254
227
|
}
|
|
255
228
|
}
|
|
@@ -278,9 +251,15 @@ class Project {
|
|
|
278
251
|
return startWorker(worker.create());
|
|
279
252
|
}));
|
|
280
253
|
}
|
|
281
|
-
spinner.stop(
|
|
282
|
-
? `Processed ${processed} files with cache. (Use
|
|
283
|
-
: `Processed ${processed} files.`));
|
|
254
|
+
spinner.stop(cached
|
|
255
|
+
? darkGray(`Processed ${processed} files with cache. (Use `) + cyan(`--force`) + darkGray(` to ignore cache.)`)
|
|
256
|
+
: darkGray(`Processed ${processed} files.`));
|
|
257
|
+
if (process.argv.includes('--fix') && !formattingSettings) {
|
|
258
|
+
const vscodeSettings = ts.findConfigFile(process.cwd(), ts.sys.fileExists, '.vscode/settings.json');
|
|
259
|
+
if (vscodeSettings) {
|
|
260
|
+
clack.log.message(darkGray(`Found available editor settings, you can use `) + cyan(`--vscode-settings ${path.relative(process.cwd(), vscodeSettings)}`) + darkGray(` to enable code format.`));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
284
263
|
const data = [
|
|
285
264
|
[passed, 'passed', lightGreen],
|
|
286
265
|
[errors, 'errors', lightRed],
|
|
@@ -292,7 +271,7 @@ class Project {
|
|
|
292
271
|
.map(([count, label, color]) => color(`${count} ${label}`))
|
|
293
272
|
.join(darkGray(' | '));
|
|
294
273
|
if (hasFix) {
|
|
295
|
-
summary += darkGray(` (Use
|
|
274
|
+
summary += darkGray(` (Use `) + cyan(`--fix`) + darkGray(` to apply automatic fixes.)`);
|
|
296
275
|
}
|
|
297
276
|
else if (errors || warnings) {
|
|
298
277
|
summary += darkGray(` (No fixes available.)`);
|
|
@@ -315,7 +294,7 @@ class Project {
|
|
|
315
294
|
})[0];
|
|
316
295
|
}
|
|
317
296
|
project.workers.push(linterWorker);
|
|
318
|
-
const setupSuccess = await linterWorker.setup(project.tsconfig, project.languages, project.configFile, project.builtConfig, project.fileNames, project.options);
|
|
297
|
+
const setupSuccess = await linterWorker.setup(project.tsconfig, project.languages, project.configFile, project.builtConfig, project.fileNames, project.options, formattingSettings);
|
|
319
298
|
if (!setupSuccess) {
|
|
320
299
|
projects = projects.filter(p => p !== project);
|
|
321
300
|
startWorker(linterWorker);
|
|
@@ -342,15 +321,9 @@ class Project {
|
|
|
342
321
|
}
|
|
343
322
|
}
|
|
344
323
|
else {
|
|
345
|
-
project.cache[fileName] = fileCache = [fileMtime, {}, {}];
|
|
346
|
-
}
|
|
347
|
-
let diagnostics;
|
|
348
|
-
if (process.argv.includes('--fix')) {
|
|
349
|
-
diagnostics = await linterWorker.lintAndFix(fileName, fileCache);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
diagnostics = await linterWorker.lint(fileName, fileCache);
|
|
324
|
+
project.cache[fileName] = fileCache = [fileMtime, {}, {}, false];
|
|
353
325
|
}
|
|
326
|
+
let diagnostics = await linterWorker.lint(fileName, process.argv.includes('--fix'), fileCache);
|
|
354
327
|
diagnostics = diagnostics.filter(diagnostic => diagnostic.category !== ts.DiagnosticCategory.Suggestion);
|
|
355
328
|
if (diagnostics.length) {
|
|
356
329
|
hasFix ||= await linterWorker.hasCodeFixes(fileName);
|
|
@@ -388,6 +361,60 @@ class Project {
|
|
|
388
361
|
cache.saveCache(project.tsconfig, project.configFile, project.cache, ts.sys.createHash);
|
|
389
362
|
await startWorker(linterWorker);
|
|
390
363
|
}
|
|
364
|
+
function getFormattingSettings() {
|
|
365
|
+
let formattingSettings;
|
|
366
|
+
if (process.argv.includes('--vscode-settings')) {
|
|
367
|
+
formattingSettings = {
|
|
368
|
+
typescript: {},
|
|
369
|
+
javascript: {},
|
|
370
|
+
vue: {},
|
|
371
|
+
};
|
|
372
|
+
for (const section of ['typescript', 'javascript']) {
|
|
373
|
+
formattingSettings[section] = {
|
|
374
|
+
...ts.getDefaultFormatCodeSettings('\n'),
|
|
375
|
+
indentStyle: ts.IndentStyle.Smart,
|
|
376
|
+
newLineCharacter: '\n',
|
|
377
|
+
insertSpaceAfterCommaDelimiter: true,
|
|
378
|
+
insertSpaceAfterConstructor: false,
|
|
379
|
+
insertSpaceAfterSemicolonInForStatements: true,
|
|
380
|
+
insertSpaceBeforeAndAfterBinaryOperators: true,
|
|
381
|
+
insertSpaceAfterKeywordsInControlFlowStatements: true,
|
|
382
|
+
insertSpaceAfterFunctionKeywordForAnonymousFunctions: true,
|
|
383
|
+
insertSpaceBeforeFunctionParenthesis: false,
|
|
384
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
|
385
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
|
386
|
+
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
|
387
|
+
insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: true,
|
|
388
|
+
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
|
389
|
+
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
|
|
390
|
+
insertSpaceAfterTypeAssertion: false,
|
|
391
|
+
placeOpenBraceOnNewLineForFunctions: false,
|
|
392
|
+
placeOpenBraceOnNewLineForControlBlocks: false,
|
|
393
|
+
semicolons: ts.SemicolonPreference.Ignore,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
let vscodeSettingsConfig = process.argv[process.argv.indexOf('--vscode-settings') + 1];
|
|
397
|
+
if (!vscodeSettingsConfig || vscodeSettingsConfig.startsWith('-')) {
|
|
398
|
+
clack.log.error(lightRed(`Missing argument for --vscode-settings.`));
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
const vscodeSettingsFile = resolvePath(vscodeSettingsConfig);
|
|
402
|
+
const vscodeSettings = (0, formatting_js_1.getVSCodeFormattingSettings)(vscodeSettingsFile);
|
|
403
|
+
formattingSettings.typescript = {
|
|
404
|
+
...formattingSettings.typescript,
|
|
405
|
+
...vscodeSettings.typescript,
|
|
406
|
+
};
|
|
407
|
+
formattingSettings.javascript = {
|
|
408
|
+
...formattingSettings.javascript,
|
|
409
|
+
...vscodeSettings.javascript,
|
|
410
|
+
};
|
|
411
|
+
formattingSettings.vue = {
|
|
412
|
+
...formattingSettings.vue,
|
|
413
|
+
...vscodeSettings.vue,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return formattingSettings;
|
|
417
|
+
}
|
|
391
418
|
async function getBuiltConfig(configFile) {
|
|
392
419
|
if (!builtConfigs.has(configFile)) {
|
|
393
420
|
builtConfigs.set(configFile, core.buildConfig(configFile, ts.sys.createHash, spinner, (s, code) => log(darkGray(s), code)));
|
|
@@ -416,6 +443,20 @@ class Project {
|
|
|
416
443
|
spinner = clack.spinner();
|
|
417
444
|
spinner.start();
|
|
418
445
|
}
|
|
446
|
+
function resolvePath(p) {
|
|
447
|
+
if (!path.isAbsolute(p)
|
|
448
|
+
&& !p.startsWith('./')
|
|
449
|
+
&& !p.startsWith('../')) {
|
|
450
|
+
p = `./${p}`;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
return require.resolve(p, { paths: [process.cwd()] });
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
clack.log.error(lightRed(`No such file: ${p}`));
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
419
460
|
})();
|
|
420
461
|
async function parseCommonLine(tsconfig, languages) {
|
|
421
462
|
const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type * as ts from 'typescript';
|
|
2
|
+
export declare function getVSCodeFormattingSettings(settingsFile: string): {
|
|
3
|
+
javascript: ts.FormatCodeSettings;
|
|
4
|
+
typescript: ts.FormatCodeSettings;
|
|
5
|
+
vue: {
|
|
6
|
+
'script.initialIndent'?: boolean;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare function computeInitialIndent(content: string, i: number, baseTabSize?: number): number;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVSCodeFormattingSettings = getVSCodeFormattingSettings;
|
|
4
|
+
exports.computeInitialIndent = computeInitialIndent;
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const json5 = require("json5");
|
|
7
|
+
function getVSCodeFormattingSettings(settingsFile) {
|
|
8
|
+
const jsonc = fs.readFileSync(settingsFile, 'utf8');
|
|
9
|
+
const editorSettings = json5.parse(jsonc);
|
|
10
|
+
const jsSettings = {};
|
|
11
|
+
const tsSettings = {};
|
|
12
|
+
const vueSettings = {};
|
|
13
|
+
if ('editor.insertSpaces' in editorSettings) {
|
|
14
|
+
jsSettings.convertTabsToSpaces = !!editorSettings['editor.insertSpaces'];
|
|
15
|
+
tsSettings.convertTabsToSpaces = !!editorSettings['editor.insertSpaces'];
|
|
16
|
+
}
|
|
17
|
+
if ('editor.tabSize' in editorSettings) {
|
|
18
|
+
jsSettings.tabSize = editorSettings['editor.tabSize'];
|
|
19
|
+
tsSettings.tabSize = editorSettings['editor.tabSize'];
|
|
20
|
+
}
|
|
21
|
+
for (const key in editorSettings) {
|
|
22
|
+
if (key.startsWith('javascript.format.')) {
|
|
23
|
+
const settingKey = key.slice('javascript.format.'.length);
|
|
24
|
+
// @ts-expect-error
|
|
25
|
+
jsSettings[settingKey] = editorSettings[key];
|
|
26
|
+
}
|
|
27
|
+
else if (key.startsWith('typescript.format.')) {
|
|
28
|
+
const settingKey = key.slice('typescript.format.'.length);
|
|
29
|
+
// @ts-expect-error
|
|
30
|
+
tsSettings[settingKey] = editorSettings[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if ('vue.format.script.initialIndent' in editorSettings) {
|
|
34
|
+
vueSettings['script.initialIndent'] = !!editorSettings['vue.format.script.initialIndent'];
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
javascript: jsSettings,
|
|
38
|
+
typescript: tsSettings,
|
|
39
|
+
vue: vueSettings,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function computeInitialIndent(content, i, baseTabSize) {
|
|
43
|
+
let nChars = 0;
|
|
44
|
+
const tabSize = baseTabSize || 4;
|
|
45
|
+
while (i < content.length) {
|
|
46
|
+
const ch = content.charAt(i);
|
|
47
|
+
if (ch === ' ') {
|
|
48
|
+
nChars++;
|
|
49
|
+
}
|
|
50
|
+
else if (ch === '\t') {
|
|
51
|
+
nChars += tabSize;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
return Math.floor(nChars / tabSize);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=formatting.js.map
|
package/lib/worker.d.ts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import ts = require('typescript');
|
|
2
2
|
import core = require('@tsslint/core');
|
|
3
|
+
import { type getVSCodeFormattingSettings } from './formatting.js';
|
|
3
4
|
export declare function createLocal(): {
|
|
4
|
-
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions, _fmtSettings: {
|
|
6
|
+
javascript: ts.FormatCodeSettings;
|
|
7
|
+
typescript: ts.FormatCodeSettings;
|
|
8
|
+
vue: {
|
|
9
|
+
'script.initialIndent'?: boolean;
|
|
10
|
+
};
|
|
11
|
+
} | undefined): Promise<boolean>;
|
|
12
|
+
lint(fileName: string, fix: boolean, fileCache: core.FileLintCache): ts.DiagnosticWithLocation[];
|
|
7
13
|
hasCodeFixes(fileName: string): boolean;
|
|
8
14
|
hasRules(fileName: string, minimatchCache: Record<string, boolean>): boolean;
|
|
9
15
|
};
|
|
10
16
|
export declare function create(): {
|
|
11
|
-
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions, _fmtSettings: {
|
|
18
|
+
javascript: ts.FormatCodeSettings;
|
|
19
|
+
typescript: ts.FormatCodeSettings;
|
|
20
|
+
vue: {
|
|
21
|
+
'script.initialIndent'?: boolean;
|
|
22
|
+
};
|
|
23
|
+
} | undefined): Promise<boolean>;
|
|
24
|
+
lint(fileName: string, fix: boolean, fileCache: core.FileLintCache): Promise<ts.DiagnosticWithLocation[]>;
|
|
14
25
|
hasCodeFixes(fileName: string): Promise<boolean>;
|
|
15
26
|
hasRules(fileName: string, minimatchCache: Record<string, boolean>): Promise<boolean>;
|
|
16
27
|
};
|
|
17
|
-
declare function setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
18
|
-
declare function
|
|
19
|
-
declare function lint(fileName: string, fileCache: core.FileLintCache): readonly [ts.DiagnosticWithLocation[], core.FileLintCache];
|
|
28
|
+
declare function setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions, _fmtSettings: ReturnType<typeof getVSCodeFormattingSettings> | undefined): Promise<boolean>;
|
|
29
|
+
declare function lint(fileName: string, fix: boolean, fileCache: core.FileLintCache): readonly [ts.DiagnosticWithLocation[], core.FileLintCache];
|
|
20
30
|
declare function hasCodeFixes(fileName: string): boolean;
|
|
21
31
|
declare function hasRules(fileName: string, minimatchCache: core.FileLintCache[2]): readonly [boolean, Record<string, boolean>];
|
|
22
32
|
export {};
|
package/lib/worker.js
CHANGED
|
@@ -6,11 +6,14 @@ const ts = require("typescript");
|
|
|
6
6
|
const core = require("@tsslint/core");
|
|
7
7
|
const url = require("url");
|
|
8
8
|
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
9
10
|
const worker_threads = require("worker_threads");
|
|
10
11
|
const languagePlugins = require("./languagePlugins.js");
|
|
11
12
|
const language_core_1 = require("@volar/language-core");
|
|
12
13
|
const typescript_1 = require("@volar/typescript");
|
|
13
14
|
const transform_1 = require("@volar/typescript/lib/node/transform");
|
|
15
|
+
const transform_js_1 = require("@volar/typescript/lib/node/transform.js");
|
|
16
|
+
const formatting_js_1 = require("./formatting.js");
|
|
14
17
|
let projectVersion = 0;
|
|
15
18
|
let typeRootsVersion = 0;
|
|
16
19
|
let options = {};
|
|
@@ -19,6 +22,42 @@ let language;
|
|
|
19
22
|
let linter;
|
|
20
23
|
let linterLanguageService;
|
|
21
24
|
let linterSyntaxOnlyLanguageService;
|
|
25
|
+
let fmtSettings;
|
|
26
|
+
const formatLanguageService = ts.createLanguageService({
|
|
27
|
+
...ts.sys,
|
|
28
|
+
getCompilationSettings() {
|
|
29
|
+
return options;
|
|
30
|
+
},
|
|
31
|
+
getScriptFileNames() {
|
|
32
|
+
return [];
|
|
33
|
+
},
|
|
34
|
+
getScriptVersion() {
|
|
35
|
+
return '0';
|
|
36
|
+
},
|
|
37
|
+
getScriptSnapshot() {
|
|
38
|
+
return formattingSnapshot;
|
|
39
|
+
},
|
|
40
|
+
getScriptKind() {
|
|
41
|
+
return formattingScriptKind;
|
|
42
|
+
},
|
|
43
|
+
useCaseSensitiveFileNames() {
|
|
44
|
+
return ts.sys.useCaseSensitiveFileNames;
|
|
45
|
+
},
|
|
46
|
+
getCurrentDirectory() {
|
|
47
|
+
return ts.sys.getCurrentDirectory();
|
|
48
|
+
},
|
|
49
|
+
getDefaultLibFileName() {
|
|
50
|
+
return ts.getDefaultLibFilePath(options);
|
|
51
|
+
},
|
|
52
|
+
}, undefined, true);
|
|
53
|
+
let formattingSnapshot;
|
|
54
|
+
let formattingScriptKind;
|
|
55
|
+
let formattingIndex = 0;
|
|
56
|
+
function formatVirtualScript(kind, settings, snapshot) {
|
|
57
|
+
formattingSnapshot = snapshot;
|
|
58
|
+
formattingScriptKind = kind;
|
|
59
|
+
return formatLanguageService.getFormattingEditsForDocument(`${formattingIndex++}.txt`, settings);
|
|
60
|
+
}
|
|
22
61
|
const snapshots = new Map();
|
|
23
62
|
const versions = new Map();
|
|
24
63
|
const originalHost = {
|
|
@@ -78,9 +117,6 @@ function createLocal() {
|
|
|
78
117
|
lint(...args) {
|
|
79
118
|
return lint(...args)[0];
|
|
80
119
|
},
|
|
81
|
-
lintAndFix(...args) {
|
|
82
|
-
return lintAndFix(...args)[0];
|
|
83
|
-
},
|
|
84
120
|
hasCodeFixes(...args) {
|
|
85
121
|
return hasCodeFixes(...args);
|
|
86
122
|
},
|
|
@@ -100,11 +136,6 @@ function create() {
|
|
|
100
136
|
Object.assign(args[1], newCache); // Sync the cache
|
|
101
137
|
return res;
|
|
102
138
|
},
|
|
103
|
-
async lintAndFix(...args) {
|
|
104
|
-
const [res, newCache] = await sendRequest(lintAndFix, ...args);
|
|
105
|
-
Object.assign(args[1], newCache); // Sync the cache
|
|
106
|
-
return res;
|
|
107
|
-
},
|
|
108
139
|
hasCodeFixes(...args) {
|
|
109
140
|
return sendRequest(hasCodeFixes, ...args);
|
|
110
141
|
},
|
|
@@ -131,11 +162,10 @@ worker_threads.parentPort?.on('message', async (json) => {
|
|
|
131
162
|
const handlers = {
|
|
132
163
|
setup,
|
|
133
164
|
lint,
|
|
134
|
-
lintAndFix,
|
|
135
165
|
hasCodeFixes,
|
|
136
166
|
hasRules,
|
|
137
167
|
};
|
|
138
|
-
async function setup(tsconfig, languages, configFile, builtConfig, _fileNames, _options) {
|
|
168
|
+
async function setup(tsconfig, languages, configFile, builtConfig, _fileNames, _options, _fmtSettings) {
|
|
139
169
|
const clack = await import('@clack/prompts');
|
|
140
170
|
let config;
|
|
141
171
|
try {
|
|
@@ -192,27 +222,26 @@ async function setup(tsconfig, languages, configFile, builtConfig, _fileNames, _
|
|
|
192
222
|
allowNonTsExtensions: true,
|
|
193
223
|
}
|
|
194
224
|
: _options;
|
|
225
|
+
fmtSettings = _fmtSettings;
|
|
195
226
|
linter = core.createLinter({
|
|
196
|
-
configFile,
|
|
197
227
|
languageService: linterLanguageService,
|
|
198
228
|
languageServiceHost: linterHost,
|
|
199
229
|
typescript: ts,
|
|
200
|
-
|
|
201
|
-
}, config, 'cli', linterSyntaxOnlyLanguageService);
|
|
230
|
+
}, path.dirname(configFile), config, 'cli', linterSyntaxOnlyLanguageService);
|
|
202
231
|
return true;
|
|
203
232
|
}
|
|
204
|
-
function
|
|
205
|
-
let retry = 1;
|
|
206
|
-
let shouldRetry = true;
|
|
233
|
+
function lint(fileName, fix, fileCache) {
|
|
207
234
|
let newSnapshot;
|
|
208
235
|
let diagnostics;
|
|
209
|
-
|
|
236
|
+
let shouldCheck = true;
|
|
237
|
+
if (fix) {
|
|
210
238
|
if (Object.values(fileCache[1]).some(([hasFix]) => hasFix)) {
|
|
211
239
|
// Reset the cache if there are any fixes applied.
|
|
212
240
|
fileCache[1] = {};
|
|
213
241
|
fileCache[2] = {};
|
|
214
242
|
}
|
|
215
243
|
diagnostics = linter.lint(fileName, fileCache);
|
|
244
|
+
shouldCheck = false;
|
|
216
245
|
let fixes = linter
|
|
217
246
|
.getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics, fileCache[2])
|
|
218
247
|
.filter(fix => fix.fixId === 'tsslint');
|
|
@@ -224,21 +253,128 @@ function lintAndFix(fileName, fileCache) {
|
|
|
224
253
|
}
|
|
225
254
|
const textChanges = core.combineCodeFixes(fileName, fixes);
|
|
226
255
|
if (textChanges.length) {
|
|
256
|
+
fileCache[3] = false;
|
|
227
257
|
const oldSnapshot = snapshots.get(fileName);
|
|
228
258
|
newSnapshot = core.applyTextChanges(oldSnapshot, textChanges);
|
|
229
259
|
snapshots.set(fileName, newSnapshot);
|
|
230
260
|
versions.set(fileName, (versions.get(fileName) ?? 0) + 1);
|
|
231
261
|
projectVersion++;
|
|
232
|
-
|
|
262
|
+
}
|
|
263
|
+
if (!fileCache[3] && fmtSettings) {
|
|
264
|
+
fileCache[3] = true;
|
|
265
|
+
let script = language?.scripts.get(fileName);
|
|
266
|
+
let linterEdits = [];
|
|
267
|
+
let serviceEdits = [];
|
|
268
|
+
if (script?.generated) {
|
|
269
|
+
for (const code of (0, language_core_1.forEachEmbeddedCode)(script.generated.root)) {
|
|
270
|
+
if ((code.languageId === 'javascript'
|
|
271
|
+
|| code.languageId === 'typescript'
|
|
272
|
+
|| code.languageId === 'javascriptreact'
|
|
273
|
+
|| code.languageId === 'typescriptreact')
|
|
274
|
+
&& code.mappings.some(mapping => (0, language_core_1.isFormattingEnabled)(mapping.data))) {
|
|
275
|
+
const scriptKind = code.languageId === 'javascript' ? ts.ScriptKind.JS
|
|
276
|
+
: code.languageId === 'javascriptreact' ? ts.ScriptKind.JSX
|
|
277
|
+
: code.languageId === 'typescript' ? ts.ScriptKind.TS
|
|
278
|
+
: ts.ScriptKind.TSX;
|
|
279
|
+
const sourceFile = ts.createSourceFile(fileName, code.snapshot.getText(0, code.snapshot.getLength()), ts.ScriptTarget.Latest, true, scriptKind);
|
|
280
|
+
linterEdits.push(...linter
|
|
281
|
+
.format(sourceFile, fileCache[2])
|
|
282
|
+
.map(edit => {
|
|
283
|
+
return (0, transform_js_1.transformTextChange)(script, language, {
|
|
284
|
+
code,
|
|
285
|
+
extension: '',
|
|
286
|
+
scriptKind: scriptKind,
|
|
287
|
+
preventLeadingOffset: true,
|
|
288
|
+
}, edit, false, language_core_1.isFormattingEnabled)?.[1];
|
|
289
|
+
})
|
|
290
|
+
.filter(edit => !!edit));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const sourceFile = originalSyntaxOnlyService.getNonBoundSourceFile(fileName);
|
|
296
|
+
linterEdits = linter.format(sourceFile, fileCache[2]);
|
|
297
|
+
}
|
|
298
|
+
if (linterEdits.length) {
|
|
299
|
+
const oldSnapshot = snapshots.get(fileName);
|
|
300
|
+
newSnapshot = core.applyTextChanges(oldSnapshot, linterEdits);
|
|
301
|
+
snapshots.set(fileName, newSnapshot);
|
|
302
|
+
versions.set(fileName, (versions.get(fileName) ?? 0) + 1);
|
|
303
|
+
projectVersion++;
|
|
304
|
+
script = language?.scripts.get(fileName);
|
|
305
|
+
}
|
|
306
|
+
if (script?.generated) {
|
|
307
|
+
let sourceFile;
|
|
308
|
+
for (const code of (0, language_core_1.forEachEmbeddedCode)(script.generated.root)) {
|
|
309
|
+
if ((code.languageId === 'javascript'
|
|
310
|
+
|| code.languageId === 'typescript'
|
|
311
|
+
|| code.languageId === 'javascriptreact'
|
|
312
|
+
|| code.languageId === 'typescriptreact')
|
|
313
|
+
&& code.mappings.some(mapping => (0, language_core_1.isFormattingEnabled)(mapping.data))) {
|
|
314
|
+
const scriptKind = code.languageId === 'javascript' ? ts.ScriptKind.JS
|
|
315
|
+
: code.languageId === 'javascriptreact' ? ts.ScriptKind.JSX
|
|
316
|
+
: code.languageId === 'typescript' ? ts.ScriptKind.TS
|
|
317
|
+
: ts.ScriptKind.TSX;
|
|
318
|
+
let settings = scriptKind === ts.ScriptKind.JS || scriptKind === ts.ScriptKind.JSX
|
|
319
|
+
? fmtSettings.javascript
|
|
320
|
+
: fmtSettings.typescript;
|
|
321
|
+
if (settings.tabSize !== undefined) {
|
|
322
|
+
const firstMapping = code.mappings[0];
|
|
323
|
+
sourceFile ??= ts.createSourceFile(fileName, script.snapshot.getText(0, script.snapshot.getLength()), ts.ScriptTarget.Latest, true, ts.ScriptKind.Deferred);
|
|
324
|
+
const line = sourceFile.getLineAndCharacterOfPosition(firstMapping.sourceOffsets[0]).line;
|
|
325
|
+
const offset = sourceFile.getPositionOfLineAndCharacter(line, 0);
|
|
326
|
+
let initialIndentLevel = (0, formatting_js_1.computeInitialIndent)(script.snapshot.getText(0, script.snapshot.getLength()), offset, settings.tabSize);
|
|
327
|
+
if (script.languageId === 'vue'
|
|
328
|
+
&& fmtSettings.vue['script.initialIndent']
|
|
329
|
+
&& (code.id === 'script_raw'
|
|
330
|
+
|| code.id === 'scriptsetup_raw')) {
|
|
331
|
+
initialIndentLevel++;
|
|
332
|
+
}
|
|
333
|
+
settings = {
|
|
334
|
+
...settings,
|
|
335
|
+
baseIndentSize: initialIndentLevel * settings.tabSize,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
serviceEdits.push(...formatVirtualScript(scriptKind, settings, code.snapshot)
|
|
339
|
+
.map(edit => {
|
|
340
|
+
return (0, transform_js_1.transformTextChange)(script, language, {
|
|
341
|
+
code,
|
|
342
|
+
extension: '',
|
|
343
|
+
scriptKind: scriptKind,
|
|
344
|
+
preventLeadingOffset: true,
|
|
345
|
+
}, edit, false, language_core_1.isFormattingEnabled)?.[1];
|
|
346
|
+
})
|
|
347
|
+
.filter(edit => !!edit));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const scriptKind = linterHost.getScriptKind(fileName);
|
|
353
|
+
const settings = scriptKind === ts.ScriptKind.JS || scriptKind === ts.ScriptKind.JSX ? fmtSettings.javascript : fmtSettings.typescript;
|
|
354
|
+
serviceEdits = linterLanguageService.getFormattingEditsForDocument(fileName, settings);
|
|
355
|
+
}
|
|
356
|
+
if (serviceEdits.length) {
|
|
357
|
+
const oldSnapshot = snapshots.get(fileName);
|
|
358
|
+
newSnapshot = core.applyTextChanges(oldSnapshot, serviceEdits);
|
|
359
|
+
snapshots.set(fileName, newSnapshot);
|
|
360
|
+
versions.set(fileName, (versions.get(fileName) ?? 0) + 1);
|
|
361
|
+
projectVersion++;
|
|
362
|
+
}
|
|
233
363
|
}
|
|
234
364
|
}
|
|
235
365
|
if (newSnapshot) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
366
|
+
const newText = newSnapshot.getText(0, newSnapshot.getLength());
|
|
367
|
+
const oldText = ts.sys.readFile(fileName);
|
|
368
|
+
if (newText !== oldText) {
|
|
369
|
+
ts.sys.writeFile(fileName, newSnapshot.getText(0, newSnapshot.getLength()));
|
|
370
|
+
fileCache[0] = fs.statSync(fileName).mtimeMs;
|
|
371
|
+
fileCache[1] = {};
|
|
372
|
+
fileCache[2] = {};
|
|
373
|
+
fileCache[3] = false;
|
|
374
|
+
shouldCheck = true;
|
|
375
|
+
}
|
|
240
376
|
}
|
|
241
|
-
if (
|
|
377
|
+
if (shouldCheck) {
|
|
242
378
|
diagnostics = linter.lint(fileName, fileCache);
|
|
243
379
|
}
|
|
244
380
|
if (language) {
|
|
@@ -278,45 +414,6 @@ function lintAndFix(fileName, fileCache) {
|
|
|
278
414
|
}
|
|
279
415
|
return [diagnostics, fileCache];
|
|
280
416
|
}
|
|
281
|
-
function lint(fileName, fileCache) {
|
|
282
|
-
let diagnostics = linter.lint(fileName, fileCache);
|
|
283
|
-
if (language) {
|
|
284
|
-
diagnostics = diagnostics
|
|
285
|
-
.map(d => (0, transform_1.transformDiagnostic)(language, d, originalService.getCurrentProgram(), false))
|
|
286
|
-
.filter(d => !!d);
|
|
287
|
-
diagnostics = diagnostics.map(diagnostic => ({
|
|
288
|
-
...diagnostic,
|
|
289
|
-
file: {
|
|
290
|
-
fileName: diagnostic.file.fileName,
|
|
291
|
-
text: getFileText(diagnostic.file.fileName),
|
|
292
|
-
},
|
|
293
|
-
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
294
|
-
...info,
|
|
295
|
-
file: info.file ? {
|
|
296
|
-
fileName: info.file.fileName,
|
|
297
|
-
text: getFileText(info.file.fileName),
|
|
298
|
-
} : undefined,
|
|
299
|
-
})),
|
|
300
|
-
}));
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
diagnostics = diagnostics.map(diagnostic => ({
|
|
304
|
-
...diagnostic,
|
|
305
|
-
file: {
|
|
306
|
-
fileName: diagnostic.file.fileName,
|
|
307
|
-
text: diagnostic.file.text,
|
|
308
|
-
},
|
|
309
|
-
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
310
|
-
...info,
|
|
311
|
-
file: info.file ? {
|
|
312
|
-
fileName: info.file.fileName,
|
|
313
|
-
text: info.file.text,
|
|
314
|
-
} : undefined,
|
|
315
|
-
})),
|
|
316
|
-
}));
|
|
317
|
-
}
|
|
318
|
-
return [diagnostics, fileCache];
|
|
319
|
-
}
|
|
320
417
|
function getFileText(fileName) {
|
|
321
418
|
return originalHost.getScriptSnapshot(fileName).getText(0, Number.MAX_VALUE);
|
|
322
419
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsslint/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tsslint": "./bin/tsslint.js"
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@clack/prompts": "^0.8.2",
|
|
19
|
-
"@tsslint/config": "1.
|
|
20
|
-
"@tsslint/core": "1.
|
|
19
|
+
"@tsslint/config": "1.5.0",
|
|
20
|
+
"@tsslint/core": "1.5.0",
|
|
21
21
|
"@volar/language-core": "~2.4.0",
|
|
22
22
|
"@volar/typescript": "~2.4.0",
|
|
23
|
-
"glob": "^10.4.1"
|
|
23
|
+
"glob": "^10.4.1",
|
|
24
|
+
"json5": "^2.2.3"
|
|
24
25
|
},
|
|
25
26
|
"peerDependencies": {
|
|
26
27
|
"typescript": "*"
|
|
@@ -28,5 +29,5 @@
|
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@vue/language-core": "latest"
|
|
30
31
|
},
|
|
31
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "6585740b4ecbc8251163923b4e8c7978fe9cb12c"
|
|
32
33
|
}
|