loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request '[PORT] Enable `no-jquery/no-parse-html-literal` and fix violation (gitea#31684)' (#4719) from gusted/forgejo-gt-31684 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4719
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>

Gusted 0851aca8 e8ea1266

+44 -13
+1 -1
.eslintrc.yaml
··· 460 460 no-jquery/no-param: [2] 461 461 no-jquery/no-parent: [0] 462 462 no-jquery/no-parents: [2] 463 - no-jquery/no-parse-html-literal: [0] 463 + no-jquery/no-parse-html-literal: [2] 464 464 no-jquery/no-parse-html: [2] 465 465 no-jquery/no-parse-json: [2] 466 466 no-jquery/no-parse-xml: [2]
+31 -12
web_src/js/features/repo-editor.js
··· 1 1 import $ from 'jquery'; 2 2 import {htmlEscape} from 'escape-goat'; 3 3 import {createCodeEditor} from './codeeditor.js'; 4 - import {hideElem, showElem} from '../utils/dom.js'; 4 + import {hideElem, showElem, createElementFromHTML} from '../utils/dom.js'; 5 5 import {initMarkupContent} from '../markup/content.js'; 6 6 import {attachRefIssueContextPopup} from './contextpopup.js'; 7 7 import {POST} from '../modules/fetch.js'; ··· 9 9 function initEditPreviewTab($form) { 10 10 const $tabMenu = $form.find('.tabular.menu'); 11 11 $tabMenu.find('.item').tab(); 12 - const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`); 12 + const $previewTab = $tabMenu.find( 13 + `.item[data-tab="${$tabMenu.data('preview')}"]`, 14 + ); 13 15 if ($previewTab.length) { 14 16 $previewTab.on('click', async function () { 15 17 const $this = $(this); ··· 24 26 const formData = new FormData(); 25 27 formData.append('mode', mode); 26 28 formData.append('context', context); 27 - formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()); 29 + formData.append( 30 + 'text', 31 + $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(), 32 + ); 28 33 formData.append('file_path', $treePathEl.val()); 29 34 try { 30 35 const response = await POST($this.data('url'), {data: formData}); 31 36 const data = await response.text(); 32 - const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); 37 + const $previewPanel = $form.find( 38 + `.tab[data-tab="${$tabMenu.data('preview')}"]`, 39 + ); 33 40 renderPreviewPanelContent($previewPanel, data); 34 41 } catch (error) { 35 42 console.error('Error:', error); ··· 96 103 const value = parts[i]; 97 104 if (i < parts.length - 1) { 98 105 if (value.length) { 99 - $(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this)); 100 - $('<div class="breadcrumb-divider">/</div>').insertBefore($(this)); 106 + $editFilename[0].before( 107 + createElementFromHTML( 108 + `<span class="section"><a href="#">${htmlEscape(value)}</a></span>`, 109 + ), 110 + ); 111 + $editFilename[0].before( 112 + createElementFromHTML(`<div class="breadcrumb-divider">/</div>`), 113 + ); 101 114 } 102 115 } else { 103 116 $(this).val(value); ··· 113 126 const $section = $('.breadcrumb span.section'); 114 127 115 128 // Jump back to last directory once the filename is empty 116 - if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) { 129 + if ( 130 + e.code === 'Backspace' && 131 + getCursorPosition($(this)) === 0 && 132 + $section.length > 0 133 + ) { 117 134 e.preventDefault(); 118 135 const $divider = $('.breadcrumb .breadcrumb-divider'); 119 136 const value = $section.last().find('a').text(); ··· 164 181 commitButton?.addEventListener('click', (e) => { 165 182 // A modal which asks if an empty file should be committed 166 183 if (!$editArea.val()) { 167 - $('#edit-empty-content-modal').modal({ 168 - onApprove() { 169 - $('.edit.form').trigger('submit'); 170 - }, 171 - }).modal('show'); 184 + $('#edit-empty-content-modal') 185 + .modal({ 186 + onApprove() { 187 + $('.edit.form').trigger('submit'); 188 + }, 189 + }) 190 + .modal('show'); 172 191 e.preventDefault(); 173 192 } 174 193 });
+7
web_src/js/utils/dom.js
··· 296 296 textarea.dispatchEvent(new CustomEvent('change', {bubbles: true, cancelable: true})); 297 297 } 298 298 } 299 + 300 + // Warning: Do not enter any unsanitized variables here 301 + export function createElementFromHTML(htmlString) { 302 + const div = document.createElement('div'); 303 + div.innerHTML = htmlString.trim(); 304 + return div.firstChild; 305 + }
+5
web_src/js/utils/dom.test.js
··· 1 + import {createElementFromHTML} from './dom.js'; 2 + 3 + test('createElementFromHTML', () => { 4 + expect(createElementFromHTML('<a>foo<span>bar</span></a>').outerHTML).toEqual('<a>foo<span>bar</span></a>'); 5 + });