@mintlify/common
Version:
Commonly shared code within Mintlify
130 lines (129 loc) • 5.27 kB
JavaScript
import { visit } from 'unist-util-visit';
import { buildMetaAttributes } from './buildMetaAttributes.js';
import { parseMetaString } from './parseMetaString.js';
export const FOCUS_CLASS_NAME = 'line-focus';
export const HIGHLIGHT_CLASS_NAME = 'line-highlight';
const exampleNames = ['RequestExample', 'ResponseExample'];
const flowElementType = 'mdxJsxFlowElement';
function isElement(node, key = 'type', element = 'element') {
return node != undefined && node[key] === element;
}
function addCodeBlocks(tree) {
visit(tree, (node, i, parent) => {
var _a, _b, _c;
if (parent == null || i == null || !isElement(node) || !isElement(node, 'tagName', 'pre')) {
return;
}
const code = node.children[0];
if (!isElement(code, 'tagName', 'code'))
return;
const isFlowElement = isElement(parent, 'type', flowElementType);
const parentName = isFlowElement ? parent.name : undefined;
let properties = Object.entries(code.properties);
let meta;
if (parentName && exampleNames.includes(parentName)) {
const parentType = parentName.slice(0, -7);
meta = parseMetaString(parentType);
if (!((_a = code.data) === null || _a === void 0 ? void 0 : _a.meta)) {
const data = (_b = code.data) !== null && _b !== void 0 ? _b : {};
data.meta = i === 0 ? parentType : `${parentType} ${i + 1}`;
code.data = data;
}
}
if (((_c = code.data) === null || _c === void 0 ? void 0 : _c.meta) && typeof code.data.meta === 'string') {
meta = parseMetaString(code.data.meta);
}
const attributes = buildMetaAttributes(meta !== null && meta !== void 0 ? meta : {});
const lineNumber = addLineHighlightAndFocus(code, meta === null || meta === void 0 ? void 0 : meta.highlight, meta === null || meta === void 0 ? void 0 : meta.focus);
if (lineNumber) {
properties = properties.filter(([key]) => key !== 'numberOfLines');
attributes.push({
type: 'mdxJsxAttribute',
name: 'numberOfLines',
value: lineNumber.toString(),
});
}
const wrapElement = {
type: flowElementType,
name: 'CodeBlock',
attributes: [
...attributes,
...properties
.filter(([_key, val]) => val != null)
.map(([key, val]) => ({
type: 'mdxJsxAttribute',
name: key,
value: Array.isArray(val) ? val.join(' ') : val === null || val === void 0 ? void 0 : val.toString(),
})),
],
data: { _mdxExplicitJsx: true },
children: [],
};
wrapElement.children = [node];
parent.children[i] = wrapElement;
});
tree.children = [...tree.children];
}
function addLineHighlightAndFocus(codeElement, highlightLines, focusLines) {
let lineNumber = 0;
// remove last empty line span if it exists
const lastChild = codeElement.children[codeElement.children.length - 1];
if (lastChild &&
lastChild.type === 'element' &&
lastChild.tagName === 'span' &&
lastChild.properties.class &&
lastChild.children.length === 0) {
const classValue = lastChild.properties.class;
const hasLineClass = typeof classValue === 'string'
? classValue.includes('line')
: Array.isArray(classValue) && classValue.includes('line');
if (hasLineClass) {
codeElement.children.pop();
}
}
visit(codeElement, 'element', (span, _, spanParent) => {
if (!spanParent ||
spanParent.type !== 'element' ||
spanParent.tagName !== 'code' ||
span.tagName !== 'span' ||
!span.properties.class) {
return;
}
const classValue = span.properties.class;
const hasLineClass = typeof classValue === 'string'
? classValue.includes('line')
: Array.isArray(classValue) && classValue.includes('line');
if (!hasLineClass) {
return;
}
lineNumber++;
if (highlightLines && highlightLines.length && highlightLines.includes(lineNumber)) {
addClassToSpan(span, HIGHLIGHT_CLASS_NAME);
}
if (focusLines && focusLines.length && focusLines.includes(lineNumber)) {
addClassToSpan(span, FOCUS_CLASS_NAME);
}
});
if (lineNumber > 0) {
codeElement.properties.numberOfLines = lineNumber;
}
return lineNumber;
}
function addClassToSpan(span, className) {
const currentClass = span.properties.class;
if (typeof currentClass === 'string') {
span.properties.class = currentClass + ' ' + className;
}
else if (Array.isArray(currentClass)) {
span.properties.class = [...currentClass, className];
}
else {
span.properties.class = className;
}
}
export function rehypeCodeBlocks() {
return addCodeBlocks;
}
export * from './parseMetaString.js';
export * from './metaOptions.js';
export * from './buildMetaAttributes.js';