diff --git a/packages/language-server/src/lib/documents/Document.ts b/packages/language-server/src/lib/documents/Document.ts index a03000114..3272530f3 100644 --- a/packages/language-server/src/lib/documents/Document.ts +++ b/packages/language-server/src/lib/documents/Document.ts @@ -1,6 +1,6 @@ import { urlToPath } from '../../utils'; import { WritableDocument } from './DocumentBase'; -import { extractScriptTags, extractStyleTag, TagInformation } from './utils'; +import { extractScriptTags, extractStyleTag, extractTemplateTag, TagInformation } from './utils'; import { parseHtml } from './parseHtml'; import { SvelteConfig, configLoader } from './configLoader'; import { HTMLDocument } from 'vscode-html-languageservice'; @@ -13,6 +13,7 @@ export class Document extends WritableDocument { scriptInfo: TagInformation | null = null; moduleScriptInfo: TagInformation | null = null; styleInfo: TagInformation | null = null; + templateInfo: TagInformation | null = null; configPromise: Promise; config?: SvelteConfig; html!: HTMLDocument; @@ -25,8 +26,8 @@ export class Document extends WritableDocument { private updateDocInfo() { this.html = parseHtml(this.content); - const scriptTags = extractScriptTags(this.content, this.html); const update = (config: SvelteConfig | undefined) => { + const scriptTags = extractScriptTags(this.content, this.html); this.config = config; this.scriptInfo = this.addDefaultLanguage(config, scriptTags?.script || null, 'script'); this.moduleScriptInfo = this.addDefaultLanguage( @@ -39,6 +40,11 @@ export class Document extends WritableDocument { extractStyleTag(this.content, this.html), 'style' ); + this.templateInfo = this.addDefaultLanguage( + config, + extractTemplateTag(this.content, this.html), + 'markup' + ); }; const config = configLoader.getConfig(this.getFilePath() || ''); @@ -82,14 +88,16 @@ export class Document extends WritableDocument { } /** - * Returns the language associated to either script or style. + * Returns the language associated to script, style or template. * Returns an empty string if there's nothing set. */ - getLanguageAttribute(tag: 'script' | 'style'): string { + getLanguageAttribute(tag: 'script' | 'style' | 'template'): string { const attrs = (tag === 'style' ? this.styleInfo?.attributes - : this.scriptInfo?.attributes || this.moduleScriptInfo?.attributes) || {}; + : tag === 'script' + ? this.scriptInfo?.attributes || this.moduleScriptInfo?.attributes + : this.templateInfo?.attributes) || {}; const lang = attrs.lang || attrs.type || ''; return lang.replace(/^text\//, ''); } @@ -97,7 +105,7 @@ export class Document extends WritableDocument { private addDefaultLanguage( config: SvelteConfig | undefined, tagInfo: TagInformation | null, - tag: 'style' | 'script' + tag: 'style' | 'script' | 'markup' ): TagInformation | null { if (!tagInfo || !config) { return tagInfo; diff --git a/packages/language-server/src/lib/documents/utils.ts b/packages/language-server/src/lib/documents/utils.ts index f0553af5d..30a8c1864 100644 --- a/packages/language-server/src/lib/documents/utils.ts +++ b/packages/language-server/src/lib/documents/utils.ts @@ -72,7 +72,11 @@ function blankIfBlocks(text: string): string { * @param source text content to extract tag from * @param tag the tag to extract */ -function extractTags(text: string, tag: 'script' | 'style', html?: HTMLDocument): TagInformation[] { +function extractTags( + text: string, + tag: 'script' | 'style' | 'template', + html?: HTMLDocument +): TagInformation[] { text = blankIfBlocks(text); const rootNodes = html?.roots || parseHtml(text).roots; const matchedNodes = rootNodes @@ -173,6 +177,16 @@ export function extractStyleTag(source: string, html?: HTMLDocument): TagInforma return styles[0]; } +export function extractTemplateTag(source: string, html?: HTMLDocument): TagInformation | null { + const templates = extractTags(source, 'template', html); + if (!templates.length) { + return null; + } + + // There should only be one style tag + return templates[0]; +} + /** * Get the line and character based on the offset * @param offset The index of the position diff --git a/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts b/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts index 33c0771f7..e2272e9e2 100644 --- a/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts +++ b/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts @@ -1,6 +1,11 @@ import ts from 'typescript'; import { Diagnostic, DiagnosticSeverity, DiagnosticTag } from 'vscode-languageserver'; -import { Document, mapObjWithRangeToOriginal, getTextInRange } from '../../../lib/documents'; +import { + Document, + mapObjWithRangeToOriginal, + getTextInRange, + isRangeInTag +} from '../../../lib/documents'; import { DiagnosticsProvider } from '../../interfaces'; import { LSAndTSDocResolver } from '../LSAndTSDocResolver'; import { convertRange, mapSeverity } from '../utils'; @@ -52,7 +57,7 @@ export class DiagnosticsProviderImpl implements DiagnosticsProvider { })) .map((diagnostic) => mapObjWithRangeToOriginal(fragment, diagnostic)) .filter(hasNoNegativeLines) - .filter(isNoFalsePositive(document.getText(), tsDoc)) + .filter(isNoFalsePositive(document, tsDoc)) .map(enhanceIfNecessary) .map(swapRangeStartEndIfNecessary); } @@ -82,16 +87,32 @@ function hasNoNegativeLines(diagnostic: Diagnostic): boolean { return diagnostic.range.start.line >= 0 && diagnostic.range.end.line >= 0; } -function isNoFalsePositive(text: string, tsDoc: SvelteDocumentSnapshot) { - return (diagnostic: Diagnostic, idx: number) => { +function isNoFalsePositive(document: Document, tsDoc: SvelteDocumentSnapshot) { + const text = document.getText(); + const usesPug = document.getLanguageAttribute('template') === 'pug'; + + return (diagnostic: Diagnostic) => { return ( isNoJsxCannotHaveMultipleAttrsError(diagnostic) && isNoUnusedLabelWarningForReactiveStatement(diagnostic) && - isNoUsedBeforeAssigned(diagnostic, text, tsDoc) + isNoUsedBeforeAssigned(diagnostic, text, tsDoc) && + (!usesPug || isNoPugFalsePositive(diagnostic, document)) ); }; } +/** + * All diagnostics inside the template tag and the unused import/variable diagnostics + * are marked as false positive. + */ +function isNoPugFalsePositive(diagnostic: Diagnostic, document: Document): boolean { + return ( + !isRangeInTag(diagnostic.range, document.templateInfo) && + diagnostic.code !== 6133 && + diagnostic.code !== 6192 + ); +} + /** * Variable used before being assigned, can happen when you do `export let x` * without assigning a value in strict mode. Should not throw an error here diff --git a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts index 04ffb7ec2..801cba91b 100644 --- a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts @@ -871,4 +871,63 @@ describe('DiagnosticsProvider', () => { } ]); }); + + it('Pug: Ignores errors in template and unused warnings in script', async () => { + const { plugin, document } = setup('diagnostics-pug.svelte'); + const diagnostics = await plugin.getDiagnostics(document); + + assert.deepStrictEqual(diagnostics, [ + { + code: 2307, + message: "Cannot find module '.' or its corresponding type declarations.", + range: { + end: { + character: 22, + line: 1 + }, + start: { + character: 19, + line: 1 + } + }, + severity: 1, + source: 'ts', + tags: [] + }, + { + code: 2307, + message: "Cannot find module '.' or its corresponding type declarations.", + range: { + end: { + character: 30, + line: 2 + }, + start: { + character: 27, + line: 2 + } + }, + severity: 1, + source: 'ts', + tags: [] + }, + { + code: 2322, + message: "Type 'boolean' is not assignable to type 'string | number'.", + range: { + end: { + character: 10, + line: 4 + }, + start: { + character: 9, + line: 4 + } + }, + severity: 1, + source: 'ts', + tags: [] + } + ]); + }); }); diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/diagnostics-pug.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/diagnostics-pug.svelte new file mode 100644 index 000000000..722fc4699 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/diagnostics-pug.svelte @@ -0,0 +1,11 @@ + + +