From d5f44c249977e540293f60fd7f9ff27f7bfcf073 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 16 Mar 2024 14:22:16 +0200
Subject: [PATCH] Forbid variables containing jQuery collections not having the
 `$` prefix (#29839)

See
https://github.com/wikimedia/eslint-plugin-no-jquery/blob/master/docs/rules/variable-pattern.md

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 3cd64949ae1402a4ff45fba0a27c4acca1c5aead)
---
 .eslintrc.yaml                                |  2 +-
 .../js/components/RepoBranchTagSelector.vue   |  2 +-
 web_src/js/features/common-global.js          | 16 ++--
 web_src/js/features/comp/LabelEdit.js         | 42 ++++-----
 web_src/js/features/comp/ReactionSelector.js  | 22 ++---
 web_src/js/features/notification.js           | 20 ++--
 web_src/js/features/repo-diff.js              | 12 +--
 web_src/js/features/repo-editor.js            | 20 ++--
 web_src/js/features/repo-graph.js             |  8 +-
 web_src/js/features/repo-home.js              | 72 +++++++--------
 web_src/js/features/repo-issue.js             | 92 +++++++++----------
 web_src/js/features/repo-legacy.js            | 22 ++---
 web_src/js/features/repo-projects.js          | 52 +++++------
 web_src/js/jquery.js                          |  2 +-
 14 files changed, 192 insertions(+), 192 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index f84cb39a41..5f291f13e7 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -487,7 +487,7 @@ rules:
   no-jquery/no-visibility: [2]
   no-jquery/no-when: [2]
   no-jquery/no-wrap: [2]
-  no-jquery/variable-pattern: [0]
+  no-jquery/variable-pattern: [2]
   no-label-var: [2]
   no-labels: [0] # handled by no-restricted-syntax
   no-lone-blocks: [2]
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 70ce9bd6a0..83289c8852 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -123,7 +123,7 @@ const sfc = {
       return -1;
     },
     scrollToActive() {
-      let el = this.$refs[`listItem${this.active}`];
+      let el = this.$refs[`listItem${this.active}`]; // eslint-disable-line no-jquery/variable-pattern
       if (!el || !el.length) return;
       if (Array.isArray(el)) {
         el = el[0];
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index ee4ade1f04..d99f606c8a 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -231,8 +231,8 @@ export function initDropzone(el) {
     init() {
       this.on('success', (file, data) => {
         file.uuid = data.uuid;
-        const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
-        $dropzone.find('.files').append(input);
+        const $input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
+        $dropzone.find('.files').append($input);
         // Create a "Copy Link" element, to conveniently copy the image
         // or file link as Markdown to the clipboard
         const copyLinkElement = document.createElement('div');
@@ -305,15 +305,15 @@ export function initGlobalLinkActions() {
       filter += `#${$this.attr('data-modal-id')}`;
     }
 
-    const dialog = $(`.delete.modal${filter}`);
-    dialog.find('.name').text($this.data('name'));
+    const $dialog = $(`.delete.modal${filter}`);
+    $dialog.find('.name').text($this.data('name'));
     for (const [key, value] of Object.entries(dataArray)) {
       if (key && key.startsWith('data')) {
-        dialog.find(`.${key}`).text(value);
+        $dialog.find(`.${key}`).text(value);
       }
     }
 
-    dialog.modal({
+    $dialog.modal({
       closable: false,
       onApprove: async () => {
         if ($this.data('type') === 'form') {
@@ -380,8 +380,8 @@ function initGlobalShowModal() {
         $attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p
       }
     }
-    const colorPickers = $modal.find('.color-picker');
-    if (colorPickers.length > 0) {
+    const $colorPickers = $modal.find('.color-picker');
+    if ($colorPickers.length > 0) {
       initCompColorPicker(); // FIXME: this might cause duplicate init
     }
     $modal.modal('setting', {
diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js
index 2a22190e10..2e7e1df669 100644
--- a/web_src/js/features/comp/LabelEdit.js
+++ b/web_src/js/features/comp/LabelEdit.js
@@ -6,23 +6,23 @@ function isExclusiveScopeName(name) {
 }
 
 function updateExclusiveLabelEdit(form) {
-  const nameInput = $(`${form} .label-name-input`);
-  const exclusiveField = $(`${form} .label-exclusive-input-field`);
-  const exclusiveCheckbox = $(`${form} .label-exclusive-input`);
-  const exclusiveWarning = $(`${form} .label-exclusive-warning`);
+  const $nameInput = $(`${form} .label-name-input`);
+  const $exclusiveField = $(`${form} .label-exclusive-input-field`);
+  const $exclusiveCheckbox = $(`${form} .label-exclusive-input`);
+  const $exclusiveWarning = $(`${form} .label-exclusive-warning`);
 
-  if (isExclusiveScopeName(nameInput.val())) {
-    exclusiveField.removeClass('muted');
-    exclusiveField.removeAttr('aria-disabled');
-    if (exclusiveCheckbox.prop('checked') && exclusiveCheckbox.data('exclusive-warn')) {
-      exclusiveWarning.removeClass('gt-hidden');
+  if (isExclusiveScopeName($nameInput.val())) {
+    $exclusiveField.removeClass('muted');
+    $exclusiveField.removeAttr('aria-disabled');
+    if ($exclusiveCheckbox.prop('checked') && $exclusiveCheckbox.data('exclusive-warn')) {
+      $exclusiveWarning.removeClass('gt-hidden');
     } else {
-      exclusiveWarning.addClass('gt-hidden');
+      $exclusiveWarning.addClass('gt-hidden');
     }
   } else {
-    exclusiveField.addClass('muted');
-    exclusiveField.attr('aria-disabled', 'true');
-    exclusiveWarning.addClass('gt-hidden');
+    $exclusiveField.addClass('muted');
+    $exclusiveField.attr('aria-disabled', 'true');
+    $exclusiveWarning.addClass('gt-hidden');
   }
 }
 
@@ -46,18 +46,18 @@ export function initCompLabelEdit(selector) {
     $('.edit-label .color-picker').minicolors('value', $(this).data('color'));
     $('#label-modal-id').val($(this).data('id'));
 
-    const nameInput = $('.edit-label .label-name-input');
-    nameInput.val($(this).data('title'));
+    const $nameInput = $('.edit-label .label-name-input');
+    $nameInput.val($(this).data('title'));
 
-    const isArchivedCheckbox = $('.edit-label .label-is-archived-input');
-    isArchivedCheckbox.prop('checked', this.hasAttribute('data-is-archived'));
+    const $isArchivedCheckbox = $('.edit-label .label-is-archived-input');
+    $isArchivedCheckbox.prop('checked', this.hasAttribute('data-is-archived'));
 
-    const exclusiveCheckbox = $('.edit-label .label-exclusive-input');
-    exclusiveCheckbox.prop('checked', this.hasAttribute('data-exclusive'));
+    const $exclusiveCheckbox = $('.edit-label .label-exclusive-input');
+    $exclusiveCheckbox.prop('checked', this.hasAttribute('data-exclusive'));
     // Warn when label was previously not exclusive and used in issues
-    exclusiveCheckbox.data('exclusive-warn',
+    $exclusiveCheckbox.data('exclusive-warn',
       $(this).data('num-issues') > 0 &&
-      (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName(nameInput.val())));
+      (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName($nameInput.val())));
     updateExclusiveLabelEdit('.edit-label');
 
     $('.edit-label .label-desc-input').val($(this).data('description'));
diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js
index 76834f8844..6df4bde069 100644
--- a/web_src/js/features/comp/ReactionSelector.js
+++ b/web_src/js/features/comp/ReactionSelector.js
@@ -17,21 +17,21 @@ export function initCompReactionSelector($parent) {
 
     const data = await res.json();
     if (data && (data.html || data.empty)) {
-      const content = $(this).closest('.content');
-      let react = content.find('.segment.reactions');
-      if ((!data.empty || data.html === '') && react.length > 0) {
-        react.remove();
+      const $content = $(this).closest('.content');
+      let $react = $content.find('.segment.reactions');
+      if ((!data.empty || data.html === '') && $react.length > 0) {
+        $react.remove();
       }
       if (!data.empty) {
-        const attachments = content.find('.segment.bottom:first');
-        react = $(data.html);
-        if (attachments.length > 0) {
-          react.insertBefore(attachments);
+        const $attachments = $content.find('.segment.bottom:first');
+        $react = $(data.html);
+        if ($attachments.length > 0) {
+          $react.insertBefore($attachments);
         } else {
-          react.appendTo(content);
+          $react.appendTo($content);
         }
-        react.find('.dropdown').dropdown();
-        initCompReactionSelector(react);
+        $react.find('.dropdown').dropdown();
+        initCompReactionSelector($react);
       }
     }
   });
diff --git a/web_src/js/features/notification.js b/web_src/js/features/notification.js
index 1c95804e4c..b04ee0bf52 100644
--- a/web_src/js/features/notification.js
+++ b/web_src/js/features/notification.js
@@ -45,9 +45,9 @@ async function receiveUpdateCount(event) {
 }
 
 export function initNotificationCount() {
-  const notificationCount = $('.notification_count');
+  const $notificationCount = $('.notification_count');
 
-  if (!notificationCount.length) {
+  if (!$notificationCount.length) {
     return;
   }
 
@@ -55,7 +55,7 @@ export function initNotificationCount() {
   const startPeriodicPoller = (timeout, lastCount) => {
     if (timeout <= 0 || !Number.isFinite(timeout)) return;
     usingPeriodicPoller = true;
-    lastCount = lastCount ?? notificationCount.text();
+    lastCount = lastCount ?? $notificationCount.text();
     setTimeout(async () => {
       await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount);
     }, timeout);
@@ -143,8 +143,8 @@ async function updateNotificationCountWithCallback(callback, timeout, lastCount)
 }
 
 async function updateNotificationTable() {
-  const notificationDiv = $('#notification_div');
-  if (notificationDiv.length > 0) {
+  const $notificationDiv = $('#notification_div');
+  if ($notificationDiv.length > 0) {
     try {
       const params = new URLSearchParams(window.location.search);
       params.set('div-only', true);
@@ -158,7 +158,7 @@ async function updateNotificationTable() {
 
       const data = await response.text();
       if ($(data).data('sequence-number') === notificationSequenceNumber) {
-        notificationDiv.replaceWith(data);
+        $notificationDiv.replaceWith(data);
         initNotificationsTable();
       }
     } catch (error) {
@@ -177,14 +177,14 @@ async function updateNotificationCount() {
 
     const data = await response.json();
 
-    const notificationCount = $('.notification_count');
+    const $notificationCount = $('.notification_count');
     if (data.new === 0) {
-      notificationCount.addClass('gt-hidden');
+      $notificationCount.addClass('gt-hidden');
     } else {
-      notificationCount.removeClass('gt-hidden');
+      $notificationCount.removeClass('gt-hidden');
     }
 
-    notificationCount.text(`${data.new}`);
+    $notificationCount.text(`${data.new}`);
 
     return `${data.new}`;
   } catch (error) {
diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js
index 7c7c238b1a..ffe3fada85 100644
--- a/web_src/js/features/repo-diff.js
+++ b/web_src/js/features/repo-diff.js
@@ -99,10 +99,10 @@ function initRepoDiffConversationForm() {
       const data = await response.text();
 
       if ($(this).closest('.conversation-holder').length) {
-        const conversation = $(data);
-        $(this).closest('.conversation-holder').replaceWith(conversation);
-        conversation.find('.dropdown').dropdown();
-        initCompReactionSelector(conversation);
+        const $conversation = $(data);
+        $(this).closest('.conversation-holder').replaceWith($conversation);
+        $conversation.find('.dropdown').dropdown();
+        initCompReactionSelector($conversation);
       } else {
         window.location.reload();
       }
@@ -211,8 +211,8 @@ function initRepoDiffShowMore() {
 
 export function initRepoDiffView() {
   initRepoDiffConversationForm();
-  const diffFileList = $('#diff-file-list');
-  if (diffFileList.length === 0) return;
+  const $diffFileList = $('#diff-file-list');
+  if ($diffFileList.length === 0) return;
   initDiffFileTree();
   initDiffCommitSelect();
   initRepoDiffShowMore();
diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js
index 4fe7ed8a4d..fea98e2df8 100644
--- a/web_src/js/features/repo-editor.js
+++ b/web_src/js/features/repo-editor.js
@@ -15,9 +15,9 @@ function initEditPreviewTab($form) {
       const $this = $(this);
       let context = `${$this.data('context')}/`;
       const mode = $this.data('markup-mode') || 'comment';
-      const treePathEl = $form.find('input#tree_path');
-      if (treePathEl.length > 0) {
-        context += treePathEl.val();
+      const $treePathEl = $form.find('input#tree_path');
+      if ($treePathEl.length > 0) {
+        context += $treePathEl.val();
       }
       context = context.substring(0, context.lastIndexOf('/'));
 
@@ -25,7 +25,7 @@ function initEditPreviewTab($form) {
       formData.append('mode', mode);
       formData.append('context', context);
       formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val());
-      formData.append('file_path', treePathEl.val());
+      formData.append('file_path', $treePathEl.val());
       try {
         const response = await POST($this.data('url'), {data: formData});
         const data = await response.text();
@@ -78,11 +78,11 @@ export function initRepoEditor() {
   const joinTreePath = ($fileNameEl) => {
     const parts = [];
     $('.breadcrumb span.section').each(function () {
-      const element = $(this);
-      if (element.find('a').length) {
-        parts.push(element.find('a').text());
+      const $element = $(this);
+      if ($element.find('a').length) {
+        parts.push($element.find('a').text());
       } else {
-        parts.push(element.text());
+        parts.push($element.text());
       }
     });
     if ($fileNameEl.val()) parts.push($fileNameEl.val());
@@ -181,6 +181,6 @@ export function renderPreviewPanelContent($panelPreviewer, data) {
   $panelPreviewer.html(data);
   initMarkupContent();
 
-  const refIssues = $panelPreviewer.find('p .ref-issue');
-  attachRefIssueContextPopup(refIssues);
+  const $refIssues = $panelPreviewer.find('p .ref-issue');
+  attachRefIssueContextPopup($refIssues);
 }
diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js
index c83f448b76..4fbf95b9d5 100644
--- a/web_src/js/features/repo-graph.js
+++ b/web_src/js/features/repo-graph.js
@@ -63,10 +63,10 @@ export function initRepoGraphGit() {
     (async () => {
       const response = await GET(String(ajaxUrl));
       const html = await response.text();
-      const div = $(html);
-      $('#pagination').html(div.find('#pagination').html());
-      $('#rel-container').html(div.find('#rel-container').html());
-      $('#rev-container').html(div.find('#rev-container').html());
+      const $div = $(html);
+      $('#pagination').html($div.find('#pagination').html());
+      $('#rel-container').html($div.find('#rel-container').html());
+      $('#rev-container').html($div.find('#rev-container').html());
       $('#loading-indicator').addClass('gt-hidden');
       $('#rel-container').removeClass('gt-hidden');
       $('#rev-container').removeClass('gt-hidden');
diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js
index bbba7b103e..50f324d788 100644
--- a/web_src/js/features/repo-home.js
+++ b/web_src/js/features/repo-home.js
@@ -6,55 +6,55 @@ import {POST} from '../modules/fetch.js';
 const {appSubUrl} = window.config;
 
 export function initRepoTopicBar() {
-  const mgrBtn = $('#manage_topic');
-  if (!mgrBtn.length) return;
-  const editDiv = $('#topic_edit');
-  const viewDiv = $('#repo-topics');
-  const saveBtn = $('#save_topic');
-  const topicDropdown = $('#topic_edit .dropdown');
-  const topicForm = editDiv; // the old logic, editDiv is topicForm
-  const topicDropdownSearch = topicDropdown.find('input.search');
+  const $mgrBtn = $('#manage_topic');
+  if (!$mgrBtn.length) return;
+  const $editDiv = $('#topic_edit');
+  const $viewDiv = $('#repo-topics');
+  const $saveBtn = $('#save_topic');
+  const $topicDropdown = $('#topic_edit .dropdown');
+  const $topicForm = $editDiv; // the old logic, $editDiv is topicForm
+  const $topicDropdownSearch = $topicDropdown.find('input.search');
   const topicPrompts = {
-    countPrompt: topicDropdown.attr('data-text-count-prompt'),
-    formatPrompt: topicDropdown.attr('data-text-format-prompt'),
+    countPrompt: $topicDropdown.attr('data-text-count-prompt'),
+    formatPrompt: $topicDropdown.attr('data-text-format-prompt'),
   };
 
-  mgrBtn.on('click', () => {
-    hideElem(viewDiv);
-    showElem(editDiv);
-    topicDropdownSearch.focus();
+  $mgrBtn.on('click', () => {
+    hideElem($viewDiv);
+    showElem($editDiv);
+    $topicDropdownSearch.trigger('focus');
   });
 
   $('#cancel_topic_edit').on('click', () => {
-    hideElem(editDiv);
-    showElem(viewDiv);
-    mgrBtn.focus();
+    hideElem($editDiv);
+    showElem($viewDiv);
+    $mgrBtn.trigger('focus');
   });
 
-  saveBtn.on('click', async () => {
+  $saveBtn.on('click', async () => {
     const topics = $('input[name=topics]').val();
 
     const data = new FormData();
     data.append('topics', topics);
 
-    const response = await POST(saveBtn.attr('data-link'), {data});
+    const response = await POST($saveBtn.attr('data-link'), {data});
 
     if (response.ok) {
       const responseData = await response.json();
       if (responseData.status === 'ok') {
-        viewDiv.children('.topic').remove();
+        $viewDiv.children('.topic').remove();
         if (topics.length) {
           const topicArray = topics.split(',');
           topicArray.sort();
           for (const topic of topicArray) {
-            const link = $('<a class="ui repo-topic large label topic gt-m-0"></a>');
-            link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`);
-            link.text(topic);
-            link.insertBefore(mgrBtn); // insert all new topics before manage button
+            const $link = $('<a class="ui repo-topic large label topic gt-m-0"></a>');
+            $link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`);
+            $link.text(topic);
+            $link.insertBefore($mgrBtn); // insert all new topics before manage button
           }
         }
-        hideElem(editDiv);
-        showElem(viewDiv);
+        hideElem($editDiv);
+        showElem($viewDiv);
       }
     } else if (response.status === 422) {
       const responseData = await response.json();
@@ -62,10 +62,10 @@ export function initRepoTopicBar() {
         topicPrompts.formatPrompt = responseData.message;
 
         const {invalidTopics} = responseData;
-        const topicLabels = topicDropdown.children('a.ui.label');
+        const $topicLabels = $topicDropdown.children('a.ui.label');
         for (const [index, value] of topics.split(',').entries()) {
           if (invalidTopics.includes(value)) {
-            topicLabels.eq(index).removeClass('green').addClass('red');
+            $topicLabels.eq(index).removeClass('green').addClass('red');
           }
         }
       } else {
@@ -74,10 +74,10 @@ export function initRepoTopicBar() {
     }
 
     // Always validate the form
-    topicForm.form('validate form');
+    $topicForm.form('validate form');
   });
 
-  topicDropdown.dropdown({
+  $topicDropdown.dropdown({
     allowAdditions: true,
     forceSelection: false,
     fullTextSearch: 'exact',
@@ -100,7 +100,7 @@ export function initRepoTopicBar() {
         const query = stripTags(this.urlData.query.trim());
         let found_query = false;
         const current_topics = [];
-        topicDropdown.find('a.label.visible').each((_, el) => {
+        $topicDropdown.find('a.label.visible').each((_, el) => {
           current_topics.push(el.getAttribute('data-value'));
         });
 
@@ -150,15 +150,15 @@ export function initRepoTopicBar() {
   });
 
   $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
-    const topics = topicDropdown.children('a.ui.label');
-    const status = topics.length === 0 || topics.last().attr('data-value').match(regExp);
+    const $topics = $topicDropdown.children('a.ui.label');
+    const status = $topics.length === 0 || $topics.last().attr('data-value').match(regExp);
     if (!status) {
-      topics.last().removeClass('green').addClass('red');
+      $topics.last().removeClass('green').addClass('red');
     }
-    return status && topicDropdown.children('a.ui.label.red').length === 0;
+    return status && $topicDropdown.children('a.ui.label.red').length === 0;
   };
 
-  topicForm.form({
+  $topicForm.form({
     on: 'change',
     inline: true,
     fields: {
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 01c30a9e53..bca062bcc7 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -144,9 +144,9 @@ export function initRepoIssueSidebarList() {
 
   $('.menu .ui.dropdown.label-filter').on('keydown', (e) => {
     if (e.altKey && e.keyCode === 13) {
-      const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected');
-      if (selectedItems.length > 0) {
-        excludeLabel($(selectedItems[0]));
+      const $selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected');
+      if ($selectedItems.length > 0) {
+        excludeLabel($($selectedItems[0]));
       }
     }
   });
@@ -214,12 +214,12 @@ export function initRepoIssueDependencyDelete() {
 export function initRepoIssueCodeCommentCancel() {
   // Cancel inline code comment
   $(document).on('click', '.cancel-code-comment', (e) => {
-    const form = $(e.currentTarget).closest('form');
-    if (form.length > 0 && form.hasClass('comment-form')) {
-      form.addClass('gt-hidden');
-      showElem(form.closest('.comment-code-cloud').find('button.comment-form-reply'));
+    const $form = $(e.currentTarget).closest('form');
+    if ($form.length > 0 && $form.hasClass('comment-form')) {
+      $form.addClass('gt-hidden');
+      showElem($form.closest('.comment-code-cloud').find('button.comment-form-reply'));
     } else {
-      form.closest('.comment-code-cloud').remove();
+      $form.closest('.comment-code-cloud').remove();
     }
   });
 }
@@ -370,10 +370,10 @@ export function initRepoIssueComments() {
   });
 
   $(document).on('click', (event) => {
-    const urlTarget = $(':target');
-    if (urlTarget.length === 0) return;
+    const $urlTarget = $(':target');
+    if ($urlTarget.length === 0) return;
 
-    const urlTargetId = urlTarget.attr('id');
+    const urlTargetId = $urlTarget.attr('id');
     if (!urlTargetId) return;
     if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
 
@@ -390,18 +390,18 @@ export function initRepoIssueComments() {
 
 export async function handleReply($el) {
   hideElem($el);
-  const form = $el.closest('.comment-code-cloud').find('.comment-form');
-  form.removeClass('gt-hidden');
+  const $form = $el.closest('.comment-code-cloud').find('.comment-form');
+  $form.removeClass('gt-hidden');
 
-  const $textarea = form.find('textarea');
+  const $textarea = $form.find('textarea');
   let editor = getComboMarkdownEditor($textarea);
   if (!editor) {
     // FIXME: the initialization of the dropzone is not consistent.
     // When the page is loaded, the dropzone is initialized by initGlobalDropzone, but the editor is not initialized.
     // When the form is submitted and partially reload, none of them is initialized.
-    const dropzone = form.find('.dropzone')[0];
+    const dropzone = $form.find('.dropzone')[0];
     if (!dropzone.dropzone) initDropzone(dropzone);
-    editor = await initComboMarkdownEditor(form.find('.combo-markdown-editor'));
+    editor = await initComboMarkdownEditor($form.find('.combo-markdown-editor'));
   }
   editor.focus();
   return editor;
@@ -413,30 +413,30 @@ export function initRepoPullRequestReview() {
     if (window.history.scrollRestoration !== 'manual') {
       window.history.scrollRestoration = 'manual';
     }
-    const commentDiv = $(window.location.hash);
-    if (commentDiv) {
+    const $commentDiv = $(window.location.hash);
+    if ($commentDiv) {
       // get the name of the parent id
-      const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id');
+      const groupID = $commentDiv.closest('div[id^="code-comments-"]').attr('id');
       if (groupID && groupID.startsWith('code-comments-')) {
         const id = groupID.slice(14);
-        const ancestorDiffBox = commentDiv.closest('.diff-file-box');
+        const $ancestorDiffBox = $commentDiv.closest('.diff-file-box');
         // on pages like conversation, there is no diff header
-        const diffHeader = ancestorDiffBox.find('.diff-file-header');
+        const $diffHeader = $ancestorDiffBox.find('.diff-file-header');
         // offset is for scrolling
         let offset = 30;
-        if (diffHeader[0]) {
-          offset += $('.diff-detail-box').outerHeight() + diffHeader.outerHeight();
+        if ($diffHeader[0]) {
+          offset += $('.diff-detail-box').outerHeight() + $diffHeader.outerHeight();
         }
         $(`#show-outdated-${id}`).addClass('gt-hidden');
         $(`#code-comments-${id}`).removeClass('gt-hidden');
         $(`#code-preview-${id}`).removeClass('gt-hidden');
         $(`#hide-outdated-${id}`).removeClass('gt-hidden');
         // if the comment box is folded, expand it
-        if (ancestorDiffBox.attr('data-folded') && ancestorDiffBox.attr('data-folded') === 'true') {
-          setFileFolding(ancestorDiffBox[0], ancestorDiffBox.find('.fold-file')[0], false);
+        if ($ancestorDiffBox.attr('data-folded') && $ancestorDiffBox.attr('data-folded') === 'true') {
+          setFileFolding($ancestorDiffBox[0], $ancestorDiffBox.find('.fold-file')[0], false);
         }
         window.scrollTo({
-          top: commentDiv.offset().top - offset,
+          top: $commentDiv.offset().top - offset,
           behavior: 'instant'
         });
       }
@@ -504,12 +504,12 @@ export function initRepoPullRequestReview() {
     const side = $(this).data('side');
     const idx = $(this).data('idx');
     const path = $(this).closest('[data-path]').data('path');
-    const tr = $(this).closest('tr');
-    const lineType = tr.data('line-type');
+    const $tr = $(this).closest('tr');
+    const lineType = $tr.data('line-type');
 
-    let ntr = tr.next();
-    if (!ntr.hasClass('add-comment')) {
-      ntr = $(`
+    let $ntr = $tr.next();
+    if (!$ntr.hasClass('add-comment')) {
+      $ntr = $(`
         <tr class="add-comment" data-line-type="${lineType}">
           ${isSplit ? `
             <td class="add-comment-left" colspan="4"></td>
@@ -518,22 +518,22 @@ export function initRepoPullRequestReview() {
             <td class="add-comment-left add-comment-right" colspan="5"></td>
           `}
         </tr>`);
-      tr.after(ntr);
+      $tr.after($ntr);
     }
 
-    const td = ntr.find(`.add-comment-${side}`);
-    const commentCloud = td.find('.comment-code-cloud');
-    if (commentCloud.length === 0 && !ntr.find('button[name="pending_review"]').length) {
+    const $td = $ntr.find(`.add-comment-${side}`);
+    const $commentCloud = $td.find('.comment-code-cloud');
+    if ($commentCloud.length === 0 && !$ntr.find('button[name="pending_review"]').length) {
       try {
         const response = await GET($(this).closest('[data-new-comment-url]').attr('data-new-comment-url'));
         const html = await response.text();
-        td.html(html);
-        td.find("input[name='line']").val(idx);
-        td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
-        td.find("input[name='path']").val(path);
+        $td.html(html);
+        $td.find("input[name='line']").val(idx);
+        $td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
+        $td.find("input[name='path']").val(path);
 
-        initDropzone(td.find('.dropzone')[0]);
-        const editor = await initComboMarkdownEditor(td.find('.combo-markdown-editor'));
+        initDropzone($td.find('.dropzone')[0]);
+        const editor = await initComboMarkdownEditor($td.find('.combo-markdown-editor'));
         editor.focus();
       } catch (error) {
         console.error(error);
@@ -646,18 +646,18 @@ export function initRepoIssueTitleEdit() {
 
 export function initRepoIssueBranchSelect() {
   const changeBranchSelect = function () {
-    const selectionTextField = $('#pull-target-branch');
+    const $selectionTextField = $('#pull-target-branch');
 
-    const baseName = selectionTextField.data('basename');
+    const baseName = $selectionTextField.data('basename');
     const branchNameNew = $(this).data('branch');
-    const branchNameOld = selectionTextField.data('branch');
+    const branchNameOld = $selectionTextField.data('branch');
 
     // Replace branch name to keep translation from HTML template
-    selectionTextField.html(selectionTextField.html().replace(
+    $selectionTextField.html($selectionTextField.html().replace(
       `${baseName}:${branchNameOld}`,
       `${baseName}:${branchNameNew}`
     ));
-    selectionTextField.data('branch', branchNameNew); // update branch name in setting
+    $selectionTextField.data('branch', branchNameNew); // update branch name in setting
   };
   $('#branch-select > .item').on('click', changeBranchSelect);
 }
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 24fcc7c223..1df409c79e 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -76,11 +76,11 @@ export function initRepoCommentForm() {
       }
 
       if (editMode === 'true') {
-        const form = $('#update_issueref_form');
+        const $form = $('#update_issueref_form');
         const params = new URLSearchParams();
         params.append('ref', selectedValue);
         try {
-          await POST(form.attr('action'), {data: params});
+          await POST($form.attr('action'), {data: params});
           window.location.reload();
         } catch (error) {
           console.error(error);
@@ -139,7 +139,7 @@ export function initRepoCommentForm() {
 
       hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
 
-      const clickedItem = $(this);
+      const $clickedItem = $(this);
       const scope = $(this).attr('data-scope');
 
       $(this).parent().find('.item').each(function () {
@@ -148,10 +148,10 @@ export function initRepoCommentForm() {
           if ($(this).attr('data-scope') !== scope) {
             return true;
           }
-          if (!$(this).is(clickedItem) && !$(this).hasClass('checked')) {
+          if (!$(this).is($clickedItem) && !$(this).hasClass('checked')) {
             return true;
           }
-        } else if (!$(this).is(clickedItem)) {
+        } else if (!$(this).is($clickedItem)) {
           // Toggle for other labels
           return true;
         }
@@ -352,8 +352,8 @@ async function onEditContent(event) {
         this.on('success', (file, data) => {
           file.uuid = data.uuid;
           fileUuidDict[file.uuid] = {submitted: false};
-          const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
-          $dropzone.find('.files').append(input);
+          const $input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
+          $dropzone.find('.files').append($input);
         });
         this.on('removedfile', async (file) => {
           if (disableRemovedfileEvent) return;
@@ -390,8 +390,8 @@ async function onEditContent(event) {
               dz.files.push(attachment);
               fileUuidDict[attachment.uuid] = {submitted: true};
               $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%');
-              const input = $(`<input id="${attachment.uuid}" name="files" type="hidden">`).val(attachment.uuid);
-              $dropzone.find('.files').append(input);
+              const $input = $(`<input id="${attachment.uuid}" name="files" type="hidden">`).val(attachment.uuid);
+              $dropzone.find('.files').append($input);
             }
           } catch (error) {
             console.error(error);
@@ -430,8 +430,8 @@ async function onEditContent(event) {
       } else {
         $renderContent.html(data.content);
         $rawContent.text(comboMarkdownEditor.value());
-        const refIssues = $renderContent.find('p .ref-issue');
-        attachRefIssueContextPopup(refIssues);
+        const $refIssues = $renderContent.find('p .ref-issue');
+        attachRefIssueContextPopup($refIssues);
       }
       const $content = $segment;
       if (!$content.find('.dropzone-attachments').length) {
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index 5dd80b29b9..1f86711ab1 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -105,14 +105,14 @@ export function initRepoProject() {
   const _promise = initRepoProjectSortable();
 
   $('.edit-project-column-modal').each(function () {
-    const projectHeader = $(this).closest('.project-column-header');
-    const projectTitleLabel = projectHeader.find('.project-column-title');
-    const projectTitleInput = $(this).find('.project-column-title-input');
-    const projectColorInput = $(this).find('#new_project_column_color');
-    const boardColumn = $(this).closest('.project-column');
+    const $projectHeader = $(this).closest('.project-column-header');
+    const $projectTitleLabel = $projectHeader.find('.project-column-title');
+    const $projectTitleInput = $(this).find('.project-column-title-input');
+    const $projectColorInput = $(this).find('#new_project_column_color');
+    const $boardColumn = $(this).closest('.project-column');
 
-    if (boardColumn.css('backgroundColor')) {
-      setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
+    if ($boardColumn.css('backgroundColor')) {
+      setLabelColor($projectHeader, rgbToHex($boardColumn.css('backgroundColor')));
     }
 
     $(this).find('.edit-project-column-button').on('click', async function (e) {
@@ -121,34 +121,34 @@ export function initRepoProject() {
       try {
         await PUT($(this).data('url'), {
           data: {
-            title: projectTitleInput.val(),
-            color: projectColorInput.val(),
+            title: $projectTitleInput.val(),
+            color: $projectColorInput.val(),
           },
         });
       } catch (error) {
         console.error(error);
       } finally {
-        projectTitleLabel.text(projectTitleInput.val());
-        projectTitleInput.closest('form').removeClass('dirty');
-        if (projectColorInput.val()) {
-          setLabelColor(projectHeader, projectColorInput.val());
+        $projectTitleLabel.text($projectTitleInput.val());
+        $projectTitleInput.closest('form').removeClass('dirty');
+        if ($projectColorInput.val()) {
+          setLabelColor($projectHeader, $projectColorInput.val());
         }
-        boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
+        $boardColumn.attr('style', `background: ${$projectColorInput.val()}!important`);
         $('.ui.modal').modal('hide');
       }
     });
   });
 
   $('.default-project-column-modal').each(function () {
-    const boardColumn = $(this).closest('.project-column');
-    const showButton = $(boardColumn).find('.default-project-column-show');
-    const commitButton = $(this).find('.actions > .ok.button');
+    const $boardColumn = $(this).closest('.project-column');
+    const $showButton = $($boardColumn).find('.default-project-column-show');
+    const $commitButton = $(this).find('.actions > .ok.button');
 
-    $(commitButton).on('click', async (e) => {
+    $($commitButton).on('click', async (e) => {
       e.preventDefault();
 
       try {
-        await POST($(showButton).data('url'));
+        await POST($($showButton).data('url'));
       } catch (error) {
         console.error(error);
       } finally {
@@ -158,11 +158,11 @@ export function initRepoProject() {
   });
 
   $('.show-delete-project-column-modal').each(function () {
-    const deleteColumnModal = $(`${$(this).attr('data-modal')}`);
-    const deleteColumnButton = deleteColumnModal.find('.actions > .ok.button');
+    const $deleteColumnModal = $(`${$(this).attr('data-modal')}`);
+    const $deleteColumnButton = $deleteColumnModal.find('.actions > .ok.button');
     const deleteUrl = $(this).attr('data-url');
 
-    deleteColumnButton.on('click', async (e) => {
+    $deleteColumnButton.on('click', async (e) => {
       e.preventDefault();
 
       try {
@@ -177,13 +177,13 @@ export function initRepoProject() {
 
   $('#new_project_column_submit').on('click', (e) => {
     e.preventDefault();
-    const columnTitle = $('#new_project_column');
-    const projectColorInput = $('#new_project_column_color_picker');
-    if (!columnTitle.val()) {
+    const $columnTitle = $('#new_project_column');
+    const $projectColorInput = $('#new_project_column_color_picker');
+    if (!$columnTitle.val()) {
       return;
     }
     const url = e.target.getAttribute('data-url');
-    createNewColumn(url, columnTitle, projectColorInput);
+    createNewColumn(url, $columnTitle, $projectColorInput);
   });
 }
 
diff --git a/web_src/js/jquery.js b/web_src/js/jquery.js
index 892e2763cb..6b2199896c 100644
--- a/web_src/js/jquery.js
+++ b/web_src/js/jquery.js
@@ -1,3 +1,3 @@
 import $ from 'jquery';
 
-window.$ = window.jQuery = $;
+window.$ = window.jQuery = $; // eslint-disable-line no-jquery/variable-pattern