UNPKG

chrome-devtools-frontend

Version:
160 lines (139 loc) 5.36 kB
// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as LitHtml from '../third_party/lit-html/lit-html.js'; import {MarkdownImageData} from './MarkdownImage.js'; import {MarkdownLinkData} from './MarkdownLink.js'; const html = LitHtml.html; const render = LitHtml.render; export interface MarkdownViewData { tokens: Object[]; } export class MarkdownView extends HTMLElement { private readonly shadow = this.attachShadow({mode: 'open'}); private tokenData: readonly Object[] = []; set data(data: MarkdownViewData) { this.tokenData = data.tokens; this.update(); } private update(): void { this.render(); } private render(): void { // Disabled until https://crbug.com/1079231 is fixed. // clang-format off render(html` <style> .message { line-height: 20px; font-size: 14px; color: var(--issue-gray); /* stylelint-disable-line plugin/use_theme_colors */ /* See: crbug.com/1152736 for color variable migration. */ margin-bottom: 4px; user-select: text; } .message p { margin-bottom: 16px; margin-block-start: 2px; } .message ul { list-style-type: none; list-style-position: inside; padding-inline-start: 0; } .message li { margin-top: 8px; display: list-item; } .message li::before { content: "→"; -webkit-mask-image: none; padding-right: 5px; position: relative; top: -1px; } .message code { color: var(--issue-black); /* stylelint-disable-line plugin/use_theme_colors */ /* See: crbug.com/1152736 for color variable migration. */ font-size: 12px; user-select: text; cursor: text; background: var(--issue-code); /* stylelint-disable-line plugin/use_theme_colors */ /* See: crbug.com/1152736 for color variable migration. */ } </style> <div class='message'> ${this.tokenData.map(renderToken)} </div> `, this.shadow); // clang-format on } } customElements.define('devtools-markdown-view', MarkdownView); declare global { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface HTMLElementTagNameMap { 'devtools-markdown-view': MarkdownView; } } // TODO(crbug.com/1108699): Fix types when they are available. // eslint-disable-next-line @typescript-eslint/no-explicit-any const renderChildTokens = (token: any): string => { return token.tokens.map(renderToken); }; const unescape = (text: string): string => { // Unescape will get rid of the escaping done by Marked to avoid double escaping due to escaping it also with Lit-html // Table taken from: front_end/third_party/marked/package/src/helpers.js /** @type {Map<string,string>} */ const escapeReplacements = new Map<string, string>([ ['&amp;', '&'], ['&lt;', '<'], ['&gt;', '>'], ['&quot;', '"'], ['&#39;', '\''], ]); return text.replace(/&(amp|lt|gt|quot|#39);/g, (matchedString: string) => { const replacement = escapeReplacements.get(matchedString); return replacement ? replacement : matchedString; }); }; // TODO(crbug.com/1108699): Fix types when they are available. // eslint-disable-next-line @typescript-eslint/no-explicit-any const renderText = (token: any): LitHtml.TemplateResult => { if (token.tokens && token.tokens.length > 0) { return html`${renderChildTokens(token)}`; } // Due to unescaping, unescaped html entities (see escapeReplacements' keys) will be rendered // as their corresponding symbol while the rest will be rendered as verbatim. // Marked's escape function can be found in front_end/third_party/marked/package/src/helpers.js return html`${unescape(token.text)}`; }; // TODO(crbug.com/1108699): Fix types when they are available. // eslint-disable-next-line @typescript-eslint/no-explicit-any const tokenRenderers = new Map<string, (token: any) => LitHtml.TemplateResult>([ ['paragraph', (token): LitHtml.TemplateResult => html`<p>${renderChildTokens(token)}</p>`], ['list', (token): LitHtml.TemplateResult => html`<ul>${token.items.map(renderToken)}</ul>`], ['list_item', (token): LitHtml.TemplateResult => html`<li>${renderChildTokens(token)}</li>`], ['text', renderText], ['codespan', (token): LitHtml.TemplateResult => html`<code>${unescape(token.text)}</code>`], ['space', (): LitHtml.TemplateResult => html``], [ 'link', (token): LitHtml.TemplateResult => html`<devtools-markdown-link .data=${ {key: token.href, title: token.text} as MarkdownLinkData}></devtools-markdown-link>`, ], [ 'image', (token): LitHtml.TemplateResult => html`<devtools-markdown-image .data=${ {key: token.href, title: token.text} as MarkdownImageData}></devtools-markdown-image>`, ], ]); // TODO(crbug.com/1108699): Fix types when they are available. // eslint-disable-next-line @typescript-eslint/no-explicit-any export const renderToken = (token: any): LitHtml.TemplateResult => { const renderFn = tokenRenderers.get(token.type); if (!renderFn) { throw new Error(`Markdown token type '${token.type}' not supported.`); } return renderFn(token); };