chore: update eslint, enable more rules and fix their findings (#39438)

Update eslint and its plugins, enable more rules and fix their findings:

1. `unicorn/no-unsafe-string-replacement` found that uploading a file
whose name contains `$&` inserted a broken markdown link, because
`String#replace` expands such patterns in the replacement string
2. `@typescript-eslint/require-await` removes `async` from functions
that never await
3. Plugin rules not covered by a preset are now listed explicitly

---------

Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
silverwind
2026-09-26 07:20:39 +00:00
committed by GitHub
co-authored by bircni
parent a15f032026
commit 857d3d3df3
35 changed files with 299 additions and 197 deletions
+1 -1
View File
@@ -289,7 +289,7 @@ function initAdminNotice() {
const checkboxes = document.querySelectorAll<HTMLInputElement>('.select.table .ui.checkbox input');
queryElems(pageContent, '.select.action', (el) => el.addEventListener('click', () => {
switch (el.getAttribute('data-action')) {
switch (el.getAttribute('data-action')!) {
case 'select-all':
for (const checkbox of checkboxes) {
checkbox.checked = true;
+1 -1
View File
@@ -12,7 +12,7 @@ export async function initCaptcha() {
theme: isDark ? 'dark' : 'light',
};
switch (captchaEl.getAttribute('data-captcha-type')) {
switch (captchaEl.getAttribute('data-captcha-type') ?? '') {
case 'g-recaptcha': {
if (window.grecaptcha) {
window.grecaptcha.ready(() => {
+1 -1
View File
@@ -24,7 +24,7 @@ async function initInputCitationValue(citationCopyApa: HTMLButtonElement, citati
citationCopyApa.setAttribute('data-text', apaOutput);
}
export async function initCitationFileCopyContent() {
export function initCitationFileCopyContent() {
const defaultCitationFormat = 'apa'; // apa or bibtex
if (!pageData.citationFileContent) return;
+1 -1
View File
@@ -1,7 +1,7 @@
import {createTippy} from '../modules/tippy.ts';
import {registerGlobalInitFunc} from '../modules/observer.ts';
export async function initColorPickers() {
export function initColorPickers() {
let imported = false;
registerGlobalInitFunc('initColorPicker', async (el) => {
if (!imported) {
+2 -2
View File
@@ -56,7 +56,7 @@ class TextareaEditor {
editor.value = editor.value.substring(0, startPos) + newVal + editor.value.substring(endPos);
editor.selectionEnd = startPos + newVal.length;
} else {
editor.value = editor.value.replace(oldVal, newVal);
editor.value = editor.value.replace(oldVal, () => newVal);
editor.selectionEnd -= oldVal.length;
editor.selectionEnd += newVal.length;
}
@@ -90,7 +90,7 @@ class CodeMirrorEditor {
if (editor.getSelection() === oldVal) {
editor.replaceSelection(newVal);
} else {
editor.setValue(editor.getValue().replace(oldVal, newVal));
editor.setValue(editor.getValue().replace(oldVal, () => newVal));
}
endPoint.ch -= oldVal.length;
endPoint.ch += newVal.length;
+1 -1
View File
@@ -3,7 +3,7 @@ import {toggleElem} from '../utils/dom.ts';
import {registerGlobalEventFunc, registerGlobalInitFunc} from '../modules/observer.ts';
export function initRepoEllipsisButton() {
registerGlobalEventFunc('click', 'onRepoEllipsisButtonClick', async (el: HTMLInputElement, e: Event) => {
registerGlobalEventFunc('click', 'onRepoEllipsisButtonClick', (el: HTMLInputElement, e: Event) => {
e.preventDefault();
const expanded = el.getAttribute('aria-expanded') === 'true';
toggleElem(el.parentElement!.querySelector('.commit-body')!);
+1 -1
View File
@@ -49,7 +49,7 @@ export function substituteRepoOpenWithUrl(tmpl: string, url: string): string {
if (pos === -1) return tmpl;
const posQuestionMark = tmpl.indexOf('?');
const needEncode = posQuestionMark >= 0 && posQuestionMark < pos;
return tmpl.replace('{url}', needEncode ? encodeURIComponent(url) : url);
return tmpl.replace('{url}', () => needEncode ? encodeURIComponent(url) : url);
}
function initRepoCloneButtonsCombo(parent: Element) {
+2 -2
View File
@@ -95,7 +95,7 @@ function initDropdownUserRemoteSearch(el: Element) {
fullTextSearch: true,
selectOnKeydown: false,
action: (_text: string, value: string) => {
window.location.assign(actionJumpUrl.replace('{username}', encodeURIComponent(value)));
window.location.assign(actionJumpUrl.replace('{username}', () => encodeURIComponent(value)));
},
});
@@ -166,7 +166,7 @@ async function pinMoveEnd(e: SortableEvent) {
await POST(url, {data: {id, position: e.newIndex! + 1}});
}
async function initIssuePinSort() {
function initIssuePinSort() {
const pinDiv = document.querySelector<HTMLElement>('#issue-pins');
if (pinDiv === null) return;
@@ -151,7 +151,7 @@ export class IssueSidebarComboList {
}
}
async onItemClick(elItem: HTMLElement, e: Event) {
onItemClick(elItem: HTMLElement, e: Event) {
e.preventDefault();
if (elItem.hasAttribute('data-can-change') && elItem.getAttribute('data-can-change') !== 'true') return;
@@ -184,7 +184,7 @@ export class IssueSidebarComboList {
this.onChange();
}
async onHide() {
onHide() {
if (this.selectionMode === 'multiple') this.doUpdate();
}
+1 -1
View File
@@ -20,7 +20,7 @@ import RepoBranchTagSelector from '../components/RepoBranchTagSelector.vue';
import {initRepoPullMergeBox, initRepoPullRequestUpdate} from './repo-issue-pull.ts';
function initRepoBranchTagSelector() {
registerGlobalInitFunc('initRepoBranchTagSelector', async (elRoot: HTMLInputElement) => {
registerGlobalInitFunc('initRepoBranchTagSelector', (elRoot: HTMLInputElement) => {
createApp(RepoBranchTagSelector, {elRoot}).mount(elRoot);
});
}
+2 -2
View File
@@ -39,7 +39,7 @@ async function moveIssue({item, from, to, oldIndex}: SortableEvent): Promise<voi
}
}
async function initRepoProjectSortable(): Promise<void> {
function initRepoProjectSortable(): void {
// the HTML layout is: #project-board.board > .project-column .cards > .issue-card
const mainBoard = document.querySelector<HTMLElement>('#project-board')!;
let boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
@@ -182,7 +182,7 @@ export function initRepoProjectsView(): void {
const writableProjectBoard = document.querySelector('#project-board[data-project-board-writable="true"]');
if (!writableProjectBoard) return;
initRepoProjectSortable(); // no await
initRepoProjectSortable();
initRepoProjectColumnEdit(writableProjectBoard);
});
}
+1 -1
View File
@@ -1,6 +1,6 @@
import {guessPreviousReleaseTag} from './repo-release.ts';
test('guessPreviousReleaseTag', async () => {
test('guessPreviousReleaseTag', () => {
expect(guessPreviousReleaseTag('v0.9', ['v1.0', 'v1.2', 'v1.4', 'v1.6'])).toBe('');
expect(guessPreviousReleaseTag('1.3', ['v1.0', 'v1.2', 'v1.4', 'v1.6'])).toBe('v1.2');
expect(guessPreviousReleaseTag('rel/1.3', ['v1.0', 'v1.2', 'v1.4', 'v1.6'])).toBe('v1.2');
+1 -1
View File
@@ -25,7 +25,7 @@ async function toggleSidebar(btn: HTMLElement) {
});
}
export async function initRepoViewFileTree() {
export function initRepoViewFileTree() {
const sidebar = document.querySelector<HTMLElement>('.repo-view-file-tree-container');
const repoViewContent = document.querySelector('.repo-view-content');
if (!sidebar || !repoViewContent) return;
+1 -1
View File
@@ -8,7 +8,7 @@ const {appSubUrl} = window.config;
/** One of the possible values for the `data-webauthn-error-msg` attribute on the webauthn error message element */
type ErrorType = 'general' | 'insecure' | 'browser' | 'unable-to-process' | 'duplicated' | 'unknown';
export async function initUserAuthWebAuthn() {
export function initUserAuthWebAuthn() {
const elPrompt = document.querySelector('.user.signin.webauthn-prompt');
const elSignInPasskeyBtn = document.querySelector('.signin-passkey');
if (!elPrompt && !elSignInPasskeyBtn) {