UNPKG

markdown-it-livecodes

Version:

A markdown-it plugin for to convert code blocks to LiveCodes playgrounds.

154 lines (151 loc) 5.01 kB
import { getPlaygroundUrl } from "livecodes"; //#region ../shared/index.ts function parseMeta(metaString) { const meta = {}; metaString.split(" ").forEach((str) => { const equalIndex = str.indexOf("="); if (equalIndex > 0) { const key = str.slice(0, equalIndex); const value = str.slice(equalIndex + 1); meta[key] = value; } else meta[str] = ""; }); return meta; } function processOptions({ options, meta, language, content }) { const render = meta.render ?? options.render ?? "playground"; const height = meta.height ?? options.height; const className = [...new Set([ options.className, meta.className, meta.class ])].filter((x) => x != null).map((x) => x.replace(/['"{}<>\n]/g, "")).join(" ").trim(); const config = options.config; const configIsObj = config && typeof config === "object"; const configMode = configIsObj ? config.mode : void 0; const mode = meta.mode ?? options.params?.mode ?? configMode ?? (render === "playground" ? "simple" : void 0); const lang = language.includes("{") ? language.split("{")[0] : language; const params = { ...options.params, ...meta, ...mode ? { mode } : {}, [lang]: content }; const extraKeys = [ "livecodes", "render", "height", "className", "class", "lang" ]; extraKeys.forEach((key) => { delete params[key]; }); const embedOptions = { appUrl: options.appUrl, config, params, import: meta.import ?? options.import, loading: meta.loading ?? options.loading, template: meta.template ?? options.template }; const optionKeys = Object.keys(embedOptions); optionKeys.forEach((key) => { if (embedOptions[key] == null) delete embedOptions[key]; }); return { embedOptions, render, height, className }; } const h = (tag, props = {}, children = []) => { if (Array.isArray(props) || typeof props === "string") { children = props; props = {}; } const tagName = tag === "link" ? "a" : tag === "image" ? "img" : tag === "text" ? "span" : tag === "element" && props.data?.hName === "iframe" ? "iframe" : "div"; const attrs = {}; if (props.url) { if (tag === "link") attrs.href = props.url; else if (tag === "image") attrs.src = props.url; } for (const [key, value] of Object.entries(props.data?.hProperties || {})) attrs[key] = value; if (tag === "text" && Object.keys(attrs).length === 0 && typeof children === "string") return children; return `<${tagName} ${Object.entries(attrs).map(([k, v]) => `${k}="${v ?? ""}"`).join(" ")}>${typeof children === "string" ? children : children.join("")}</${tagName}>`; }; function renderLink({ url, render, className = "", u = h }) { const linkContent = render === "link" ? u("text", "Edit in LiveCodes") : u("image", { url: `https://livecodes.io/livecodes/assets/images/edit-in-livecodes-button${className.toLowerCase().includes("dark") ? "-dark" : ""}.svg`, data: { hProperties: { alt: "Edit in LiveCodes", style: "height: 28px;" } } }); return u("link", { url, data: { hProperties: { target: "_blank", rel: "noopener noreferrer", class: className } } }, [linkContent]); } function renderIframe({ url, height, className, loading = "lazy", data, u = h }) { return u("element", { data: { ...data, hName: "iframe", hProperties: { ...data?.hProperties, scrolling: "no", loading: loading === "eager" ? "eager" : "lazy", style: `height: ${height ?? "300px"}; width: 100%; border:1px solid black; border-radius:6px;`, class: className, src: url, sandbox: "allow-same-origin allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-presentation allow-scripts" } } }); } //#endregion //#region src/index.ts function markDownItLiveCodes(md, options = {}) { const { unescapeAll } = md.utils; const defaultRenderer = md.renderer.rules.fence ?? ((tokens, idx, options$1, _env, slf) => slf.renderToken(tokens, idx, options$1)); md.renderer.rules.fence = (tokens, idx, mdOptions, env, slf) => { const token = tokens[idx]; const info = token.info ? unescapeAll(token.info).trim() : ""; const content = (token.content || "").trim(); const renderDefault = () => defaultRenderer(tokens, idx, mdOptions, env, slf); const [lang, ...restInfo] = info.split(/(\s+)/g); const meta = parseMeta(restInfo.join("") || ""); if (meta.livecodes == null && !options.auto || meta.livecodes === "false") return renderDefault(); const language = meta.lang ?? lang; const { embedOptions, render, height, className } = processOptions({ options, meta, language, content }); const url = getPlaygroundUrl(embedOptions); if (render === "link" || render === "button") return renderDefault() + renderLink({ url, render, className }); if (render === "meta") { env.livecodesUrl = url; token.attrSet("data-livecodes-url", url); return renderDefault(); } return renderIframe({ url, height, className, loading: options.loading }); }; } //#endregion export { markDownItLiveCodes as default };