@theme-ui/prism
Version:
A syntax highlighting component based on [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer) that works seamlessly with Theme UI.
138 lines (129 loc) • 4.28 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var Highlight = require('prism-react-renderer');
var mdx = require('@theme-ui/mdx');
var core = require('@theme-ui/core');
var jsxRuntime = require('@theme-ui/core/jsx-runtime');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
var Highlight__default = /*#__PURE__*/_interopDefault(Highlight);
const aliases = {
js: 'javascript',
sh: 'bash'
};
const isInRange = (start, end, num) => {
if (num >= start && num <= end) {
return true;
}
return false;
};
const checkRanges = (range, num) => {
for (let i = 0; i < range.length; i += 2) {
if (isInRange(range[i], range[i + 1], num)) {
return true;
}
}
return false;
};
// prism-react-renderer doesn't export `Token` type
function ThemeUIPrism({
children,
className: outerClassName = '',
...props
}) {
const [language] = outerClassName.replace(/language-/, '').split(' ');
const lang = aliases[language] || language;
let startEndRangesToHighlight = [];
let countHighlightCommentsRemoved = 0;
const findStartAndEndHighlights = tokens => {
const tokensWithoutHighlightComments = tokens.filter((item, index) => {
const removeLine = item.map(({
content
}) => {
if (content.trim() === '// highlight-start') {
/**
* Track highlighted lines, including countHighlightCommentsRemoved
* so we can keep track of multiple highlight-start and highlight-end blocks.
* */
startEndRangesToHighlight.push(index - countHighlightCommentsRemoved);
countHighlightCommentsRemoved += 1;
return true;
}
if (content.trim() === '// highlight-end') {
/**
* Subtract by (countHighlightCommentsRemoved + 1) to account for
* the current highlight-end block being removed.
* */
startEndRangesToHighlight.push(index - (countHighlightCommentsRemoved + 1));
countHighlightCommentsRemoved += 1;
return true;
}
}).filter(Boolean)[0];
if (!removeLine) {
return item;
}
});
return tokensWithoutHighlightComments;
};
const isStartEndHighlighted = index => {
return checkRanges(startEndRangesToHighlight, index);
};
const isInlineHighlighted = line => {
const regex = new RegExp('// highlight-line$');
for (let token of line) {
if (regex.test(token.content)) {
token.content = token.content.replace(regex, ''); // remove the highlight-line comment now that we've acted on it
return true;
}
}
return false;
};
const shouldHighlightLine = (line, index) => {
return isStartEndHighlighted(index) || isInlineHighlighted(line);
};
const code = typeof children === 'string' ? children.trim() : typeof children === 'object' && 'props' in children && typeof children.props.children === 'string' ? children.props.children.trim() : '';
return jsxRuntime.jsx(Highlight__default["default"], {
...Highlight.defaultProps,
...props,
code: code,
language: lang,
theme: undefined,
children: ({
className,
style,
tokens,
getLineProps,
getTokenProps
}) => {
const tokensWithoutHighlightComments = findStartAndEndHighlights(tokens);
return jsxRuntime.jsx(mdx.Themed.pre, {
className: `${outerClassName} ${className}`,
style: style,
children: tokensWithoutHighlightComments.map((line, i) => {
const lineProps = getLineProps({
line,
key: i
});
if (shouldHighlightLine(line, i)) {
lineProps.className = `${lineProps.className} highlight`;
}
return jsxRuntime.jsx("div", {
...lineProps,
children: line.map((token, key) => {
return core.createElement("span", {
...getTokenProps({
token,
key
}),
key: key,
sx: token.empty ? {
display: 'inline-block'
} : undefined
});
})
});
})
});
}
});
}
exports["default"] = ThemeUIPrism;