Commits

Yury Bondarenko authored 674a4829ce5
96252: Even more lazy dsMarkdown
No tags

src/app/shared/utils/markdown.pipe.ts

Modified
1 -import { Inject, InjectionToken, Pipe, PipeTransform } from '@angular/core';
2 -import MarkdownIt from 'markdown-it';
3 -import * as sanitizeHtml from 'sanitize-html';
1 +import { Inject, InjectionToken, Pipe, PipeTransform, SecurityContext } from '@angular/core';
4 2 import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
5 3 import { environment } from '../../../environments/environment';
6 4
5 +const markdownItLoader = async () => (await import('markdown-it')).default;
6 +type LazyMarkdownIt = ReturnType<typeof markdownItLoader>;
7 +const MARKDOWN_IT = new InjectionToken<LazyMarkdownIt>(
8 + 'Lazily loaded MarkdownIt',
9 + { providedIn: 'root', factory: markdownItLoader }
10 +);
11 +
7 12 const mathjaxLoader = async () => (await import('markdown-it-mathjax3')).default;
8 13 type Mathjax = ReturnType<typeof mathjaxLoader>;
9 14 const MATHJAX = new InjectionToken<Mathjax>(
10 15 'Lazily loaded mathjax',
11 16 { providedIn: 'root', factory: mathjaxLoader }
12 17 );
13 18
19 +const sanitizeHtmlLoader = async () => (await import('sanitize-html') as any).default;
20 +type SanitizeHtml = ReturnType<typeof sanitizeHtmlLoader>;
21 +const SANITIZE_HTML = new InjectionToken<SanitizeHtml>(
22 + 'Lazily loaded sanitize-html',
23 + { providedIn: 'root', factory: sanitizeHtmlLoader }
24 +);
25 +
14 26 /**
15 27 * Pipe for rendering markdown and mathjax.
16 28 * - markdown will only be rendered if {@link MarkdownConfig#enabled} is true
17 29 * - mathjax will only be rendered if both {@link MarkdownConfig#enabled} and {@link MarkdownConfig#mathjax} are true
18 30 *
19 31 * This pipe should be used on the 'innerHTML' attribute of a component, in combination with an async pipe.
20 32 * Example usage:
21 33 * <span class="example" [innerHTML]="'# title' | dsMarkdown | async"></span>
22 34 * Result:
23 35 * <span class="example">
24 36 * <h1>title</h1>
25 37 * </span>
26 38 */
27 39 @Pipe({
28 40 name: 'dsMarkdown'
29 41 })
30 42 export class MarkdownPipe implements PipeTransform {
31 43
32 44 constructor(
33 45 protected sanitizer: DomSanitizer,
46 + @Inject(MARKDOWN_IT) private markdownIt: LazyMarkdownIt,
34 47 @Inject(MATHJAX) private mathjax: Mathjax,
48 + @Inject(SANITIZE_HTML) private sanitizeHtml: SanitizeHtml,
35 49 ) {
36 50 }
37 51
38 52 async transform(value: string): Promise<SafeHtml> {
39 53 if (!environment.markdown.enabled) {
40 54 return value;
41 55 }
56 + const MarkdownIt = await this.markdownIt;
42 57 const md = new MarkdownIt({
43 58 html: true,
44 59 linkify: true,
45 60 });
61 +
62 + let html: string;
46 63 if (environment.markdown.mathjax) {
47 64 md.use(await this.mathjax);
48 - }
49 - return this.sanitizer.bypassSecurityTrustHtml(
50 - sanitizeHtml(md.render(value), {
65 + const sanitizeHtml = await this.sanitizeHtml
66 + html = sanitizeHtml(md.render(value), {
51 67 // sanitize-html doesn't let through SVG by default, so we extend its allowlists to cover MathJax SVG
52 68 allowedTags: [
53 69 ...sanitizeHtml.defaults.allowedTags,
54 70 'mjx-container', 'svg', 'g', 'path', 'rect', 'text'
55 71 ],
56 72 allowedAttributes: {
57 73 ...sanitizeHtml.defaults.allowedAttributes,
58 74 'mjx-container': [
59 75 'class', 'style', 'jax'
60 76 ],
70 86 rect: [
71 87 'width', 'height', 'x', 'y', 'transform', 'style'
72 88 ],
73 89 text: [
74 90 'transform', 'font-size'
75 91 ]
76 92 },
77 93 parser: {
78 94 lowerCaseAttributeNames: false,
79 95 },
80 - })
81 - );
96 + });
97 + } else {
98 + html = this.sanitizer.sanitize(SecurityContext.HTML, md.render(value));
99 + }
100 +
101 + return this.sanitizer.bypassSecurityTrustHtml(html)
82 102 }
83 103 }

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut