expressive-code-twoslash
Version:
Add Twoslash support to your Expressive Code TypeScript code blocks.
1,186 lines • 95.5 kB
JavaScript
import path from "node:path";
import { ExpressiveCodeAnnotation, PluginStyleSettings, definePlugin, lighten, toHexColor } from "@expressive-code/core";
import { ExpressiveCode } from "expressive-code";
import ts from "typescript";
import { h } from "@expressive-code/core/hast";
import { fromMarkdown } from "mdast-util-from-markdown";
import { gfmFromMarkdown } from "mdast-util-gfm";
import { toHast } from "mdast-util-to-hast";
import fs from "node:fs";
import tsParser from "@typescript-eslint/parser";
import { css, stylesheet } from "css-js-gen";
//#region src/helpers/comparisons.ts
/**
* Checks if the given node is of type `NodeCompletion`.
*
* @param node - The node to check.
* @returns True if the node is of type `NodeCompletion`, otherwise false.
*/
function isNodeCompletion(node) {
return node.type === "completion";
}
/**
* Compares two TwoslashNode objects based on specified criteria.
*
* @param node1 - The first TwoslashNode to compare.
* @param node2 - The second TwoslashNode to compare.
* @param checks - An object specifying which properties to check for equality.
* @param checks.line - If true, compares the line property of the nodes.
* @param checks.start - If true, compares the start property of the nodes.
* @param checks.character - If true, compares the character property of the nodes.
* @param checks.length - If true, compares the length property of the nodes.
* @param checks.text - If true, compares the text property of the nodes.
* @returns A boolean indicating whether the nodes are considered equal based on the specified criteria.
*/
function compareNodes(node1, node2, checks) {
if (checks.line && node1.line !== node2.line) return false;
if (checks.start && node1.start !== node2.start) return false;
if (checks.character && node1.character !== node2.character) return false;
if (checks.length && node1.length !== node2.length) return false;
if (!isNodeCompletion(node1) && !isNodeCompletion(node2) && checks.text && node1.text !== node2.text) return false;
return true;
}
//#endregion
//#region src/helpers/ec-config.ts
/**
* Generates an ExpressiveCodeConfig object based on the provided ResolvedExpressiveCodeEngineConfig.
*
* @param config - The resolved configuration object for the Expressive Code Engine.
* @returns An ExpressiveCodeConfig object with properties derived from the input config.
*/
const ecConfig = (config) => {
return {
cascadeLayer: config.cascadeLayer,
customizeTheme: config.customizeTheme,
defaultLocale: config.defaultLocale,
defaultProps: config.defaultProps,
logger: config.logger,
minSyntaxHighlightingColorContrast: config.minSyntaxHighlightingColorContrast,
styleOverrides: config.styleOverrides,
themeCssRoot: config.themeCssRoot,
themeCssSelector: config.themeCssSelector,
themes: config.themes,
useDarkModeMediaQuery: config.useDarkModeMediaQuery,
useStyleReset: config.useStyleReset,
useThemedScrollbars: config.useThemedScrollbars,
useThemedSelectionColors: config.useThemedSelectionColors,
frames: {
showCopyToClipboardButton: false,
extractFileNameFromCode: false
}
};
};
//#endregion
//#region src/helpers/includes.ts
var TwoslashIncludesManager = class {
constructor(map = /* @__PURE__ */ new Map()) {
this.map = map;
}
add(name, code) {
const lines = [];
code.split("\n").forEach((l, _i) => {
const trimmed = l.trim();
if (trimmed.startsWith("// - ")) {
const key = trimmed.split("// - ")[1].split(" ")[0];
this.map.set(`${name}-${key}`, lines.join("\n"));
} else lines.push(l);
});
this.map.set(name, lines.join("\n"));
}
applyInclude(code) {
const reMarker = /\/\/ @include: (.*)$/gm;
const toReplace = [];
for (const match of code.matchAll(reMarker)) {
const key = match[1];
const replaceWith = this.map.get(key);
if (!replaceWith) {
const msg = `Could not find an include with the key: '${key}'.\nThere is: ${Array.from(this.map.keys())}.`;
throw new Error(msg);
}
toReplace.push([
match.index,
match[0].length,
replaceWith
]);
}
let newCode = code.toString();
for (const [index, length, replacementCode] of toReplace.reverse()) newCode = newCode.slice(0, index) + replacementCode + newCode.slice(index + length);
return newCode;
}
};
/**
* An "include [name]" segment in a raw meta string has a "name" that is a sequence of word
* characters, possibly connected by dashes, that ends at a word boundary.
*/
const INCLUDE_META_REGEX = /include\s+([\w-]+)\b.*/;
/**
* @param meta The raw meta string of a code block, e.g. 'twoslash include main-hello-world meta=miscellaneous'.
* @returns The name of the reusable code block, e.g. "main-hello-world", if it exists.
*/
function parseIncludeMeta(meta) {
if (!meta) return null;
return meta.match(INCLUDE_META_REGEX)?.[1] ?? null;
}
//#endregion
//#region src/icons/completionIcons.ts
const completionIcons = {
module: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [
h("path", {
fill: "currentColor",
d: "M11 2H2v9h2V4h7V2z"
}, []),
h("path", {
fill: "currentColor",
d: "M2 21v9h9v-2H4v-7H2z"
}, []),
h("path", {
fill: "currentColor",
d: "M30 11V2h-9v2h7v7h2z"
}, []),
h("path", {
fill: "currentColor",
d: "M21 30h9v-9h-2v7h-7v2z"
}, []),
h("path", {
fill: "currentColor",
d: "M25.49 10.13l-9-5a1 1 0 0 0-1 0l-9 5A1 1 0 0 0 6 11v10a1 1 0 0 0 .51.87l9 5a1 1 0 0 0 1 0l9-5A1 1 0 0 0 26 21V11a1 1 0 0 0-.51-.87zM16 7.14L22.94 11L16 14.86L9.06 11zM8 12.7l7 3.89v7.71l-7-3.89zm9 11.6v-7.71l7-3.89v7.71z"
}, [])
]),
class: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M26 16a3.961 3.961 0 0 0-2.02.566l-2.859-2.859l2.293-2.293a2 2 0 0 0 0-2.828l-6-6a2 2 0 0 0-2.828 0l-6 6a2 2 0 0 0 0 2.828l2.293 2.293l-2.859 2.859a4.043 4.043 0 1 0 1.414 1.414l2.859-2.859l2.293 2.293a1.977 1.977 0 0 0 .414.31V22h-3v8h8v-8h-3v-4.277a1.977 1.977 0 0 0 .414-.309l2.293-2.293l2.859 2.859A3.989 3.989 0 1 0 26 16M8 20a2 2 0 1 1-2-2a2.002 2.002 0 0 1 2 2m10 4v4h-4v-4zm-2-8l-6-6l6-6l6 6Zm10 6a2 2 0 1 1 2-2a2.002 2.002 0 0 1-2 2"
}, [])]),
method: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [
h("path", {
fill: "currentColor",
d: "m19.626 29.526l-.516-1.933a12.004 12.004 0 0 0 6.121-19.26l1.538-1.28a14.003 14.003 0 0 1-7.143 22.473"
}, []),
h("path", {
fill: "currentColor",
d: "M10 29H8v-3.82l.804-.16C10.262 24.727 12 23.62 12 20v-1.382l-4-2v-2.236l4-2V12c0-5.467 3.925-9 10-9h2v3.82l-.804.16C21.738 7.273 20 8.38 20 12v.382l4 2v2.236l-4 2V20c0 5.467-3.925 9-10 9m0-2c4.935 0 8-2.682 8-7v-2.618l3.764-1.882L18 13.618V12c0-4.578 2.385-6.192 4-6.76V5c-4.935 0-8 2.682-8 7v1.618L10.236 15.5L14 17.382V20c0 4.578-2.385 6.192-4 6.76Z"
}, []),
h("path", {
fill: "currentColor",
d: "M5.231 24.947a14.003 14.003 0 0 1 7.147-22.474l.516 1.932a12.004 12.004 0 0 0-6.125 19.263Z"
}, [])
]),
property: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M12.1 2a9.8 9.8 0 0 0-5.4 1.6l6.4 6.4a2.1 2.1 0 0 1 .2 3a2.1 2.1 0 0 1-3-.2L3.7 6.4A9.84 9.84 0 0 0 2 12.1a10.14 10.14 0 0 0 10.1 10.1a10.9 10.9 0 0 0 2.6-.3l6.7 6.7a5 5 0 0 0 7.1-7.1l-6.7-6.7a10.9 10.9 0 0 0 .3-2.6A10 10 0 0 0 12.1 2m8 10.1a7.61 7.61 0 0 1-.3 2.1l-.3 1.1l.8.8l6.7 6.7a2.88 2.88 0 0 1 .9 2.1A2.72 2.72 0 0 1 27 27a2.9 2.9 0 0 1-4.2 0l-6.7-6.7l-.8-.8l-1.1.3a7.61 7.61 0 0 1-2.1.3a8.27 8.27 0 0 1-5.7-2.3A7.63 7.63 0 0 1 4 12.1a8.33 8.33 0 0 1 .3-2.2l4.4 4.4a4.14 4.14 0 0 0 5.9.2a4.14 4.14 0 0 0-.2-5.9L10 4.2a6.45 6.45 0 0 1 2-.3a8.27 8.27 0 0 1 5.7 2.3a8.49 8.49 0 0 1 2.4 5.9"
}, [])]),
constructor: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M21.49 13.115l-9-5a1 1 0 0 0-1 0l-9 5A1.008 1.008 0 0 0 2 14v9.995a1 1 0 0 0 .52.87l9 5A1.004 1.004 0 0 0 12 30a1.056 1.056 0 0 0 .49-.135l9-5A.992.992 0 0 0 22 24V14a1.008 1.008 0 0 0-.51-.885zM11 27.295l-7-3.89v-7.72l7 3.89zm1-9.45L5.06 14L12 10.135l6.94 3.86zm8 5.56l-7 3.89v-7.72l7-3.89z"
}, []), h("path", {
fill: "currentColor",
d: "M30 6h-4V2h-2v4h-4v2h4v4h2V8h4V6z"
}, [])]),
interface: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M23 16.01a7 7 0 0 0-4.18 1.39l-4.22-4.22A6.86 6.86 0 0 0 16 9.01a7 7 0 1 0-2.81 5.59l4.21 4.22a7 7 0 1 0 5.6-2.81m-19-7a5 5 0 1 1 5 5a5 5 0 0 1-5-5"
}, [])]),
function: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [
h("path", {
fill: "currentColor",
d: "m19.626 29.526l-.516-1.933a12.004 12.004 0 0 0 6.121-19.26l1.538-1.28a14.003 14.003 0 0 1-7.143 22.473"
}, []),
h("path", {
fill: "currentColor",
d: "M10 29H8v-3.82l.804-.16C10.262 24.727 12 23.62 12 20v-1.382l-4-2v-2.236l4-2V12c0-5.467 3.925-9 10-9h2v3.82l-.804.16C21.738 7.273 20 8.38 20 12v.382l4 2v2.236l-4 2V20c0 5.467-3.925 9-10 9m0-2c4.935 0 8-2.682 8-7v-2.618l3.764-1.882L18 13.618V12c0-4.578 2.385-6.192 4-6.76V5c-4.935 0-8 2.682-8 7v1.618L10.236 15.5L14 17.382V20c0 4.578-2.385 6.192-4 6.76Z"
}, []),
h("path", {
fill: "currentColor",
d: "M5.231 24.947a14.003 14.003 0 0 1 7.147-22.474l.516 1.932a12.004 12.004 0 0 0-6.125 19.263Z"
}, [])
]),
string: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M29 22h-5a2.003 2.003 0 0 1-2-2v-6a2.002 2.002 0 0 1 2-2h5v2h-5v6h5zM18 12h-4V8h-2v14h6a2.003 2.003 0 0 0 2-2v-6a2.002 2.002 0 0 0-2-2m-4 8v-6h4v6zm-6-8H3v2h5v2H4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h6v-8a2.002 2.002 0 0 0-2-2m0 8H4v-2h4z"
}, [])])
};
//#endregion
//#region src/helpers/processors.ts
/**
* Splits the given code string into an array of objects, each containing the line index and the line content.
*
* @param code - The code string to be split into lines.
* @returns An array of objects, each with an `index` representing the line number and a `line` containing the line content.
*/
function splitCodeToLines(code) {
return code.split("\n").map((line, index) => ({
index,
line
}));
}
/**
* Processes a code block by replacing its content with the provided Twoslash code block.
*
* @param codeBlock - The ExpressiveCodeBlock instance representing the code block to be processed.
* @param codeWithIncludes - The original code block content including any includes.
* @param twoslashCode - The Twoslash code block to replace the original content with.
*/
function processTwoslashCodeBlock(codeBlock, codeWithIncludes, twoslashCode) {
const ecCodeBlock = splitCodeToLines(codeWithIncludes);
const twoslashCodeBlock = splitCodeToLines(twoslashCode);
for (const line of twoslashCodeBlock) {
const ln = codeBlock.getLine(line.index);
if (ln) ln.editText(0, ln.text.length, line.line);
}
if (twoslashCodeBlock.length < ecCodeBlock.length) {
for (let i = ecCodeBlock.length - 1; i >= twoslashCodeBlock.length; i--) if (codeBlock.getLine(i)) codeBlock.deleteLine(i);
}
}
/**
* Processes a NodeCompletion object and returns a CompletionItem.
*
* @param completion - The NodeCompletion object to process.
* @returns A CompletionItem containing the processed completion data.
*/
function processCompletion(completion) {
const items = completion.completions.map((c) => {
const kind = c.kind || "property";
const isDeprecated = "kindModifiers" in c && typeof c.kindModifiers === "string" && c.kindModifiers.split(",").includes("deprecated");
const icon = completionIcons[kind];
return {
name: c.name,
kind,
icon,
isDeprecated
};
}).slice(0, 5);
const { character, start, completionsPrefix } = completion;
return {
startCharacter: character,
completionsPrefix,
start,
length: start - character,
items
};
}
//#endregion
//#region src/helpers/regex.ts
/**
* Default tags used in the twoslash plugin.
*
* @constant
* @default ["annotate", "log", "warn", "error"]
*/
const twoslashDefaultTags = [
"annotate",
"log",
"warn",
"error"
];
/**
* Regular expression to match the word "twoslash" as a whole word.
*
* @constant {RegExp} reTrigger - The regular expression pattern.
* @type {RegExp}
*/
const reTrigger = /\btwoslash\b/;
/**
* A regular expression to match and clean up type annotations.
*
* This regular expression matches strings that start with an uppercase letter,
* followed by any word characters, and optionally a generic type parameter enclosed in angle brackets.
* The matched string ends with a colon.
*
* Example matches:
* - `TypeName:`
* - `GenericType<T>:`
*
* @constant {RegExp} reTypeCleanup - The regular expression pattern.
* @type {RegExp}
*/
const reTypeCleanup = /^[A-Z]\w*(<[^>]*>)?:/;
/**
* Regular expression to match the beginning of a function call.
* It matches any word characters followed by an opening parenthesis.
*
* @constant {RegExp} reFunctionCleanup - The regular expression pattern.
* @type {RegExp}
*/
const reFunctionCleanup = /^\w*\(/;
/**
* Regular expression to match leading property method annotations.
*
* This regex matches lines that start with an optional whitespace, followed by an opening parenthesis,
* and then any word characters or hyphens. The matched string ends with a closing parenthesis and optional whitespace.
*
* Example matches:
* - `(property) `
* - `(method) `
*
* @constant {RegExp} reLeadingPropertyMethod - The regular expression pattern.
* @type {RegExp}
*/
const reLeadingPropertyMethod = /^\(([\w-]+)\)\s+/gm;
/**
* Regular expression to match import statements.
*
* This regex matches lines that start with `import` followed by any characters until the end of the line.
*
* @constant {RegExp} reImportStatement - The regular expression pattern.
* @type {RegExp}
*/
const reImportStatement = /\nimport .*$/;
/**
* Regular expression to match interface or namespace declarations.
*
* This regex matches lines that start with `interface` or `namespace` followed by a space and any word characters.
*
* @constant {RegExp} reInterfaceOrNamespace - The regular expression pattern.
* @type {RegExp}
*/
const reInterfaceOrNamespace = /^(interface|namespace) \w+$/gm;
/**
* Regular expression to match JSDoc links.
*
* This regex matches `{@link ...}` patterns in a string.
*
* @constant {RegExp} reJsDocLink - The regular expression pattern.
* @type {RegExp}
*/
const reJsDocLink = /\{@link ([^}]*)\}/g;
/**
* Regular expression to match JSDoc tag filters.
*
* @constant {RegExp} reJsDocTagFilter - The regular expression pattern.
* @type {RegExp}
*/
const reJsDocTagFilter = /\b(example|description)\b/;
const jsdocTags = [
"abstract",
"access",
"alias",
"async",
"augments",
"author",
"borrows",
"callback",
"class",
"classdesc",
"constant",
"constructs",
"copyright",
"default",
"deprecated",
"description",
"enum",
"event",
"example",
"exports",
"external",
"file",
"fires",
"function",
"generator",
"global",
"hideconstructor",
"ignore",
"implements",
"inheritdoc",
"inner",
"instance",
"interface",
"kind",
"lends",
"license",
"listens",
"member",
"memberof",
"mixes",
"mixin",
"module",
"name",
"namespace",
"override",
"package",
"param",
"private",
"property",
"protected",
"public",
"readonly",
"requires",
"returns",
"see",
"since",
"static",
"summary",
"this",
"throws",
"todo",
"tutorial",
"type",
"typedef",
"variation",
"version",
"yields"
];
//#endregion
//#region src/helpers/rendering.ts
/**
* Renders markdown content with code blocks using ExpressiveCode.
*
* This function processes the given markdown string, converts it to an MDAST (Markdown Abstract Syntax Tree),
* and then transforms it into HAST (Hypertext Abstract Syntax Tree) with custom handlers for code blocks.
* It then uses ExpressiveCode to render the code blocks with syntax highlighting and other features.
*
* @param md - The markdown string to be processed.
* @param ec - An instance of ExpressiveCode used to render the code blocks.
* @returns A promise that resolves to an array of HAST nodes representing the processed markdown content.
*/
async function renderMarkdownWithCodeBlocks(md, ec) {
const nodes = toHast(fromMarkdown(md.replace(reJsDocLink, "$1"), { mdastExtensions: [gfmFromMarkdown()] }), { handlers: { code: (_, node) => {
const lang = node.lang || "plaintext";
return {
type: "element",
tagName: "div",
properties: {
class: "expressive-code",
"data-lang": lang
},
children: [{
type: "text",
value: node.value
}]
};
} } });
const codeBlocks = nodes.children ? nodes.children.filter((node) => node.type === "element" && node.tagName === "div" && node.properties.class === "expressive-code") : [];
for (const codeBlock of codeBlocks) if (codeBlock.type === "element") codeBlock.children = codeBlock.type === "element" && codeBlock.children[0].type === "text" ? (await ec.render({
code: codeBlock.children[0].value,
language: codeBlock.properties["data-lang"]
}).then((res) => {
codeBlock.properties["data-lang"] = null;
return res;
})).renderedGroupAst.children : [];
return nodes.children;
}
/**
* Renders the given markdown string inline using the ExpressiveCode instance.
*
* This function processes the markdown string and returns the rendered children.
* If the rendered children contain a single paragraph element, it returns the children of that paragraph.
* Otherwise, it returns the entire rendered children array.
*
* @param md - The markdown string to be rendered.
* @param ec - The ExpressiveCode instance used for rendering.
* @returns A promise that resolves to the rendered children.
*/
async function renderMDInline(md, ec) {
const children = await renderMarkdownWithCodeBlocks(md, ec);
if (children.length === 1 && children[0].type === "element" && children[0].tagName === "p") return children[0].children;
return children;
}
/**
* Checks if the given markdown string consists of a single paragraph element.
*
* @param md - The markdown string to check.
* @param filterTags - A boolean indicating whether to filter tags.
* @returns A boolean indicating if the markdown string is a single paragraph element.
*/
async function checkIfSingleParagraph(md, filterTags, ec) {
const children = await renderMDInline(md, ec);
if (filterTags) return !(children.length === 1 && children[0].type === "element" && children[0].tagName === "p");
return false;
}
/**
* The default hover info processor, which will do some basic cleanup
*/
function defaultHoverInfoProcessor(type) {
let content = type.replace(reLeadingPropertyMethod, "").replace(reImportStatement, "").replace(reInterfaceOrNamespace, "").trim();
if (content.match(reTypeCleanup)) content = `type ${content}`;
else if (content.match(reFunctionCleanup)) content = `function ${content}`;
if (content.length === 0) return false;
return content;
}
/**
* Filters tags based on specific keywords.
*
* @param tag - The tag string to be checked.
* @returns A boolean indicating whether the tag includes any of the specified keywords
*/
function filterTags(tag) {
return !reJsDocTagFilter.test(tag);
}
/**
* Renders the type information for the given text using the provided ExpressiveCode instance.
*
* @param text - The text to be processed and rendered.
* @param ec - An instance of ExpressiveCode used for rendering the text.
* @returns A promise that resolves to the rendered group AST.
*/
async function renderType(text, ec) {
const info = defaultHoverInfoProcessor(text);
if (typeof info === "string") {
const { renderedGroupAst } = await ec.render({
code: info,
language: "ts",
meta: ""
});
return renderedGroupAst;
}
const { renderedGroupAst } = await ec.render({
code: text,
language: "ts",
meta: ""
});
return renderedGroupAst;
}
/**
* Renders JSDoc comments for a given hover node.
*
* @param hover - The hover node containing documentation and tags.
* @param includeJsDoc - A boolean indicating whether to include JSDoc comments.
* @param ec - The ExpressiveCode instance used for rendering.
* @returns A promise that resolves to an object containing rendered documentation and tags.
*/
async function renderJSDocs(hover, includeJsDoc, ec, allowNonStandardJsDocTags) {
if (!includeJsDoc) return {
docs: [],
tags: []
};
return {
docs: hover.docs ? h("div.twoslash-popup-docs", [h("p", hover.docs ? await renderMarkdownWithCodeBlocks(hover.docs, ec) : [])]) : [],
tags: hover.tags ? h("div.twoslash-popup-docs.twoslash-popup-docs-tags", [...await Promise.all(hover.tags ? hover.tags.map(async (tag) => (allowNonStandardJsDocTags ? true : jsdocTags.includes(tag[0])) ? h("p.twoslash-popup-docs-tagline", [h("span.twoslash-popup-docs-tag-name", `@${tag[0]}`), tag[1] ? [await checkIfSingleParagraph(tag[1], filterTags(tag[0]), ec) ? " ― " : " ", h("span.twoslash-popup-docs-tag-value", await renderMDInline(tag[1], ec))] : []]) : []) : [])]) : []
};
}
//#endregion
//#region src/helpers/string-gen.ts
/**
* Returns a string representation of a custom tag.
*
* @param tag - The custom tag to convert to a string. Can be one of "warn", "annotate", or "log".
* @returns A string that represents the custom tag. Returns "Warning" for "warn", "Message" for "annotate",
* "Log" for "log", and "Error" for any other value.
*/
function getCustomTagString(tag) {
switch (tag) {
case "warn": return "Warning";
case "annotate": return "Message";
case "log": return "Log";
default: return "Error";
}
}
/**
* Returns a custom CSS class name based on the provided TwoslashTag.
*
* @param tag - The TwoslashTag to get the custom class for. Possible values are "warn", "annotate", "log", or any other string.
* @returns The corresponding CSS class name as a string.
* - "twoslash-custom-level-warning" for "warn"
* - "twoslash-custom-level-suggestion" for "annotate"
* - "twoslash-custom-level-message" for "log"
* - "twoslash-custom-level-error" for any other value
*/
function getCustomTagClass(tag) {
switch (tag) {
case "warn": return "twoslash-custom-level-warning";
case "annotate": return "twoslash-custom-level-suggestion";
case "log": return "twoslash-custom-level-message";
default: return "twoslash-custom-level-error";
}
}
/**
* Returns a CSS class name based on the error level of the provided NodeError.
*
* @param error - The NodeError object containing the error level.
* @returns A string representing the CSS class name corresponding to the error level.
*
* The possible error levels and their corresponding CSS class names are:
* - "warning" -> "twoslash-error-level-warning"
* - "suggestion" -> "twoslash-error-level-suggestion"
* - "message" -> "twoslash-error-level-message"
* - Any other value -> "twoslash-error-level-error"
*/
function getErrorLevelClass(error) {
switch (error.level) {
case "warning": return "twoslash-error-level-warning";
case "suggestion": return "twoslash-error-level-suggestion";
case "message": return "twoslash-error-level-message";
default: return "twoslash-error-level-error";
}
}
/**
* Returns a string representation of the error level.
*
* @param error - The error object containing the level property.
* @returns A string that represents the error level. Possible values are:
* - "Warning" for level "warning"
* - "Suggestion" for level "suggestion"
* - "Message" for level "message"
* - "Error" for any other level
*/
function getErrorLevelString(error) {
switch (error.level) {
case "warning": return "Warning";
case "suggestion": return "Suggestion";
case "message": return "Message";
default: return "Error";
}
}
//#endregion
//#region src/helpers/utils.ts
/**
* Calculates the width of a given text in pixels based on the character location, font size, and character width.
*
* @param textLoc - The location of the text (number of characters).
* @param fontSize - The font size in pixels. Defaults to 16.
* @param charWidth - The width of a single character in pixels. Defaults to 8.
* @returns The width of the text in pixels.
*/
function getTextWidthInPixels(textLoc, fontSize = 16, charWidth = 8) {
return textLoc * charWidth * (fontSize / 16);
}
/**
* Merges custom tags from the provided `twoslashOptions` with the default tags.
* Ensures that there are no duplicate tags in the final list.
*
* @param twoslashOptions - The options object containing custom tags to be merged.
* @returns A new `TwoslashOptions` object with merged custom tags.
*/
function checkForCustomTagsAndMerge(twoslashOptions) {
const customTags = twoslashOptions?.customTags ?? [];
const allTags = [...twoslashDefaultTags];
for (const tag of customTags) if (!allTags.includes(tag)) allTags.push(tag);
return {
...twoslashOptions,
customTags: allTags
};
}
const BuiltInTwoslashers = ["twoslash", "eslint"];
/**
* Retrieves or creates a base Twoslash instance and caches it in the `TwoslasherMap` for future use. If an instance already exists for the "twoslash" key, it returns the cached instance; otherwise, it creates a new one using the provided options and stores it in the map before returning it.
*/
const getBaseTwoslasher = (opts, TwoslasherMap) => {
const key = "twoslash";
const twoslasher = TwoslasherMap.get(key);
if (!twoslasher) {
const instance = async () => (await import("@ec-ts/twoslash")).createTwoslasher(opts);
TwoslasherMap.set(key, instance);
return instance;
}
return twoslasher;
};
/**
* Retrieves or creates a Twoslash instance specific to Vue and caches it in the `TwoslasherMap` for future use. If an instance already exists for the "twoslash-vue" key, it returns the cached instance; otherwise, it attempts to create a new one using the provided options and stores it in the map before returning it. If the module fails to load, it logs an error and throws a new error with a user-friendly message.
*/
const getVueTwoslasher = (opts, TwoslasherMap) => {
const key = "twoslash-vue";
const twoslasher = TwoslasherMap.get(key);
if (!twoslasher) try {
const instance = async () => (await import("@ec-ts/twoslash-vue")).createTwoslasher(opts);
TwoslasherMap.set(key, instance);
return instance;
} catch (error) {
console.error("Failed to load twoslash-vue:", error);
throw new Error("Failed to load twoslash-vue. Please ensure vue is installed and try again.");
}
return twoslasher;
};
/**
* Retrieves or creates a Twoslash instance specific to ESLint and caches it in the `TwoslasherMap` for future use. If an instance already exists for the "eslint" key, it returns the cached instance; otherwise, it attempts to create a new one using the provided options and stores it in the map before returning it. If the module fails to load, it logs an error and throws a new error with a user-friendly message.
*/
const getEslintTwoslasher = (opts, TwoslasherMap) => {
const key = "eslint";
const twoslasher = TwoslasherMap.get(key);
if (!twoslasher) try {
const instance = async () => (await import("twoslash-eslint")).createTwoslasher(opts);
TwoslasherMap.set(key, instance);
return instance;
} catch (error) {
console.error("Failed to load twoslash-eslint:", error);
throw new Error("Failed to load twoslash-eslint. Please ensure eslint is installed and try again.");
}
return twoslasher;
};
/**
* A map that holds the configuration for built-in twoslash instances, including their trigger patterns, supported languages, and the corresponding twoslasher functions.
*/
const TwoslashInstanceMap = (TwoslasherMap) => new Map([["twoslash", {
trigger: reTrigger,
languages: [
"ts",
"tsx",
"vue"
],
twoslashers: (options) => ({
default: getBaseTwoslasher(options, TwoslasherMap),
vue: getVueTwoslasher(options, TwoslasherMap)
})
}], ["eslint", {
trigger: /\beslint\b/,
languages: ["ts", "tsx"],
twoslashers: (options) => ({ default: getEslintTwoslasher(options, TwoslasherMap) })
}]]);
/**
* Retrieves a twoslasher instance configuration and applies optional overrides from plugin options.
*
* @param key - The built-in twoslasher type identifier to retrieve from the instance map
* @param opts - Optional plugin configuration containing instance-specific overrides
*
* @returns An object containing the twoslasher instances, trigger pattern, and supported languages
*
* @throws {Error} When no twoslash instance is found for the provided key
*
* @internal
*/
const __getTwoslasherAndOverride = (key, opts, TwoslasherMap) => {
const data = TwoslashInstanceMap(TwoslasherMap).get(key);
if (!data) throw new Error(`No twoslash instance found for key: ${key}`);
const { trigger, languages, twoslashers } = data;
const triggerOverride = opts?.[key]?.explicitTrigger;
const languagesOverride = opts?.[key]?.languages;
return {
twoslashers,
trigger: triggerOverride instanceof RegExp ? triggerOverride : trigger,
languages: languagesOverride ?? languages
};
};
/**
* Creates a function that applies Twoslash transformations to code blocks.
*
* @param opts - Plugin configuration options for Twoslash instances
* @param options - Twoslash processing options
* @returns A function that processes a code block and applies the appropriate Twoslash transformer based on language and metadata triggers
*
* @template A - The return type of the transformation function
*
* @example
* ```ts
* const transformer = getTwoslasher(config, options);
* const result = await transformer(codeBlock, (twoslash) => twoslash.transform());
* ```
*/
const getTwoslasher = (opts, options, TwoslasherMap) => {
const twoslashersMap = BuiltInTwoslashers.reduce((acc, key) => {
acc[key] = __getTwoslasherAndOverride(key, opts, TwoslasherMap);
return acc;
}, {});
return async (codeBlock, fn) => {
for (const key in twoslashersMap) {
const { trigger, languages, twoslashers } = twoslashersMap[key];
if (languages.includes(codeBlock.language) && trigger.test(codeBlock.meta)) return fn(await (twoslashers(options)[codeBlock.language] ?? twoslashers(options).default)(), key);
}
return null;
};
};
const resolveTsconfigPath = (paths, overridePath) => {
if (overridePath === void 0) return path.join(paths, "tsconfig.json");
if (path.isAbsolute(overridePath) === true) return overridePath;
return path.resolve(paths, overridePath);
};
const parseSnippetTsconfig = (tsconfigPath) => {
if (fs.existsSync(tsconfigPath) === false) throw new Error(`tsconfig not found at ${tsconfigPath}`);
const source = fs.readFileSync(tsconfigPath, "utf8");
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
if (configFile.error !== void 0) {
const message = ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
throw new Error(`Unable to read ${tsconfigPath}: ${message}`);
}
return {
source,
options: ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(tsconfigPath), void 0, tsconfigPath).options
};
};
//#endregion
//#region src/annotations/completion.ts
/**
* Represents a completion annotation for Twoslash.
* Extends the `ExpressiveCodeAnnotation` class.
*/
var TwoslashCompletionAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-completion-annotation";
/**
* Creates an instance of TwoslashCompletionAnnotation.
*
* @param completion - The completion item to be annotated.
* @param query - The node completion query.
*/
constructor(completion, query, line) {
super({ inlineRange: {
columnStart: completion.startCharacter,
columnEnd: completion.startCharacter + line.text.length
} });
this.completion = completion;
this.query = query;
this.line = line;
}
/**
* Renders the completion annotation.
* @param nodesToTransform - The nodes to transform with the error box annotation.
* @returns An array of transformed nodes with the error box annotation.
*/
render({ nodesToTransform }) {
return nodesToTransform.map((node) => {
return h("span.twoslash-noline", [
h("span.twoslash-cursor", [" "]),
node,
h("div.twoslash-completion", { style: { "margin-left": `${getTextWidthInPixels(this.completion.startCharacter)}px` } }, [h("div.twoslash-completion-container", [...this.completion.items.map((item, index) => {
return h("div.twoslash-completion-item", { class: `
${item.isDeprecated ? "twoslash-completion-item-deprecated" : ""} ${index === 0 ? "" : "twoslash-completion-item-separator"}` }, [h("span.twoslash-completion-icon", { class: item.kind }, item.icon), h("span.twoslash-completion-name", [h("span.twoslash-completion-name-matched", [item.name.startsWith(this.query.completionsPrefix) ? this.query.completionsPrefix : ""]), h("span.twoslash-completion-name-unmatched", [item.name.startsWith(this.query.completionsPrefix) ? item.name.slice(this.query.completionsPrefix.length || 0) : item.name])])]);
})])])
]);
});
}
};
//#endregion
//#region src/icons/customTagsIcons.ts
const customTagsIcons = {
log: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M17 22v-8h-4v2h2v6h-3v2h8v-2zM16 8a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 8"
}, []), h("path", {
fill: "currentColor",
d: "M26 28H6a2.002 2.002 0 0 1-2-2V6a2.002 2.002 0 0 1 2-2h20a2.002 2.002 0 0 1 2 2v20a2.002 2.002 0 0 1-2 2M6 6v20h20V6Z"
}, [])]),
warn: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M16 23a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 23m-1-11h2v9h-2z"
}, []), h("path", {
fill: "currentColor",
d: "M29 30H3a1 1 0 0 1-.887-1.461l13-25a1 1 0 0 1 1.774 0l13 25A1 1 0 0 1 29 30M4.65 28h22.7l.001-.003L16.002 6.17h-.004L4.648 27.997Z"
}, [])]),
error: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12 12 0 0 1-12 12"
}, []), h("path", {
fill: "currentColor",
d: "M15 8h2v11h-2zm1 14a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 22"
}, [])]),
annotate: h("svg", {
viewBox: "0 0 32 32",
width: "1rem",
height: "auto"
}, [h("path", {
fill: "currentColor",
d: "M11 24h10v2H11zm2 4h6v2h-6zm3-26A10 10 0 0 0 6 12a9.19 9.19 0 0 0 3.46 7.62c1 .93 1.54 1.46 1.54 2.38h2c0-1.84-1.11-2.87-2.19-3.86A7.2 7.2 0 0 1 8 12a8 8 0 0 1 16 0a7.2 7.2 0 0 1-2.82 6.14c-1.07 1-2.18 2-2.18 3.86h2c0-.92.53-1.45 1.54-2.39A9.18 9.18 0 0 0 26 12A10 10 0 0 0 16 2"
}, [])])
};
//#endregion
//#region src/annotations/customTags.ts
/**
* Represents a custom annotation for Twoslash tags.
* Extends the `ExpressiveCodeAnnotation` class to provide custom rendering for Twoslash tags.
*/
var TwoslashCustomTagsAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-custom-tags";
/**
* Creates an instance of TwoslashCustomTagsAnnotation.
* @param tag - The NodeTag object representing the Twoslash tag.
*/
constructor(tag, line) {
super({ inlineRange: {
columnStart: 0,
columnEnd: line.text.length
} });
this.tag = tag;
this.line = line;
}
render({ nodesToTransform }) {
const tag = this.tag;
const customTagClass = getCustomTagClass(tag.name);
return nodesToTransform.map((node) => {
return h("span.twoslash.twocustom", [h("div.twoslash-custom-box", { class: customTagClass }, [h("span.twoslash-custom-box-icon", [customTagsIcons[tag.name]]), h("span.twoslash-custom-box-content", [h("span.twoslash-custom-box-content-title", [`${getCustomTagString(tag.name)}:`]), h("span.twoslash-custom-box-content-message", [` ${tag.text}`])])]), node]);
});
}
};
//#endregion
//#region src/annotations/errors.ts
var TwoslashErrorUnderlineAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-error-underline";
constructor(error) {
super({ inlineRange: {
columnStart: error.character,
columnEnd: error.character + error.length
} });
this.error = error;
}
render({ nodesToTransform }) {
return nodesToTransform.map((node) => {
return h("span.twoslash.twoslash-error-underline", [node]);
});
}
};
/**
* Represents an annotation for displaying error boxes in Twoslash.
* Extends the `ExpressiveCodeAnnotation` class.
*/
var TwoslashErrorBoxAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-error-box";
/**
* Creates an instance of `TwoslashErrorBoxAnnotation`.
*
* @param error - The error object containing error details.
* @param line - The line of code where the error occurred.
*/
constructor(error, line) {
super({ inlineRange: {
columnStart: line.text.length,
columnEnd: line.text.length + error.length
} });
this.error = error;
this.line = line;
}
/**
* Renders the error box annotation.
*
* @param nodesToTransform - The nodes to transform with the error box annotation.
* @returns An array of transformed nodes with the error box annotation.
*/
render({ nodesToTransform }) {
const error = this.error;
const errorLevelClass = getErrorLevelClass(error);
return nodesToTransform.map((node) => {
return h("span.twoslash.twoerror", [node, h("div.twoslash-error-box", { class: errorLevelClass }, [h("span.twoslash-error-box-icon"), h("span.twoslash-error-box-content", [h("span.twoslash-error-box-content-title", [`${getErrorLevelString(error)} ${error.code && `ts(${error.code}) `} ― `]), h("span.twoslash-error-box-content-message", [error.text])])])]);
});
}
};
//#endregion
//#region src/annotations/highlight.ts
/**
* Represents a highlight annotation for Twoslash.
* Extends the `ExpressiveCodeAnnotation` class.
*/
var TwoslashHighlightAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-highlight-annotation";
/**
* Creates an instance of `TwoslashHighlightAnnotation`.
* @param highlight - The highlight details including start position and length.
*/
constructor(highlight) {
super({ inlineRange: {
columnStart: highlight.start,
columnEnd: highlight.start + highlight.length
} });
this.highlight = highlight;
}
/**
* Renders the highlight annotation.
* @param nodesToTransform - The nodes to be transformed.
* @returns An array of transformed nodes wrapped in a span with the class `twoslash-highlighted`.
*/
render({ nodesToTransform }) {
return nodesToTransform.map((node) => {
return h("span.twoslash-highlighted", [node]);
});
}
};
//#endregion
//#region src/annotations/hover.ts
/**
* Represents a hover annotation for Twoslash.
* Extends the `ExpressiveCodeAnnotation` class to provide hover functionality.
*/
var TwoslashHoverAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-hover-annotation";
/**
* Creates an instance of `TwoslashHoverAnnotation`.
* @param hover - The hover information including character position and text.
*/
constructor(hover, codeType, renderedDocs) {
super({ inlineRange: {
columnStart: hover.character,
columnEnd: hover.character + hover.length
} });
this.hover = hover;
this.codeType = codeType;
this.renderedDocs = renderedDocs;
}
/**
* Renders the hover annotation.
* @param nodesToTransform - The nodes to be transformed with hover annotations.
* @returns The transformed nodes with hover annotations.
*/
render({ nodesToTransform }) {
return nodesToTransform.map((node) => {
if (node.type === "element") return h("span.twoslash", node.properties, [h("span.twoslash-hover", [h("div.twoslash-popup-container.not-content", [
h("code.twoslash-popup-code", [h("span.twoslash-popup-code-type", this.codeType)]),
this.renderedDocs.docs,
this.renderedDocs.tags
]), node])]);
return node;
});
}
};
//#endregion
//#region src/annotations/static.ts
/**
* Represents a static annotation for Twoslash.
* Extends the ExpressiveCodeAnnotation class.
*/
var TwoslashStaticAnnotation = class extends ExpressiveCodeAnnotation {
name = "twoslash-static-annotation";
/**
* Creates an instance of TwoslashStaticAnnotation.
*
* @param hover - The hover information for the node.
* @param line - The line of code associated with the annotation.
* @param includeJsDoc - A flag indicating whether to include JSDoc comments.
* @param query - The query information for the node.
*/
constructor(query, line, codeType, renderedDocs) {
super({ inlineRange: {
columnStart: line.text.length,
columnEnd: line.text.length + query.length
} });
this.query = query;
this.line = line;
this.codeType = codeType;
this.renderedDocs = renderedDocs;
}
/**
* Renders the static annotation.
* @param nodesToTransform - The nodes to transform with the error box annotation.
* @returns An array of transformed nodes with the error box annotation.
*/
render({ nodesToTransform }) {
return nodesToTransform.map((node) => {
return h("span.twoslash-noline", [node, h("div.twoslash-static", { style: { "margin-left": `${getTextWidthInPixels(this.query.character)}px` } }, [h("div.twoslash-static-container.not-content", [
h("code.twoslash-popup-code", [h("span.twoslash-popup-code-type", this.codeType)]),
this.renderedDocs.docs,
this.renderedDocs.tags
])])]);
});
}
};
//#endregion
//#region src/consts.ts
const instanceConfigsDefaults = {
twoslash: {
explicitTrigger: true,
languages: [
"ts",
"tsx",
"vue"
]
},
eslint: {
explicitTrigger: true,
languages: ["ts", "tsx"]
}
};
const twoslashEslintDefaults = { eslintConfig: [{
files: ["**"],
rules: {
"constructor-super": ["error"],
"for-direction": ["error"],
"getter-return": ["error"],
"no-async-promise-executor": ["error"],
"no-case-declarations": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": ["error"],
"no-const-assign": ["error"],
"no-constant-binary-expression": ["error"],
"no-constant-condition": ["error"],
"no-control-regex": ["error"],
"no-debugger": ["error"],
"no-delete-var": ["error"],
"no-dupe-args": ["error"],
"no-dupe-class-members": ["error"],
"no-dupe-else-if": ["error"],
"no-dupe-keys": ["error"],
"no-duplicate-case": ["error"],
"no-empty": ["error"],
"no-empty-character-class": ["error"],
"no-empty-pattern": ["error"],
"no-empty-static-block": ["error"],
"no-ex-assign": ["error"],
"no-extra-boolean-cast": ["error"],
"no-fallthrough": ["error"],
"no-func-assign": ["error"],
"no-global-assign": ["error"],
"no-import-assign": ["error"],
"no-invalid-regexp": ["error"],
"no-irregular-whitespace": ["error"],
"no-loss-of-precision": ["error"],
"no-misleading-character-class": ["error"],
"no-new-native-nonconstructor": ["error"],
"no-nonoctal-decimal-escape": ["error"],
"no-obj-calls": ["error"],
"no-octal": ["error"],
"no-prototype-builtins": ["error"],
"no-redeclare": ["error"],
"no-regex-spaces": ["error"],
"no-self-assign": ["error"],
"no-setter-return": ["error"],
"no-shadow-restricted-names": ["error"],
"no-sparse-arrays": ["error"],
"no-this-before-super": ["error"],
"no-unassigned-vars": ["error"],
"no-undef": ["error"],
"no-unexpected-multiline": ["error"],
"no-unreachable": ["error"],
"no-unsafe-finally": ["error"],
"no-unsafe-negation": ["error"],
"no-unsafe-optional-chaining": ["error"],
"no-unused-labels": ["error"],
"no-unused-private-class-members": ["error"],
"no-unused-vars": ["error"],
"no-useless-assignment": ["error"],
"no-useless-backreference": ["error"],
"no-useless-catch": ["error"],
"no-useless-escape": ["error"],
"no-with": ["error"],
"preserve-caught-error": ["error"],
"require-yield": ["error"],
"use-isnan": ["error"],
"valid-typeof": ["error"]
},
languageOptions: {
parserOptions: {
ecmaFeatures: { jsx: true },
sourceType: "module"
},
ecmaVersion: "latest",
parser: tsParser
}
}] };
//#endregion
//#region src/module-code/floating-ui-core.min.ts
var floating_ui_core_min_default = "!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?define([\"exports\"],e):e((t=\"undefined\"!=typeof globalThis?globalThis:t||self).FloatingUICore={})}(this,(function(t){\"use strict\";const e=[\"top\",\"right\",\"bottom\",\"left\"],n=[\"start\",\"end\"],i=e.reduce(((t,e)=>t.concat(e,e+\"-\"+n[0],e+\"-\"+n[1])),[]),o=Math.min,r=Math.max,a={left:\"right\",right:\"left\",bottom:\"top\",top:\"bottom\"},l={start:\"end\",end:\"start\"};function s(t,e,n){return r(t,o(e,n))}function f(t,e){return\"function\"==typeof t?t(e):t}function c(t){return t.split(\"-\")[0]}function u(t){return t.split(\"-\")[1]}function m(t){return\"x\"===t?\"y\":\"x\"}function d(t){return\"y\"===t?\"height\":\"width\"}function g(t){return[\"top\",\"bottom\"].includes(c(t))?\"y\":\"x\"}function p(t){return m(g(t))}function h(t,e,n){void 0===n&&(n=!1);const i=u(t),o=p(t),r=d(o);let a=\"x\"===o?i===(n?\"end\":\"start\")?\"right\":\"left\":\"start\"===i?\"bottom\":\"top\";return e.reference[r]>e.floating[r]&&(a=w(a)),[a,w(a)]}function y(t){return t.replace(/start|end/g,(t=>l[t]))}function w(t){return t.replace(/left|right|bottom|top/g,(t=>a[t]))}function x(t){return\"number\"!=typeof t?function(t){return{top:0,right:0,bottom:0,left:0,...t}}(t):{top:t,right:t,bottom:t,left:t}}function v(t){const{x:e,y:n,width:i,height:o}=t;return{width:i,height:o,top:n,left:e,right:e+i,bottom:n+o,x:e,y:n}}function b(t,e,n){let{reference:i,floating:o}=t;const r=g(e),a=p(e),l=d(a),s=c(e),f=\"y\"===r,m=i.x+i.width/2-o.width/2,h=i.y+i.height/2-o.height/2,y=i[l]/2-o[l]/2;let w;switch(s){case\"top\":w={x:m,y:i.y-o.height};break;case\"bottom\":w={x:m,y:i.y+i.height};break;case\"right\":w={x:i.x+i.width,y:h};break;case\"left\":w={x:i.x-o.width,y:h};break;default:w={x:i.x,y:i.y}}switch(u(e)){case\"start\":w[a]-=y*(n&&f?-1:1);break;case\"end\":w[a]+=y*(n&&f?-1:1)}return w}async function A(t,e){var n;void 0===e&&(e={});const{x:i,y:o,platform:r,rects:a,elements:l,strategy:s}=t,{boundary:c=\"clippingAncestors\",rootBoundary:u=\"viewport\",elementContext:m=\"floating\",altBoundary:d=!1,padding:g=0}=f(e,t),p=x(g),h=l[d?\"floating\"===m?\"reference\":\"floating\":m],y=v(await r.getClippingRect({element:null==(n=await(null==r.isElement?void 0:r.isElement(h)))||n?h:h.contextElement||await(null==r.getDocumentElement?void 0:r.getDocumentElement(l.floating)),boundary:c,rootBoundary:u,strategy:s})),w=\"floating\"===m?{x:i,y:o,width:a.floating.width,height:a.floating.height}:a.reference,b=await(null==r.getOffsetParent?void 0:r.getOffsetParent(l.floating)),A=await(null==r.isElement?void 0:r.isElement(b))&&await(null==r.getScale?void 0:r.getScale(b))||{x:1,y:1},R=v(r.convertOffsetParentRelativeRectToViewportRelativeRect?await r.convertOffsetParentRelativeRectToViewportRelativeRect({elements:l,rect:w,offsetParent:b,strategy:s}):w);return{top:(y.top-R.top+p.top)/A.y,bottom:(R.bottom-y.bottom+p.bottom)/A.y,left:(y.left-R.left+p.left)/A.x,right:(R.right-y.right+p.right)/A.x}}function R(t,e){return{top:t.top-e.height,right:t.right-e.width,bottom:t.bottom-e.height,left:t.left-e.width}}function P(t){return e.some((e=>t[e]>=0))}function D(t){const e=o(...t.map((t=>t.left))),n=o(...t.map((t=>t.top)));return{x:e,y:n,width:r(...t.map((t=>t.right)))-e,height:r(...t.map((t=>t.bottom)))-n}}t.arrow=t=>({name:\"arrow\",options:t,async fn(e){const{x:n,y:i,placement:r,rects:a,platform:l,elements:c,middlewareData:m}=e,{element:g,padding:h=0}=f(t,e)||{};if(null==g)return{};const y=x(h),w={x:n,y:i},v=p(r),b=d(v),A=await l.getDimensions(g),R=\"y\"===v,P=R?\"top\":\"left\",D=R?\"bottom\":\"right\",T=R?\"clientHeight\":\"clientWidth\",O=a.reference[b]+a.reference[v]-w[v]-a.floating[b],E=w[v]-a.reference[v],L=await(null==l.getOffsetParent?void 0:l.getOffsetParent(g));let k=L?L[T]:0;k&&await(null==l.isElement?void 0:l.isElement(L))||(k=c.floating[T]||a.floating[b]);const C=O/2-E/2,B=k/2-A[b]/2-1,H=o(y[P],B),S=o(y[D],B),F=H,j=k-A[b]-S,z=k/2-A[b]/2+C,M=s(F,z,j),V=!m.arrow&&null!=u(r)&&z!==M&&a.reference[b]/2-(z<F?H:S)-A[b]/2<0,W=V?z<F?z-F:z-j:0;return{[v]:w[v]+W,data:{[v]:M,centerOffset:z-M-W,...V&&{alignmentOffset:W}},reset:V}}}),t.autoPlacement=function(t){return void 0===t&&(t={}),{name:\"autoPlacement\",options:t,async fn(e){var n,o,r;const{rects:a,middlewareData:l,placement:s,platform:m,elements:d}=e,{crossAxis:g=!1,alignment:p,allowedPlacements:w=i,autoAlignment:x=!0,...v}=f(t,e),b=void 0!==p||w===i?function(t,e,n){return(t?[...n.filter((e=>u(e)===t)),...n.filter((e=>u(e)!==t))]:n.filter((t=>c(t)===t))).filter((n=>!t||u(n)===t||!!e&&y(n)!==n))}(p||null,x,w):w,R=await A(e,v),P=(null==(n=l.autoPlacement)?void 0:n.index)||0,D=b[P];if(null==D)return{};const T=h(D,a,await(null==m.isRTL?void 0:m.isRTL(d.floating)));if(s!==D)return{reset:{placement:b[0]}};const O=[R[c(D)],R[T[0]],R[T[1]]],E=[...(null==(o=l.autoPlacement)?void 0:o.overflows)||[],{placement:D,overflows:O}],L=b[P+1];if(L)return{data:{index:P+1,overflows:E},reset:{placement:L}};const k=E.map((t=>{const e=u(t.placement);return[t.placement,e&&g?t.overflows.slice(0,2).reduce(((t,e)=>t+e),0):t.overflows[0],t.overflows]})).sort(((t,e)=>t[1]-e[1])),C=(null==(r=k.filter((t=>t[2].slice(0,u(t[0])?2:3).every((t=>t<=0))))[0])?void 0:r[0])||k[0][0];return C!==s?{data:{index:P+1,overflows:E},reset:{placement:C}}:{}}}},t.computePosition=async(t,e,n)=>{const{placement:i=\"bottom\",strategy:o=\"absolute\",middleware:r=[],platform:a}=n,l=r.filter(Boolean),s=await(null==a.isRTL?void 0:a.isRTL(e));let f=await a.getElementRects({reference:t,floating:e,strategy:o}),{x:c,y:u}=b(f,i,s),m=i,d={},g=0;for(let n=0;n<l.length;n++){const{name:r,fn:p}=l[n],{x:h,y:y,data:w,reset:x}=await p({x:c,y:u,initialPlacement:i,placement:m,strategy:o,middlewareData:d,rects:f,platform:a,elements:{reference:t,floating:e}});c=null!=h?h:c,u=null!=y?y:u,d={...d,[r]:{...d[r],...w}},x&&g<=50&&(g++,\"object\"==typeof x&&(x.placement&&(m=x.placement),x.rects&&(f=!0===x.rects?await a.getElementRects({reference:t,floating:e,strategy:o}):x.rects),({x:c,y:u}=b(f,m,s))),n=-1)}return{x:c,y:u,placement:m,strategy:o,middlewareData:d}},t.detectOverflow=A,t.flip=function(t){return void 0===t&&(t={}),{name:\"flip\",options:t,async fn(e){var n,i;const{placement:o,middlewareData:r,rects:a,initialPlacement:l,platform:s,elements:m}=e,{mainAxis:d=!0,crossAxis:p=!0,fallbackPlacements:x,fallbackStrategy:v=\"bestFit