0
0
Fork 0
mirror of https://github.com/go-gitea/gitea synced 2024-12-01 07:33:10 +01:00

Add typescript guideline and typescript-specific eslint plugins and fix issues (#31521)

1. Add some general guidelines how to write our typescript code
2. Add `@typescript-eslint/eslint-plugin`, general typescript rules
3. Add `eslint-plugin-deprecation` to detect deprecated code
4. Fix all new lint issues that came up
This commit is contained in:
silverwind 2024-07-03 17:48:14 +02:00 committed by GitHub
parent b270b30aeb
commit 2c92c7c522
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 253 additions and 178 deletions

View file

@ -13,6 +13,7 @@ parserOptions:
ecmaVersion: latest ecmaVersion: latest
project: true project: true
extraFileExtensions: [".vue"] extraFileExtensions: [".vue"]
parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
settings: settings:
import/extensions: [".js", ".ts"] import/extensions: [".js", ".ts"]
@ -24,7 +25,9 @@ settings:
plugins: plugins:
- "@eslint-community/eslint-plugin-eslint-comments" - "@eslint-community/eslint-plugin-eslint-comments"
- "@stylistic/eslint-plugin-js" - "@stylistic/eslint-plugin-js"
- "@typescript-eslint/eslint-plugin"
- eslint-plugin-array-func - eslint-plugin-array-func
- eslint-plugin-deprecation
- eslint-plugin-github - eslint-plugin-github
- eslint-plugin-i - eslint-plugin-i
- eslint-plugin-no-jquery - eslint-plugin-no-jquery
@ -209,6 +212,123 @@ rules:
"@stylistic/js/wrap-iife": [2, inside] "@stylistic/js/wrap-iife": [2, inside]
"@stylistic/js/wrap-regex": [0] "@stylistic/js/wrap-regex": [0]
"@stylistic/js/yield-star-spacing": [2, after] "@stylistic/js/yield-star-spacing": [2, after]
"@typescript-eslint/adjacent-overload-signatures": [0]
"@typescript-eslint/array-type": [0]
"@typescript-eslint/await-thenable": [2]
"@typescript-eslint/ban-ts-comment": [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}]
"@typescript-eslint/ban-tslint-comment": [0]
"@typescript-eslint/ban-types": [2, {extendDefaults: true, types: {Function: false}}]
"@typescript-eslint/class-literal-property-style": [0]
"@typescript-eslint/class-methods-use-this": [0]
"@typescript-eslint/consistent-generic-constructors": [0]
"@typescript-eslint/consistent-indexed-object-style": [0]
"@typescript-eslint/consistent-return": [0]
"@typescript-eslint/consistent-type-assertions": [2, {assertionStyle: as, objectLiteralTypeAssertions: allow}]
"@typescript-eslint/consistent-type-definitions": [2, type]
"@typescript-eslint/consistent-type-exports": [2, {fixMixedExportsWithInlineTypeSpecifier: false}]
"@typescript-eslint/consistent-type-imports": [2, {prefer: type-imports, fixStyle: separate-type-imports, disallowTypeAnnotations: true}]
"@typescript-eslint/default-param-last": [0]
"@typescript-eslint/dot-notation": [0]
"@typescript-eslint/explicit-function-return-type": [0]
"@typescript-eslint/explicit-member-accessibility": [0]
"@typescript-eslint/explicit-module-boundary-types": [0]
"@typescript-eslint/init-declarations": [0]
"@typescript-eslint/max-params": [0]
"@typescript-eslint/member-ordering": [0]
"@typescript-eslint/method-signature-style": [0]
"@typescript-eslint/naming-convention": [0]
"@typescript-eslint/no-array-constructor": [2]
"@typescript-eslint/no-array-delete": [2]
"@typescript-eslint/no-base-to-string": [0]
"@typescript-eslint/no-confusing-non-null-assertion": [2]
"@typescript-eslint/no-confusing-void-expression": [0]
"@typescript-eslint/no-dupe-class-members": [0]
"@typescript-eslint/no-duplicate-enum-values": [2]
"@typescript-eslint/no-duplicate-type-constituents": [2, {ignoreUnions: true}]
"@typescript-eslint/no-dynamic-delete": [0]
"@typescript-eslint/no-empty-function": [0]
"@typescript-eslint/no-empty-interface": [0]
"@typescript-eslint/no-explicit-any": [0]
"@typescript-eslint/no-extra-non-null-assertion": [2]
"@typescript-eslint/no-extraneous-class": [0]
"@typescript-eslint/no-floating-promises": [0]
"@typescript-eslint/no-for-in-array": [2]
"@typescript-eslint/no-implied-eval": [2]
"@typescript-eslint/no-import-type-side-effects": [0] # dupe with consistent-type-imports
"@typescript-eslint/no-inferrable-types": [0]
"@typescript-eslint/no-invalid-this": [0]
"@typescript-eslint/no-invalid-void-type": [0]
"@typescript-eslint/no-loop-func": [0]
"@typescript-eslint/no-loss-of-precision": [2]
"@typescript-eslint/no-magic-numbers": [0]
"@typescript-eslint/no-meaningless-void-operator": [0]
"@typescript-eslint/no-misused-new": [2]
"@typescript-eslint/no-misused-promises": [2, {checksVoidReturn: {attributes: false, arguments: false}}]
"@typescript-eslint/no-mixed-enums": [0]
"@typescript-eslint/no-namespace": [2]
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": [0]
"@typescript-eslint/no-non-null-asserted-optional-chain": [2]
"@typescript-eslint/no-non-null-assertion": [0]
"@typescript-eslint/no-redeclare": [0]
"@typescript-eslint/no-redundant-type-constituents": [2]
"@typescript-eslint/no-require-imports": [0]
"@typescript-eslint/no-restricted-imports": [0]
"@typescript-eslint/no-shadow": [0]
"@typescript-eslint/no-this-alias": [0] # handled by unicorn/no-this-assignment
"@typescript-eslint/no-unnecessary-boolean-literal-compare": [0]
"@typescript-eslint/no-unnecessary-condition": [0]
"@typescript-eslint/no-unnecessary-qualifier": [0]
"@typescript-eslint/no-unnecessary-template-expression": [0]
"@typescript-eslint/no-unnecessary-type-arguments": [0]
"@typescript-eslint/no-unnecessary-type-assertion": [2]
"@typescript-eslint/no-unnecessary-type-constraint": [2]
"@typescript-eslint/no-unsafe-argument": [0]
"@typescript-eslint/no-unsafe-assignment": [0]
"@typescript-eslint/no-unsafe-call": [0]
"@typescript-eslint/no-unsafe-declaration-merging": [2]
"@typescript-eslint/no-unsafe-enum-comparison": [2]
"@typescript-eslint/no-unsafe-member-access": [0]
"@typescript-eslint/no-unsafe-return": [0]
"@typescript-eslint/no-unsafe-unary-minus": [2]
"@typescript-eslint/no-unused-expressions": [0]
"@typescript-eslint/no-unused-vars": [2, {vars: all, args: all, caughtErrors: all, ignoreRestSiblings: false, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_}]
"@typescript-eslint/no-use-before-define": [0]
"@typescript-eslint/no-useless-constructor": [0]
"@typescript-eslint/no-useless-empty-export": [0]
"@typescript-eslint/no-var-requires": [2]
"@typescript-eslint/non-nullable-type-assertion-style": [0]
"@typescript-eslint/only-throw-error": [2]
"@typescript-eslint/parameter-properties": [0]
"@typescript-eslint/prefer-as-const": [2]
"@typescript-eslint/prefer-destructuring": [0]
"@typescript-eslint/prefer-enum-initializers": [0]
"@typescript-eslint/prefer-find": [2]
"@typescript-eslint/prefer-for-of": [2]
"@typescript-eslint/prefer-function-type": [2]
"@typescript-eslint/prefer-includes": [2]
"@typescript-eslint/prefer-literal-enum-member": [0]
"@typescript-eslint/prefer-namespace-keyword": [0]
"@typescript-eslint/prefer-nullish-coalescing": [0]
"@typescript-eslint/prefer-optional-chain": [2, {requireNullish: true}]
"@typescript-eslint/prefer-promise-reject-errors": [0]
"@typescript-eslint/prefer-readonly": [0]
"@typescript-eslint/prefer-readonly-parameter-types": [0]
"@typescript-eslint/prefer-reduce-type-parameter": [0]
"@typescript-eslint/prefer-regexp-exec": [0]
"@typescript-eslint/prefer-return-this-type": [0]
"@typescript-eslint/prefer-string-starts-ends-with": [2, {allowSingleElementEquality: always}]
"@typescript-eslint/promise-function-async": [0]
"@typescript-eslint/require-array-sort-compare": [0]
"@typescript-eslint/require-await": [0]
"@typescript-eslint/restrict-plus-operands": [2]
"@typescript-eslint/restrict-template-expressions": [0]
"@typescript-eslint/return-await": [0]
"@typescript-eslint/strict-boolean-expressions": [0]
"@typescript-eslint/switch-exhaustiveness-check": [0]
"@typescript-eslint/triple-slash-reference": [2]
"@typescript-eslint/typedef": [0]
"@typescript-eslint/unbound-method": [2]
"@typescript-eslint/unified-signatures": [2]
accessor-pairs: [2] accessor-pairs: [2]
array-callback-return: [2, {checkForEach: true}] array-callback-return: [2, {checkForEach: true}]
array-func/avoid-reverse: [2] array-func/avoid-reverse: [2]
@ -230,6 +350,7 @@ rules:
default-case-last: [2] default-case-last: [2]
default-case: [0] default-case: [0]
default-param-last: [0] default-param-last: [0]
deprecation/deprecation: [2]
dot-notation: [0] dot-notation: [0]
eqeqeq: [2] eqeqeq: [2]
for-direction: [2] for-direction: [2]
@ -321,7 +442,7 @@ rules:
multiline-comment-style: [2, separate-lines] multiline-comment-style: [2, separate-lines]
new-cap: [0] new-cap: [0]
no-alert: [0] no-alert: [0]
no-array-constructor: [2] no-array-constructor: [0] # handled by @typescript-eslint/no-array-constructor
no-async-promise-executor: [0] no-async-promise-executor: [0]
no-await-in-loop: [0] no-await-in-loop: [0]
no-bitwise: [0] no-bitwise: [0]
@ -365,7 +486,7 @@ rules:
no-global-assign: [2] no-global-assign: [2]
no-implicit-coercion: [2] no-implicit-coercion: [2]
no-implicit-globals: [0] no-implicit-globals: [0]
no-implied-eval: [2] no-implied-eval: [0] # handled by @typescript-eslint/no-implied-eval
no-import-assign: [2] no-import-assign: [2]
no-inline-comments: [0] no-inline-comments: [0]
no-inner-declarations: [2] no-inner-declarations: [2]
@ -471,7 +592,7 @@ rules:
no-lone-blocks: [2] no-lone-blocks: [2]
no-lonely-if: [0] no-lonely-if: [0]
no-loop-func: [0] no-loop-func: [0]
no-loss-of-precision: [2] no-loss-of-precision: [0] # handled by @typescript-eslint/no-loss-of-precision
no-magic-numbers: [0] no-magic-numbers: [0]
no-misleading-character-class: [2] no-misleading-character-class: [2]
no-multi-assign: [0] no-multi-assign: [0]
@ -493,7 +614,7 @@ rules:
no-promise-executor-return: [0] no-promise-executor-return: [0]
no-proto: [2] no-proto: [2]
no-prototype-builtins: [2] no-prototype-builtins: [2]
no-redeclare: [2] no-redeclare: [0] # must be disabled for typescript overloads
no-regex-spaces: [2] no-regex-spaces: [2]
no-restricted-exports: [0] no-restricted-exports: [0]
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename] no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
@ -526,7 +647,7 @@ rules:
no-unused-expressions: [2] no-unused-expressions: [2]
no-unused-labels: [2] no-unused-labels: [2]
no-unused-private-class-members: [2] no-unused-private-class-members: [2]
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_, ignoreRestSiblings: false}] no-unused-vars: [0] # handled by @typescript-eslint/no-unused-vars
no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}] no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}]
no-use-extend-native/no-use-extend-native: [2] no-use-extend-native/no-use-extend-native: [2]
no-useless-backreference: [2] no-useless-backreference: [2]
@ -641,7 +762,7 @@ rules:
regexp/unicode-escape: [0] regexp/unicode-escape: [0]
regexp/use-ignore-case: [0] regexp/use-ignore-case: [0]
require-atomic-updates: [0] require-atomic-updates: [0]
require-await: [0] require-await: [0] # handled by @typescript-eslint/require-await
require-unicode-regexp: [0] require-unicode-regexp: [0]
require-yield: [2] require-yield: [2]
sonarjs/cognitive-complexity: [0] sonarjs/cognitive-complexity: [0]

View file

@ -79,6 +79,22 @@ We use htmx for simple interactions. You can see an example for simple interacti
Although mixing different frameworks is discouraged, Although mixing different frameworks is discouraged,
it should also work if the mixing is necessary and the code is well-designed and maintainable. it should also work if the mixing is necessary and the code is well-designed and maintainable.
### Typescript
Gitea is in the process of migrating to type-safe Typescript. Here are some specific guidelines regarding Typescript in the codebase:
#### Use type aliases instead of interfaces
Prefer to use type aliases because they can represent any type and are generally more flexible to use than interfaces.
#### Use separate type imports
We use `verbatimModuleSyntax` so type and non-type imports from the same file must be split into two `import type` statements. This enables the typescript compiler to completely eliminate the type import statements during compilation.
#### Use `@ts-expect-error` instead of `@ts-ignore`
Both annotations should be avoided, but if you have to use them, use `@ts-expect-error` because it will not leave ineffective statements after the issue is fixed.
### `async` Functions ### `async` Functions
Only mark a function as `async` if and only if there are `await` calls Only mark a function as `async` if and only if there are `await` calls

230
package-lock.json generated
View file

@ -69,11 +69,13 @@
"@stoplight/spectral-cli": "6.11.1", "@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "2.2.1", "@stylistic/eslint-plugin-js": "2.2.1",
"@stylistic/stylelint-plugin": "2.1.2", "@stylistic/stylelint-plugin": "2.1.2",
"@typescript-eslint/eslint-plugin": "7.14.1",
"@typescript-eslint/parser": "7.14.1", "@typescript-eslint/parser": "7.14.1",
"@vitejs/plugin-vue": "5.0.5", "@vitejs/plugin-vue": "5.0.5",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-import-resolver-typescript": "3.6.1", "eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-array-func": "4.0.0", "eslint-plugin-array-func": "4.0.0",
"eslint-plugin-deprecation": "3.0.0",
"eslint-plugin-github": "5.0.1", "eslint-plugin-github": "5.0.1",
"eslint-plugin-i": "2.29.1", "eslint-plugin-i": "2.29.1",
"eslint-plugin-no-jquery": "3.0.1", "eslint-plugin-no-jquery": "3.0.1",
@ -2370,16 +2372,17 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.13.1", "version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz",
"integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", "integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.13.1", "@typescript-eslint/scope-manager": "7.14.1",
"@typescript-eslint/type-utils": "7.13.1", "@typescript-eslint/type-utils": "7.14.1",
"@typescript-eslint/utils": "7.13.1", "@typescript-eslint/utils": "7.14.1",
"@typescript-eslint/visitor-keys": "7.13.1", "@typescript-eslint/visitor-keys": "7.14.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.3.1", "ignore": "^5.3.1",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -2431,7 +2434,7 @@
} }
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "7.14.1", "version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz",
"integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==",
@ -2449,7 +2452,35 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/type-utils": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz",
"integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "7.14.1",
"@typescript-eslint/utils": "7.14.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.56.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.14.1", "version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz",
"integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==",
@ -2463,7 +2494,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "7.14.1", "version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz",
"integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==",
@ -2492,7 +2523,30 @@
} }
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/utils": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz",
"integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.14.1",
"@typescript-eslint/types": "7.14.1",
"@typescript-eslint/typescript-estree": "7.14.1"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.56.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.14.1", "version": "7.14.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz",
"integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==",
@ -2510,148 +2564,12 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz",
"integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz",
"integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.13.1",
"@typescript-eslint/utils": "7.13.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.56.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz",
"integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==",
"dev": true,
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz",
"integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/visitor-keys": "7.13.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz",
"integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.13.1",
"@typescript-eslint/types": "7.13.1",
"@typescript-eslint/typescript-estree": "7.13.1"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.56.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz",
"integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.13.1",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "3.4.3", "version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true, "dev": true,
"license": "Apache-2.0",
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
@ -5513,6 +5431,22 @@
"eslint": ">=8.40.0" "eslint": ">=8.40.0"
} }
}, },
"node_modules/eslint-plugin-deprecation": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-3.0.0.tgz",
"integrity": "sha512-JuVLdNg/uf0Adjg2tpTyYoYaMbwQNn/c78P1HcccokvhtRphgnRjZDKmhlxbxYptppex03zO76f97DD/yQHv7A==",
"dev": true,
"license": "LGPL-3.0-or-later",
"dependencies": {
"@typescript-eslint/utils": "^7.0.0",
"ts-api-utils": "^1.3.0",
"tslib": "^2.3.1"
},
"peerDependencies": {
"eslint": "^8.0.0",
"typescript": "^4.2.4 || ^5.0.0"
}
},
"node_modules/eslint-plugin-escompat": { "node_modules/eslint-plugin-escompat": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz",

View file

@ -68,11 +68,13 @@
"@stoplight/spectral-cli": "6.11.1", "@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "2.2.1", "@stylistic/eslint-plugin-js": "2.2.1",
"@stylistic/stylelint-plugin": "2.1.2", "@stylistic/stylelint-plugin": "2.1.2",
"@typescript-eslint/eslint-plugin": "7.14.1",
"@typescript-eslint/parser": "7.14.1", "@typescript-eslint/parser": "7.14.1",
"@vitejs/plugin-vue": "5.0.5", "@vitejs/plugin-vue": "5.0.5",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-import-resolver-typescript": "3.6.1", "eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-array-func": "4.0.0", "eslint-plugin-array-func": "4.0.0",
"eslint-plugin-deprecation": "3.0.0",
"eslint-plugin-github": "5.0.1", "eslint-plugin-github": "5.0.1",
"eslint-plugin-i": "2.29.1", "eslint-plugin-i": "2.29.1",
"eslint-plugin-no-jquery": "3.0.1", "eslint-plugin-no-jquery": "3.0.1",

View file

@ -7,21 +7,21 @@ test.beforeAll(async ({browser}, workerInfo) => {
test('homepage', async ({page}) => { test('homepage', async ({page}) => {
const response = await page.goto('/'); const response = await page.goto('/');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await expect(page).toHaveTitle(/^Gitea: Git with a cup of tea\s*$/); await expect(page).toHaveTitle(/^Gitea: Git with a cup of tea\s*$/);
await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg'); await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg');
}); });
test('register', async ({page}, workerInfo) => { test('register', async ({page}, workerInfo) => {
const response = await page.goto('/user/sign_up'); const response = await page.goto('/user/sign_up');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`); await page.locator('input[name=user_name]').fill(`e2e-test-${workerInfo.workerIndex}`);
await page.type('input[name=email]', `e2e-test-${workerInfo.workerIndex}@test.com`); await page.locator('input[name=email]').fill(`e2e-test-${workerInfo.workerIndex}@test.com`);
await page.type('input[name=password]', 'test123test123'); await page.locator('input[name=password]').fill('test123test123');
await page.type('input[name=retype]', 'test123test123'); await page.locator('input[name=retype]').fill('test123test123');
await page.click('form button.ui.primary.button:visible'); await page.click('form button.ui.primary.button:visible');
// Make sure we routed to the home page. Else login failed. // Make sure we routed to the home page. Else login failed.
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
await expect(page.locator('.secondary-nav span>img.ui.avatar')).toBeVisible(); await expect(page.locator('.secondary-nav span>img.ui.avatar')).toBeVisible();
await expect(page.locator('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!'); await expect(page.locator('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!');
@ -30,15 +30,15 @@ test('register', async ({page}, workerInfo) => {
test('login', async ({page}, workerInfo) => { test('login', async ({page}, workerInfo) => {
const response = await page.goto('/user/login'); const response = await page.goto('/user/login');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await page.type('input[name=user_name]', `user2`); await page.locator('input[name=user_name]').fill(`user2`);
await page.type('input[name=password]', `password`); await page.locator('input[name=password]').fill(`password`);
await page.click('form button.ui.primary.button:visible'); await page.click('form button.ui.primary.button:visible');
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
save_visual(page); save_visual(page);
}); });
@ -50,7 +50,7 @@ test('logged in user', async ({browser}, workerInfo) => {
await page.goto('/'); await page.goto('/');
// Make sure we routed to the home page. Else login failed. // Make sure we routed to the home page. Else login failed.
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
save_visual(page); save_visual(page);
}); });

View file

@ -14,7 +14,7 @@ export async function login_user(browser, workerInfo, user) {
// Route to login page // Route to login page
// Note: this could probably be done more quickly with a POST // Note: this could probably be done more quickly with a POST
const response = await page.goto('/user/login'); const response = await page.goto('/user/login');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
// Fill out form // Fill out form
await page.type('input[name=user_name]', user); await page.type('input[name=user_name]', user);
@ -23,7 +23,7 @@ export async function login_user(browser, workerInfo, user) {
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
await expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`);
// Save state // Save state
await context.storageState({path: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); await context.storageState({path: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`});

View file

@ -89,7 +89,9 @@ const sfc = {
// load job data and then auto-reload periodically // load job data and then auto-reload periodically
// need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener // need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener
await this.loadJob(); await this.loadJob();
this.intervalID = setInterval(this.loadJob, 1000); this.intervalID = setInterval(() => {
this.loadJob();
}, 1000);
document.body.addEventListener('click', this.closeDropdown); document.body.addEventListener('click', this.closeDropdown);
this.hashChangeListener(); this.hashChangeListener();
window.addEventListener('hashchange', this.hashChangeListener); window.addEventListener('hashchange', this.hashChangeListener);

View file

@ -153,7 +153,7 @@ export function initRepoCodeView() {
}); });
$(window).on('hashchange', () => { $(window).on('hashchange', () => {
let m = rangeAnchorRegex.exec(window.location.hash.match); let m = rangeAnchorRegex.exec(window.location.hash);
const $linesEls = $(getLineEls()); const $linesEls = $(getLineEls());
let $first; let $first;
if (m) { if (m) {
@ -170,7 +170,7 @@ export function initRepoCodeView() {
return; return;
} }
} }
m = singleAnchorRegex.exec(window.location.hash.match); m = singleAnchorRegex.exec(window.location.hash);
if (m) { if (m) {
$first = $linesEls.filter(`[rel=L${m[2]}]`); $first = $linesEls.filter(`[rel=L${m[2]}]`);
if ($first.length) { if ($first.length) {

View file

@ -55,8 +55,8 @@ export function filterRepoFilesWeighted(files, filter) {
const filterLower = filter.toLowerCase(); const filterLower = filter.toLowerCase();
// TODO: for large repo, this loop could be slow, maybe there could be one more limit: // TODO: for large repo, this loop could be slow, maybe there could be one more limit:
// ... && filterResult.length < threshold * 20, wait for more feedbacks // ... && filterResult.length < threshold * 20, wait for more feedbacks
for (let i = 0; i < files.length; i++) { for (const file of files) {
const res = strSubMatch(files[i], filterLower); const res = strSubMatch(file, filterLower);
if (res.length > 1) { // length==1 means unmatched, >1 means having matched sub strings if (res.length > 1) { // length==1 means unmatched, >1 means having matched sub strings
filterResult.push({matchResult: res, matchWeight: calcMatchedWeight(res)}); filterResult.push({matchResult: res, matchWeight: calcMatchedWeight(res)});
} }

View file

@ -102,16 +102,16 @@ export function initRepoTopicBar() {
if (res.topics) { if (res.topics) {
let found = false; let found = false;
for (let i = 0; i < res.topics.length; i++) { for (const {topic_name} of res.topics) {
// skip currently added tags // skip currently added tags
if (current_topics.includes(res.topics[i].topic_name)) { if (current_topics.includes(topic_name)) {
continue; continue;
} }
if (res.topics[i].topic_name.toLowerCase() === query.toLowerCase()) { if (topic_name.toLowerCase() === query.toLowerCase()) {
found_query = true; found_query = true;
} }
formattedResponse.results.push({description: res.topics[i].topic_name, 'data-value': res.topics[i].topic_name}); formattedResponse.results.push({description: topic_name, 'data-value': topic_name});
found = true; found = true;
} }
formattedResponse.success = found; formattedResponse.success = found;

View file

@ -270,7 +270,7 @@ export function replaceTextareaSelection(textarea, text) {
textarea.contentEditable = 'true'; textarea.contentEditable = 'true';
try { try {
success = document.execCommand('insertText', false, text); success = document.execCommand('insertText', false, text); // eslint-disable-line deprecation/deprecation
} catch { } catch {
success = false; success = false;
} }