|
2 | 2 | import darkTheme from "solarflare-theme/themes/cloudflare-dark-color-theme.json" with { type: "json" }; |
3 | 3 | import lightTheme from "solarflare-theme/themes/cloudflare-light-color-theme.json" with { type: "json" }; |
4 | 4 |
|
5 | | -import { definePlugin } from "@expressive-code/core"; |
6 | | -import { h } from "@expressive-code/core/hast"; |
| 5 | +import pluginWorkersPlayground from "./plugins/expressive-code/workers-playground.js"; |
| 6 | +import pluginOutputFrame from "./plugins/expressive-code/output-frame.js"; |
| 7 | +import pluginDefaultTitles from "./plugins/expressive-code/default-titles.js"; |
7 | 8 |
|
8 | 9 | import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections"; |
9 | 10 |
|
10 | | -import lzstring from "lz-string"; |
11 | | - |
12 | | -/** |
13 | | - * @param {string} code |
14 | | - */ |
15 | | -export function serialiseWorker(code) { |
16 | | - const formData = new FormData(); |
17 | | - |
18 | | - const metadata = { |
19 | | - main_module: "index.js", |
20 | | - }; |
21 | | - |
22 | | - formData.set( |
23 | | - "index.js", |
24 | | - new Blob([code], { |
25 | | - type: "application/javascript+module", |
26 | | - }), |
27 | | - "index.js", |
28 | | - ); |
29 | | - |
30 | | - formData.set( |
31 | | - "metadata", |
32 | | - new Blob([JSON.stringify(metadata)], { type: "application/json" }), |
33 | | - ); |
34 | | - |
35 | | - return formData; |
36 | | -} |
37 | | - |
38 | | -/** |
39 | | - * @param {FormData} worker |
40 | | - */ |
41 | | -export async function compressWorker(worker) { |
42 | | - const serialisedWorker = new Response(worker); |
43 | | - return lzstring.compressToEncodedURIComponent( |
44 | | - `${serialisedWorker.headers.get( |
45 | | - "content-type", |
46 | | - )}:${await serialisedWorker.text()}`, |
47 | | - ); |
48 | | -} |
49 | | - |
50 | | -function workersPlaygroundButton() { |
51 | | - return definePlugin({ |
52 | | - name: "Adds 'Run Worker' button to JS codeblocks", |
53 | | - baseStyles: ` |
54 | | - .run { |
55 | | - display: flex; |
56 | | - gap: 0.25rem; |
57 | | - flex-direction: row; |
58 | | - position: absolute; |
59 | | - inset-block-start: calc(var(--ec-brdWd) + var(--button-spacing)); |
60 | | - inset-inline-end: calc(var(--ec-brdWd) + var(--ec-uiPadInl) * 3); |
61 | | - direction: ltr; |
62 | | - unicode-bidi: isolate; |
63 | | -
|
64 | | - text-decoration-color: var(--sl-color-accent); |
65 | | - span { |
66 | | - color: var(--sl-color-white); |
67 | | - font-family: var(--sl-font-system); |
68 | | - } |
69 | | - } |
70 | | - `, |
71 | | - hooks: { |
72 | | - postprocessRenderedBlock: async (context) => { |
73 | | - if (!context.codeBlock.meta.includes("playground")) return; |
74 | | - |
75 | | - const serialised = await compressWorker( |
76 | | - serialiseWorker(context.codeBlock.code), |
77 | | - ); |
78 | | - |
79 | | - const url = `https://workers.cloudflare.com/playground#${serialised}`; |
80 | | - |
81 | | - const runButton = h("a.run", { href: url, target: "__blank" }, [ |
82 | | - h("span", "Run Worker in Playground"), |
83 | | - ]); |
84 | | - |
85 | | - const ast = context.renderData.blockAst; |
86 | | - ast.children.push(runButton); |
87 | | - |
88 | | - context.renderData.blockAst = ast; |
89 | | - }, |
90 | | - }, |
91 | | - }); |
92 | | -} |
93 | | - |
94 | | -function outputCodeblocks() { |
95 | | - return definePlugin({ |
96 | | - name: "Adds the '.code-output' class if 'output' is passed on the opening codefence.", |
97 | | - hooks: { |
98 | | - preprocessMetadata: async (context) => { |
99 | | - if (!context.codeBlock.meta.includes("output")) return; |
100 | | - context.codeBlock.props.frame = "none"; |
101 | | - }, |
102 | | - postprocessRenderedBlock: async (context) => { |
103 | | - if (!context.codeBlock.meta.includes("output")) return; |
104 | | - context.renderData.blockAst.properties.className ??= []; |
105 | | - if (Array.isArray(context.renderData.blockAst.properties.className)) { |
106 | | - context.renderData.blockAst.properties.className.push("code-output"); |
107 | | - } |
108 | | - context.addStyles(` |
109 | | - div.expressive-code:has(figure.code-output) { |
110 | | - margin-top: 0 !important; |
111 | | - } |
112 | | -
|
113 | | - .code-output .copy { |
114 | | - display: none !important; |
115 | | - } |
116 | | -
|
117 | | - .code-output > pre { |
118 | | - border-top-width: 0 !important; |
119 | | - background: var(--sl-color-gray-6) !important; |
120 | | - } |
121 | | -
|
122 | | - .code-output > pre > code { |
123 | | - user-select: none; |
124 | | - transition: opacity 0.5s ease; |
125 | | - } |
126 | | -
|
127 | | - .code-output > pre > code:hover { |
128 | | - cursor: default; |
129 | | - opacity: 0.5; |
130 | | - } |
131 | | - `); |
132 | | - }, |
133 | | - }, |
134 | | - }); |
135 | | -} |
136 | | - |
137 | | -function defaultLanguageTitles() { |
138 | | - return definePlugin({ |
139 | | - name: "Adds language-specific default titles.", |
140 | | - hooks: { |
141 | | - preprocessLanguage: async (context) => { |
142 | | - switch (context.codeBlock.language) { |
143 | | - case "powershell": { |
144 | | - context.codeBlock.props.title ??= "PowerShell"; |
145 | | - break; |
146 | | - } |
147 | | - default: { |
148 | | - return; |
149 | | - } |
150 | | - } |
151 | | - }, |
152 | | - }, |
153 | | - }); |
154 | | -} |
155 | | - |
156 | 11 | export default { |
157 | 12 | plugins: [ |
158 | | - workersPlaygroundButton(), |
159 | | - outputCodeblocks(), |
160 | | - defaultLanguageTitles(), |
| 13 | + pluginWorkersPlayground(), |
| 14 | + pluginOutputFrame(), |
| 15 | + pluginDefaultTitles(), |
161 | 16 | pluginCollapsibleSections(), |
162 | 17 | ], |
163 | 18 | themes: [darkTheme, lightTheme], |
|
0 commit comments