@podlite/schema
Version:
AST tools for Podlite markup language
279 lines (278 loc) • 9.96 kB
JavaScript
import toAny from './exportAny';
import { subUse, wrapContent, emptyContent, content, setFn, handleNested } from './helpers/handlers';
import { isNamedBlock } from './helpers/makeTransformer';
import makeAttrs from './helpers/config';
import htmlWriter from './writerHtml';
import clean_plugin from './plugin-clean-location';
import { getNodeId } from './ast-helpers';
const rules = {
':text': (writer, processor) => (node, ctx, interator) => {
// handle text with content
if (node.value) {
writer.write(node.value);
}
else {
interator(node.content, ctx);
}
},
// Formatting codes
'A<>': (writer, processor) => (node, ctx, interator) => {
//get replacement text
if (!(ctx.alias && ctx.alias.hasOwnProperty(node.content))) {
writer.write(`A<${node.content}>`);
}
else {
const src = ctx.alias[node.content].join('\n');
const tree_1 = processor(src);
// now clean locations
const tree = clean_plugin()(tree_1);
if (tree[0].type === 'para') {
interator(tree[0].content, ctx);
}
else {
interator(tree, ctx);
}
}
},
'C<>': wrapContent('<code>', '</code>'),
'D<>': (writer, processor) => (node, ctx, interator) => {
// @ts-ignore
let synonyms = { node };
let definition = [node.content[0]];
if (synonyms) {
definition = synonyms;
}
if (!writer.hasOwnProperty('DEFINITIONS')) {
writer.DEFINITIONS = [];
}
writer.DEFINITIONS.push({ definition });
writer.writeRaw('<dfn>');
interator(node.content, ctx);
writer.writeRaw('</dfn>');
},
'B<>': wrapContent('<strong>', '</strong>'),
'I<>': wrapContent('<em>', '</em>'),
'R<>': wrapContent('<var>', '</var>'),
'K<>': wrapContent('<kbd>', '</kbd>'),
'L<>': setFn((node, ctx) => {
let { meta } = node;
if (meta === null) {
meta = node.content;
}
return wrapContent(`<a href="${meta}">`, `</a>`);
}),
/**
* CSS rules for footnotes
.footnote a {
text-decoration: none;
}
.footnotes {
border-top-style: solid;
border-top-width: 1px;
border-top-color: #eee;
}
*/
'N<>': (writer, processor) => {
writer.addListener('end', () => {
if (!writer.hasOwnProperty('FOOTNOTES')) {
return;
}
const footnotes = writer.FOOTNOTES;
if (footnotes.length < 1) {
return;
} // if empty footnotes
writer.writeRaw(`<div class="footnotes">`);
footnotes.map(footnote => {
writer.writeRaw(`<p><sup id="${footnote.fnId}" class="footnote"><a href="#${footnote.fnRefId}">[${footnote.gid}]</a></sup> `);
footnote.make();
writer.writeRaw(`</p>`);
});
writer.writeRaw(`</div>`);
});
return (node, ctx, interator) => {
// skip empty notes
if (node.content.length < 1) {
return;
}
if (!writer.hasOwnProperty('gid')) {
writer.gid = 1;
}
// get foot note id
const gid = writer.gid++;
const fnRefId = `fnref:${gid}`;
const fnId = `fn:${gid}`;
writer.writeRaw(`<sup id="${fnRefId}" class="footnote"><a href="#${fnId}">[${gid}]</a></sup>`);
if (!writer.hasOwnProperty('FOOTNOTES')) {
writer.FOOTNOTES = [];
}
writer.FOOTNOTES.push({
gid,
fnRefId,
fnId,
make: () => {
interator(node.content, ctx);
},
});
};
},
'S<>': (writer, processor) => (node, ctx, interator) => {
const content = node.content || '';
const spaces = content.replace(/ /g, ' ');
const newFeed = spaces.replace(/\n/g, '</br>');
writer.writeRaw(newFeed);
},
'T<>': wrapContent('<samp>', '</samp>'),
'U<>': wrapContent('<u>', '</u>'),
'V<>': content,
'X<>': (writer, processor) => (node, ctx, interator) => {
interator(node.content, ctx);
let { entry } = node;
if (entry === null && node.content.length > 0) {
//@ts-ignore
entry = [node.content[0]];
}
else {
return;
}
if (!writer.hasOwnProperty('INDEXTERMS')) {
writer.INDEXTERMS = [];
}
writer.INDEXTERMS.push({
entry,
});
},
'Z<>': emptyContent,
pod: content,
':code': wrapContent('<pre><code>', '</code></pre>'),
code: handleNested(wrapContent('<pre><code>', '</code></pre>')),
data: emptyContent,
':verbatim': (writer, processor) => (node, ctx, interator) => {
if (node.error) {
writer.emit('errors', node.location);
}
interator(node.value);
},
':blankline': emptyContent,
':ambient': emptyContent,
// Directives
':config': setFn((node, ctx) => {
// setup context
if (!ctx.hasOwnProperty('config'))
ctx.config = {};
//collect configs in context
ctx.config[node.name] = node.config;
return emptyContent;
}),
':alias': setFn((node, ctx) => {
// set alias
if (!ctx.hasOwnProperty('alias'))
ctx.alias = {};
//collect configs in context
ctx.alias[node.name] = node.replacement;
return emptyContent;
}),
// block =para
para: handleNested(content),
':para': wrapContent('<p>', '</p>'),
'head:block': subUse({
// inside head don't wrap into <p>
':para': content,
}, setFn((node, ctx) => {
const { level } = node;
const id = getNodeId(node, ctx);
return wrapContent(`<h${level}${id ? ` id="${id}"` : ''}>`, `</h${level}>`);
})),
':list': setFn((node, ctx) => node.list === 'ordered'
? wrapContent('<ol>', '</ol>')
: node.list === 'variable'
? wrapContent('<dl>', '</dl>')
: wrapContent('<ul>', '</ul>')),
'item:block': (writer, processor) => (node, ctx, interator) => {
// make text from first para
if (!(node.content instanceof Array)) {
console.error('[pod6] item:block : Error in content of ' + JSON.stringify(node));
}
const [firstPara, ...other] = node.content;
writer.writeRaw('<li>');
// TODO: get cases for handle first para in items
// interator([...firstPara.content, ...other], ctx)
interator(node.content, ctx);
writer.writeRaw('</li>');
},
'comment:block': emptyContent,
defn: wrapContent('', '</dd>'),
'term:para': wrapContent('<dt>', '</dt><dd>'),
nested: handleNested(content, 1),
output: handleNested(wrapContent('<pre><samp>', '</samp></pre>'), 1),
input: handleNested(wrapContent('<pre><kbd>', '</kbd></pre>'), 1),
// table section
'table:block': handleNested((writer, processor) => (node, ctx, interator) => {
const conf = makeAttrs(node, ctx);
writer.writeRaw('<table>');
if (conf.exists('caption')) {
writer.writeRaw('<caption>');
writer.write(conf.getFirstValue('caption'));
writer.writeRaw('</caption>');
}
interator(node.content, ctx);
writer.writeRaw('</table>');
}),
':separator': emptyContent,
table_row: wrapContent('<tr>', '</tr>'),
table_cell: wrapContent('<td>', '</td>'),
table_head: subUse({
table_cell: wrapContent('<th>', '</th>'),
}, wrapContent('<tr>', '</tr>')),
// Toc
':toc': (writer, processor) => (node, ctx, interator) => {
writer.writeRaw('<div className="toc">');
// get toc title
const conf = makeAttrs(node, ctx);
if (conf.exists('title')) {
const title = conf.getFirstValue('title');
writer.writeRaw('<div className="toctitle">');
writer.write(title);
writer.writeRaw('</div>');
}
interator(node.content, ctx);
writer.writeRaw('</div>');
},
':toc-list': setFn((node, ctx) => wrapContent(`<ul class="toc-list listlevel${node.level}">`, '</ul>')),
':toc-item': setFn((node, ctx) => wrapContent('<li class="toc-item">', '</li>')),
':image': (writer, processor) => (node, ctx, interator) => {
writer.writeRaw(`<img src="${node.src}" alt="${node.alt}"/>`);
},
};
const toHtml = opt => toAny({ writer: htmlWriter, ...opt })
.use('*', (writer, processor) => {
return (node, ctx, interator) => {
// skip warnings for semantic blocks
const isSemanticBlock = node => {
const name = node.name || '';
const isTypeBlock = (node.type || '') === 'block';
return isTypeBlock && name === name.toUpperCase();
};
//Named blocks for which no explicit class has been defined or loaded are
//usually not rendered by the standard renderers.
if (isNamedBlock(node.name)) {
return true;
}
if (isSemanticBlock(node)) {
const name = node.name;
writer.writeRaw('<h1 class="');
writer.write(name);
writer.writeRaw('">');
writer.write(name);
writer.writeRaw('</h1>');
}
else {
console.warn('[podlite] Unhandled node' + JSON.stringify(node, null, 2));
}
if (node.hasOwnProperty('content')) {
interator(node.content, ctx);
}
};
})
.use(rules);
export default toHtml;
//# sourceMappingURL=exportHtml.js.map