diff --git a/deno.json b/deno.json index d7eff020..317826f5 100644 --- a/deno.json +++ b/deno.json @@ -44,7 +44,6 @@ "@std/json": "jsr:@std/json@^0.223.0", "@std/media-types": "jsr:@std/media-types@^0.224.1", "@std/streams": "jsr:@std/streams@^0.223.0", - "campfire.js": "https://esm.sh/campfire.js@4.0.0-alpha3", "comlink": "npm:comlink@^4.4.1", "comlink-async-generator": "npm:comlink-async-generator@^0.0.1", "deno-safe-fetch/load": "https://gitlab.com/soapbox-pub/deno-safe-fetch/-/raw/v1.0.0/load.ts", diff --git a/src/middleware/serveStaticWithOG.ts b/src/middleware/serveStaticWithOG.ts index 3adf64f3..8f679a90 100644 --- a/src/middleware/serveStaticWithOG.ts +++ b/src/middleware/serveStaticWithOG.ts @@ -1,6 +1,6 @@ import { Context, Env, MiddlewareHandler, Next } from '@hono/hono'; import { serveStatic as baseServeStatic, ServeStaticOptions } from '@hono/hono/serve-static'; -import { html, r } from 'campfire.js'; +import { html, r } from '@/utils/html.ts'; import { Conf } from '@/config.ts'; import { getInstanceName, diff --git a/src/utils/html.ts b/src/utils/html.ts new file mode 100644 index 00000000..5e9ff918 --- /dev/null +++ b/src/utils/html.ts @@ -0,0 +1,58 @@ +interface RawHtml { + raw: true; + contents: string; +} + +/** + * Options for r() + */ +interface RawHtmlOptions { + joiner?: string; +} + +export function escape(str: string) { + if (!str) return ''; + + return str.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** + * Prevent values from being escaped by html``. + * @param val Any value. + * @returns An object that tells html`` to not escape `val` while building the HTML string. + */ +export function r(val: any, options?: RawHtmlOptions): RawHtml { + return { + raw: true, + contents: Array.isArray(val) ? val.join(options?.joiner ?? ' ') : val.toString(), + }; +} + +/** + * @param strings The constant portions of the template string. + * @param values The templated values. + * @returns The built HTML. + * @example + * ``` + * const unsafe = `oops `; + * testing.innerHTML = html`foo bar baz ${unsafe}`; + * console.assert(testing === "foo bar baz oops%20%3Cscript%3Ealert%281%29%3C/script%3E"); + * ``` + */ +export function html(strings: TemplateStringsArray, ...values: (string | number | RawHtml)[]) { + const built = []; + for (let i = 0; i < strings.length; i++) { + built.push(strings[i] || ''); + const val = values[i]; + if (typeof val !== 'undefined' && typeof val !== 'object') { + built.push(escape((val || '').toString())); + } else { + built.push(val?.contents || ''); + } + } + return built.join(''); +}