Skip to content

Commit a8d19a8

Browse files
authored
(fix) better css error diagnostics (#1009)
- Place error at start of script if coming from another file (#976) - Add preprocessing wrapping for new TranspiledSvelteDocument for better error messages
1 parent 472c364 commit a8d19a8

File tree

3 files changed

+67
-12
lines changed

3 files changed

+67
-12
lines changed

packages/language-server/src/plugins/svelte/SvelteDocument.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
TagInformation
1919
} from '../../lib/documents';
2020
import { SvelteConfig } from '../../lib/documents/configLoader';
21-
import { isNotNullOrUndefined } from '../../utils';
21+
import { getLastPartOfPath, isNotNullOrUndefined } from '../../utils';
2222

2323
export type SvelteCompileResult = ReturnType<typeof compile>;
2424

@@ -125,9 +125,13 @@ export class TranspiledSvelteDocument implements ITranspiledSvelteDocument {
125125

126126
const filename = document.getFilePath() || '';
127127
const svelte = importSvelte(filename);
128-
const preprocessed = await svelte.preprocess(document.getText(), config?.preprocess || [], {
129-
filename
130-
});
128+
const preprocessed = await svelte.preprocess(
129+
document.getText(),
130+
wrapPreprocessors(config?.preprocess),
131+
{
132+
filename
133+
}
134+
);
131135

132136
if (preprocessed.code === document.getText()) {
133137
return new TranspiledSvelteDocument(document.getText());
@@ -141,7 +145,7 @@ export class TranspiledSvelteDocument implements ITranspiledSvelteDocument {
141145
// The "sources" array only contains the Svelte filename, not its path.
142146
// For getting generated positions, the sourcemap consumer wants an exact match
143147
// of the source filepath. Therefore only pass in the filename here.
144-
filename.replace(/\\/g, '/').split('/').pop() || ''
148+
getLastPartOfPath(filename)
145149
)
146150
: undefined
147151
);
@@ -394,6 +398,40 @@ export class SvelteFragmentMapper implements PositionMapper {
394398
}
395399
}
396400

401+
/**
402+
* Wrap preprocessors and rethrow on errors with more info on where the error came from.
403+
*/
404+
function wrapPreprocessors(preprocessors: PreprocessorGroup | PreprocessorGroup[] = []) {
405+
preprocessors = Array.isArray(preprocessors) ? preprocessors : [preprocessors];
406+
return preprocessors.map((preprocessor) => {
407+
const wrappedPreprocessor: PreprocessorGroup = { markup: preprocessor.markup };
408+
409+
if (preprocessor.script) {
410+
wrappedPreprocessor.script = async (args: any) => {
411+
try {
412+
return await preprocessor.script!(args);
413+
} catch (e) {
414+
e.__source = TranspileErrorSource.Script;
415+
throw e;
416+
}
417+
};
418+
}
419+
420+
if (preprocessor.style) {
421+
wrappedPreprocessor.style = async (args: any) => {
422+
try {
423+
return await preprocessor.style!(args);
424+
} catch (e) {
425+
e.__source = TranspileErrorSource.Style;
426+
throw e;
427+
}
428+
};
429+
}
430+
431+
return wrappedPreprocessor;
432+
});
433+
}
434+
397435
async function transpile(
398436
document: Document,
399437
preprocessors: PreprocessorGroup | PreprocessorGroup[] = []

packages/language-server/src/plugins/svelte/features/getDiagnostics.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Diagnostic, DiagnosticSeverity, Position, Range } from 'vscode-language
33
import { Document, isInTag, mapObjWithRangeToOriginal } from '../../../lib/documents';
44
import { Logger } from '../../../logger';
55
import { CompilerWarningsSettings } from '../../../ls-config';
6+
import { getLastPartOfPath } from '../../../utils';
67
import { SvelteDocument, TranspileErrorSource } from '../SvelteDocument';
78

89
/**
@@ -140,6 +141,12 @@ function getConfigLoadErrorDiagnostics(error: any): Diagnostic[] {
140141
* Try to infer a nice diagnostic error message from the transpilation error.
141142
*/
142143
function getStyleErrorDiagnostics(error: any, document: Document): Diagnostic[] {
144+
// Error could be from another file that was mixed into the Svelte file as part of preprocessing.
145+
// Some preprocessors set the file property from which we can infer that
146+
const isErrorFromOtherFile =
147+
typeof error?.file === 'string' &&
148+
getLastPartOfPath(error.file) !== getLastPartOfPath(document.getFilePath() || '');
149+
143150
return [
144151
{
145152
message: getStyleErrorMessage(),
@@ -155,20 +162,22 @@ function getStyleErrorDiagnostics(error: any, document: Document): Diagnostic[]
155162
return getErrorMessage(error.message, 'style', hint);
156163
}
157164

158-
return (
165+
const msg =
159166
error.formatted /* sass error messages have this */ ||
160167
error.message ||
161-
'Style error. Transpilation failed.'
162-
);
168+
'Style error. Transpilation failed.';
169+
return isErrorFromOtherFile ? 'Error in referenced file\n\n' + msg : msg;
163170
}
164171

165172
function getStyleErrorRange() {
166173
const lineOffset = document.styleInfo?.startPos.line || 0;
167174
const position =
168-
typeof error?.column === 'number' && typeof error?.line === 'number'
169-
? // Some preprocessors like sass or less return error objects with these attributes.
170-
// Use it to display a nice error message.
171-
Position.create(lineOffset + error.line - 1, error.column)
175+
!isErrorFromOtherFile &&
176+
// Some preprocessors like sass or less return error objects with these attributes.
177+
// Use it to display message at better position.
178+
typeof error?.column === 'number' &&
179+
typeof error?.line === 'number'
180+
? Position.create(lineOffset + error.line - 1, error.column)
172181
: document.styleInfo?.startPos || Position.create(0, 0);
173182
return Range.create(position, position);
174183
}

packages/language-server/src/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ export function normalizeUri(uri: string): string {
2626
return URI.parse(uri).toString();
2727
}
2828

29+
/**
30+
* Given a path like foo/bar or foo/bar.svelte , returns its last path
31+
* (bar or bar.svelte in this example).
32+
*/
33+
export function getLastPartOfPath(path: string): string {
34+
return path.replace(/\\/g, '/').split('/').pop() || '';
35+
}
36+
2937
export function flatten<T>(arr: T[][]): T[] {
3038
return arr.reduce((all, item) => [...all, ...item], []);
3139
}

0 commit comments

Comments
 (0)