Skip to content

Commit ca7748c

Browse files
committed
refactor(core): handle error stack in typescript-plugin
1 parent 15681ab commit ca7748c

6 files changed

Lines changed: 62 additions & 62 deletions

File tree

packages/cli/lib/worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ async function setup(
212212
},
213213
path.dirname(configFile),
214214
config,
215-
'cli',
215+
() => { },
216216
linterSyntaxOnlyLanguageService
217217
);
218218

packages/core/index.ts

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import type {
1111
} from '@tsslint/types';
1212
import type * as ts from 'typescript';
1313

14-
import ErrorStackParser = require('error-stack-parser');
1514
import path = require('path');
1615
import minimatch = require('minimatch');
1716

@@ -30,7 +29,7 @@ export function createLinter(
3029
ctx: ProjectContext,
3130
rootDir: string,
3231
config: Config | Config[],
33-
mode: 'cli' | 'typescript-plugin',
32+
handleError: (diag: ts.DiagnosticWithLocation, err: Error, stackOffset: number) => void,
3433
syntaxOnlyLanguageService?: ts.LanguageService & {
3534
getNonBoundSourceFile?(fileName: string): ts.SourceFile;
3635
}
@@ -227,12 +226,8 @@ export function createLinter(
227226
});
228227
}
229228

230-
if (mode === 'typescript-plugin' && typeof stackOffset === 'number') {
231-
err ??= new Error();
232-
const relatedInfo = createRelatedInformation(ts, err, stackOffset);
233-
if (relatedInfo) {
234-
error.relatedInformation!.push(relatedInfo);
235-
}
229+
if (typeof stackOffset === 'number') {
230+
handleError(error, err ?? new Error(), stackOffset);
236231
}
237232

238233
let lintResult = lintResults.get(fileName);
@@ -442,53 +437,6 @@ export function createLinter(
442437
}
443438
}
444439

445-
const fsFiles = new Map<string, [exist: boolean, mtime: number, ts.SourceFile]>();
446-
447-
export function createRelatedInformation(ts: typeof import('typescript'), err: Error, stackOffset: number): ts.DiagnosticRelatedInformation | undefined {
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(
466-
fileName,
467-
[
468-
text !== undefined,
469-
mtime,
470-
ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
471-
]
472-
);
473-
}
474-
const [exist, _mtime, relatedFile] = fsFiles.get(fileName)!;
475-
let pos = 0;
476-
if (exist) {
477-
try {
478-
pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
479-
} catch { }
480-
}
481-
return {
482-
category: ts.DiagnosticCategory.Message,
483-
code: 0,
484-
file: relatedFile,
485-
start: pos,
486-
length: 0,
487-
messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
488-
};
489-
}
490-
}
491-
492440
export function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]) {
493441

494442
const changes = fixes

packages/core/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
},
1414
"dependencies": {
1515
"@tsslint/types": "1.5.18",
16-
"error-stack-parser": "^2.1.4",
1716
"esbuild": ">=0.17.0",
1817
"minimatch": "^10.0.1"
1918
},

packages/typescript-plugin/index.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import core = require('@tsslint/core');
55
import path = require('path');
66
import url = require('url');
77
import fs = require('fs');
8+
import ErrorStackParser = require('error-stack-parser');
89

910
const languageServiceDecorators = new WeakMap<ts.server.Project, ReturnType<typeof decorateLanguageService>>();
1011
const plugin: ts.server.PluginModuleFactory = modules => {
@@ -27,6 +28,7 @@ const plugin: ts.server.PluginModuleFactory = modules => {
2728
};
2829
return pluginModule;
2930
};
31+
const fsFiles = new Map<string, [exist: boolean, mtime: number, ts.SourceFile]>();
3032

3133
export = plugin;
3234

@@ -192,13 +194,18 @@ function decorateLanguageService(
192194
initSourceMapSupport();
193195
const mtime = ts.sys.getModifiedTime?.(builtConfig)?.getTime() ?? Date.now();
194196
config = (await import(url.pathToFileURL(builtConfig).toString() + '?tsslint_time=' + mtime)).default;
195-
linter = core.createLinter(projectContext, path.dirname(configFile!), config!, 'typescript-plugin');
197+
linter = core.createLinter(projectContext, path.dirname(configFile!), config!, (diag, err, stackOffset) => {
198+
const relatedInfo = createRelatedInformation(ts, err, stackOffset);
199+
if (relatedInfo) {
200+
diag.relatedInformation!.push(relatedInfo);
201+
}
202+
});
196203
} catch (err) {
197204
config = undefined;
198205
linter = undefined;
199206
const prevLength = configFileDiagnostics.length;
200207
if (err instanceof Error) {
201-
const relatedInfo = core.createRelatedInformation(ts, err, 0);
208+
const relatedInfo = createRelatedInformation(ts, err, 0);
202209
if (relatedInfo) {
203210
configFileDiagnostics.push({
204211
category: ts.DiagnosticCategory.Error,
@@ -278,3 +285,48 @@ function initSourceMapSupport() {
278285
},
279286
});
280287
}
288+
289+
function createRelatedInformation(ts: typeof import('typescript'), err: Error, stackOffset: number): ts.DiagnosticRelatedInformation | undefined {
290+
const stacks = ErrorStackParser.parse(err);
291+
if (stacks.length <= stackOffset) {
292+
return;
293+
}
294+
const stack = stacks[stackOffset];
295+
if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) {
296+
let fileName = stack.fileName.replace(/\\/g, '/');
297+
if (fileName.startsWith('file://')) {
298+
fileName = fileName.substring('file://'.length);
299+
}
300+
if (fileName.includes('http-url:')) {
301+
fileName = fileName.split('http-url:')[1];
302+
}
303+
const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0;
304+
const lastMtime = fsFiles.get(fileName)?.[1];
305+
if (mtime !== lastMtime) {
306+
const text = ts.sys.readFile(fileName);
307+
fsFiles.set(
308+
fileName,
309+
[
310+
text !== undefined,
311+
mtime,
312+
ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true)
313+
]
314+
);
315+
}
316+
const [exist, _mtime, relatedFile] = fsFiles.get(fileName)!;
317+
let pos = 0;
318+
if (exist) {
319+
try {
320+
pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0;
321+
} catch { }
322+
}
323+
return {
324+
category: ts.DiagnosticCategory.Message,
325+
code: 0,
326+
file: relatedFile,
327+
start: pos,
328+
length: 0,
329+
messageText: 'at ' + (stack.functionName ?? '<anonymous>'),
330+
};
331+
}
332+
}

packages/typescript-plugin/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"dependencies": {
1515
"@tsslint/core": "1.5.18",
16+
"error-stack-parser": "^2.1.4",
1617
"source-map-support": "^0.5.21"
1718
},
1819
"devDependencies": {

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)