@tsslint/cli 1.4.0 → 1.4.1
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 +193 -86
- package/lib/languagePlugins.d.ts +2 -0
- package/lib/languagePlugins.js +78 -0
- package/lib/worker.d.ts +4 -4
- package/lib/worker.js +102 -18
- package/package.json +9 -4
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const worker = require("./lib/worker.js");
|
|
|
8
8
|
const glob = require("glob");
|
|
9
9
|
const fs = require("fs");
|
|
10
10
|
const os = require("os");
|
|
11
|
+
const languagePlugins = require("./lib/languagePlugins.js");
|
|
11
12
|
const _reset = '\x1b[0m';
|
|
12
13
|
const purple = (s) => '\x1b[35m' + s + _reset;
|
|
13
14
|
const darkGray = (s) => '\x1b[90m' + s + _reset;
|
|
@@ -24,39 +25,45 @@ if (process.argv.includes('--threads')) {
|
|
|
24
25
|
}
|
|
25
26
|
threads = Math.min(os.availableParallelism(), Number(threadsArg));
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
console.error(lightRed(`No such file: ${tsconfigOption}`));
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
this.configFile = ts.findConfigFile(path.dirname(this.tsconfig), ts.sys.fileExists, 'tsslint.config.ts');
|
|
43
|
-
if (!this.configFile) {
|
|
44
|
-
log(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray('(No tsslint.config.ts found)')}`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const commonLine = parseCommonLine(this.tsconfig);
|
|
48
|
-
this.fileNames = commonLine.fileNames;
|
|
49
|
-
this.options = commonLine.options;
|
|
50
|
-
if (!this.fileNames.length) {
|
|
51
|
-
log(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray('(No included files)')}`);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
log(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray(`(${this.fileNames.length})`)}`);
|
|
55
|
-
if (!process.argv.includes('--force')) {
|
|
56
|
-
this.cache = cache.loadCache(this.tsconfig, this.configFile, ts.sys.createHash);
|
|
57
|
-
}
|
|
28
|
+
class Project {
|
|
29
|
+
constructor(tsconfigOption, languages) {
|
|
30
|
+
this.languages = languages;
|
|
31
|
+
this.workers = [];
|
|
32
|
+
this.fileNames = [];
|
|
33
|
+
this.options = {};
|
|
34
|
+
this.currentFileIndex = 0;
|
|
35
|
+
this.cache = {};
|
|
36
|
+
try {
|
|
37
|
+
this.tsconfig = require.resolve(tsconfigOption, { paths: [process.cwd()] });
|
|
58
38
|
}
|
|
39
|
+
catch {
|
|
40
|
+
console.error(lightRed(`No such file: ${tsconfigOption}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async init(
|
|
45
|
+
// @ts-expect-error
|
|
46
|
+
clack) {
|
|
47
|
+
this.configFile = ts.findConfigFile(path.dirname(this.tsconfig), ts.sys.fileExists, 'tsslint.config.ts');
|
|
48
|
+
if (!this.configFile) {
|
|
49
|
+
clack.log.error(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray('(No tsslint.config.ts found)')}`);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
const commonLine = await parseCommonLine(this.tsconfig, this.languages);
|
|
53
|
+
this.fileNames = commonLine.fileNames;
|
|
54
|
+
this.options = commonLine.options;
|
|
55
|
+
if (!this.fileNames.length) {
|
|
56
|
+
clack.log.warn(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray('(No included files)')}`);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
clack.log.info(`${purple('[project]')} ${path.relative(process.cwd(), this.tsconfig)} ${darkGray(`(${this.fileNames.length})`)}`);
|
|
60
|
+
if (!process.argv.includes('--force')) {
|
|
61
|
+
this.cache = cache.loadCache(this.tsconfig, this.configFile, ts.sys.createHash);
|
|
62
|
+
}
|
|
63
|
+
return this;
|
|
59
64
|
}
|
|
65
|
+
}
|
|
66
|
+
(async () => {
|
|
60
67
|
const builtConfigs = new Map();
|
|
61
68
|
const clack = await import('@clack/prompts');
|
|
62
69
|
const processFiles = new Set();
|
|
@@ -71,45 +78,164 @@ if (process.argv.includes('--threads')) {
|
|
|
71
78
|
let errors = 0;
|
|
72
79
|
let warnings = 0;
|
|
73
80
|
let cached = 0;
|
|
74
|
-
|
|
75
|
-
if (process.argv.includes('--project')
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
const tsconfigAndLanguages = new Map();
|
|
82
|
+
if (!process.argv.includes('--project')
|
|
83
|
+
&& !process.argv.includes('--projects')
|
|
84
|
+
&& !process.argv.includes('--vue-project')
|
|
85
|
+
&& !process.argv.includes('--vue-projects')
|
|
86
|
+
&& !process.argv.includes('--mdx-project')
|
|
87
|
+
&& !process.argv.includes('--mdx-projects')
|
|
88
|
+
&& !process.argv.includes('--astro-project')
|
|
89
|
+
&& !process.argv.includes('--astro-projects')) {
|
|
90
|
+
const languages = await clack.multiselect({
|
|
91
|
+
required: false,
|
|
92
|
+
message: 'Select frameworks (optional)',
|
|
93
|
+
options: [{
|
|
94
|
+
label: 'Vue',
|
|
95
|
+
value: 'vue',
|
|
96
|
+
}, {
|
|
97
|
+
label: 'MDX',
|
|
98
|
+
value: 'mdx',
|
|
99
|
+
}, {
|
|
100
|
+
label: 'Astro',
|
|
101
|
+
value: 'astro',
|
|
102
|
+
}],
|
|
103
|
+
});
|
|
104
|
+
if (clack.isCancel(languages)) {
|
|
80
105
|
process.exit(1);
|
|
81
106
|
}
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
const tsconfigOptions = glob.sync('**/{tsconfig.json,jsconfig.json}');
|
|
108
|
+
let options = await Promise.all(tsconfigOptions.map(async (tsconfigOption) => {
|
|
109
|
+
const tsconfig = require.resolve(tsconfigOption.startsWith('.') ? tsconfigOption : `./${tsconfigOption}`, { paths: [process.cwd()] });
|
|
110
|
+
try {
|
|
111
|
+
const commonLine = await parseCommonLine(tsconfig, languages);
|
|
112
|
+
return {
|
|
113
|
+
label: path.relative(process.cwd(), tsconfig) + ` (${commonLine.fileNames.length})`,
|
|
114
|
+
value: tsconfigOption,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
}));
|
|
121
|
+
options = options.filter(option => !!option);
|
|
122
|
+
if (!options.length) {
|
|
123
|
+
clack.log.error(lightRed('No projects found.'));
|
|
124
|
+
process.exit(1);
|
|
84
125
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
const selectedTsconfigs = await clack.multiselect({
|
|
127
|
+
message: 'Select one or multiple projects',
|
|
128
|
+
// @ts-expect-error
|
|
129
|
+
options,
|
|
130
|
+
});
|
|
131
|
+
if (clack.isCancel(selectedTsconfigs)) {
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
let command = 'tsslint';
|
|
135
|
+
if (!languages.length) {
|
|
136
|
+
if (selectedTsconfigs.length === 1) {
|
|
137
|
+
command += ' --project ' + selectedTsconfigs[0];
|
|
93
138
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
139
|
+
else {
|
|
140
|
+
command += ' --projects ' + selectedTsconfigs.join(' ');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
for (const language of languages) {
|
|
145
|
+
if (selectedTsconfigs.length === 1) {
|
|
146
|
+
command += ` --${language}-project ` + selectedTsconfigs[0];
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
command += ` --${language}-projects ` + selectedTsconfigs.join(' ');
|
|
100
150
|
}
|
|
101
|
-
projects.push(new Project(tsconfig));
|
|
102
151
|
}
|
|
103
152
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
153
|
+
clack.log.info(`Running: ${purple(command)}`);
|
|
154
|
+
for (let tsconfig of selectedTsconfigs) {
|
|
155
|
+
if (!tsconfig.startsWith('.')) {
|
|
156
|
+
tsconfig = `./${tsconfig}`;
|
|
157
|
+
}
|
|
158
|
+
tsconfigAndLanguages.set(tsconfig, languages);
|
|
107
159
|
}
|
|
108
160
|
}
|
|
109
161
|
else {
|
|
110
|
-
const
|
|
111
|
-
|
|
162
|
+
const options = [
|
|
163
|
+
{
|
|
164
|
+
projectFlag: '--project',
|
|
165
|
+
projectsFlag: '--projects',
|
|
166
|
+
language: undefined,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
projectFlag: '--vue-project',
|
|
170
|
+
projectsFlag: '--vue-projects',
|
|
171
|
+
language: 'vue',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
projectFlag: '--mdx-project',
|
|
175
|
+
projectsFlag: '--mdx-projects',
|
|
176
|
+
language: 'mdx',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
projectFlag: '--astro-project',
|
|
180
|
+
projectsFlag: '--astro-projects',
|
|
181
|
+
language: 'astro',
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
for (const { projectFlag, projectsFlag, language } of options) {
|
|
185
|
+
if (process.argv.includes(projectFlag)) {
|
|
186
|
+
const projectIndex = process.argv.indexOf(projectFlag);
|
|
187
|
+
let tsconfig = process.argv[projectIndex + 1];
|
|
188
|
+
if (!tsconfig || tsconfig.startsWith('-')) {
|
|
189
|
+
clack.log.error(lightRed(`Missing argument for ${projectFlag}.`));
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
if (!tsconfig.startsWith('.')) {
|
|
193
|
+
tsconfig = `./${tsconfig}`;
|
|
194
|
+
}
|
|
195
|
+
if (!tsconfigAndLanguages.has(tsconfig)) {
|
|
196
|
+
tsconfigAndLanguages.set(tsconfig, []);
|
|
197
|
+
}
|
|
198
|
+
if (language) {
|
|
199
|
+
tsconfigAndLanguages.get(tsconfig).push(language);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (process.argv.includes(projectsFlag)) {
|
|
203
|
+
const projectsIndex = process.argv.indexOf(projectsFlag);
|
|
204
|
+
let foundArg = false;
|
|
205
|
+
for (let i = projectsIndex + 1; i < process.argv.length; i++) {
|
|
206
|
+
if (process.argv[i].startsWith('-')) {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
foundArg = true;
|
|
210
|
+
const searchGlob = process.argv[i];
|
|
211
|
+
const tsconfigs = glob.sync(searchGlob);
|
|
212
|
+
if (!tsconfigs.length) {
|
|
213
|
+
clack.log.error(lightRed(`No projects found for ${projectsFlag} ${searchGlob}.`));
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
for (let tsconfig of tsconfigs) {
|
|
217
|
+
if (!tsconfig.startsWith('.')) {
|
|
218
|
+
tsconfig = `./${tsconfig}`;
|
|
219
|
+
}
|
|
220
|
+
if (!tsconfigAndLanguages.has(tsconfig)) {
|
|
221
|
+
tsconfigAndLanguages.set(tsconfig, []);
|
|
222
|
+
}
|
|
223
|
+
if (language) {
|
|
224
|
+
tsconfigAndLanguages.get(tsconfig).push(language);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (!foundArg) {
|
|
229
|
+
clack.log.error(lightRed(`Missing argument for ${projectsFlag}.`));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
112
234
|
}
|
|
235
|
+
for (const [tsconfig, languages] of tsconfigAndLanguages) {
|
|
236
|
+
projects.push(await new Project(tsconfig, languages).init(clack));
|
|
237
|
+
}
|
|
238
|
+
spinner.start();
|
|
113
239
|
projects = projects.filter(project => !!project.configFile);
|
|
114
240
|
projects = projects.filter(project => !!project.fileNames.length);
|
|
115
241
|
for (const project of projects) {
|
|
@@ -168,7 +294,7 @@ if (process.argv.includes('--threads')) {
|
|
|
168
294
|
})[0];
|
|
169
295
|
}
|
|
170
296
|
project.workers.push(linterWorker);
|
|
171
|
-
const setupSuccess = await linterWorker.setup(project.tsconfig, 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);
|
|
172
298
|
if (!setupSuccess) {
|
|
173
299
|
projects = projects.filter(p => p !== project);
|
|
174
300
|
startWorker(linterWorker);
|
|
@@ -207,7 +333,7 @@ if (process.argv.includes('--threads')) {
|
|
|
207
333
|
diagnostics = await linterWorker.lint(fileName, fileCache);
|
|
208
334
|
}
|
|
209
335
|
if (diagnostics.length) {
|
|
210
|
-
hasFix ||= await linterWorker.hasCodeFixes(fileName);
|
|
336
|
+
hasFix ||= Object.values(fileCache[1]).some(fixes => fixes > 0) || await linterWorker.hasCodeFixes(fileName);
|
|
211
337
|
for (const diagnostic of diagnostics) {
|
|
212
338
|
if (diagnostic.category === ts.DiagnosticCategory.Suggestion) {
|
|
213
339
|
continue;
|
|
@@ -249,31 +375,6 @@ if (process.argv.includes('--threads')) {
|
|
|
249
375
|
}
|
|
250
376
|
return await builtConfigs.get(configFile);
|
|
251
377
|
}
|
|
252
|
-
async function askTSConfig() {
|
|
253
|
-
const presetConfig = ts.findConfigFile(process.cwd(), ts.sys.fileExists);
|
|
254
|
-
let shortTsconfig = presetConfig ? path.relative(process.cwd(), presetConfig) : undefined;
|
|
255
|
-
if (!shortTsconfig?.startsWith('.')) {
|
|
256
|
-
shortTsconfig = `./${shortTsconfig}`;
|
|
257
|
-
}
|
|
258
|
-
return await clack.text({
|
|
259
|
-
message: 'Select the project. (Use --project or --projects to skip this prompt.)',
|
|
260
|
-
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.',
|
|
261
|
-
defaultValue: shortTsconfig,
|
|
262
|
-
validate(value) {
|
|
263
|
-
value ||= shortTsconfig;
|
|
264
|
-
try {
|
|
265
|
-
require.resolve(value, { paths: [process.cwd()] });
|
|
266
|
-
}
|
|
267
|
-
catch {
|
|
268
|
-
return 'No such file.';
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
function parseCommonLine(tsconfig) {
|
|
274
|
-
const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile);
|
|
275
|
-
return ts.parseJsonSourceFileConfigFileContent(jsonConfigFile, ts.sys, path.dirname(tsconfig), {}, tsconfig);
|
|
276
|
-
}
|
|
277
378
|
function addProcessFile(fileName) {
|
|
278
379
|
processFiles.add(fileName);
|
|
279
380
|
updateSpinner();
|
|
@@ -297,4 +398,10 @@ if (process.argv.includes('--threads')) {
|
|
|
297
398
|
spinner.start();
|
|
298
399
|
}
|
|
299
400
|
})();
|
|
401
|
+
async function parseCommonLine(tsconfig, languages) {
|
|
402
|
+
const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile);
|
|
403
|
+
const plugins = await languagePlugins.load(tsconfig, languages);
|
|
404
|
+
const extraFileExtensions = plugins.flatMap(plugin => plugin.typescript?.extraFileExtensions ?? []).flat();
|
|
405
|
+
return ts.parseJsonSourceFileConfigFileContent(jsonConfigFile, ts.sys, path.dirname(tsconfig), {}, tsconfig, undefined, extraFileExtensions);
|
|
406
|
+
}
|
|
300
407
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.load = load;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const ts = require("typescript");
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
async function load(tsconfig, languages) {
|
|
8
|
+
if (cache.has(tsconfig)) {
|
|
9
|
+
return cache.get(tsconfig);
|
|
10
|
+
}
|
|
11
|
+
const plugins = [];
|
|
12
|
+
if (languages.includes('vue')) {
|
|
13
|
+
let vue;
|
|
14
|
+
let vueTscPkgPath;
|
|
15
|
+
if (findPackageJson('@vue/language-core')) {
|
|
16
|
+
vue = require('@vue/language-core');
|
|
17
|
+
}
|
|
18
|
+
else if (vueTscPkgPath = findPackageJson('vue-tsc')) {
|
|
19
|
+
const vueTscPath = path.dirname(vueTscPkgPath);
|
|
20
|
+
vue = require(require.resolve('@vue/language-core', { paths: [vueTscPath] }));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const pkg = ts.findConfigFile(path.dirname(tsconfig), ts.sys.fileExists, 'package.json');
|
|
24
|
+
if (pkg) {
|
|
25
|
+
throw new Error('Please install @vue/language-core or vue-tsc to ' + path.relative(process.cwd(), pkg));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error('Please install @vue/language-core or vue-tsc for ' + path.relative(process.cwd(), tsconfig));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const commonLine = vue.createParsedCommandLine(ts, ts.sys, tsconfig);
|
|
32
|
+
const vueLanguagePlugin = vue.createVueLanguagePlugin(ts, commonLine.options, commonLine.vueOptions, fileName => fileName);
|
|
33
|
+
plugins.push(vueLanguagePlugin);
|
|
34
|
+
}
|
|
35
|
+
if (languages.includes('mdx')) {
|
|
36
|
+
let mdx;
|
|
37
|
+
try {
|
|
38
|
+
mdx = await import(require.resolve('@mdx-js/language-service', { paths: [path.dirname(tsconfig)] }));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
const pkg = ts.findConfigFile(path.dirname(tsconfig), ts.sys.fileExists, 'package.json');
|
|
42
|
+
if (pkg) {
|
|
43
|
+
throw new Error('Please install @mdx-js/language-service to ' + path.relative(process.cwd(), pkg));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
throw new Error('Please install @mdx-js/language-service for ' + path.relative(process.cwd(), tsconfig));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const mdxLanguagePlugin = mdx.createMdxLanguagePlugin();
|
|
50
|
+
plugins.push(mdxLanguagePlugin);
|
|
51
|
+
}
|
|
52
|
+
if (languages.includes('astro')) {
|
|
53
|
+
let astro;
|
|
54
|
+
try {
|
|
55
|
+
astro = require(require.resolve('@astrojs/ts-plugin/dist/language.js', { paths: [path.dirname(tsconfig)] }));
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const pkg = ts.findConfigFile(path.dirname(tsconfig), ts.sys.fileExists, 'package.json');
|
|
59
|
+
if (pkg) {
|
|
60
|
+
throw new Error('Please install @astrojs/ts-plugin to ' + path.relative(process.cwd(), pkg));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
throw new Error('Please install @astrojs/ts-plugin for ' + path.relative(process.cwd(), tsconfig));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const astroLanguagePlugin = astro.getLanguagePlugin();
|
|
67
|
+
plugins.push(astroLanguagePlugin);
|
|
68
|
+
}
|
|
69
|
+
cache.set(tsconfig, plugins);
|
|
70
|
+
return plugins;
|
|
71
|
+
function findPackageJson(pkgName) {
|
|
72
|
+
try {
|
|
73
|
+
return require.resolve(`${pkgName}/package.json`, { paths: [path.dirname(tsconfig)] });
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=languagePlugins.js.map
|
package/lib/worker.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import ts = require('typescript');
|
|
2
2
|
import core = require('@tsslint/core');
|
|
3
3
|
export declare function createLocal(): {
|
|
4
|
-
setup(tsconfig: string, configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
4
|
+
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
5
5
|
lint(fileName: string, fileCache: core.FileLintCache): ts.DiagnosticWithLocation[];
|
|
6
6
|
lintAndFix(fileName: string, fileCache: core.FileLintCache): ts.DiagnosticWithLocation[];
|
|
7
7
|
hasCodeFixes(fileName: string): boolean;
|
|
8
|
-
hasRules(fileName: string, minimatchCache: Record<string, boolean>):
|
|
8
|
+
hasRules(fileName: string, minimatchCache: Record<string, boolean>): boolean;
|
|
9
9
|
};
|
|
10
10
|
export declare function create(): {
|
|
11
|
-
setup(tsconfig: string, configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
11
|
+
setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
12
12
|
lint(fileName: string, fileCache: core.FileLintCache): Promise<ts.DiagnosticWithLocation[]>;
|
|
13
13
|
lintAndFix(fileName: string, fileCache: core.FileLintCache): Promise<ts.DiagnosticWithLocation[]>;
|
|
14
14
|
hasCodeFixes(fileName: string): Promise<boolean>;
|
|
15
15
|
hasRules(fileName: string, minimatchCache: Record<string, boolean>): Promise<boolean>;
|
|
16
16
|
};
|
|
17
|
-
declare function setup(tsconfig: string, configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
17
|
+
declare function setup(tsconfig: string, languages: string[], configFile: string, builtConfig: string, _fileNames: string[], _options: ts.CompilerOptions): Promise<boolean>;
|
|
18
18
|
declare function lintAndFix(fileName: string, fileCache: core.FileLintCache): readonly [ts.DiagnosticWithLocation[], core.FileLintCache];
|
|
19
19
|
declare function lint(fileName: string, fileCache: core.FileLintCache): readonly [ts.DiagnosticWithLocation[], core.FileLintCache];
|
|
20
20
|
declare function hasCodeFixes(fileName: string): boolean;
|
package/lib/worker.js
CHANGED
|
@@ -7,14 +7,20 @@ const core = require("@tsslint/core");
|
|
|
7
7
|
const url = require("url");
|
|
8
8
|
const fs = require("fs");
|
|
9
9
|
const worker_threads = require("worker_threads");
|
|
10
|
+
const languagePlugins = require("./languagePlugins.js");
|
|
11
|
+
const language_core_1 = require("@volar/language-core");
|
|
12
|
+
const typescript_1 = require("@volar/typescript");
|
|
13
|
+
const transform_1 = require("@volar/typescript/lib/node/transform");
|
|
10
14
|
let projectVersion = 0;
|
|
11
15
|
let typeRootsVersion = 0;
|
|
12
16
|
let options = {};
|
|
13
17
|
let fileNames = [];
|
|
18
|
+
let language;
|
|
14
19
|
let linter;
|
|
20
|
+
let linterLanguageService;
|
|
15
21
|
const snapshots = new Map();
|
|
16
22
|
const versions = new Map();
|
|
17
|
-
const
|
|
23
|
+
const originalHost = {
|
|
18
24
|
...ts.sys,
|
|
19
25
|
useCaseSensitiveFileNames() {
|
|
20
26
|
return ts.sys.useCaseSensitiveFileNames;
|
|
@@ -44,7 +50,8 @@ const languageServiceHost = {
|
|
|
44
50
|
return ts.getDefaultLibFilePath(options);
|
|
45
51
|
},
|
|
46
52
|
};
|
|
47
|
-
const
|
|
53
|
+
const linterHost = { ...originalHost };
|
|
54
|
+
const originalService = ts.createLanguageService(linterHost);
|
|
48
55
|
function createLocal() {
|
|
49
56
|
return {
|
|
50
57
|
setup(...args) {
|
|
@@ -59,7 +66,7 @@ function createLocal() {
|
|
|
59
66
|
hasCodeFixes(...args) {
|
|
60
67
|
return hasCodeFixes(...args);
|
|
61
68
|
},
|
|
62
|
-
|
|
69
|
+
hasRules(...args) {
|
|
63
70
|
return hasRules(...args)[0];
|
|
64
71
|
},
|
|
65
72
|
};
|
|
@@ -110,7 +117,7 @@ const handlers = {
|
|
|
110
117
|
hasCodeFixes,
|
|
111
118
|
hasRules,
|
|
112
119
|
};
|
|
113
|
-
async function setup(tsconfig, configFile, builtConfig, _fileNames, _options) {
|
|
120
|
+
async function setup(tsconfig, languages, configFile, builtConfig, _fileNames, _options) {
|
|
114
121
|
const clack = await import('@clack/prompts');
|
|
115
122
|
let config;
|
|
116
123
|
try {
|
|
@@ -125,21 +132,50 @@ async function setup(tsconfig, configFile, builtConfig, _fileNames, _options) {
|
|
|
125
132
|
}
|
|
126
133
|
return false;
|
|
127
134
|
}
|
|
135
|
+
for (let key in linterHost) {
|
|
136
|
+
if (!(key in originalHost)) {
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
delete linterHost[key];
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
linterHost[key] = originalHost[key];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
linterLanguageService = originalService;
|
|
146
|
+
language = undefined;
|
|
147
|
+
const plugins = await languagePlugins.load(tsconfig, languages);
|
|
148
|
+
if (plugins.length) {
|
|
149
|
+
const { getScriptSnapshot } = originalHost;
|
|
150
|
+
language = (0, language_core_1.createLanguage)([
|
|
151
|
+
...plugins,
|
|
152
|
+
{ getLanguageId: fileName => (0, typescript_1.resolveFileLanguageId)(fileName) },
|
|
153
|
+
], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
|
|
154
|
+
const snapshot = getScriptSnapshot(fileName);
|
|
155
|
+
if (snapshot) {
|
|
156
|
+
language.scripts.set(fileName, snapshot);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
(0, typescript_1.decorateLanguageServiceHost)(ts, language, linterHost);
|
|
160
|
+
const proxy = (0, typescript_1.createProxyLanguageService)(linterLanguageService);
|
|
161
|
+
proxy.initialize(language);
|
|
162
|
+
linterLanguageService = proxy.proxy;
|
|
163
|
+
}
|
|
128
164
|
projectVersion++;
|
|
129
165
|
typeRootsVersion++;
|
|
130
166
|
fileNames = _fileNames;
|
|
131
167
|
options = _options;
|
|
132
168
|
linter = core.createLinter({
|
|
133
169
|
configFile,
|
|
134
|
-
languageService,
|
|
135
|
-
languageServiceHost,
|
|
170
|
+
languageService: linterLanguageService,
|
|
171
|
+
languageServiceHost: linterHost,
|
|
136
172
|
typescript: ts,
|
|
137
173
|
tsconfig: ts.server.toNormalizedPath(tsconfig),
|
|
138
174
|
}, config, 'cli', clack);
|
|
139
175
|
return true;
|
|
140
176
|
}
|
|
141
177
|
function lintAndFix(fileName, fileCache) {
|
|
142
|
-
let retry =
|
|
178
|
+
let retry = 1;
|
|
143
179
|
let shouldRetry = true;
|
|
144
180
|
let newSnapshot;
|
|
145
181
|
let diagnostics;
|
|
@@ -151,9 +187,15 @@ function lintAndFix(fileName, fileCache) {
|
|
|
151
187
|
fileCache[3].length = 0;
|
|
152
188
|
}
|
|
153
189
|
diagnostics = linter.lint(fileName, fileCache);
|
|
154
|
-
|
|
190
|
+
let fixes = linter
|
|
155
191
|
.getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics, fileCache[4])
|
|
156
192
|
.filter(fix => fix.fixId === 'tsslint');
|
|
193
|
+
if (language) {
|
|
194
|
+
fixes = fixes.map(fix => {
|
|
195
|
+
fix.changes = (0, transform_1.transformFileTextChanges)(language, fix.changes, false, language_core_1.isCodeActionsEnabled);
|
|
196
|
+
return fix;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
157
199
|
const textChanges = core.combineCodeFixes(fileName, fixes);
|
|
158
200
|
if (textChanges.length) {
|
|
159
201
|
const oldSnapshot = snapshots.get(fileName);
|
|
@@ -174,8 +216,27 @@ function lintAndFix(fileName, fileCache) {
|
|
|
174
216
|
if (shouldRetry) {
|
|
175
217
|
diagnostics = linter.lint(fileName, fileCache);
|
|
176
218
|
}
|
|
177
|
-
|
|
178
|
-
diagnostics
|
|
219
|
+
if (language) {
|
|
220
|
+
diagnostics = diagnostics
|
|
221
|
+
.map(d => (0, transform_1.transformDiagnostic)(language, d, originalService.getCurrentProgram(), false))
|
|
222
|
+
.filter(d => !!d);
|
|
223
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
224
|
+
...diagnostic,
|
|
225
|
+
file: {
|
|
226
|
+
fileName: diagnostic.file.fileName,
|
|
227
|
+
text: getFileText(diagnostic.file.fileName),
|
|
228
|
+
},
|
|
229
|
+
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
230
|
+
...info,
|
|
231
|
+
file: info.file ? {
|
|
232
|
+
fileName: info.file.fileName,
|
|
233
|
+
text: getFileText(info.file.fileName),
|
|
234
|
+
} : undefined,
|
|
235
|
+
})),
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
179
240
|
...diagnostic,
|
|
180
241
|
file: {
|
|
181
242
|
fileName: diagnostic.file.fileName,
|
|
@@ -188,13 +249,33 @@ function lintAndFix(fileName, fileCache) {
|
|
|
188
249
|
text: info.file.text,
|
|
189
250
|
} : undefined,
|
|
190
251
|
})),
|
|
191
|
-
}))
|
|
192
|
-
|
|
193
|
-
];
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
return [diagnostics, fileCache];
|
|
194
255
|
}
|
|
195
256
|
function lint(fileName, fileCache) {
|
|
196
|
-
|
|
197
|
-
|
|
257
|
+
let diagnostics = linter.lint(fileName, fileCache);
|
|
258
|
+
if (language) {
|
|
259
|
+
diagnostics = diagnostics
|
|
260
|
+
.map(d => (0, transform_1.transformDiagnostic)(language, d, originalService.getCurrentProgram(), false))
|
|
261
|
+
.filter(d => !!d);
|
|
262
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
263
|
+
...diagnostic,
|
|
264
|
+
file: {
|
|
265
|
+
fileName: diagnostic.file.fileName,
|
|
266
|
+
text: getFileText(diagnostic.file.fileName),
|
|
267
|
+
},
|
|
268
|
+
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
269
|
+
...info,
|
|
270
|
+
file: info.file ? {
|
|
271
|
+
fileName: info.file.fileName,
|
|
272
|
+
text: getFileText(info.file.fileName),
|
|
273
|
+
} : undefined,
|
|
274
|
+
})),
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
198
279
|
...diagnostic,
|
|
199
280
|
file: {
|
|
200
281
|
fileName: diagnostic.file.fileName,
|
|
@@ -207,9 +288,12 @@ function lint(fileName, fileCache) {
|
|
|
207
288
|
text: info.file.text,
|
|
208
289
|
} : undefined,
|
|
209
290
|
})),
|
|
210
|
-
}))
|
|
211
|
-
|
|
212
|
-
];
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
return [diagnostics, fileCache];
|
|
294
|
+
}
|
|
295
|
+
function getFileText(fileName) {
|
|
296
|
+
return originalHost.getScriptSnapshot(fileName).getText(0, Number.MAX_VALUE);
|
|
213
297
|
}
|
|
214
298
|
function hasCodeFixes(fileName) {
|
|
215
299
|
return linter.hasCodeFixes(fileName);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsslint/cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tsslint": "./bin/tsslint.js"
|
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@clack/prompts": "^0.8.2",
|
|
19
|
-
"@tsslint/config": "1.4.
|
|
20
|
-
"@tsslint/core": "1.4.
|
|
19
|
+
"@tsslint/config": "1.4.1",
|
|
20
|
+
"@tsslint/core": "1.4.1",
|
|
21
|
+
"@volar/language-core": "~2.4.0",
|
|
22
|
+
"@volar/typescript": "~2.4.0",
|
|
21
23
|
"glob": "^10.4.1"
|
|
22
24
|
},
|
|
23
25
|
"peerDependencies": {
|
|
24
26
|
"typescript": "*"
|
|
25
27
|
},
|
|
26
|
-
"
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@vue/language-core": "latest"
|
|
30
|
+
},
|
|
31
|
+
"gitHead": "54f42ec9414029a356fa19a762260f03392563fa"
|
|
27
32
|
}
|