UNPKG

qingkuai-language-service

Version:

The language service for qingkuai

1,312 lines (1,296 loc) 69.9 kB
'use strict'; var warn = require('./chunks/shared.cjs'); var compiler = require('qingkuai/compiler'); let ts; function setState(options) { ({ ts } = options); } function convertJsDocTagsToMarkdown(tags) { const labels = tags.map((tag) => { switch (tag.name) { case "augments": case "extends": case "param": case "template": { const body = getTagBody(tag); if (body?.length === 3) { const param = body[1]; const doc = body[2]; const label2 = `*@${tag.name}* \`${param}\``; if (!doc) { return label2; } return label2 + (doc.match(/\r\n|\n/g) ? " \n" + doc : ` \u2014 ${doc}`); } break; } case "return": case "returns": { if (!tag.text?.length) { return void 0; } break; } } const label = `*@${tag.name}*`; const text = getTagBodyText(tag); if (!text) { return label; } return label + (text.match(/\r\n|\n/g) ? " \n" + text : ` \u2014 ${text}`); }); return labels.filter((label) => !!label).join(" \n\n"); } function convertDisplayPartsToPlainTextWithLink(parts) { if (warn.isUndefined(parts)) { return ""; } return parts.reduce((ret, part) => { if (part.kind === "linkText") { let spaceIndex = part.text.indexOf(" "); if (spaceIndex === -1) { spaceIndex = part.text.length; } return ret + `[${part.text.slice(spaceIndex)}](${part.text.slice(0, spaceIndex)})`; } else if (part.kind === "linkName") { const target = part.target; const args = encodeURIComponent( JSON.stringify({ end: target.textSpan.start, start: target.textSpan.start, path: ts.server.toNormalizedPath(target.fileName) }) ); return ret + `[${part.text}](command:qingkuai.openFileByFilePath?${args})`; } return (ret + (part.kind === "link" ? "" : part.text || "")).replace(warn.LSU_AND_DOT, ""); }, ""); } function getTagBodyText(tag) { if (!tag.text) { return void 0; } function makeCodeblock(text2) { if (/^\s*[~`]{3}/m.test(text2)) { return text2; } return "```\n" + text2 + "\n```"; } let text = convertDisplayPartsToPlainTextWithLink(tag.text); switch (tag.name) { case "example": { const captionTagMatches = text.match(/<caption>(.*?)<\/caption>\s*(\r\n|\n)/); if (captionTagMatches && captionTagMatches.index === 0) { return captionTagMatches[1] + "\n" + makeCodeblock(text.slice(captionTagMatches[0].length)); } else { return makeCodeblock(text); } } case "author": { const emailMatch = text.match(/(.+)\s<([-.\w]+@[-.\w]+)>/); if (emailMatch === null) { return text; } else { return `${emailMatch[1]} ${emailMatch[2]}`; } } case "default": { return makeCodeblock(text); } default: { return text; } } } function getTagBody(tag) { if (tag.name === "template") { const parts = tag.text; if (parts && typeof parts !== "string") { const docs = parts.filter((p) => p.kind === "text"); const docsText = docs.map((p) => p.text.replace(/^\s*-?\s*/, "")).join(" "); const params = parts.filter((p) => p.kind === "typeParameterName"); const paramsText = params.map((p) => p.text).join(", "); return params ? ["", paramsText, docsText] : void 0; } } return convertDisplayPartsToPlainTextWithLink(tag.text).split(/^(\S+)\s*-?\s*/); } function getAndConvertCompletionInfo(adapter, params) { const filePath = adapter.getNormalizedPath(params.fileName); const languageService = adapter.getDefaultLanguageService(filePath); warn.debugAssert(!!languageService); const completionRes = languageService.getCompletionsAtPosition( filePath, params.pos, { ...adapter.getUserPreferences(filePath), triggerKind: params.triggerKind, includeInsertTextCompletions: true, triggerCharacter: params.triggerCharacter }, adapter.getFormattingOptions(filePath) ); if (!completionRes) { return null; } const convertedEntries = []; for (const entry of completionRes.entries || []) { const kindModifiers = parseKindModifier(entry.kindModifiers); convertedEntries.push({ ...entry, name: entry.name, isColor: kindModifiers.has("color"), detail: getScriptKindDetails(entry), isDeprecated: kindModifiers.has("deprecated"), label: entry.name + (kindModifiers.has("optional") ? "?" : "") }); } return { ...completionRes, entries: convertedEntries }; } function getAndConvertCompletionDetail(adapter, lsParams) { const { fileName, pos, entryName, original, source } = lsParams; const filePath = adapter.getNormalizedPath(fileName); const languageService = adapter.getDefaultLanguageService(filePath); warn.debugAssert(!!languageService); const userPreferences = adapter.getUserPreferences(fileName); const formattingOptions = adapter.getFormattingOptions(fileName); const detail = languageService.getCompletionEntryDetails( fileName, pos, entryName, formattingOptions, source, userPreferences, original ); if (!detail) { return null; } const converted = { codeActions: [], name: detail.name, kind: detail.kind, kindModifiers: detail.kindModifiers, detail: ts.displayPartsToString(detail.displayParts) }; if (detail.tags?.length) { converted.tags = detail.tags; } if (detail.codeActions?.length) { const resolveImportExtension = adapter.getQingkuaiConfig(fileName)?.resolveImportExtension; detail.codeActions?.forEach((action) => { const effects = []; const currentFileChanges = []; if (resolveImportExtension) { action.description = action.description.replace(warn.qkExtInImportRE, "$1"); } action.changes.forEach((change) => { const changeFilePath = adapter.getNormalizedPath(change.fileName); const changeFileInfo = adapter.service.ensureGetQingkuaiFileInfo(changeFilePath); const currentEffect = { textChanges: [], fileName: changeFilePath, isNewFile: change.isNewFile }; change.textChanges.forEach((item) => { const sourceStartIndex = changeFileInfo.getSourceIndex(item.span.start); if (fileName === change.fileName) { const sourceEndIndex = changeFileInfo.getSourceIndex( item.span.start + item.span.length ); const willEditImport = warn.CompletionImportTextEditRE.test(item.newText); if (willEditImport && resolveImportExtension) { item.newText = item.newText.replace(warn.qkExtInImportRE, "$1"); } if (willEditImport || !warn.isIndexesInvalid(sourceStartIndex, sourceEndIndex)) { currentFileChanges.push({ newText: item.newText, range: [sourceStartIndex, sourceEndIndex] }); } } else if (!warn.isIndexesInvalid(sourceStartIndex)) { currentEffect.textChanges.push({ newText: item.newText, span: { start: sourceStartIndex, length: item.span.length } }); } }); if (currentEffect.textChanges.length) { effects.push(currentEffect); } }); converted.codeActions.push({ effects, currentFileChanges, commands: action.commands, description: action.description }); }); } if (detail.documentation) { converted.documentation = convertDisplayPartsToPlainTextWithLink(detail.documentation); } return converted; } function proxyGetCompletionsAtPositionToConvert(adapter, project) { const languageService = project.getLanguageService(); const getCompletionsAtPosition = languageService.getCompletionsAtPosition; languageService.getCompletionsAtPosition = (fileName, position, preference, formattingOptions) => { const originalResult = getCompletionsAtPosition.call( languageService, fileName, position, preference, formattingOptions ); if (originalResult?.entries) { originalResult.entries = originalResult.entries.filter((entry) => { const sourceFileName = entry.data?.fileName; if (sourceFileName && warn.isQingkuaiFileName(sourceFileName)) { entry.name = adapter.service.ensureGetQingkuaiFileInfo(sourceFileName).componentName; } if (entry.name.startsWith(compiler.constants.PRESERVED_IDPREFIX)) { return false; } if (warn.INVALID_COMPLETION_TEXT_LABELS.has(entry.name)) { const entryDetail = languageService.getCompletionEntryDetails( fileName, position, entry.name, formattingOptions, entry.source, preference, entry.data ); return !entryDetail?.displayParts.some((part) => { return part.text === compiler.constants.LSC.UTIL; }); } return !entry.source || !warn.INVLALID_COMPLETION_PACKAGES.has(entry.source); }); } return originalResult; }; } function proxyGetCompletionEntryDetailsToConvert(_, project) { const languageService = project.getLanguageService(); const getCompletionEntryDetails = languageService.getCompletionEntryDetails; languageService.getCompletionEntryDetails = (...args) => { const originalRet = getCompletionEntryDetails.apply(languageService, args); if (originalRet && args[6]?.fileName && warn.isQingkuaiFileName(args[6].fileName)) { originalRet.tags?.forEach((tag) => { tag.text?.forEach((item) => { item.text = item.text.replaceAll(warn.LSU_AND_DOT, ""); }); }); for (let i = 0; i < originalRet.displayParts.length; i++) { if (originalRet.displayParts[i].text !== compiler.constants.LSC.UTIL) { continue; } if (originalRet.displayParts[i + 1].text === ".") { originalRet.displayParts[i + 1].text = ""; } originalRet.displayParts[i].text = ""; } } return originalRet; }; } function parseKindModifier(kindModifiers) { if (warn.isUndefined(kindModifiers)) { kindModifiers = ""; } return new Set(kindModifiers.split(/,|\s+/g)); } function getScriptKindDetails(entry) { if (!entry.kindModifiers || entry.kind !== ts.ScriptElementKind.scriptElement) { return void 0; } const kindModifiers = parseKindModifier(entry.kindModifiers); for (const extModifier of warn.SCRIPT_EXTENSIONS) { if (kindModifiers.has(extModifier)) { if (entry.name.toLowerCase().endsWith(extModifier)) { return entry.name; } else { return entry.name + extModifier; } } } return void 0; } function getAndConvertDefinitions(adapter, params) { const filePath = adapter.getNormalizedPath(params.fileName); const languageService = adapter.getDefaultLanguageService(filePath); if (!warn.debugAssert(languageService)) { return null; } const data = languageService.getDefinitionAndBoundSpan(filePath, params.pos); if (!data?.definitions) { return null; } const locationConvertor = adapter.service.createLocationConvertor(filePath); const dealtDefinitions = data.definitions.map((definition) => { const definitionLocationConvertor = adapter.service.createLocationConvertor( definition.fileName ); const range = definitionLocationConvertor.languageServerRange.fromSourceTextSpan( definition.textSpan ); if (definition.contextSpan) { const contextRange = definition.contextSpan && definitionLocationConvertor.languageServerRange.fromSourceTextSpan( definition.contextSpan ); return { fileName: definition.fileName, targetSelectionRange: range, targetRange: contextRange ?? range }; } return { fileName: definition.fileName, targetSelectionRange: range, targetRange: range }; }); return { definitions: dealtDefinitions, range: locationConvertor.languageServerRange.fromTextSpan(data.textSpan) }; } function getAndConvertTypeDefinitions(adapter, params) { const filePath = adapter.getNormalizedPath(params.fileName); const languageService = adapter.getDefaultLanguageService(filePath); if (!warn.debugAssert(languageService)) { return null; } const definitions = languageService.getTypeDefinitionAtPosition(filePath, params.pos); if (!definitions?.length) { return null; } return definitions.map((definition) => { let contextRange = void 0; const definitionFilePath = adapter.getNormalizedPath(definition.fileName); const definitionLocationConvertor = adapter.service.createLocationConvertor(definitionFilePath); const range = definitionLocationConvertor.languageServerRange.fromTextSpan( definition.textSpan ); if (definition.contextSpan) { contextRange = definitionLocationConvertor.languageServerRange.fromTextSpan( definition.contextSpan ); } return { fileName: filePath, targetRange: range, targetSelectionRange: contextRange ?? range }; }); } function proxyGetDefinitionAndBoundSpanToConvert(adapter, project) { const languageService = project.getLanguageService(); const getDefinitionAndBoundSpan = languageService.getDefinitionAndBoundSpan; languageService.getDefinitionAndBoundSpan = (fileName, position) => { const originalRet = getDefinitionAndBoundSpan.call(languageService, fileName, position); if (!originalRet?.definitions) { return; } originalRet.definitions.forEach((definition) => { if (warn.isQingkuaiFileName(definition.fileName)) { const definitionLocationConvertor = adapter.service.createLocationConvertor( definition.fileName ); definition.textSpan = definitionLocationConvertor.textSpan.toSourceTextSpan( definition.textSpan ); definition.contextSpan = definition.contextSpan && definitionLocationConvertor.textSpan.toSourceTextSpan(definition.contextSpan); } }); return originalRet; }; } function proxyGetTypeDefinitionAtPositionToConvert(adapter, project) { const languageService = project.getLanguageService(); const getTypeDefinitionAtPosition = languageService.getTypeDefinitionAtPosition; languageService.getTypeDefinitionAtPosition = (fileName, position) => { const originalRet = getTypeDefinitionAtPosition.call(languageService, fileName, position); originalRet?.forEach((definition) => { if (warn.isQingkuaiFileName(definition.fileName)) { const definitionLocationConvertor = adapter.service.createLocationConvertor( definition.fileName ); definition.textSpan = definitionLocationConvertor.textSpan.toSourceTextSpan( definition.textSpan ); definition.contextSpan = definition.contextSpan && definitionLocationConvertor.textSpan.toSourceTextSpan(definition.contextSpan); } }); return originalRet; }; } function getAndConvertReferences(adapter, params) { const result = []; const filePath = adapter.getNormalizedPath(params.fileName); const languageService = adapter.getDefaultLanguageService(filePath); if (!warn.debugAssert(languageService)) { return null; } const data = languageService.findReferences(filePath, params.pos); data?.forEach((item) => { item.references.forEach((reference) => { if (reference.isDefinition) { return; } const referenceLocationConvertor = adapter.service.createLocationConvertor( reference.fileName ); result.push({ range: referenceLocationConvertor.languageServerRange.fromSourceTextSpan( reference.textSpan ), fileName: adapter.getNormalizedPath(reference.fileName) }); }); }); return result; } function proxyFindReferencesToConvert(adapter, project) { const languageService = project.getLanguageService(); const findReferences = languageService.findReferences; languageService.findReferences = (fileName, position) => { const originalRet = findReferences.call(languageService, fileName, position); originalRet?.forEach((item) => { item.references = item.references.filter((reference) => { if (!warn.isQingkuaiFileName(reference.fileName)) { return true; } reference.textSpan.start; const referenceLocationConvertor = adapter.service.createLocationConvertor( reference.fileName ); reference.textSpan = referenceLocationConvertor.textSpan.toSourceTextSpan( reference.textSpan ); reference.contextSpan = reference.contextSpan && referenceLocationConvertor.textSpan.toSourceTextSpan(reference.contextSpan); return reference.textSpan !== referenceLocationConvertor.textSpan.defaultValue; }); }); return originalRet; }; } function findAndConvertImplementations(adapter, params) { const filePath = adapter.getNormalizedPath(params.fileName); const languageService = adapter.getDefaultLanguageService(filePath); if (!warn.debugAssert(languageService)) { return null; } const implementations = languageService.getImplementationAtPosition(filePath, params.pos); if (!implementations) { return null; } return implementations.map((implementation) => { const implementationLocationConvertor = adapter.service.createLocationConvertor( implementation.fileName ); return { range: implementationLocationConvertor.languageServerRange.fromSourceTextSpan( implementation.textSpan ), fileName: adapter.getNormalizedPath(implementation.fileName) }; }); } function proxyGetImplementationAtPositionToConvert(adapter, project) { const languageService = project.getLanguageService(); const getImplementationsAtPosition = languageService.getImplementationAtPosition; languageService.getImplementationAtPosition = (fileName, position) => { const originalRet = getImplementationsAtPosition.call(languageService, fileName, position); return originalRet?.filter((implementation) => { if (!warn.isQingkuaiFileName(implementation.fileName)) { return true; } implementation.textSpan.start; const implementationLocationConvertor = adapter.service.createLocationConvertor( implementation.fileName ); implementation.contextSpan = implementation.contextSpan && implementationLocationConvertor.textSpan.toSourceTextSpan( implementation.contextSpan ); implementation.textSpan = implementationLocationConvertor.textSpan.toSourceTextSpan( implementation.textSpan ); return implementation.textSpan !== implementationLocationConvertor.textSpan.defaultValue; }); }; } function isInTopScope(node) { switch (node.parent.kind) { case void 0: case ts.SyntaxKind.SourceFile: { return true; } case ts.SyntaxKind.Block: case ts.SyntaxKind.CaseBlock: case ts.SyntaxKind.ModuleBlock: case ts.SyntaxKind.ForStatement: case ts.SyntaxKind.ForInStatement: case ts.SyntaxKind.ForOfStatement: case ts.SyntaxKind.ClassStaticBlockDeclaration: { return false; } default: { return !ts.isParameter(node) && isInTopScope(node.parent); } } } function isInComponentFunctionTopScope(node) { const blockNode = ts.findAncestor(node, (ancestor) => { return ts.isBlock(ancestor); }); return !!(blockNode?.parent && ts.isFunctionDeclaration(blockNode.parent) && blockNode.parent.name?.text === compiler.constants.LSC.COMPONENT); } function walkTsNode(node, callback) { if (!warn.isUndefined(node)) { callback(node); ts.forEachChild(node, (cn) => { walkTsNode(cn, callback); }); } } function getNodeAtPositionAndWithin(node, pos) { const [start, len] = [node.getStart(), node.getWidth()]; if (pos >= start && pos <= start + len) { for (const child of node.getChildren()) { const foundInChild = getNodeAtPositionAndWithin(child, pos); if (foundInChild) { return foundInChild; } } return node; } return void 0; } function getAndConvertHoverTip(adapter, { fileName, pos }) { const fileInfo = adapter.service.ensureGetQingkuaiFileInfo(fileName); const program = adapter.getDefaultProgram(fileInfo.path); if (!warn.debugAssert(program)) { return null; } let idStatusDisplay = ""; const typeChecker = program.getTypeChecker(); const config = adapter.getQingkuaiConfig(fileInfo.path); const languageService = adapter.getDefaultLanguageService(fileInfo.path); const node = getNodeAtPositionAndWithin(program.getSourceFile(fileName), pos); if (node && ts.isIdentifier(node)) { const nodeRange = [node.getStart(), node.getEnd()]; if (config?.hoverTipReactiveStatus && fileInfo.idStatusInfo[node.text]) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol && symbol.declarations && !(symbol.flags & ts.SymbolFlags.Alias) && symbol.declarations.some((decl) => isInTopScope(decl))) { const statusInfo = fileInfo.idStatusInfo[node.text]; idStatusDisplay = ` // - ${statusInfo} -`; } } if (node.text.startsWith(compiler.constants.PRESERVED_IDPREFIX)) { return { content: "any", range: nodeRange }; } if (node.parent && ts.isPropertyAccessExpression(node.parent) && node.parent.expression.getText() === compiler.constants.LSC.UTIL) { return { content: "any", range: nodeRange }; } } const ret = languageService.getQuickInfoAtPosition(fileName, pos); if (warn.isUndefined(ret)) { return null; } const { start, length } = ret.textSpan; const documentation = convertDisplayPartsToPlainTextWithLink(ret.documentation); const display = convertDisplayPartsToPlainTextWithLink(ret.displayParts).replace( warn.LSU_AND_DOT, "" ); return { range: [start, start + length], content: warn.mdCodeBlockGen("ts", display + idStatusDisplay) + "\n" + documentation }; } function proxyGetQuickInfoAtPosition(_, project) { const languageService = project.getLanguageService(); const getQuickInfoAtPosition = languageService.getQuickInfoAtPosition; languageService.getQuickInfoAtPosition = (fileName, position) => { const originalRet = getQuickInfoAtPosition.call(languageService, fileName, position); if (originalRet?.displayParts) { originalRet.displayParts = filterDisplayParts(originalRet.displayParts); } if (originalRet?.documentation) { originalRet.documentation = filterDisplayParts(originalRet.documentation); } return originalRet; }; } function filterDisplayParts(target) { return target?.filter((item, index) => { switch (item.text) { case compiler.constants.LSC.UTIL: case compiler.constants.LSC.COMPONENT: case compiler.constants.PRESERVED_IDPREFIX: case compiler.constants.LSC.GET_TYPE_DELAY_MARKING: { return false; } case ".": { return target[index - 1].text !== compiler.constants.LSC.UTIL; } default: { return true; } } }); } function proxyProject(adapter, project) { const projectAny = project; if (!projectAny[warn.PROXIED_MARK]) { projectAny[warn.PROXIED_MARK] = true; for (const proxyFn of [ proxyGetScriptVersion, proxyGetScriptKind, proxyGetScriptSnapshot, proxyGetQuickInfoAtPosition, proxyFindReferencesToConvert, proxyResolveModuleNameLiterals, proxyGetCompletionsAtPositionToConvert, proxyGetCompletionEntryDetailsToConvert, proxyGetDefinitionAndBoundSpanToConvert, proxyGetTypeDefinitionAtPositionToConvert, proxyGetImplementationAtPositionToConvert ]) { proxyFn(adapter, project); } } } function proxyGetScriptSnapshot(adapter, languageServiceHost) { const getScriptSnapshot = languageServiceHost.getScriptSnapshot; languageServiceHost.getScriptSnapshot = (fileName) => { const originalRet = getScriptSnapshot?.call(languageServiceHost, fileName); if (!warn.isQingkuaiFileName(fileName)) { return originalRet; } return adapter.ts.ScriptSnapshot.fromString( adapter.service.ensureGetQingkuaiFileInfo(fileName).code ); }; } function proxyGetScriptVersion(adapter, languageServiceHost) { const getScriptVersion = languageServiceHost.getScriptVersion; languageServiceHost.getScriptVersion = (fileName) => { if (!warn.isQingkuaiFileName(fileName)) { return getScriptVersion?.call(languageServiceHost, fileName) ?? ""; } return adapter.service.ensureGetQingkuaiFileInfo(fileName).version.toString(); }; } function proxyGetScriptKind(adapter, languageServiceHost) { const getScriptKind = languageServiceHost.getScriptKind; if (getScriptKind) { languageServiceHost.getScriptKind = (fileName) => { if (!warn.isQingkuaiFileName(fileName)) { return getScriptKind.call(languageServiceHost, fileName); } return adapter.service.ensureGetQingkuaiFileInfo(fileName).isTS ? adapter.ts.ScriptKind.TS : adapter.ts.ScriptKind.JS; }; } } function proxyResolveModuleNameLiterals(adapter, languageServiceHost) { const resolveModuleLiterals = languageServiceHost.resolveModuleNameLiterals; if (warn.isUndefined(resolveModuleLiterals)) { return; } languageServiceHost.resolveModuleNameLiterals = (moduleLiterals, containingFile, ...rest) => { const originalRet = resolveModuleLiterals.call( languageServiceHost, moduleLiterals, containingFile, ...rest ); const containingFileInfo = warn.isQingkuaiFileName(containingFile) ? adapter.service.ensureGetQingkuaiFileInfo(containingFile) : void 0; const importByQingkuaiFile = warn.isQingkuaiFileName(containingFile); const containingFilePath = adapter.getNormalizedPath(containingFile); const qingkuaiConfig = adapter.getQingkuaiConfig(containingFilePath); const dirPath = adapter.getNormalizedPath(adapter.path.dir(containingFilePath)); const importedQingkuaiModuleTexts = /* @__PURE__ */ new Set(); adapter.resolvedQingkuaiModules.set(containingFilePath, importedQingkuaiModuleTexts); const ret = originalRet.map((item, index) => { const moduleText = moduleLiterals[index].text; const isDirectory = warn.isEmptyString(adapter.path.ext(moduleText)); const inferredAsQingkuaiFile = importByQingkuaiFile && isDirectory && qingkuaiConfig?.resolveImportExtension; const modulePath = adapter.getNormalizedPath( adapter.path.resolve(dirPath, moduleText + (inferredAsQingkuaiFile ? ".qk" : "")) ); const moduleFileInfo = warn.isQingkuaiFileName(modulePath) && adapter.fs.exist(modulePath) ? adapter.service.ensureGetQingkuaiFileInfo(modulePath) : void 0; if (!moduleFileInfo || modulePath === containingFileInfo?.path) { if (inferredAsQingkuaiFile && !item.resolvedModule) { (item.failedLookupLocations ?? (item.failedLookupLocations = [])).push(modulePath); } return item; } importedQingkuaiModuleTexts.add(moduleText); return { ...item, resolvedModule: { isExternalLibraryImport: false, resolvedUsingTsExtension: false, extension: moduleFileInfo.isTS ? ".ts" : ".js", resolvedFileName: adapter.getNormalizedPath(modulePath) }, failedLookupLocations: void 0 }; }); return ret; }; } function getNavigationTree(adapter, fileName) { const filePath = adapter.getNormalizedPath(fileName); const languageService = adapter.getDefaultLanguageService(filePath); if (!languageService) { return null; } return languageService.getNavigationTree(filePath); } function getComponentInfos(adapter, filePath) { const sourceFile = adapter?.getDefaultSourceFile(filePath); if (!warn.debugAssert(sourceFile)) { return []; } const dirPath = adapter.path.dir(filePath); const config = adapter.getQingkuaiConfig(filePath); const importedQingkuaiFileNames = /* @__PURE__ */ new Set(); const componentInfos = []; const qingkuaiModules = adapter.resolvedQingkuaiModules.get(filePath); walkTsNode(sourceFile, (node) => { if (adapter.ts.isImportDeclaration(node) && isInTopScope(node)) { if (!warn.isUndefined(node.importClause?.name)) { const identifierName = node.importClause.name.text; if (adapter.ts.isStringLiteral(node.moduleSpecifier) && qingkuaiModules?.has(node.moduleSpecifier.text)) { let relative = adapter.getNormalizedPath(node.moduleSpecifier.text); let absolute = adapter.path.resolve(dirPath, node.moduleSpecifier.text); const extension = !warn.isQingkuaiFileName(relative) ? ".qk" : ""; const targetFileInfo = adapter.service.ensureGetQingkuaiFileInfo( absolute + extension ); componentInfos.push({ imported: true, name: identifierName, absolutePath: targetFileInfo.path, relativePath: relative + extension, slotNames: targetFileInfo.slotNames, attributes: targetFileInfo.attributes, type: targetFileInfo.defaultExportTypeStr }); importedQingkuaiFileNames.add(absolute + extension); } } } }); for (const targetFileName of adapter.getDefaultProject(filePath).getScriptFileNames()) { const targetFilePath = adapter.getNormalizedPath(targetFileName); if (targetFilePath !== filePath && warn.isQingkuaiFileName(targetFilePath) && !importedQingkuaiFileNames.has(targetFilePath)) { let relativePath = getRelativePathWithStartDot(adapter.path, dirPath, targetFilePath); const targetFileInfo = adapter.service.ensureGetQingkuaiFileInfo(targetFilePath); if (config?.resolveImportExtension) { relativePath = relativePath.slice(0, -adapter.path.ext(relativePath).length); } componentInfos.push({ imported: false, relativePath, absolutePath: targetFilePath, name: targetFileInfo.componentName, slotNames: targetFileInfo.slotNames, attributes: targetFileInfo.attributes, type: targetFileInfo.defaultExportTypeStr }); } } return componentInfos; } function getRelativePathWithStartDot(adapterPath, from, to) { const relativePath = adapterPath.relative(from, to); return /\.{1,2}\//.test(relativePath) ? relativePath : `./${relativePath}`; } var __defProp$3 = Object.defineProperty; var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value); class LocationConvertor { constructor(adapter, fileName) { __publicField$3(this, "sourceFile"); __publicField$3(this, "adapter"); __publicField$3(this, "filePath"); __publicField$3(this, "lineAndCharacter", { fromInterIndex: (interIndex) => { if (!this.sourceFile) { return this.lineAndCharacter.defaultValue; } return this.sourceFile.getLineAndCharacterOfPosition(interIndex); }, toInterIndex: (lineAndCharacter) => { if (!this.sourceFile) { return -1; } return this.sourceFile.getPositionOfLineAndCharacter( lineAndCharacter.line, lineAndCharacter.character ); }, toSourceLineAndCharacter: (lineAndCharacter) => { if (!this.sourceFile) { return this.lineAndCharacter.defaultValue; } if (!warn.isQingkuaiFileName(this.filePath)) { return lineAndCharacter; } const fileInfo = this.adapter.service.ensureGetQingkuaiFileInfo(this.filePath); const interIndex = this.lineAndCharacter.toInterIndex(lineAndCharacter); const sourceIndex = fileInfo.getSourceIndex(interIndex); if (!warn.isIndexesInvalid(sourceIndex)) { const position = fileInfo.getPositionByIndex(sourceIndex); return { line: position.line - 1, character: position.column }; } }, defaultValue: { line: 0, character: 0 } }); __publicField$3(this, "textSpan", { toSourceTextSpan: (span) => { if (!warn.isQingkuaiFileName(this.filePath)) { return span; } const fileInfo = this.adapter.service.ensureGetQingkuaiFileInfo(this.filePath); const sourceStart = fileInfo.getSourceIndex(span.start); const sourceEnd = fileInfo.getSourceIndex(span.start + span.length); if (warn.isIndexesInvalid(sourceStart, sourceEnd)) { return this.textSpan.defaultValue; } return { start: sourceStart, length: sourceEnd - sourceStart, [warn.SOURCE_SPAN_MARK]: true }; }, defaultValue: { start: 0, length: 0 } }); __publicField$3(this, "languageServerRange", { fromTextSpan: (span) => { if (!warn.isQingkuaiFileName(this.filePath)) { return this.languageServerRange.fromSourceStartAndEnd( span.start, span.start + span.length ); } const fileInfo = this.adapter.service.ensureGetQingkuaiFileInfo(this.filePath); const sourceStartIndex = fileInfo.getSourceIndex(span.start); const sourceEndIndex = fileInfo.getSourceIndex(span.start + span.length); if (warn.isIndexesInvalid(sourceStartIndex, sourceEndIndex)) { return this.languageServerRange.defaultValue; } return this.languageServerRange.fromSourceStartAndEnd(sourceStartIndex, sourceEndIndex); }, fromSourceTextSpan: (span) => { return this.languageServerRange.fromSourceStartAndEnd( span.start, span.start + span.length ); }, fromSourceStartAndEnd: (sourceStart, sourceEnd) => { if (!this.sourceFile) { return this.languageServerRange.defaultValue; } if (!warn.isQingkuaiFileName(this.filePath)) { return { start: this.sourceFile.getLineAndCharacterOfPosition(sourceStart), end: this.sourceFile.getLineAndCharacterOfPosition(sourceEnd) }; } const fileInfo = this.adapter.service.ensureGetQingkuaiFileInfo(this.filePath); const startPosition = fileInfo.getPositionByIndex(sourceStart); const endPosition = fileInfo.getPositionByIndex(sourceEnd); return { start: { line: startPosition.line - 1, character: startPosition.column }, end: { line: endPosition.line - 1, character: endPosition.column } }; }, defaultValue: { start: this.lineAndCharacter.defaultValue, end: this.lineAndCharacter.defaultValue } }); this.adapter = adapter; this.filePath = adapter.getNormalizedPath(fileName); this.sourceFile = adapter.getDefaultSourceFile(this.filePath); } } function getAndConvertDiagnostics(adapter, fileName) { const filePath = adapter.getNormalizedPath(fileName); const languageService = adapter.getDefaultLanguageService(filePath); const fileInfo = adapter.service.ensureGetQingkuaiFileInfo(filePath); if (!warn.debugAssert(languageService)) { return []; } const diagnostics = fileInfo.lsDiagnostics; const result = []; const diagnosticMethods = ["getSyntacticDiagnostics"]; const isSemanticProject = adapter.projectService.serverMode === adapter.ts.LanguageServiceMode.Semantic; if (isSemanticProject) { diagnosticMethods.push("getSemanticDiagnostics", "getSuggestionDiagnostics"); } diagnosticMethods.forEach((m) => diagnostics.push(...languageService[m](fileName))); for (const item of diagnostics) { const start = item.start ?? 0; const end = start + (item.length ?? 0); const sourceStart = fileInfo.getSourceIndex(start); const sourceEnd = fileInfo.getSourceIndex(end); if (warn.isIndexesInvalid(sourceStart, sourceEnd) || shouldDiagnosticBeIgnored(item, fileInfo, [sourceStart, sourceEnd])) { continue; } const relatedInformations = []; const filteredRelatedInformations = (item.relatedInformation || []).filter((ri) => !!ri.file); for (const relatedInfo of filteredRelatedInformations) { const relatedSourceFile = relatedInfo.file; if (!relatedSourceFile) { continue; } let range; const start2 = relatedInfo.start ?? 0; const end2 = start2 + (relatedInfo.length ?? 0); const relatedFileInfo = adapter.service.ensureGetQingkuaiFileInfo( relatedSourceFile.fileName ); const relatedFilePath = adapter.getNormalizedPath(relatedSourceFile.fileName); if (!warn.isQingkuaiFileName(relatedFilePath ?? "")) { range = { start: relatedSourceFile.getLineAndCharacterOfPosition(start2), end: relatedSourceFile.getLineAndCharacterOfPosition(end2) }; } else { const relatedSourceStart = relatedFileInfo.getSourceIndex(start2); const relatedSourceEnd = relatedFileInfo.getSourceIndex(end2); if (warn.isIndexesInvalid(relatedSourceStart, relatedSourceEnd)) { continue; } const relatedLocationConvertor = adapter.service.createLocationConvertor(relatedFilePath); range = relatedLocationConvertor.languageServerRange.fromSourceStartAndEnd( relatedSourceStart, relatedSourceEnd ); } relatedInformations.push({ range, filePath: relatedFilePath, message: formatDiagnosticMessage(relatedInfo.messageText) }); } const locationConvertor = adapter.service.createLocationConvertor(filePath); const formattedMsg = formatDiagnosticMessage(item.messageText); result.push({ message: formattedMsg.replaceAll(` Did you mean '${compiler.constants.LSC.UTIL}'?`, ""), range: locationConvertor.languageServerRange.fromSourceStartAndEnd( sourceStart, sourceEnd ), url: item.url, code: item.code, relatedInformations, kind: item.category, source: item.source || "ts", deprecated: Boolean(item.reportsDeprecated), unnecessary: Boolean(item.reportsUnnecessary) }); } return result; } function shouldDiagnosticBeIgnored(diag, fileInfo, sourceRange) { switch (diag.code) { case 6133: { if (fileInfo.isPositionFlagSetAtIndex("IsAttributeStart", sourceRange[0])) { return true; } break; } } } function formatDiagnosticMessage(mt, indentLevel = 0) { const indentStr = " ".repeat(indentLevel); if (warn.isString(mt)) { return indentStr + mt; } const nextMsg = mt.next?.reduce((p, c) => { return p + "\n" + indentStr + formatDiagnosticMessage(c, indentLevel + 1); }, ""); return indentStr + mt.messageText + (nextMsg ?? ""); } const ExternalGlobalTypeWithGenerics = () => { return [ 3001, `The external type with generics cannot be used as the global type.`, "https://qingkuai.dev/misc/typescript.html#generics" ]; }; const GlobalTypeIsNonObjectTs = (name) => { return [ 3002, `The global type "${name}" must satisfy the constraint of being an object type.`, "https://qingkuai.dev/misc/typescript.html#component-attribute-types" ]; }; var __defProp$2 = Object.defineProperty; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); function confirmTypesForCompileResult(adapter, fileInfo) { let sourceFile; let typeChecker; let componentFuncNode; let componentReturnsNode; const updateSourceFile = () => { const program = adapter.getDefaultProgram(fileInfo.path); sourceFile = program?.getSourceFile(fileInfo.path); typeChecker = program?.getTypeChecker(); return sourceFile; }; if (fileInfo.typesConfirmed || !updateSourceFile()) { return; } fileInfo.typesConfirmed = true; const componentGenerics = []; const slotNames = []; const edit = new FileEdit(fileInfo, updateSourceFile); const globalTypes = {}; const extractedSlotContexts = []; const anyValueStr = compiler.constants.LSC.UTIL + ".anyValue"; const getTypeDelayIndexesSet = new Set(fileInfo.getTypeDelayIndexes); walkTsNode(sourceFile, (node) => { var _a; if (ts.isFunctionDeclaration(node) && node.name?.text === compiler.constants.LSC.COMPONENT && isInTopScope(node)) { componentFuncNode = node; } if (!fileInfo.isTS) { const jsDocs = node.jsDoc; jsDocs?.forEach((jsDoc) => { for (const jsDocTag of jsDoc.tags ?? []) { if (ts.isJSDocTypedefTag(jsDocTag) && jsDocTag.name?.text && warn.GLOBAL_TYPE_IDS.has(jsDocTag.name.text) && !globalTypes[jsDocTag.name.text] && isInComponentFunctionTopScope(jsDocTag)) { const constraints = []; const genericNames = []; const globalType = typeChecker.getTypeAtLocation(jsDocTag); const templateTags = jsDocTag.parent.tags?.filter(ts.isJSDocTemplateTag); if (!(globalType.flags & ts.TypeFlags.Object)) { fileInfo.pushDiagnostic( jsDocTag.getStart(), jsDocTag.getEnd(), warn.GlobalTypeIsNonObjectJs(jsDocTag.name.text) ); } templateTags?.forEach((tag) => { tag.typeParameters.forEach((param, index) => { const genericName = param.name.getText() + jsDocTag.name.text.slice(0, 1); const constraint = param.constraint ?? (index === 0 ? tag.constraint : void 0); const constraintText = constraint?.getText() ?? ""; if (constraintText) { constraints.push(constraintText); } genericNames.push(genericName); componentGenerics.push(`@template${constraintText} ${genericName}`); }); }); globalTypes[jsDocTag.name.text] = { constraints, genericNames, type: globalType, isExternal: false }; } } }); } else if ((ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && warn.GLOBAL_TYPE_IDS.has(node.name.text) && !globalTypes[node.name.text] && isInComponentFunctionTopScope(node)) { const constraints = []; const genericNames = []; const globalType = typeChecker.getTypeAtLocation(node); if (!(globalType.flags & ts.TypeFlags.Object)) { fileInfo.pushDiagnostic( node.name.getStart(), node.name.getEnd(), GlobalTypeIsNonObjectTs(node.name.text) ); } node.typeParameters?.forEach((param) => { const genericName = param.name.getText() + node.name.text.slice(0, 1); if (param.constraint) { constraints.push(param.constraint.getText()); } componentGenerics.push( `${genericName}${param.constraint ? ` extends ${param.constraint.getText()}` : ""}` ); genericNames.push(genericName); }); globalTypes[node.name.text] = { genericNames, constraints, isExternal: false, type: typeChecker.getTypeAtLocation(node) }; } if (ts.isImportDeclaration(node)) { if (isInTopScope(node)) { const identifiers = []; if (ts.isImportDeclaration(node) && node.importClause) { if (node.importClause.name) { identifiers.push(node.importClause.name); } if (node.importClause.namedBindings && !ts.isNamespaceImport(node.importClause.namedBindings)) { for (const spec of node.importClause.namedBindings.elements) { identifiers.push(spec.name); } } } for (const id of identifiers) { if (!globalTypes[id.text] && warn.GLOBAL_TYPE_IDS.has(id.text)) { const symbol = typeChecker.getSymbolAtLocation(id); const aliasedSymbol = symbol && typeChecker.getAliasedSymbol(symbol); if (aliasedSymbol && aliasedSymbol.flags & ts.SymbolFlags.Type) { const globalType = typeChecker.getTypeAtLocation( aliasedSymbol.declarations[0] ); if (!(globalType.flags & ts.TypeFlags.Object)) { fileInfo.pushDiagnostic( id.getStart(), id.getEnd(), GlobalTypeIsNonObjectTs(id.text) ); } else if (globalType.objectFlags & ts.ObjectFlags.Reference && typeChecker.getTypeArguments(globalType).length) { fileInfo.pushDiagnostic( id.getStart(), id.getEnd(), ExternalGlobalTypeWithGenerics() ); } globalTypes[id.text] = { isExternal: true, constraints: [], genericNames: [], type: globalType }; } } } } } if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && ts.isIdentifier(node.expression.expression) && getTypeDelayIndexesSet.has(node.getStart()) && node.expression.getText() === compiler.constants.LSC.GET_TYPE_DELAY_MARKING) { const slotNameNode = node.arguments[0]; const contextPropertyNode = node.arguments[1]; if (slotNameNode.text !== slotNames[slotNames.length - 1]?.name) { slotNames.push({ name: slotNameNode.text, sourceRange: [ fileInfo.getSourceIndex(slotNameNode.getStart()), fileInfo.getSourceIndex(slotNameNode.getEnd()) ] }); fileInfo.slotNames.push(slotNameNode.text); } (extractedSlotContexts[_a = slotNames.length - 1] ?? (extractedSlotContexts[_a] = [])).push({ property: { name: contextPropertyNode.text, sourceRange: [ fileInfo.getSourceIndex(contextPropertyNode.getStart()), fileInfo.getSourceIndex(contextPropertyNode.getEnd()) ] }, valueType: typeChecker.typeToString( typeChecker.getTypeAtLocation(node.arguments[2]) ) }); } if (ts.isReturnStatement(node) && node.getEnd() === componentFuncNode.getEnd() - 2) { componentReturnsNode = node; } }); warn.traverseObject(globalTypes, (kind, globalType) => { if (globalType && (kind === "Props" || kind === "Refs") && globalType.type.flags & ts.TypeFlags.Object) { for (const property of typeChecker.getPropertiesOfType(globalType.type)) { const propertyType = typeChecker.getTypeOfSymbolAtLocation(property, sourceFile); const attributeItem = { kind, name: property.name, stringCandidates: [], type: typeChecker.typeToString(propertyType), optional: !!(property.flags & ts.SymbolFlags.Optional), mayBeEvent: kind === "Props" && isMayBeEventType(propertyType), couldBeString: !!(propertyType.flags & ts.TypeFlags.StringLike) }; if (propertyType.isUnion()) { propertyType.types.forEach((t) => { if (t.flags & ts.TypeFlags.StringLiteral) { attributeItem.stringCandidates.push( JSON.parse(typeChecker.typeToString(t)) ); } attributeItem.couldBeString || (attributeItem.couldBeString = !!(t.flags & ts.TypeFlags.StringLike)); }); } else if (propertyType.flags & ts.TypeFlags.StringLiteral) { attributeItem.stringCandidates.push( JSON.parse(typeChecker.typeToString(propertyType)) ); } if (isEnumerablePropertyOfGlobalTypes(property)) { fileInfo.attributes.push(attributeItem); } } } }); ["Props", "Refs"].forEach((kind) => { const globalType = globalTypes[kind]; if (!globalType) { edit.setEditIndex(componentFuncNode.getStart()); if (fileInfo.isTS) { edit.push(`type ${kind} = ${compiler.constants.LSC.UTIL}.EmptyObject; `); } else { edit.push(`/** @typedef {${compiler.constants.LSC.UTIL}.EmptyObject} ${kind} */ `); } } }); if (fileInfo.isTS && !edit.isEmpty) { edit.flush(); } let contextRefsType = "Refs"; let declareRefsType = "Refs"; let contextPropsType = "Props"; let declarePropsType = "Props"; if (globalTypes.Refs?.constraints.length) { declareRefsType = `Refs<${globalTypes.Refs.constraints.join(", ")}>`; } if (globalTypes.Refs?.genericNames.length) { contextRefsType = `Refs<${globalTypes.Refs.genericNames.join(", ")}>`; } if (globalTypes.Props?.constraints.length) { declarePropsType = `Props<${globalTypes.Props.constraints.join(", ")}>`; } if (globalTypes.Props?.genericNames.length) { contextPropsType = `Props<${globalTypes.Props.genericNames.join(", ")}>`; } if (componentReturnsNode) { if (fileInfo.isTS) { edit.setEditIndex(componentFuncNode.body.getStart() + 2); edit.push(` const props: Readonly<${declarePropsType}> = ${anyValueStr}; `); edit.push(`