Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/heavy-radios-lose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-check': patch
---

fix: report diagnostics in tsconfig.json
5 changes: 5 additions & 0 deletions .changeset/two-cups-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-language-server': patch
---

fix: provide tsconfig.json diagnostics for svelte-check
60 changes: 43 additions & 17 deletions packages/language-server/src/svelte-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
import { isInGeneratedCode } from './plugins/typescript/features/utils';
import { mapAndFilterDiagnostics } from './plugins/typescript/features/DiagnosticsProvider';
import { convertRange, getDiagnosticTag, mapSeverity } from './plugins/typescript/utils';
import { pathToUrl, urlToPath } from './utils';
import { normalizePath, pathToUrl, urlToPath } from './utils';
import { groupBy } from 'lodash';

export function mapSvelteCheckDiagnostics(
Expand Down Expand Up @@ -226,6 +226,7 @@ export class SvelteCheck {

private async getDiagnosticsForTsconfig(tsconfigPath: string) {
const lsContainer = await this.getLSContainer(tsconfigPath);
const normalizedTsconfigPath = normalizePath(tsconfigPath);
const map = (diagnostic: ts.Diagnostic, range?: Range): Diagnostic => {
const file = diagnostic.file;
range ??= file
Expand All @@ -241,23 +242,34 @@ export class SvelteCheck {
source: diagnostic.source,
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
code: diagnostic.code,
tags: getDiagnosticTag(diagnostic)
tags: getDiagnosticTag(diagnostic),
data: {
positionUnknown: !diagnostic.start || !diagnostic.length
}
};
};

if (
lsContainer.configErrors.some((error) => error.category === ts.DiagnosticCategory.Error)
) {
return reportConfigError();
const isErrorCategory = (diagnostic: ts.Diagnostic) =>
diagnostic.category === ts.DiagnosticCategory.Error;

if (lsContainer.configErrors.some(isErrorCategory)) {
return reportConfigError(lsContainer.configErrors);
}

const lang = lsContainer.getService();
if (
lsContainer.configErrors.some((error) => error.category === ts.DiagnosticCategory.Error)
) {
return reportConfigError();
if (lsContainer.configErrors.some(isErrorCategory)) {
return reportConfigError(lsContainer.configErrors);
}

const program = lang.getProgram();
const globalOrConfigFileDiagnostics = program
? [...program.getGlobalDiagnostics(), ...program.getOptionsDiagnostics()]
: [];
// TODO: enable this in svelte-check v5. For now, we report these as warnings along with other diagnostics.
// if (globalOrConfigFileDiagnostics.some(isErrorCategory)) {
// return reportConfigError(globalOrConfigFileDiagnostics);
// }

const files = lang.getProgram()?.getSourceFiles() || [];
const options = lang.getProgram()?.getCompilerOptions() || {};

Expand Down Expand Up @@ -356,22 +368,36 @@ export class SvelteCheck {
})
);

if (lsContainer.configErrors.length) {
diagnostics.push(...reportConfigError());
const configErrors = lsContainer.configErrors
// TODO: remove this in svelte-check v5.
.concat(
globalOrConfigFileDiagnostics.map((diagnostic) => ({
...diagnostic,
category:
diagnostic.category === ts.DiagnosticCategory.Error
? ts.DiagnosticCategory.Warning
: diagnostic.category
}))
);
if (configErrors.length) {
diagnostics.push(...reportConfigError(configErrors));
}

return diagnostics;

function reportConfigError() {
function reportConfigError(errors: readonly ts.Diagnostic[]) {
const grouped = groupBy(
lsContainer.configErrors,
(error) => error.file?.fileName ?? tsconfigPath
errors,
(error) => error.file?.fileName ?? normalizedTsconfigPath
);
const lspDiagnostics = errors.map((diagnostic) => map(diagnostic));

return Object.entries(grouped).map(([filePath, errors]) => ({
filePath,
text: '',
diagnostics: errors.map((diagnostic) => map(diagnostic))
text: lspDiagnostics.some((diagnostic) => diagnostic.data?.positionUnknown)
? (ts.sys?.readFile(filePath) ?? '')
: '',
diagnostics: lspDiagnostics
}));
}
}
Expand Down
45 changes: 29 additions & 16 deletions packages/svelte-check/src/incremental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export type EmitResult = {
};

export type ParsedDiagnostic = {
filePath: string;
filePath: string | null;
line: number;
character: number;
/** Span length in characters, parsed from tsc pretty-output ~~ underlines */
Expand Down Expand Up @@ -548,7 +548,8 @@ export function runTypeScriptDiagnostics(
*/
export function mapCliDiagnosticsToLsp(
diagnostics: ParsedDiagnostic[],
emitResult: EmitResult
emitResult: EmitResult,
tsconfigPath: string
): Array<{ filePath: string; text: string; diagnostics: Diagnostic[] }> {
const entryByOutPath = new Map(
emitResult.entries.map((entry) => [path.normalize(entry.outPath), entry])
Expand All @@ -561,7 +562,8 @@ export function mapCliDiagnosticsToLsp(

const diagnosticsByFile = new Map<string, ParsedDiagnostic[]>();
for (const diagnostic of diagnostics) {
const key = path.normalize(diagnostic.filePath);
const filePath = diagnostic.filePath ?? tsconfigPath;
const key = filePath ? path.normalize(filePath) : '';
// Even though we try to exclude +page.js etc files that had code inserted (due to SvelteKit's zero types feature)
// we might still have them included through code in .svelte-kit/types importing them. So we exclude the diagnostics for these.
if (excludedSourcePaths.has(key)) {
Expand Down Expand Up @@ -662,7 +664,10 @@ export function mapCliDiagnosticsToLsp(
severity: diag.severity,
code: diag.code,
message: diag.message,
source
source,
data: {
positionUnknown: diag.filePath === null
}
}));

results.set(filePath, {
Expand Down Expand Up @@ -704,7 +709,7 @@ function parseDiagnostics(output: string, baseDir: string): ParsedDiagnostic[] {
const diagnostics: ParsedDiagnostic[] = [];
const lines = clean.split(/\r?\n/);
// Pretty format: file.ts:5:10 - error TS2322: message
const headerRegex = /^(.+):(\d+):(\d+) - (error|warning) TS(\d+): (.*)$/;
const headerRegex = /^((.+):(\d+):(\d+) - )?(error|warning) TS(\d+): (.*)$/;
// Tilde underline: optional leading whitespace followed by one or more tildes
const tildeRegex = /^(\s*)(~+)\s*$/;

Expand All @@ -713,23 +718,31 @@ function parseDiagnostics(output: string, baseDir: string): ParsedDiagnostic[] {
if (!match) {
continue;
}
const [, filePath, lineStr, colStr, severity, codeStr, message] = match;
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(baseDir, filePath);
const [, , filePath, lineStr = '0', colStr = '0', severity, codeStr, message] = match;
const resolvedPath = filePath
? path.isAbsolute(filePath)
? filePath
: path.resolve(baseDir, filePath)
: null;
const lineNum = Math.max(0, Number(lineStr) - 1);
const colNum = Math.max(0, Number(colStr) - 1);

// Look ahead (up to 4 lines) for a ~~ underline to determine span length.
// The underline appears after the source context line in pretty output.
let length = 1;
for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
const tildeMatch = tildeRegex.exec(lines[j]);
if (tildeMatch) {
length = tildeMatch[2].length;
break;
}
// Stop looking if we hit another diagnostic header
if (headerRegex.test(lines[j].trim())) {
break;
// No file path, so no source context line.
if (filePath) {
for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
const tildeMatch = tildeRegex.exec(lines[j]);
if (tildeMatch) {
length = tildeMatch[2].length;
break;
}

// Stop looking if we hit another diagnostic header
if (headerRegex.test(lines[j].trim())) {
break;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/svelte-check/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ async function runWithVirtualFiles(
opts.incremental,
opts.workspaceUri.fsPath
),
emitResult
emitResult,
opts.tsconfig
);

const {
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte-check/src/writers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class HumanFriendlyWriter implements Writer {
}

private formatRelatedCode(diagnostic: Diagnostic, text: string) {
if (!text) {
if (!text || diagnostic.data?.positionUnknown) {
return '';
}

Expand Down
Loading