UNPKG

svelte-language-server

Version:
284 lines 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDiagnostics = getDiagnostics; const vscode_languageserver_1 = require("vscode-languageserver"); const documents_1 = require("../../../lib/documents"); const logger_1 = require("../../../logger"); const utils_1 = require("../../../utils"); const SvelteDocument_1 = require("../SvelteDocument"); /** * Returns diagnostics from the svelte compiler. * Also tries to return errors at correct position if transpilation/preprocessing fails. */ async function getDiagnostics(document, svelteDoc, settings, cancellationToken) { const config = await svelteDoc.config; if (config?.loadConfigError) { return getConfigLoadErrorDiagnostics(config.loadConfigError); } if (cancellationToken?.isCancellationRequested) { return []; } try { return await tryGetDiagnostics(document, svelteDoc, settings, cancellationToken); } catch (error) { return getPreprocessErrorDiagnostics(document, error); } } /** * Try to transpile and compile the svelte file and return diagnostics. */ async function tryGetDiagnostics(document, svelteDoc, settings, cancellationToken) { const transpiled = await svelteDoc.getTranspiled(); if (cancellationToken?.isCancellationRequested) { return []; } try { const res = await svelteDoc.getCompiled(); if (cancellationToken?.isCancellationRequested) { return []; } let ignoreScriptWarnings = false; let ignoreStyleWarnings = false; let ignoreTemplateWarnings = false; if (!document.config?.preprocess || !!document.config.isFallbackConfig) { ignoreTemplateWarnings = !!document.getLanguageAttribute('template'); ignoreStyleWarnings = !!document.getLanguageAttribute('style'); const scriptAttr = document.getLanguageAttribute('script'); ignoreScriptWarnings = !!scriptAttr && scriptAttr !== 'ts'; } return (res.warnings || []) .filter((warning) => settings[warning.code] !== 'ignore') .map((warning) => { const start = warning.start || { line: 1, column: 0 }; const end = warning.end || start; return { range: vscode_languageserver_1.Range.create(start.line - 1, start.column, end.line - 1, end.column), message: warning.message, severity: settings[warning.code] === 'error' ? vscode_languageserver_1.DiagnosticSeverity.Error : vscode_languageserver_1.DiagnosticSeverity.Warning, source: 'svelte', code: warning.code }; }) .map((diag) => (0, documents_1.mapObjWithRangeToOriginal)(transpiled, diag)) .map((diag) => adjustMappings(diag, document)) .filter((diag) => isNoFalsePositive(diag, document, ignoreScriptWarnings, ignoreStyleWarnings, ignoreTemplateWarnings)); } catch (err) { return createParserErrorDiagnostic(err, document) .map((diag) => (0, documents_1.mapObjWithRangeToOriginal)(transpiled, diag)) .map((diag) => adjustMappings(diag, document)); } } /** * Try to infer a nice diagnostic error message from the compilation error. */ function createParserErrorDiagnostic(error, document) { const start = error.start || { line: 1, column: 0 }; const end = error.end || start; const diagnostic = { range: vscode_languageserver_1.Range.create(start.line - 1, start.column, end.line - 1, end.column), message: error.message, severity: vscode_languageserver_1.DiagnosticSeverity.Error, source: 'svelte', code: error.code }; if (diagnostic.message.includes('expected')) { const isInStyle = (0, documents_1.isInTag)(diagnostic.range.start, document.styleInfo); const isInScript = (0, documents_1.isInTag)(diagnostic.range.start, document.scriptInfo || document.moduleScriptInfo); if ((!document.config?.preprocess || document.config.isFallbackConfig) && document.hasLanguageAttribute()) { logger_1.Logger.error(`Parsing ${document.getFilePath()} failed. No preprocess config found but lang tag exists. Skip showing error because they likely use other preprocessors.`); return []; } if (isInStyle || isInScript) { diagnostic.message += '\n\nIf you expect this syntax to work, here are some suggestions: '; if (isInScript) { diagnostic.message += '\nIf you use typescript with `svelte-preprocess`, did you add `lang="ts"` to your `script` tag? '; } else { diagnostic.message += '\nIf you use less/SCSS with `svelte-preprocess`, did you add `lang="scss"`/`lang="less"` to your `style` tag? ' + scssNodeRuntimeHint; } diagnostic.message += '\nDid you setup a `svelte.config.js`? ' + '\nSee https://github.com/sveltejs/language-tools/tree/master/docs#using-with-preprocessors for more info.'; } } return [diagnostic]; } /** * Try to infer a nice diagnostic error message from the transpilation error. */ function getPreprocessErrorDiagnostics(document, error) { logger_1.Logger.error('Preprocessing failed'); logger_1.Logger.error(error); if (document.styleInfo && error.__source === SvelteDocument_1.TranspileErrorSource.Style) { return getStyleErrorDiagnostics(error, document); } if ((document.scriptInfo || document.moduleScriptInfo) && error.__source === SvelteDocument_1.TranspileErrorSource.Script) { return getScriptErrorDiagnostics(error, document); } return getOtherErrorDiagnostics(error); } function getConfigLoadErrorDiagnostics(error) { return [ { message: 'Error in svelte.config.js\n\n' + error, range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 5)), severity: vscode_languageserver_1.DiagnosticSeverity.Error, source: 'svelte' } ]; } /** * Try to infer a nice diagnostic error message from the transpilation error. */ function getStyleErrorDiagnostics(error, document) { // Error could be from another file that was mixed into the Svelte file as part of preprocessing. // Some preprocessors set the file property from which we can infer that const isErrorFromOtherFile = typeof error?.file === 'string' && (0, utils_1.getLastPartOfPath)(error.file) !== (0, utils_1.getLastPartOfPath)(document.getFilePath() || ''); return [ { message: getStyleErrorMessage(), range: getStyleErrorRange(), severity: vscode_languageserver_1.DiagnosticSeverity.Error, source: 'svelte(style)' } ]; function getStyleErrorMessage() { if (isSveltePreprocessCannotFindModulesError(error)) { const hint = error.message.includes('node-sass') ? scssNodeRuntimeHint : ''; return getErrorMessage(error.message, 'style', hint); } const msg = error.formatted /* sass error messages have this */ || error.message || 'Style error. Transpilation failed.'; return isErrorFromOtherFile ? 'Error in referenced file\n\n' + msg : msg; } function getStyleErrorRange() { const lineOffset = document.styleInfo?.startPos.line || 0; const position = !isErrorFromOtherFile && // Some preprocessors like sass or less return error objects with these attributes. // Use it to display message at better position. typeof error?.column === 'number' && typeof error?.line === 'number' ? vscode_languageserver_1.Position.create(lineOffset + error.line - 1, error.column) : document.styleInfo?.startPos || vscode_languageserver_1.Position.create(0, 0); return vscode_languageserver_1.Range.create(position, position); } } /** * Try to infer a nice diagnostic error message from the transpilation error. */ function getScriptErrorDiagnostics(error, document) { return [ { message: getScriptErrorMessage(), range: getScriptErrorRange(), severity: vscode_languageserver_1.DiagnosticSeverity.Error, source: 'svelte(script)' } ]; function getScriptErrorMessage() { if (isSveltePreprocessCannotFindModulesError(error)) { return getErrorMessage(error.message, 'script'); } return error.message || 'Script error. Transpilation failed.'; } function getScriptErrorRange() { const position = document.scriptInfo?.startPos || document.moduleScriptInfo?.startPos || vscode_languageserver_1.Position.create(0, 0); return vscode_languageserver_1.Range.create(position, position); } } /** * Try to infer a nice diagnostic error message from the transpilation error. */ function getOtherErrorDiagnostics(error) { return [ { message: getOtherErrorMessage(), range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 5)), severity: vscode_languageserver_1.DiagnosticSeverity.Warning, source: 'svelte' } ]; function getOtherErrorMessage() { if (isSveltePreprocessCannotFindModulesError(error)) { return getErrorMessage(error.message, 'it'); } return error.message || 'Error. Transpilation failed.'; } } /** * Preprocessing could fail if packages cannot be resolved. * A warning about a broken svelte.configs.js/preprocessor setup should be added then. */ function isSveltePreprocessCannotFindModulesError(error) { return error instanceof Error && error.message.startsWith('Cannot find any of modules'); } function getErrorMessage(error, source, hint = '') { return (error + '\n\nThe file cannot be parsed because ' + source + " requires a preprocessor that doesn't seem to be setup or failed during setup. " + 'Did you setup a `svelte.config.js`? ' + hint + '\n\nSee https://github.com/sveltejs/language-tools/tree/master/docs#using-with-preprocessors for more info.'); } function isNoFalsePositive(diag, doc, ignoreScriptWarnings, ignoreStyleWarnings, ignoreTemplateWarnings) { if ((ignoreTemplateWarnings || ignoreScriptWarnings) && (typeof diag.code !== 'string' || !diag.code.startsWith('a11y'))) { return false; } if (ignoreStyleWarnings && (diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector')) { return false; } if (diag.code !== 'unused-export-let' && diag.code !== 'export_let_unused') { return true; } // TypeScript transpiles `export enum A` and `export namespace A` to `export var A`, // which the compiler will warn about. // Silence this edge case. We extract the property from the message and don't use the position // because that position could be wrong when source mapping trips up. const unusedExportName = diag.message.substring(diag.message.indexOf("'") + 1, diag.message.lastIndexOf("'")); const hasExportedEnumWithThatName = new RegExp(`\\bexport\\s+?(enum|namespace)\\s+?${unusedExportName}\\b`).test(doc.getText()); return !hasExportedEnumWithThatName; } /** * Some mappings might be invalid. Try to catch these cases here. */ function adjustMappings(diag, doc) { if (diag.range.start.character < 0) { diag.range.start.character = 0; } if (diag.range.end.character < 0) { diag.range.end.character = 0; } if (diag.range.start.line < 0) { diag.range.start = { line: 0, character: 0 }; } if (diag.range.end.line < 0) { diag.range.end = { line: 0, character: 0 }; } diag.range = (0, utils_1.moveRangeStartToEndIfNecessary)(diag.range); if ((diag.code === 'css-unused-selector' || diag.code === 'css_unused_selector') && doc.styleInfo && !(0, documents_1.isInTag)(diag.range.start, doc.styleInfo)) { diag.range.start = doc.styleInfo.startPos; diag.range.end = diag.range.start; } return diag; } 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`. '; //# sourceMappingURL=getDiagnostics.js.map