UNPKG

svelte-language-server

Version:
140 lines 6.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getQuickfixActions = getQuickfixActions; exports.isIgnorableSvelteDiagnostic = isIgnorableSvelteDiagnostic; const estree_walker_1 = require("estree-walker"); const os_1 = require("os"); const vscode_languageserver_1 = require("vscode-languageserver"); const documents_1 = require("../../../../lib/documents"); const utils_1 = require("../../../../utils"); /** * Get applicable quick fixes. */ async function getQuickfixActions(svelteDoc, svelteDiagnostics) { const textDocument = vscode_languageserver_1.OptionalVersionedTextDocumentIdentifier.create((0, utils_1.pathToUrl)(svelteDoc.getFilePath()), null); const { ast } = await svelteDoc.getCompiled(); const transpiled = await svelteDoc.getTranspiled(); const content = transpiled.getText(); const lineOffsets = (0, documents_1.getLineOffsets)(content); const codeActions = []; for (const diagnostic of svelteDiagnostics) { codeActions.push(...(await createQuickfixActions(textDocument, transpiled, content, lineOffsets, ast, diagnostic))); } return codeActions; } async function createQuickfixActions(textDocument, transpiled, content, lineOffsets, ast, diagnostic) { const { range: { start, end } } = diagnostic; const generatedStart = transpiled.getGeneratedPosition(start); const generatedEnd = transpiled.getGeneratedPosition(end); const diagnosticStartOffset = (0, documents_1.offsetAt)(generatedStart, content, lineOffsets); const diagnosticEndOffset = (0, documents_1.offsetAt)(generatedEnd, content, lineOffsets); const offsetRange = { pos: diagnosticStartOffset, end: diagnosticEndOffset }; const { html, instance, module } = ast; const tree = [html, instance, module].find((part) => { return (part?.start != null && offsetRange.pos >= part.start && part?.end != null && offsetRange.pos <= part.end && part?.end != null && offsetRange.end <= part.end && part?.start != null && offsetRange.end >= part.start); }); const node = findTagForRange(tree, offsetRange, tree === html); const codeActions = []; if (diagnostic.code == 'security-anchor-rel-noreferrer') { codeActions.push(createSvelteAnchorMissingAttributeQuickfixAction(textDocument, transpiled, content, lineOffsets, node)); } codeActions.push(createSvelteIgnoreQuickfixAction(textDocument, transpiled, content, lineOffsets, node, diagnostic, tree === html)); return codeActions; } function createSvelteAnchorMissingAttributeQuickfixAction(textDocument, transpiled, content, lineOffsets, node) { // Assert non-null because the node target attribute is required for 'security-anchor-rel-noreferrer' const targetAttribute = node.attributes.find((i) => i.name == 'target'); const relAttribute = node.attributes.find((i) => i.name == 'rel'); const codeActionTextEdit = relAttribute ? vscode_languageserver_1.TextEdit.insert((0, documents_1.positionAt)(relAttribute.end - 1, content, lineOffsets), ' noreferrer') : vscode_languageserver_1.TextEdit.insert((0, documents_1.positionAt)(targetAttribute.end, content, lineOffsets), ' rel="noreferrer"'); return vscode_languageserver_1.CodeAction.create('(svelte) Add missing attribute rel="noreferrer"', { documentChanges: [ vscode_languageserver_1.TextDocumentEdit.create(textDocument, [ (0, documents_1.mapObjWithRangeToOriginal)(transpiled, codeActionTextEdit) ]) ] }, vscode_languageserver_1.CodeActionKind.QuickFix); } function createSvelteIgnoreQuickfixAction(textDocument, transpiled, content, lineOffsets, node, diagnostic, isHtml) { return vscode_languageserver_1.CodeAction.create(getCodeActionTitle(diagnostic), { documentChanges: [ vscode_languageserver_1.TextDocumentEdit.create(textDocument, [ getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic, isHtml) ]) ] }, vscode_languageserver_1.CodeActionKind.QuickFix); } function getCodeActionTitle(diagnostic) { // make it distinguishable with eslint's code action return `(svelte) Disable ${diagnostic.code} for this line`; } /** * Whether or not the given diagnostic can be ignored via a * <!-- svelte-ignore <code> --> */ function isIgnorableSvelteDiagnostic(diagnostic) { const { source, severity, code } = diagnostic; return (code && !nonIgnorableWarnings.includes(code) && source === 'svelte' && severity !== vscode_languageserver_1.DiagnosticSeverity.Error); } const nonIgnorableWarnings = [ 'missing-custom-element-compile-options', 'unused-export-let', 'css-unused-selector' ]; function getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic, isHtml) { const { code } = diagnostic; const nodeStartPosition = (0, documents_1.positionAt)(node.start, content, lineOffsets); const nodeLineStart = (0, documents_1.offsetAt)({ line: nodeStartPosition.line, character: 0 }, content, lineOffsets); const afterStartLineStart = content.slice(nodeLineStart); const indent = (0, utils_1.getIndent)(afterStartLineStart); // TODO: Make all code action's new line consistent let ignore = `${indent}// svelte-ignore ${code}${os_1.EOL}${indent}`; if (isHtml) { ignore = `${indent}<!-- svelte-ignore ${code} -->${os_1.EOL}`; } const position = vscode_languageserver_1.Position.create(nodeStartPosition.line, 0); return (0, documents_1.mapObjWithRangeToOriginal)(transpiled, vscode_languageserver_1.TextEdit.insert(position, ignore)); } const elementOrComponent = ['Component', 'Element', 'InlineComponent']; function findTagForRange(ast, range, isHtml) { let nearest = ast; (0, estree_walker_1.walk)(ast, { enter(node, parent) { if (isHtml) { const { type } = node; const isBlock = 'block' in node || node.type.toLowerCase().includes('block'); const isFragment = type === 'Fragment'; const keepLooking = isFragment || elementOrComponent.includes(type) || isBlock; if (!keepLooking) { this.skip(); return; } } if (within(node, range) && parent === nearest) { nearest = node; } } }); return nearest; } function within(node, range) { return node.end >= range.end && node.start <= range.pos; } //# sourceMappingURL=getQuickfixes.js.map