mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-27 18:24:21 +00:00
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:
@@ -179,7 +179,7 @@ test('matrix legs that call a reusable workflow are folded into a single matrix
|
||||
const matrixNodes = graph.nodes.filter((n) => n.type === 'matrix');
|
||||
expect(matrixNodes).toHaveLength(1);
|
||||
expect(matrixNodes[0].name).toBe('build-call');
|
||||
expect(matrixNodes[0].jobs.map((j) => j.id).sort()).toEqual([2, 3, 4]);
|
||||
expect(matrixNodes[0].jobs.map((j) => j.id).sort((a, b) => a - b)).toEqual([2, 3, 4]);
|
||||
});
|
||||
|
||||
test('directed highlight state covers ancestors and descendants of the hovered node', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import './external-render-helper.ts';
|
||||
|
||||
test('isValidCssColor', async () => {
|
||||
test('isValidCssColor', () => {
|
||||
const isValidCssColor = window.giteaExternalRenderHelper!.isValidCssColor;
|
||||
expect(isValidCssColor(null)).toBe(false);
|
||||
expect(isValidCssColor('')).toBe(false);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')!);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -13,7 +13,7 @@ function targetElement(elCode: Element): {target: Element, displayAsBlock: boole
|
||||
};
|
||||
}
|
||||
|
||||
export async function initMarkupCodeMath(elMarkup: HTMLElement): Promise<void> {
|
||||
export function initMarkupCodeMath(elMarkup: HTMLElement): void {
|
||||
// .markup code.language-math'
|
||||
queryElems(elMarkup, 'code.language-math', async (el) => {
|
||||
const [{default: katex}] = await Promise.all([
|
||||
|
||||
@@ -16,7 +16,7 @@ function initMermaidViewController(viewController: Element, dragElement: SVGSVGE
|
||||
|
||||
for (const el of viewController.querySelectorAll('[data-control-action]')) {
|
||||
el.addEventListener('click', () => {
|
||||
switch (el.getAttribute('data-control-action')) {
|
||||
switch (el.getAttribute('data-control-action')!) {
|
||||
case 'zoom-in':
|
||||
scale *= 1.2;
|
||||
break;
|
||||
|
||||
@@ -42,7 +42,7 @@ function getRealBackgroundColor(el: HTMLElement) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export async function initExternalRenderIframe(iframe: HTMLIFrameElement) {
|
||||
export function initExternalRenderIframe(iframe: HTMLIFrameElement) {
|
||||
const iframeSrcUrl = iframe.getAttribute('data-src')!;
|
||||
if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {html, htmlRaw} from '../utils/html.ts';
|
||||
|
||||
const {svgOuter, svgInnerHtml: giteaFaviconInner} = svgParseOuterInner('gitea-favicon');
|
||||
const faviconViewBox = svgOuter.getAttribute('viewBox')!;
|
||||
const [, , faviconViewBoxWidth, faviconViewBoxHeight] = faviconViewBox.split(/\s+/).map(Number);
|
||||
const [faviconViewBoxWidth, faviconViewBoxHeight] = faviconViewBox.split(/\s+/).slice(2).map(Number);
|
||||
|
||||
// the status badge is rendered in the bottom-right corner, following GitHub Actions favicon proportions
|
||||
const badgeIconSize = 16;
|
||||
@@ -45,7 +45,7 @@ function buildStatusIconMarkup(status: ActionsStatus): string {
|
||||
const {name, colorClass} = getActionStatusIcon(status, 'circle-fill');
|
||||
const color = resolveTailwindTextColor(colorClass);
|
||||
const {svgInnerHtml} = svgParseOuterInner(name);
|
||||
const coloredInner = svgInnerHtml.replaceAll('currentColor', color);
|
||||
const coloredInner = svgInnerHtml.replaceAll('currentColor', () => color);
|
||||
const ring = html`<circle cx="${badgeX + badgeCenter}" cy="${badgeY + badgeCenter}" r="${badgeRingRadius}" fill="#ffffff"/>`;
|
||||
const badge = html`<g data-actions-status-name="${status}" transform="translate(${badgeX}, ${badgeY}) scale(${badgeScale})" fill="${color}" color="${color}">${htmlRaw(coloredInner)}</g>`;
|
||||
return html`${htmlRaw(ring)}${htmlRaw(badge)}`;
|
||||
|
||||
@@ -40,11 +40,11 @@ test('execPseudoSelectorCommands', () => {
|
||||
expect(ret.targets).toEqual(Array.from(document.querySelectorAll('#d1 .x')));
|
||||
});
|
||||
|
||||
test('handleFetchActionSuccessJson', async () => {
|
||||
test('handleFetchActionSuccessJson', () => {
|
||||
const navigations = captureNavigations();
|
||||
await handleFetchActionSuccessJson(document.body, {redirect: '/'});
|
||||
await handleFetchActionSuccessJson(document.body, {redirect: ''});
|
||||
await handleFetchActionSuccessJson(document.body, {});
|
||||
handleFetchActionSuccessJson(document.body, {redirect: '/'});
|
||||
handleFetchActionSuccessJson(document.body, {redirect: ''});
|
||||
handleFetchActionSuccessJson(document.body, {});
|
||||
expect(navigations.map((n) => n.type)).toEqual(['push', 'reload', 'reload']);
|
||||
});
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ function toggleLoadingIndicator(el: HTMLElement, opt: FetchActionOpts, isLoading
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleFetchActionSuccessJson(el: HTMLElement, respJson: {redirect?: unknown} | null) {
|
||||
export function handleFetchActionSuccessJson(el: HTMLElement, respJson: {redirect?: unknown} | null) {
|
||||
ignoreAreYouSure(el); // ignore the areYouSure check before reloading
|
||||
const redirect = respJson?.redirect;
|
||||
if (typeof redirect === 'string' && redirect) {
|
||||
@@ -84,7 +84,7 @@ async function handleFetchActionSuccess(el: HTMLElement, opt: FetchActionOpts, r
|
||||
const respText = await resp.text();
|
||||
const respJson = isRespJson ? JSON.parse(respText) : null;
|
||||
if (isRespJson) {
|
||||
await handleFetchActionSuccessJson(el, respJson);
|
||||
handleFetchActionSuccessJson(el, respJson);
|
||||
} else if (opt.successSync) {
|
||||
await handleFetchActionSuccessSync(el, opt.successSync, respText);
|
||||
} else {
|
||||
|
||||
@@ -74,7 +74,7 @@ export function attachSearchBox<T = unknown>(container: HTMLElement, url: string
|
||||
if (query.length < minCharacters) return hide();
|
||||
const ctrl = (fetchController = new AbortController());
|
||||
try {
|
||||
const response = await GET(url.replaceAll('{query}', urlQueryEscape(query)), {signal: ctrl.signal});
|
||||
const response = await GET(url.replaceAll('{query}', () => urlQueryEscape(query)), {signal: ctrl.signal});
|
||||
if (!response.ok) return hide();
|
||||
const results = parse(await response.json(), query);
|
||||
// only render if the fetch wasn't aborted (e.g. by hide()) and the input still matches
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import {showSuccessToast, showInfoToast, showErrorToast, showWarningToast} from './toast.ts';
|
||||
|
||||
test('showSuccessToast', async () => {
|
||||
test('showSuccessToast', () => {
|
||||
showSuccessToast('success', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('showInfoToast', async () => {
|
||||
test('showInfoToast', () => {
|
||||
showInfoToast('info', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('showWarningToast', async () => {
|
||||
test('showWarningToast', () => {
|
||||
showWarningToast('warning', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('showErrorToast', async () => {
|
||||
test('showErrorToast', () => {
|
||||
showErrorToast('error', {duration: -1});
|
||||
expect(document.querySelector('.toastify')).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type {Promisable} from '../types.ts';
|
||||
|
||||
// there are 2 kinds of plugins:
|
||||
// * "inplace" plugins: render file content in-place, e.g. PDF viewer
|
||||
// * "frontend" plugins: render file content in a separate iframe by a huge frontend library (need to protect from XSS risks)
|
||||
@@ -18,4 +20,4 @@ export type FrontendRenderOptions = {
|
||||
contentBytes(): Uint8Array<ArrayBuffer>;
|
||||
};
|
||||
|
||||
export type FrontendRenderFunc = (opts: FrontendRenderOptions) => Promise<boolean>;
|
||||
export type FrontendRenderFunc = (opts: FrontendRenderOptions) => Promisable<boolean>;
|
||||
|
||||
@@ -4,9 +4,9 @@ import {initSwaggerUI} from '../swagger.ts';
|
||||
// HINT: SWAGGER-CSS-IMPORT: these styles are for the render only.
|
||||
import '../../../css/swagger-render.css';
|
||||
|
||||
export const frontendRender: FrontendRenderFunc = async (opts): Promise<boolean> => {
|
||||
export const frontendRender: FrontendRenderFunc = (opts) => {
|
||||
try {
|
||||
await initSwaggerUI(opts.container, {specText: opts.contentString()});
|
||||
initSwaggerUI(opts.container, {specText: opts.contentString()});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -15,7 +15,7 @@ solid SimpleTriangle
|
||||
endsolid SimpleTriangle
|
||||
*/
|
||||
|
||||
export const frontendRender: FrontendRenderFunc = async (opts): Promise<boolean> => {
|
||||
export const frontendRender: FrontendRenderFunc = (opts) => {
|
||||
try {
|
||||
opts.container.style.height = `${window.innerHeight}px`;
|
||||
const bgColor = colord(getComputedStyle(document.body).backgroundColor).toRgb();
|
||||
|
||||
@@ -15,7 +15,7 @@ function syncDarkModeClass(): void {
|
||||
document.documentElement.classList.toggle('dark-mode', isDark);
|
||||
}
|
||||
|
||||
export async function initSwaggerUI(container: HTMLElement, opts: {specText: string}): Promise<void> {
|
||||
export function initSwaggerUI(container: HTMLElement, opts: {specText: string}): void {
|
||||
// swagger-ui has built-in dark mode triggered by html.dark-mode class
|
||||
syncDarkModeClass();
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', syncDarkModeClass);
|
||||
|
||||
@@ -6,7 +6,7 @@ async function initGiteaAPIViewer() {
|
||||
const url = elSwaggerUi.getAttribute('data-source')!;
|
||||
const res = await fetch(url); // eslint-disable-line no-restricted-globals -- standalone entry, it must not pull in main site modules
|
||||
// HINT: SWAGGER-CSS-IMPORT: this is used in the standalone page which already has the related CSS imported by `<link>`
|
||||
await initSwaggerUI(elSwaggerUi, {specText: await res.text()});
|
||||
initSwaggerUI(elSwaggerUi, {specText: await res.text()});
|
||||
}
|
||||
|
||||
initGiteaAPIViewer();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {html, htmlEscape, htmlRaw} from './html.ts';
|
||||
|
||||
test('html', async () => {
|
||||
test('html', () => {
|
||||
expect(html`<a>${'<>&\'"'}</a>`).toBe(`<a><>&'"</a>`);
|
||||
expect(html`<a>${htmlRaw('<img>')}</a>`).toBe(`<a><img></a>`);
|
||||
expect(html`<a>${htmlRaw`<img ${'&'}>`}</a>`).toBe(`<a><img &></a>`);
|
||||
|
||||
Reference in New Issue
Block a user