1
0
Fork 0
forked from forgejo/forgejo

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:
silverwind 2021-11-22 09:19:01 +01:00 committed by GitHub
parent 7743f13bed
commit a159c3175f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 844 additions and 108 deletions

View file

@ -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();

View file

@ -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';

View file

@ -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');

View file

@ -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('.'));

View file

@ -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]};

View file

@ -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;

View file

@ -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;

View file

@ -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}`,

View file

@ -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('');

View file

@ -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]);
},
});
},
);
});
}
}

View file

@ -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);
});

View file

@ -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');
}

View file

@ -14,14 +14,14 @@ export function initMarkupTasklist() {
const checkboxes = el.querySelectorAll(`.task-list-item input[type=checkbox]`);
for (const checkbox of checkboxes) {
if (checkbox.dataset.editable) {
if (checkbox.hasAttribute('data-editable')) {
return;
}
checkbox.dataset.editable = 'true';
checkbox.setAttribute('data-editable', 'true');
checkbox.addEventListener('input', async () => {
const checkboxCharacter = checkbox.checked ? 'x' : ' ';
const position = parseInt(checkbox.dataset.sourcePosition) + 1;
const position = parseInt(checkbox.getAttribute('data-source-position')) + 1;
const rawContent = container.querySelector('.raw-content');
const oldContent = rawContent.textContent;
@ -43,7 +43,8 @@ export function initMarkupTasklist() {
try {
const editContentZone = container.querySelector('.edit-content-zone');
const {updateUrl, context} = editContentZone.dataset;
const updateUrl = editContentZone.getAttribute('data-update-url');
const context = editContentZone.getAttribute('data-context');
await $.post(updateUrl, {
ignore_attachments: true,

View file

@ -2,7 +2,7 @@ import SwaggerUI from 'swagger-ui-dist/swagger-ui-es-bundle.js';
import 'swagger-ui-dist/swagger-ui.css';
window.addEventListener('load', async () => {
const url = document.getElementById('swagger-ui').dataset.source;
const url = document.getElementById('swagger-ui').getAttribute('data-source');
const res = await fetch(url);
const spec = await res.json();