@tsslint/core 1.5.4 → 1.5.5

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.
Files changed (2) hide show
  1. package/index.js +493 -506
  2. package/package.json +3 -3
package/index.js CHANGED
@@ -1,25 +1,17 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
3
- if (k2 === undefined) {
4
- k2 = k;
5
- }
6
- var desc = Object.getOwnPropertyDescriptor(m, k);
7
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
- desc = { enumerable: true, get: function () { return m[k]; } };
9
- }
10
- Object.defineProperty(o, k2, desc);
11
- }) : (function (o, m, k, k2) {
12
- if (k2 === undefined) {
13
- k2 = k;
14
- }
15
- o[k2] = m[k];
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
16
12
  }));
17
- var __exportStar = (this && this.__exportStar) || function (m, exports) {
18
- for (var p in m) {
19
- if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) {
20
- __createBinding(exports, m, p);
21
- }
22
- }
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
23
15
  };
24
16
  Object.defineProperty(exports, "__esModule", { value: true });
25
17
  exports.createLinter = createLinter;
@@ -32,498 +24,493 @@ const ErrorStackParser = require("error-stack-parser");
32
24
  const path = require("path");
33
25
  const minimatch = require("minimatch");
34
26
  function createLinter(ctx, rootDir, config, mode, syntaxOnlyLanguageService) {
35
- const ts = ctx.typescript;
36
- const fileRules = new Map();
37
- const fileFmtProcesses = new Map();
38
- const fileConfigs = new Map();
39
- const lintResults = new Map();
40
- const configs = (Array.isArray(config) ? config : [config])
41
- .map(config => ({
42
- include: config.include,
43
- exclude: config.exclude,
44
- rules: config.rules ?? {},
45
- formatting: config.formatting,
46
- plugins: (config.plugins ?? []).map(plugin => plugin(ctx)),
47
- }));
48
- const normalizedPath = new Map();
49
- const rule2Mode = new Map();
50
- const getNonBoundSourceFile = syntaxOnlyLanguageService?.getNonBoundSourceFile;
51
- let shouldEnableTypeAware = false;
52
- return {
53
- format(sourceFile, minimatchCache) {
54
- const preprocess = getFileFmtProcesses(sourceFile.fileName, minimatchCache);
55
- const changes = [];
56
- const tmpChanges = [];
57
- const fmtCtx = {
58
- typescript: ts,
59
- sourceFile,
60
- insert(pos, text) {
61
- tmpChanges.push({ span: { start: pos, length: 0 }, newText: text });
62
- },
63
- remove(start, end) {
64
- tmpChanges.push({ span: { start, length: end - start }, newText: '' });
65
- },
66
- replace(start, end, text) {
67
- tmpChanges.push({ span: { start, length: end - start }, newText: text });
68
- },
69
- };
70
- for (const process of preprocess) {
71
- process(fmtCtx);
72
- if (tmpChanges.every(a => {
73
- const aStart = a.span.start;
74
- const aEnd = aStart + a.span.length;
75
- for (const b of changes) {
76
- const bStart = b.span.start;
77
- const bEnd = bStart + b.span.length;
78
- if ((bStart >= aEnd && bStart > aStart)
79
- || (bEnd <= aStart && bEnd < aEnd)) {
80
- continue;
81
- }
82
- return false;
83
- }
84
- return true;
85
- })) {
86
- changes.push(...tmpChanges);
87
- }
88
- tmpChanges.length = 0;
89
- }
90
- return changes;
91
- },
92
- lint(fileName, cache) {
93
- const configs = getFileConfigs(fileName, cache?.[2]);
94
- if (!configs.length) {
95
- lintResults.set(fileName, [rulesContext.sourceFile, new Map(), []]);
96
- return [];
97
- }
98
-
99
- let currentRuleId;
100
- let shouldRetry = false;
101
- const rules = getFileRules(fileName, cache?.[2]);
102
- const typeAwareMode = !getNonBoundSourceFile
103
- || shouldEnableTypeAware && !Object.keys(rules).some(ruleId => !rule2Mode.has(ruleId));
104
- const rulesContext = typeAwareMode
105
- ? {
106
- ...ctx,
107
- sourceFile: ctx.languageService.getProgram().getSourceFile(fileName),
108
- reportError,
109
- reportWarning,
110
- reportSuggestion,
111
- }
112
- : {
113
- ...ctx,
114
- languageService: syntaxOnlyLanguageService,
115
- sourceFile: getNonBoundSourceFile(fileName),
116
- reportError,
117
- reportWarning,
118
- reportSuggestion,
119
- };
120
- const token = ctx.languageServiceHost.getCancellationToken?.();
121
- lintResults.set(fileName, [rulesContext.sourceFile, new Map(), []]);
122
- const lintResult = lintResults.get(fileName);
123
- for (const [ruleId, rule] of Object.entries(rules)) {
124
- if (token?.isCancellationRequested()) {
125
- break;
126
- }
127
- currentRuleId = ruleId;
128
- const ruleCache = cache?.[1][currentRuleId];
129
- if (ruleCache) {
130
- let lintResult = lintResults.get(fileName);
131
- if (!lintResult) {
132
- lintResults.set(fileName, lintResult = [rulesContext.sourceFile, new Map(), []]);
133
- }
134
- for (const cacheDiagnostic of ruleCache[1]) {
135
- lintResult[1].set({
136
- ...cacheDiagnostic,
137
- file: rulesContext.sourceFile,
138
- relatedInformation: cacheDiagnostic.relatedInformation?.map(info => ({
139
- ...info,
140
- file: info.file ? syntaxOnlyLanguageService.getNonBoundSourceFile(info.file.fileName) : undefined,
141
- })),
142
- }, []);
143
- }
144
- if (!typeAwareMode) {
145
- rule2Mode.set(currentRuleId, false);
146
- }
147
- continue;
148
- }
149
- try {
150
- rule(rulesContext);
151
- if (!typeAwareMode) {
152
- rule2Mode.set(currentRuleId, false);
153
- }
154
- }
155
- catch (err) {
156
- if (!typeAwareMode) {
157
- // console.log(`Rule "${currentRuleId}" is type aware.`);
158
- rule2Mode.set(currentRuleId, true);
159
- shouldRetry = true;
160
- }
161
- else if (err instanceof Error) {
162
- report(ts.DiagnosticCategory.Error, err.stack ?? err.message, 0, 0, 0, err);
163
- }
164
- else {
165
- report(ts.DiagnosticCategory.Error, String(err), 0, 0, false);
166
- }
167
- }
168
- if (cache && !rule2Mode.get(currentRuleId)) {
169
- cache[1][currentRuleId] ??= [false, []];
170
- for (const [_, fixes] of lintResult[1]) {
171
- if (fixes.length) {
172
- cache[1][currentRuleId][0] = true;
173
- break;
174
- }
175
- }
176
- }
177
- }
178
- if (shouldRetry) {
179
- // Retry
180
- shouldEnableTypeAware = true;
181
- return this.lint(fileName, cache);
182
- }
183
- let diagnostics = [...lintResult[1].keys()];
184
- try {
185
- for (const { plugins } of configs) {
186
- for (const { resolveDiagnostics } of plugins) {
187
- if (resolveDiagnostics) {
188
- diagnostics = resolveDiagnostics(rulesContext.sourceFile, diagnostics);
189
- }
190
- }
191
- }
192
- }
193
- catch (error) {
194
- if (!typeAwareMode) {
195
- // Retry
196
- shouldEnableTypeAware = true;
197
- return this.lint(fileName, cache);
198
- }
199
- throw error;
200
- }
201
- // Remove fixes and refactors that removed by resolveDiagnostics
202
- const diagnosticSet = new Set(diagnostics);
203
- for (const diagnostic of [...lintResult[1].keys()]) {
204
- if (!diagnosticSet.has(diagnostic)) {
205
- lintResult[1].delete(diagnostic);
206
- }
207
- }
208
- lintResult[2] = lintResult[2].filter(refactor => diagnosticSet.has(refactor.diagnostic));
209
- return diagnostics;
210
- function reportError(message, start, end, stackOffset) {
211
- return report(ts.DiagnosticCategory.Error, message, start, end, stackOffset);
212
- }
213
- function reportWarning(message, start, end, stackOffset) {
214
- return report(ts.DiagnosticCategory.Warning, message, start, end, stackOffset);
215
- }
216
- function reportSuggestion(message, start, end, stackOffset) {
217
- return report(ts.DiagnosticCategory.Suggestion, message, start, end, stackOffset);
218
- }
219
- function report(category, message, start, end, stackOffset = 2, err) {
220
- const error = {
221
- category,
222
- code: currentRuleId,
223
- messageText: message,
224
- file: rulesContext.sourceFile,
225
- start,
226
- length: end - start,
227
- source: 'tsslint',
228
- relatedInformation: [],
229
- };
230
- if (cache && !rule2Mode.get(currentRuleId)) {
231
- cache[1][currentRuleId] ??= [false, []];
232
- cache[1][currentRuleId][1].push({
233
- ...error,
234
- file: undefined,
235
- relatedInformation: error.relatedInformation?.map(info => ({
236
- ...info,
237
- file: info.file ? { fileName: info.file.fileName } : undefined,
238
- })),
239
- });
240
- }
241
- if (mode === 'typescript-plugin' && typeof stackOffset === 'number') {
242
- err ??= new Error();
243
- const relatedInfo = createRelatedInformation(ts, err, stackOffset);
244
- if (relatedInfo) {
245
- error.relatedInformation.push(relatedInfo);
246
- }
247
- }
248
- let lintResult = lintResults.get(fileName);
249
- if (!lintResult) {
250
- lintResults.set(fileName, lintResult = [rulesContext.sourceFile, new Map(), []]);
251
- }
252
- const diagnostic2Fixes = lintResult[1];
253
- const refactors = lintResult[2];
254
- diagnostic2Fixes.set(error, []);
255
- const fixes = diagnostic2Fixes.get(error);
256
- return {
257
- withDeprecated() {
258
- error.reportsDeprecated = true;
259
- return this;
260
- },
261
- withUnnecessary() {
262
- error.reportsUnnecessary = true;
263
- return this;
264
- },
265
- withFix(title, getEdits) {
266
- fixes.push(({ title, getEdits }));
267
- return this;
268
- },
269
- withRefactor(title, getEdits) {
270
- refactors.push(({
271
- diagnostic: error,
272
- title,
273
- getEdits,
274
- }));
275
- return this;
276
- },
277
- };
278
- }
279
- },
280
- hasCodeFixes(fileName) {
281
- const lintResult = lintResults.get(fileName);
282
- if (!lintResult) {
283
- return false;
284
- }
285
- for (const [_, fixes] of lintResult[1]) {
286
- if (fixes.length) {
287
- return true;
288
- }
289
- }
290
- return false;
291
- },
292
- getCodeFixes(fileName, start, end, diagnostics, minimatchCache) {
293
- const lintResult = lintResults.get(fileName);
294
- if (!lintResult) {
295
- return [];
296
- }
297
- const sourceFile = lintResult[0];
298
- const configs = getFileConfigs(fileName, minimatchCache);
299
- const result = [];
300
- for (const [diagnostic, actions] of lintResult[1]) {
301
- if (diagnostics?.length && !diagnostics.includes(diagnostic)) {
302
- continue;
303
- }
304
- const diagStart = diagnostic.start;
305
- const diagEnd = diagStart + diagnostic.length;
306
- if ((diagStart >= start && diagStart <= end) ||
307
- (diagEnd >= start && diagEnd <= end) ||
308
- (start >= diagStart && start <= diagEnd) ||
309
- (end >= diagStart && end <= diagEnd)) {
310
- let codeFixes = [];
311
- for (const action of actions) {
312
- codeFixes.push({
313
- fixName: `tsslint:${diagnostic.code}`,
314
- description: action.title,
315
- changes: action.getEdits(),
316
- fixId: 'tsslint',
317
- fixAllDescription: 'Fix all TSSLint errors'
318
- });
319
- }
320
- for (const { plugins } of configs) {
321
- for (const { resolveCodeFixes } of plugins) {
322
- if (resolveCodeFixes) {
323
- codeFixes = resolveCodeFixes(sourceFile, diagnostic, codeFixes);
324
- }
325
- }
326
- }
327
- result.push(...codeFixes);
328
- }
329
- }
330
- return result;
331
- },
332
- getRefactors(fileName, start, end) {
333
- const lintResult = lintResults.get(fileName);
334
- if (!lintResult) {
335
- return [];
336
- }
337
- const result = [];
338
- for (let i = 0; i < lintResult[2].length; i++) {
339
- const refactor = lintResult[2][i];
340
- const diagStart = refactor.diagnostic.start;
341
- const diagEnd = diagStart + refactor.diagnostic.length;
342
- if ((diagStart >= start && diagStart <= end) ||
343
- (diagEnd >= start && diagEnd <= end) ||
344
- (start >= diagStart && start <= diagEnd) ||
345
- (end >= diagStart && end <= diagEnd)) {
346
- result.push({
347
- name: `tsslint:${i}`,
348
- description: refactor.title,
349
- kind: 'quickfix',
350
- });
351
- }
352
- }
353
- return result;
354
- },
355
- getRefactorEdits(fileName, actionName) {
356
- if (actionName.startsWith('tsslint:')) {
357
- const lintResult = lintResults.get(fileName);
358
- if (!lintResult) {
359
- return [];
360
- }
361
- const index = actionName.slice('tsslint:'.length);
362
- const refactor = lintResult[2][Number(index)];
363
- if (refactor) {
364
- return refactor.getEdits();
365
- }
366
- }
367
- },
368
- getRules: getFileRules,
369
- getConfigs: getFileConfigs,
370
- };
371
- function getFileFmtProcesses(fileName, minimatchCache) {
372
- if (!fileFmtProcesses.has(fileName)) {
373
- const allPreprocess = [];
374
- const configs = getFileConfigs(fileName, minimatchCache);
375
- for (const { formatting } of configs) {
376
- if (formatting) {
377
- allPreprocess.push(...formatting);
378
- }
379
- }
380
- fileFmtProcesses.set(fileName, allPreprocess);
381
- }
382
- return fileFmtProcesses.get(fileName);
383
- }
384
- function getFileRules(fileName, minimatchCache) {
385
- let rules = fileRules.get(fileName);
386
- if (!rules) {
387
- rules = {};
388
- const configs = getFileConfigs(fileName, minimatchCache);
389
- for (const config of configs) {
390
- collectRules(rules, config.rules, []);
391
- }
392
- for (const { plugins } of configs) {
393
- for (const { resolveRules } of plugins) {
394
- if (resolveRules) {
395
- rules = resolveRules(fileName, rules);
396
- }
397
- }
398
- }
399
- fileRules.set(fileName, rules);
400
- }
401
- return rules;
402
- }
403
- function collectRules(record, rules, paths) {
404
- for (const [path, rule] of Object.entries(rules)) {
405
- if (typeof rule === 'object') {
406
- collectRules(record, rule, [...paths, path]);
407
- continue;
408
- }
409
- record[[...paths, path].join('/')] = rule;
410
- }
411
- }
412
- function getFileConfigs(fileName, minimatchCache) {
413
- let result = fileConfigs.get(fileName);
414
- if (!result) {
415
- result = configs.filter(({ include, exclude }) => {
416
- if (exclude?.some(_minimatch)) {
417
- return false;
418
- }
419
- if (include && !include.some(_minimatch)) {
420
- return false;
421
- }
422
- return true;
423
- });
424
- fileConfigs.set(fileName, result);
425
- function _minimatch(pattern) {
426
- if (minimatchCache) {
427
- if (pattern in minimatchCache) {
428
- return minimatchCache[pattern];
429
- }
430
- }
431
- let normalized = normalizedPath.get(pattern);
432
- if (!normalized) {
433
- normalized = ts.server.toNormalizedPath(path.resolve(rootDir, pattern));
434
- normalizedPath.set(pattern, normalized);
435
- }
436
- const res = minimatch.minimatch(fileName, normalized);
437
- if (minimatchCache) {
438
- minimatchCache[pattern] = res;
439
- }
440
- return res;
441
- }
442
- }
443
- return result;
444
- }
27
+ const ts = ctx.typescript;
28
+ const fileRules = new Map();
29
+ const fileFmtProcesses = new Map();
30
+ const fileConfigs = new Map();
31
+ const lintResults = new Map();
32
+ const configs = (Array.isArray(config) ? config : [config])
33
+ .map(config => ({
34
+ include: config.include,
35
+ exclude: config.exclude,
36
+ rules: config.rules ?? {},
37
+ formatting: config.formatting,
38
+ plugins: (config.plugins ?? []).map(plugin => plugin(ctx)),
39
+ }));
40
+ const normalizedPath = new Map();
41
+ const rule2Mode = new Map();
42
+ const getNonBoundSourceFile = syntaxOnlyLanguageService?.getNonBoundSourceFile;
43
+ let shouldEnableTypeAware = false;
44
+ return {
45
+ format(sourceFile, minimatchCache) {
46
+ const preprocess = getFileFmtProcesses(sourceFile.fileName, minimatchCache);
47
+ const changes = [];
48
+ const tmpChanges = [];
49
+ const fmtCtx = {
50
+ typescript: ts,
51
+ sourceFile,
52
+ insert(pos, text) {
53
+ tmpChanges.push({ span: { start: pos, length: 0 }, newText: text });
54
+ },
55
+ remove(start, end) {
56
+ tmpChanges.push({ span: { start, length: end - start }, newText: '' });
57
+ },
58
+ replace(start, end, text) {
59
+ tmpChanges.push({ span: { start, length: end - start }, newText: text });
60
+ },
61
+ };
62
+ for (const process of preprocess) {
63
+ process(fmtCtx);
64
+ if (tmpChanges.every(a => {
65
+ const aStart = a.span.start;
66
+ const aEnd = aStart + a.span.length;
67
+ for (const b of changes) {
68
+ const bStart = b.span.start;
69
+ const bEnd = bStart + b.span.length;
70
+ if ((bStart >= aEnd && bStart > aStart)
71
+ || (bEnd <= aStart && bEnd < aEnd)) {
72
+ continue;
73
+ }
74
+ return false;
75
+ }
76
+ return true;
77
+ })) {
78
+ changes.push(...tmpChanges);
79
+ }
80
+ tmpChanges.length = 0;
81
+ }
82
+ return changes;
83
+ },
84
+ lint(fileName, cache) {
85
+ let currentRuleId;
86
+ let shouldRetry = false;
87
+ const rules = getFileRules(fileName, cache?.[2]);
88
+ const typeAwareMode = !getNonBoundSourceFile
89
+ || shouldEnableTypeAware && !Object.keys(rules).some(ruleId => !rule2Mode.has(ruleId));
90
+ const rulesContext = typeAwareMode
91
+ ? {
92
+ ...ctx,
93
+ sourceFile: ctx.languageService.getProgram().getSourceFile(fileName),
94
+ reportError,
95
+ reportWarning,
96
+ reportSuggestion,
97
+ }
98
+ : {
99
+ ...ctx,
100
+ languageService: syntaxOnlyLanguageService,
101
+ sourceFile: getNonBoundSourceFile(fileName),
102
+ reportError,
103
+ reportWarning,
104
+ reportSuggestion,
105
+ };
106
+ const token = ctx.languageServiceHost.getCancellationToken?.();
107
+ const configs = getFileConfigs(fileName, cache?.[2]);
108
+ lintResults.set(fileName, [rulesContext.sourceFile, new Map(), []]);
109
+ const lintResult = lintResults.get(fileName);
110
+ for (const [ruleId, rule] of Object.entries(rules)) {
111
+ if (token?.isCancellationRequested()) {
112
+ break;
113
+ }
114
+ currentRuleId = ruleId;
115
+ const ruleCache = cache?.[1][currentRuleId];
116
+ if (ruleCache) {
117
+ let lintResult = lintResults.get(fileName);
118
+ if (!lintResult) {
119
+ lintResults.set(fileName, lintResult = [rulesContext.sourceFile, new Map(), []]);
120
+ }
121
+ for (const cacheDiagnostic of ruleCache[1]) {
122
+ lintResult[1].set({
123
+ ...cacheDiagnostic,
124
+ file: rulesContext.sourceFile,
125
+ relatedInformation: cacheDiagnostic.relatedInformation?.map(info => ({
126
+ ...info,
127
+ file: info.file ? syntaxOnlyLanguageService.getNonBoundSourceFile(info.file.fileName) : undefined,
128
+ })),
129
+ }, []);
130
+ }
131
+ if (!typeAwareMode) {
132
+ rule2Mode.set(currentRuleId, false);
133
+ }
134
+ continue;
135
+ }
136
+ try {
137
+ rule(rulesContext);
138
+ if (!typeAwareMode) {
139
+ rule2Mode.set(currentRuleId, false);
140
+ }
141
+ }
142
+ catch (err) {
143
+ if (!typeAwareMode) {
144
+ // console.log(`Rule "${currentRuleId}" is type aware.`);
145
+ rule2Mode.set(currentRuleId, true);
146
+ shouldRetry = true;
147
+ }
148
+ else if (err instanceof Error) {
149
+ report(ts.DiagnosticCategory.Error, err.stack ?? err.message, 0, 0, 0, err);
150
+ }
151
+ else {
152
+ report(ts.DiagnosticCategory.Error, String(err), 0, 0, false);
153
+ }
154
+ }
155
+ if (cache && !rule2Mode.get(currentRuleId)) {
156
+ cache[1][currentRuleId] ??= [false, []];
157
+ for (const [_, fixes] of lintResult[1]) {
158
+ if (fixes.length) {
159
+ cache[1][currentRuleId][0] = true;
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ if (shouldRetry) {
166
+ // Retry
167
+ shouldEnableTypeAware = true;
168
+ return this.lint(fileName, cache);
169
+ }
170
+ let diagnostics = [...lintResult[1].keys()];
171
+ try {
172
+ for (const { plugins } of configs) {
173
+ for (const { resolveDiagnostics } of plugins) {
174
+ if (resolveDiagnostics) {
175
+ diagnostics = resolveDiagnostics(rulesContext.sourceFile, diagnostics);
176
+ }
177
+ }
178
+ }
179
+ }
180
+ catch (error) {
181
+ if (!typeAwareMode) {
182
+ // Retry
183
+ shouldEnableTypeAware = true;
184
+ return this.lint(fileName, cache);
185
+ }
186
+ throw error;
187
+ }
188
+ // Remove fixes and refactors that removed by resolveDiagnostics
189
+ const diagnosticSet = new Set(diagnostics);
190
+ for (const diagnostic of [...lintResult[1].keys()]) {
191
+ if (!diagnosticSet.has(diagnostic)) {
192
+ lintResult[1].delete(diagnostic);
193
+ }
194
+ }
195
+ lintResult[2] = lintResult[2].filter(refactor => diagnosticSet.has(refactor.diagnostic));
196
+ return diagnostics;
197
+ function reportError(message, start, end, stackOffset) {
198
+ return report(ts.DiagnosticCategory.Error, message, start, end, stackOffset);
199
+ }
200
+ function reportWarning(message, start, end, stackOffset) {
201
+ return report(ts.DiagnosticCategory.Warning, message, start, end, stackOffset);
202
+ }
203
+ function reportSuggestion(message, start, end, stackOffset) {
204
+ return report(ts.DiagnosticCategory.Suggestion, message, start, end, stackOffset);
205
+ }
206
+ function report(category, message, start, end, stackOffset = 2, err) {
207
+ const error = {
208
+ category,
209
+ code: currentRuleId,
210
+ messageText: message,
211
+ file: rulesContext.sourceFile,
212
+ start,
213
+ length: end - start,
214
+ source: 'tsslint',
215
+ relatedInformation: [],
216
+ };
217
+ if (cache && !rule2Mode.get(currentRuleId)) {
218
+ cache[1][currentRuleId] ??= [false, []];
219
+ cache[1][currentRuleId][1].push({
220
+ ...error,
221
+ file: undefined,
222
+ relatedInformation: error.relatedInformation?.map(info => ({
223
+ ...info,
224
+ file: info.file ? { fileName: info.file.fileName } : undefined,
225
+ })),
226
+ });
227
+ }
228
+ if (mode === 'typescript-plugin' && typeof stackOffset === 'number') {
229
+ err ??= new Error();
230
+ const relatedInfo = createRelatedInformation(ts, err, stackOffset);
231
+ if (relatedInfo) {
232
+ error.relatedInformation.push(relatedInfo);
233
+ }
234
+ }
235
+ let lintResult = lintResults.get(fileName);
236
+ if (!lintResult) {
237
+ lintResults.set(fileName, lintResult = [rulesContext.sourceFile, new Map(), []]);
238
+ }
239
+ const diagnostic2Fixes = lintResult[1];
240
+ const refactors = lintResult[2];
241
+ diagnostic2Fixes.set(error, []);
242
+ const fixes = diagnostic2Fixes.get(error);
243
+ return {
244
+ withDeprecated() {
245
+ error.reportsDeprecated = true;
246
+ return this;
247
+ },
248
+ withUnnecessary() {
249
+ error.reportsUnnecessary = true;
250
+ return this;
251
+ },
252
+ withFix(title, getEdits) {
253
+ fixes.push(({ title, getEdits }));
254
+ return this;
255
+ },
256
+ withRefactor(title, getEdits) {
257
+ refactors.push(({
258
+ diagnostic: error,
259
+ title,
260
+ getEdits,
261
+ }));
262
+ return this;
263
+ },
264
+ };
265
+ }
266
+ },
267
+ hasCodeFixes(fileName) {
268
+ const lintResult = lintResults.get(fileName);
269
+ if (!lintResult) {
270
+ return false;
271
+ }
272
+ for (const [_, fixes] of lintResult[1]) {
273
+ if (fixes.length) {
274
+ return true;
275
+ }
276
+ }
277
+ return false;
278
+ },
279
+ getCodeFixes(fileName, start, end, diagnostics, minimatchCache) {
280
+ const lintResult = lintResults.get(fileName);
281
+ if (!lintResult) {
282
+ return [];
283
+ }
284
+ const sourceFile = lintResult[0];
285
+ const configs = getFileConfigs(fileName, minimatchCache);
286
+ const result = [];
287
+ for (const [diagnostic, actions] of lintResult[1]) {
288
+ if (diagnostics?.length && !diagnostics.includes(diagnostic)) {
289
+ continue;
290
+ }
291
+ const diagStart = diagnostic.start;
292
+ const diagEnd = diagStart + diagnostic.length;
293
+ if ((diagStart >= start && diagStart <= end) ||
294
+ (diagEnd >= start && diagEnd <= end) ||
295
+ (start >= diagStart && start <= diagEnd) ||
296
+ (end >= diagStart && end <= diagEnd)) {
297
+ let codeFixes = [];
298
+ for (const action of actions) {
299
+ codeFixes.push({
300
+ fixName: `tsslint:${diagnostic.code}`,
301
+ description: action.title,
302
+ changes: action.getEdits(),
303
+ fixId: 'tsslint',
304
+ fixAllDescription: 'Fix all TSSLint errors'
305
+ });
306
+ }
307
+ for (const { plugins } of configs) {
308
+ for (const { resolveCodeFixes } of plugins) {
309
+ if (resolveCodeFixes) {
310
+ codeFixes = resolveCodeFixes(sourceFile, diagnostic, codeFixes);
311
+ }
312
+ }
313
+ }
314
+ result.push(...codeFixes);
315
+ }
316
+ }
317
+ return result;
318
+ },
319
+ getRefactors(fileName, start, end) {
320
+ const lintResult = lintResults.get(fileName);
321
+ if (!lintResult) {
322
+ return [];
323
+ }
324
+ const result = [];
325
+ for (let i = 0; i < lintResult[2].length; i++) {
326
+ const refactor = lintResult[2][i];
327
+ const diagStart = refactor.diagnostic.start;
328
+ const diagEnd = diagStart + refactor.diagnostic.length;
329
+ if ((diagStart >= start && diagStart <= end) ||
330
+ (diagEnd >= start && diagEnd <= end) ||
331
+ (start >= diagStart && start <= diagEnd) ||
332
+ (end >= diagStart && end <= diagEnd)) {
333
+ result.push({
334
+ name: `tsslint:${i}`,
335
+ description: refactor.title,
336
+ kind: 'quickfix',
337
+ });
338
+ }
339
+ }
340
+ return result;
341
+ },
342
+ getRefactorEdits(fileName, actionName) {
343
+ if (actionName.startsWith('tsslint:')) {
344
+ const lintResult = lintResults.get(fileName);
345
+ if (!lintResult) {
346
+ return [];
347
+ }
348
+ const index = actionName.slice('tsslint:'.length);
349
+ const refactor = lintResult[2][Number(index)];
350
+ if (refactor) {
351
+ return refactor.getEdits();
352
+ }
353
+ }
354
+ },
355
+ getRules: getFileRules,
356
+ getConfigs: getFileConfigs,
357
+ };
358
+ function getFileFmtProcesses(fileName, minimatchCache) {
359
+ if (!fileFmtProcesses.has(fileName)) {
360
+ const allPreprocess = [];
361
+ const configs = getFileConfigs(fileName, minimatchCache);
362
+ for (const { formatting } of configs) {
363
+ if (formatting) {
364
+ allPreprocess.push(...formatting);
365
+ }
366
+ }
367
+ fileFmtProcesses.set(fileName, allPreprocess);
368
+ }
369
+ return fileFmtProcesses.get(fileName);
370
+ }
371
+ function getFileRules(fileName, minimatchCache) {
372
+ let rules = fileRules.get(fileName);
373
+ if (!rules) {
374
+ rules = {};
375
+ const configs = getFileConfigs(fileName, minimatchCache);
376
+ for (const config of configs) {
377
+ collectRules(rules, config.rules, []);
378
+ }
379
+ for (const { plugins } of configs) {
380
+ for (const { resolveRules } of plugins) {
381
+ if (resolveRules) {
382
+ rules = resolveRules(fileName, rules);
383
+ }
384
+ }
385
+ }
386
+ fileRules.set(fileName, rules);
387
+ }
388
+ return rules;
389
+ }
390
+ function collectRules(record, rules, paths) {
391
+ for (const [path, rule] of Object.entries(rules)) {
392
+ if (typeof rule === 'object') {
393
+ collectRules(record, rule, [...paths, path]);
394
+ continue;
395
+ }
396
+ record[[...paths, path].join('/')] = rule;
397
+ }
398
+ }
399
+ function getFileConfigs(fileName, minimatchCache) {
400
+ let result = fileConfigs.get(fileName);
401
+ if (!result) {
402
+ result = configs.filter(({ include, exclude }) => {
403
+ if (exclude?.some(_minimatch)) {
404
+ return false;
405
+ }
406
+ if (include && !include.some(_minimatch)) {
407
+ return false;
408
+ }
409
+ return true;
410
+ });
411
+ fileConfigs.set(fileName, result);
412
+ function _minimatch(pattern) {
413
+ if (minimatchCache) {
414
+ if (pattern in minimatchCache) {
415
+ return minimatchCache[pattern];
416
+ }
417
+ }
418
+ let normalized = normalizedPath.get(pattern);
419
+ if (!normalized) {
420
+ normalized = ts.server.toNormalizedPath(path.resolve(rootDir, pattern));
421
+ normalizedPath.set(pattern, normalized);
422
+ }
423
+ const res = minimatch.minimatch(fileName, normalized, { dot: true });
424
+ if (minimatchCache) {
425
+ minimatchCache[pattern] = res;
426
+ }
427
+ return res;
428
+ }
429
+ }
430
+ return result;
431
+ }
445
432
  }
446
433
  const fsFiles = new Map();
447
434
  function createRelatedInformation(ts, err, stackOffset) {
448
- const stacks = ErrorStackParser.parse(err);
449
- if (stacks.length <= stackOffset) {
450
- return;
451
- }
452
- const stack = stacks[stackOffset];
453
- if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
454
- let fileName = stack.fileName.replace(/\\/g, '/');
455
- if (fileName.startsWith('file://')) {
456
- fileName = fileName.substring('file://'.length);
457
- }
458
- if (fileName.includes('http-url:')) {
459
- fileName = fileName.split('http-url:')[1];
460
- }
461
- const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0;
462
- const lastMtime = fsFiles.get(fileName)?.[1];
463
- if (mtime !== lastMtime) {
464
- const text = ts.sys.readFile(fileName);
465
- fsFiles.set(fileName, [
466
- text !== undefined,
467
- mtime,
468
- ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
469
- ]);
470
- }
471
- const [exist, _mtime, relatedFile] = fsFiles.get(fileName);
472
- let pos = 0;
473
- if (exist) {
474
- try {
475
- pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
476
- }
477
- catch { }
478
- }
479
- return {
480
- category: ts.DiagnosticCategory.Message,
481
- code: 0,
482
- file: relatedFile,
483
- start: pos,
484
- length: 0,
485
- messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
486
- };
487
- }
435
+ const stacks = ErrorStackParser.parse(err);
436
+ if (stacks.length <= stackOffset) {
437
+ return;
438
+ }
439
+ const stack = stacks[stackOffset];
440
+ if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
441
+ let fileName = stack.fileName.replace(/\\/g, '/');
442
+ if (fileName.startsWith('file://')) {
443
+ fileName = fileName.substring('file://'.length);
444
+ }
445
+ if (fileName.includes('http-url:')) {
446
+ fileName = fileName.split('http-url:')[1];
447
+ }
448
+ const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0;
449
+ const lastMtime = fsFiles.get(fileName)?.[1];
450
+ if (mtime !== lastMtime) {
451
+ const text = ts.sys.readFile(fileName);
452
+ fsFiles.set(fileName, [
453
+ text !== undefined,
454
+ mtime,
455
+ ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
456
+ ]);
457
+ }
458
+ const [exist, _mtime, relatedFile] = fsFiles.get(fileName);
459
+ let pos = 0;
460
+ if (exist) {
461
+ try {
462
+ pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
463
+ }
464
+ catch { }
465
+ }
466
+ return {
467
+ category: ts.DiagnosticCategory.Message,
468
+ code: 0,
469
+ file: relatedFile,
470
+ start: pos,
471
+ length: 0,
472
+ messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
473
+ };
474
+ }
488
475
  }
489
476
  function combineCodeFixes(fileName, fixes) {
490
- const changes = fixes
491
- .map(fix => fix.changes)
492
- .flat()
493
- .filter(change => change.fileName === fileName && change.textChanges.length)
494
- .sort((a, b) => b.textChanges[0].span.start - a.textChanges[0].span.start);
495
- let lastChangeAt = Number.MAX_VALUE;
496
- let finalTextChanges = [];
497
- for (const change of changes) {
498
- const textChanges = [...change.textChanges].sort((a, b) => a.span.start - b.span.start);
499
- const firstChange = textChanges[0];
500
- const lastChange = textChanges[textChanges.length - 1];
501
- if (lastChangeAt >= lastChange.span.start + lastChange.span.length) {
502
- lastChangeAt = firstChange.span.start;
503
- finalTextChanges = finalTextChanges.concat(textChanges);
504
- }
505
- }
506
- return finalTextChanges;
477
+ const changes = fixes
478
+ .map(fix => fix.changes)
479
+ .flat()
480
+ .filter(change => change.fileName === fileName && change.textChanges.length)
481
+ .sort((a, b) => b.textChanges[0].span.start - a.textChanges[0].span.start);
482
+ let lastChangeAt = Number.MAX_VALUE;
483
+ let finalTextChanges = [];
484
+ for (const change of changes) {
485
+ const textChanges = [...change.textChanges].sort((a, b) => a.span.start - b.span.start);
486
+ const firstChange = textChanges[0];
487
+ const lastChange = textChanges[textChanges.length - 1];
488
+ if (lastChangeAt >= lastChange.span.start + lastChange.span.length) {
489
+ lastChangeAt = firstChange.span.start;
490
+ finalTextChanges = finalTextChanges.concat(textChanges);
491
+ }
492
+ }
493
+ return finalTextChanges;
507
494
  }
508
495
  function applyTextChanges(baseSnapshot, textChanges) {
509
- textChanges = [...textChanges].sort((a, b) => b.span.start - a.span.start);
510
- let text = baseSnapshot.getText(0, baseSnapshot.getLength());
511
- for (const change of textChanges) {
512
- text = text.slice(0, change.span.start) + change.newText + text.slice(change.span.start + change.span.length);
513
- }
514
- return {
515
- getText(start, end) {
516
- return text.substring(start, end);
517
- },
518
- getLength() {
519
- return text.length;
520
- },
521
- getChangeRange(oldSnapshot) {
522
- if (oldSnapshot === baseSnapshot) {
523
- // TODO
524
- }
525
- return undefined;
526
- },
527
- };
496
+ textChanges = [...textChanges].sort((a, b) => b.span.start - a.span.start);
497
+ let text = baseSnapshot.getText(0, baseSnapshot.getLength());
498
+ for (const change of textChanges) {
499
+ text = text.slice(0, change.span.start) + change.newText + text.slice(change.span.start + change.span.length);
500
+ }
501
+ return {
502
+ getText(start, end) {
503
+ return text.substring(start, end);
504
+ },
505
+ getLength() {
506
+ return text.length;
507
+ },
508
+ getChangeRange(oldSnapshot) {
509
+ if (oldSnapshot === baseSnapshot) {
510
+ // TODO
511
+ }
512
+ return undefined;
513
+ },
514
+ };
528
515
  }
529
516
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsslint/core",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/core"
13
13
  },
14
14
  "dependencies": {
15
- "@tsslint/types": "1.5.4",
15
+ "@tsslint/types": "1.5.5",
16
16
  "error-stack-parser": "^2.1.4",
17
17
  "esbuild": ">=0.17.0",
18
18
  "minimatch": "^10.0.1"
@@ -23,5 +23,5 @@
23
23
  "scripts": {
24
24
  "postinstall": "node scripts/cleanCache.js"
25
25
  },
26
- "gitHead": "02cbc403c85b4224b5ccd732dc96f7a0991a6ae2"
26
+ "gitHead": "aa1bd7ed5b26cab99048001d1efa8880260f0c6e"
27
27
  }