aubade
Version:
filesystem-based content processor
47 lines (45 loc) • 1.62 kB
JavaScript
import { createHighlighter, bundledLanguages } from 'shiki';
import { escape } from '../utils.js';
export const highlighter = await createHighlighter({
themes: ['github-dark'],
langs: Object.keys(bundledLanguages),
});
export function transform(source, dataset) {
const { codeToTokensBase } = highlighter;
const { file, ...rest } = dataset;
let highlighted = '';
let line = +(rest['line-start'] || 1);
for (const tokens of codeToTokensBase(source, {
lang: rest.language,
})) {
let code = `<code data-line="${line++}">`;
for (const { content, color } of tokens) {
const style = color ? `style="color: ${color}"` : '';
code += `<span ${style}>${escape(content)}</span>`;
}
highlighted += `${code}</code>\n`;
}
const attrs = Object.entries(rest).map(([k, v]) => `data-${k.toLowerCase().replace(/[^a-z\-]/g, '')}="${escape(v || '')}"`);
// needs to be /^<pre/ to prevent added wrapper from markdown-it
return `<pre data-aubade="block">
<header
data-aubade="header"
${attrs.join('\n\t\t')}
class="aubade ${file ? '' : 'empty'}"
>${file ? `<span>${file}</span>` : ''}
<div data-aubade="toolbar">
${icon('list', 'Toggle\nNumbering')}
${icon('clipboard', 'Copy')}
</div>
</header>
<div
data-aubade="pre"
${attrs.join('\n\t\t')}
class="aubade language-${rest.language || 'none'}"
>${highlighted.trim()}</div>
</pre>`;
}
function icon(name, tooltip) {
const span = `<span data-aubade="tooltip">${tooltip}</span>`;
return `<button data-aubade-toolbar="${name}">${span}</button>`;
}