@tsslint/typescript-plugin 1.5.18 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +52 -95
- package/package.json +5 -4
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@ const core = require("@tsslint/core");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const url = require("url");
|
|
5
5
|
const fs = require("fs");
|
|
6
|
+
const ErrorStackParser = require("error-stack-parser");
|
|
6
7
|
const languageServiceDecorators = new WeakMap();
|
|
7
8
|
const plugin = modules => {
|
|
8
9
|
const { typescript: ts } = modules;
|
|
@@ -25,106 +26,15 @@ const plugin = modules => {
|
|
|
25
26
|
};
|
|
26
27
|
return pluginModule;
|
|
27
28
|
};
|
|
29
|
+
const fsFiles = new Map();
|
|
28
30
|
function decorateLanguageService(ts, projectRoot, info) {
|
|
29
|
-
const { getSemanticDiagnostics, getCodeFixesAtPosition, getCombinedCodeFix, getApplicableRefactors, getEditsForRefactor,
|
|
30
|
-
const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
31
|
-
const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
|
|
31
|
+
const { getSemanticDiagnostics, getCodeFixesAtPosition, getCombinedCodeFix, getApplicableRefactors, getEditsForRefactor, } = info.languageService;
|
|
32
32
|
const projectFileNameKeys = new Set();
|
|
33
33
|
let configFile;
|
|
34
34
|
let configFileBuildContext;
|
|
35
35
|
let configFileDiagnostics = [];
|
|
36
36
|
let config;
|
|
37
37
|
let linter;
|
|
38
|
-
let formattingSnapshot;
|
|
39
|
-
let formattingSnapshotVersion = 0;
|
|
40
|
-
info.languageServiceHost.getScriptSnapshot = fileName => {
|
|
41
|
-
if (formattingSnapshot) {
|
|
42
|
-
return formattingSnapshot;
|
|
43
|
-
}
|
|
44
|
-
return getScriptSnapshot(fileName);
|
|
45
|
-
};
|
|
46
|
-
info.languageServiceHost.getScriptVersion = fileName => {
|
|
47
|
-
if (formattingSnapshot) {
|
|
48
|
-
return `tsslint-fmt-${formattingSnapshotVersion++}`;
|
|
49
|
-
}
|
|
50
|
-
return getScriptVersion(fileName);
|
|
51
|
-
};
|
|
52
|
-
info.languageService.getFormattingEditsForDocument = (fileName, options) => {
|
|
53
|
-
if (linter) {
|
|
54
|
-
try {
|
|
55
|
-
const sourceFile = info.languageService.getNonBoundSourceFile(fileName);
|
|
56
|
-
const linterEdits = linter.format(sourceFile);
|
|
57
|
-
if (linterEdits.length) {
|
|
58
|
-
const originalLength = sourceFile.text.length;
|
|
59
|
-
let text = sourceFile.text;
|
|
60
|
-
for (const edit of linterEdits.sort((a, b) => (b.span.start + b.span.length) - (a.span.start + a.span.length))) {
|
|
61
|
-
text = text.slice(0, edit.span.start) + edit.newText + text.slice(edit.span.start + edit.span.length);
|
|
62
|
-
}
|
|
63
|
-
formattingSnapshot = ts.ScriptSnapshot.fromString(text);
|
|
64
|
-
const serviceEdits = getFormattingEditsForDocument(fileName, options);
|
|
65
|
-
formattingSnapshot = undefined;
|
|
66
|
-
if (serviceEdits.length) {
|
|
67
|
-
for (const edit of serviceEdits.sort((a, b) => (b.span.start + b.span.length) - (a.span.start + a.span.length))) {
|
|
68
|
-
text = text.slice(0, edit.span.start) + edit.newText + text.slice(edit.span.start + edit.span.length);
|
|
69
|
-
}
|
|
70
|
-
return [{
|
|
71
|
-
span: { start: 0, length: originalLength },
|
|
72
|
-
newText: text,
|
|
73
|
-
}];
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
return linterEdits;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
debugger;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return getFormattingEditsForDocument(fileName, options);
|
|
85
|
-
};
|
|
86
|
-
info.languageService.getFormattingEditsForRange = (fileName, start, end, options) => {
|
|
87
|
-
if (linter) {
|
|
88
|
-
try {
|
|
89
|
-
const sourceFile = info.languageService.getNonBoundSourceFile(fileName);
|
|
90
|
-
const linterEdits = linter.format(sourceFile);
|
|
91
|
-
if (linterEdits.length) {
|
|
92
|
-
const originalLength = sourceFile.text.length;
|
|
93
|
-
let text = sourceFile.text;
|
|
94
|
-
let formattingStart = start;
|
|
95
|
-
let formattingEnd = end;
|
|
96
|
-
for (const edit of linterEdits.sort((a, b) => (b.span.start + b.span.length) - (a.span.start + a.span.length))) {
|
|
97
|
-
text = text.slice(0, edit.span.start) + edit.newText + text.slice(edit.span.start + edit.span.length);
|
|
98
|
-
if (edit.span.start < start) {
|
|
99
|
-
formattingStart += edit.newText.length - edit.span.length;
|
|
100
|
-
}
|
|
101
|
-
if (edit.span.start + edit.span.length < end) {
|
|
102
|
-
formattingEnd += edit.newText.length - edit.span.length;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
formattingSnapshot = ts.ScriptSnapshot.fromString(text);
|
|
106
|
-
const serviceEdits = getFormattingEditsForRange(fileName, formattingStart, formattingEnd, options);
|
|
107
|
-
formattingSnapshot = undefined;
|
|
108
|
-
if (serviceEdits.length) {
|
|
109
|
-
for (const edit of serviceEdits.sort((a, b) => (b.span.start + b.span.length) - (a.span.start + a.span.length))) {
|
|
110
|
-
text = text.slice(0, edit.span.start) + edit.newText + text.slice(edit.span.start + edit.span.length);
|
|
111
|
-
}
|
|
112
|
-
return [{
|
|
113
|
-
span: { start: 0, length: originalLength },
|
|
114
|
-
newText: text,
|
|
115
|
-
}];
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
return linterEdits;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
debugger;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return getFormattingEditsForRange(fileName, start, end, options);
|
|
127
|
-
};
|
|
128
38
|
info.languageService.getSemanticDiagnostics = fileName => {
|
|
129
39
|
let result = getSemanticDiagnostics(fileName);
|
|
130
40
|
if (!isProjectFileName(fileName)) {
|
|
@@ -256,14 +166,19 @@ function decorateLanguageService(ts, projectRoot, info) {
|
|
|
256
166
|
initSourceMapSupport();
|
|
257
167
|
const mtime = ts.sys.getModifiedTime?.(builtConfig)?.getTime() ?? Date.now();
|
|
258
168
|
config = (await import(url.pathToFileURL(builtConfig).toString() + '?tsslint_time=' + mtime)).default;
|
|
259
|
-
linter = core.createLinter(projectContext, path.dirname(configFile), config,
|
|
169
|
+
linter = core.createLinter(projectContext, path.dirname(configFile), config, (diag, err, stackOffset) => {
|
|
170
|
+
const relatedInfo = createRelatedInformation(ts, err, stackOffset);
|
|
171
|
+
if (relatedInfo) {
|
|
172
|
+
diag.relatedInformation.push(relatedInfo);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
260
175
|
}
|
|
261
176
|
catch (err) {
|
|
262
177
|
config = undefined;
|
|
263
178
|
linter = undefined;
|
|
264
179
|
const prevLength = configFileDiagnostics.length;
|
|
265
180
|
if (err instanceof Error) {
|
|
266
|
-
const relatedInfo =
|
|
181
|
+
const relatedInfo = createRelatedInformation(ts, err, 0);
|
|
267
182
|
if (relatedInfo) {
|
|
268
183
|
configFileDiagnostics.push({
|
|
269
184
|
category: ts.DiagnosticCategory.Error,
|
|
@@ -340,5 +255,47 @@ function initSourceMapSupport() {
|
|
|
340
255
|
},
|
|
341
256
|
});
|
|
342
257
|
}
|
|
258
|
+
function createRelatedInformation(ts, err, stackOffset) {
|
|
259
|
+
const stacks = ErrorStackParser.parse(err);
|
|
260
|
+
if (stacks.length <= stackOffset) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const stack = stacks[stackOffset];
|
|
264
|
+
if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
|
|
265
|
+
let fileName = stack.fileName.replace(/\\/g, '/');
|
|
266
|
+
if (fileName.startsWith('file://')) {
|
|
267
|
+
fileName = fileName.substring('file://'.length);
|
|
268
|
+
}
|
|
269
|
+
if (fileName.includes('http-url:')) {
|
|
270
|
+
fileName = fileName.split('http-url:')[1];
|
|
271
|
+
}
|
|
272
|
+
const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0;
|
|
273
|
+
const lastMtime = fsFiles.get(fileName)?.[1];
|
|
274
|
+
if (mtime !== lastMtime) {
|
|
275
|
+
const text = ts.sys.readFile(fileName);
|
|
276
|
+
fsFiles.set(fileName, [
|
|
277
|
+
text !== undefined,
|
|
278
|
+
mtime,
|
|
279
|
+
ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
const [exist, _mtime, relatedFile] = fsFiles.get(fileName);
|
|
283
|
+
let pos = 0;
|
|
284
|
+
if (exist) {
|
|
285
|
+
try {
|
|
286
|
+
pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
|
|
287
|
+
}
|
|
288
|
+
catch { }
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
category: ts.DiagnosticCategory.Message,
|
|
292
|
+
code: 0,
|
|
293
|
+
file: relatedFile,
|
|
294
|
+
start: pos,
|
|
295
|
+
length: 0,
|
|
296
|
+
messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
343
300
|
module.exports = plugin;
|
|
344
301
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsslint/typescript-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
"directory": "packages/typescript-plugin"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@tsslint/core": "1.
|
|
15
|
+
"@tsslint/core": "1.6.1",
|
|
16
|
+
"error-stack-parser": "^2.1.4",
|
|
16
17
|
"source-map-support": "^0.5.21"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"@tsslint/config": "1.
|
|
20
|
+
"@tsslint/config": "1.6.1"
|
|
20
21
|
},
|
|
21
|
-
"gitHead": "
|
|
22
|
+
"gitHead": "875699df671c2d9a8331c930af8c549ebab19f17"
|
|
22
23
|
}
|