chore: upgrade eslint plugins, remove eslint-plugin-github

- Upgrade `typescript-eslint` to 8.61 and `eslint-plugin-unicorn` to 65,
  configuring the rules introduced in unicorn 65.
- Remove `eslint-plugin-github` and its workarounds: rules, the type-stub
  module declaration, the pnpm peer-dependency override, and the in-code
  `eslint-disable` comments that referenced its rules.
- Remove rules deprecated or deleted in their plugins and switch to their
  replacements (e.g. `prefer-single-call`, `no-object-constructor`,
  `id-denylist`, `no-unnecessary-slice-end`).
- Apply the resulting autofixes, mainly wrapping interpolated values in CSS
  selectors with `CSS.escape()`.

Assisted-by: claude-code:opus-4.8
This commit is contained in:
silverwind
2026-06-09 12:04:49 +02:00
parent 49a0d19fa3
commit 1b23df42df
18 changed files with 334 additions and 626 deletions
+2 -2
View File
@@ -5,14 +5,14 @@ export function initAdminUserListSearchForm(): void {
const form = document.querySelector<HTMLFormElement>('#user-list-search-form');
if (!form) return;
for (const button of form.querySelectorAll(`button[name=sort][value="${searchForm.SortType}"]`)) {
for (const button of form.querySelectorAll(`button[name=sort][value="${CSS.escape(searchForm.SortType)}"]`)) {
button.classList.add('active');
}
if (searchForm.StatusFilterMap) {
for (const [k, v] of Object.entries(searchForm.StatusFilterMap)) {
if (!v) continue;
for (const input of form.querySelectorAll<HTMLInputElement>(`input[name="status_filter[${k}]"][value="${v}"]`)) {
for (const input of form.querySelectorAll<HTMLInputElement>(`input[name="status_filter[${CSS.escape(k)}]"][value="${CSS.escape(v)}"]`)) {
input.checked = true;
}
}
+2 -2
View File
@@ -24,7 +24,7 @@ export function initGlobalDeleteButton(): void {
btn.addEventListener('click', (e) => {
e.preventDefault();
// eslint-disable-next-line github/no-dataset -- code depends on the camel-casing
// `dataset` is used here because the code below depends on it
const dataObj = btn.dataset;
const modalId = btn.getAttribute('data-modal-id');
@@ -156,7 +156,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
const [attrTargetName, attrTargetProp] = attrTargetCombo.split('.');
// try to find target by: "#target" -> "[name=target]" -> ".target" -> "<target> tag", and then try the modal itself
const attrTarget = elModal.querySelector(`#${attrTargetName}`) ||
elModal.querySelector(`[name=${attrTargetName}]`) ||
elModal.querySelector(`[name=${CSS.escape(attrTargetName)}]`) ||
elModal.querySelector(`.${attrTargetName}`) ||
elModal.querySelector(`${attrTargetName}`) ||
(elModal.matches(`${attrTargetName}`) || elModal.matches(`#${attrTargetName}`) || elModal.matches(`.${attrTargetName}`) ? elModal : null);
+1 -1
View File
@@ -245,7 +245,7 @@ async function onLocationHashChange() {
const issueCommentPrefix = '#issuecomment-';
if (currentHash.startsWith(issueCommentPrefix)) {
const commentId = currentHash.substring(issueCommentPrefix.length);
const expandButton = document.querySelector<HTMLElement>(`.code-expander-button[data-hidden-comment-ids*=",${commentId},"]`);
const expandButton = document.querySelector<HTMLElement>(`.code-expander-button[data-hidden-comment-ids*=",${CSS.escape(commentId)},"]`);
if (expandButton) {
// avoid infinite loop, do not re-click the button if already clicked
const attrAutoLoadClicked = 'data-auto-load-clicked';
+1 -1
View File
@@ -192,7 +192,7 @@ function initPinRemoveButton() {
// Delete the tooltip
el._tippy.destroy();
// Remove the Card
el.closest(`div.issue-card[data-issue-id="${id}"]`)!.remove();
el.closest(`div.issue-card[data-issue-id="${CSS.escape(String(id))}"]`)!.remove();
}
});
}
+2 -2
View File
@@ -130,9 +130,9 @@ export function initRepoIssueCommentDelete() {
// on the Conversation page, there is no parent "tr", so no need to do anything for "add-code-comment"
if (lineType) {
if (lineType === 'same') {
document.querySelector(`[data-path="${path}"] .add-code-comment[data-idx="${idx}"]`)!.classList.remove('tw-invisible');
document.querySelector(`[data-path="${CSS.escape(String(path))}"] .add-code-comment[data-idx="${CSS.escape(String(idx))}"]`)!.classList.remove('tw-invisible');
} else {
document.querySelector(`[data-path="${path}"] .add-code-comment[data-side="${side}"][data-idx="${idx}"]`)!.classList.remove('tw-invisible');
document.querySelector(`[data-path="${CSS.escape(String(path))}"] .add-code-comment[data-side="${CSS.escape(String(side))}"][data-idx="${CSS.escape(String(idx))}"]`)!.classList.remove('tw-invisible');
}
}
conversationHolder.remove();
+2 -2
View File
@@ -120,11 +120,11 @@ function initRepoProjectColumnEdit(writableProjectBoard: Element): void {
}
// update the newly saved column title and color in the project board (to avoid reload)
const elEditButton = writableProjectBoard.querySelector<HTMLButtonElement>(`.show-project-column-modal-edit[${attrDataColumnId}="${columnId}"]`)!;
const elEditButton = writableProjectBoard.querySelector<HTMLButtonElement>(`.show-project-column-modal-edit[${CSS.escape(attrDataColumnId)}="${CSS.escape(columnId)}"]`)!;
elEditButton.setAttribute(attrDataColumnTitle, elColumnTitle.value);
elEditButton.setAttribute(attrDataColumnColor, elColumnColor.value);
const elBoardColumn = writableProjectBoard.querySelector<HTMLElement>(`.project-column[data-id="${columnId}"]`)!;
const elBoardColumn = writableProjectBoard.querySelector<HTMLElement>(`.project-column[data-id="${CSS.escape(columnId)}"]`)!;
const elBoardColumnTitle = elBoardColumn.querySelector<HTMLElement>(`.project-column-title-text`)!;
elBoardColumnTitle.textContent = elColumnTitle.value;
if (elColumnColor.value) {
+1 -1
View File
@@ -12,7 +12,7 @@ export function initRepoReleaseNew() {
registerGlobalEventFunc('click', 'onReleaseEditAttachmentDelete', (el) => {
const uuid = el.getAttribute('data-uuid')!;
const id = el.getAttribute('data-id')!;
document.querySelector<HTMLInputElement>(`input[name='attachment-del-${uuid}']`)!.value = 'true';
document.querySelector<HTMLInputElement>(`input[name='attachment-del-${CSS.escape(uuid)}']`)!.value = 'true';
hideElem(`#attachment-${id}`);
});
registerGlobalInitFunc('initReleaseEditForm', (elForm: HTMLFormElement) => {
+1 -1
View File
@@ -188,7 +188,7 @@ function webAuthnError(errorType: ErrorType, message:string = '') {
if (errorType === 'general') {
elErrorMsg.textContent = message || 'unknown error';
} else {
const elTypedError = document.querySelector(`#webauthn-error [data-webauthn-error-msg=${errorType}]`);
const elTypedError = document.querySelector(`#webauthn-error [data-webauthn-error-msg=${CSS.escape(errorType)}]`);
if (elTypedError) {
elErrorMsg.textContent = `${elTypedError.textContent}${message ? ` ${message}` : ''}`;
} else {