2024-04-17 11:20:27 -04:00
|
|
|
import { parse } from 'node-html-parser';
|
|
|
|
|
import { v4 as uuid } from 'uuid';
|
|
|
|
|
|
2024-12-09 13:44:14 -05:00
|
|
|
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 `
|
|
|
|
|
<div class="code-example ${open ? 'open' : ''}">
|
|
|
|
|
<div class="code-example-preview">
|
|
|
|
|
${preview}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="code-example-source" id="${id}">
|
|
|
|
|
${pre.outerHTML}
|
|
|
|
|
</div>
|
|
|
|
|
${
|
|
|
|
|
buttons
|
|
|
|
|
? `
|
|
|
|
|
<div class="code-example-buttons">
|
|
|
|
|
<button
|
|
|
|
|
class="code-example-toggle"
|
|
|
|
|
type="button"
|
|
|
|
|
aria-expanded="${open ? 'true' : 'false'}"
|
|
|
|
|
aria-controls="${id}"
|
|
|
|
|
>
|
|
|
|
|
Code
|
|
|
|
|
<wa-icon name="chevron-down"></wa-icon>
|
|
|
|
|
</button>
|
|
|
|
|
${
|
|
|
|
|
edit
|
|
|
|
|
? `
|
|
|
|
|
<button class="code-example-pen" type="button">
|
|
|
|
|
<wa-icon name="pen-to-square"></wa-icon>
|
|
|
|
|
Edit
|
|
|
|
|
</button>
|
|
|
|
|
`
|
|
|
|
|
: ''
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
: ''
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
},
|
2024-12-09 21:41:54 -05:00
|
|
|
new(pre, code, { open, first, attributes }) {
|
|
|
|
|
attributes = {
|
2024-12-09 21:01:49 -05:00
|
|
|
open,
|
2024-12-09 21:41:54 -05:00
|
|
|
include: `link[rel=stylesheet][href^='/dist/']`,
|
|
|
|
|
...attributes
|
2024-12-09 13:44:14 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const attributesString = Object.entries(attributes)
|
|
|
|
|
.map(([key, value]) => {
|
|
|
|
|
if (value === true) {
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
if (value === false || value === null) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
return `${key}="${value}"`;
|
|
|
|
|
})
|
|
|
|
|
.join(' ');
|
|
|
|
|
|
|
|
|
|
let includes = '';
|
|
|
|
|
if (first) {
|
|
|
|
|
includes = `
|
|
|
|
|
<template class="wa-code-demo-include-isolated">
|
2024-12-09 19:40:28 -05:00
|
|
|
<script src="/dist/webawesome.loader.js" type="module"></script>
|
2024-12-09 13:44:14 -05:00
|
|
|
</template>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let preview = '';
|
2024-12-09 20:43:58 -05:00
|
|
|
if (attributes.viewport === undefined) {
|
2024-12-10 15:23:16 -05:00
|
|
|
// Slot in pre-rendered preview
|
2024-12-09 20:43:58 -05:00
|
|
|
preview = `<div style="display:contents" slot="preview">${code.textContent}</div>`;
|
2024-12-10 15:23:16 -05:00
|
|
|
|
|
|
|
|
// 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();
|
2024-12-09 20:43:58 -05:00
|
|
|
}
|
2024-12-09 13:44:14 -05:00
|
|
|
|
|
|
|
|
return `${includes}
|
|
|
|
|
<wa-code-demo ${attributesString}>
|
|
|
|
|
${preview}
|
|
|
|
|
${pre.outerHTML}
|
|
|
|
|
</wa-code-demo>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-17 11:20:27 -04:00
|
|
|
/**
|
|
|
|
|
* Eleventy plugin to turn `<code class="example">` blocks into live examples.
|
|
|
|
|
*/
|
2024-12-09 13:44:14 -05:00
|
|
|
export function codeExamplesPlugin(eleventyConfig, options = {}) {
|
2024-12-09 21:41:54 -05:00
|
|
|
const defaultOptions = {
|
2024-04-17 11:20:27 -04:00
|
|
|
container: 'body',
|
2024-12-09 21:41:54 -05:00
|
|
|
defaultOpen: (code, { outputPathIndex }) => {
|
|
|
|
|
return (
|
|
|
|
|
outputPathIndex === 1 && // is first
|
|
|
|
|
code.textContent.length < 500
|
|
|
|
|
); // is short
|
|
|
|
|
}
|
2024-04-17 11:20:27 -04:00
|
|
|
};
|
2024-12-09 21:41:54 -05:00
|
|
|
options = { ...defaultOptions, ...options };
|
2024-04-17 11:20:27 -04:00
|
|
|
|
2024-12-09 13:44:14 -05:00
|
|
|
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);
|
2024-04-17 11:20:27 -04:00
|
|
|
|
2024-12-09 13:44:14 -05:00
|
|
|
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
|
2024-12-09 21:41:54 -05:00
|
|
|
attributes: {}
|
2024-12-09 13:44:14 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2024-04-17 11:20:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-09 21:41:54 -05:00
|
|
|
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
|
2024-12-09 13:44:14 -05:00
|
|
|
localOptions.new = true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 21:41:54 -05:00
|
|
|
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]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 13:44:14 -05:00
|
|
|
const template = localOptions.new ? 'new' : 'old';
|
|
|
|
|
const codeExample = parse(templates[template](pre, code, localOptions));
|
|
|
|
|
|
|
|
|
|
pre.replaceWith(codeExample);
|
2024-04-17 11:20:27 -04:00
|
|
|
});
|
2024-12-09 13:44:14 -05:00
|
|
|
|
|
|
|
|
return doc.toString();
|
|
|
|
|
});
|
2024-04-17 11:20:27 -04:00
|
|
|
}
|