Skip to content

Commit

Permalink
Merge pull request #19 from Parcility/master
Browse files Browse the repository at this point in the history
fix: crash when color is not a string, and ssr mode in node
  • Loading branch information
magnetardev authored Sep 6, 2022
2 parents ec8620e + 79e220b commit 7c696b2
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 18 deletions.
35 changes: 22 additions & 13 deletions lib/renderable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import DOMPurify from "dompurify";
import createDOMPurify, { DOMPurifyI } from "dompurify";
import { escapeHTML } from "./util";

const PURIFY_OPTIONS: DOMPurify.Config = {
const PURIFY_OPTIONS: createDOMPurify.Config = {
RETURN_DOM_FRAGMENT: false,
RETURN_DOM: false,
FORCE_BODY: true,
Expand All @@ -12,6 +12,15 @@ const PURIFY_OPTIONS: DOMPurify.Config = {
},
};

let DOMPurify: Promise<DOMPurifyI> = (async function () {
if (!("window" in globalThis)) {
const { JSDOM } = await import("jsdom");
const window = new JSDOM("").window;
return createDOMPurify(window as any);
}
return createDOMPurify;
})();

export interface RenderableElement {
tag: string;
attributes: Record<string, string | boolean>;
Expand All @@ -23,10 +32,10 @@ export interface RenderableNode {
contents: string;
}

export function createRawNode(contents: string): RenderableNode {
export async function createRawNode(contents: string): Promise<RenderableNode> {
return {
raw: true,
contents: DOMPurify.sanitize(contents, PURIFY_OPTIONS) as string,
contents: (await DOMPurify).sanitize(contents, PURIFY_OPTIONS) as string,
};
}

Expand Down Expand Up @@ -67,15 +76,15 @@ export function renderElementDOM(el: RenderableElement): HTMLElement {
return element;
}

export function renderElementString(el: RenderableElement): string {
export async function renderElementString(el: RenderableElement): Promise<string> {
let result = `<${el.tag} `;
result += Object.entries(el.attributes)
.map(([key, value]) =>
typeof value === "boolean" ? `${value ? key : ""}` : `${key}="${escapeHTML(value, true)}"`
)
.join(" ");
let children = el.children
.map((child) => {
let children = await Promise.all(
el.children.map(async (child) => {
if (!child) return "";
if (typeof child === "string") {
return escapeHTML(child);
Expand All @@ -84,19 +93,19 @@ export function renderElementString(el: RenderableElement): string {
}
return renderElementString(child as RenderableElement);
})
.join("");
result += `>${children}</${el.tag}>`;
);
result += `>${children.join("")}</${el.tag}>`;

let res = DOMPurify.sanitize(result, PURIFY_OPTIONS) as string;
let res = (await DOMPurify).sanitize(result, PURIFY_OPTIONS) as string;
return res;
}

export function renderElement<T extends boolean, U extends T extends true ? string : HTMLElement>(
el: RenderableElement,
ssr: T
): U {
if (ssr) return renderElementString(el) as unknown as U;
return renderElementDOM(el) as unknown as U;
): Promise<U> {
if (ssr) return renderElementString(el) as unknown as Promise<U>;
return Promise.resolve(renderElementDOM(el) as unknown as U);
}

export function setStyles(el: RenderableElement, styles: Record<string, string>, original: string = "") {
Expand Down
1 change: 1 addition & 0 deletions lib/util/colors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This validates CSS colors according to Sileo, though for speed purporses we try to use the CSS.supports API.
export function isValidColor(color: string): boolean {
if (typeof color !== "string") return false;
if ("CSS" in globalThis && "supports" in CSS) return CSS.supports("color", color);
return isHex(color) || isRGB(color) || isHSL(color) || isNamed(color);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/views/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default class DepictionMarkdownView extends DepictionBaseView {
let xssWarn = `<p style="opacity:0.3">[Warning: This depiction may be trying to maliciously run code in your browser.]</p><br>`;
rendered = rendered.replace(
/<hr>/gi,
renderElementString(await makeView(new DepictionSeparatorView(undefined)))
await renderElementString(await makeView(new DepictionSeparatorView(undefined)))
);
if (
rendered.toLowerCase().indexOf("<script>") !== -1 ||
Expand Down Expand Up @@ -67,7 +67,7 @@ export default class DepictionMarkdownView extends DepictionBaseView {
let bottomSpacing = this.useSpacing ? 13 : 0;
let el = createShadowedElement({ class: "nd-markdown" }, [
createElement("style", {}, [markdownStyles]),
createRawNode(resp),
await createRawNode(resp),
]);
let styles: any = {
margin: "0 " + margins + "px",
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@parcility/kennel",
"version": "2.0.2",
"version": "2.0.3",
"description": "A comprehensive, easy-to-use native depiction renderer.",
"type": "module",
"repository": {
Expand Down Expand Up @@ -40,6 +40,7 @@
},
"devDependencies": {
"@types/dompurify": "^2.3.4",
"@types/jsdom": "^20.0.0",
"@types/marked": "^4.0.6",
"sass": "^1.54.8",
"typescript": "^4.6.4",
Expand All @@ -48,6 +49,7 @@
},
"dependencies": {
"dompurify": "^2.4.0",
"jsdom": "^20.0.0",
"marked": "^4.1.0"
}
}
4 changes: 4 additions & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export default defineConfig({
// the proper extensions will be added
fileName: "index",
},
sourcemap: "inline",
rollupOptions: {
external: ["fs", "jsdom"],
},
},
plugins: [dts()],
});
Loading

0 comments on commit 7c696b2

Please sign in to comment.