|
| 1 | +// Register directive nodes in mdast: |
| 2 | +/// <reference types="mdast-util-directive" /> |
| 3 | +// |
| 4 | +import { HyperbookContext } from "@hyperbook/types"; |
| 5 | +import { Root } from "mdast"; |
| 6 | +import fs from "fs"; |
| 7 | +import path from "path"; |
| 8 | +import { visit } from "unist-util-visit"; |
| 9 | +import { VFile } from "vfile"; |
| 10 | +import { |
| 11 | + expectContainerDirective, |
| 12 | + isDirective, |
| 13 | + registerDirective, |
| 14 | +} from "./remarkHelper"; |
| 15 | + |
| 16 | +interface CodeBundle { |
| 17 | + js?: string; |
| 18 | + scripts?: string[]; |
| 19 | +} |
| 20 | + |
| 21 | +export default (ctx: HyperbookContext) => () => { |
| 22 | + const name = "p5"; |
| 23 | + const cdnLibraryUrl = ctx.makeUrl( |
| 24 | + path.posix.join("directive-p5", "p5.min.js"), |
| 25 | + "assets", |
| 26 | + ); |
| 27 | + |
| 28 | + const wrapSketch = (sketchCode?: string) => { |
| 29 | + if (sketchCode !== "" && !sketchCode?.includes("setup")) { |
| 30 | + return ` |
| 31 | + function setup() { |
| 32 | + createCanvas(100, 100); |
| 33 | + background(200); |
| 34 | + ${sketchCode} |
| 35 | + }`; |
| 36 | + } |
| 37 | + return sketchCode; |
| 38 | + }; |
| 39 | + |
| 40 | + // see https://github.com/processing/p5.js-website/blob/main/src/components/CodeEmbed/frame.tsx |
| 41 | + const wrapInMarkup = (code: CodeBundle) => |
| 42 | + `<!DOCTYPE html> |
| 43 | +<meta charset="utf8" /> |
| 44 | +<base href="${ctx.makeUrl("", "public")}" /> |
| 45 | +<style type='text/css'> |
| 46 | +html, body { |
| 47 | + margin: 0; |
| 48 | + padding: 0; |
| 49 | +} |
| 50 | +canvas { |
| 51 | + display: block; |
| 52 | +} |
| 53 | +</style> |
| 54 | +${(code.scripts ? [cdnLibraryUrl, ...code.scripts] : []).map((src) => `<script type="text/javascript" src="${src}"></script>`).join("\n")} |
| 55 | +<body>Hallo</body> |
| 56 | +<script id="code" type="text/javascript">${wrapSketch(code.js) || ""}</script> |
| 57 | +${ |
| 58 | + (code.scripts?.length ?? 0) > 0 |
| 59 | + ? "" |
| 60 | + : ` |
| 61 | +<script type="text/javascript"> |
| 62 | + // Listen for p5.min.js text content and include in iframe's head as script |
| 63 | + window.addEventListener("message", event => { |
| 64 | + // Include check to prevent p5.min.js from being loaded twice |
| 65 | + const scriptExists = !!document.getElementById("p5ScriptTagInIframe"); |
| 66 | + if (!scriptExists && event.data?.sender === '${cdnLibraryUrl}') { |
| 67 | + const p5ScriptElement = document.createElement('script'); |
| 68 | + p5ScriptElement.id = "p5ScriptTagInIframe"; |
| 69 | + p5ScriptElement.type = 'text/javascript'; |
| 70 | + p5ScriptElement.textContent = event.data.message; |
| 71 | + document.head.appendChild(p5ScriptElement); |
| 72 | + } |
| 73 | + }) |
| 74 | +</script> |
| 75 | +` |
| 76 | +} |
| 77 | +`.replace(/\u00A0/g, " "); |
| 78 | + |
| 79 | + return (tree: Root, file: VFile) => { |
| 80 | + visit(tree, function (node) { |
| 81 | + if (node.type === "element" && node.tagName === "p5") { |
| 82 | + const { src = "" } = node.properties || {}; |
| 83 | + |
| 84 | + expectContainerDirective(node, file, name); |
| 85 | + registerDirective(file, name, [], ["style.css"]); |
| 86 | + |
| 87 | + const srcFile = fs.readFileSync( |
| 88 | + path.join(ctx.root, "public", String(src)), |
| 89 | + "utf8", |
| 90 | + ); |
| 91 | + |
| 92 | + const srcdoc = wrapInMarkup({ |
| 93 | + js: srcFile, |
| 94 | + scripts: [ |
| 95 | + ctx.makeUrl( |
| 96 | + path.posix.join("directive-p5", "p5.sound.min.js"), |
| 97 | + "assets", |
| 98 | + ), |
| 99 | + ], |
| 100 | + }); |
| 101 | + node.tagName = "div"; |
| 102 | + node.properties = { |
| 103 | + class: "directive-p5", |
| 104 | + }; |
| 105 | + node.children = [ |
| 106 | + { |
| 107 | + type: "element", |
| 108 | + tagName: "iframe", |
| 109 | + properties: { |
| 110 | + srcdoc: srcdoc, |
| 111 | + loading: "eager", |
| 112 | + sandbox: |
| 113 | + "allow-scripts allow-popups allow-modals allow-forms allow-same-origin", |
| 114 | + "aria-label": "Code Preview", |
| 115 | + title: "Code Preview", |
| 116 | + }, |
| 117 | + children: [], |
| 118 | + }, |
| 119 | + ]; |
| 120 | + } |
| 121 | + }); |
| 122 | + }; |
| 123 | +}; |
0 commit comments