mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	Add new JS linter rules (#17699)
* Add new JS linter rules Adds a few useful rules from eslint-plugin-github. Notable changes: - Forbid dataset usage, its camel-casing behaviour makes it hard to grep for attributes. - Forbid .then() and .catch(), we should generally prefer await for new code. For rare cases where they are useful, a eslint-disable-line directive can be set. - Add docs js to linting * also enable github/array-foreach * small tweak Co-authored-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -52,12 +52,8 @@ export default function initGlobalCopyToClipboardListener() { | ||||
|     // in case <button data-clipboard-text><svg></button>, so we just search | ||||
|     // up to 3 levels for performance | ||||
|     for (let i = 0; i < 3 && target; i++) { | ||||
|       let text; | ||||
|       if (target.dataset.clipboardText) { | ||||
|         text = target.dataset.clipboardText; | ||||
|       } else if (target.dataset.clipboardTarget) { | ||||
|         text = document.querySelector(target.dataset.clipboardTarget)?.value; | ||||
|       } | ||||
|       const text = target.getAttribute('data-clipboard-text') || document.querySelector(target.getAttribute('data-clipboard-target'))?.value; | ||||
|  | ||||
|       if (text) { | ||||
|         e.preventDefault(); | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ const baseOptions = { | ||||
|  | ||||
| function getEditorconfig(input) { | ||||
|   try { | ||||
|     return JSON.parse(input.dataset.editorconfig); | ||||
|     return JSON.parse(input.getAttribute('data-editorconfig')); | ||||
|   } catch { | ||||
|     return null; | ||||
|   } | ||||
| @@ -132,14 +132,15 @@ function getFileBasedOptions(filename, lineWrapExts) { | ||||
| export async function createCodeEditor(textarea, filenameInput, previewFileModes) { | ||||
|   const filename = basename(filenameInput.value); | ||||
|   const previewLink = document.querySelector('a[data-tab=preview]'); | ||||
|   const markdownExts = (textarea.dataset.markdownFileExts || '').split(','); | ||||
|   const lineWrapExts = (textarea.dataset.lineWrapExtensions || '').split(','); | ||||
|   const markdownExts = (textarea.getAttribute('data-markdown-file-exts') || '').split(','); | ||||
|   const lineWrapExts = (textarea.getAttribute('data-line-wrap-extensions') || '').split(','); | ||||
|   const isMarkdown = markdownExts.includes(extname(filename)); | ||||
|   const editorConfig = getEditorconfig(filenameInput); | ||||
|  | ||||
|   if (previewLink) { | ||||
|     if (isMarkdown && (previewFileModes || []).includes('markdown')) { | ||||
|       previewLink.dataset.url = previewLink.dataset.url.replace(/(.*)\/.*/i, `$1/markdown`); | ||||
|       const newUrl = (previewLink.getAttribute('data-url') || '').replace(/(.*)\/.*/i, `$1/markdown`); | ||||
|       previewLink.setAttribute('data-url', newUrl); | ||||
|       previewLink.style.display = ''; | ||||
|     } else { | ||||
|       previewLink.style.display = 'none'; | ||||
|   | ||||
| @@ -12,17 +12,25 @@ export function initCommonIssue() { | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   $('.issue-action').on('click', function () { | ||||
|     let {action, elementId, url} = this.dataset; | ||||
|   $('.issue-action').on('click', async function () { | ||||
|     let action = this.getAttribute('data-action'); | ||||
|     let elementId = this.getAttribute('data-element-id'); | ||||
|     const url = this.getAttribute('data-url'); | ||||
|     const issueIDs = $('.issue-checkbox').children('input:checked').map((_, el) => { | ||||
|       return el.dataset.issueId; | ||||
|       return el.getAttribute('data-issue-id'); | ||||
|     }).get().join(','); | ||||
|     if (elementId === '0' && url.substr(-9) === '/assignee') { | ||||
|       elementId = ''; | ||||
|       action = 'clear'; | ||||
|     } | ||||
|     updateIssuesMeta(url, action, issueIDs, elementId).then(() => { | ||||
|       // NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the checkboxes stay checked after reload | ||||
|     updateIssuesMeta( | ||||
|       url, | ||||
|       action, | ||||
|       issueIDs, | ||||
|       elementId | ||||
|     ).then(() => { // eslint-disable-line github/no-then | ||||
|       // NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the | ||||
|       // checkboxes stay checked after reload | ||||
|       if (action === 'close' || action === 'open') { | ||||
|         // uncheck all checkboxes | ||||
|         $('.issue-checkbox input[type="checkbox"]').each((_, e) => { e.checked = false }); | ||||
| @@ -31,8 +39,8 @@ export function initCommonIssue() { | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   // NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay checked after reload | ||||
|   // trigger ckecked event, if checkboxes are checked on load | ||||
|   // NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay | ||||
|   // checked after reload trigger ckecked event, if checkboxes are checked on load | ||||
|   $('.issue-checkbox input[type="checkbox"]:checked').first().each((_, e) => { | ||||
|     e.checked = false; | ||||
|     $(e).trigger('click'); | ||||
|   | ||||
| @@ -59,7 +59,7 @@ export function initCompImagePaste($target) { | ||||
|     if (!dropzone) { | ||||
|       return; | ||||
|     } | ||||
|     const uploadUrl = dropzone.dataset.uploadUrl; | ||||
|     const uploadUrl = dropzone.getAttribute('data-upload-url'); | ||||
|     const dropzoneFiles = dropzone.querySelector('.files'); | ||||
|     for (const textarea of this.querySelectorAll('textarea')) { | ||||
|       textarea.addEventListener('paste', async (e) => { | ||||
| @@ -77,7 +77,7 @@ export function initCompImagePaste($target) { | ||||
| } | ||||
|  | ||||
| export function initSimpleMDEImagePaste(simplemde, dropzone, files) { | ||||
|   const uploadUrl = dropzone.dataset.uploadUrl; | ||||
|   const uploadUrl = dropzone.getAttribute('data-upload-url'); | ||||
|   simplemde.codemirror.on('paste', async (_, e) => { | ||||
|     for (const img of clipboardPastedImages(e)) { | ||||
|       const name = img.name.substr(0, img.name.lastIndexOf('.')); | ||||
|   | ||||
| @@ -8,11 +8,11 @@ export default function initHeatmap() { | ||||
|  | ||||
|   try { | ||||
|     const heatmap = {}; | ||||
|     JSON.parse(el.dataset.heatmapData).forEach(({contributions, timestamp}) => { | ||||
|     for (const {contributions, timestamp} of JSON.parse(el.getAttribute('data-heatmap-data'))) { | ||||
|       // Convert to user timezone and sum contributions by date | ||||
|       const dateStr = new Date(timestamp * 1000).toDateString(); | ||||
|       heatmap[dateStr] = (heatmap[dateStr] || 0) + contributions; | ||||
|     }); | ||||
|     } | ||||
|  | ||||
|     const values = Object.keys(heatmap).map((v) => { | ||||
|       return {date: new Date(v), count: heatmap[v]}; | ||||
|   | ||||
| @@ -131,12 +131,14 @@ export function initRepoCodeView() { | ||||
|   } | ||||
|   $(document).on('click', '.fold-file', ({currentTarget}) => { | ||||
|     const box = currentTarget.closest('.file-content'); | ||||
|     const folded = box.dataset.folded !== 'true'; | ||||
|     const folded = box.getAttribute('data-folded') !== 'true'; | ||||
|     currentTarget.innerHTML = svg(`octicon-chevron-${folded ? 'right' : 'down'}`, 18); | ||||
|     box.dataset.folded = String(folded); | ||||
|     box.setAttribute('data-folded', String(folded)); | ||||
|   }); | ||||
|   $(document).on('click', '.blob-excerpt', async ({currentTarget}) => { | ||||
|     const {url, query, anchor} = currentTarget.dataset; | ||||
|     const url = currentTarget.getAttribute('data-url'); | ||||
|     const query = currentTarget.getAttribute('data-query'); | ||||
|     const anchor = currentTarget.getAttribute('data-anchor'); | ||||
|     if (!url) return; | ||||
|     const blob = await $.get(`${url}?${query}&anchor=${anchor}`); | ||||
|     currentTarget.closest('tr').outerHTML = blob; | ||||
|   | ||||
| @@ -57,13 +57,13 @@ export function initRepoTopicBar() { | ||||
|           const {invalidTopics} = xhr.responseJSON; | ||||
|           const topicLables = topicDropdown.children('a.ui.label'); | ||||
|  | ||||
|           topics.split(',').forEach((value, index) => { | ||||
|           for (const [index, value] of topics.split(',').entries()) { | ||||
|             for (let i = 0; i < invalidTopics.length; i++) { | ||||
|               if (invalidTopics[i] === value) { | ||||
|                 topicLables.eq(index).removeClass('green').addClass('red'); | ||||
|               } | ||||
|             } | ||||
|           }); | ||||
|           } | ||||
|         } else { | ||||
|           topicPrompts.countPrompt = xhr.responseJSON.message; | ||||
|         } | ||||
| @@ -101,7 +101,9 @@ export function initRepoTopicBar() { | ||||
|         const query = stripTags(this.urlData.query.trim()); | ||||
|         let found_query = false; | ||||
|         const current_topics = []; | ||||
|         topicDropdown.find('div.label.visible.topic,a.label.visible').each((_, e) => { current_topics.push(e.dataset.value) }); | ||||
|         topicDropdown.find('div.label.visible.topic,a.label.visible').each((_, el) => { | ||||
|           current_topics.push(el.getAttribute('data-value')); | ||||
|         }); | ||||
|  | ||||
|         if (res.topics) { | ||||
|           let found = false; | ||||
|   | ||||
| @@ -182,7 +182,8 @@ export function initRepoIssueCommentDelete() { | ||||
| export function initRepoIssueDependencyDelete() { | ||||
|   // Delete Issue dependency | ||||
|   $(document).on('click', '.delete-dependency-button', (e) => { | ||||
|     const {id, type} = e.currentTarget.dataset; | ||||
|     const id = e.currentTarget.getAttribute('data-id'); | ||||
|     const type = e.currentTarget.getAttribute('data-type'); | ||||
|  | ||||
|     $('.remove-dependency').modal({ | ||||
|       closable: false, | ||||
| @@ -348,22 +349,19 @@ export async function updateIssuesMeta(url, action, issueIds, elementId) { | ||||
| export function initRepoIssueComments() { | ||||
|   if ($('.repository.view.issue .timeline').length === 0) return; | ||||
|  | ||||
|   $('.re-request-review').on('click', function (event) { | ||||
|   $('.re-request-review').on('click', function (e) { | ||||
|     e.preventDefault(); | ||||
|     const url = $(this).data('update-url'); | ||||
|     const issueId = $(this).data('issue-id'); | ||||
|     const id = $(this).data('id'); | ||||
|     const isChecked = $(this).hasClass('checked'); | ||||
|  | ||||
|     event.preventDefault(); | ||||
|     updateIssuesMeta( | ||||
|       url, | ||||
|       isChecked ? 'detach' : 'attach', | ||||
|       issueId, | ||||
|       id, | ||||
|     ).then(() => { | ||||
|       window.location.reload(); | ||||
|     }); | ||||
|     return false; | ||||
|     ).then(() => window.location.reload()); // eslint-disable-line github/no-then | ||||
|   }); | ||||
|  | ||||
|   $('.dismiss-review-btn').on('click', function (e) { | ||||
| @@ -550,7 +548,10 @@ export function initRepoIssueWipToggle() { | ||||
|   // Toggle WIP | ||||
|   $('.toggle-wip a, .toggle-wip button').on('click', async (e) => { | ||||
|     e.preventDefault(); | ||||
|     const {title, wipPrefix, updateUrl} = e.currentTarget.closest('.toggle-wip').dataset; | ||||
|     const toggleWip = e.currentTarget.closest('.toggle-wip'); | ||||
|     const title = toggleWip.getAttribute('data-title'); | ||||
|     const wipPrefix = toggleWip.getAttribute('data-wip-prefix'); | ||||
|     const updateUrl = toggleWip.getAttribute('data-update-url'); | ||||
|     await $.post(updateUrl, { | ||||
|       _csrf: csrfToken, | ||||
|       title: title?.startsWith(wipPrefix) ? title.substr(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`, | ||||
|   | ||||
| @@ -162,7 +162,7 @@ export function initRepoCommentForm() { | ||||
|           'clear', | ||||
|           $listMenu.data('issue-id'), | ||||
|           '', | ||||
|         ).then(() => window.location.reload()); | ||||
|         ).then(() => window.location.reload()); // eslint-disable-line github/no-then | ||||
|       } | ||||
|  | ||||
|       $(this).parent().find('.item').each(function () { | ||||
| @@ -205,7 +205,7 @@ export function initRepoCommentForm() { | ||||
|           '', | ||||
|           $menu.data('issue-id'), | ||||
|           $(this).data('id'), | ||||
|         ).then(() => window.location.reload()); | ||||
|         ).then(() => window.location.reload()); // eslint-disable-line github/no-then | ||||
|       } | ||||
|  | ||||
|       let icon = ''; | ||||
| @@ -238,7 +238,7 @@ export function initRepoCommentForm() { | ||||
|           '', | ||||
|           $menu.data('issue-id'), | ||||
|           $(this).data('id'), | ||||
|         ).then(() => window.location.reload()); | ||||
|         ).then(() => window.location.reload()); // eslint-disable-line github/no-then | ||||
|       } | ||||
|  | ||||
|       $list.find('.selected').html(''); | ||||
|   | ||||
| @@ -6,57 +6,54 @@ async function initRepoProjectSortable() { | ||||
|  | ||||
|   const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs'); | ||||
|   const boardColumns = document.getElementsByClassName('board-column'); | ||||
|   new Sortable( | ||||
|     els[0], | ||||
|     { | ||||
|       group: 'board-column', | ||||
|       draggable: '.board-column', | ||||
|       animation: 150, | ||||
|       ghostClass: 'card-ghost', | ||||
|       onSort: () => { | ||||
|         const board = document.getElementsByClassName('board')[0]; | ||||
|         const boardColumns = board.getElementsByClassName('board-column'); | ||||
|  | ||||
|         boardColumns.forEach((column, i) => { | ||||
|           if (parseInt($(column).data('sorting')) !== i) { | ||||
|             $.ajax({ | ||||
|               url: $(column).data('url'), | ||||
|               data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}), | ||||
|               headers: { | ||||
|                 'X-Csrf-Token': csrfToken, | ||||
|                 'X-Remote': true, | ||||
|               }, | ||||
|               contentType: 'application/json', | ||||
|               method: 'PUT', | ||||
|             }); | ||||
|           } | ||||
|         }); | ||||
|       }, | ||||
|     }, | ||||
|   ); | ||||
|   new Sortable(els[0], { | ||||
|     group: 'board-column', | ||||
|     draggable: '.board-column', | ||||
|     animation: 150, | ||||
|     ghostClass: 'card-ghost', | ||||
|     onSort: () => { | ||||
|       const board = document.getElementsByClassName('board')[0]; | ||||
|       const boardColumns = board.getElementsByClassName('board-column'); | ||||
|  | ||||
|   for (const column of boardColumns) { | ||||
|     new Sortable( | ||||
|       column.getElementsByClassName('board')[0], | ||||
|       { | ||||
|         group: 'shared', | ||||
|         animation: 150, | ||||
|         ghostClass: 'card-ghost', | ||||
|         onAdd: (e) => { | ||||
|           $.ajax(`${e.to.dataset.url}/${e.item.dataset.issue}`, { | ||||
|       for (const [i, column] of boardColumns.entries()) { | ||||
|         if (parseInt($(column).data('sorting')) !== i) { | ||||
|           $.ajax({ | ||||
|             url: $(column).data('url'), | ||||
|             data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}), | ||||
|             headers: { | ||||
|               'X-Csrf-Token': csrfToken, | ||||
|               'X-Remote': true, | ||||
|             }, | ||||
|             contentType: 'application/json', | ||||
|             type: 'POST', | ||||
|             error: () => { | ||||
|               e.from.insertBefore(e.item, e.from.children[e.oldIndex]); | ||||
|             }, | ||||
|             method: 'PUT', | ||||
|           }); | ||||
|         }, | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
|   for (const column of boardColumns) { | ||||
|     new Sortable(column.getElementsByClassName('board')[0], { | ||||
|       group: 'shared', | ||||
|       animation: 150, | ||||
|       ghostClass: 'card-ghost', | ||||
|       onAdd: ({item, from, to, oldIndex}) => { | ||||
|         const url = to.getAttribute('data-url'); | ||||
|         const issue = item.getAttribute('data-issue'); | ||||
|         $.ajax(`${url}/${issue}`, { | ||||
|           headers: { | ||||
|             'X-Csrf-Token': csrfToken, | ||||
|             'X-Remote': true, | ||||
|           }, | ||||
|           contentType: 'application/json', | ||||
|           type: 'POST', | ||||
|           error: () => { | ||||
|             from.insertBefore(item, from.children[oldIndex]); | ||||
|           }, | ||||
|         }); | ||||
|       }, | ||||
|     ); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| export default function initTableSort() { | ||||
|   for (const header of document.querySelectorAll('th[data-sortt-asc]') || []) { | ||||
|     const {sorttAsc, sorttDesc, sorttDefault} = header.dataset; | ||||
|     const sorttAsc = header.getAttribute('sortt-asc'); | ||||
|     const sorttDesc = header.getAttribute('sortt-desc'); | ||||
|     const sorttDefault = header.getAttribute('sortt-default'); | ||||
|     header.addEventListener('click', () => { | ||||
|       tableSort(sorttAsc, sorttDesc, sorttDefault); | ||||
|     }); | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| /* eslint-disable github/no-then */ | ||||
| const {appSubUrl, csrfToken} = window.config; | ||||
|  | ||||
| export function initUserAuthU2fAuth() { | ||||
| @@ -77,11 +78,11 @@ function u2fError(errorType) { | ||||
|   }; | ||||
|   u2fErrors[errorType].removeClass('hide'); | ||||
|  | ||||
|   Object.keys(u2fErrors).forEach((type) => { | ||||
|   for (const type of Object.keys(u2fErrors)) { | ||||
|     if (type !== `${errorType}`) { | ||||
|       u2fErrors[type].addClass('hide'); | ||||
|     } | ||||
|   }); | ||||
|   } | ||||
|   $('#u2f-error').modal('show'); | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user