@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 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
- (async () => {
28
- class Project {
29
- constructor(tsconfigOption) {
30
- this.workers = [];
31
- this.fileNames = [];
32
- this.options = {};
33
- this.currentFileIndex = 0;
34
- this.cache = {};
35
- try {
36
- this.tsconfig = require.resolve(tsconfigOption, { paths: [process.cwd()] });
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
- spinner.start();
75
- if (process.argv.includes('--project')) {
76
- const projectIndex = process.argv.indexOf('--project');
77
- let tsconfig = process.argv[projectIndex + 1];
78
- if (!tsconfig || tsconfig.startsWith('-')) {
79
- console.error(lightRed(`Missing argument for --project.`));
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
- if (!tsconfig.startsWith('.')) {
83
- tsconfig = `./${tsconfig}`;
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
- projects.push(new Project(tsconfig));
86
- }
87
- else if (process.argv.includes('--projects')) {
88
- const projectsIndex = process.argv.indexOf('--projects');
89
- let foundArg = false;
90
- for (let i = projectsIndex + 1; i < process.argv.length; i++) {
91
- if (process.argv[i].startsWith('-')) {
92
- break;
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
- foundArg = true;
95
- const searchGlob = process.argv[i];
96
- const tsconfigs = glob.sync(searchGlob);
97
- for (let tsconfig of tsconfigs) {
98
- if (!tsconfig.startsWith('.')) {
99
- tsconfig = `./${tsconfig}`;
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
- if (!foundArg) {
105
- console.error(lightRed(`Missing argument for --projects.`));
106
- process.exit(1);
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 tsconfig = await askTSConfig();
111
- projects.push(new Project(tsconfig));
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,2 @@
1
+ import { LanguagePlugin } from '@volar/language-core';
2
+ export declare function load(tsconfig: string, languages: string[]): Promise<LanguagePlugin<string, import("@volar/language-core").VirtualCode>[]>;
@@ -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>): Promise<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 languageServiceHost = {
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 languageService = ts.createLanguageService(languageServiceHost);
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
- async hasRules(...args) {
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 = _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 = 3;
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
- const fixes = linter
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
- return [
178
- diagnostics.map(diagnostic => ({
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
- fileCache,
193
- ];
273
+ }));
274
+ }
275
+ return [diagnostics, fileCache];
194
276
  }
195
277
  function lint(fileName, fileCache) {
196
- return [
197
- linter.lint(fileName, fileCache).map(diagnostic => ({
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
- fileCache,
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.0",
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.0",
20
- "@tsslint/core": "1.4.0",
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
- "gitHead": "9a3f7ce55f079eaaedfb61af9b72d8ba736f0123"
28
+ "devDependencies": {
29
+ "@vue/language-core": "latest"
30
+ },
31
+ "gitHead": "4cdc9070fda83e226ab04fa90b99c2b8d748b315"
27
32
  }