@tsslint/cli 1.4.0 → 1.4.2
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 +124 -19
- 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;
|
|
@@ -40,11 +46,28 @@ const languageServiceHost = {
|
|
|
40
46
|
}
|
|
41
47
|
return snapshots.get(fileName);
|
|
42
48
|
},
|
|
49
|
+
getScriptKind(fileName) {
|
|
50
|
+
const languageId = (0, typescript_1.resolveFileLanguageId)(fileName);
|
|
51
|
+
switch (languageId) {
|
|
52
|
+
case 'javascript':
|
|
53
|
+
return ts.ScriptKind.JS;
|
|
54
|
+
case 'javascriptreact':
|
|
55
|
+
return ts.ScriptKind.JSX;
|
|
56
|
+
case 'typescript':
|
|
57
|
+
return ts.ScriptKind.TS;
|
|
58
|
+
case 'typescriptreact':
|
|
59
|
+
return ts.ScriptKind.TSX;
|
|
60
|
+
case 'json':
|
|
61
|
+
return ts.ScriptKind.JSON;
|
|
62
|
+
}
|
|
63
|
+
return ts.ScriptKind.Unknown;
|
|
64
|
+
},
|
|
43
65
|
getDefaultLibFileName(options) {
|
|
44
66
|
return ts.getDefaultLibFilePath(options);
|
|
45
67
|
},
|
|
46
68
|
};
|
|
47
|
-
const
|
|
69
|
+
const linterHost = { ...originalHost };
|
|
70
|
+
const originalService = ts.createLanguageService(linterHost);
|
|
48
71
|
function createLocal() {
|
|
49
72
|
return {
|
|
50
73
|
setup(...args) {
|
|
@@ -59,7 +82,7 @@ function createLocal() {
|
|
|
59
82
|
hasCodeFixes(...args) {
|
|
60
83
|
return hasCodeFixes(...args);
|
|
61
84
|
},
|
|
62
|
-
|
|
85
|
+
hasRules(...args) {
|
|
63
86
|
return hasRules(...args)[0];
|
|
64
87
|
},
|
|
65
88
|
};
|
|
@@ -110,7 +133,7 @@ const handlers = {
|
|
|
110
133
|
hasCodeFixes,
|
|
111
134
|
hasRules,
|
|
112
135
|
};
|
|
113
|
-
async function setup(tsconfig, configFile, builtConfig, _fileNames, _options) {
|
|
136
|
+
async function setup(tsconfig, languages, configFile, builtConfig, _fileNames, _options) {
|
|
114
137
|
const clack = await import('@clack/prompts');
|
|
115
138
|
let config;
|
|
116
139
|
try {
|
|
@@ -125,21 +148,55 @@ async function setup(tsconfig, configFile, builtConfig, _fileNames, _options) {
|
|
|
125
148
|
}
|
|
126
149
|
return false;
|
|
127
150
|
}
|
|
151
|
+
for (let key in linterHost) {
|
|
152
|
+
if (!(key in originalHost)) {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
delete linterHost[key];
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
// @ts-ignore
|
|
158
|
+
linterHost[key] = originalHost[key];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
linterLanguageService = originalService;
|
|
162
|
+
language = undefined;
|
|
163
|
+
const plugins = await languagePlugins.load(tsconfig, languages);
|
|
164
|
+
if (plugins.length) {
|
|
165
|
+
const { getScriptSnapshot } = originalHost;
|
|
166
|
+
language = (0, language_core_1.createLanguage)([
|
|
167
|
+
...plugins,
|
|
168
|
+
{ getLanguageId: fileName => (0, typescript_1.resolveFileLanguageId)(fileName) },
|
|
169
|
+
], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), fileName => {
|
|
170
|
+
const snapshot = getScriptSnapshot(fileName);
|
|
171
|
+
if (snapshot) {
|
|
172
|
+
language.scripts.set(fileName, snapshot);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
(0, typescript_1.decorateLanguageServiceHost)(ts, language, linterHost);
|
|
176
|
+
const proxy = (0, typescript_1.createProxyLanguageService)(linterLanguageService);
|
|
177
|
+
proxy.initialize(language);
|
|
178
|
+
linterLanguageService = proxy.proxy;
|
|
179
|
+
}
|
|
128
180
|
projectVersion++;
|
|
129
181
|
typeRootsVersion++;
|
|
130
182
|
fileNames = _fileNames;
|
|
131
|
-
options =
|
|
183
|
+
options = plugins.some(plugin => plugin.typescript?.extraFileExtensions.length)
|
|
184
|
+
? {
|
|
185
|
+
..._options,
|
|
186
|
+
allowNonTsExtensions: true,
|
|
187
|
+
}
|
|
188
|
+
: _options;
|
|
132
189
|
linter = core.createLinter({
|
|
133
190
|
configFile,
|
|
134
|
-
languageService,
|
|
135
|
-
languageServiceHost,
|
|
191
|
+
languageService: linterLanguageService,
|
|
192
|
+
languageServiceHost: linterHost,
|
|
136
193
|
typescript: ts,
|
|
137
194
|
tsconfig: ts.server.toNormalizedPath(tsconfig),
|
|
138
195
|
}, config, 'cli', clack);
|
|
139
196
|
return true;
|
|
140
197
|
}
|
|
141
198
|
function lintAndFix(fileName, fileCache) {
|
|
142
|
-
let retry =
|
|
199
|
+
let retry = 1;
|
|
143
200
|
let shouldRetry = true;
|
|
144
201
|
let newSnapshot;
|
|
145
202
|
let diagnostics;
|
|
@@ -151,9 +208,15 @@ function lintAndFix(fileName, fileCache) {
|
|
|
151
208
|
fileCache[3].length = 0;
|
|
152
209
|
}
|
|
153
210
|
diagnostics = linter.lint(fileName, fileCache);
|
|
154
|
-
|
|
211
|
+
let fixes = linter
|
|
155
212
|
.getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics, fileCache[4])
|
|
156
213
|
.filter(fix => fix.fixId === 'tsslint');
|
|
214
|
+
if (language) {
|
|
215
|
+
fixes = fixes.map(fix => {
|
|
216
|
+
fix.changes = (0, transform_1.transformFileTextChanges)(language, fix.changes, false, language_core_1.isCodeActionsEnabled);
|
|
217
|
+
return fix;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
157
220
|
const textChanges = core.combineCodeFixes(fileName, fixes);
|
|
158
221
|
if (textChanges.length) {
|
|
159
222
|
const oldSnapshot = snapshots.get(fileName);
|
|
@@ -174,8 +237,27 @@ function lintAndFix(fileName, fileCache) {
|
|
|
174
237
|
if (shouldRetry) {
|
|
175
238
|
diagnostics = linter.lint(fileName, fileCache);
|
|
176
239
|
}
|
|
177
|
-
|
|
178
|
-
diagnostics
|
|
240
|
+
if (language) {
|
|
241
|
+
diagnostics = diagnostics
|
|
242
|
+
.map(d => (0, transform_1.transformDiagnostic)(language, d, originalService.getCurrentProgram(), false))
|
|
243
|
+
.filter(d => !!d);
|
|
244
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
245
|
+
...diagnostic,
|
|
246
|
+
file: {
|
|
247
|
+
fileName: diagnostic.file.fileName,
|
|
248
|
+
text: getFileText(diagnostic.file.fileName),
|
|
249
|
+
},
|
|
250
|
+
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
251
|
+
...info,
|
|
252
|
+
file: info.file ? {
|
|
253
|
+
fileName: info.file.fileName,
|
|
254
|
+
text: getFileText(info.file.fileName),
|
|
255
|
+
} : undefined,
|
|
256
|
+
})),
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
179
261
|
...diagnostic,
|
|
180
262
|
file: {
|
|
181
263
|
fileName: diagnostic.file.fileName,
|
|
@@ -188,13 +270,33 @@ function lintAndFix(fileName, fileCache) {
|
|
|
188
270
|
text: info.file.text,
|
|
189
271
|
} : undefined,
|
|
190
272
|
})),
|
|
191
|
-
}))
|
|
192
|
-
|
|
193
|
-
];
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
return [diagnostics, fileCache];
|
|
194
276
|
}
|
|
195
277
|
function lint(fileName, fileCache) {
|
|
196
|
-
|
|
197
|
-
|
|
278
|
+
let diagnostics = linter.lint(fileName, fileCache);
|
|
279
|
+
if (language) {
|
|
280
|
+
diagnostics = diagnostics
|
|
281
|
+
.map(d => (0, transform_1.transformDiagnostic)(language, d, originalService.getCurrentProgram(), false))
|
|
282
|
+
.filter(d => !!d);
|
|
283
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
284
|
+
...diagnostic,
|
|
285
|
+
file: {
|
|
286
|
+
fileName: diagnostic.file.fileName,
|
|
287
|
+
text: getFileText(diagnostic.file.fileName),
|
|
288
|
+
},
|
|
289
|
+
relatedInformation: diagnostic.relatedInformation?.map(info => ({
|
|
290
|
+
...info,
|
|
291
|
+
file: info.file ? {
|
|
292
|
+
fileName: info.file.fileName,
|
|
293
|
+
text: getFileText(info.file.fileName),
|
|
294
|
+
} : undefined,
|
|
295
|
+
})),
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
diagnostics = diagnostics.map(diagnostic => ({
|
|
198
300
|
...diagnostic,
|
|
199
301
|
file: {
|
|
200
302
|
fileName: diagnostic.file.fileName,
|
|
@@ -207,9 +309,12 @@ function lint(fileName, fileCache) {
|
|
|
207
309
|
text: info.file.text,
|
|
208
310
|
} : undefined,
|
|
209
311
|
})),
|
|
210
|
-
}))
|
|
211
|
-
|
|
212
|
-
];
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
return [diagnostics, fileCache];
|
|
315
|
+
}
|
|
316
|
+
function getFileText(fileName) {
|
|
317
|
+
return originalHost.getScriptSnapshot(fileName).getText(0, Number.MAX_VALUE);
|
|
213
318
|
}
|
|
214
319
|
function hasCodeFixes(fileName) {
|
|
215
320
|
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.2",
|
|
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.2",
|
|
20
|
+
"@tsslint/core": "1.4.2",
|
|
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": "4cdc9070fda83e226ab04fa90b99c2b8d748b315"
|
|
27
32
|
}
|