8 Commits

Author SHA1 Message Date
ae7b491107 0.1.17 2023-05-12 14:59:49 -07:00
8599460702 whitelist rdfa tags/attrs in html sanitizer 2023-05-12 14:59:45 -07:00
fb52d31090 0.1.16 2023-05-12 14:54:12 -07:00
859064f00b add rdfa for breadcrumbs 2023-05-12 14:54:05 -07:00
7803b495c2 0.1.15 2023-05-12 14:43:14 -07:00
f48c0c6b75 fixing bug 2023-05-12 14:42:57 -07:00
34cacb48bb 0.1.14 2023-05-12 14:35:54 -07:00
7d5d29dade add support for breadcrumb navs 2023-05-12 14:35:49 -07:00
6 changed files with 73 additions and 5 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@doc-utils/markdown2html", "name": "@doc-utils/markdown2html",
"version": "0.1.13", "version": "0.1.17",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@doc-utils/markdown2html", "name": "@doc-utils/markdown2html",
"version": "0.1.13", "version": "0.1.17",
"dependencies": { "dependencies": {
"bytefield-svg": "^1.6.1", "bytefield-svg": "^1.6.1",
"dompurify": "^2.3.6", "dompurify": "^2.3.6",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@doc-utils/markdown2html", "name": "@doc-utils/markdown2html",
"version": "0.1.13", "version": "0.1.17",
"publishConfig": { "publishConfig": {
"registry": "https://gitea.home.jbrumond.me/api/packages/doc-utils/npm/" "registry": "https://gitea.home.jbrumond.me/api/packages/doc-utils/npm/"
}, },

63
src/breadcrumb-nav.ts Normal file
View File

@@ -0,0 +1,63 @@
import { marked } from 'marked';
import { ParsedAttributes, parse_attributes } from './attrs';
import { MarkdownOptions } from './render';
export interface BreadcrumbNavToken extends marked.Tokens.Generic {
text: string;
attrs: ParsedAttributes;
items: marked.Token[][];
}
export function breadcrumb_nav_ext(renderer: marked.Renderer, opts: MarkdownOptions) : marked.TokenizerExtension & marked.RendererExtension {
return {
name: 'breadcrumb_nav',
level: 'block',
start: (src) => src.match(/^\/\/\//)?.index,
tokenizer(src, tokens) {
const rule = /^\/\/\/(\/*)([^\n]+)?(?:\n)((?:[^\/]|\/\/?(?!\/\1))+)\/\/\/\1/;
const match = rule.exec(src);
if (match) {
const token: BreadcrumbNavToken = {
type: 'breadcrumb_nav',
raw: match[0],
text: match[3],
attrs: parse_attributes(match[2] || ''),
tokens: [ ],
items: [ ],
};
const lines = match[3].trim().split('\n');
for (const line of lines) {
const tokens = this.lexer.inlineTokens(line, [ ]);
token.tokens.push(...tokens);
token.items.push(tokens);
}
return token;
}
},
renderer(token: BreadcrumbNavToken) {
return `<nav aria-label="breadcrumbs" ${token.attrs.html_attrs.join(' ')}>\n`
+ `\t<ol typeof="https://schema.org/BreadcrumbList">\n`
+ '\t\t'
+ token.items.map((tokens, index) =>{
let item = '<li property="itemListElement" typeof="https://schema.org/ListItem">\n';
if (index) {
item += '\t\t\t<span class="separator" aria-hidden="true">/</span>\n';
}
item += `\t\t\t<span property="name">${this.parser.parseInline(tokens, renderer)}</span>\n`;
item += `\t\t\t<meta property="position" content="${index + 1}">\n`;
return item + + '\t\t</li>';
}).join('\n\t\t')
+ '\n'
+ `\t</ol>\n`
+ `</nav>`;
}
};
}

View File

@@ -8,6 +8,9 @@ export function sanitize_html(html: string, custom_elements?: CustomElementHandl
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_elements CUSTOM_ELEMENT_HANDLING: custom_elements,
ALLOWED_TAGS: ['meta'],
ALLOWED_ATTR: ['typeof', 'property', 'content'],
ADD_URI_SAFE_ATTR: ['typeof']
}); });
} }

View File

@@ -9,6 +9,7 @@ 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';
import { resolve_async_bindings } from './async-steps'; import { resolve_async_bindings } from './async-steps';
import { breadcrumb_nav_ext } from './breadcrumb-nav';
export interface MarkdownOptions { export interface MarkdownOptions {
base_url?: string; base_url?: string;
@@ -40,6 +41,7 @@ export async function render_markdown_to_html(markdown: string, options: Markdow
description_list_ext(marked_options.renderer, options), description_list_ext(marked_options.renderer, options),
section_ext(marked_options.renderer, options), section_ext(marked_options.renderer, options),
icon_ext(marked_options.renderer, options), icon_ext(marked_options.renderer, options),
breadcrumb_nav_ext(marked_options.renderer, options),
...(options.extensions || [ ]).map((ext) => { ...(options.extensions || [ ]).map((ext) => {
return ext(marked_options.renderer, options); return ext(marked_options.renderer, options);
}), }),

View File

@@ -197,7 +197,7 @@ function code(renderer: marked.Renderer, opts: MarkdownOptions) {
const arg_pattern = /^(?:[a-zA-Z0-9_:-]+|"(?:[^"\n]|(?<=\\)")*")/; const arg_pattern = /^(?:[a-zA-Z0-9_:-]+|"(?:[^"\n]|(?<=\\)")*")/;
function parse_code_args(text: string) { function parse_code_args(text = '') {
const args: string[] = [ ]; const args: string[] = [ ];
text = text.trim(); text = text.trim();