UNPKG

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