@scalar/code-highlight
Advanced tools
+6
-0
| # @scalar/code-highlight | ||
| ## 0.3.1 | ||
| ### Patch Changes | ||
| - [#8466](https://github.com/scalar/scalar/pull/8466): chore: new build pipeline | ||
| ## 0.3.0 | ||
@@ -4,0 +10,0 @@ |
+58
-38
@@ -1,42 +0,62 @@ | ||
| import rehypeParse from "rehype-parse"; | ||
| import rehypeStringify from "rehype-stringify"; | ||
| import { unified } from "unified"; | ||
| import { visit } from "unist-util-visit"; | ||
| import { lowlightLanguageMappings } from "../constants.js"; | ||
| import { rehypeHighlight } from "../rehype-highlight/index.js"; | ||
| import { codeBlockLinesPlugin } from "./line-numbers.js"; | ||
| function syntaxHighlight(codeString, options) { | ||
| const credentials = (typeof options?.maskCredentials === "string" ? [options.maskCredentials] : options?.maskCredentials ?? []).filter((c) => { | ||
| if (c.length < 3) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`; | ||
| const nullPlugin = () => { | ||
| }; | ||
| const html = unified().use(rehypeParse, { fragment: true }).use(injectRawCodeStringPlugin(codeString)).use(rehypeHighlight, { | ||
| languages: options.languages | ||
| }).use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin).use(rehypeStringify).processSync(`<pre><code class="${className}"></code></pre>`); | ||
| const htmlString = html.toString(); | ||
| return credentials.length ? credentials.reduce( | ||
| (acc, credential) => acc.split(credential).join(`<span class="credential"><span class="credential-value">${credential}</span></span>`), | ||
| htmlString | ||
| ) : htmlString; | ||
| import rehypeParse from 'rehype-parse'; | ||
| import rehypeStringify from 'rehype-stringify'; | ||
| import { unified } from 'unified'; | ||
| import { visit } from 'unist-util-visit'; | ||
| import { lowlightLanguageMappings } from '../constants.js'; | ||
| import { rehypeHighlight } from '../rehype-highlight/index.js'; | ||
| import { codeBlockLinesPlugin } from './line-numbers.js'; | ||
| /** | ||
| * Syntax highlights a code string using the `rehype-highlight` library. | ||
| */ | ||
| export function syntaxHighlight(codeString, options) { | ||
| // Simple restriction on credentials to prevent unexpected behavior | ||
| const credentials = (typeof options?.maskCredentials === 'string' ? [options.maskCredentials] : (options?.maskCredentials ?? [])).filter((c) => { | ||
| // Credentials must be at least 3 characters to mask. | ||
| if (c.length < 3) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| // Classname is used by lowlight to select the language model | ||
| const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`; | ||
| // biome-ignore lint/suspicious/noEmptyBlockStatements: empty plugin | ||
| const nullPlugin = (() => { }); | ||
| const html = unified() | ||
| // Parses markdown | ||
| .use(rehypeParse, { fragment: true }) | ||
| // Raw code string must be injected after initial hast parsing | ||
| // so that HTML code is not parsed into the hast tree | ||
| .use(injectRawCodeStringPlugin(codeString)) | ||
| // Syntax highlighting | ||
| .use(rehypeHighlight, { | ||
| languages: options.languages, | ||
| }) | ||
| .use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin) | ||
| // Converts the HTML AST to a string | ||
| .use(rehypeStringify) | ||
| // Run the pipeline | ||
| .processSync(`<pre><code class="${className}"></code></pre>`); | ||
| const htmlString = html.toString(); | ||
| // Replace any credentials with a wrapper element | ||
| return credentials.length | ||
| ? credentials.reduce((acc, credential) => acc | ||
| .split(credential) | ||
| .join(`<span class="credential"><span class="credential-value">${credential}</span></span>`), htmlString) | ||
| : htmlString; | ||
| } | ||
| /** | ||
| * To prevent unified from parsing any content of the code string we inject | ||
| * it as a raw text node into the AST tree as a child of the code element | ||
| */ | ||
| function injectRawCodeStringPlugin(rawCodeString) { | ||
| return () => (tree) => { | ||
| visit(tree, "element", (node) => { | ||
| if (node.tagName === "code") { | ||
| node.children.push({ | ||
| type: "text", | ||
| value: rawCodeString | ||
| return () => (tree) => { | ||
| visit(tree, 'element', (node) => { | ||
| if (node.tagName === 'code') { | ||
| node.children.push({ | ||
| type: 'text', | ||
| value: rawCodeString, | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| }; | ||
| } | ||
| export { | ||
| syntaxHighlight | ||
| }; | ||
| //# sourceMappingURL=highlight.js.map |
@@ -1,5 +0,1 @@ | ||
| import { syntaxHighlight } from "./highlight.js"; | ||
| export { | ||
| syntaxHighlight | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| export { syntaxHighlight } from './highlight.js'; |
@@ -1,69 +0,94 @@ | ||
| import { visit } from "unist-util-visit"; | ||
| import { visit } from 'unist-util-visit'; | ||
| // --------------------------------------------------------------------------- | ||
| // Line Numbering plugin | ||
| function isText(element) { | ||
| return element?.type === "text"; | ||
| return element?.type === 'text'; | ||
| } | ||
| function isElement(node) { | ||
| return node?.type === "element"; | ||
| return node?.type === 'element'; | ||
| } | ||
| function textElement(value) { | ||
| return { type: "text", value }; | ||
| return { type: 'text', value }; | ||
| } | ||
| function lineBreak() { | ||
| return { type: "text", value: "\n" }; | ||
| return { type: 'text', value: '\n' }; | ||
| } | ||
| function codeBlockLinesPlugin() { | ||
| return (tree) => { | ||
| visit(tree, "element", (node, _i, parent) => { | ||
| if (parent?.type === "element" && parent.tagName === "pre" && node.tagName === "code") { | ||
| let numLines = 0; | ||
| node.children = addLines(node); | ||
| node.children.forEach((child) => { | ||
| if (child.type === "element" && child.tagName === "span") { | ||
| const lastChild = child.children[child.children.length - 1]; | ||
| if (lastChild && (!isText(lastChild) || isText(lastChild) && !hasLineBreak(lastChild))) { | ||
| child.children.push(lineBreak()); | ||
| numLines++; | ||
| /** | ||
| * Adds lines to code blocks | ||
| */ | ||
| export function codeBlockLinesPlugin() { | ||
| return (tree) => { | ||
| visit(tree, 'element', (node, _i, parent) => { | ||
| if (parent?.type === 'element' && parent.tagName === 'pre' && node.tagName === 'code') { | ||
| let numLines = 0; | ||
| // Wraps each line in a span | ||
| node.children = addLines(node); | ||
| // Adds a line break to the end of each line | ||
| node.children.forEach((child) => { | ||
| if (child.type === 'element' && child.tagName === 'span') { | ||
| const lastChild = child.children[child.children.length - 1]; | ||
| if (lastChild && (!isText(lastChild) || (isText(lastChild) && !hasLineBreak(lastChild)))) { | ||
| child.children.push(lineBreak()); | ||
| numLines++; | ||
| } | ||
| } | ||
| }); | ||
| // We need to maintain a count of the total lines to allow space for the labels | ||
| node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`]; | ||
| } | ||
| } | ||
| }); | ||
| node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`]; | ||
| } | ||
| }); | ||
| }; | ||
| // console.log('NUMBER OF LINES IS: ', numLines) | ||
| }; | ||
| } | ||
| /** | ||
| * Adds lines to a node recursively and returns them | ||
| * | ||
| * @param node - The node to add lines to | ||
| * @param lines - The current lines | ||
| * @param copyParent - Whether to copy the parent node to save the original node styles | ||
| */ | ||
| function addLines(node, lines = [], copyParent) { | ||
| const line = () => lines[lines.length - 1] ?? (lines.push(createLine()) && lines[lines.length - 1] || void 0); | ||
| node.children.forEach((child) => { | ||
| if (isText(child) && hasLineBreak(child)) { | ||
| const split = child.value.split(/\n/); | ||
| split.forEach((content, i) => { | ||
| if (copyParent) { | ||
| line()?.children.push({ ...node, children: [textElement(content)] }); | ||
| } else { | ||
| line()?.children.push(textElement(content)); | ||
| const line = () => lines[lines.length - 1] ?? ((lines.push(createLine()) && lines[lines.length - 1]) || undefined); | ||
| node.children.forEach((child) => { | ||
| if (isText(child) && hasLineBreak(child)) { | ||
| const split = child.value.split(/\n/); | ||
| split.forEach((content, i) => { | ||
| if (copyParent) { | ||
| line()?.children.push({ ...node, children: [textElement(content)] }); | ||
| } | ||
| else { | ||
| line()?.children.push(textElement(content)); | ||
| } | ||
| i !== split.length - 1 && lines.push(createLine()); | ||
| }); | ||
| } | ||
| i !== split.length - 1 && lines.push(createLine()); | ||
| }); | ||
| } else if (isElement(child) && child.children.some(hasLineBreak)) { | ||
| addLines(child, lines, true); | ||
| } else { | ||
| line()?.children.push(child); | ||
| } | ||
| }); | ||
| return lines; | ||
| else if (isElement(child) && child.children.some(hasLineBreak)) { | ||
| addLines(child, lines, true); | ||
| } | ||
| else { | ||
| line()?.children.push(child); | ||
| } | ||
| }); | ||
| return lines; | ||
| } | ||
| /** | ||
| * Creates a new line element | ||
| * | ||
| * @param children - The children the line should have initially | ||
| */ | ||
| function createLine(...children) { | ||
| return { | ||
| type: "element", | ||
| tagName: "span", | ||
| properties: { class: ["line"] }, | ||
| children | ||
| }; | ||
| return { | ||
| type: 'element', | ||
| tagName: 'span', | ||
| properties: { class: ['line'] }, | ||
| children, | ||
| }; | ||
| } | ||
| /** | ||
| * Checks if a node has a line break | ||
| * | ||
| * @param node - The node to check | ||
| */ | ||
| function hasLineBreak(node) { | ||
| return isText(node) && /\r?\n/.test(node.value) || isElement(node) && node.children.some(hasLineBreak); | ||
| return (isText(node) && /\r?\n/.test(node.value)) || (isElement(node) && node.children.some(hasLineBreak)); | ||
| } | ||
| export { | ||
| codeBlockLinesPlugin | ||
| }; | ||
| //# sourceMappingURL=line-numbers.js.map |
+10
-12
@@ -1,13 +0,11 @@ | ||
| const lowlightLanguageMappings = { | ||
| "ts": "typescript", | ||
| "js": "javascript", | ||
| "py": "python", | ||
| "py3": "python", | ||
| "c#": "csharp", | ||
| "c++": "cpp", | ||
| "node": "javascript" | ||
| /** Map common markdown language shortcuts to lowlight languages */ | ||
| export const lowlightLanguageMappings = { | ||
| 'ts': 'typescript', | ||
| 'js': 'javascript', | ||
| 'py': 'python', | ||
| 'py3': 'python', | ||
| 'c#': 'csharp', | ||
| 'c++': 'cpp', | ||
| 'node': 'javascript', | ||
| }; | ||
| export { | ||
| lowlightLanguageMappings | ||
| }; | ||
| //# sourceMappingURL=constants.js.map | ||
| // |
+5
-14
@@ -1,14 +0,5 @@ | ||
| import { syntaxHighlight } from "./code/index.js"; | ||
| import { lowlightLanguageMappings } from "./constants.js"; | ||
| export * from "./languages/index.js"; | ||
| import { htmlFromMarkdown, isHeading, textFromNode } from "./markdown/index.js"; | ||
| import { rehypeHighlight } from "./rehype-highlight/index.js"; | ||
| export { | ||
| htmlFromMarkdown, | ||
| isHeading, | ||
| lowlightLanguageMappings, | ||
| rehypeHighlight, | ||
| syntaxHighlight, | ||
| textFromNode | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| export { syntaxHighlight } from './code/index.js'; | ||
| export { lowlightLanguageMappings } from './constants.js'; | ||
| export * from './languages/index.js'; | ||
| export { htmlFromMarkdown, isHeading, textFromNode } from './markdown/index.js'; | ||
| export { rehypeHighlight } from './rehype-highlight/index.js'; |
+28
-31
@@ -1,33 +0,30 @@ | ||
| import bash from "highlight.js/lib/languages/bash"; | ||
| import css from "highlight.js/lib/languages/css"; | ||
| import javascript from "highlight.js/lib/languages/javascript"; | ||
| import json from "highlight.js/lib/languages/json"; | ||
| import less from "highlight.js/lib/languages/less"; | ||
| import markdown from "highlight.js/lib/languages/markdown"; | ||
| import plaintext from "highlight.js/lib/languages/plaintext"; | ||
| import python from "highlight.js/lib/languages/python"; | ||
| import scss from "highlight.js/lib/languages/scss"; | ||
| import shell from "highlight.js/lib/languages/shell"; | ||
| import typescript from "highlight.js/lib/languages/typescript"; | ||
| import xml from "highlight.js/lib/languages/xml"; | ||
| import yaml from "highlight.js/lib/languages/yaml"; | ||
| import bash from 'highlight.js/lib/languages/bash'; | ||
| import css from 'highlight.js/lib/languages/css'; | ||
| import javascript from 'highlight.js/lib/languages/javascript'; | ||
| import json from 'highlight.js/lib/languages/json'; | ||
| import less from 'highlight.js/lib/languages/less'; | ||
| import markdown from 'highlight.js/lib/languages/markdown'; | ||
| import plaintext from 'highlight.js/lib/languages/plaintext'; | ||
| import python from 'highlight.js/lib/languages/python'; | ||
| import scss from 'highlight.js/lib/languages/scss'; | ||
| import shell from 'highlight.js/lib/languages/shell'; | ||
| import typescript from 'highlight.js/lib/languages/typescript'; | ||
| import xml from 'highlight.js/lib/languages/xml'; | ||
| import yaml from 'highlight.js/lib/languages/yaml'; | ||
| const basicLanguages = { | ||
| bash, | ||
| css, | ||
| html: xml, | ||
| javascript, | ||
| json, | ||
| less, | ||
| markdown, | ||
| plaintext, | ||
| python, | ||
| scss, | ||
| shell, | ||
| typescript, | ||
| xml, | ||
| yaml | ||
| bash, | ||
| css, | ||
| html: xml, | ||
| javascript, | ||
| json, | ||
| less, | ||
| markdown, | ||
| plaintext, | ||
| python, | ||
| scss, | ||
| shell, | ||
| typescript, | ||
| xml, | ||
| yaml, | ||
| }; | ||
| export { | ||
| basicLanguages | ||
| }; | ||
| //# sourceMappingURL=basic.js.map | ||
| export { basicLanguages }; |
@@ -1,9 +0,3 @@ | ||
| import { basicLanguages } from "./basic.js"; | ||
| import { jsonYamlLanguages } from "./json-yaml.js"; | ||
| import { standardLanguages } from "./standard.js"; | ||
| export { | ||
| basicLanguages, | ||
| jsonYamlLanguages, | ||
| standardLanguages | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| export { basicLanguages } from './basic.js'; | ||
| export { jsonYamlLanguages } from './json-yaml.js'; | ||
| export { standardLanguages } from './standard.js'; |
@@ -1,12 +0,9 @@ | ||
| import json from "highlight.js/lib/languages/json"; | ||
| import plaintext from "highlight.js/lib/languages/plaintext"; | ||
| import yaml from "highlight.js/lib/languages/yaml"; | ||
| import json from 'highlight.js/lib/languages/json'; | ||
| import plaintext from 'highlight.js/lib/languages/plaintext'; | ||
| import yaml from 'highlight.js/lib/languages/yaml'; | ||
| const jsonYamlLanguages = { | ||
| json, | ||
| plaintext, | ||
| yaml | ||
| json, | ||
| plaintext, | ||
| yaml, | ||
| }; | ||
| export { | ||
| jsonYamlLanguages | ||
| }; | ||
| //# sourceMappingURL=json-yaml.js.map | ||
| export { jsonYamlLanguages }; |
+115
-100
@@ -1,101 +0,116 @@ | ||
| import bash from "highlight.js/lib/languages/bash"; | ||
| import c from "highlight.js/lib/languages/c"; | ||
| import clojure from "highlight.js/lib/languages/clojure"; | ||
| import cpp from "highlight.js/lib/languages/cpp"; | ||
| import csharp from "highlight.js/lib/languages/csharp"; | ||
| import css from "highlight.js/lib/languages/css"; | ||
| import dart from "highlight.js/lib/languages/dart"; | ||
| import diff from "highlight.js/lib/languages/diff"; | ||
| import dockerfile from "highlight.js/lib/languages/dockerfile"; | ||
| import elixir from "highlight.js/lib/languages/elixir"; | ||
| import fsharp from "highlight.js/lib/languages/fsharp"; | ||
| import go from "highlight.js/lib/languages/go"; | ||
| import graphql from "highlight.js/lib/languages/graphql"; | ||
| import haskell from "highlight.js/lib/languages/haskell"; | ||
| import http from "highlight.js/lib/languages/http"; | ||
| import ini from "highlight.js/lib/languages/ini"; | ||
| import java from "highlight.js/lib/languages/java"; | ||
| import javascript from "highlight.js/lib/languages/javascript"; | ||
| import json from "highlight.js/lib/languages/json"; | ||
| import kotlin from "highlight.js/lib/languages/kotlin"; | ||
| import less from "highlight.js/lib/languages/less"; | ||
| import lua from "highlight.js/lib/languages/lua"; | ||
| import makefile from "highlight.js/lib/languages/makefile"; | ||
| import markdown from "highlight.js/lib/languages/markdown"; | ||
| import matlab from "highlight.js/lib/languages/matlab"; | ||
| import nginx from "highlight.js/lib/languages/nginx"; | ||
| import objectivec from "highlight.js/lib/languages/objectivec"; | ||
| import ocaml from "highlight.js/lib/languages/ocaml"; | ||
| import perl from "highlight.js/lib/languages/perl"; | ||
| import php from "highlight.js/lib/languages/php"; | ||
| import plaintext from "highlight.js/lib/languages/plaintext"; | ||
| import powershell from "highlight.js/lib/languages/powershell"; | ||
| import properties from "highlight.js/lib/languages/properties"; | ||
| import python from "highlight.js/lib/languages/python"; | ||
| import r from "highlight.js/lib/languages/r"; | ||
| import ruby from "highlight.js/lib/languages/ruby"; | ||
| import rust from "highlight.js/lib/languages/rust"; | ||
| import scala from "highlight.js/lib/languages/scala"; | ||
| import scss from "highlight.js/lib/languages/scss"; | ||
| import shell from "highlight.js/lib/languages/shell"; | ||
| import sql from "highlight.js/lib/languages/sql"; | ||
| import swift from "highlight.js/lib/languages/swift"; | ||
| import typescript from "highlight.js/lib/languages/typescript"; | ||
| import xml from "highlight.js/lib/languages/xml"; | ||
| import yaml from "highlight.js/lib/languages/yaml"; | ||
| import curl from "highlightjs-curl"; | ||
| const standardLanguages = { | ||
| bash, | ||
| c, | ||
| clojure, | ||
| cpp, | ||
| csharp, | ||
| css, | ||
| curl, | ||
| dart, | ||
| diff, | ||
| docker: dockerfile, | ||
| dockerfile, | ||
| elixir, | ||
| fsharp, | ||
| go, | ||
| graphql, | ||
| haskell, | ||
| html: xml, | ||
| http, | ||
| ini, | ||
| java, | ||
| javascript, | ||
| json, | ||
| kotlin, | ||
| less, | ||
| lua, | ||
| makefile, | ||
| markdown, | ||
| matlab, | ||
| nginx, | ||
| objectivec, | ||
| ocaml, | ||
| perl, | ||
| php, | ||
| plaintext, | ||
| powershell, | ||
| properties, | ||
| python, | ||
| r, | ||
| ruby, | ||
| rust, | ||
| scala, | ||
| scss, | ||
| shell, | ||
| sql, | ||
| swift, | ||
| toml: ini, | ||
| typescript, | ||
| xml, | ||
| yaml | ||
| import bash from 'highlight.js/lib/languages/bash'; | ||
| import c from 'highlight.js/lib/languages/c'; | ||
| import clojure from 'highlight.js/lib/languages/clojure'; | ||
| import cpp from 'highlight.js/lib/languages/cpp'; | ||
| import csharp from 'highlight.js/lib/languages/csharp'; | ||
| import css from 'highlight.js/lib/languages/css'; | ||
| import dart from 'highlight.js/lib/languages/dart'; | ||
| import diff from 'highlight.js/lib/languages/diff'; | ||
| import dockerfile from 'highlight.js/lib/languages/dockerfile'; | ||
| import elixir from 'highlight.js/lib/languages/elixir'; | ||
| import fsharp from 'highlight.js/lib/languages/fsharp'; | ||
| import go from 'highlight.js/lib/languages/go'; | ||
| import graphql from 'highlight.js/lib/languages/graphql'; | ||
| import haskell from 'highlight.js/lib/languages/haskell'; | ||
| import http from 'highlight.js/lib/languages/http'; | ||
| import ini from 'highlight.js/lib/languages/ini'; | ||
| import java from 'highlight.js/lib/languages/java'; | ||
| import javascript from 'highlight.js/lib/languages/javascript'; | ||
| import json from 'highlight.js/lib/languages/json'; | ||
| import kotlin from 'highlight.js/lib/languages/kotlin'; | ||
| import less from 'highlight.js/lib/languages/less'; | ||
| import lua from 'highlight.js/lib/languages/lua'; | ||
| import makefile from 'highlight.js/lib/languages/makefile'; | ||
| import markdown from 'highlight.js/lib/languages/markdown'; | ||
| import matlab from 'highlight.js/lib/languages/matlab'; | ||
| import nginx from 'highlight.js/lib/languages/nginx'; | ||
| import objectivec from 'highlight.js/lib/languages/objectivec'; | ||
| import ocaml from 'highlight.js/lib/languages/ocaml'; | ||
| import perl from 'highlight.js/lib/languages/perl'; | ||
| import php from 'highlight.js/lib/languages/php'; | ||
| import plaintext from 'highlight.js/lib/languages/plaintext'; | ||
| import powershell from 'highlight.js/lib/languages/powershell'; | ||
| import properties from 'highlight.js/lib/languages/properties'; | ||
| import python from 'highlight.js/lib/languages/python'; | ||
| import r from 'highlight.js/lib/languages/r'; | ||
| import ruby from 'highlight.js/lib/languages/ruby'; | ||
| import rust from 'highlight.js/lib/languages/rust'; | ||
| import scala from 'highlight.js/lib/languages/scala'; | ||
| import scss from 'highlight.js/lib/languages/scss'; | ||
| import shell from 'highlight.js/lib/languages/shell'; | ||
| import sql from 'highlight.js/lib/languages/sql'; | ||
| import swift from 'highlight.js/lib/languages/swift'; | ||
| import typescript from 'highlight.js/lib/languages/typescript'; | ||
| import xml from 'highlight.js/lib/languages/xml'; | ||
| import yaml from 'highlight.js/lib/languages/yaml'; | ||
| // @ts-expect-error No types available | ||
| import curl from 'highlightjs-curl'; | ||
| /** | ||
| * We group languages into three categories based on their popularity and usage. | ||
| * This helps in optimizing the bundle size by allowing users to include only | ||
| * the languages they need. | ||
| * | ||
| * 1. Standard Languages: These are the most popular languages that cover the | ||
| * majority of use cases. | ||
| * 2. Medium Languages: These languages are not as popular as the ones in | ||
| * `standardLanguages`, but still have a decent amount of users. | ||
| * 3. Specialized Languages: These languages are more specialized and have a | ||
| * smaller user base. | ||
| * | ||
| * Each category is represented as a separate object, making it easy to import | ||
| * only the languages you need. | ||
| */ | ||
| /** | ||
| * These are the most popular languages that cover the majority of use cases. | ||
| */ | ||
| export const standardLanguages = { | ||
| bash, | ||
| c, | ||
| clojure, | ||
| cpp, | ||
| csharp, | ||
| css, | ||
| curl, | ||
| dart, | ||
| diff, | ||
| docker: dockerfile, | ||
| dockerfile, | ||
| elixir, | ||
| fsharp, | ||
| go, | ||
| graphql, | ||
| haskell, | ||
| html: xml, | ||
| http, | ||
| ini, | ||
| java, | ||
| javascript, | ||
| json, | ||
| kotlin, | ||
| less, | ||
| lua, | ||
| makefile, | ||
| markdown, | ||
| matlab, | ||
| nginx, | ||
| objectivec, | ||
| ocaml, | ||
| perl, | ||
| php, | ||
| plaintext, | ||
| powershell, | ||
| properties, | ||
| python, | ||
| r, | ||
| ruby, | ||
| rust, | ||
| scala, | ||
| scss, | ||
| shell, | ||
| sql, | ||
| swift, | ||
| toml: ini, | ||
| typescript, | ||
| xml, | ||
| yaml, | ||
| }; | ||
| export { | ||
| standardLanguages | ||
| }; | ||
| //# sourceMappingURL=standard.js.map |
@@ -1,2 +0,1 @@ | ||
| export * from "./markdown.js"; | ||
| //# sourceMappingURL=index.js.map | ||
| export * from './markdown.js'; |
+148
-100
@@ -1,114 +0,162 @@ | ||
| import rehypeExternalLinks from "rehype-external-links"; | ||
| import rehypeFormat from "rehype-format"; | ||
| import rehypeRaw from "rehype-raw"; | ||
| import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; | ||
| import rehypeStringify from "rehype-stringify"; | ||
| import remarkGfm from "remark-gfm"; | ||
| import remarkParse from "remark-parse"; | ||
| import remarkRehype from "remark-rehype"; | ||
| import remarkStringify from "remark-stringify"; | ||
| import { unified } from "unified"; | ||
| import { SKIP, visit } from "unist-util-visit"; | ||
| import { standardLanguages } from "../languages/index.js"; | ||
| import { rehypeAlert } from "../rehype-alert/index.js"; | ||
| import { rehypeHighlight } from "../rehype-highlight/index.js"; | ||
| const isHeading = (node) => { | ||
| return node.type === "heading" && "depth" in node && "children" in node; | ||
| import rehypeExternalLinks from 'rehype-external-links'; | ||
| import rehypeFormat from 'rehype-format'; | ||
| import rehypeRaw from 'rehype-raw'; | ||
| import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; | ||
| import rehypeStringify from 'rehype-stringify'; | ||
| import remarkGfm from 'remark-gfm'; | ||
| import remarkParse from 'remark-parse'; | ||
| import remarkRehype from 'remark-rehype'; | ||
| import remarkStringify from 'remark-stringify'; | ||
| import { unified } from 'unified'; | ||
| import { SKIP, visit } from 'unist-util-visit'; | ||
| import { standardLanguages } from '../languages/index.js'; | ||
| import { rehypeAlert } from '../rehype-alert/index.js'; | ||
| import { rehypeHighlight } from '../rehype-highlight/index.js'; | ||
| /** | ||
| * Type-guard to check if a node is a heading. | ||
| */ | ||
| export const isHeading = (node) => { | ||
| return node.type === 'heading' && 'depth' in node && 'children' in node; | ||
| }; | ||
| /** | ||
| * Plugin to transform nodes in a Markdown AST | ||
| */ | ||
| const transformNodes = (options, ..._ignored) => (tree) => { | ||
| if (!options?.transform || !options?.type) { | ||
| if (!options?.transform || !options?.type) { | ||
| return; | ||
| } | ||
| visit(tree, options?.type, (node) => { | ||
| options?.transform ? options?.transform(node) : node; | ||
| return SKIP; | ||
| }); | ||
| return; | ||
| } | ||
| visit(tree, options?.type, (node) => { | ||
| options?.transform ? options?.transform(node) : node; | ||
| return SKIP; | ||
| }); | ||
| return; | ||
| }; | ||
| function htmlFromMarkdown(markdown, options) { | ||
| const removeTags = options?.removeTags ?? []; | ||
| const tagNames = [...defaultSchema.tagNames ?? [], ...options?.allowTags ?? []].filter( | ||
| (t) => !removeTags.includes(t) | ||
| ); | ||
| const html = unified().use(remarkParse).use(remarkGfm).use(transformNodes, { | ||
| transform: options?.transform, | ||
| type: options?.transformType | ||
| }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeAlert).use(rehypeRaw).use(rehypeSanitize, { | ||
| ...defaultSchema, | ||
| // Don't prefix the heading ids | ||
| clobberPrefix: "", | ||
| // Makes it even more strict | ||
| tagNames, | ||
| attributes: { | ||
| ...defaultSchema.attributes, | ||
| abbr: ["title"], | ||
| // Allow all class names while preserving the existing default attributes | ||
| "*": [...defaultSchema.attributes?.["*"] ?? [], "className"] | ||
| }, | ||
| // Strip content of dangerous elements, not just the tags | ||
| strip: ["script", "style", "object", "embed", "form"] | ||
| }).use(rehypeHighlight, { | ||
| languages: standardLanguages, | ||
| // Enable auto detection | ||
| detect: true | ||
| }).use(rehypeExternalLinks, { target: "_blank" }).use(rehypeFormat).use(rehypeStringify).processSync(markdown); | ||
| return html.toString(); | ||
| /** | ||
| * Take a Markdown string and generate HTML from it | ||
| */ | ||
| export function htmlFromMarkdown(markdown, options) { | ||
| // Add permitted tags and remove stripped ones | ||
| const removeTags = options?.removeTags ?? []; | ||
| const tagNames = [...(defaultSchema.tagNames ?? []), ...(options?.allowTags ?? [])].filter((t) => !removeTags.includes(t)); | ||
| const html = unified() | ||
| // Parses markdown | ||
| .use(remarkParse) | ||
| // Support autolink literals, footnotes, strikethrough, tables and tasklists | ||
| .use(remarkGfm) | ||
| .use(transformNodes, { | ||
| transform: options?.transform, | ||
| type: options?.transformType, | ||
| }) | ||
| // Allows any HTML tags | ||
| .use(remarkRehype, { allowDangerousHtml: true }) | ||
| // Adds GitHub alerts | ||
| .use(rehypeAlert) | ||
| // Creates a HTML AST | ||
| .use(rehypeRaw) | ||
| // Removes disallowed tags | ||
| .use(rehypeSanitize, { | ||
| ...defaultSchema, | ||
| // Don't prefix the heading ids | ||
| clobberPrefix: '', | ||
| // Makes it even more strict | ||
| tagNames, | ||
| attributes: { | ||
| ...defaultSchema.attributes, | ||
| abbr: ['title'], | ||
| // Allow all class names while preserving the existing default attributes | ||
| '*': [...(defaultSchema.attributes?.['*'] ?? []), 'className'], | ||
| }, | ||
| // Strip content of dangerous elements, not just the tags | ||
| strip: ['script', 'style', 'object', 'embed', 'form'], | ||
| }) | ||
| // Syntax highlighting | ||
| .use(rehypeHighlight, { | ||
| languages: standardLanguages, | ||
| // Enable auto detection | ||
| detect: true, | ||
| }) | ||
| // Adds target="_blank" to external links | ||
| .use(rehypeExternalLinks, { target: '_blank' }) | ||
| // Formats the HTML | ||
| .use(rehypeFormat) | ||
| // Converts the HTML AST to a string | ||
| .use(rehypeStringify) | ||
| // Run the pipeline | ||
| .processSync(markdown); | ||
| return html.toString(); | ||
| } | ||
| /** | ||
| * Create a Markdown AST from a string. | ||
| */ | ||
| function getMarkdownAst(markdown) { | ||
| return unified().use(remarkParse).use(remarkGfm).parse(markdown); | ||
| return unified().use(remarkParse).use(remarkGfm).parse(markdown); | ||
| } | ||
| function getHeadings(markdown, depth = 1) { | ||
| const tree = getMarkdownAst(markdown); | ||
| const nodes = []; | ||
| visit(tree, "heading", (node) => { | ||
| const text = textFromNode(node); | ||
| if (text) { | ||
| nodes.push({ depth: node.depth ?? depth, value: text }); | ||
| /** | ||
| * Find all headings of a specific type in a Markdown AST. | ||
| */ | ||
| export function getHeadings(markdown, depth = 1) { | ||
| const tree = getMarkdownAst(markdown); | ||
| const nodes = []; | ||
| visit(tree, 'heading', (node) => { | ||
| const text = textFromNode(node); | ||
| if (text) { | ||
| nodes.push({ depth: node.depth ?? depth, value: text }); | ||
| } | ||
| }); | ||
| return nodes; | ||
| } | ||
| /** | ||
| * Extract plain text from a Markdown AST node (recursively). | ||
| * | ||
| * Handles headings with nested phrasing content such as links. | ||
| */ | ||
| export function textFromNode(node) { | ||
| if (node.type === 'text') { | ||
| return node.value ?? ''; | ||
| } | ||
| }); | ||
| return nodes; | ||
| if ('children' in node && Array.isArray(node.children)) { | ||
| return node.children.map((child) => textFromNode(child)).join(''); | ||
| } | ||
| return ''; | ||
| } | ||
| function textFromNode(node) { | ||
| if (node.type === "text") { | ||
| return node.value ?? ""; | ||
| } | ||
| if ("children" in node && Array.isArray(node.children)) { | ||
| return node.children.map((child) => textFromNode(child)).join(""); | ||
| } | ||
| return ""; | ||
| } | ||
| function splitContent(markdown) { | ||
| const tree = getMarkdownAst(markdown); | ||
| const sections = []; | ||
| let nodes = []; | ||
| tree.children?.forEach((node) => { | ||
| if (node.type === "heading") { | ||
| if (nodes.length) { | ||
| /** | ||
| * Return multiple Markdown documents. Every heading should be its own document. | ||
| */ | ||
| export function splitContent(markdown) { | ||
| const tree = getMarkdownAst(markdown); | ||
| /** Sections */ | ||
| const sections = []; | ||
| /** Nodes inside a section */ | ||
| let nodes = []; | ||
| tree.children?.forEach((node) => { | ||
| // If the node is a heading, start a new section | ||
| if (node.type === 'heading') { | ||
| if (nodes.length) { | ||
| sections.push(nodes); | ||
| } | ||
| sections.push([node]); | ||
| nodes = []; | ||
| } | ||
| // Otherwise, add the node to the current section | ||
| else { | ||
| nodes.push(node); | ||
| } | ||
| }); | ||
| // Add any remaining nodes | ||
| if (nodes.length) { | ||
| sections.push(nodes); | ||
| } | ||
| sections.push([node]); | ||
| nodes = []; | ||
| } else { | ||
| nodes.push(node); | ||
| } | ||
| }); | ||
| if (nodes.length) { | ||
| sections.push(nodes); | ||
| } | ||
| return sections.map((section) => createDocument(section)); | ||
| return sections.map((section) => createDocument(section)); | ||
| } | ||
| /** | ||
| * Use remark to create a Markdown document from a list of nodes. | ||
| */ | ||
| function createDocument(nodes) { | ||
| const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({ | ||
| type: "root", | ||
| children: nodes | ||
| }); | ||
| return markdown.trim(); | ||
| // Create the Markdown string | ||
| const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({ | ||
| type: 'root', | ||
| children: nodes, | ||
| }); | ||
| // Remove the whitespace | ||
| return markdown.trim(); | ||
| } | ||
| export { | ||
| getHeadings, | ||
| htmlFromMarkdown, | ||
| isHeading, | ||
| splitContent, | ||
| textFromNode | ||
| }; | ||
| //# sourceMappingURL=markdown.js.map |
@@ -1,5 +0,1 @@ | ||
| import { rehypeAlert } from "./rehype-alert.js"; | ||
| export { | ||
| rehypeAlert | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| export { rehypeAlert } from './rehype-alert.js'; |
@@ -1,94 +0,102 @@ | ||
| import { visit } from "unist-util-visit"; | ||
| const ALERT_TYPES = ["note", "tip", "important", "warning", "caution", "success"]; | ||
| import { visit } from 'unist-util-visit'; | ||
| const ALERT_TYPES = ['note', 'tip', 'important', 'warning', 'caution', 'success']; | ||
| // Simple whitespace check function | ||
| function isWhitespace(node) { | ||
| return node.type === "text" && typeof node.value === "string" && /^\s*$/.test(node.value); | ||
| return node.type === 'text' && typeof node.value === 'string' && /^\s*$/.test(node.value); | ||
| } | ||
| function rehypeAlert() { | ||
| return (tree) => { | ||
| visit(tree, "element", (node, index, parent) => { | ||
| if (node.tagName !== "blockquote" || typeof index !== "number" || !parent || parent.type !== "root") { | ||
| return; | ||
| } | ||
| const headIndex = node.children.findIndex((child) => !isWhitespace(child)); | ||
| if (headIndex === -1) { | ||
| return; | ||
| } | ||
| const head = node.children[headIndex]; | ||
| if (!head || head.type !== "element" || head.tagName !== "p") { | ||
| return; | ||
| } | ||
| const text = head.children[0]; | ||
| if (!text || text.type !== "text" || !text.value.startsWith("[!")) { | ||
| return; | ||
| } | ||
| const end = text.value.indexOf("]"); | ||
| if (end === -1) { | ||
| return; | ||
| } | ||
| const alertType = text.value.slice(2, end).toLowerCase(); | ||
| if (!ALERT_TYPES.includes(alertType)) { | ||
| return; | ||
| } | ||
| if (end + 1 === text.value.length) { | ||
| const next = head.children[1]; | ||
| if (next) { | ||
| if (next.type !== "element" || next.tagName !== "br") { | ||
| return; | ||
| } | ||
| if (!head.children[2]) { | ||
| return; | ||
| } | ||
| head.children = head.children.slice(2); | ||
| const node2 = head.children[0]; | ||
| if (node2 && node2.type === "text" && node2.value.charAt(0) === "\n") { | ||
| node2.value = node2.value.slice(1); | ||
| } | ||
| } else { | ||
| const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1]); | ||
| const nextIndex = skipped ? headIndex + 2 : headIndex + 1; | ||
| if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== "element") { | ||
| return; | ||
| } | ||
| node.children = node.children.slice(nextIndex); | ||
| } | ||
| } else if (text.value.charAt(end + 1) === "\n" && // Check if the next character is a newline or a non-whitespace character | ||
| (end + 2 === text.value.length || !/^\s*$/.test(text.value.slice(end + 2)))) { | ||
| text.value = text.value.slice(end + 2); | ||
| } else { | ||
| text.value = text.value.replace(/^\s*\[!.*?\]\s*/, ""); | ||
| } | ||
| const contentChildren = []; | ||
| for (let i = headIndex; i < node.children.length; i++) { | ||
| const child = node.children[i]; | ||
| if (child?.type === "element" && child.tagName === "p" && child.children) { | ||
| contentChildren.push(...child.children); | ||
| } else { | ||
| contentChildren.push(child); | ||
| } | ||
| } | ||
| parent.children[index] = { | ||
| type: "element", | ||
| tagName: "div", | ||
| properties: { className: ["markdown-alert", `markdown-alert-${alertType}`] }, | ||
| children: [ | ||
| { | ||
| type: "element", | ||
| tagName: "div", | ||
| properties: { className: ["markdown-alert-icon"] }, | ||
| children: [] | ||
| }, | ||
| { | ||
| type: "element", | ||
| tagName: "div", | ||
| properties: { className: ["markdown-alert-content"] }, | ||
| children: [{ type: "text", value: " " }, ...contentChildren] | ||
| } | ||
| ] | ||
| }; | ||
| }); | ||
| }; | ||
| export function rehypeAlert() { | ||
| return (tree) => { | ||
| visit(tree, 'element', (node, index, parent) => { | ||
| if (node.tagName !== 'blockquote' || typeof index !== 'number' || !parent || parent.type !== 'root') { | ||
| return; | ||
| } | ||
| // Find the first non-whitespace child | ||
| const headIndex = node.children.findIndex((child) => !isWhitespace(child)); | ||
| if (headIndex === -1) { | ||
| return; | ||
| } | ||
| const head = node.children[headIndex]; | ||
| if (!head || head.type !== 'element' || head.tagName !== 'p') { | ||
| return; | ||
| } | ||
| const text = head.children[0]; | ||
| if (!text || text.type !== 'text' || !text.value.startsWith('[!')) { | ||
| return; | ||
| } | ||
| const end = text.value.indexOf(']'); | ||
| if (end === -1) { | ||
| return; | ||
| } | ||
| // Extract the alert type | ||
| const alertType = text.value.slice(2, end).toLowerCase(); | ||
| if (!ALERT_TYPES.includes(alertType)) { | ||
| return; | ||
| } | ||
| // Remove the blockquote if it's empty | ||
| if (end + 1 === text.value.length) { | ||
| const next = head.children[1]; | ||
| if (next) { | ||
| if (next.type !== 'element' || next.tagName !== 'br') { | ||
| return; | ||
| } | ||
| if (!head.children[2]) { | ||
| return; | ||
| } | ||
| head.children = head.children.slice(2); | ||
| const node = head.children[0]; | ||
| if (node && node.type === 'text' && node.value.charAt(0) === '\n') { | ||
| node.value = node.value.slice(1); | ||
| } | ||
| } | ||
| else { | ||
| const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1]); | ||
| const nextIndex = skipped ? headIndex + 2 : headIndex + 1; | ||
| if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== 'element') { | ||
| return; | ||
| } | ||
| node.children = node.children.slice(nextIndex); | ||
| } | ||
| } | ||
| else if (text.value.charAt(end + 1) === '\n' && | ||
| // Check if the next character is a newline or a non-whitespace character | ||
| (end + 2 === text.value.length || !/^\s*$/.test(text.value.slice(end + 2)))) { | ||
| text.value = text.value.slice(end + 2); | ||
| } | ||
| else { | ||
| // Remove the alert marker if it's not followed by a newline or a non-whitespace character | ||
| text.value = text.value.replace(/^\s*\[!.*?\]\s*/, ''); | ||
| } | ||
| // Extract content from paragraphs to avoid wrapping in <p> tags | ||
| const contentChildren = []; | ||
| for (let i = headIndex; i < node.children.length; i++) { | ||
| const child = node.children[i]; | ||
| if (child?.type === 'element' && child.tagName === 'p' && child.children) { | ||
| contentChildren.push(...child.children); | ||
| } | ||
| else { | ||
| contentChildren.push(child); | ||
| } | ||
| } | ||
| // Replace the blockquote with a div containing the alert | ||
| parent.children[index] = { | ||
| type: 'element', | ||
| tagName: 'div', | ||
| properties: { className: ['markdown-alert', `markdown-alert-${alertType}`] }, | ||
| children: [ | ||
| { | ||
| type: 'element', | ||
| tagName: 'div', | ||
| properties: { className: ['markdown-alert-icon'] }, | ||
| children: [], | ||
| }, | ||
| { | ||
| type: 'element', | ||
| tagName: 'div', | ||
| properties: { className: ['markdown-alert-content'] }, | ||
| children: [{ type: 'text', value: ' ' }, ...contentChildren], | ||
| }, | ||
| ], | ||
| }; | ||
| }); | ||
| }; | ||
| } | ||
| export { | ||
| rehypeAlert | ||
| }; | ||
| //# sourceMappingURL=rehype-alert.js.map |
@@ -1,5 +0,1 @@ | ||
| import { rehypeHighlight } from "./rehype-highlight.js"; | ||
| export { | ||
| rehypeHighlight | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| export { rehypeHighlight } from './rehype-highlight.js'; |
@@ -1,90 +0,98 @@ | ||
| import { toText } from "hast-util-to-text"; | ||
| import { createLowlight } from "lowlight"; | ||
| import { visit } from "unist-util-visit"; | ||
| import { lowlightLanguageMappings } from "../constants.js"; | ||
| import { toText } from 'hast-util-to-text'; | ||
| import { createLowlight } from 'lowlight'; | ||
| import { visit } from 'unist-util-visit'; | ||
| import { lowlightLanguageMappings } from '../constants.js'; | ||
| const emptyOptions = {}; | ||
| function rehypeHighlight(options) { | ||
| const settings = options || emptyOptions; | ||
| const aliases = settings.aliases; | ||
| const detect = options?.detect ?? false; | ||
| const languages = settings.languages; | ||
| const plainText = settings.plainText; | ||
| const prefix = settings.prefix; | ||
| const subset = settings.subset; | ||
| let name = "hljs"; | ||
| const lowlight = options?.lowlight ?? createLowlight(languages); | ||
| if (aliases) { | ||
| lowlight.registerAlias(aliases); | ||
| } | ||
| if (prefix) { | ||
| const pos = prefix.indexOf("-"); | ||
| name = pos > -1 ? prefix.slice(0, pos) : prefix; | ||
| } | ||
| return (tree, file) => { | ||
| visit(tree, "element", (node, _, parent) => { | ||
| if (node.tagName !== "code" || !parent || parent.type !== "element" || parent.tagName !== "pre") { | ||
| return; | ||
| } | ||
| const lang = language(node); | ||
| if (lang === "no-highlight" || !lang && !detect || lang && plainText?.includes(lang)) { | ||
| return; | ||
| } | ||
| if (!Array.isArray(node.properties.className)) { | ||
| node.properties.className = []; | ||
| } | ||
| if (!node.properties.className.includes(name)) { | ||
| node.properties.className.unshift(name); | ||
| } | ||
| let result; | ||
| try { | ||
| result = lang ? lowlight.highlight(lang, toText(parent), { prefix }) : lowlight.highlightAuto(toText(parent), { prefix, subset }); | ||
| } catch (error) { | ||
| const cause = error; | ||
| if (lang && /Unknown language/.test(cause.message)) { | ||
| file.message(`Cannot highlight as \`${lang}\`, it's not registered`, { | ||
| ancestors: [parent, node], | ||
| cause, | ||
| place: node.position, | ||
| ruleId: "missing-language", | ||
| source: "rehype-highlight" | ||
| }); | ||
| return; | ||
| } | ||
| throw cause; | ||
| } | ||
| if (!lang && result.data?.language) { | ||
| node.properties.className.push("language-" + result.data.language); | ||
| } | ||
| if (result.children.length > 0) { | ||
| node.children = result.children; | ||
| } | ||
| }); | ||
| }; | ||
| /** | ||
| * Lowlight syntax highlighting plugin for rehype pipelines | ||
| * | ||
| * Derived from: @url https://github.com/rehypejs/rehype-highlight/blob/main/lib/index.js | ||
| */ | ||
| export function rehypeHighlight(options) { | ||
| const settings = options || emptyOptions; | ||
| const aliases = settings.aliases; | ||
| const detect = options?.detect ?? false; | ||
| const languages = settings.languages; | ||
| const plainText = settings.plainText; | ||
| const prefix = settings.prefix; | ||
| const subset = settings.subset; | ||
| let name = 'hljs'; | ||
| // Create a lowlight instance if not provided | ||
| const lowlight = options?.lowlight ?? createLowlight(languages); | ||
| if (aliases) { | ||
| lowlight.registerAlias(aliases); | ||
| } | ||
| if (prefix) { | ||
| const pos = prefix.indexOf('-'); | ||
| name = pos > -1 ? prefix.slice(0, pos) : prefix; | ||
| } | ||
| /** Transform.*/ | ||
| return (tree, file) => { | ||
| visit(tree, 'element', (node, _, parent) => { | ||
| if (node.tagName !== 'code' || !parent || parent.type !== 'element' || parent.tagName !== 'pre') { | ||
| return; | ||
| } | ||
| const lang = language(node); | ||
| if (lang === 'no-highlight' || (!lang && !detect) || (lang && plainText?.includes(lang))) { | ||
| return; | ||
| } | ||
| if (!Array.isArray(node.properties.className)) { | ||
| node.properties.className = []; | ||
| } | ||
| if (!node.properties.className.includes(name)) { | ||
| node.properties.className.unshift(name); | ||
| } | ||
| let result; | ||
| try { | ||
| result = lang | ||
| ? lowlight.highlight(lang, toText(parent), { prefix }) | ||
| : lowlight.highlightAuto(toText(parent), { prefix, subset }); | ||
| } | ||
| catch (error) { | ||
| const cause = error; | ||
| if (lang && /Unknown language/.test(cause.message)) { | ||
| file.message(`Cannot highlight as \`${lang}\`, it's not registered`, { | ||
| ancestors: [parent, node], | ||
| cause, | ||
| place: node.position, | ||
| ruleId: 'missing-language', | ||
| source: 'rehype-highlight', | ||
| }); | ||
| /* c8 ignore next 5 -- throw arbitrary hljs errors */ | ||
| return; | ||
| } | ||
| throw cause; | ||
| } | ||
| if (!lang && result.data?.language) { | ||
| node.properties.className.push('language-' + result.data.language); | ||
| } | ||
| if (result.children.length > 0) { | ||
| node.children = result.children; | ||
| } | ||
| }); | ||
| }; | ||
| } | ||
| /** Get the programming language of `node` or an empty string */ | ||
| function language(node) { | ||
| const list = node.properties.className; | ||
| if (!Array.isArray(list)) { | ||
| return ""; | ||
| } | ||
| const name = list.reduce((result, _item) => { | ||
| if (result) { | ||
| return result; | ||
| const list = node.properties.className; | ||
| if (!Array.isArray(list)) { | ||
| return ''; | ||
| } | ||
| const item = String(_item); | ||
| if (item === "no-highlight" || item === "nohighlight") { | ||
| return "no-highlight"; | ||
| } | ||
| if (item.slice(0, 5) === "lang-") { | ||
| return item.slice(5); | ||
| } | ||
| if (item.slice(0, 9) === "language-") { | ||
| return item.slice(9); | ||
| } | ||
| return result; | ||
| }, ""); | ||
| return lowlightLanguageMappings[name || ""] || name; | ||
| const name = list.reduce((result, _item) => { | ||
| if (result) { | ||
| return result; | ||
| } | ||
| const item = String(_item); | ||
| if (item === 'no-highlight' || item === 'nohighlight') { | ||
| return 'no-highlight'; | ||
| } | ||
| if (item.slice(0, 5) === 'lang-') { | ||
| return item.slice(5); | ||
| } | ||
| if (item.slice(0, 9) === 'language-') { | ||
| return item.slice(9); | ||
| } | ||
| return result; | ||
| }, ''); | ||
| return lowlightLanguageMappings[name || ''] || name; | ||
| } | ||
| export { | ||
| rehypeHighlight | ||
| }; | ||
| //# sourceMappingURL=rehype-highlight.js.map |
+5
-11
@@ -19,3 +19,3 @@ { | ||
| ], | ||
| "version": "0.3.0", | ||
| "version": "0.3.1", | ||
| "engines": { | ||
@@ -96,17 +96,11 @@ "node": ">=22" | ||
| "vfile": "^6.0.1", | ||
| "vite": "^7.3.1", | ||
| "@scalar/build-tooling": "0.5.0", | ||
| "@scalar/themes": "0.15.0" | ||
| "vite": "8.0.0", | ||
| "@scalar/themes": "0.15.1" | ||
| }, | ||
| "scripts": { | ||
| "build": "scalar-build-esbuild", | ||
| "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/css dist/css", | ||
| "dev": "vite", | ||
| "format": "scalar-format", | ||
| "format:check": "scalar-format-check", | ||
| "lint:check": "scalar-lint-check", | ||
| "lint:fix": "scalar-lint-fix", | ||
| "test": "vitest", | ||
| "types:build": "scalar-types-build", | ||
| "types:check": "scalar-types-check" | ||
| "types:check": "tsc --noEmit" | ||
| } | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/code/highlight.ts"], | ||
| "sourcesContent": ["import type { Element, Root } from 'hast'\nimport type { LanguageFn } from 'highlight.js'\nimport rehypeParse from 'rehype-parse'\nimport rehypeStringify from 'rehype-stringify'\nimport { type Plugin, unified } from 'unified'\nimport { visit } from 'unist-util-visit'\n\nimport { lowlightLanguageMappings } from '@/constants'\nimport { rehypeHighlight } from '@/rehype-highlight'\n\nimport { codeBlockLinesPlugin } from './line-numbers'\n\n/**\n * Syntax highlights a code string using the `rehype-highlight` library.\n */\nexport function syntaxHighlight(\n codeString: string,\n options: {\n lang: string\n languages: Record<string, LanguageFn>\n lineNumbers?: boolean\n maskCredentials?: string | string[]\n },\n) {\n // Simple restriction on credentials to prevent unexpected behavior\n const credentials = (\n typeof options?.maskCredentials === 'string' ? [options.maskCredentials] : (options?.maskCredentials ?? [])\n ).filter((c) => {\n // Credentials must be at least 3 characters to mask.\n if (c.length < 3) {\n return false\n }\n\n return true\n })\n\n // Classname is used by lowlight to select the language model\n const className = `language-${lowlightLanguageMappings[options.lang] ?? options.lang}`\n\n // biome-ignore lint/suspicious/noEmptyBlockStatements: empty plugin\n const nullPlugin = (() => {}) satisfies Plugin\n\n const html = unified()\n // Parses markdown\n .use(rehypeParse, { fragment: true })\n // Raw code string must be injected after initial hast parsing\n // so that HTML code is not parsed into the hast tree\n .use(injectRawCodeStringPlugin(codeString))\n // Syntax highlighting\n .use(rehypeHighlight, {\n languages: options.languages,\n })\n .use(options?.lineNumbers ? codeBlockLinesPlugin : nullPlugin)\n // Converts the HTML AST to a string\n .use(rehypeStringify)\n // Run the pipeline\n .processSync(`<pre><code class=\"${className}\"></code></pre>`)\n\n const htmlString = html.toString()\n\n // Replace any credentials with a wrapper element\n return credentials.length\n ? credentials.reduce(\n (acc, credential) =>\n acc\n .split(credential)\n .join(`<span class=\"credential\"><span class=\"credential-value\">${credential}</span></span>`),\n htmlString,\n )\n : htmlString\n}\n\n/**\n * To prevent unified from parsing any content of the code string we inject\n * it as a raw text node into the AST tree as a child of the code element\n */\nfunction injectRawCodeStringPlugin(rawCodeString: string) {\n return () => (tree: Root) => {\n visit(tree, 'element', (node: Element) => {\n if (node.tagName === 'code') {\n node.children.push({\n type: 'text',\n value: rawCodeString,\n })\n }\n })\n }\n}\n"], | ||
| "mappings": "AAEA,OAAO,iBAAiB;AACxB,OAAO,qBAAqB;AAC5B,SAAsB,eAAe;AACrC,SAAS,aAAa;AAEtB,SAAS,gCAAgC;AACzC,SAAS,uBAAuB;AAEhC,SAAS,4BAA4B;AAK9B,SAAS,gBACd,YACA,SAMA;AAEA,QAAM,eACJ,OAAO,SAAS,oBAAoB,WAAW,CAAC,QAAQ,eAAe,IAAK,SAAS,mBAAmB,CAAC,GACzG,OAAO,CAAC,MAAM;AAEd,QAAI,EAAE,SAAS,GAAG;AAChB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AAGD,QAAM,YAAY,YAAY,yBAAyB,QAAQ,IAAI,KAAK,QAAQ,IAAI;AAGpF,QAAM,aAAc,MAAM;AAAA,EAAC;AAE3B,QAAM,OAAO,QAAQ,EAElB,IAAI,aAAa,EAAE,UAAU,KAAK,CAAC,EAGnC,IAAI,0BAA0B,UAAU,CAAC,EAEzC,IAAI,iBAAiB;AAAA,IACpB,WAAW,QAAQ;AAAA,EACrB,CAAC,EACA,IAAI,SAAS,cAAc,uBAAuB,UAAU,EAE5D,IAAI,eAAe,EAEnB,YAAY,qBAAqB,SAAS,iBAAiB;AAE9D,QAAM,aAAa,KAAK,SAAS;AAGjC,SAAO,YAAY,SACf,YAAY;AAAA,IACV,CAAC,KAAK,eACJ,IACG,MAAM,UAAU,EAChB,KAAK,2DAA2D,UAAU,gBAAgB;AAAA,IAC/F;AAAA,EACF,IACA;AACN;AAMA,SAAS,0BAA0B,eAAuB;AACxD,SAAO,MAAM,CAAC,SAAe;AAC3B,UAAM,MAAM,WAAW,CAAC,SAAkB;AACxC,UAAI,KAAK,YAAY,QAAQ;AAC3B,aAAK,SAAS,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AACF;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/code/index.ts"], | ||
| "sourcesContent": ["export { syntaxHighlight } from './highlight'\n"], | ||
| "mappings": "AAAA,SAAS,uBAAuB;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/code/line-numbers.ts"], | ||
| "sourcesContent": ["import type { Element, ElementContent, Root, Text } from 'hast'\nimport { visit } from 'unist-util-visit'\n\n// ---------------------------------------------------------------------------\n// Line Numbering plugin\n\nfunction isText(element?: ElementContent): element is Text {\n return element?.type === 'text'\n}\n\nfunction isElement(node?: ElementContent): node is Element {\n return node?.type === 'element'\n}\n\nfunction textElement(value: string): Text {\n return { type: 'text', value }\n}\n\nfunction lineBreak(): Text {\n return { type: 'text', value: '\\n' }\n}\n\n/**\n * Adds lines to code blocks\n */\nexport function codeBlockLinesPlugin() {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element, _i, parent: Root | Element | null) => {\n if (parent?.type === 'element' && parent.tagName === 'pre' && node.tagName === 'code') {\n let numLines = 0\n\n // Wraps each line in a span\n node.children = addLines(node)\n\n // Adds a line break to the end of each line\n node.children.forEach((child: ElementContent) => {\n if (child.type === 'element' && child.tagName === 'span') {\n const lastChild: ElementContent | undefined = child.children[child.children.length - 1]\n\n if (lastChild && (!isText(lastChild) || (isText(lastChild) && !hasLineBreak(lastChild)))) {\n child.children.push(lineBreak())\n numLines++\n }\n }\n })\n\n // We need to maintain a count of the total lines to allow space for the labels\n node.properties.style = [`--line-count: ${numLines};`, `--line-digits: ${numLines.toString().length};`]\n }\n })\n\n // console.log('NUMBER OF LINES IS: ', numLines)\n }\n}\n\n/**\n * Adds lines to a node recursively and returns them\n *\n * @param node - The node to add lines to\n * @param lines - The current lines\n * @param copyParent - Whether to copy the parent node to save the original node styles\n */\nfunction addLines(node: Element, lines: Element[] = [], copyParent?: boolean): Element[] {\n const line = () => lines[lines.length - 1] ?? ((lines.push(createLine()) && lines[lines.length - 1]) || undefined)\n\n node.children.forEach((child: ElementContent) => {\n if (isText(child) && hasLineBreak(child)) {\n const split: string[] = child.value.split(/\\n/)\n\n split.forEach((content: string, i: number) => {\n if (copyParent) {\n line()?.children.push({ ...node, children: [textElement(content)] })\n } else {\n line()?.children.push(textElement(content))\n }\n\n i !== split.length - 1 && lines.push(createLine())\n })\n } else if (isElement(child) && child.children.some(hasLineBreak)) {\n addLines(child, lines, true)\n } else {\n line()?.children.push(child)\n }\n })\n\n return lines\n}\n\n/**\n * Creates a new line element\n *\n * @param children - The children the line should have initially\n */\nfunction createLine(...children: ElementContent[]): Element {\n return {\n type: 'element',\n tagName: 'span',\n properties: { class: ['line'] },\n children,\n }\n}\n\n/**\n * Checks if a node has a line break\n *\n * @param node - The node to check\n */\nfunction hasLineBreak(node: ElementContent): boolean {\n return (isText(node) && /\\r?\\n/.test(node.value)) || (isElement(node) && node.children.some(hasLineBreak))\n}\n"], | ||
| "mappings": "AACA,SAAS,aAAa;AAKtB,SAAS,OAAO,SAA2C;AACzD,SAAO,SAAS,SAAS;AAC3B;AAEA,SAAS,UAAU,MAAwC;AACzD,SAAO,MAAM,SAAS;AACxB;AAEA,SAAS,YAAY,OAAqB;AACxC,SAAO,EAAE,MAAM,QAAQ,MAAM;AAC/B;AAEA,SAAS,YAAkB;AACzB,SAAO,EAAE,MAAM,QAAQ,OAAO,KAAK;AACrC;AAKO,SAAS,uBAAuB;AACrC,SAAO,CAAC,SAAe;AACrB,UAAM,MAAM,WAAW,CAAC,MAAe,IAAI,WAAkC;AAC3E,UAAI,QAAQ,SAAS,aAAa,OAAO,YAAY,SAAS,KAAK,YAAY,QAAQ;AACrF,YAAI,WAAW;AAGf,aAAK,WAAW,SAAS,IAAI;AAG7B,aAAK,SAAS,QAAQ,CAAC,UAA0B;AAC/C,cAAI,MAAM,SAAS,aAAa,MAAM,YAAY,QAAQ;AACxD,kBAAM,YAAwC,MAAM,SAAS,MAAM,SAAS,SAAS,CAAC;AAEtF,gBAAI,cAAc,CAAC,OAAO,SAAS,KAAM,OAAO,SAAS,KAAK,CAAC,aAAa,SAAS,IAAK;AACxF,oBAAM,SAAS,KAAK,UAAU,CAAC;AAC/B;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAGD,aAAK,WAAW,QAAQ,CAAC,iBAAiB,QAAQ,KAAK,kBAAkB,SAAS,SAAS,EAAE,MAAM,GAAG;AAAA,MACxG;AAAA,IACF,CAAC;AAAA,EAGH;AACF;AASA,SAAS,SAAS,MAAe,QAAmB,CAAC,GAAG,YAAiC;AACvF,QAAM,OAAO,MAAM,MAAM,MAAM,SAAS,CAAC,MAAO,MAAM,KAAK,WAAW,CAAC,KAAK,MAAM,MAAM,SAAS,CAAC,KAAM;AAExG,OAAK,SAAS,QAAQ,CAAC,UAA0B;AAC/C,QAAI,OAAO,KAAK,KAAK,aAAa,KAAK,GAAG;AACxC,YAAM,QAAkB,MAAM,MAAM,MAAM,IAAI;AAE9C,YAAM,QAAQ,CAAC,SAAiB,MAAc;AAC5C,YAAI,YAAY;AACd,eAAK,GAAG,SAAS,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,YAAY,OAAO,CAAC,EAAE,CAAC;AAAA,QACrE,OAAO;AACL,eAAK,GAAG,SAAS,KAAK,YAAY,OAAO,CAAC;AAAA,QAC5C;AAEA,cAAM,MAAM,SAAS,KAAK,MAAM,KAAK,WAAW,CAAC;AAAA,MACnD,CAAC;AAAA,IACH,WAAW,UAAU,KAAK,KAAK,MAAM,SAAS,KAAK,YAAY,GAAG;AAChE,eAAS,OAAO,OAAO,IAAI;AAAA,IAC7B,OAAO;AACL,WAAK,GAAG,SAAS,KAAK,KAAK;AAAA,IAC7B;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAOA,SAAS,cAAc,UAAqC;AAC1D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE;AAAA,IAC9B;AAAA,EACF;AACF;AAOA,SAAS,aAAa,MAA+B;AACnD,SAAQ,OAAO,IAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAO,UAAU,IAAI,KAAK,KAAK,SAAS,KAAK,YAAY;AAC1G;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../src/constants.ts"], | ||
| "sourcesContent": ["/** Map common markdown language shortcuts to lowlight languages */\nexport const lowlightLanguageMappings: Record<string, string> = {\n 'ts': 'typescript',\n 'js': 'javascript',\n 'py': 'python',\n 'py3': 'python',\n 'c#': 'csharp',\n 'c++': 'cpp',\n 'node': 'javascript',\n}\n\n//\n"], | ||
| "mappings": "AACO,MAAM,2BAAmD;AAAA,EAC9D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.ts"], | ||
| "sourcesContent": ["export { syntaxHighlight } from './code'\nexport { lowlightLanguageMappings } from './constants'\nexport * from './languages'\nexport { type Node, htmlFromMarkdown, isHeading, textFromNode } from './markdown'\nexport { rehypeHighlight } from './rehype-highlight'\n"], | ||
| "mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gCAAgC;AACzC,cAAc;AACd,SAAoB,kBAAkB,WAAW,oBAAoB;AACrE,SAAS,uBAAuB;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/languages/basic.ts"], | ||
| "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport bash from 'highlight.js/lib/languages/bash'\nimport css from 'highlight.js/lib/languages/css'\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport json from 'highlight.js/lib/languages/json'\nimport less from 'highlight.js/lib/languages/less'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport python from 'highlight.js/lib/languages/python'\nimport scss from 'highlight.js/lib/languages/scss'\nimport shell from 'highlight.js/lib/languages/shell'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport xml from 'highlight.js/lib/languages/xml'\nimport yaml from 'highlight.js/lib/languages/yaml'\n\nconst basicLanguages: Record<string, LanguageFn> = {\n bash,\n css,\n html: xml,\n javascript,\n json,\n less,\n markdown,\n plaintext,\n python,\n scss,\n shell,\n typescript,\n xml,\n yaml,\n}\n\nexport { basicLanguages }\n"], | ||
| "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,gBAAgB;AACvB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,cAAc;AACrB,OAAO,eAAe;AACtB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,SAAS;AAChB,OAAO,UAAU;AAEjB,MAAM,iBAA6C;AAAA,EACjD;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/languages/index.ts"], | ||
| "sourcesContent": ["export { basicLanguages } from './basic'\nexport { jsonYamlLanguages } from './json-yaml'\nexport { standardLanguages } from './standard'\n"], | ||
| "mappings": "AAAA,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/languages/json-yaml.ts"], | ||
| "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport json from 'highlight.js/lib/languages/json'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport yaml from 'highlight.js/lib/languages/yaml'\n\nconst jsonYamlLanguages: Record<string, LanguageFn> = {\n json,\n plaintext,\n yaml,\n}\n\nexport { jsonYamlLanguages }\n"], | ||
| "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,eAAe;AACtB,OAAO,UAAU;AAEjB,MAAM,oBAAgD;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AACF;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/languages/standard.ts"], | ||
| "sourcesContent": ["import type { LanguageFn } from 'highlight.js'\nimport bash from 'highlight.js/lib/languages/bash'\nimport c from 'highlight.js/lib/languages/c'\nimport clojure from 'highlight.js/lib/languages/clojure'\nimport cpp from 'highlight.js/lib/languages/cpp'\nimport csharp from 'highlight.js/lib/languages/csharp'\nimport css from 'highlight.js/lib/languages/css'\nimport dart from 'highlight.js/lib/languages/dart'\nimport diff from 'highlight.js/lib/languages/diff'\nimport dockerfile from 'highlight.js/lib/languages/dockerfile'\nimport elixir from 'highlight.js/lib/languages/elixir'\nimport fsharp from 'highlight.js/lib/languages/fsharp'\nimport go from 'highlight.js/lib/languages/go'\nimport graphql from 'highlight.js/lib/languages/graphql'\nimport haskell from 'highlight.js/lib/languages/haskell'\nimport http from 'highlight.js/lib/languages/http'\nimport ini from 'highlight.js/lib/languages/ini'\nimport java from 'highlight.js/lib/languages/java'\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport json from 'highlight.js/lib/languages/json'\nimport kotlin from 'highlight.js/lib/languages/kotlin'\nimport less from 'highlight.js/lib/languages/less'\nimport lua from 'highlight.js/lib/languages/lua'\nimport makefile from 'highlight.js/lib/languages/makefile'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport matlab from 'highlight.js/lib/languages/matlab'\nimport nginx from 'highlight.js/lib/languages/nginx'\nimport objectivec from 'highlight.js/lib/languages/objectivec'\nimport ocaml from 'highlight.js/lib/languages/ocaml'\nimport perl from 'highlight.js/lib/languages/perl'\nimport php from 'highlight.js/lib/languages/php'\nimport plaintext from 'highlight.js/lib/languages/plaintext'\nimport powershell from 'highlight.js/lib/languages/powershell'\nimport properties from 'highlight.js/lib/languages/properties'\nimport python from 'highlight.js/lib/languages/python'\nimport r from 'highlight.js/lib/languages/r'\nimport ruby from 'highlight.js/lib/languages/ruby'\nimport rust from 'highlight.js/lib/languages/rust'\nimport scala from 'highlight.js/lib/languages/scala'\nimport scss from 'highlight.js/lib/languages/scss'\nimport shell from 'highlight.js/lib/languages/shell'\nimport sql from 'highlight.js/lib/languages/sql'\nimport swift from 'highlight.js/lib/languages/swift'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport xml from 'highlight.js/lib/languages/xml'\nimport yaml from 'highlight.js/lib/languages/yaml'\n// @ts-expect-error No types available\nimport curl from 'highlightjs-curl'\n\n/**\n * We group languages into three categories based on their popularity and usage.\n * This helps in optimizing the bundle size by allowing users to include only\n * the languages they need.\n *\n * 1. Standard Languages: These are the most popular languages that cover the\n * majority of use cases.\n * 2. Medium Languages: These languages are not as popular as the ones in\n * `standardLanguages`, but still have a decent amount of users.\n * 3. Specialized Languages: These languages are more specialized and have a\n * smaller user base.\n *\n * Each category is represented as a separate object, making it easy to import\n * only the languages you need.\n */\n\n/**\n * These are the most popular languages that cover the majority of use cases.\n */\nexport const standardLanguages = {\n bash,\n c,\n clojure,\n cpp,\n csharp,\n css,\n curl,\n dart,\n diff,\n docker: dockerfile,\n dockerfile,\n elixir,\n fsharp,\n go,\n graphql,\n haskell,\n html: xml,\n http,\n ini,\n java,\n javascript,\n json,\n kotlin,\n less,\n lua,\n makefile,\n markdown,\n matlab,\n nginx,\n objectivec,\n ocaml,\n perl,\n php,\n plaintext,\n powershell,\n properties,\n python,\n r,\n ruby,\n rust,\n scala,\n scss,\n shell,\n sql,\n swift,\n toml: ini,\n typescript,\n xml,\n yaml,\n} as const satisfies Record<string, LanguageFn>\n"], | ||
| "mappings": "AACA,OAAO,UAAU;AACjB,OAAO,OAAO;AACd,OAAO,aAAa;AACpB,OAAO,SAAS;AAChB,OAAO,YAAY;AACnB,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,aAAa;AACpB,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,OAAO,gBAAgB;AACvB,OAAO,UAAU;AACjB,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,cAAc;AACrB,OAAO,cAAc;AACrB,OAAO,YAAY;AACnB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,eAAe;AACtB,OAAO,gBAAgB;AACvB,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,OAAO,OAAO;AACd,OAAO,UAAU;AACjB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,OAAO,gBAAgB;AACvB,OAAO,SAAS;AAChB,OAAO,UAAU;AAEjB,OAAO,UAAU;AAqBV,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AACF;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/markdown/index.ts"], | ||
| "sourcesContent": ["export * from './markdown'\n"], | ||
| "mappings": "AAAA,cAAc;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/markdown/markdown.ts"], | ||
| "sourcesContent": ["import type { Heading, Node, PhrasingContent, Root, RootContent } from 'mdast'\nimport rehypeExternalLinks from 'rehype-external-links'\nimport rehypeFormat from 'rehype-format'\nimport rehypeRaw from 'rehype-raw'\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize'\nimport rehypeStringify from 'rehype-stringify'\nimport remarkGfm from 'remark-gfm'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport remarkStringify from 'remark-stringify'\nimport { unified } from 'unified'\nimport { SKIP, visit } from 'unist-util-visit'\n\nimport { standardLanguages } from '@/languages'\nimport { rehypeAlert } from '@/rehype-alert'\nimport { rehypeHighlight } from '@/rehype-highlight'\n\ntype Options = {\n transform?: (node: Node) => Node\n type?: string\n}\n\nexport type { Node } from 'mdast'\n\n/**\n * Type-guard to check if a node is a heading.\n */\nexport const isHeading = (node: Node): node is Heading => {\n return node.type === 'heading' && 'depth' in node && 'children' in node\n}\n\n/**\n * Plugin to transform nodes in a Markdown AST\n */\nconst transformNodes =\n (options?: Readonly<Options> | null | undefined, ..._ignored: any[]) =>\n (tree: Node) => {\n if (!options?.transform || !options?.type) {\n return\n }\n\n visit(tree, options?.type, (node) => {\n options?.transform ? options?.transform(node) : node\n\n return SKIP\n })\n\n return\n }\n\n/**\n * Take a Markdown string and generate HTML from it\n */\nexport function htmlFromMarkdown(\n markdown: string,\n options?: {\n removeTags?: string[]\n allowTags?: string[]\n transform?: (node: Node) => Node\n transformType?: string\n },\n) {\n // Add permitted tags and remove stripped ones\n const removeTags = options?.removeTags ?? []\n const tagNames = [...(defaultSchema.tagNames ?? []), ...(options?.allowTags ?? [])].filter(\n (t) => !removeTags.includes(t),\n )\n\n const html = unified()\n // Parses markdown\n .use(remarkParse)\n // Support autolink literals, footnotes, strikethrough, tables and tasklists\n .use(remarkGfm)\n .use(transformNodes, {\n transform: options?.transform,\n type: options?.transformType,\n })\n // Allows any HTML tags\n .use(remarkRehype, { allowDangerousHtml: true })\n // Adds GitHub alerts\n .use(rehypeAlert)\n // Creates a HTML AST\n .use(rehypeRaw)\n // Removes disallowed tags\n .use(rehypeSanitize, {\n ...defaultSchema,\n // Don't prefix the heading ids\n clobberPrefix: '',\n // Makes it even more strict\n tagNames,\n attributes: {\n ...defaultSchema.attributes,\n abbr: ['title'],\n // Allow all class names while preserving the existing default attributes\n '*': [...(defaultSchema.attributes?.['*'] ?? []), 'className'],\n },\n // Strip content of dangerous elements, not just the tags\n strip: ['script', 'style', 'object', 'embed', 'form'],\n })\n // Syntax highlighting\n .use(rehypeHighlight, {\n languages: standardLanguages,\n // Enable auto detection\n detect: true,\n })\n // Adds target=\"_blank\" to external links\n .use(rehypeExternalLinks, { target: '_blank' })\n // Formats the HTML\n .use(rehypeFormat)\n // Converts the HTML AST to a string\n .use(rehypeStringify)\n // Run the pipeline\n .processSync(markdown)\n\n return html.toString()\n}\n\n/**\n * Create a Markdown AST from a string.\n */\nfunction getMarkdownAst(markdown: string): Root {\n return unified().use(remarkParse).use(remarkGfm).parse(markdown)\n}\n\n/**\n * Find all headings of a specific type in a Markdown AST.\n */\nexport function getHeadings(\n markdown: string,\n depth: number = 1,\n): {\n depth: number\n value: string\n}[] {\n const tree = getMarkdownAst(markdown)\n\n const nodes: {\n depth: number\n value: string\n }[] = []\n\n visit(tree, 'heading', (node) => {\n const text = textFromNode(node)\n\n if (text) {\n nodes.push({ depth: node.depth ?? depth, value: text })\n }\n })\n\n return nodes\n}\n\n/**\n * Extract plain text from a Markdown AST node (recursively).\n *\n * Handles headings with nested phrasing content such as links.\n */\nexport function textFromNode(node: Heading | PhrasingContent): string {\n if (node.type === 'text') {\n return node.value ?? ''\n }\n\n if ('children' in node && Array.isArray(node.children)) {\n return node.children.map((child) => textFromNode(child)).join('')\n }\n\n return ''\n}\n\n/**\n * Return multiple Markdown documents. Every heading should be its own document.\n */\nexport function splitContent(markdown: string) {\n const tree = getMarkdownAst(markdown)\n\n /** Sections */\n const sections: RootContent[][] = []\n\n /** Nodes inside a section */\n let nodes: RootContent[] = []\n\n tree.children?.forEach((node) => {\n // If the node is a heading, start a new section\n if (node.type === 'heading') {\n if (nodes.length) {\n sections.push(nodes)\n }\n\n sections.push([node])\n\n nodes = []\n }\n // Otherwise, add the node to the current section\n else {\n nodes.push(node)\n }\n })\n\n // Add any remaining nodes\n if (nodes.length) {\n sections.push(nodes)\n }\n\n return sections.map((section) => createDocument(section))\n}\n\n/**\n * Use remark to create a Markdown document from a list of nodes.\n */\nfunction createDocument(nodes: RootContent[]) {\n // Create the Markdown string\n const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({\n type: 'root',\n children: nodes,\n })\n\n // Remove the whitespace\n return markdown.trim()\n}\n"], | ||
| "mappings": "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", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/rehype-alert/index.ts"], | ||
| "sourcesContent": ["export { rehypeAlert } from './rehype-alert'\n"], | ||
| "mappings": "AAAA,SAAS,mBAAmB;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/rehype-alert/rehype-alert.ts"], | ||
| "sourcesContent": ["import type { Element } from 'hast'\nimport type { Root } from 'mdast'\nimport { visit } from 'unist-util-visit'\n\nconst ALERT_TYPES = ['note', 'tip', 'important', 'warning', 'caution', 'success'] as const\n\n// Simple whitespace check function\nfunction isWhitespace(node: any): boolean {\n return node.type === 'text' && typeof node.value === 'string' && /^\\s*$/.test(node.value)\n}\n\nexport function rehypeAlert() {\n return (tree: Root) => {\n visit(tree, 'element', (node: Element, index, parent: any) => {\n if (node.tagName !== 'blockquote' || typeof index !== 'number' || !parent || parent.type !== 'root') {\n return\n }\n\n // Find the first non-whitespace child\n const headIndex = node.children.findIndex((child) => !isWhitespace(child))\n if (headIndex === -1) {\n return\n }\n\n const head = node.children[headIndex]\n\n if (!head || head.type !== 'element' || head.tagName !== 'p') {\n return\n }\n\n const text = head.children[0]\n if (!text || text.type !== 'text' || !text.value.startsWith('[!')) {\n return\n }\n\n const end = text.value.indexOf(']')\n if (end === -1) {\n return\n }\n\n // Extract the alert type\n const alertType = text.value.slice(2, end).toLowerCase() as (typeof ALERT_TYPES)[number]\n if (!ALERT_TYPES.includes(alertType)) {\n return\n }\n\n // Remove the blockquote if it's empty\n if (end + 1 === text.value.length) {\n const next = head.children[1]\n if (next) {\n if (next.type !== 'element' || next.tagName !== 'br') {\n return\n }\n if (!head.children[2]) {\n return\n }\n head.children = head.children.slice(2)\n const node = head.children[0]\n if (node && node.type === 'text' && node.value.charAt(0) === '\\n') {\n node.value = node.value.slice(1)\n }\n } else {\n const skipped = headIndex + 1 < node.children.length && isWhitespace(node.children[headIndex + 1])\n const nextIndex = skipped ? headIndex + 2 : headIndex + 1\n if (nextIndex >= node.children.length || node.children[nextIndex]?.type !== 'element') {\n return\n }\n\n node.children = node.children.slice(nextIndex)\n }\n } else if (\n text.value.charAt(end + 1) === '\\n' &&\n // Check if the next character is a newline or a non-whitespace character\n (end + 2 === text.value.length || !/^\\s*$/.test(text.value.slice(end + 2)))\n ) {\n text.value = text.value.slice(end + 2)\n } else {\n // Remove the alert marker if it's not followed by a newline or a non-whitespace character\n text.value = text.value.replace(/^\\s*\\[!.*?\\]\\s*/, '')\n }\n\n // Extract content from paragraphs to avoid wrapping in <p> tags\n const contentChildren = []\n for (let i = headIndex; i < node.children.length; i++) {\n const child = node.children[i]\n if (child?.type === 'element' && child.tagName === 'p' && child.children) {\n contentChildren.push(...child.children)\n } else {\n contentChildren.push(child)\n }\n }\n\n // Replace the blockquote with a div containing the alert\n parent.children[index] = {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert', `markdown-alert-${alertType}`] },\n children: [\n {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert-icon'] },\n children: [],\n },\n {\n type: 'element',\n tagName: 'div',\n properties: { className: ['markdown-alert-content'] },\n children: [{ type: 'text', value: ' ' }, ...contentChildren],\n },\n ],\n }\n })\n }\n}\n"], | ||
| "mappings": "AAEA,SAAS,aAAa;AAEtB,MAAM,cAAc,CAAC,QAAQ,OAAO,aAAa,WAAW,WAAW,SAAS;AAGhF,SAAS,aAAa,MAAoB;AACxC,SAAO,KAAK,SAAS,UAAU,OAAO,KAAK,UAAU,YAAY,QAAQ,KAAK,KAAK,KAAK;AAC1F;AAEO,SAAS,cAAc;AAC5B,SAAO,CAAC,SAAe;AACrB,UAAM,MAAM,WAAW,CAAC,MAAe,OAAO,WAAgB;AAC5D,UAAI,KAAK,YAAY,gBAAgB,OAAO,UAAU,YAAY,CAAC,UAAU,OAAO,SAAS,QAAQ;AACnG;AAAA,MACF;AAGA,YAAM,YAAY,KAAK,SAAS,UAAU,CAAC,UAAU,CAAC,aAAa,KAAK,CAAC;AACzE,UAAI,cAAc,IAAI;AACpB;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,SAAS,SAAS;AAEpC,UAAI,CAAC,QAAQ,KAAK,SAAS,aAAa,KAAK,YAAY,KAAK;AAC5D;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,SAAS,CAAC;AAC5B,UAAI,CAAC,QAAQ,KAAK,SAAS,UAAU,CAAC,KAAK,MAAM,WAAW,IAAI,GAAG;AACjE;AAAA,MACF;AAEA,YAAM,MAAM,KAAK,MAAM,QAAQ,GAAG;AAClC,UAAI,QAAQ,IAAI;AACd;AAAA,MACF;AAGA,YAAM,YAAY,KAAK,MAAM,MAAM,GAAG,GAAG,EAAE,YAAY;AACvD,UAAI,CAAC,YAAY,SAAS,SAAS,GAAG;AACpC;AAAA,MACF;AAGA,UAAI,MAAM,MAAM,KAAK,MAAM,QAAQ;AACjC,cAAM,OAAO,KAAK,SAAS,CAAC;AAC5B,YAAI,MAAM;AACR,cAAI,KAAK,SAAS,aAAa,KAAK,YAAY,MAAM;AACpD;AAAA,UACF;AACA,cAAI,CAAC,KAAK,SAAS,CAAC,GAAG;AACrB;AAAA,UACF;AACA,eAAK,WAAW,KAAK,SAAS,MAAM,CAAC;AACrC,gBAAMA,QAAO,KAAK,SAAS,CAAC;AAC5B,cAAIA,SAAQA,MAAK,SAAS,UAAUA,MAAK,MAAM,OAAO,CAAC,MAAM,MAAM;AACjE,YAAAA,MAAK,QAAQA,MAAK,MAAM,MAAM,CAAC;AAAA,UACjC;AAAA,QACF,OAAO;AACL,gBAAM,UAAU,YAAY,IAAI,KAAK,SAAS,UAAU,aAAa,KAAK,SAAS,YAAY,CAAC,CAAC;AACjG,gBAAM,YAAY,UAAU,YAAY,IAAI,YAAY;AACxD,cAAI,aAAa,KAAK,SAAS,UAAU,KAAK,SAAS,SAAS,GAAG,SAAS,WAAW;AACrF;AAAA,UACF;AAEA,eAAK,WAAW,KAAK,SAAS,MAAM,SAAS;AAAA,QAC/C;AAAA,MACF,WACE,KAAK,MAAM,OAAO,MAAM,CAAC,MAAM;AAAA,OAE9B,MAAM,MAAM,KAAK,MAAM,UAAU,CAAC,QAAQ,KAAK,KAAK,MAAM,MAAM,MAAM,CAAC,CAAC,IACzE;AACA,aAAK,QAAQ,KAAK,MAAM,MAAM,MAAM,CAAC;AAAA,MACvC,OAAO;AAEL,aAAK,QAAQ,KAAK,MAAM,QAAQ,mBAAmB,EAAE;AAAA,MACvD;AAGA,YAAM,kBAAkB,CAAC;AACzB,eAAS,IAAI,WAAW,IAAI,KAAK,SAAS,QAAQ,KAAK;AACrD,cAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,YAAI,OAAO,SAAS,aAAa,MAAM,YAAY,OAAO,MAAM,UAAU;AACxE,0BAAgB,KAAK,GAAG,MAAM,QAAQ;AAAA,QACxC,OAAO;AACL,0BAAgB,KAAK,KAAK;AAAA,QAC5B;AAAA,MACF;AAGA,aAAO,SAAS,KAAK,IAAI;AAAA,QACvB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY,EAAE,WAAW,CAAC,kBAAkB,kBAAkB,SAAS,EAAE,EAAE;AAAA,QAC3E,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,YAAY,EAAE,WAAW,CAAC,qBAAqB,EAAE;AAAA,YACjD,UAAU,CAAC;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,YAAY,EAAE,WAAW,CAAC,wBAAwB,EAAE;AAAA,YACpD,UAAU,CAAC,EAAE,MAAM,QAAQ,OAAO,IAAI,GAAG,GAAG,eAAe;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;", | ||
| "names": ["node"] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/rehype-highlight/index.ts"], | ||
| "sourcesContent": ["export { rehypeHighlight } from './rehype-highlight'\n"], | ||
| "mappings": "AAAA,SAAS,uBAAuB;", | ||
| "names": [] | ||
| } |
| { | ||
| "version": 3, | ||
| "sources": ["../../src/rehype-highlight/rehype-highlight.ts"], | ||
| "sourcesContent": ["import type { Element, ElementContent, Root } from 'hast'\nimport { toText } from 'hast-util-to-text'\nimport { type LanguageFn, createLowlight } from 'lowlight'\nimport { visit } from 'unist-util-visit'\nimport type { VFile } from 'vfile'\n\nimport { lowlightLanguageMappings } from '../constants'\n\ntype HighlightOptions = {\n /** Optional existing lowlight instance to use */\n lowlight?: ReturnType<typeof createLowlight> | undefined\n /** Register more aliases (optional); passed to `lowlight.registerAlias` */\n aliases?: Readonly<Record<string, ReadonlyArray<string> | string>> | null | undefined\n /** Register languages (default: `common`) passed to `lowlight.register` */\n languages?: Readonly<Record<string, LanguageFn>> | null | undefined\n /** List of language names to not highlight (optional). Note: you can also add `no-highlight` classes. */\n plainText?: ReadonlyArray<string> | null | undefined\n /** Class prefix (default: `'hljs-'`) */\n prefix?: string | null | undefined\n /** Names of languages to check when detecting (default: all registered languages) */\n subset?: ReadonlyArray<string> | null | undefined\n /** Option to autodetect languages */\n detect?: boolean\n}\n\nconst emptyOptions: HighlightOptions = {}\n\n/**\n * Lowlight syntax highlighting plugin for rehype pipelines\n *\n * Derived from: @url https://github.com/rehypejs/rehype-highlight/blob/main/lib/index.js\n */\nexport function rehypeHighlight(options?: Readonly<HighlightOptions> | null | undefined) {\n const settings = options || emptyOptions\n const aliases = settings.aliases\n const detect = options?.detect ?? false\n const languages = settings.languages\n const plainText = settings.plainText\n const prefix = settings.prefix\n const subset = settings.subset\n let name = 'hljs'\n\n // Create a lowlight instance if not provided\n const lowlight = options?.lowlight ?? createLowlight(languages)\n\n if (aliases) {\n lowlight.registerAlias(aliases)\n }\n\n if (prefix) {\n const pos = prefix.indexOf('-')\n name = pos > -1 ? prefix.slice(0, pos) : prefix\n }\n\n /** Transform.*/\n return (tree: Root, file: VFile) => {\n visit(tree, 'element', (node, _, parent) => {\n if (node.tagName !== 'code' || !parent || parent.type !== 'element' || parent.tagName !== 'pre') {\n return\n }\n\n const lang = language(node)\n\n if (lang === 'no-highlight' || (!lang && !detect) || (lang && plainText?.includes(lang))) {\n return\n }\n\n if (!Array.isArray(node.properties.className)) {\n node.properties.className = []\n }\n\n if (!node.properties.className.includes(name)) {\n node.properties.className.unshift(name)\n }\n\n let result: Root | undefined\n\n try {\n result = lang\n ? lowlight.highlight(lang, toText(parent), { prefix })\n : lowlight.highlightAuto(toText(parent), { prefix, subset })\n } catch (error) {\n const cause = error as Error\n\n if (lang && /Unknown language/.test(cause.message)) {\n file.message(`Cannot highlight as \\`${lang}\\`, it's not registered`, {\n ancestors: [parent, node],\n cause,\n place: node.position,\n ruleId: 'missing-language',\n source: 'rehype-highlight',\n })\n\n /* c8 ignore next 5 -- throw arbitrary hljs errors */\n return\n }\n\n throw cause\n }\n\n if (!lang && result.data?.language) {\n node.properties.className.push('language-' + result.data.language)\n }\n\n if (result.children.length > 0) {\n node.children = result.children as Array<ElementContent>\n }\n })\n }\n}\n\n/** Get the programming language of `node` or an empty string */\nfunction language(node: Element) {\n const list = node.properties.className\n\n if (!Array.isArray(list)) {\n return ''\n }\n\n const name: string = list.reduce<string>((result, _item) => {\n if (result) {\n return result\n }\n const item = String(_item)\n\n if (item === 'no-highlight' || item === 'nohighlight') {\n return 'no-highlight'\n }\n if (item.slice(0, 5) === 'lang-') {\n return item.slice(5)\n }\n if (item.slice(0, 9) === 'language-') {\n return item.slice(9)\n }\n\n return result\n }, '')\n\n return lowlightLanguageMappings[name || ''] || name\n}\n"], | ||
| "mappings": "AACA,SAAS,cAAc;AACvB,SAA0B,sBAAsB;AAChD,SAAS,aAAa;AAGtB,SAAS,gCAAgC;AAmBzC,MAAM,eAAiC,CAAC;AAOjC,SAAS,gBAAgB,SAAyD;AACvF,QAAM,WAAW,WAAW;AAC5B,QAAM,UAAU,SAAS;AACzB,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,YAAY,SAAS;AAC3B,QAAM,YAAY,SAAS;AAC3B,QAAM,SAAS,SAAS;AACxB,QAAM,SAAS,SAAS;AACxB,MAAI,OAAO;AAGX,QAAM,WAAW,SAAS,YAAY,eAAe,SAAS;AAE9D,MAAI,SAAS;AACX,aAAS,cAAc,OAAO;AAAA,EAChC;AAEA,MAAI,QAAQ;AACV,UAAM,MAAM,OAAO,QAAQ,GAAG;AAC9B,WAAO,MAAM,KAAK,OAAO,MAAM,GAAG,GAAG,IAAI;AAAA,EAC3C;AAGA,SAAO,CAAC,MAAY,SAAgB;AAClC,UAAM,MAAM,WAAW,CAAC,MAAM,GAAG,WAAW;AAC1C,UAAI,KAAK,YAAY,UAAU,CAAC,UAAU,OAAO,SAAS,aAAa,OAAO,YAAY,OAAO;AAC/F;AAAA,MACF;AAEA,YAAM,OAAO,SAAS,IAAI;AAE1B,UAAI,SAAS,kBAAmB,CAAC,QAAQ,CAAC,UAAY,QAAQ,WAAW,SAAS,IAAI,GAAI;AACxF;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,QAAQ,KAAK,WAAW,SAAS,GAAG;AAC7C,aAAK,WAAW,YAAY,CAAC;AAAA,MAC/B;AAEA,UAAI,CAAC,KAAK,WAAW,UAAU,SAAS,IAAI,GAAG;AAC7C,aAAK,WAAW,UAAU,QAAQ,IAAI;AAAA,MACxC;AAEA,UAAI;AAEJ,UAAI;AACF,iBAAS,OACL,SAAS,UAAU,MAAM,OAAO,MAAM,GAAG,EAAE,OAAO,CAAC,IACnD,SAAS,cAAc,OAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,CAAC;AAAA,MAC/D,SAAS,OAAO;AACd,cAAM,QAAQ;AAEd,YAAI,QAAQ,mBAAmB,KAAK,MAAM,OAAO,GAAG;AAClD,eAAK,QAAQ,yBAAyB,IAAI,2BAA2B;AAAA,YACnE,WAAW,CAAC,QAAQ,IAAI;AAAA,YACxB;AAAA,YACA,OAAO,KAAK;AAAA,YACZ,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV,CAAC;AAGD;AAAA,QACF;AAEA,cAAM;AAAA,MACR;AAEA,UAAI,CAAC,QAAQ,OAAO,MAAM,UAAU;AAClC,aAAK,WAAW,UAAU,KAAK,cAAc,OAAO,KAAK,QAAQ;AAAA,MACnE;AAEA,UAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,aAAK,WAAW,OAAO;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAGA,SAAS,SAAS,MAAe;AAC/B,QAAM,OAAO,KAAK,WAAW;AAE7B,MAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,OAAe,KAAK,OAAe,CAAC,QAAQ,UAAU;AAC1D,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AACA,UAAM,OAAO,OAAO,KAAK;AAEzB,QAAI,SAAS,kBAAkB,SAAS,eAAe;AACrD,aAAO;AAAA,IACT;AACA,QAAI,KAAK,MAAM,GAAG,CAAC,MAAM,SAAS;AAChC,aAAO,KAAK,MAAM,CAAC;AAAA,IACrB;AACA,QAAI,KAAK,MAAM,GAAG,CAAC,MAAM,aAAa;AACpC,aAAO,KAAK,MAAM,CAAC;AAAA,IACrB;AAEA,WAAO;AAAA,EACT,GAAG,EAAE;AAEL,SAAO,yBAAyB,QAAQ,EAAE,KAAK;AACjD;", | ||
| "names": [] | ||
| } |
5
-16.67%1024
9.4%50205
-40.6%49
-23.44%