import { parse } from 'node-html-parser'; import { v4 as uuid } from 'uuid'; const templates = { old(pre, code, { open, buttons, edit }) { const id = `code-example-${uuid().slice(-12)}`; let preview = code.textContent; // Run preview scripts as modules to prevent collisions const root = parse(preview, { blockTextElements: { script: true } }); root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module')); preview = root.toString(); return `
` blocks into live examples.
*/
export function codeExamplesPlugin(eleventyConfig, options = {}) {
const defaultOptions = {
container: 'body',
defaultOpen: (code, { outputPathIndex }) => {
return (
outputPathIndex === 1 && // is first
code.textContent.length < 500
); // is short
}
};
options = { ...defaultOptions, ...options };
const stats = {
inputPaths: {},
outputPaths: {}
};
eleventyConfig.addTransform('code-examples', function (content) {
const { inputPath, outputPath } = this.page;
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
if (!container) {
return content;
}
// Look for external links
container.querySelectorAll('code.example').forEach(code => {
stats.inputPaths[inputPath] ??= 0;
stats.outputPaths[outputPath] ??= 0;
stats.inputPaths[inputPath]++;
stats.outputPaths[outputPath]++;
const pre = code.closest('pre');
const first = stats.inputPaths[inputPath] === 1;
const localOptions = {
...options,
first,
// Modifier defaults
edit: true,
buttons: true,
new: true, // comment this line to default back to the old demos
attributes: {}
};
for (const prop of ['new', 'open', 'buttons', 'edit']) {
if (code.classList.contains(prop)) {
localOptions[prop] = true;
} else if (code.classList.contains(`no-${prop}`)) {
localOptions[prop] = false;
}
}
for (const attribute of ['viewport', 'include']) {
if (code.hasAttribute(attribute)) {
localOptions.attributes[attribute] = code.getAttribute(attribute);
code.removeAttribute(attribute);
}
}
if (Object.keys(localOptions.attributes).length > 0) {
// attributes only work on the new syntax
localOptions.new = true;
}
if (localOptions.open === undefined) {
if (localOptions.defaultOpen === true) {
localOptions.open = localOptions.defaultOpen;
} else if (typeof localOptions.defaultOpen === 'function') {
localOptions.open = localOptions.defaultOpen(code, {
pre,
inputPathIndex: stats.inputPaths[inputPath],
outputPathIndex: stats.outputPaths[outputPath]
});
}
}
const template = localOptions.new ? 'new' : 'old';
const codeExample = parse(templates[template](pre, code, localOptions));
pre.replaceWith(codeExample);
});
return doc.toString();
});
}