qingkuai-language-service
Version:
The language service for qingkuai
1,312 lines (1,296 loc) • 69.9 kB
JavaScript
'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(`