🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@scalar/code-highlight

Package Overview
Dependencies
Maintainers
8
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@scalar/code-highlight - npm Package Compare versions

Comparing version
0.3.0
to
0.3.1
+6
-0
CHANGELOG.md
# @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

@@ -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
//

@@ -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';

@@ -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 };

@@ -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';

@@ -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

@@ -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": "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",
"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": []
}