Skip to content

Commit

Permalink
(feat) better error diagnostics
Browse files Browse the repository at this point in the history
Narrow down in which step the error occurs and try to extract better error messages.

sveltejs#86, sveltejs#232, sveltejs#129
  • Loading branch information
Simon Holthausen committed Jun 28, 2020
1 parent d3f83b0 commit 0988a5d
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 135 deletions.
31 changes: 23 additions & 8 deletions packages/language-server/src/plugins/svelte/SvelteDocument.ts
Expand Up @@ -23,6 +23,11 @@ export interface SvelteConfig extends CompileOptions {
preprocess?: PreprocessorGroup;
}

export enum TranspileErrorSource {
Script = 'Script',
Style = 'Style',
}

/**
* Represents a text document that contains a svelte component.
*/
Expand Down Expand Up @@ -272,21 +277,31 @@ async function transpile(document: Document, preprocessors: PreprocessorGroup =

if (preprocessors.script) {
preprocessor.script = async (args: any) => {
const res = await preprocessors.script!(args);
if (res && res.map) {
processedScript = res;
try {
const res = await preprocessors.script!(args);
if (res && res.map) {
processedScript = res;
}
return res;
} catch (e) {
e.__source = TranspileErrorSource.Script;
throw e;
}
return res;
};
}

if (preprocessors.style) {
preprocessor.style = async (args: any) => {
const res = await preprocessors.style!(args);
if (res && res.map) {
processedStyle = res;
try {
const res = await preprocessors.style!(args);
if (res && res.map) {
processedStyle = res;
}
return res;
} catch (e) {
e.__source = TranspileErrorSource.Style;
throw e;
}
return res;
};
}

Expand Down
159 changes: 32 additions & 127 deletions packages/language-server/src/plugins/svelte/SveltePlugin.ts
@@ -1,31 +1,31 @@
import { cosmiconfig } from 'cosmiconfig';
import { CompileOptions, Warning } from 'svelte/types/compiler/interfaces';
import { CompileOptions } from 'svelte/types/compiler/interfaces';
import {
CodeAction,
CodeActionContext,
CompletionList,
Diagnostic,
DiagnosticSeverity,
Hover,
Position,
Range,
TextEdit,
CodeActionContext,
CodeAction,
} from 'vscode-languageserver';
import { Document, isInTag, mapDiagnosticToOriginal } from '../../lib/documents';
import { Document } from '../../lib/documents';
import { Logger } from '../../logger';
import { LSConfigManager, LSSvelteConfig } from '../../ls-config';
import { importPrettier, importSveltePreprocess } from '../importPackage';
import {
CodeActionsProvider,
CompletionsProvider,
DiagnosticsProvider,
FormattingProvider,
HoverProvider,
CodeActionsProvider,
} from '../interfaces';
import { getCodeActions } from './features/getCodeActions';
import { getCompletions } from './features/getCompletions';
import { getDiagnostics } from './features/getDiagnostics';
import { getHoverInfo } from './features/getHoverInfo';
import { SvelteDocument, SvelteConfig, SvelteCompileResult } from './SvelteDocument';
import { Logger } from '../../logger';
import { getCodeActions } from './features/getCodeActions';
import { SvelteCompileResult, SvelteConfig, SvelteDocument } from './SvelteDocument';

const DEFAULT_OPTIONS: CompileOptions = {
dev: true,
Expand All @@ -34,10 +34,6 @@ const DEFAULT_OPTIONS: CompileOptions = {
const NO_GENERATE: CompileOptions = {
generate: false,
};

const scssNodeRuntimeHint =
'If you use SCSS, it may be necessary to add the path to your NODE runtime to the setting `svelte.language-server.runtime`, or use `sass` instead of `node-sass`. ';

export class SveltePlugin
implements
DiagnosticsProvider,
Expand All @@ -58,63 +54,7 @@ export class SveltePlugin
return [];
}

try {
return await this.tryGetDiagnostics(document);
} catch (error) {
Logger.error('Preprocessing failed');
Logger.error(error);
// Preprocessing could fail if packages like less/sass/babel cannot be resolved
// when our fallback-version of svelte-preprocess is used.
// Add a warning about a broken svelte.configs.js/preprocessor setup
// Also add svelte-preprocess error message.
const errorMsg =
error instanceof Error && error.message.startsWith('Cannot find any of modules')
? error.message + '. '
: '';
const hint =
error instanceof Error && error.message.includes('node-sass')
? scssNodeRuntimeHint
: '';
return [
{
message:
errorMsg +
"The file cannot be parsed because script or style require a preprocessor that doesn't seem to be setup. " +
'Did you setup a `svelte.config.js`? ' +
hint +
'See https://github.com/sveltejs/language-tools/tree/master/packages/svelte-vscode#using-with-preprocessors for more info.',
range: Range.create(Position.create(0, 0), Position.create(0, 5)),
severity: DiagnosticSeverity.Warning,
source: 'svelte',
},
];
}
}

private async tryGetDiagnostics(document: Document): Promise<Diagnostic[]> {
const svelteDoc = await this.getSvelteDoc(document);
const transpiled = await svelteDoc.getTranspiled();

try {
const res = await svelteDoc.getCompiled();
return (((res.stats as any).warnings || res.warnings || []) as Warning[])
.map((warning) => {
const start = warning.start || { line: 1, column: 0 };
const end = warning.end || start;
return {
range: Range.create(start.line - 1, start.column, end.line - 1, end.column),
message: warning.message,
severity: DiagnosticSeverity.Warning,
source: 'svelte',
code: warning.code,
};
})
.map((diag) => mapDiagnosticToOriginal(transpiled, diag));
} catch (err) {
return (await this.createParserErrorDiagnostic(err, document)).map((diag) =>
mapDiagnosticToOriginal(transpiled, diag),
);
}
return getDiagnostics(document, await this.getSvelteDoc(document));
}

async getCompiledResult(document: Document): Promise<SvelteCompileResult | null> {
Expand All @@ -126,63 +66,6 @@ export class SveltePlugin
}
}

private async createParserErrorDiagnostic(error: any, document: Document) {
const start = error.start || { line: 1, column: 0 };
const end = error.end || start;
const diagnostic: Diagnostic = {
range: Range.create(start.line - 1, start.column, end.line - 1, end.column),
message: error.message,
severity: DiagnosticSeverity.Error,
source: 'svelte',
code: error.code,
};

if (diagnostic.message.includes('expected')) {
const isInStyle = isInTag(diagnostic.range.start, document.styleInfo);
const isInScript = isInTag(diagnostic.range.start, document.scriptInfo);

if (isInStyle || isInScript) {
diagnostic.message +=
'. If you expect this syntax to work, here are some suggestions: ';
if (isInScript) {
diagnostic.message +=
'If you use typescript with `svelte-preprocessor`, did you add `lang="typescript"` to your `script` tag? ';
} else {
diagnostic.message +=
'If you use less/SCSS with `svelte-preprocessor`, did you add `lang="scss"`/`lang="less"` to you `style` tag? ' +
scssNodeRuntimeHint;
}
diagnostic.message +=
'Did you setup a `svelte.config.js`? ' +
'See https://github.com/sveltejs/language-tools/tree/master/packages/svelte-vscode#using-with-preprocessors for more info.';
}
}

return [diagnostic];
}

private useFallbackPreprocessor(document: Document, foundConfig: boolean) {
if (
document.styleInfo?.attributes.lang ||
document.styleInfo?.attributes.type ||
document.scriptInfo?.attributes.lang ||
document.scriptInfo?.attributes.type
) {
Logger.log(
(foundConfig
? 'Found svelte.config.js but there was an error loading it. '
: 'No svelte.config.js found but one is needed. ') +
'Using https://github.com/sveltejs/svelte-preprocess as fallback',
);
return {
preprocess: importSveltePreprocess(document.getFilePath() || '')({
typescript: { transpileOnly: true },
}),
};
}
return {};
}

async formatDocument(document: Document): Promise<TextEdit[]> {
if (!this.featureEnabled('format')) {
return [];
Expand Down Expand Up @@ -277,4 +160,26 @@ export class SveltePlugin
};
}
}

private useFallbackPreprocessor(document: Document, foundConfig: boolean) {
if (
document.styleInfo?.attributes.lang ||
document.styleInfo?.attributes.type ||
document.scriptInfo?.attributes.lang ||
document.scriptInfo?.attributes.type
) {
Logger.log(
(foundConfig
? 'Found svelte.config.js but there was an error loading it. '
: 'No svelte.config.js found but one is needed. ') +
'Using https://github.com/sveltejs/svelte-preprocess as fallback',
);
return {
preprocess: importSveltePreprocess(document.getFilePath() || '')({
typescript: { transpileOnly: true },
}),
};
}
return {};
}
}

0 comments on commit 0988a5d

Please sign in to comment.