chore: migrate unescaped-html-literal eslint rule to our repo and fix more cases (#38072)

This commit is contained in:
wxiaoguang
2026-06-11 22:37:22 +08:00
committed by GitHub
parent 360f34d7fa
commit 250a38abb5
10 changed files with 184 additions and 45 deletions
+1
View File
@@ -848,6 +848,7 @@ table th[data-sortt-desc] .svg {
}
/* this is useful to make a left-right (e.g.: title .... operations) layout with default gap, and it wrap for small widths */
.ui.modal .header.flex-left-right,
.flex-left-right {
display: flex;
flex-wrap: wrap;
+6 -5
View File
@@ -1,4 +1,4 @@
import {svg} from '../svg.ts';
import {svgRaw} from '../svg.ts';
import {html} from '../utils/html.ts';
import {copyToClipboardWithFeedback} from '../modules/clipboard.ts';
import {GET, POST} from '../modules/fetch.ts';
@@ -45,10 +45,11 @@ export function generateMarkdownLinkForAttachment(file: Partial<CustomDropzoneFi
function addCopyLink(file: Partial<CustomDropzoneFile>) {
// Create a "Copy Link" element, to conveniently copy the image or file link as Markdown to the clipboard
// The "<a>" element has a hardcoded cursor: pointer because the default is overridden by .dropzone
const copyLinkEl = createElementFromHTML<HTMLDivElement>(`
<div class="tw-text-center">
<a href="#" class="tw-cursor-pointer">${svg('octicon-copy', 14)} Copy link</a>
</div>`);
const copyLinkEl = createElementFromHTML<HTMLDivElement>(html`
<div class="tw-text-center">
<a href="#" class="tw-cursor-pointer">${svgRaw('octicon-copy', 14)} Copy link</a>
</div>
`);
copyLinkEl.addEventListener('click', async (e) => {
e.preventDefault();
await copyToClipboardWithFeedback(copyLinkEl, generateMarkdownLinkForAttachment(file));
+22 -19
View File
@@ -1,10 +1,11 @@
import {svg} from '../svg.ts';
import {svgRaw} from '../svg.ts';
import {showErrorToast} from '../modules/toast.ts';
import {GET, POST} from '../modules/fetch.ts';
import {createElementFromHTML, showElem} from '../utils/dom.ts';
import {parseIssuePageInfo} from '../utils.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {hideFomanticModal, showFomanticModal} from '../modules/fomantic/modal.ts';
import {html, htmlRaw} from '../utils/html.ts';
let i18nTextEdited: string;
let i18nTextOptions: string;
@@ -12,21 +13,22 @@ let i18nTextDeleteFromHistory: string;
let i18nTextDeleteFromHistoryConfirm: string;
function showContentHistoryDetail(issueBaseUrl: string, commentId: string, historyId: string, itemTitleHtml: string) {
const elDetailDialog = createElementFromHTML(`
<div class="ui modal content-history-detail-dialog">
${svg('octicon-x', 16, 'close icon inside')}
<div class="header flex-left-right">
<div>${itemTitleHtml}</div>
<div class="ui dropdown dialog-header-options tw-mr-8 tw-hidden">
${i18nTextOptions}
${svg('octicon-triangle-down', 14, 'dropdown icon')}
<div class="menu">
<div class="item tw-text-red" data-option-item="delete">${i18nTextDeleteFromHistory}</div>
const elDetailDialog = createElementFromHTML(html`
<div class="ui modal content-history-detail-dialog">
${svgRaw('octicon-x', 16, 'close icon inside')}
<div class="header flex-left-right">
<div>${htmlRaw(itemTitleHtml)}</div>
<div class="ui dropdown dialog-header-options tw-mr-8 tw-hidden">
${i18nTextOptions}
${svgRaw('octicon-triangle-down', 14, 'dropdown icon')}
<div class="menu">
<div class="item tw-text-red" data-option-item="delete">${i18nTextDeleteFromHistory}</div>
</div>
</div>
</div>
<div class="comment-diff-data is-loading"></div>
</div>
</div>
<div class="comment-diff-data is-loading"></div>
</div>`);
`);
document.body.append(elDetailDialog);
const elOptionsDropdown = elDetailDialog.querySelector('.ui.dropdown.dialog-header-options')!;
const $fomanticDropdownOptions = fomanticQuery(elOptionsDropdown);
@@ -93,12 +95,13 @@ function showContentHistoryDetail(issueBaseUrl: string, commentId: string, histo
function showContentHistoryMenu(issueBaseUrl: string, elCommentItem: Element, commentId: string) {
const elHeaderLeft = elCommentItem.querySelector('.comment-header-left')!;
const menuHtml = `
<div class="ui dropdown interact-fg content-history-menu tw-flex-shrink-0" data-comment-id="${commentId}">
&bull; ${i18nTextEdited}${svg('octicon-triangle-down', 14, 'dropdown icon')}
<div class="menu">
const menuHtml = html`
<div class="ui dropdown interact-fg content-history-menu tw-flex-shrink-0" data-comment-id="${commentId}">
&bull; ${i18nTextEdited}${svgRaw('octicon-triangle-down', 14, 'dropdown icon')}
<div class="menu">
</div>
</div>
</div>`;
`;
elHeaderLeft.querySelector(`.ui.dropdown.content-history-menu`)?.remove(); // remove the old one if exists
elHeaderLeft.append(createElementFromHTML(menuHtml));
+8 -10
View File
@@ -1,5 +1,5 @@
import {errorMessage} from '../modules/errors.ts';
import {htmlEscape} from '../utils/html.ts';
import {html, htmlEscape, htmlRaw} from '../utils/html.ts';
import {createTippy} from '../modules/tippy.ts';
import {
addDelegatedEventListener,
@@ -274,15 +274,13 @@ export function initRepoPullRequestReview() {
let ntr = tr.nextElementSibling;
if (!ntr?.classList.contains('add-comment')) {
ntr = createElementFromHTML(`
<tr class="add-comment" data-line-type="${htmlEscape(lineType)}">
${isSplit ? `
<td class="add-comment-left" colspan="4"></td>
<td class="add-comment-right" colspan="4"></td>
` : `
<td class="add-comment-left add-comment-right" colspan="5"></td>
`}
</tr>`);
const tdSplit = html`<td class="add-comment-left" colspan="4"></td><td class="add-comment-right" colspan="4"></td>`;
const tdUnified = html`<td class="add-comment-left add-comment-right" colspan="5"></td>`;
ntr = createElementFromHTML(html`
<tr class="add-comment" data-line-type="${lineType}">
${isSplit ? htmlRaw(tdSplit) : htmlRaw(tdUnified)}
</tr>
`);
tr.after(ntr);
}
const td = ntr.querySelector(`.add-comment-${side}`)!;
+8 -8
View File
@@ -1,5 +1,5 @@
import {htmlEscape} from '../utils/html.ts';
import {svg} from '../svg.ts';
import {html, htmlEscape, htmlRaw} from '../utils/html.ts';
import {svgRaw} from '../svg.ts';
import {animateOnce, queryElems, showElem} from '../utils/dom.ts';
import Toastify from 'toastify-js'; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown
import type {Intent} from '../types.ts';
@@ -44,9 +44,8 @@ type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast};
/** See https://github.com/apvarun/toastify-js#api for options */
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast | null {
const body = useHtmlBody ? message : htmlEscape(message);
const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${message}` : preventDuplicates) : '';
// prevent showing duplicate toasts with the same level and message, and give visual feedback for end users
if (preventDuplicates) {
@@ -61,12 +60,13 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
}
const {icon, background, duration: levelDuration} = levels[level ?? 'info'];
const bodyHtml = useHtmlBody ? message : htmlEscape(message);
const toast = Toastify({
selector: parent,
text: `
<div class='toast-icon'>${svg(icon)}</div>
<div class='toast-body'><span class="toast-duplicate-number tw-hidden">1</span>${body}</div>
<button class='btn toast-close'>${svg('octicon-x')}</button>
text: html`
<div class='toast-icon'>${svgRaw(icon)}</div>
<div class='toast-body'><span class="toast-duplicate-number tw-hidden">1</span>${htmlRaw(bodyHtml)}</div>
<button class='btn toast-close'>${svgRaw('octicon-x')}</button>
`,
escapeMarkup: false,
gravity: gravity ?? 'top',
+4
View File
@@ -199,6 +199,10 @@ export function svg(name: SvgName, size = 16, classNames?: string | string[]): s
return serializeXml(svgNode);
}
export function svgRaw(name: SvgName, size = 16, classNames?: string | string[]) {
return htmlRaw(svg(name, size, classNames));
}
export function svgParseOuterInner(name: SvgName) {
const svgStr = svgs[name];
if (!svgStr) throw new Error(`Unknown SVG icon: ${name}`);