UNPKG

svelte-language-server

Version:
331 lines 13.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.gatherIdentifiers = exports.isInReactiveStatement = exports.isReactiveStatement = exports.SnapshotMap = exports.IGNORE_POSITION_COMMENT = exports.IGNORE_END_COMMENT = exports.IGNORE_START_COMMENT = void 0; exports.getComponentAtPosition = getComponentAtPosition; exports.isComponentAtPosition = isComponentAtPosition; exports.surroundWithIgnoreComments = surroundWithIgnoreComments; exports.isInGeneratedCode = isInGeneratedCode; exports.startsWithIgnoredPosition = startsWithIgnoredPosition; exports.isTextSpanInGeneratedCode = isTextSpanInGeneratedCode; exports.isPartOfImportStatement = isPartOfImportStatement; exports.isStoreVariableIn$storeDeclaration = isStoreVariableIn$storeDeclaration; exports.get$storeOffsetOf$storeDeclaration = get$storeOffsetOf$storeDeclaration; exports.is$storeVariableIn$storeDeclaration = is$storeVariableIn$storeDeclaration; exports.getStoreOffsetOf$storeDeclaration = getStoreOffsetOf$storeDeclaration; exports.isAfterSvelte2TsxPropsReturn = isAfterSvelte2TsxPropsReturn; exports.findContainingNode = findContainingNode; exports.findClosestContainingNode = findClosestContainingNode; exports.findNodeAtSpan = findNodeAtSpan; exports.findRenderFunction = findRenderFunction; exports.gatherDescendants = gatherDescendants; exports.isKitTypePath = isKitTypePath; exports.getFormatCodeBasis = getFormatCodeBasis; exports.getQuotePreference = getQuotePreference; exports.findChildOfKind = findChildOfKind; exports.getNewScriptStartTag = getNewScriptStartTag; exports.checkRangeMappingWithGeneratedSemi = checkRangeMappingWithGeneratedSemi; const typescript_1 = __importDefault(require("typescript")); const documents_1 = require("../../../lib/documents"); const ComponentInfoProvider_1 = require("../ComponentInfoProvider"); const utils_1 = require("../../../utils"); const fileCollection_1 = require("../../../lib/documents/fileCollection"); const svelte2tsx_1 = require("svelte2tsx"); /** * If the given original position is within a Svelte starting tag, * return the snapshot of that component. */ function getComponentAtPosition(lang, doc, tsDoc, originalPosition) { if (tsDoc.parserError) { return null; } if ((0, documents_1.isInTag)(originalPosition, doc.scriptInfo) || (0, documents_1.isInTag)(originalPosition, doc.moduleScriptInfo)) { // Inside script tags -> not a component return null; } const node = (0, documents_1.getNodeIfIsInComponentStartTag)(doc.html, doc, doc.offsetAt(originalPosition)); if (!node) { return null; } const symbolPosWithinNode = node.tag?.includes('.') ? node.tag.lastIndexOf('.') + 1 : 0; const generatedPosition = tsDoc.getGeneratedPosition(doc.positionAt(node.start + symbolPosWithinNode + 1)); const def = lang.getDefinitionAtPosition(tsDoc.filePath, tsDoc.offsetAt(generatedPosition))?.[0]; if (!def) { return null; } return ComponentInfoProvider_1.JsOrTsComponentInfoProvider.create(lang, def, tsDoc.isSvelte5Plus); } function isComponentAtPosition(doc, tsDoc, originalPosition) { if (tsDoc.parserError) { return false; } if ((0, documents_1.isInTag)(originalPosition, doc.scriptInfo) || (0, documents_1.isInTag)(originalPosition, doc.moduleScriptInfo)) { // Inside script tags -> not a component return false; } return !!(0, documents_1.getNodeIfIsInComponentStartTag)(doc.html, doc, doc.offsetAt(originalPosition)); } exports.IGNORE_START_COMMENT = '/*Ωignore_startΩ*/'; exports.IGNORE_END_COMMENT = '/*Ωignore_endΩ*/'; exports.IGNORE_POSITION_COMMENT = '/*Ωignore_positionΩ*/'; /** * Surrounds given string with a start/end comment which marks it * to be ignored by tooling. */ function surroundWithIgnoreComments(str) { return exports.IGNORE_START_COMMENT + str + exports.IGNORE_END_COMMENT; } /** * Checks if this a section that should be completely ignored * because it's purely generated. */ function isInGeneratedCode(text, start, end = start) { const lastStart = text.lastIndexOf(exports.IGNORE_START_COMMENT, start); const lastEnd = text.lastIndexOf(exports.IGNORE_END_COMMENT, start); const nextEnd = text.indexOf(exports.IGNORE_END_COMMENT, end); // if lastEnd === nextEnd, this means that the str was found at the index // up to which is searched for it return (lastStart > lastEnd || lastEnd === nextEnd) && lastStart < nextEnd; } function startsWithIgnoredPosition(text, offset) { return text.slice(offset).startsWith(exports.IGNORE_POSITION_COMMENT); } /** * Checks if this is a text span that is inside svelte2tsx-generated code * (has no mapping to the original) */ function isTextSpanInGeneratedCode(text, span) { return isInGeneratedCode(text, span.start, span.start + span.length); } function isPartOfImportStatement(text, position) { const line = (0, documents_1.getLineAtPosition)(position, text); return /\s*from\s+["'][^"']*/.test(line.slice(0, position.character)); } function isStoreVariableIn$storeDeclaration(text, varStart) { return (text.lastIndexOf('__sveltets_2_store_get(', varStart) === varStart - '__sveltets_2_store_get('.length); } function get$storeOffsetOf$storeDeclaration(text, storePosition) { return text.lastIndexOf(' =', storePosition) - 1; } function is$storeVariableIn$storeDeclaration(text, varStart) { return /^\$\w+ = __sveltets_2_store_get/.test(text.substring(varStart)); } function getStoreOffsetOf$storeDeclaration(text, $storeVarStart) { return text.indexOf(');', $storeVarStart) - 1; } class SnapshotMap { constructor(resolver, sourceLs) { this.resolver = resolver; this.sourceLs = sourceLs; this.map = new fileCollection_1.FileMap(); } set(fileName, snapshot) { this.map.set(fileName, snapshot); } get(fileName) { return this.map.get(fileName); } async retrieve(fileName) { let snapshot = this.get(fileName); if (snapshot) { return snapshot; } const snap = this.sourceLs.snapshotManager.get(fileName) ?? // should not happen in most cases, // the file should be in the project otherwise why would we know about it (await this.resolver.getOrCreateSnapshot(fileName)); this.set(fileName, snap); return snap; } } exports.SnapshotMap = SnapshotMap; function isAfterSvelte2TsxPropsReturn(text, end) { const textBeforeProp = text.substring(0, end); // This is how svelte2tsx writes out the props if (textBeforeProp.includes('\nreturn { props: {')) { return true; } } function findContainingNode(node, textSpan, predicate) { const children = node.getChildren(); const end = textSpan.start + textSpan.length; for (const child of children) { if (!(child.getStart() <= textSpan.start && child.getEnd() >= end)) { continue; } if (predicate(child)) { return child; } const foundInChildren = findContainingNode(child, textSpan, predicate); if (foundInChildren) { return foundInChildren; } } } function findClosestContainingNode(node, textSpan, predicate) { let current = findContainingNode(node, textSpan, predicate); if (!current) { return; } let closest = current; while (current) { const foundInChildren = findContainingNode(current, textSpan, predicate); closest = current; current = foundInChildren; } return closest; } /** * Finds node exactly matching span {start, length}. */ function findNodeAtSpan(node, span, predicate) { const { start, length } = span; const end = start + length; for (const child of node.getChildren()) { const childStart = child.getStart(); if (end <= childStart) { return; } const childEnd = child.getEnd(); if (start >= childEnd) { continue; } if (start === childStart && end === childEnd) { if (!predicate) { return child; } if (predicate(child)) { return child; } } const foundInChildren = findNodeAtSpan(child, span, predicate); if (foundInChildren) { return foundInChildren; } } } function isSomeAncestor(node, predicate) { for (let parent = node.parent; parent; parent = parent.parent) { if (predicate(parent)) { return true; } } return false; } /** * Tests a node then its parent and successive ancestors for some respective predicates. */ function nodeAndParentsSatisfyRespectivePredicates(selfPredicate, ...predicates) { return (node) => { let next = node; return [selfPredicate, ...predicates].every((predicate) => { if (!next) { return false; } const current = next; next = next.parent; return predicate(current); }); }; } const isRenderFunction = nodeAndParentsSatisfyRespectivePredicates((node) => typescript_1.default.isFunctionDeclaration(node) && node?.name?.getText() === svelte2tsx_1.internalHelpers.renderName, typescript_1.default.isSourceFile); const isRenderFunctionBody = nodeAndParentsSatisfyRespectivePredicates(typescript_1.default.isBlock, isRenderFunction); exports.isReactiveStatement = nodeAndParentsSatisfyRespectivePredicates((node) => typescript_1.default.isLabeledStatement(node) && node.label.getText() === '$', (0, utils_1.or)( // function $$render() { // $: x2 = __sveltets_2_invalidate(() => x * x) // } isRenderFunctionBody, // function $$render() { // ;() => {$: x, update(); // } nodeAndParentsSatisfyRespectivePredicates(typescript_1.default.isBlock, typescript_1.default.isArrowFunction, typescript_1.default.isExpressionStatement, isRenderFunctionBody))); function findRenderFunction(sourceFile) { // only search top level for (const child of sourceFile.statements) { if (isRenderFunction(child)) { return child; } } } const isInReactiveStatement = (node) => isSomeAncestor(node, exports.isReactiveStatement); exports.isInReactiveStatement = isInReactiveStatement; function gatherDescendants(node, predicate, dest = []) { if (predicate(node)) { dest.push(node); } else { for (const child of node.getChildren()) { gatherDescendants(child, predicate, dest); } } return dest; } const gatherIdentifiers = (node) => gatherDescendants(node, typescript_1.default.isIdentifier); exports.gatherIdentifiers = gatherIdentifiers; function isKitTypePath(path) { return !!path?.includes('.svelte-kit/types'); } function getFormatCodeBasis(formatCodeSetting) { const { baseIndentSize, indentSize, convertTabsToSpaces } = formatCodeSetting; const baseIndent = convertTabsToSpaces ? ' '.repeat(baseIndentSize ?? 4) : baseIndentSize ? '\t' : ''; const indent = convertTabsToSpaces ? ' '.repeat(indentSize ?? 4) : baseIndentSize ? '\t' : ''; const semi = formatCodeSetting.semicolons === 'remove' ? '' : ';'; const newLine = formatCodeSetting.newLineCharacter ?? typescript_1.default.sys.newLine; return { baseIndent, indent, semi, newLine }; } /** * https://github.com/microsoft/TypeScript/blob/00dc0b6674eef3fbb3abb86f9d71705b11134446/src/services/utilities.ts#L2452 */ function getQuotePreference(sourceFile, preferences) { const single = "'"; const double = '"'; if (preferences.quotePreference && preferences.quotePreference !== 'auto') { return preferences.quotePreference === 'single' ? single : double; } const firstModuleSpecifier = Array.from(sourceFile.statements).find((statement) => typescript_1.default.isImportDeclaration(statement) && typescript_1.default.isStringLiteral(statement.moduleSpecifier))?.moduleSpecifier; return firstModuleSpecifier ? sourceFile.getText()[firstModuleSpecifier.pos] === '"' ? double : single : double; } function findChildOfKind(node, kind) { for (const child of node.getChildren()) { if (child.kind === kind) { return child; } const foundInChildren = findChildOfKind(child, kind); if (foundInChildren) { return foundInChildren; } } } function getNewScriptStartTag(lsConfig, newLine) { const lang = lsConfig.svelte.defaultScriptLanguage; const scriptLang = lang === 'none' ? '' : ` lang="${lang}"`; return `<script${scriptLang}>${newLine}`; } function checkRangeMappingWithGeneratedSemi(originalRange, generatedRange, tsDoc) { const originalLength = originalRange.end.character - originalRange.start.character; const generatedLength = generatedRange.end.character - generatedRange.start.character; // sourcemap off by one character issue + a generated semicolon if (originalLength === generatedLength - 2 && tsDoc.getFullText()[tsDoc.offsetAt(generatedRange.end) - 1] === ';') { originalRange.end.character += 1; } } //# sourceMappingURL=utils.js.map