mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-23 13:00:37 +00:00
chore: update eslint and stylelint configs and re-sync modern-normalize (#38982)
- update the vendored `modern-normalize` to v3.0.1 - require descriptions for lint disables in TS and CSS, same as we already have in Go. - disable core rules covered by `regexp/*` and `unicorn/*`, and ones that cannot fire - stop applying vitest rules to the playwright files in `tests/e2e` - enable 7 stylelint rules, mostly `no-unknown` and `no-invalid` checks - drop 2 unnecessary vendor prefixes (safari v17+, chrome v120+) - look up ids via `querySelector` with `CSS.escape` instead of `getElementById` - remove stale doc about `@ts-expect-error`, it's forbidden - misc dev doc fixes Every declaration that `modern-normalize` v3 removes was checked against chromium, webkit and firefox defaults first. The `hr` color and the `:-moz-focusring` outline are kept as documented deviations, dropping those does change rendering. --------- Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import {POST} from '../../modules/fetch.ts';
|
||||
import {registerGlobalInitFunc} from '../../modules/observer.ts';
|
||||
import {queryElems} from '../../utils/dom.ts';
|
||||
import {errorMessage} from '../../modules/errors.ts';
|
||||
import {submitFormFetchAction} from '../common-fetch-action.ts';
|
||||
import {submitFormFetchAction} from '../../modules/fetch-action.ts';
|
||||
import {cutString} from '../../utils/string.ts';
|
||||
|
||||
const {appSubUrl} = window.config;
|
||||
|
||||
@@ -71,19 +71,16 @@ export function initGlobalDropdown() {
|
||||
action: 'hide',
|
||||
onShow() {
|
||||
// hide associated tooltip while dropdown is open
|
||||
this._tippy?.hide();
|
||||
this._tippy?.disable();
|
||||
el._tippy?.hide();
|
||||
el._tippy?.disable();
|
||||
},
|
||||
onHide() {
|
||||
this._tippy?.enable();
|
||||
// eslint-disable-next-line unicorn/no-this-assignment
|
||||
const elDropdown = this;
|
||||
el._tippy?.enable();
|
||||
|
||||
// hide all tippy elements of items after a while. eg: use Enter to click "Copy Link" in the Issue Context Menu
|
||||
// hide all tippy elements of items after a while, in case some items have tooltip popup.
|
||||
setTimeout(() => {
|
||||
const $dropdown = fomanticQuery(elDropdown);
|
||||
if ($dropdown.dropdown('is hidden')) {
|
||||
queryElems(elDropdown, '.menu > .item', (el) => el._tippy?.hide());
|
||||
queryElems(el, '.menu > .item', (item) => item._tippy?.hide());
|
||||
}
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: st
|
||||
textarea.focus();
|
||||
let success = false;
|
||||
try {
|
||||
success = document.execCommand('insertText', false, text); // eslint-disable-line @typescript-eslint/no-deprecated
|
||||
success = document.execCommand('insertText', false, text); // eslint-disable-line @typescript-eslint/no-deprecated -- no replacement keeps the undo history
|
||||
} catch {}
|
||||
|
||||
// fall back to regular replacement
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {toggleElem} from '../../utils/dom.ts';
|
||||
import {showFomanticModal} from '../../modules/fomantic/modal.ts';
|
||||
import {submitFormFetchAction} from '../common-fetch-action.ts';
|
||||
import {submitFormFetchAction} from '../../modules/fetch-action.ts';
|
||||
|
||||
function nameHasScope(name: string): boolean {
|
||||
return /.*[^/]\/[^/].*/.test(name);
|
||||
|
||||
@@ -11,7 +11,7 @@ import {createTippy} from '../modules/tippy.ts';
|
||||
import {invertFileFolding} from './file-fold.ts';
|
||||
import {parseDom} from '../utils.ts';
|
||||
import {registerGlobalEventFunc, registerGlobalInitFunc} from '../modules/observer.ts';
|
||||
import {performFetchActionTrigger} from './common-fetch-action.ts';
|
||||
import {performFetchActionTrigger} from '../modules/fetch-action.ts';
|
||||
import {initImageDiff} from './imagediff.ts';
|
||||
|
||||
function initDiffFileViewToggle(el: HTMLElement) {
|
||||
@@ -206,9 +206,7 @@ async function onLocationHashChange() {
|
||||
|
||||
const targetElementId = currentHash.substring(1);
|
||||
while (currentHash === window.location.hash) {
|
||||
// use getElementById to avoid querySelector throws an error when the hash is invalid
|
||||
// eslint-disable-next-line unicorn/prefer-query-selector
|
||||
const targetElement = document.getElementById(targetElementId);
|
||||
const targetElement = document.querySelector<HTMLElement>(`#${CSS.escape(targetElementId)}`);
|
||||
if (targetElement) {
|
||||
// need to change hash to re-trigger ":target" CSS selector, let's manually scroll to it
|
||||
targetElement.scrollIntoView();
|
||||
|
||||
@@ -6,7 +6,7 @@ import {POST} from '../modules/fetch.ts';
|
||||
import {initDropzone} from './dropzone.ts';
|
||||
import {confirmModal} from './comp/ConfirmModal.ts';
|
||||
import {applyAreYouSure, ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
||||
import {submitFormFetchAction} from './common-fetch-action.ts';
|
||||
import {submitFormFetchAction} from '../modules/fetch-action.ts';
|
||||
import {dirname} from '../utils.ts';
|
||||
import {pathEscapeSegments} from '../utils/url.ts';
|
||||
import {showErrorToast} from '../modules/toast.ts';
|
||||
|
||||
@@ -4,7 +4,7 @@ import {confirmModal} from './comp/ConfirmModal.ts';
|
||||
import {createSortable} from '../modules/sortable.ts';
|
||||
import {DELETE, POST} from '../modules/fetch.ts';
|
||||
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||
import {performFetchAction} from './common-fetch-action.ts';
|
||||
import {performFetchAction} from '../modules/fetch-action.ts';
|
||||
import type {SortableEvent} from 'sortablejs';
|
||||
|
||||
function initRepoIssueListCheckboxes() {
|
||||
|
||||
@@ -48,7 +48,7 @@ async function initRepoProjectSortable(): Promise<void> {
|
||||
handle: '.project-column-header',
|
||||
delayOnTouchOnly: true,
|
||||
delay: 500,
|
||||
onSort: async () => { // eslint-disable-line @typescript-eslint/no-misused-promises
|
||||
onSort: async () => { // eslint-disable-line @typescript-eslint/no-misused-promises -- Sortable ignores the returned promise, the body catches its own errors
|
||||
boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
|
||||
|
||||
const columnSorting = {
|
||||
@@ -72,8 +72,8 @@ async function initRepoProjectSortable(): Promise<void> {
|
||||
const boardCardList = boardColumn.querySelector<HTMLElement>('.cards')!;
|
||||
createSortable(boardCardList, {
|
||||
group: 'shared',
|
||||
onAdd: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises
|
||||
onUpdate: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises
|
||||
onAdd: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises -- Sortable ignores the returned promise, moveIssue catches its own errors
|
||||
onUpdate: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises -- Sortable ignores the returned promise, moveIssue catches its own errors
|
||||
delayOnTouchOnly: true,
|
||||
delay: 500,
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function attachTribute(element: HTMLElement) {
|
||||
};
|
||||
|
||||
const mentionCollection: TributeCollection<Mention> = {
|
||||
values: async (_query: string, cb: (matches: Mention[]) => void) => { // eslint-disable-line @typescript-eslint/no-misused-promises
|
||||
values: async (_query: string, cb: (matches: Mention[]) => void) => { // eslint-disable-line @typescript-eslint/no-misused-promises -- tributejs ignores the returned promise, results arrive via the callback
|
||||
cb(mentionsUrl ? await fetchMentions(mentionsUrl) : []);
|
||||
},
|
||||
requireLeadingSpace: true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import jquery from 'jquery'; // eslint-disable-line no-restricted-imports
|
||||
import jquery from 'jquery'; // eslint-disable-line no-restricted-imports -- this is where the global $ comes from
|
||||
|
||||
// Some users still use inline scripts and expect jQuery to be available globally.
|
||||
// To avoid breaking existing users and custom plugins, import jQuery globally without ES module.
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ import {initRepositorySearch} from './features/repo-search.ts';
|
||||
import {initColorPickers} from './features/colorpicker.ts';
|
||||
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
||||
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
||||
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
||||
import {initGlobalFetchAction} from './modules/fetch-action.ts';
|
||||
import {initCommmPageComponents, initGlobalComponent, initGlobalDropdown, initGlobalInput} from './features/common-page.ts';
|
||||
import {initGlobalButtonClickOnEnter, initGlobalButtons} from './features/common-button.ts';
|
||||
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
||||
|
||||
@@ -29,15 +29,13 @@ function scrollToAnchor(encodedId?: string): void {
|
||||
if (!elemId) return;
|
||||
|
||||
const prefixedId = addPrefix(elemId);
|
||||
// eslint-disable-next-line unicorn/prefer-query-selector
|
||||
let el = document.getElementById(prefixedId);
|
||||
let el = document.querySelector<HTMLElement>(`#${CSS.escape(prefixedId)}`);
|
||||
|
||||
// check for matching user-generated `a[name]`
|
||||
el = el ?? document.querySelector(`a[name="${CSS.escape(prefixedId)}"]`);
|
||||
|
||||
// compat for links with old 'user-content-' prefixed hashes
|
||||
// eslint-disable-next-line unicorn/prefer-query-selector
|
||||
el = (!el && hasPrefix(elemId)) ? document.getElementById(elemId) : el;
|
||||
el = (!el && hasPrefix(elemId)) ? document.querySelector<HTMLElement>(`#${CSS.escape(elemId)}`) : el;
|
||||
|
||||
el?.scrollIntoView();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('navigateToIframeLink', () => {
|
||||
const navigations = captureNavigations();
|
||||
const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
|
||||
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
// eslint-disable-next-line no-script-url
|
||||
// eslint-disable-next-line no-script-url -- the test asserts that javascript: links are rejected
|
||||
navigateToIframeLink('javascript:void(0);', '_blank');
|
||||
navigateToIframeLink('data:image/svg+xml;utf8,<svg></svg>', '');
|
||||
expect(openSpy).toHaveBeenCalledTimes(0);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import {execPseudoSelectorCommands, handleFetchActionErrorFields, handleFetchActionSuccessJson} from './common-fetch-action.ts';
|
||||
import {execPseudoSelectorCommands, handleFetchActionErrorFields, handleFetchActionSuccessJson} from './fetch-action.ts';
|
||||
import {createElementFromHTML} from '../utils/dom.ts';
|
||||
import {captureNavigations, normalizeTestHtml} from '../utils/testhelper.ts';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {GET, request} from '../modules/fetch.ts';
|
||||
import {hideToastsAll, showErrorToast} from '../modules/toast.ts';
|
||||
import {GET, request} from './fetch.ts';
|
||||
import {hideToastsAll, showErrorToast} from './toast.ts';
|
||||
import {activePageTimerRefresh, addDelegatedEventListener, createElementFromHTML, queryElems} from '../utils/dom.ts';
|
||||
import {errorMessage, errorName} from '../modules/errors.ts';
|
||||
import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts';
|
||||
import {errorMessage, errorName} from './errors.ts';
|
||||
import {confirmModal, createConfirmModal} from '../features/comp/ConfirmModal.ts';
|
||||
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
||||
import {registerGlobalSelectorFunc} from '../modules/observer.ts';
|
||||
import {registerGlobalSelectorFunc} from './observer.ts';
|
||||
import {Idiomorph} from 'idiomorph';
|
||||
import {parseDom} from '../utils.ts';
|
||||
import {html} from '../utils/html.ts';
|
||||
@@ -233,9 +233,8 @@ export async function submitFormFetchAction(elForm: HTMLFormElement, opts: Submi
|
||||
async function confirmFetchAction(el: HTMLElement) {
|
||||
let elModal: HTMLElement | null = null;
|
||||
const dataModalConfirm = el.getAttribute('data-modal-confirm') || '';
|
||||
if (dataModalConfirm.startsWith('#')) {
|
||||
// eslint-disable-next-line unicorn/prefer-query-selector
|
||||
elModal = document.getElementById(dataModalConfirm.substring(1));
|
||||
if (dataModalConfirm.startsWith('#') && dataModalConfirm.length > 1) {
|
||||
elModal = document.querySelector<HTMLElement>(`#${CSS.escape(dataModalConfirm.substring(1))}`);
|
||||
if (elModal) {
|
||||
elModal = createElementFromHTML(elModal.outerHTML);
|
||||
elModal.removeAttribute('id');
|
||||
@@ -350,8 +349,7 @@ async function fetchActionReloadOutdatedElements() {
|
||||
const newPageHtml = await resp.text();
|
||||
const newPageDom = parseDom(newPageHtml, 'text/html');
|
||||
for (const oldEl of outdatedElems) {
|
||||
// eslint-disable-next-line unicorn/prefer-query-selector
|
||||
const newEl = newPageDom.getElementById(oldEl.id);
|
||||
const newEl = newPageDom.querySelector<HTMLElement>(`#${CSS.escape(oldEl.id)}`);
|
||||
if (newEl) {
|
||||
oldEl.replaceWith(newEl);
|
||||
} else {
|
||||
@@ -18,7 +18,7 @@ export function request(url: string, {method = 'GET', data, headers = {}, ...oth
|
||||
if (!headers.has('content-type') && contentType) {
|
||||
headers.set('content-type', contentType);
|
||||
}
|
||||
return fetch(url, { // eslint-disable-line no-restricted-globals
|
||||
return fetch(url, { // eslint-disable-line no-restricted-globals -- this is the wrapper the rule points to
|
||||
method,
|
||||
headers,
|
||||
...other,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
/* eslint-disable no-restricted-globals -- this is the wrapper the rule points to */
|
||||
// Some people deploy Gitea under a subpath, so it needs prefix to avoid local storage key conflicts.
|
||||
// And these keys are for user settings only, it also needs a specific prefix,
|
||||
// in case in the future there are other uses of local storage, and/or we need to clear some keys when the quota is exceeded.
|
||||
|
||||
@@ -24,7 +24,7 @@ let lastWorker: MockSharedWorker;
|
||||
|
||||
class MockSharedWorker {
|
||||
port = new MockMessagePort();
|
||||
// eslint-disable-next-line unicorn/no-this-assignment
|
||||
// eslint-disable-next-line unicorn/no-this-assignment -- the test needs a handle on the instance the module constructs
|
||||
constructor() { lastWorker = this }
|
||||
addEventListener() {}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {initSwaggerUI} from './render/swagger.ts';
|
||||
async function initGiteaAPIViewer() {
|
||||
const elSwaggerUi = document.querySelector<HTMLElement>('#swagger-ui')!;
|
||||
const url = elSwaggerUi.getAttribute('data-source')!;
|
||||
const res = await fetch(url); // eslint-disable-line no-restricted-globals
|
||||
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()});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ const pngPhys = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
|
||||
const pngEmpty = 'data:image/png;base64,';
|
||||
|
||||
async function dataUriToBlob(datauri: string) {
|
||||
return await (await globalThis.fetch(datauri)).blob(); // eslint-disable-line no-restricted-properties
|
||||
return await (await globalThis.fetch(datauri)).blob(); // eslint-disable-line no-restricted-properties -- decodes a data URI, the fetch wrapper adds nothing here
|
||||
}
|
||||
|
||||
test('pngChunks', async () => {
|
||||
|
||||
@@ -236,9 +236,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
|
||||
// check whether the mandatory `.overflow-menu-items` element is present initially which happens
|
||||
// with Vue which renders differently than browsers. If it's not there, like in the case of browser
|
||||
// template rendering, wait for its addition.
|
||||
// The eslint rule is not sophisticated enough or aware of this problem, see
|
||||
// https://github.com/43081j/eslint-plugin-wc/pull/130
|
||||
const menuItemsEl = this.querySelector<HTMLElement>('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback
|
||||
const menuItemsEl = this.querySelector<HTMLElement>('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback -- the observer below covers the case the rule warns about, see https://github.com/43081j/eslint-plugin-wc/pull/130
|
||||
if (menuItemsEl) {
|
||||
this.menuItemsEl = menuItemsEl;
|
||||
this.init();
|
||||
|
||||
Reference in New Issue
Block a user