From dde7c4a7706ded56d706cff331ac8326170abd07 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 1 Apr 2025 03:55:34 +0200 Subject: [PATCH 1/5] chore: add new `no-jquery` rules Were added in version v3.1.0. --- eslint.config.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 17f461a8f4..035c70a338 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -318,6 +318,7 @@ export default tseslint.config( 'no-jquery/no-data': [0], 'no-jquery/no-deferred': [2], 'no-jquery/no-delegate': [2], + 'no-jquery/no-done-fail': [2], 'no-jquery/no-each-collection': [0], 'no-jquery/no-each-util': [0], 'no-jquery/no-each': [0], @@ -331,6 +332,7 @@ export default tseslint.config( 'no-jquery/no-find-collection': [0], 'no-jquery/no-find-util': [2], 'no-jquery/no-find': [0], + 'no-jquery/no-fx': [2], 'no-jquery/no-fx-interval': [2], 'no-jquery/no-global-eval': [2], 'no-jquery/no-global-selector': [0], From 9eb67ba8eded4d64a4a21e641e6c28a1e70f3830 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 1 Apr 2025 03:58:09 +0200 Subject: [PATCH 2/5] chore: enable `no-jquery/no-sizzle` Don't rely on Jquery's CSS selector engine. --- eslint.config.mjs | 2 +- web_src/js/features/comp/ReactionSelector.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 035c70a338..1b130cd2e6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -376,7 +376,7 @@ export default tseslint.config( 'no-jquery/no-selector-prop': [2], 'no-jquery/no-serialize': [2], 'no-jquery/no-size': [2], - 'no-jquery/no-sizzle': [0], + 'no-jquery/no-sizzle': [2], 'no-jquery/no-slide': [2], 'no-jquery/no-sub': [2], 'no-jquery/no-support': [2], diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index fd4601fb91..357a33f469 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -23,7 +23,7 @@ export function initCompReactionSelector($parent) { $react.remove(); } if (!data.empty) { - const $attachments = $content.find('.segment.bottom:first'); + const $attachments = $content.find('.segment.bottom').first(); $react = $(data.html); if ($attachments.length > 0) { $react.insertBefore($attachments); From c124014115c477e4577d154555b72bb1f12d2640 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 1 Apr 2025 03:59:12 +0200 Subject: [PATCH 3/5] chore: enable `no-jquery/no-each-util` - No longer allow `$.each`. This can easily be done with vanilla javascript. --- eslint.config.mjs | 2 +- web_src/js/features/org-team.js | 5 ++--- web_src/js/features/repo-issue.js | 8 ++++---- web_src/js/features/repo-settings.js | 5 ++--- web_src/js/features/repo-template.js | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 1b130cd2e6..c7d01c79da 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -320,7 +320,7 @@ export default tseslint.config( 'no-jquery/no-delegate': [2], 'no-jquery/no-done-fail': [2], 'no-jquery/no-each-collection': [0], - 'no-jquery/no-each-util': [0], + 'no-jquery/no-each-util': [2], 'no-jquery/no-each': [0], 'no-jquery/no-error-shorthand': [2], 'no-jquery/no-error': [2], diff --git a/web_src/js/features/org-team.js b/web_src/js/features/org-team.js index 9b059b3a46..fe816da865 100644 --- a/web_src/js/features/org-team.js +++ b/web_src/js/features/org-team.js @@ -10,13 +10,12 @@ export function initOrgTeamSearchRepoBox() { url: `${appSubUrl}/repo/search?q={query}&uid=${$searchRepoBox.data('uid')}`, onResponse(response) { const items = []; - $.each(response.data, (_i, item) => { + for (const item of response.data) { items.push({ title: item.repository.full_name.split('/')[1], description: item.repository.full_name, }); - }); - + } return {results: items}; }, }, diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index c28bf08442..77ceb0401c 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -139,7 +139,7 @@ export function initRepoIssueSidebarList() { const filteredResponse = {success: true, results: []}; const currIssueId = $('#new-dependency-drop-list').data('issue-id'); // Parse the response from the api to work with our dropdown - $.each(response, (_i, issue) => { + for (const [_, issue] of Object.entries(response)) { // Don't list current issue in the dependency list. if (issue.id === currIssueId) { return; @@ -149,7 +149,7 @@ export function initRepoIssueSidebarList() { }
${htmlEscape(issue.repository.full_name)}
`, value: issue.id, }); - }); + } return filteredResponse; }, cache: false, @@ -345,12 +345,12 @@ export function initRepoIssueReferenceRepositorySearch() { url: `${appSubUrl}/repo/search?q={query}&limit=20`, onResponse(response) { const filteredResponse = {success: true, results: []}; - $.each(response.data, (_r, repo) => { + for (const repo of response.data) { filteredResponse.results.push({ name: htmlEscape(repo.repository.full_name), value: repo.repository.full_name, }); - }); + } return filteredResponse; }, cache: false, diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 52c5de2bfa..47ada545de 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -55,13 +55,12 @@ export function initRepoSettingSearchTeamBox() { headers: {'X-Csrf-Token': csrfToken}, onResponse(response) { const items = []; - $.each(response.data, (_i, item) => { + for (const item of response.data) { items.push({ title: item.name, description: `${item.permission} access`, // TODO: translate this string }); - }); - + } return {results: items}; }, }, diff --git a/web_src/js/features/repo-template.js b/web_src/js/features/repo-template.js index 5f63e8b3ba..5f01be5ad3 100644 --- a/web_src/js/features/repo-template.js +++ b/web_src/js/features/repo-template.js @@ -32,12 +32,12 @@ export function initRepoTemplateSearch() { value: '', }); // Parse the response from the api to work with our dropdown - $.each(response.data, (_r, repo) => { + for (const repo of response.data) { filteredResponse.results.push({ name: htmlEscape(repo.repository.full_name), value: repo.repository.id, }); - }); + } return filteredResponse; }, cache: false, From 9420d3d0a5caad8968aaaa588a0c2ec84fc4340a Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 1 Apr 2025 04:41:57 +0200 Subject: [PATCH 4/5] chore: enable `no-jquery/no-trigger` - A lot of substitution to `.requestSubmit()`. - Where possible, rewrite some other jquery to vanilla javascript to ease the linter fix. --- eslint.config.mjs | 2 +- .../js/components/RepoBranchTagSelector.vue | 4 ++-- web_src/js/features/admin/common.js | 10 ++++++---- web_src/js/features/common-global.js | 4 ++-- web_src/js/features/comp/LabelEdit.js | 4 ++-- web_src/js/features/repo-code.js | 3 ++- web_src/js/features/repo-common.js | 16 +++++++-------- web_src/js/features/repo-editor.js | 2 +- web_src/js/features/repo-issue.js | 20 +++++++++---------- 9 files changed, 34 insertions(+), 31 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index c7d01c79da..cb11195d4d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -381,7 +381,7 @@ export default tseslint.config( 'no-jquery/no-sub': [2], 'no-jquery/no-support': [2], 'no-jquery/no-text': [0], - 'no-jquery/no-trigger': [0], + 'no-jquery/no-trigger': [2], 'no-jquery/no-trim': [2], 'no-jquery/no-type': [2], 'no-jquery/no-unique': [2], diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index cd86499ddc..12ff564aa7 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -98,14 +98,14 @@ const sfc = { $(`#${this.branchForm} input[name="refType"]`).val('branch'); } if (this.submitForm) { - $(`#${this.branchForm}`).trigger('submit'); + document.getElementById(this.branchForm).requestSubmit(); } this.menuVisible = false; } }, createNewBranch() { if (!this.showCreateNewBranch) return; - $(this.$refs.newBranchForm).trigger('submit'); + this.$refs.newBranchForm.requestSubmit(); }, focusSearchField() { nextTick(() => { diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index 9934c3dd17..b46f4f8a74 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -175,7 +175,7 @@ export function initAdminCommon() { onUsePagedSearchChange(); } }); - $('#auth_type').trigger('change'); + document.getElementById('auth_type').dispatchEvent(new Event('change')); document.getElementById('security_protocol')?.addEventListener('change', onSecurityProtocolChange); document.getElementById('use_paged_search')?.addEventListener('change', onUsePagedSearchChange); document.getElementById('oauth2_provider')?.addEventListener('change', () => onOAuth2Change(true)); @@ -200,10 +200,12 @@ export function initAdminCommon() { } if (document.querySelector('.admin.authentication')) { - $('#auth_name').on('input', function () { + const authNameEl = document.getElementById('auth_name'); + authNameEl.addEventListener('input', (el) => { // appSubUrl is either empty or is a path that starts with `/` and doesn't have a trailing slash. - document.getElementById('oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(this.value)}/callback`; - }).trigger('input'); + document.getElementById('oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(el.target.value)}/callback`; + }); + authNameEl.dispatchEvent(new Event('input')); } // Notice diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 7848a14b66..7d553f9692 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -57,7 +57,7 @@ export function initGlobalEnterQuickSubmit() { export function initGlobalButtonClickOnEnter() { $(document).on('keypress', 'div.ui.button,span.ui.button', (e) => { if (e.code === ' ' || e.code === 'Enter') { - $(e.target).trigger('click'); + e.target.click(); e.preventDefault(); } }); @@ -314,7 +314,7 @@ export function initGlobalLinkActions() { closable: false, onApprove: async () => { if ($this.data('type') === 'form') { - $($this.data('form')).trigger('submit'); + document.querySelector($this.data('form')).requestSubmit(); return; } if ($this[0].getAttribute('hx-confirm')) { diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js index 2cc75cc6b0..a97c7fe34c 100644 --- a/web_src/js/features/comp/LabelEdit.js +++ b/web_src/js/features/comp/LabelEdit.js @@ -38,7 +38,7 @@ export function initCompLabelEdit(selector) { form.reportValidity(); return false; } - $('.new-label.form').trigger('submit'); + document.querySelector('.new-label.form').requestSubmit(); }, }).modal('show'); return false; @@ -75,7 +75,7 @@ export function initCompLabelEdit(selector) { form.reportValidity(); return false; } - $('.edit-label.form').trigger('submit'); + document.querySelector('.edit-label.form').requestSubmit(); }, }).modal('show'); return false; diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 794cc38010..2ff9e611ae 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -184,7 +184,8 @@ export function initRepoCodeView() { $('html, body').scrollTop($first.offset().top - 200); } } - }).trigger('hashchange'); + }); + window.dispatchEvent(new Event('hashchange')); } $(document).on('click', '.fold-file', ({currentTarget}) => { invertFileFolding(currentTarget.closest('.file-content'), currentTarget); diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 88aa93d850..02a0499f95 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -33,25 +33,25 @@ export function initRepoArchiveLinks() { } export function initRepoCloneLink() { - const $repoCloneSsh = $('#repo-clone-ssh'); - const $repoCloneHttps = $('#repo-clone-https'); - const $inputLink = $('#repo-clone-url'); + const repoCloneSSH = document.getElementById('repo-clone-ssh'); + const repoCloneHTTPS = document.getElementById('repo-clone-https'); + const inputLink = document.getElementById('repo-clone-url'); - if ((!$repoCloneSsh.length && !$repoCloneHttps.length) || !$inputLink.length) { + if ((!repoCloneSSH && !repoCloneHTTPS) || !inputLink) { return; } - $repoCloneSsh.on('click', () => { + repoCloneSSH.addEventListener('click', () => { localStorage.setItem('repo-clone-protocol', 'ssh'); window.updateCloneStates(); }); - $repoCloneHttps.on('click', () => { + repoCloneHTTPS.addEventListener('click', () => { localStorage.setItem('repo-clone-protocol', 'https'); window.updateCloneStates(); }); - $inputLink.on('focus', () => { - $inputLink.trigger('select'); + inputLink.addEventListener('focus', () => { + inputLink.select(); }); } diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index 5b59b66f80..d3f5f2f8a9 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -185,7 +185,7 @@ export function initRepoEditor() { $('#edit-empty-content-modal') .modal({ onApprove() { - $('.edit.form').trigger('submit'); + document.querySelector('.edit.form').requestSubmit(); }, }) .modal('show'); diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 77ceb0401c..889687da3e 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -37,27 +37,27 @@ export function initRepoIssueTimeTracking() { $('.issue-start-time-modal').modal({ duration: 200, onApprove() { - $('#add_time_manual_form').trigger('submit'); + document.getElementById('add_time_manual_form').requestSubmit(); }, }).modal('show'); $('.issue-start-time-modal input').on('keydown', (e) => { if ((e.keyCode || e.key) === 13) { - $('#add_time_manual_form').trigger('submit'); + document.getElementById('add_time_manual_form').requestSubmit(); } }); }); $(document).on('click', '.issue-start-time, .issue-stop-time', () => { - $('#toggle_stopwatch_form').trigger('submit'); + document.getElementById('toggle_stopwatch_form').requestSubmit(); }); $(document).on('click', '.issue-cancel-time', () => { - $('#cancel_stopwatch_form').trigger('submit'); + document.getElementById('cancel_stopwatch_form').requestSubmit(); }); $(document).on('click', 'button.issue-delete-time', function () { const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`; $(sel).modal({ duration: 200, onApprove() { - $(`${sel} form`).trigger('submit'); + document.getElementById(`${sel} form`).requestSubmit(); }, }).modal('show'); }); @@ -247,7 +247,7 @@ export function initRepoIssueDependencyDelete() { onApprove: () => { $('#removeDependencyID').val(id); $('#dependencyType').val(type); - $('#removeDependencyForm').trigger('submit'); + document.getElementById('removeDependencyForm').requestSubmit(); }, }).modal('show'); }); @@ -369,9 +369,9 @@ export function initRepoIssueWipTitle() { $('.title_wip_desc > a').on('click', (e) => { e.preventDefault(); - const $issueTitle = $('#issue_title'); - $issueTitle.trigger('focus'); - const value = $issueTitle.val().trim().toUpperCase(); + const issueTitleEl = document.getElementById('issue_title'); + issueTitleEl.focus(); + const value = issueTitleEl.value.trim().toUpperCase(); const wipPrefixes = $('.title_wip_desc').data('wip-prefixes'); for (const prefix of wipPrefixes) { @@ -380,7 +380,7 @@ export function initRepoIssueWipTitle() { } } - $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`); + issueTitleEl.value = `${wipPrefixes[0]} ${issueTitleEl.value}`; }); } From d9892e57bd65e0955a80344993ff98d471d15f18 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 1 Apr 2025 04:44:44 +0200 Subject: [PATCH 5/5] chore: enable `no-jquery/no-map-collection` No linter errors reported. --- eslint.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index cb11195d4d..f635a8b504 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -352,7 +352,7 @@ export default tseslint.config( 'no-jquery/no-live': [2], 'no-jquery/no-load-shorthand': [2], 'no-jquery/no-load': [2], - 'no-jquery/no-map-collection': [0], + 'no-jquery/no-map-collection': [2], 'no-jquery/no-map-util': [2], 'no-jquery/no-map': [2], 'no-jquery/no-merge': [2],