diff --git a/.eslintrc.json b/.eslintrc.json index 5f5867080..4d69942fb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -56,13 +56,9 @@ "no-new": "warn", "no-return-assign": "warn", "no-script-url": "warn", - "no-throw-literal": "warn", - "no-underscore-dangle": "warn", "no-unused-expressions": "warn", "no-use-before-define": "warn", - "no-useless-concat": "warn", "no-var": "warn", - "prefer-const": "warn", "unicorn/prefer-string-slice": "warn" } } diff --git a/DEVELOPER.md b/DEVELOPER.md index cfc3334ab..08e1883e0 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -180,3 +180,14 @@ All the dependencies that Twinkle uses are JavaScript **dev** dependencies. They - mwn - Used when you run `npm start`. `npm start` is what enables localhost testing. When updating dependencies, CI should take care of testing most of that. Manually testing `npm start` should be the only additional check needed. + +### Adding a CSD + +Here is a checklist for writing a patch to add a speedy deletion criteria called A1: + +* Write a patch similar to this one: https://github.com/wikimedia-gadgets/twinkle/pull/2097/files +* If you want any of the config options to be on by default (instead of off by default), modify twinkle.js -> Twinkle.defaultConfig. +* On wiki, create these pages. Copy an existing template and adjust as needed. + * Template:Db-a1, which will be placed on the page tagged for deletion. + * Template:Db-a1-notice, which will be placed on the user talk page of the author of the page tagged for deletion. + * Template:Db-a1-deleted, which will be placed on the user talk page of the author of the page deleted. diff --git a/modules/twinklearv.js b/modules/twinklearv.js index a115db0b5..81019b349 100644 --- a/modules/twinklearv.js +++ b/modules/twinklearv.js @@ -306,7 +306,7 @@ Twinkle.arv.callback.changeCategory = function (e) { work_area.append( { type: 'dyninput', - name: 'sockpuppet', + name: 'sockpuppets', label: 'Sockpuppets', sublabel: 'Sock:', tooltip: 'The username of the sockpuppet without the "User:" prefix', @@ -574,26 +574,32 @@ Twinkle.arv.callback.evaluate = function(e) { case 'sock': /* falls through */ case 'puppet': - var sockParameters = { - evidence: form.evidence.value.trim(), - checkuser: form.checkuser.checked - }; + var reportData = Twinkle.arv.callback.getSpiReportData(input); - var puppetReport = form.category.value === 'puppet'; - if (puppetReport && !form.sockmaster.value.trim()) { - alert('You have not entered a sockmaster account for this puppet. Consider reporting this account as a sockpuppeteer instead.'); - return; - } else if (!puppetReport && !form.sockpuppet[0].value.trim()) { - alert('You have not entered any sockpuppet account(s) for this sockmaster. Consider reporting this account as a sockpuppet instead.'); + if (reportData.error) { + alert(reportData.error); return; } - sockParameters.uid = puppetReport ? form.sockmaster.value.trim() : uid; - sockParameters.sockpuppets = puppetReport ? [uid] : Morebits.array.uniq($.map($('input:text[name=sockpuppet]', form), (o) => $(o).val() || null)); - Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); - Twinkle.arv.processSock(sockParameters); + + Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" + + var reportpage = 'Wikipedia:Sockpuppet investigations/' + reportData.sockmaster; + + Morebits.wiki.actionCompleted.redirect = reportpage; + Morebits.wiki.actionCompleted.notice = 'Reporting complete'; + + var spiPage = new Morebits.wiki.page(reportpage, 'Retrieving discussion page'); + spiPage.setFollowRedirect(true); + spiPage.setEditSummary('Adding new report for [[Special:Contributions/' + reportData.sockmaster + '|' + reportData.sockmaster + ']].'); + spiPage.setChangeTags(Twinkle.changeTags); + spiPage.setAppendText(reportData.wikitext); + spiPage.setWatchlist(Twinkle.getPref('spiWatchReport')); + spiPage.append(); + + Morebits.wiki.removeCheckpoint(); // all page updates have been started break; case 'an3': @@ -818,32 +824,34 @@ Twinkle.arv.callback.getUsernameReportWikitext = function(input) { return text; }; -Twinkle.arv.processSock = function(params) { - Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" +Twinkle.arv.callback.getSpiReportData = function(input) { + const isPuppetReport = input.category === 'puppet'; - // prepare the SPI report - let text = '\n{{subst:SPI report|' + - params.sockpuppets.map((sock, index) => (index + 1) + '=' + sock).join('|') + '\n|evidence=' + params.evidence + ' \n'; + if (!isPuppetReport) { + input.sockpuppets = input.sockpuppets.filter((sock) => sock !== ''); // ignore empty sockpuppet inputs + } - if (params.checkuser) { - text += '|checkuser=yes'; + if (isPuppetReport && !input.sockmaster) { + return { error: 'You have not entered a sockmaster account for this puppet. Consider reporting this account as a sockpuppeteer instead.' }; + } else if (!isPuppetReport && input.sockpuppets.length === 0) { + return { error: 'You have not entered any sockpuppet account(s) for this sockmaster. Consider reporting this account as a sockpuppet instead.' }; } - text += '}}'; - const reportpage = 'Wikipedia:Sockpuppet investigations/' + params.uid; + input.sockmaster = input.sockmaster || input.uid; + input.sockpuppets = isPuppetReport ? [input.uid] : Morebits.array.uniq(input.sockpuppets); - Morebits.wiki.actionCompleted.redirect = reportpage; - Morebits.wiki.actionCompleted.notice = 'Reporting complete'; + let text = '\n{{subst:SPI report|' + + input.sockpuppets.map((sock, index) => (index + 1) + '=' + sock).join('|') + '\n|evidence=' + input.evidence + ' \n'; - const spiPage = new Morebits.wiki.Page(reportpage, 'Retrieving discussion page'); - spiPage.setFollowRedirect(true); - spiPage.setEditSummary('Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'); - spiPage.setChangeTags(Twinkle.changeTags); - spiPage.setAppendText(text); - spiPage.setWatchlist(Twinkle.getPref('spiWatchReport')); - spiPage.append(); + if (input.checkuser) { + text += '|checkuser=yes'; + } + text += '}}'; - Morebits.wiki.removeCheckpoint(); // all page updates have been started + return { + sockmaster: input.sockmaster, + wikitext: text + }; }; Twinkle.arv.processAN3 = function(params) { @@ -921,19 +929,19 @@ Twinkle.arv.processAN3 = function(params) { ret += sub.reverse().map((v) => (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); return ret; }).reverse().join('\n'); - const warningtext = params.warnings.reverse().map((v) => '# ' + ' {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); - let resolvetext = params.resolves.reverse().map((v) => '# ' + ' {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); + const warningtext = params.warnings.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); + let resolvetext = params.resolves.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); if (params.free_resolves) { const page = params.free_resolves; if (page.compare) { - resolvetext += '\n# ' + ' {{diff|oldid=' + page.compare.fromrevid + '|diff=' + page.compare.torevid + '|label=Consecutive edits on ' + page.compare.totitle + '}}'; + resolvetext += '\n# {{diff|oldid=' + page.compare.fromrevid + '|diff=' + page.compare.torevid + '|label=Consecutive edits on ' + page.compare.totitle + '}}'; } else if (page.revisions) { const revCount = page.revisions.length; let rev; if (revCount < 3) { // diff=prev or next rev = revCount === 1 ? page.revisions[0] : page.revisions[1]; - resolvetext += '\n# ' + ' {{diff2|' + rev.revid + '|' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title + '}} ' + hasHiddenComment(rev); + resolvetext += '\n# {{diff2|' + rev.revid + '|' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title + '}} ' + hasHiddenComment(rev); } else { // diff and oldid are nonconsecutive rev = page.revisions[0]; const revLatest = page.revisions[revCount - 1]; @@ -949,7 +957,7 @@ Twinkle.arv.processAN3 = function(params) { comment += ' ~~~~'; } - const text = '\n\n' + '{{subst:AN3 report|diffs=' + difftext + '|warnings=' + warningtext + '|resolves=' + resolvetext + '|pagename=' + params.page + '|orig=' + origtext + '|comment=' + comment + '|uid=' + params.uid + '}}'; + const text = '\n\n{{subst:AN3 report|diffs=' + difftext + '|warnings=' + warningtext + '|resolves=' + resolvetext + '|pagename=' + params.page + '|orig=' + origtext + '|comment=' + comment + '|uid=' + params.uid + '}}'; const reportpage = 'Wikipedia:Administrators\' noticeboard/Edit warring'; diff --git a/modules/twinklebatchdelete.js b/modules/twinklebatchdelete.js index 257c6097c..11a4b0940 100644 --- a/modules/twinklebatchdelete.js +++ b/modules/twinklebatchdelete.js @@ -277,9 +277,9 @@ Twinkle.batchdelete.callback.toggleSubpages = function twDbatchToggleSubpages(e) if (subpagesLoaded) { $.each(Twinkle.batchdelete.pages, (i, el) => { - // Get back the subgroup from subgroup_, where we saved it - if (el.subgroup === null && el.subgroup_) { - el.subgroup = el.subgroup_; + // Get back the subgroup from subgroupBeforeDeletion, where we saved it + if (el.subgroup === null && el.subgroupBeforeDeletion) { + el.subgroup = el.subgroupBeforeDeletion; } }); @@ -388,10 +388,10 @@ Twinkle.batchdelete.callback.toggleSubpages = function twDbatchToggleSubpages(e) $.each(Twinkle.batchdelete.pages, (i, el) => { if (el.subgroup) { - // Remove subgroup after saving its contents in subgroup_ + // Remove subgroup after saving its contents in subgroupBeforeDeletion // so that it can be retrieved easily if user decides to // delete the subpages again - el.subgroup_ = el.subgroup; + el.subgroupBeforeDeletion = el.subgroup; el.subgroup = null; } }); diff --git a/modules/twinklebatchprotect.js b/modules/twinklebatchprotect.js index 6f745f26d..8e7c61486 100644 --- a/modules/twinklebatchprotect.js +++ b/modules/twinklebatchprotect.js @@ -180,7 +180,8 @@ Twinkle.batchprotect.callback = function twinklebatchprotectCallback() { pages.sort(Twinkle.sortByNamespace); pages.forEach((page) => { const metadata = []; - let missing = !!page.missing, editProt; + const missing = !!page.missing; + let editProt; if (missing) { metadata.push('page does not exist'); diff --git a/modules/twinkleblock.js b/modules/twinkleblock.js index 16c39050c..f4342aa1c 100644 --- a/modules/twinkleblock.js +++ b/modules/twinkleblock.js @@ -2,7 +2,8 @@ (function() { -let api = new mw.Api(), relevantUserName, blockedUserName; +const api = new mw.Api(); +let relevantUserName, blockedUserName; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; @@ -156,8 +157,8 @@ Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { - let blockinfo = data.query.blocks[0], - userinfo = data.query.users[0]; + let blockinfo = data.query.blocks[0]; + const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely @@ -296,7 +297,8 @@ Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { - let field_preset, field_template_options, field_block_options, $form = $(e.target.form); + let field_preset, field_template_options, field_block_options; + const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); @@ -1534,7 +1536,7 @@ Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( - new RegExp('( )?'), '' + new RegExp('( )?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); @@ -1572,7 +1574,8 @@ Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSR }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { - let form = e.target.form, expiry = data.expiry; + const form = e.target.form; + let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { @@ -1723,11 +1726,11 @@ Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { - let $form = $(e.target), + const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), - toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), - blockoptions = {}, templateoptions = {}; + toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); + let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); @@ -1899,13 +1902,14 @@ Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTempla // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); - const params = $.extend(formData, { - messageData: Twinkle.block.blockPresetsInfo[formData.template], - reason: Twinkle.block.field_template_options.block_reason, - disabletalk: Twinkle.block.field_template_options.notalk, - noemail: Twinkle.block.field_template_options.noemail_template, - nocreate: Twinkle.block.field_template_options.nocreate_template - }); + const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( + formData, + Twinkle.block.blockPresetsInfo[formData.template], + Twinkle.block.field_template_options.block_reason, + Twinkle.block.field_template_options.notalk, + Twinkle.block.field_template_options.noemail_template, + Twinkle.block.field_template_options.nocreate_template + ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; @@ -1915,8 +1919,19 @@ Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTempla wikipedia_page.load(Twinkle.block.callback.main); }; +Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { + return $.extend(formData, { + messageData: messageData, + reason: reason, + disabletalk: disabletalk, + noemail: noemail, + nocreate: nocreate + }); +}; + Twinkle.block.callback.getBlockNoticeWikitext = function(params) { - let text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; + let text = '{{'; + const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { @@ -1990,10 +2005,10 @@ Twinkle.block.callback.getBlockNoticeWikitext = function(params) { }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { - let params = pageobj.getCallbackParameters(), + const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), - messageData = params.messageData, - text; + messageData = params.messageData; + let text; params.indefinite = Morebits.string.isInfinity(params.expiry); @@ -2003,7 +2018,8 @@ Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { } else { text = pageobj.getPageText(); - let dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; + const dateHeaderRegex = date.monthHeaderRegex(); + let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } diff --git a/modules/twinkleconfig.js b/modules/twinkleconfig.js index 6e11c3fb4..c88e74ebf 100644 --- a/modules/twinkleconfig.js +++ b/modules/twinkleconfig.js @@ -10,10 +10,6 @@ and adds an ad box to the top of user subpages belonging to the currently logged-in user which end in '.js' * Active on: What I just said. Yeah. - - I, [[User:This, that and the other]], originally wrote this. If the code is misbehaving, or you have any - questions, don't hesitate to ask me. (This doesn't at all imply [[WP:OWN]]ership - it's just meant to - point you in the right direction.) -- TTO */ Twinkle.config = {}; @@ -1075,7 +1071,8 @@ Twinkle.config.init = function twinkleconfigInit() { } cell = document.createElement('td'); - let label, input, gotPref = Twinkle.getPref(pref.name); + let label, input; + const gotPref = Twinkle.getPref(pref.name); switch (pref.type) { case 'boolean': // create a checkbox @@ -1295,9 +1292,11 @@ Twinkle.config.init = function twinkleconfigInit() { // Styled in twinkle.css box.setAttribute('id', 'twinkle-config-headerbox'); - let link, - scriptPageName = mw.config.get('wgPageName').slice(mw.config.get('wgPageName').lastIndexOf('/') + 1, - mw.config.get('wgPageName').lastIndexOf('.js')); + let link; + const scriptPageName = mw.config.get('wgPageName').slice( + mw.config.get('wgPageName').lastIndexOf('/') + 1, + mw.config.get('wgPageName').lastIndexOf('.js') + ); if (scriptPageName === 'twinkleoptions') { // place "why not try the preference panel" notice @@ -1694,6 +1693,7 @@ Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) { '// changing the configuration parameters in a valid-JavaScript way) will be\n' + '// overwritten the next time you click "save" in the Twinkle preferences\n' + '// panel. If modifying this file, make sure to use correct JavaScript.\n' + + // eslint-disable-next-line no-useless-concat '// \n' + '\n' + 'window.Twinkle.prefs = '; @@ -1701,6 +1701,7 @@ Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) { text += ';\n' + '\n' + + // eslint-disable-next-line no-useless-concat '// \n' + '// End of twinkleoptions.js\n'; diff --git a/modules/twinkleprotect.js b/modules/twinkleprotect.js index e34bb0a8c..3bc6ada64 100644 --- a/modules/twinkleprotect.js +++ b/modules/twinkleprotect.js @@ -173,7 +173,8 @@ Twinkle.protect.fetchProtectionLevel = function twinkleprotectFetchProtectionLev const pageid = protectData[0].query.pageids[0]; const page = protectData[0].query.pages[pageid]; - let current = {}, adminEditDeferred; + const current = {}; + let adminEditDeferred; // Save requested page's watched status for later in case needed when filing request Twinkle.protect.watched = page.watchlistexpiry || page.watched === ''; diff --git a/modules/twinklerollback.js b/modules/twinklerollback.js index d7a276082..747022784 100644 --- a/modules/twinklerollback.js +++ b/modules/twinklerollback.js @@ -280,7 +280,7 @@ Twinkle.rollback.addLinks = { const warnFromTalk = function(xtitle) { const $talkLink = $('#mw-diff-' + xtitle + '2 .mw-usertoollinks a').first(); if ($talkLink.length) { - let extraParams = 'vanarticle=' + mw.util.rawurlencode(Morebits.pageNameNorm) + '&' + 'noautowarn=true'; + let extraParams = 'vanarticle=' + mw.util.rawurlencode(Morebits.pageNameNorm) + '&noautowarn=true'; // diffIDs for vanarticlerevid extraParams += '&vanarticlerevid='; extraParams += xtitle === 'otitle' ? mw.config.get('wgDiffOldId') : mw.config.get('wgDiffNewId'); diff --git a/modules/twinklespeedy.js b/modules/twinklespeedy.js index 1d2a681b1..308f894a8 100644 --- a/modules/twinklespeedy.js +++ b/modules/twinklespeedy.js @@ -39,9 +39,8 @@ Twinkle.speedy.hasCSD = !!$('#delete-reason').length; // Prepares the speedy deletion dialog and displays it Twinkle.speedy.initDialog = function twinklespeedyInitDialog(callbackfunc) { - let dialog; Twinkle.speedy.dialog = new Morebits.SimpleWindow(Twinkle.getPref('speedyWindowWidth'), Twinkle.getPref('speedyWindowHeight')); - dialog = Twinkle.speedy.dialog; + const dialog = Twinkle.speedy.dialog; dialog.setTitle('Choose criteria for speedy deletion'); dialog.setScriptName('Twinkle'); dialog.addFooterLink('Speedy deletion policy', 'WP:CSD'); @@ -1140,8 +1139,8 @@ Twinkle.speedy.callbacks = { } if (initialContrib) { - let usertalkpage = new Morebits.wiki.Page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')'), - notifytext, i, editsummary; + const usertalkpage = new Morebits.wiki.Page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')'); + let notifytext, i, editsummary; // special cases: "db" and "db-multiple" if (params.normalizeds.length > 1) { @@ -1221,7 +1220,7 @@ Twinkle.speedy.callbacks = { if (reason === null) { return Morebits.Status.error('Asking for reason', 'User cancelled'); } else if (!reason || !reason.replace(/^\s*/, '').replace(/\s*$/, '')) { - return Morebits.Status.error('Asking for reason', "you didn't give one. I don't know... what with admins and their apathetic antics... I give up..."); + return Morebits.Status.error('Asking for reason', 'The "reason" for deleting was not provided, or Twinkle was unable to compute it. Aborting.'); } const deleteMain = function(callback) { @@ -1367,8 +1366,8 @@ Twinkle.speedy.callbacks = { // given the params, builds the template and also adds the user talk page parameters to the params that were passed in // returns => [ wikitext, utparams] - let buildData = Twinkle.speedy.callbacks.getTemplateCodeAndParams(params), - code = buildData[0]; + const buildData = Twinkle.speedy.callbacks.getTemplateCodeAndParams(params); + let code = buildData[0]; params.utparams = buildData[1]; // Set the correct value for |ts= parameter in {{db-g13}} diff --git a/modules/twinkletag.js b/modules/twinkletag.js index 6af4c6d92..2e7caea29 100644 --- a/modules/twinkletag.js +++ b/modules/twinkletag.js @@ -351,7 +351,6 @@ Twinkle.tag.callback = function twinkletagCallback() { const evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.sortorder.dispatchEvent(evt); - } else { // Redirects and files: Add a link to each template's description page Morebits.QuickForm.getElements(result, 'tags').forEach(generateLinks); @@ -1210,16 +1209,6 @@ Twinkle.tag.fileList = { label: 'Name on Commons:', tooltip: 'Name of the image on Commons (if different from local name), excluding the File: prefix:' } - }, - { - label: '{{Now Commons}}: file has been copied to Commons', - value: 'Now Commons', - subgroup: { - type: 'input', - name: 'nowcommonsName', - label: 'Commons image name if different:', - tooltip: 'Name of the image on Commons (if different from local name), excluding the File: prefix:' - } } ], 'Cleanup tags': [ @@ -1589,7 +1578,8 @@ Twinkle.tag.callbacks = { return; } - let tagRe, tagText = '', tags = [], groupableTags = [], groupableExistingTags = []; + let tagRe, tagText = '', tags = []; + const groupableTags = [], groupableExistingTags = []; // Executes first: addition of selected tags /** @@ -1806,10 +1796,12 @@ Twinkle.tag.callbacks = { }, redirect: function redirect(pageobj) { - let params = pageobj.getCallbackParameters(), - pageText = pageobj.getPageText(), - tagRe, tagText = '', summaryText = 'Added', - tags = [], i; + const params = pageobj.getCallbackParameters(), + tags = []; + let pageText = pageobj.getPageText(), + tagRe, tagText = '', + summaryText = 'Added', + i; for (i = 0; i < params.tags.length; i++) { tagRe = new RegExp('(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im'); @@ -1906,19 +1898,13 @@ Twinkle.tag.callbacks = { let tagtext = '', currentTag; $.each(params.tags, (k, tag) => { // when other commons-related tags are placed, remove "move to Commons" tag - if (['Keep local', 'Now Commons', 'Do not move to Commons'].indexOf(tag) !== -1) { + if (['Keep local', 'Do not move to Commons'].indexOf(tag) !== -1) { text = text.replace(/\{\{(mtc|(copy |move )?to ?commons|move to wikimedia commons|copy to wikimedia commons)[^}]*\}\}/gi, ''); } currentTag = tag; switch (tag) { - case 'Now Commons': - currentTag = 'subst:' + currentTag; // subst - if (params.nowcommonsName !== '') { - currentTag += '|1=' + params.nowcommonsName; - } - break; case 'Keep local': if (params.keeplocalName !== '') { currentTag += '|1=' + params.keeplocalName; diff --git a/modules/twinkletalkback.js b/modules/twinkletalkback.js index 2fdfc02a3..ddbab8370 100644 --- a/modules/twinkletalkback.js +++ b/modules/twinkletalkback.js @@ -273,7 +273,7 @@ Twinkle.talkback.noticeboards = { }, hd: { label: 'WP:HD (Help desk)', - text: '== Your question at the Help desk ==\n' + '{{helpdeskreply|1=$SECTION|ts=~~~~~}}', + text: '== Your question at the Help desk ==\n{{helpdeskreply|1=$SECTION|ts=~~~~~}}', editSummary: 'You have replies at the [[Wikipedia:Help desk|Wikipedia help desk]]' }, norn: { diff --git a/modules/twinklewarn.js b/modules/twinklewarn.js index cf0724fbd..dbbc7675b 100644 --- a/modules/twinklewarn.js +++ b/modules/twinklewarn.js @@ -54,9 +54,8 @@ Twinkle.warn.callback = function twinklewarnCallback() { return; } - let dialog; Twinkle.warn.dialog = new Morebits.SimpleWindow(600, 440); - dialog = Twinkle.warn.dialog; + const dialog = Twinkle.warn.dialog; dialog.setTitle('Warn/notify user'); dialog.setScriptName('Twinkle'); dialog.addFooterLink('Choosing a warning level', 'WP:UWUL#Levels'); @@ -1282,6 +1281,10 @@ Twinkle.warn.messages = { label: 'Removing {{copyvio}} template from articles', summary: 'Warning: Removing {{copyvio}} templates' }, + 'uw-derogatory': { + label: 'Addition of derogatory/hateful content', + summary: 'Warning: Addition of derogatory content' + }, 'uw-efsummary': { label: 'Edit summary triggering the edit filter', summary: 'Warning: Edit summary triggering the edit filter' @@ -1706,9 +1709,7 @@ Twinkle.warn.callbacks = { // Provided on autolevel, not otherwise templatename = templatename || input.sub_group; const linkedarticle = input.article; - let templatetext; - - templatetext = Twinkle.warn.callbacks.getWarningWikitext(templatename, linkedarticle, + const templatetext = Twinkle.warn.callbacks.getWarningWikitext(templatename, linkedarticle, input.reason, input.main_group === 'custom'); form.previewer.beginRender(templatetext, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username diff --git a/modules/twinklewelcome.js b/modules/twinklewelcome.js index c2d9233e4..497d30457 100644 --- a/modules/twinklewelcome.js +++ b/modules/twinklewelcome.js @@ -314,6 +314,11 @@ Twinkle.welcome.templates = { linkedArticle: true, syntax: '{{subst:welcome-image|$USERNAME$|art=$ARTICLE$}}' }, + 'welcome-unsourced': { + description: 'for someone whose initial efforts are unsourced', + linkedArticle: true, + syntax: '{{subst:welcome-unsourced|$ARTICLE$|$USERNAME$}} ~~~~' + }, welcomelaws: { description: 'welcome with information about copyrights, NPOV, the sandbox, and vandalism', syntax: '{{subst:welcomelaws|$USERNAME$}} ~~~~' @@ -323,11 +328,6 @@ Twinkle.welcome.templates = { linkedArticle: true, syntax: '{{subst:welcomenpov|$ARTICLE$|$USERNAME$}} ~~~~' }, - welcomeunsourced: { - description: 'for someone whose initial efforts are unsourced', - linkedArticle: true, - syntax: '{{subst:welcomeunsourced|$ARTICLE$|$USERNAME$}} ~~~~' - }, welcomevandal: { description: 'for someone whose initial efforts appear to be vandalism', linkedArticle: true, diff --git a/modules/twinklexfd.js b/modules/twinklexfd.js index 59034ac58..964a97c9a 100644 --- a/modules/twinklexfd.js +++ b/modules/twinklexfd.js @@ -424,8 +424,8 @@ Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { - let target = e.target, - tfdtarget = target.form.tfdtarget; + const target = e.target; + let tfdtarget = target.form.tfdtarget; // add/remove extra input box if (target.value === 'tfm' && !tfdtarget) { tfdtarget = new Morebits.QuickForm.Element({ @@ -554,9 +554,9 @@ Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { - let value = e.target.value, - cfdtarget = e.target.form.cfdtarget, - cfdtarget2 = e.target.form.cfdtarget2; + const value = e.target.value, + cfdtarget = e.target.form.cfdtarget; + let cfdtarget2 = e.target.form.cfdtarget2; // update enabled status cfdtarget.disabled = value === 'cfd' || value === 'sfd-t'; diff --git a/morebits.js b/morebits.js index c015ac77a..8d0269ba0 100644 --- a/morebits.js +++ b/morebits.js @@ -266,7 +266,8 @@ Morebits.namespaceRegex = function(namespaces) { if (!Array.isArray(namespaces)) { namespaces = [namespaces]; } - let aliases = [], regex; + const aliases = []; + let regex; $.each(mw.config.get('wgNamespaceIds'), (name, number) => { if (namespaces.indexOf(number) !== -1) { // Namespaces are completely agnostic as to case, @@ -1404,6 +1405,7 @@ Morebits.ip = { } ipv6 = Morebits.ip.sanitizeIPv6(ipv6); const ip_re = /^((?:[0-9A-F]{1,4}:){4})(?:[0-9A-F]{1,4}:){3}[0-9A-F]{1,4}(?:\/\d{1,3})?$/; + // eslint-disable-next-line no-useless-concat return ipv6.replace(ip_re, '$1' + '0:0:0:0/64'); } }; @@ -1500,6 +1502,7 @@ Morebits.string = { formatReasonText: function(str, addSig) { let reason = (str || '').toString().trim(); const unbinder = new Morebits.unbinder(reason); + // eslint-disable-next-line no-useless-concat unbinder.unbind('', ''); unbinder.content = unbinder.content.replace(/\|/g, '{{subst:!}}'); reason = unbinder.rebind(); @@ -1585,7 +1588,7 @@ Morebits.array = { */ uniq: function(arr) { if (!Array.isArray(arr)) { - throw 'A non-array object passed to Morebits.array.uniq'; + throw new Error('A non-array object passed to Morebits.array.uniq'); } return arr.filter((item, idx) => arr.indexOf(item) === idx); }, @@ -1600,7 +1603,7 @@ Morebits.array = { */ dups: function(arr) { if (!Array.isArray(arr)) { - throw 'A non-array object passed to Morebits.array.dups'; + throw new Error('A non-array object passed to Morebits.array.dups'); } return arr.filter((item, idx) => arr.indexOf(item) !== idx); }, @@ -1615,7 +1618,7 @@ Morebits.array = { */ chunk: function(arr, size) { if (!Array.isArray(arr)) { - throw 'A non-array object passed to Morebits.array.chunk'; + throw new Error('A non-array object passed to Morebits.array.chunk'); } if (typeof size !== 'number' || size <= 0) { // pretty impossible to do anything :) return [ arr ]; // we return an array consisting of this array. @@ -1811,20 +1814,20 @@ Morebits.date = function() { const digitMatch = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(param); if (digitMatch) { // ..... year ... month .. date ... hour .... minute ..... second - this._d = new Date(Date.UTC.apply(null, [digitMatch[1], digitMatch[2] - 1, digitMatch[3], digitMatch[4], digitMatch[5], digitMatch[6]])); + this.privateDate = new Date(Date.UTC.apply(null, [digitMatch[1], digitMatch[2] - 1, digitMatch[3], digitMatch[4], digitMatch[5], digitMatch[6]])); } } else if (typeof param === 'string') { // Wikitext signature timestamp const dateParts = Morebits.l10n.signatureTimestampFormat(param); if (dateParts) { - this._d = new Date(Date.UTC.apply(null, dateParts)); + this.privateDate = new Date(Date.UTC.apply(null, dateParts)); } } } - if (!this._d) { + if (!this.privateDate) { // Try standard date - this._d = new (Function.prototype.bind.apply(Date, [Date].concat(args)))(); + this.privateDate = new (Function.prototype.bind.apply(Date, [Date].concat(args)))(); } // Still no? @@ -2145,7 +2148,7 @@ Morebits.date.prototype = { // Allow native Date.prototype methods to be used on Morebits.date objects Object.getOwnPropertyNames(Date.prototype).forEach((func) => { Morebits.date.prototype[func] = function() { - return this._d[func].apply(this._d, Array.prototype.slice.call(arguments)); + return this.privateDate[func].apply(this.privateDate, Array.prototype.slice.call(arguments)); }; }); @@ -3831,7 +3834,8 @@ Morebits.wiki.page = function(pageName, status) { return; // abort } - let page = response.pages[0], rev; + const page = response.pages[0]; + let rev; ctx.pageExists = !page.missing; if (ctx.pageExists) { rev = page.revisions[0]; @@ -5231,7 +5235,7 @@ Morebits.status.onError = function(handler) { if (typeof handler === 'function') { Morebits.status.errorEvent = handler; } else { - throw 'Morebits.status.onError: handler is not a function'; + throw new Error('Morebits.status.onError: handler is not a function'); } }; diff --git a/package-lock.json b/package-lock.json index b38dc698d..16c311db6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "engines": { "node": ">=6.9.0" @@ -89,13 +89,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -209,12 +209,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -460,16 +460,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -487,9 +487,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1079,9 +1079,9 @@ } }, "node_modules/@mdn/browser-compat-data": { - "version": "5.6.17", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.6.17.tgz", - "integrity": "sha512-Cik21uEp29W9YcRiWHkZM1SEHIinxLIb6EzmPIj7Hc8qPoqMW4oJ5bpDBNl+CkEjvtblKrp94b1cTa4opaNQDA==", + "version": "5.6.22", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.6.22.tgz", + "integrity": "sha512-tssmUJmywWtEEhCoY9GMPCm9chGWwCytYrlbBaIFgePs6Ui5IBsTPiOyYocRApvC+wFoCjnxhPMYdz51if8Nlg==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -1275,12 +1275,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", - "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/normalize-package-data": { @@ -1339,13 +1339,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", - "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz", + "integrity": "sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1356,9 +1356,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", - "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz", + "integrity": "sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1369,13 +1369,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", - "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz", + "integrity": "sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1421,15 +1421,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", - "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz", + "integrity": "sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1448,12 +1448,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", - "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz", + "integrity": "sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/types": "8.17.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1477,9 +1477,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true }, "node_modules/@vue/compiler-core": { @@ -2030,9 +2030,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001683", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", - "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", + "version": "1.0.30001687", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", "dev": true, "funding": [ { @@ -2311,9 +2311,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { "ms": "^2.1.3" @@ -2415,9 +2415,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", - "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==", + "version": "1.5.71", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", + "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", "dev": true }, "node_modules/emittery": { @@ -2744,9 +2744,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "17.13.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.13.2.tgz", - "integrity": "sha512-MhBAKkT01h8cOXcTBTlpuR7bxH5OBUNpUXefsvwSVEy46cY4m/Kzr2osUCQvA3zJFD6KuCeNNDv0+HDuWk/OcA==", + "version": "17.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.14.0.tgz", + "integrity": "sha512-maxPLMEA0rPmRpoOlxEclKng4UpDe+N5BJS4t24I3UKnN109Qcivnfs37KMy84G0af3bxjog5lKctP5ObsvcTA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.1", @@ -2778,9 +2778,9 @@ } }, "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", + "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", "dev": true, "engines": { "node": ">=18" @@ -2805,9 +2805,9 @@ } }, "node_modules/eslint-plugin-no-jquery": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-3.0.2.tgz", - "integrity": "sha512-n/+6p6PFhWDNPVLJj1463hw4OTIRBbROGcbhmtOHTgw7yihSKzkwZiQ00EJTneyeR3jRiw5lpWSMCCBhtb8t2g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-3.1.0.tgz", + "integrity": "sha512-Ze+eRlAbLAoceBqMXI2E9s6o3dC7zE75niP2Sy4D8I/u1TyLegrIpjc4emPN90dH0IA+uXNUmQbzBuCaihxwIQ==", "dev": true, "peerDependencies": { "eslint": ">=8.0.0" @@ -2933,9 +2933,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.31.0.tgz", - "integrity": "sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", + "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -2964,13 +2964,13 @@ } }, "node_modules/eslint-plugin-yml": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.15.0.tgz", - "integrity": "sha512-leC8APYVOsKyWUlvRwVhewytK5wS70BfMqIaUplFstRfzCoVp0YoEroV4cUEvQrBj93tQ3M9LcjO/ewr6D4kjA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.16.0.tgz", + "integrity": "sha512-t4MNCetPjTn18/fUDlQ/wKkcYjnuLYKChBrZ0qUaNqRigVqChHWzTP8SrfFi5s4keX3vdlkWRSu8zHJMdKwxWQ==", "dev": true, "dependencies": { "debug": "^4.3.2", - "eslint-compat-utils": "^0.5.0", + "eslint-compat-utils": "^0.6.0", "lodash": "^4.17.21", "natural-compare": "^1.4.0", "yaml-eslint-parser": "^1.2.1" @@ -2985,6 +2985,21 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-plugin-yml/node_modules/eslint-compat-utils": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz", + "integrity": "sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -4685,9 +4700,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.13", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.13.tgz", - "integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==", + "version": "0.30.14", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", + "integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -4928,9 +4943,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -5018,9 +5033,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", - "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", "dev": true }, "node_modules/oauth-1.0a": { @@ -5399,12 +5414,15 @@ } }, "node_modules/psl": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", - "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "dev": true, "dependencies": { "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" } }, "node_modules/punycode": { @@ -5683,9 +5701,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "engines": { "node": ">=10" @@ -6104,9 +6122,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -6149,9 +6167,9 @@ } }, "node_modules/types-mediawiki": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/types-mediawiki/-/types-mediawiki-1.8.2.tgz", - "integrity": "sha512-sAvFGA0EHn3mDJVOYHhoalE2QixJpwQBwdsg8saD8qmyBro6EtSEP3ejlPvVI6va0sdUU0e5mkRlXko2B/q6SA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/types-mediawiki/-/types-mediawiki-1.9.0.tgz", + "integrity": "sha512-ramdgqi1r/+BDuTTHd7wfDcDqky06rOx2naGp9C2nPIQHcmSm0htoiIMmlP2VoOuR89H0K/ejtSBCXT8guujpA==", "dev": true, "dependencies": { "@types/jquery": "^3.5.5", @@ -6160,9 +6178,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "peer": true, "bin": { @@ -6174,9 +6192,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/universalify": { diff --git a/tests/jest.setup.js b/tests/jest.setup.js index e76d8ee6e..96a9a1696 100644 --- a/tests/jest.setup.js +++ b/tests/jest.setup.js @@ -6,6 +6,7 @@ mw.config.set({ require('../morebits.js'); require('../twinkle.js'); +require('../modules/twinkleblock.js'); require('../modules/twinkletag.js'); require('../modules/twinklewarn.js'); require('../modules/twinklexfd.js'); diff --git a/tests/twinkleblock.test.js b/tests/twinkleblock.test.js new file mode 100644 index 000000000..732af83da --- /dev/null +++ b/tests/twinkleblock.test.js @@ -0,0 +1,15 @@ +describe('modules/twinkleblock', () => { + describe('combineFormDataAndFieldTemplateOptions', () => { + // https://github.com/wikimedia-gadgets/twinkle/issues/2106 + test('regression test: merging true and undefined should return true', () => { + const formData = {disabletalk: true}; + const messageData = undefined; + const reason = undefined; + const disabletalk = undefined; + const noemail = undefined; + const nocreate = undefined; + const expected = {disabletalk: true}; + expect(Twinkle.block.combineFormDataAndFieldTemplateOptions(formData, messageData, reason, disabletalk, noemail, nocreate)).toStrictEqual(expected); + }); + }); +});