@tsslint/core 1.0.14 → 1.0.16
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.d.ts +3 -1
- package/index.js +105 -7
- package/lib/build.d.ts +2 -0
- package/lib/build.js +17 -0
- package/lib/linter.d.ts +12 -0
- package/lib/linter.js +342 -0
- package/lib/watch.d.ts +14 -0
- package/lib/watch.js +96 -0
- package/package.json +4 -5
package/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
export * from './lib/build';
|
|
2
|
+
export * from './lib/watch';
|
|
3
|
+
import type { Config, ProjectContext } from '@tsslint/types';
|
|
2
4
|
import type * as ts from 'typescript';
|
|
3
5
|
export type Linter = ReturnType<typeof createLinter>;
|
|
4
6
|
export declare function createLinter(ctx: ProjectContext, config: Config, withStack: boolean): {
|
package/index.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
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];
|
|
12
|
+
}));
|
|
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);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.createLinter = createLinter;
|
|
4
18
|
exports.combineCodeFixes = combineCodeFixes;
|
|
5
19
|
exports.applyTextChanges = applyTextChanges;
|
|
20
|
+
__exportStar(require("./lib/build"), exports);
|
|
21
|
+
__exportStar(require("./lib/watch"), exports);
|
|
6
22
|
const ErrorStackParser = require("error-stack-parser");
|
|
7
23
|
const path = require("path");
|
|
8
24
|
const minimatch = require("minimatch");
|
|
@@ -29,15 +45,63 @@ function createLinter(ctx, config, withStack) {
|
|
|
29
45
|
const fileRefactors = new Map();
|
|
30
46
|
const sourceFiles = new Map();
|
|
31
47
|
const plugins = (config.plugins ?? []).map(plugin => plugin(ctx));
|
|
48
|
+
const includes = [];
|
|
32
49
|
const excludes = [];
|
|
33
|
-
for (
|
|
50
|
+
for (const include of config.include ?? []) {
|
|
51
|
+
const basePath = path.dirname(ctx.configFile);
|
|
52
|
+
includes.push([include, path.resolve(basePath, include)]);
|
|
53
|
+
}
|
|
54
|
+
for (const exclude of config.exclude ?? []) {
|
|
34
55
|
const basePath = path.dirname(ctx.configFile);
|
|
35
|
-
excludes.push(path.resolve(basePath, exclude));
|
|
56
|
+
excludes.push([exclude, path.resolve(basePath, exclude)]);
|
|
36
57
|
}
|
|
37
58
|
return {
|
|
38
59
|
lint(fileName) {
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
let diagnostics = [];
|
|
61
|
+
let debugInfo;
|
|
62
|
+
if (config.debug) {
|
|
63
|
+
debugInfo = {
|
|
64
|
+
category: ts.DiagnosticCategory.Message,
|
|
65
|
+
code: 'debug',
|
|
66
|
+
messageText: '- Config: ' + ctx.configFile + '\n',
|
|
67
|
+
file: ctx.languageService.getProgram().getSourceFile(fileName),
|
|
68
|
+
start: 0,
|
|
69
|
+
length: 0,
|
|
70
|
+
source: 'tsslint',
|
|
71
|
+
relatedInformation: [],
|
|
72
|
+
};
|
|
73
|
+
diagnostics.push(debugInfo);
|
|
74
|
+
}
|
|
75
|
+
for (const [exclude, pattern] of excludes) {
|
|
76
|
+
if (minimatch.minimatch(fileName, pattern)) {
|
|
77
|
+
if (debugInfo) {
|
|
78
|
+
debugInfo.messageText += '- Included: ❌ (via ' + JSON.stringify({ exclude: [exclude] }) + ')\n';
|
|
79
|
+
}
|
|
80
|
+
return diagnostics;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (includes.length) {
|
|
84
|
+
let included = false;
|
|
85
|
+
for (const [include, pattern] of includes) {
|
|
86
|
+
if (minimatch.minimatch(fileName, pattern)) {
|
|
87
|
+
included = true;
|
|
88
|
+
if (debugInfo) {
|
|
89
|
+
debugInfo.messageText += '- Included: ✅ (via ' + JSON.stringify({ include: [include] }) + ')\n';
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!included) {
|
|
95
|
+
if (debugInfo) {
|
|
96
|
+
debugInfo.messageText += '- Included: ❌ (not included in any include patterns)\n';
|
|
97
|
+
}
|
|
98
|
+
return diagnostics;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
if (debugInfo) {
|
|
103
|
+
debugInfo.messageText += '- Included: ✅ (no include patterns)\n';
|
|
104
|
+
}
|
|
41
105
|
}
|
|
42
106
|
const sourceFile = ctx.languageService.getProgram()?.getSourceFile(fileName);
|
|
43
107
|
if (!sourceFile) {
|
|
@@ -54,21 +118,52 @@ function createLinter(ctx, config, withStack) {
|
|
|
54
118
|
const rules = getFileRules(sourceFile.fileName);
|
|
55
119
|
const fixes = getFileFixes(sourceFile.fileName);
|
|
56
120
|
const refactors = getFileRefactors(sourceFile.fileName);
|
|
57
|
-
let diagnostics = [];
|
|
58
121
|
let currentRuleId;
|
|
122
|
+
let currentIssues = 0;
|
|
123
|
+
let currentFixes = 0;
|
|
124
|
+
let currentRefactors = 0;
|
|
59
125
|
fixes.clear();
|
|
60
126
|
refactors.clear();
|
|
127
|
+
if (debugInfo) {
|
|
128
|
+
debugInfo.messageText += '- Rules:\n';
|
|
129
|
+
}
|
|
61
130
|
for (const [id, rule] of Object.entries(rules)) {
|
|
62
131
|
if (token?.isCancellationRequested()) {
|
|
63
132
|
break;
|
|
64
133
|
}
|
|
65
134
|
currentRuleId = id;
|
|
135
|
+
currentIssues = 0;
|
|
136
|
+
currentFixes = 0;
|
|
137
|
+
currentRefactors = 0;
|
|
138
|
+
const start = Date.now();
|
|
66
139
|
try {
|
|
67
140
|
rule(rulesContext);
|
|
141
|
+
if (debugInfo) {
|
|
142
|
+
const time = Date.now() - start;
|
|
143
|
+
debugInfo.messageText += ` - ${id}`;
|
|
144
|
+
const details = [];
|
|
145
|
+
if (currentIssues) {
|
|
146
|
+
details.push(`${currentIssues} issues`);
|
|
147
|
+
}
|
|
148
|
+
if (currentFixes) {
|
|
149
|
+
details.push(`${currentFixes} fixes`);
|
|
150
|
+
}
|
|
151
|
+
if (currentRefactors) {
|
|
152
|
+
details.push(`${currentRefactors} refactors`);
|
|
153
|
+
}
|
|
154
|
+
if (time) {
|
|
155
|
+
details.push(`${time}ms`);
|
|
156
|
+
}
|
|
157
|
+
if (details.length) {
|
|
158
|
+
debugInfo.messageText += ` (${details.join(', ')})`;
|
|
159
|
+
}
|
|
160
|
+
debugInfo.messageText += '\n';
|
|
161
|
+
}
|
|
68
162
|
}
|
|
69
163
|
catch (err) {
|
|
70
|
-
|
|
71
|
-
|
|
164
|
+
if (debugInfo) {
|
|
165
|
+
debugInfo.messageText += ` - ${id} (❌ ${err && typeof err === 'object' && 'stack' in err ? err.stack : String(err)}})\n`;
|
|
166
|
+
}
|
|
72
167
|
}
|
|
73
168
|
}
|
|
74
169
|
for (const plugin of plugins) {
|
|
@@ -128,6 +223,7 @@ function createLinter(ctx, config, withStack) {
|
|
|
128
223
|
}
|
|
129
224
|
}
|
|
130
225
|
diagnostics.push(error);
|
|
226
|
+
currentIssues++;
|
|
131
227
|
return {
|
|
132
228
|
withDeprecated() {
|
|
133
229
|
error.reportsDeprecated = true;
|
|
@@ -138,6 +234,7 @@ function createLinter(ctx, config, withStack) {
|
|
|
138
234
|
return this;
|
|
139
235
|
},
|
|
140
236
|
withFix(title, getEdits) {
|
|
237
|
+
currentFixes++;
|
|
141
238
|
if (!fixes.has(currentRuleId)) {
|
|
142
239
|
fixes.set(currentRuleId, []);
|
|
143
240
|
}
|
|
@@ -151,6 +248,7 @@ function createLinter(ctx, config, withStack) {
|
|
|
151
248
|
return this;
|
|
152
249
|
},
|
|
153
250
|
withRefactor(title, getEdits) {
|
|
251
|
+
currentRefactors++;
|
|
154
252
|
if (!refactors.has(currentRuleId)) {
|
|
155
253
|
refactors.set(currentRuleId, []);
|
|
156
254
|
}
|
package/lib/build.d.ts
ADDED
package/lib/build.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildConfigFile = buildConfigFile;
|
|
4
|
+
const watch_1 = require("./watch");
|
|
5
|
+
function buildConfigFile(configFilePath, createHash, logger) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
(0, watch_1.watchConfigFile)(configFilePath, (config, result) => {
|
|
8
|
+
if (config) {
|
|
9
|
+
resolve(config);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
reject(result);
|
|
13
|
+
}
|
|
14
|
+
}, false, createHash, logger);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=build.js.map
|
package/lib/linter.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Config, ProjectContext } from '@tsslint/types';
|
|
2
|
+
import type * as ts from 'typescript';
|
|
3
|
+
export type Linter = ReturnType<typeof createLinter>;
|
|
4
|
+
export declare function createLinter(ctx: ProjectContext, config: Config, withStack: boolean): {
|
|
5
|
+
lint(fileName: string): ts.DiagnosticWithLocation[];
|
|
6
|
+
hasCodeFixes(fileName: string): boolean;
|
|
7
|
+
getCodeFixes(fileName: string, start: number, end: number, diagnostics?: ts.Diagnostic[]): ts.CodeFixAction[];
|
|
8
|
+
getRefactors(fileName: string, start: number, end: number): ts.RefactorActionInfo[];
|
|
9
|
+
getRefactorEdits(fileName: string, name: string): ts.FileTextChanges[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]): ts.TextChange[];
|
|
12
|
+
export declare function applyTextChanges(baseSnapshot: ts.IScriptSnapshot, textChanges: ts.TextChange[]): ts.IScriptSnapshot;
|
package/lib/linter.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLinter = createLinter;
|
|
4
|
+
exports.combineCodeFixes = combineCodeFixes;
|
|
5
|
+
exports.applyTextChanges = applyTextChanges;
|
|
6
|
+
const ErrorStackParser = require("error-stack-parser");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const minimatch = require("minimatch");
|
|
9
|
+
function createLinter(ctx, config, withStack) {
|
|
10
|
+
if (withStack) {
|
|
11
|
+
require('source-map-support').install({
|
|
12
|
+
retrieveFile(path) {
|
|
13
|
+
// monkey-fix, refs: https://github.com/typescript-eslint/typescript-eslint/issues/9352
|
|
14
|
+
if (path.replace(/\\/g, '/').includes('/@typescript-eslint/eslint-plugin/dist/rules/') && path.endsWith('.js.map')) {
|
|
15
|
+
return JSON.stringify({
|
|
16
|
+
version: 3,
|
|
17
|
+
sources: [],
|
|
18
|
+
sourcesContent: [],
|
|
19
|
+
mappings: '',
|
|
20
|
+
names: [],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const ts = ctx.typescript;
|
|
27
|
+
const fileRules = new Map();
|
|
28
|
+
const fileFixes = new Map();
|
|
29
|
+
const fileRefactors = new Map();
|
|
30
|
+
const sourceFiles = new Map();
|
|
31
|
+
const plugins = (config.plugins ?? []).map(plugin => plugin(ctx));
|
|
32
|
+
const excludes = [];
|
|
33
|
+
for (let exclude of config.exclude ?? []) {
|
|
34
|
+
const basePath = path.dirname(ctx.configFile);
|
|
35
|
+
excludes.push(path.resolve(basePath, exclude));
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
lint(fileName) {
|
|
39
|
+
if (excludes.some(pattern => minimatch.minimatch(fileName, pattern))) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const sourceFile = ctx.languageService.getProgram()?.getSourceFile(fileName);
|
|
43
|
+
if (!sourceFile) {
|
|
44
|
+
throw new Error(`No source file found for ${fileName}`);
|
|
45
|
+
}
|
|
46
|
+
const rulesContext = {
|
|
47
|
+
...ctx,
|
|
48
|
+
sourceFile,
|
|
49
|
+
reportError,
|
|
50
|
+
reportWarning,
|
|
51
|
+
reportSuggestion,
|
|
52
|
+
};
|
|
53
|
+
const token = ctx.languageServiceHost.getCancellationToken?.();
|
|
54
|
+
const rules = getFileRules(sourceFile.fileName);
|
|
55
|
+
const fixes = getFileFixes(sourceFile.fileName);
|
|
56
|
+
const refactors = getFileRefactors(sourceFile.fileName);
|
|
57
|
+
let diagnostics = [];
|
|
58
|
+
let currentRuleId;
|
|
59
|
+
fixes.clear();
|
|
60
|
+
refactors.clear();
|
|
61
|
+
for (const [id, rule] of Object.entries(rules)) {
|
|
62
|
+
if (token?.isCancellationRequested()) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
currentRuleId = id;
|
|
66
|
+
try {
|
|
67
|
+
rule(rulesContext);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
category: ts.DiagnosticCategory.Error,
|
|
72
|
+
code: id,
|
|
73
|
+
messageText: String(err),
|
|
74
|
+
file: sourceFile,
|
|
75
|
+
start: 0,
|
|
76
|
+
length: 0,
|
|
77
|
+
source: 'tsslint',
|
|
78
|
+
relatedInformation: [],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const plugin of plugins) {
|
|
83
|
+
if (plugin.resolveDiagnostics) {
|
|
84
|
+
diagnostics = plugin.resolveDiagnostics(sourceFile.fileName, diagnostics);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const diagnosticSet = new Set(diagnostics);
|
|
88
|
+
for (const [ruleId, fix] of [...fixes]) {
|
|
89
|
+
const final = fix.filter(fix => diagnosticSet.has(fix.diagnostic));
|
|
90
|
+
if (final.length) {
|
|
91
|
+
fixes.set(ruleId, final);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
fixes.delete(ruleId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (const [ruleId, refactor] of [...refactors]) {
|
|
98
|
+
const final = refactor.filter(fix => diagnosticSet.has(fix.diagnostic));
|
|
99
|
+
if (final.length) {
|
|
100
|
+
refactors.set(ruleId, final);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
refactors.delete(ruleId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return diagnostics;
|
|
107
|
+
function reportError(message, start, end, traceOffset = 0) {
|
|
108
|
+
return report(ts.DiagnosticCategory.Error, message, start, end, traceOffset);
|
|
109
|
+
}
|
|
110
|
+
function reportWarning(message, start, end, traceOffset = 0) {
|
|
111
|
+
return report(ts.DiagnosticCategory.Warning, message, start, end, traceOffset);
|
|
112
|
+
}
|
|
113
|
+
function reportSuggestion(message, start, end, traceOffset = 0) {
|
|
114
|
+
return report(ts.DiagnosticCategory.Suggestion, message, start, end, traceOffset);
|
|
115
|
+
}
|
|
116
|
+
function report(category, message, start, end, traceOffset) {
|
|
117
|
+
const error = {
|
|
118
|
+
category,
|
|
119
|
+
code: currentRuleId,
|
|
120
|
+
messageText: message,
|
|
121
|
+
file: sourceFile,
|
|
122
|
+
start,
|
|
123
|
+
length: end - start,
|
|
124
|
+
source: 'tsslint',
|
|
125
|
+
relatedInformation: [],
|
|
126
|
+
};
|
|
127
|
+
if (withStack) {
|
|
128
|
+
const stacks = traceOffset === false
|
|
129
|
+
? []
|
|
130
|
+
: ErrorStackParser.parse(new Error());
|
|
131
|
+
if (typeof traceOffset === 'number') {
|
|
132
|
+
const baseOffset = 2 + traceOffset;
|
|
133
|
+
if (stacks.length > baseOffset) {
|
|
134
|
+
pushRelatedInformation(error, stacks[baseOffset]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
diagnostics.push(error);
|
|
139
|
+
return {
|
|
140
|
+
withDeprecated() {
|
|
141
|
+
error.reportsDeprecated = true;
|
|
142
|
+
return this;
|
|
143
|
+
},
|
|
144
|
+
withUnnecessary() {
|
|
145
|
+
error.reportsUnnecessary = true;
|
|
146
|
+
return this;
|
|
147
|
+
},
|
|
148
|
+
withFix(title, getEdits) {
|
|
149
|
+
if (!fixes.has(currentRuleId)) {
|
|
150
|
+
fixes.set(currentRuleId, []);
|
|
151
|
+
}
|
|
152
|
+
fixes.get(currentRuleId).push(({
|
|
153
|
+
diagnostic: error,
|
|
154
|
+
title,
|
|
155
|
+
start,
|
|
156
|
+
end,
|
|
157
|
+
getEdits,
|
|
158
|
+
}));
|
|
159
|
+
return this;
|
|
160
|
+
},
|
|
161
|
+
withRefactor(title, getEdits) {
|
|
162
|
+
if (!refactors.has(currentRuleId)) {
|
|
163
|
+
refactors.set(currentRuleId, []);
|
|
164
|
+
}
|
|
165
|
+
refactors.get(currentRuleId).push(({
|
|
166
|
+
diagnostic: error,
|
|
167
|
+
title,
|
|
168
|
+
start,
|
|
169
|
+
end,
|
|
170
|
+
getEdits,
|
|
171
|
+
}));
|
|
172
|
+
return this;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function pushRelatedInformation(error, stack) {
|
|
177
|
+
if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
|
|
178
|
+
let fileName = stack.fileName.replace(/\\/g, '/');
|
|
179
|
+
if (fileName.startsWith('file://')) {
|
|
180
|
+
fileName = fileName.substring('file://'.length);
|
|
181
|
+
}
|
|
182
|
+
if (fileName.includes('http-url:')) {
|
|
183
|
+
fileName = fileName.split('http-url:')[1];
|
|
184
|
+
}
|
|
185
|
+
if (!sourceFiles.has(fileName)) {
|
|
186
|
+
const text = ctx.languageServiceHost.readFile(fileName) ?? '';
|
|
187
|
+
sourceFiles.set(fileName, ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true));
|
|
188
|
+
}
|
|
189
|
+
const stackFile = sourceFiles.get(fileName);
|
|
190
|
+
let pos = 0;
|
|
191
|
+
try {
|
|
192
|
+
pos = stackFile?.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
error.relatedInformation?.push({
|
|
196
|
+
category: ts.DiagnosticCategory.Message,
|
|
197
|
+
code: 0,
|
|
198
|
+
file: stackFile,
|
|
199
|
+
start: pos,
|
|
200
|
+
length: 0,
|
|
201
|
+
messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
hasCodeFixes(fileName) {
|
|
207
|
+
const fixesMap = getFileFixes(fileName);
|
|
208
|
+
for (const [_ruleId, fixes] of fixesMap) {
|
|
209
|
+
if (fixes.length) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
},
|
|
215
|
+
getCodeFixes(fileName, start, end, diagnostics) {
|
|
216
|
+
const fixesMap = getFileFixes(fileName);
|
|
217
|
+
let result = [];
|
|
218
|
+
for (const [ruleId, fixes] of fixesMap) {
|
|
219
|
+
for (let i = 0; i < fixes.length; i++) {
|
|
220
|
+
const fix = fixes[i];
|
|
221
|
+
if (diagnostics?.length && !diagnostics.includes(fix.diagnostic)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if ((fix.start >= start && fix.start <= end) ||
|
|
225
|
+
(fix.end >= start && fix.end <= end) ||
|
|
226
|
+
(start >= fix.start && start <= fix.end) ||
|
|
227
|
+
(end >= fix.start && end <= fix.end)) {
|
|
228
|
+
result.push({
|
|
229
|
+
fixName: `tsslint:${ruleId}`,
|
|
230
|
+
description: fix.title,
|
|
231
|
+
changes: fix.getEdits(),
|
|
232
|
+
fixId: 'tsslint',
|
|
233
|
+
fixAllDescription: 'Fix all TSSLint errors'
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const plugin of plugins) {
|
|
239
|
+
if (plugin.resolveCodeFixes) {
|
|
240
|
+
result = plugin.resolveCodeFixes(fileName, result);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
},
|
|
245
|
+
getRefactors(fileName, start, end) {
|
|
246
|
+
const refactorsMap = getFileRefactors(fileName);
|
|
247
|
+
let result = [];
|
|
248
|
+
for (const [ruleId, refactors] of refactorsMap) {
|
|
249
|
+
for (let i = 0; i < refactors.length; i++) {
|
|
250
|
+
const refactor = refactors[i];
|
|
251
|
+
if ((refactor.start >= start && refactor.start <= end) ||
|
|
252
|
+
(refactor.end >= start && refactor.end <= end) ||
|
|
253
|
+
(start >= refactor.start && start <= refactor.end) ||
|
|
254
|
+
(end >= refactor.start && end <= refactor.end)) {
|
|
255
|
+
result.push({
|
|
256
|
+
name: `tsslint:${ruleId}:${i}`,
|
|
257
|
+
description: refactor.title + ' (' + ruleId + ')',
|
|
258
|
+
kind: 'quickfix',
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
},
|
|
265
|
+
getRefactorEdits(fileName, name) {
|
|
266
|
+
if (name.startsWith('tsslint:')) {
|
|
267
|
+
const [ruleId, index] = name.slice('tsslint:'.length).split(':');
|
|
268
|
+
const refactorsMap = getFileRefactors(fileName);
|
|
269
|
+
const refactor = refactorsMap.get(ruleId)?.[Number(index)];
|
|
270
|
+
if (refactor) {
|
|
271
|
+
return refactor.getEdits();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
function getFileRules(fileName) {
|
|
277
|
+
let rules = fileRules.get(fileName);
|
|
278
|
+
if (!rules) {
|
|
279
|
+
rules = { ...config.rules };
|
|
280
|
+
for (const plugin of plugins) {
|
|
281
|
+
if (plugin.resolveRules) {
|
|
282
|
+
rules = plugin.resolveRules(fileName, rules);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
fileRules.set(fileName, rules);
|
|
286
|
+
}
|
|
287
|
+
return rules;
|
|
288
|
+
}
|
|
289
|
+
function getFileFixes(fileName) {
|
|
290
|
+
if (!fileFixes.has(fileName)) {
|
|
291
|
+
fileFixes.set(fileName, new Map());
|
|
292
|
+
}
|
|
293
|
+
return fileFixes.get(fileName);
|
|
294
|
+
}
|
|
295
|
+
function getFileRefactors(fileName) {
|
|
296
|
+
if (!fileRefactors.has(fileName)) {
|
|
297
|
+
fileRefactors.set(fileName, new Map());
|
|
298
|
+
}
|
|
299
|
+
return fileRefactors.get(fileName);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function combineCodeFixes(fileName, fixes) {
|
|
303
|
+
const changes = fixes
|
|
304
|
+
.map(fix => fix.changes)
|
|
305
|
+
.flat()
|
|
306
|
+
.filter(change => change.fileName === fileName && change.textChanges.length)
|
|
307
|
+
.sort((a, b) => b.textChanges[0].span.start - a.textChanges[0].span.start);
|
|
308
|
+
let lastChangeAt = Number.MAX_VALUE;
|
|
309
|
+
let finalTextChanges = [];
|
|
310
|
+
for (const change of changes) {
|
|
311
|
+
const textChanges = [...change.textChanges].sort((a, b) => a.span.start - b.span.start);
|
|
312
|
+
const firstChange = textChanges[0];
|
|
313
|
+
const lastChange = textChanges[textChanges.length - 1];
|
|
314
|
+
if (lastChangeAt >= lastChange.span.start + lastChange.span.length) {
|
|
315
|
+
lastChangeAt = firstChange.span.start;
|
|
316
|
+
finalTextChanges = finalTextChanges.concat(textChanges);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return finalTextChanges;
|
|
320
|
+
}
|
|
321
|
+
function applyTextChanges(baseSnapshot, textChanges) {
|
|
322
|
+
textChanges = [...textChanges].sort((a, b) => b.span.start - a.span.start);
|
|
323
|
+
let text = baseSnapshot.getText(0, baseSnapshot.getLength());
|
|
324
|
+
for (const change of textChanges) {
|
|
325
|
+
text = text.slice(0, change.span.start) + change.newText + text.slice(change.span.start + change.span.length);
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
getText(start, end) {
|
|
329
|
+
return text.substring(start, end);
|
|
330
|
+
},
|
|
331
|
+
getLength() {
|
|
332
|
+
return text.length;
|
|
333
|
+
},
|
|
334
|
+
getChangeRange(oldSnapshot) {
|
|
335
|
+
if (oldSnapshot === baseSnapshot) {
|
|
336
|
+
// TODO
|
|
337
|
+
}
|
|
338
|
+
return undefined;
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=linter.js.map
|
package/lib/watch.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import esbuild = require('esbuild');
|
|
2
|
+
import type { Config } from '@tsslint/config';
|
|
3
|
+
export declare function watchConfigFile(configFilePath: string, onBuild: (config: Config | undefined, result: esbuild.BuildResult) => void, watch?: boolean, createHash?: (path: string) => string, logger?: Pick<typeof console, 'log' | 'warn' | 'error'>): Promise<esbuild.BuildContext<{
|
|
4
|
+
entryPoints: string[];
|
|
5
|
+
bundle: true;
|
|
6
|
+
sourcemap: true;
|
|
7
|
+
outfile: string;
|
|
8
|
+
format: "esm";
|
|
9
|
+
platform: "node";
|
|
10
|
+
plugins: {
|
|
11
|
+
name: string;
|
|
12
|
+
setup(build: esbuild.PluginBuild): void;
|
|
13
|
+
}[];
|
|
14
|
+
}>>;
|
package/lib/watch.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.watchConfigFile = watchConfigFile;
|
|
4
|
+
const esbuild = require("esbuild");
|
|
5
|
+
const _path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
async function watchConfigFile(configFilePath, onBuild, watch = true, createHash = btoa, logger = console) {
|
|
8
|
+
let start;
|
|
9
|
+
const outDir = _path.resolve(configFilePath, '..', 'node_modules', '.tsslint');
|
|
10
|
+
const outFileName = createHash(_path.relative(outDir, configFilePath)) + '.mjs';
|
|
11
|
+
const outFile = _path.join(outDir, outFileName);
|
|
12
|
+
const resultHandler = async (result) => {
|
|
13
|
+
let config;
|
|
14
|
+
if (!result.errors.length) {
|
|
15
|
+
try {
|
|
16
|
+
config = (await import(outFile + '?time=' + Date.now())).default;
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
result.errors.push({ text: String(e) });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
logger.log(`Built ${_path.relative(process.cwd(), configFilePath)} in ${Date.now() - start}ms`);
|
|
23
|
+
onBuild(config, result);
|
|
24
|
+
};
|
|
25
|
+
const cacheDir = _path.resolve(outDir, 'http_resources');
|
|
26
|
+
const cachePathToOriginalPath = new Map();
|
|
27
|
+
const ctx = await esbuild.context({
|
|
28
|
+
entryPoints: [configFilePath],
|
|
29
|
+
bundle: true,
|
|
30
|
+
sourcemap: true,
|
|
31
|
+
outfile: outFile,
|
|
32
|
+
format: 'esm',
|
|
33
|
+
platform: 'node',
|
|
34
|
+
plugins: [{
|
|
35
|
+
name: 'tsslint',
|
|
36
|
+
setup(build) {
|
|
37
|
+
build.onStart(() => {
|
|
38
|
+
start = Date.now();
|
|
39
|
+
});
|
|
40
|
+
build.onResolve({ filter: /^https?:\/\// }, ({ path }) => {
|
|
41
|
+
const cachePath = _path.join(cacheDir, createHash(path));
|
|
42
|
+
cachePathToOriginalPath.set(cachePath, path);
|
|
43
|
+
return { path: cachePath, namespace: 'http-url' };
|
|
44
|
+
});
|
|
45
|
+
build.onResolve({ filter: /.*/ }, ({ path, resolveDir }) => {
|
|
46
|
+
if (!path.endsWith('.ts')) {
|
|
47
|
+
try {
|
|
48
|
+
const maybeJsPath = require.resolve(path, { paths: [resolveDir] });
|
|
49
|
+
if (!maybeJsPath.endsWith('.ts')) {
|
|
50
|
+
return {
|
|
51
|
+
path: maybeJsPath,
|
|
52
|
+
external: true,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
}
|
|
58
|
+
return {};
|
|
59
|
+
});
|
|
60
|
+
build.onLoad({ filter: /.*/, namespace: 'http-url' }, async ({ path: cachePath }) => {
|
|
61
|
+
const path = cachePathToOriginalPath.get(cachePath);
|
|
62
|
+
if (fs.existsSync(cachePath)) {
|
|
63
|
+
return {
|
|
64
|
+
contents: fs.readFileSync(cachePath, 'utf8'),
|
|
65
|
+
loader: 'ts',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const response = await fetch(path);
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`Failed to load ${path}`);
|
|
71
|
+
}
|
|
72
|
+
const text = await response.text();
|
|
73
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
74
|
+
fs.writeFileSync(cachePath, text, 'utf8');
|
|
75
|
+
return {
|
|
76
|
+
contents: text,
|
|
77
|
+
loader: path.substring(path.lastIndexOf('.') + 1),
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
if (watch) {
|
|
81
|
+
build.onEnd(resultHandler);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
}],
|
|
85
|
+
});
|
|
86
|
+
if (watch) {
|
|
87
|
+
await ctx.watch();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const result = await ctx.rebuild();
|
|
91
|
+
await ctx.dispose();
|
|
92
|
+
resultHandler(result);
|
|
93
|
+
}
|
|
94
|
+
return ctx;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=watch.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsslint/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -12,12 +12,11 @@
|
|
|
12
12
|
"directory": "packages/core"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
+
"@tsslint/types": "1.0.16",
|
|
15
16
|
"error-stack-parser": "^2.1.4",
|
|
17
|
+
"esbuild": "^0.23.0",
|
|
16
18
|
"minimatch": "^10.0.1",
|
|
17
19
|
"source-map-support": "^0.5.21"
|
|
18
20
|
},
|
|
19
|
-
"
|
|
20
|
-
"@tsslint/config": "1.0.14"
|
|
21
|
-
},
|
|
22
|
-
"gitHead": "84ea99432b44827a79ac034896202652bf21c912"
|
|
21
|
+
"gitHead": "e430814fd394da5afbcd3157d02c246592162f75"
|
|
23
22
|
}
|