UNPKG

aubade

Version:

markdown, orchestrated.

80 lines (79 loc) 3.2 kB
import { util } from './context.js'; import { annotate, compose } from './engine.js'; import { base, standard } from './resolver.js'; import { escape } from './utils.js'; export const engrave = forge({ transform: { 'inline:text': ({ type, text }) => ({ type, text: typography(text) }) }, }); export function forge({ directive = {}, renderer = {}, transform = {} } = {}) { const resolver = { ...standard, ...renderer, 'aubade:directive'({ token: { meta }, render, sanitize }) { const transform = { ...base, ...directive }[meta.type]; if (!transform) throw new Error(`Unknown directive type: ${meta.type}`); return transform({ data: meta.data, annotate: (source) => annotate(source).map(render).join(''), print: (...lines) => lines.flatMap((l) => (!l ? [] : l)).join('\n'), sanitize, }); }, }; function walk(token, visitors = {}) { if ('children' in token) { const visited = token.children.map((child) => walk(child, visitors)); token.children = visited; } const visitor = visitors[token.type]; return visitor ? visitor(token) : token; } return (input) => { let { children: stream } = compose(input.replace(/\r\n?/g, '\n')); if (Object.keys(transform).length) stream = stream.map((t) => walk(t, transform)); return { get tokens() { return stream; }, set tokens(v) { stream = v; }, html(overrides = {}) { delete overrides['aubade:directive']; // prevent override of directives function html(token) { const resolve = { ...resolver, ...overrides }[token.type]; if (!resolve) throw new Error(`Unknown token type: ${token.type}`); const visited = transform[token.type] ? walk(token, transform) : token; return resolve({ token: visited, render: html, sanitize: escape }); } return stream.map(html).join('\n'); }, visit(visitors) { return stream.map((token) => walk(token, visitors)); }, }; }; } export function typography(text) { let output = ''; for (let i = 0; i < text.length; i++) { const char = text[i]; if (char !== "'" && char !== '"') { output += char; continue; } const prev = text[i - 1]; const next = text[i + 1]; const prime = /\d/.test(prev); const left = util.is['left-flanking'](prev || ' ', next || ' '); const right = util.is['right-flanking'](prev || ' ', next || ' '); const double = right ? (prime ? '″' : '”') : left ? '“' : char; const single = right ? (prime ? '′' : '’') : left ? '‘' : char; output += char === '"' ? double : single; } output = output.replace(/---/g, '—').replace(/--/g, '–'); return output.replace(/\.{3,}/g, '…'); }