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:
silverwind
2026-08-20 15:57:56 +02:00
committed by GitHub
parent c778c6c920
commit 61be9fcdfa
32 changed files with 97 additions and 137 deletions
+9 -6
View File
@@ -6,7 +6,7 @@ and testing see [development.md](development.md) and [testing.md](testing.md).
## Background
The frontend uses [Vue 3](https://vuejs.org/), [Fomantic-UI](https://fomantic-ui.com/) (built on jQuery)
The frontend uses [Vue 3](https://vuejs.org/), hard-forked Fomantic-UI (built on jQuery)
and [Tailwind CSS](https://tailwindcss.com/). Pages are rendered with Go HTML templates.
Source files live in:
@@ -44,8 +44,10 @@ Gitea uses Vue 3 **without** JSX to keep HTML and JavaScript separate.
## Gitea-specific conventions
- Keep features in their own files or directories.
- Use kebab-case for HTML `id`s and classes, ideally with 2-3 feature keywords.
- Use kebab-case for HTML `id`s and classes with 2-3 feature keywords.
- Prefix classes to avoid short-name conflicts between different frameworks.
- Our framework can automatically link "input" and "label" if they are the children of a `.field` element,
no need to write `id`/`for` attributes for them unless there are reasons to do so.
- Create a new class name when overriding framework styles instead of editing the framework's own classes,
or fix the framework's source to fix all cases.
- Prefer semantic elements such as `<button>` over generic `<div>`s.
@@ -68,17 +70,18 @@ Write class attributes as a single readable unit in templates:
## TypeScript
- Use `import type` for type-only imports.
- Prefer `@ts-expect-error` over `@ts-ignore`.
- Use the `!` non-null assertion (rather than `?.`/`??`) when a value is known to always exist.
- Only mark a function `async` when it actually uses `await` or returns a `Promise`.
Avoid async event listeners; if unavoidable, call `e.preventDefault()` before the
first `await`. For a deliberately un-awaited call, assign it: `const _promise = asyncFoo()`.
Avoid async event listeners; if unavoidable, call `e.preventDefault()` before the first `await`.
## Data fetching
Use the `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` wrappers from
[`web_src/js/modules/fetch.ts`](../web_src/js/modules/fetch.ts).
Prefer to use our [`fetch-action.ts`](../web_src/js/modules/fetch-action.ts) framework
for form submissions, button clicks and network requests, which provides a consistent UX and error handling.
## DOM attributes
Avoid `node.dataset` because of its camel-casing behavior; use `node.getAttribute`
@@ -86,7 +89,7 @@ in new code. Never bind user-provided data directly onto DOM nodes.
## Showing and hiding elements
- In Vue, use `v-if` and `v-show`.
- In Vue, use `v-if` and `v-show`. If an element contains unmanaged DOM, use `v-show` to avoid losing the DOM state.
- In Go templates and plain JavaScript, use the `.tw-hidden` class together with the
`showElem()`, `hideElem()`, and `toggleElem()` helpers from
[`web_src/js/utils/dom.ts`](../web_src/js/utils/dom.ts).
+7 -6
View File
@@ -79,7 +79,7 @@ export default defineConfig([
'@eslint-community/eslint-comments/no-unlimited-disable': [2],
'@eslint-community/eslint-comments/no-unused-enable': [2],
'@eslint-community/eslint-comments/no-use': [0],
'@eslint-community/eslint-comments/require-description': [0],
'@eslint-community/eslint-comments/require-description': [2, {ignore: ['eslint', 'eslint-enable', 'eslint-env', 'exported', 'global', 'globals']}],
'@stylistic/array-bracket-newline': [0],
'@stylistic/array-bracket-spacing': [2, 'never'],
'@stylistic/array-element-newline': [0],
@@ -258,7 +258,7 @@ export default defineConfig([
'@typescript-eslint/prefer-function-type': [2],
'@typescript-eslint/prefer-includes': [2],
'@typescript-eslint/prefer-literal-enum-member': [0],
'@typescript-eslint/prefer-namespace-keyword': [2],
'@typescript-eslint/prefer-namespace-keyword': [0], // handled by @typescript-eslint/no-namespace
'@typescript-eslint/prefer-nullish-coalescing': [0],
'@typescript-eslint/prefer-optional-chain': [2, {requireNullish: true}],
'@typescript-eslint/prefer-promise-reject-errors': [2],
@@ -531,8 +531,8 @@ export default defineConfig([
'no-nonoctal-decimal-escape': [2],
'no-obj-calls': [2],
'no-object-constructor': [2],
'no-octal-escape': [2],
'no-octal': [2],
'no-octal-escape': [0], // parse error under strict mode
'no-octal': [0], // parse error under strict mode
'no-param-reassign': [0],
'no-plusplus': [0],
'no-promise-executor-return': [0],
@@ -581,7 +581,7 @@ export default defineConfig([
'no-useless-call': [2],
'no-useless-catch': [2],
'no-useless-computed-key': [2],
'no-useless-concat': [2],
'no-useless-concat': [0], // handled by unicorn/no-useless-concat
'no-useless-constructor': [2],
'no-useless-escape': [2],
'no-useless-rename': [2],
@@ -592,7 +592,7 @@ export default defineConfig([
'no-with': [0], // handled by no-restricted-syntax
'object-shorthand': [2, 'always'],
'one-var': [0],
'operator-assignment': [2, 'always'],
'operator-assignment': [0], // handled by unicorn/operator-assignment
'prefer-arrow-callback': [2, {allowNamedFunctions: true, allowUnboundThis: true}],
'prefer-const': [2, {destructuring: 'all', ignoreReadBeforeAssign: true}],
'prefer-destructuring': [0],
@@ -1096,6 +1096,7 @@ export default defineConfig([
},
{
files: ['**/*.test.ts', 'web_src/js/vitest.setup.ts'],
ignores: ['tests/e2e/**'],
plugins: {vitest},
languageOptions: {globals: globals.vitest},
rules: {
+8
View File
@@ -12,6 +12,7 @@ export default {
reportUnscopedDisables: true,
reportNeedlessDisables: true,
reportInvalidScopeDisables: true,
reportDescriptionlessDisables: true,
plugins: [
'stylelint-declaration-strict-value',
'stylelint-declaration-block-no-ignored-properties',
@@ -81,6 +82,7 @@ export default {
'@stylistic/no-eol-whitespace': true,
'@stylistic/no-extra-semicolons': true,
'@stylistic/no-missing-end-of-source-newline': null,
'@stylistic/no-multiple-whitespaces': true,
'@stylistic/number-leading-zero': null,
'@stylistic/number-no-trailing-zeros': null,
'@stylistic/property-case': 'lower',
@@ -108,13 +110,17 @@ export default {
'@stylistic/value-list-max-empty-lines': 0,
'at-rule-no-unknown': [true, {ignoreAtRules: ['tailwind']}],
'at-rule-no-vendor-prefix': true,
'block-no-redundant-nested-style-rules': true,
'color-no-invalid-hex': true,
'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}],
'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}],
'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow', 'grid-template']}],
'declaration-property-unit-disallowed-list': null,
'declaration-property-value-disallowed-list': {'word-break': ['break-word']},
'font-family-name-quotes': 'always-where-recommended',
'function-linear-gradient-no-nonstandard-direction': true,
'function-name-case': 'lower',
'function-no-unknown': true,
'function-url-quotes': 'always',
'import-notation': 'string',
'length-zero-no-unit': [true, {ignore: ['custom-properties'], ignoreFunctions: ['var']}],
@@ -125,6 +131,7 @@ export default {
'no-unknown-custom-media': null, // disabled until stylelint supports multi-file linting
'no-unknown-custom-properties': null, // disabled until stylelint supports multi-file linting
'plugin/declaration-block-no-ignored-properties': true,
'property-no-vendor-prefix': true,
'scale-unlimited/declaration-strict-value': [['/color$/', 'fill', 'stroke', 'font-weight'], {ignoreValues: '/^(inherit|transparent|unset|initial|currentcolor|none)$/', ignoreFunctions: true, disableFix: true, expandShorthand: true}],
'selector-attribute-quotes': 'always',
'selector-no-vendor-prefix': true,
@@ -132,6 +139,7 @@ export default {
'selector-type-case': 'lower',
'selector-type-no-unknown': [true, {ignore: ['custom-elements']}],
'shorthand-property-no-redundant-values': true,
'unit-no-unknown': true,
'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}],
},
} satisfies Config;
+1 -1
View File
@@ -3,7 +3,7 @@ import {expect, test} from '@playwright/test';
import {apiCreateRepo, apiCreateFile, assertFlushWithParent, assertNoJsError, login, randomString} from './utils.ts';
test('3d model file', async ({page, request, browserName}) => {
test.skip(browserName === 'firefox', 'unclear firefox-only CI-only failure'); // eslint-disable-line playwright/no-skipped-test
test.skip(browserName === 'firefox', 'unclear firefox-only CI-only failure'); // eslint-disable-line playwright/no-skipped-test -- conditional skip, the reason is in the message
const repoName = `e2e-3d-render-${randomString(8)}`;
const owner = env.GITEA_TEST_E2E_USER;
await apiCreateRepo(request, {name: repoName});
+1 -1
View File
@@ -19,7 +19,7 @@ async function signInWithPassword(page: Page, username: string) {
// regression: credProtect level 3 hid the credential from the second-factor login
test('security key survives credProtect', async ({page, request, browserName}) => {
test.skip(browserName !== 'chromium', 'only the CDP authenticator emulates credProtect'); // eslint-disable-line playwright/no-skipped-test
test.skip(browserName !== 'chromium', 'only the CDP authenticator emulates credProtect'); // eslint-disable-line playwright/no-skipped-test -- conditional skip, the reason is in the message
const username = `e2e-credprotect-${randomString(8)}`;
await apiCreateUser(request, username);
@@ -1,6 +1,6 @@
// MIT license, Copyright (c) GitHub, Inc.
// https://github.com/github/eslint-plugin-github/blob/main/lib/rules/unescaped-html-literal.js
/* eslint-disable no-template-curly-in-string */
/* eslint-disable no-template-curly-in-string -- the fixtures are template literal sources inside plain strings */
import rule from './unescaped-html-literal.ts';
import {RuleTester} from 'eslint';
-1
View File
@@ -269,7 +269,6 @@ relative-time::part(root)::selection {
.lines-commit .blame-info,
.ellipsis-button {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
+20 -20
View File
@@ -51,7 +51,7 @@
local("SourceHanSans-Light"), local("Yu Gothic Regular"),
local("YuGothic Regular"), local("Droid Sans Japanese"), local("Meiryo"),
local("MS PGothic");
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -66,7 +66,7 @@
local("SourceHanSans-Regular"), local("Yu Gothic Medium"),
local("YuGothic Medium"), local("Droid Sans Japanese"), local("Meiryo"),
local("MS PGothic");
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -81,7 +81,7 @@
local("SourceHanSans-Medium"), local("Yu Gothic Medium"),
local("YuGothic Medium"), local("Droid Sans Japanese"), local("Meiryo"),
local("MS PGothic");
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -95,7 +95,7 @@
local("NotoSansCJKJP-Bold"), local("Source Han Sans Bold"),
local("SourceHanSans-Bold"), local("Yu Gothic Bold"), local("YuGothic Bold"),
local("Droid Sans Japanese"), local("Meiryo Bold"), local("MS PGothic");
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -124,7 +124,7 @@
local("NotoSansCJKSC-Light"), local("HiraginoSansGB-W3"),
local("Hiragino Sans GB W3"), local("Microsoft YaHei Light"),
local("Heiti SC Light"), local("SimHei");
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -137,7 +137,7 @@
local("NotoSansCJKSC-Regular"), local("HiraginoSansGB-W3"),
local("Hiragino Sans GB W3"), local("Microsoft YaHei"),
local("Heiti SC Light"), local("SimHei");
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -150,7 +150,7 @@
local("NotoSansCJKSC-Medium"), local("HiraginoSansGB-W3"),
local("Hiragino Sans GB W3"), local("Microsoft YaHei"),
local("Heiti SC Light"), local("SimHei");
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -163,7 +163,7 @@
local("NotoSansCJKSC-Bold"), local("HiraginoSansGB-W6"),
local("Hiragino Sans GB W6"), local("Microsoft YaHei Bold"),
local("Heiti SC Medium"), local("SimHei");
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -192,7 +192,7 @@
local("NotoSansCJKTC-Light"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei Light"),
local("Heiti TC Light"), local("PMingLiU");
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -205,7 +205,7 @@
local("NotoSansCJKTC-Regular"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei"),
local("Heiti TC Light"), local("PMingLiU");
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -218,7 +218,7 @@
local("NotoSansCJKTC-Medium"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei"),
local("Heiti TC Light"), local("PMingLiU");
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -231,7 +231,7 @@
local("NotoSansCJKTC-Bold"), local("HiraginoSansTC-W6"),
local("Hiragino Sans TC W6"), local("Microsoft JhengHei Bold"),
local("Heiti TC Medium"), local("PMingLiU");
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -262,7 +262,7 @@
local("NotoSansCJKTC-Light"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei Light"),
local("Heiti TC Light"), local("PMingLiU_HKSCS"), local("PMingLiU");
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -277,7 +277,7 @@
local("NotoSansCJKTC-Regular"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei"),
local("Heiti TC Light"), local("PMingLiU_HKSCS"), local("PMingLiU");
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -292,7 +292,7 @@
local("NotoSansCJKTC-Medium"), local("HiraginoSansTC-W3"),
local("Hiragino Sans TC W3"), local("Microsoft JhengHei"),
local("Heiti TC Light"), local("PMingLiU_HKSCS"), local("PMingLiU");
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -307,7 +307,7 @@
local("NotoSansCJKTC-Bold"), local("HiraginoSansTC-W6"),
local("Hiragino Sans TC W6"), local("Microsoft JhengHei Bold"),
local("Heiti TC Medium"), local("PMingLiU_HKSCS"), local("PMingLiU");
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -335,7 +335,7 @@
local("SourceHanSansK-Light"), local("Noto Sans CJK KR Light"),
local("NotoSansCJKKR-Light"), local("NanumBarunGothic Light"),
local("Malgun Gothic Semilight"), local("Nanum Gothic"), local("Dotum");
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 300; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -347,7 +347,7 @@
local("SourceHanSansK-Regular"), local("Noto Sans CJK KR Regular"),
local("NotoSansCJKKR-Regular"), local("NanumBarunGothic"),
local("Malgun Gothic"), local("Nanum Gothic"), local("Dotum");
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 400; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -359,7 +359,7 @@
local("SourceHanSansK-Medium"), local("Noto Sans CJK KR Medium"),
local("NotoSansCJKKR-Medium"), local("NanumBarunGothic"),
local("Malgun Gothic"), local("Nanum Gothic"), local("Dotum");
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 500; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
@@ -371,7 +371,7 @@
local("SourceHanSansK-Bold"), local("Noto Sans CJK KR Bold"),
local("NotoSansCJKKR-Bold"), local("NanumBarunGothic Bold"),
local("Malgun Gothic Bold"), local("Nanum Gothic Bold"), local("Dotum");
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value */
font-weight: 600; /* stylelint-disable-line scale-unlimited/declaration-strict-value -- @font-face descriptors do not accept var() */
unicode-range: U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF,
U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????;
}
+1 -1
View File
@@ -103,7 +103,7 @@
}
/* reference sizes (not exactly at the moment): normal: padding-x=21, height=38 ; compact: padding-x=15, height=32 */
.ui.button { /* stylelint-disable-line no-duplicate-selectors */
.ui.button { /* stylelint-disable-line no-duplicate-selectors -- deliberate second block for the reference sizes */
gap: var(--gap-block);
min-height: 38px;
padding: 0.57em /* around 8px */ 1.43em /* around 20px */;
-1
View File
@@ -401,7 +401,6 @@ input:-webkit-autofill,
input:-webkit-autofill:focus,
input:-webkit-autofill:hover,
input:-webkit-autofill:active {
-webkit-background-clip: text;
-webkit-text-fill-color: var(--color-text);
box-shadow: 0 0 0 100px var(--color-primary-light-6) inset !important;
border-color: var(--color-primary-light-4) !important;
+13 -52
View File
@@ -5,9 +5,12 @@
- Remove html tab-size, we set our own
- Remove b,strong font-weight, we set our own
- Remove b,code,samp,pre font-size, we set our own
- Keep the browser's default line-height on html, it is font-dependent and roughly 1.2
- Keep the hr color that v3 dropped, it keeps the separator colored like the surrounding text
- Keep the :-moz-focusring outline that v3 dropped, checkboxes and radios have no other focus style
*/
/*! modern-normalize v2.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
/*
Document
@@ -25,7 +28,8 @@ Use a better box model (opinionated).
}
html {
line-height: normal; /* 1. (not following the "modern-normalize") Do not change the browser's default line-height, the default value is font-dependent and roughly 1.2 */
line-height: normal;
/* stylelint-disable-next-line property-no-vendor-prefix -- no unprefixed support in Safari or Firefox */
-webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
}
@@ -43,14 +47,8 @@ Grouping content
================
*/
/**
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
color: inherit;
}
/*
@@ -58,14 +56,6 @@ Text-level semantics
====================
*/
/**
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr[title] {
text-decoration: underline dotted;
}
/**
Add the correct font size in all browsers.
*/
@@ -100,13 +90,11 @@ Tabular data
*/
/**
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-color: currentcolor;
}
/*
@@ -130,15 +118,6 @@ textarea {
margin: 0; /* 2 */
}
/**
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/**
Correct the inability to style clickable types in iOS and Safari.
*/
@@ -147,35 +126,14 @@ button,
[type="button"],
[type="reset"],
[type="submit"] {
/* stylelint-disable-next-line property-no-vendor-prefix -- upstream v3.0.1 still ships the prefix */
-webkit-appearance: button;
}
/**
Remove the inner border and padding in Firefox.
*/
::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
Restore the focus styles unset by the previous rule.
*/
:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
Remove the additional ':invalid' styles in Firefox.
See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737
*/
:-moz-ui-invalid {
box-shadow: none;
}
/**
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
*/
@@ -207,6 +165,7 @@ Correct the cursor style of increment and decrement buttons in Safari.
*/
[type="search"] {
/* stylelint-disable-next-line property-no-vendor-prefix -- upstream v3.0.1 still ships the prefix */
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
@@ -216,6 +175,7 @@ Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
/* stylelint-disable-next-line property-no-vendor-prefix -- upstream v3.0.1 still ships the prefix */
-webkit-appearance: none;
}
@@ -225,6 +185,7 @@ Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-file-upload-button {
/* stylelint-disable-next-line property-no-vendor-prefix -- upstream v3.0.1 still ships the prefix */
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
+1 -1
View File
@@ -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;
+5 -8
View File
@@ -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);
},
+1 -1
View File
@@ -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 -1
View File
@@ -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);
+2 -4
View File
@@ -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();
+1 -1
View File
@@ -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';
+1 -1
View File
@@ -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() {
+3 -3
View File
@@ -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,
});
+1 -1
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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';
+2 -4
View File
@@ -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();
}
+1 -1
View File
@@ -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,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 {
+1 -1
View File
@@ -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 -1
View File
@@ -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.
+1 -1
View File
@@ -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() {}
}
+1 -1
View File
@@ -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()});
}
+1 -1
View File
@@ -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 () => {
+1 -3
View File
@@ -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();