updates to markdown processing to allow custom elements

This commit is contained in:
James Brumond 2023-05-11 18:02:40 -07:00
parent 44315b324a
commit 3ddde81e1c
Signed by: james
GPG Key ID: E8F2FC44BAA3357A
5 changed files with 43 additions and 32 deletions

24
package-lock.json generated
View File

@ -92,9 +92,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.16.5", "version": "18.16.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.6.tgz",
"integrity": "sha512-seOA34WMo9KB+UA78qaJoCO20RJzZGVXQ5Sh6FWu0g/hfT44nKXnej3/tCQl7FL97idFpBhisLYCTB50S0EirA==", "integrity": "sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==",
"dev": true "dev": true
}, },
"node_modules/@types/prismjs": { "node_modules/@types/prismjs": {
@ -989,9 +989,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.6.9", "version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dependencies": { "dependencies": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },
@ -2021,9 +2021,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "18.16.5", "version": "18.16.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.5.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.6.tgz",
"integrity": "sha512-seOA34WMo9KB+UA78qaJoCO20RJzZGVXQ5Sh6FWu0g/hfT44nKXnej3/tCQl7FL97idFpBhisLYCTB50S0EirA==", "integrity": "sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==",
"dev": true "dev": true
}, },
"@types/prismjs": { "@types/prismjs": {
@ -2674,9 +2674,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.9", "version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"requires": { "requires": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
}, },

View File

@ -2,13 +2,12 @@
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import createDOMPurify = require('dompurify'); import createDOMPurify = require('dompurify');
export function sanitize_html(html: string) : string { export type CustomElementHandling = createDOMPurify.Config['CUSTOM_ELEMENT_HANDLING'];
export function sanitize_html(html: string, custom_elements?: CustomElementHandling) : string {
const { window } = new JSDOM(''); const { window } = new JSDOM('');
const dom_purify = createDOMPurify(window as any as Window); const dom_purify = createDOMPurify(window as any as Window);
return dom_purify.sanitize(html, { return dom_purify.sanitize(html, {
CUSTOM_ELEMENT_HANDLING: { CUSTOM_ELEMENT_HANDLING: custom_elements
tagNameCheck: (tag_name) => tag_name === 'svg-icon',
attributeNameCheck: (attr_name) => attr_name === 'icon',
}
}); });
} }

View File

@ -4,7 +4,7 @@ import { create_renderer } from './renderer';
import { mark_ext } from './mark'; import { mark_ext } from './mark';
import { section_ext } from './section'; import { section_ext } from './section';
import { icon_ext } from './icon'; import { icon_ext } from './icon';
import { sanitize_html } from './html-sanitize'; import { CustomElementHandling, sanitize_html } from './html-sanitize';
import { katex_block_ext, katex_inline_ext } from './katex'; import { katex_block_ext, katex_inline_ext } from './katex';
import { footnote_list_ext, footnote_ref_ext } from './footnotes'; import { footnote_list_ext, footnote_ref_ext } from './footnotes';
import { description_list_ext } from './description-list'; import { description_list_ext } from './description-list';
@ -16,6 +16,7 @@ export interface MarkdownOptions {
inline?: boolean; inline?: boolean;
katex_macros?: Record<string, string>; katex_macros?: Record<string, string>;
extensions?: MarkdownExtension[]; extensions?: MarkdownExtension[];
custom_elements?: CustomElementHandling;
} }
export interface MarkdownExtension { export interface MarkdownExtension {
@ -64,5 +65,5 @@ export async function render_markdown_to_html(markdown: string, options: Markdow
}); });
}); });
return sanitize_html(unsafe_html); return sanitize_html(unsafe_html, options.custom_elements);
} }

View File

@ -91,6 +91,8 @@ function code(renderer: marked.Renderer, opts: MarkdownOptions) {
// Find the first newline that is not preceeded by a "\" // Find the first newline that is not preceeded by a "\"
const end_of_input = /(?<!\\)(?:\r\n|\r|\n)/.exec(code); const end_of_input = /(?<!\\)(?:\r\n|\r|\n)/.exec(code);
// todo: handling for multi-line heredocs?
// If there is no such newline, the whole content is input // If there is no such newline, the whole content is input
if (! end_of_input) { if (! end_of_input) {
return figure(`<pre class="language-bash">${render_prism(code, 'bash')}</pre>`); return figure(`<pre class="language-bash">${render_prism(code, 'bash')}</pre>`);

View File

@ -1,18 +1,27 @@
// note: fallback colors come from "category10" scheme
// https://vega.github.io/vega/docs/schemes/#category10
// todo: css variables
export const chart_data_colors = [ export const chart_data_colors = [
'var(--theme-chart-data-0, #1f77b4)', 'var(--theme-chart-shape-red-fill, #feaea5)',
'var(--theme-chart-data-1, #ff7f0e)', 'var(--theme-chart-shape-orange-fill, #fad6bc)',
'var(--theme-chart-data-2, #2ca02c)', 'var(--theme-chart-shape-yellow-fill, #fffec6)',
'var(--theme-chart-data-3, #d62728)', 'var(--theme-chart-shape-green-fill, #d6f9d5)',
'var(--theme-chart-data-4, #9467bd)', 'var(--theme-chart-shape-teal-fill, #b0ebe9)',
'var(--theme-chart-data-5, #8c564b)', 'var(--theme-chart-shape-pink-fill, #ffcae2)',
'var(--theme-chart-data-6, #e377c2)', 'var(--theme-chart-shape-purple-fill, #efdeff)',
'var(--theme-chart-data-7, #7f7f7f)', 'var(--theme-chart-shape-blue-fill, #caebff)',
'var(--theme-chart-data-8, #bcbd22)', 'var(--theme-chart-shape-indigo-fill, #c2d6f9)',
'var(--theme-chart-data-9, #17becf)', 'var(--theme-chart-shape-magenta-fill, #ebb5cd)',
'var(--theme-chart-shape-brown-fill, #e1c3b8)',
'var(--theme-chart-shape-red-line, #ff806d)',
'var(--theme-chart-shape-orange-line, #ffb780)',
'var(--theme-chart-shape-yellow-line, #e1dc18)',
'var(--theme-chart-shape-green-line, #66cc66)',
'var(--theme-chart-shape-teal-line, #66cdcc)',
'var(--theme-chart-shape-pink-line, #ff99cb)',
'var(--theme-chart-shape-purple-line, #cc99fe)',
'var(--theme-chart-shape-blue-line, #66cbff)',
'var(--theme-chart-shape-indigo-line, #6295ee)',
'var(--theme-chart-shape-magenta-line, #cc6698)',
'var(--theme-chart-shape-brown-line, #bf8c71)',
]; ];
export function* chart_data_color_generator() : Generator<string, never> { export function* chart_data_color_generator() : Generator<string, never> {