diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ffc6a325c7a..747b74f1a557 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,8 @@ jobs: node-version-file: package.json cache: npm - run: npm ci - - run: npm run lint + - run: npm run lint:js + - run: npm run lint:css - uses: codespell-project/actions-codespell@v2 with: ignore_words_list: eror,usera @@ -82,6 +83,19 @@ jobs: - run: npm ci - run: npm run build:typescript + Format: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version-file: package.json + cache: npm + - run: npm ci + - run: npm run format:check + Build: strategy: matrix: diff --git a/build/features.test.ts b/build/features.test.ts index a0d7d5847e7c..74bfdc42122f 100755 --- a/build/features.test.ts +++ b/build/features.test.ts @@ -1,11 +1,11 @@ +import fastIgnore from 'fast-ignore'; import {existsSync, readdirSync, readFileSync} from 'node:fs'; import path from 'node:path'; -import {test, describe, assert} from 'vitest'; import {regexJoinWithSeparator} from 'regex-join'; -import fastIgnore from 'fast-ignore'; +import {assert, describe, test} from 'vitest'; import {isFeaturePrivate} from '../source/helpers/feature-utils.js'; -import {getImportedFeatures, getFeaturesMeta} from './readme-parser.js'; +import {getFeaturesMeta, getImportedFeatures} from './readme-parser.js'; // Re-run tests when these files change https://github.com/vitest-dev/vitest/discussions/5864 void import.meta.glob([ @@ -156,7 +156,10 @@ function validateTsx(file: FeatureFile): void { assert(/test url/i.test(file.contents().toString()), 'Should have test URLs'); - if (/api\.v4|getDefaultBranch|getPrInfo/.test(String(file.contents())) && /observe\(|delegate\(/.test(String(file.contents()))) { + if ( + /api\.v4|getDefaultBranch|getPrInfo/.test(String(file.contents())) + && /observe\(|delegate\(/.test(String(file.contents())) + ) { assert( /await expectToken|hasToken/.test(String(file.contents())), `${file.id} uses the v4 API, so it should include \`await expectToken()\` in its init function or, if the token is optional, \`hasToken\` anywhere`, @@ -216,6 +219,8 @@ describe('features', () => { return; } - assert.fail(`The \`/source/features\` folder should only contain .css, .tsx and .gql files. Found \`source/features/${filename}\``); + assert.fail( + `The \`/source/features\` folder should only contain .css, .tsx and .gql files. Found \`source/features/${filename}\``, + ); }); }); diff --git a/build/readme-parser.test.ts b/build/readme-parser.test.ts index 081c4ed06d7e..d86a18a5da8f 100644 --- a/build/readme-parser.test.ts +++ b/build/readme-parser.test.ts @@ -1,9 +1,6 @@ -import {test, expect} from 'vitest'; +import {expect, test} from 'vitest'; -import { - getFeaturesMeta, - getImportedFeatures, -} from './readme-parser.js'; +import {getFeaturesMeta, getImportedFeatures} from './readme-parser.js'; // Re-run tests when these files change https://github.com/vitest-dev/vitest/discussions/5864 void import.meta.glob([ diff --git a/build/readme-parser.ts b/build/readme-parser.ts index b9282f682098..39c0f26f49a6 100644 --- a/build/readme-parser.ts +++ b/build/readme-parser.ts @@ -1,12 +1,13 @@ /// -import {regexJoinWithSeparator} from 'regex-join'; import {existsSync, readFileSync} from 'node:fs'; +import {regexJoinWithSeparator} from 'regex-join'; import parseMarkdown from 'snarkdown'; // Group names must be unique because they will be merged const simpleFeatureRegex = /^- \[\]\(# "(?[^"]+)"\)(?: đŸ”„)? (?.+)$/gm; -const highlightedFeatureRegex = /

<\/a> (?.+)\n\t+

/g; +const highlightedFeatureRegex + = /

<\/a> (?.+)\n\t+

/g; const featureRegex = regexJoinWithSeparator('|', [simpleFeatureRegex, highlightedFeatureRegex]); const imageRegex = /\.\w{3}$/; // 3 since .png and .gif have 3 letters const rghUploadsRegex = /refined-github[/]refined-github[/]assets[/]/; diff --git a/dprint.json b/dprint.json index dad5455f02b3..4312e7965ee3 100644 --- a/dprint.json +++ b/dprint.json @@ -3,12 +3,14 @@ "plugins": [ "./node_modules/dprint-plugin-markup/plugin.wasm", "./node_modules/dprint-plugin-malva/plugin.wasm", - "./node_modules/dprint-plugin-graphql/plugin.wasm" + "./node_modules/dprint-plugin-graphql/plugin.wasm", + "./node_modules/@dprint/json/plugin.wasm" ], "includes": [ "**/*.svelte", "**/*.css", - "**/*.gql" + "**/*.gql", + "dprint.json" ], "useTabs": true, "markup": { diff --git a/eslint.config.js b/eslint.config.js index 5b3779032672..7a5493b1f59a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,7 @@ import xo from 'xo'; import sveltePlugin from 'eslint-plugin-svelte'; import svelteParser from 'svelte-eslint-parser'; -import eslintConfigPrettier from 'eslint-config-prettier'; +import eslintConfigPrettier from 'eslint-config-prettier/flat'; import {includeIgnoreFile} from '@eslint/compat'; import {fileURLToPath} from 'node:url'; @@ -316,6 +316,8 @@ export default [ location: 'readonly', }, }, + + // TODO: Use global `/flat` config. Currently limited to svelte files because dprint is applied to their JS rules: eslintConfigPrettier.rules, }, { diff --git a/package-lock.json b/package-lock.json index 9475512a27b0..8d4b3ecacd27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.3.12", + "@dprint/json": "^0.21.3", "@eslint-react/eslint-plugin": "^2.7.2", "@eslint/compat": "^2.0.1", "@rollup/plugin-alias": "^6.0.0", @@ -328,6 +329,13 @@ "darwin" ] }, + "node_modules/@dprint/json": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/@dprint/json/-/json-0.21.3.tgz", + "integrity": "sha512-fOmRKstf4kVZunnJcrGg1SNzoU6Y11mnLR1SbHQ7PwVvOUQTTUeCtxm3oLy/56nf69kaZmrdeegWXyxnTM5WDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@dprint/linux-arm64-glibc": { "version": "0.54.0", "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.54.0.tgz", diff --git a/package.json b/package.json index fbb55e9381a3..e5ad39c38484 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,12 @@ "build": "run-p build:* --continue-on-error", "build:typescript": "tsc --noEmit", "build:bundle": "rollup -c", - "fix": "run-p \"lint:biome -- --write\" \"lint:js -- --fix\" format \"vitest -- --update\" --continue-on-error", + "fix": "run-p format \"lint:css -- --write\" \"lint:js -- --fix\" \"vitest -- --update\" --continue-on-error", "format": "dprint fmt", + "format:check": "dprint check", "lint": "run-p lint:* --continue-on-error", "lint:js": "eslint .", - "lint:biome": "biome lint", - "lint:dprint": "dprint check", + "lint:css": "biome lint", "new": "bash build/new-feature.sh", "pack:safari": "xcodebuild -project 'safari/Refined GitHub.xcodeproj' -scheme 'Refined GitHub' -destination 'platform=macOS'", "prepare:safari": "bash build/prepare-safari-release.sh", @@ -73,6 +73,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.3.12", + "@dprint/json": "^0.21.3", "@eslint-react/eslint-plugin": "^2.7.2", "@eslint/compat": "^2.0.1", "@rollup/plugin-alias": "^6.0.0", diff --git a/source/background.ts b/source/background.ts index 67c626165e4f..125866d65baa 100644 --- a/source/background.ts +++ b/source/background.ts @@ -27,7 +27,9 @@ if (!isSafari()) { addReloadWithoutContentScripts(); // Extend the error message for the "No All URLs" bugfix -customizeNoAllUrlsErrorMessage('Refined GitHub is not meant to run on every website. If you’re looking to enable it on GitHub Enterprise, follow the instructions in the Options page.'); +customizeNoAllUrlsErrorMessage( + 'Refined GitHub is not meant to run on every website. If you’re looking to enable it on GitHub Enterprise, follow the instructions in the Options page.', +); handleMessages({ async openUrls(urls: string[], {tab}: chrome.runtime.MessageSender) { diff --git a/source/feature-manager.tsx b/source/feature-manager.tsx index 499915102fdb..33496cd33e4d 100644 --- a/source/feature-manager.tsx +++ b/source/feature-manager.tsx @@ -1,34 +1,30 @@ /* eslint-disable no-await-in-loop -- Event loops */ import React from 'dom-chef'; -import {elementExists} from 'select-dom'; import domLoaded from 'dom-loaded'; +import * as pageDetect from 'github-url-detection'; +import oneEvent from 'one-event'; +import {elementExists} from 'select-dom'; import stripIndent from 'strip-indent'; import type {Promisable} from 'type-fest'; -import * as pageDetect from 'github-url-detection'; import {isWebPage} from 'webext-detect'; import {messageRuntime} from 'webext-msg'; -import oneEvent from 'one-event'; -import waitFor from './helpers/wait-for.js'; -import ArrayMap from './helpers/map-of-arrays.js'; +import asyncForEach from './helpers/async-for-each.js'; import bisectFeatures from './helpers/bisect.js'; +import {catchErrors, disableErrorLogging} from './helpers/errors.js'; import { - shouldFeatureRun, - isFeaturePrivate, - type RunConditions, -} from './helpers/feature-utils.js'; -import optionsStorage, {isFeatureDisabled, type RghOptions} from './options-storage.js'; + getFeatureId, listenToAjaxedLoad, log, shortcutMap, +} from './helpers/feature-helpers.js'; +import {isFeaturePrivate, type RunConditions, shouldFeatureRun} from './helpers/feature-utils.js'; import { applyStyleHotfixes, + brokenFeatures, getLocalHotfixesAsOptions, preloadSyncLocalStrings, - brokenFeatures, } from './helpers/hotfix.js'; -import asyncForEach from './helpers/async-for-each.js'; -import {catchErrors, disableErrorLogging} from './helpers/errors.js'; -import { - getFeatureId, listenToAjaxedLoad, log, shortcutMap, -} from './helpers/feature-helpers.js'; +import ArrayMap from './helpers/map-of-arrays.js'; +import waitFor from './helpers/wait-for.js'; +import optionsStorage, {isFeatureDisabled, type RghOptions} from './options-storage.js'; import {contentScriptToggle} from './options/reload-without.js'; type FeatureInitResult = void | false; diff --git a/source/features/action-pr-link.tsx b/source/features/action-pr-link.tsx index 53264a40228a..2ea2418b4bf2 100644 --- a/source/features/action-pr-link.tsx +++ b/source/features/action-pr-link.tsx @@ -27,10 +27,14 @@ async function initForRepositoryActionsPage(signal: AbortSignal): Promise async function initForPrPage(signal: AbortSignal): Promise { // Exclude rgh-link, include isPRCommits - observe([ - 'main [href="/apps/github-actions"] ~ div a.status-actions', // Legacy - '[data-testid="check-run-item"] a[href*="/actions/runs/"]', // React component on isPRCommits - ], addForPr, {signal}); + observe( + [ + 'main [href="/apps/github-actions"] ~ div a.status-actions', // Legacy + '[data-testid="check-run-item"] a[href*="/actions/runs/"]', // React component on isPRCommits + ], + addForPr, + {signal}, + ); } void features.add(import.meta.url, { diff --git a/source/features/avoid-accidental-submissions.tsx b/source/features/avoid-accidental-submissions.tsx index 512ff93f53dd..4edbd74b616d 100644 --- a/source/features/avoid-accidental-submissions.tsx +++ b/source/features/avoid-accidental-submissions.tsx @@ -22,10 +22,12 @@ function onKeyDown(event: DelegateEvent): void return; } - if (elementExists([ - 'button[data-hotkey="Mod+Enter"]:disabled', - 'button[type="submit"]:disabled', - ], form)) { + if ( + elementExists([ + 'button[data-hotkey="Mod+Enter"]:disabled', + 'button[type="submit"]:disabled', + ], form) + ) { return; } @@ -37,7 +39,9 @@ function onKeyDown(event: DelegateEvent): void const message = (

- A submission via enter has been prevented. You can press enter again or use {moduleKey}enter. + A submission via enter has been prevented. You can press enter again or use{' '} + {moduleKey} + enter.

); diff --git a/source/features/ci-link.tsx b/source/features/ci-link.tsx index d83720a5fbe2..84b008a16499 100644 --- a/source/features/ci-link.tsx +++ b/source/features/ci-link.tsx @@ -39,7 +39,10 @@ async function add(anchor: HTMLElement): Promise { const endpoint = buildRepoUrl('commits/checks-statuses-rollups'); anchor.parentElement!.append( // Hide in small viewports, matches `repo-header-info` - +

- The description field was cleared by Refined GitHub. + The description field was cleared by{' '} + + Refined GitHub + .

), diff --git a/source/features/closing-remarks.tsx b/source/features/closing-remarks.tsx index 8a61dd8b4ba7..082dfc7a7539 100644 --- a/source/features/closing-remarks.tsx +++ b/source/features/closing-remarks.tsx @@ -47,7 +47,8 @@ function createReleaseUrl(): string { } async function init(signal: AbortSignal): Promise { - const mergeCommit = $(`.TimelineItem.js-details-container.Details a[href^="/${getRepo()!.nameWithOwner}/commit/" i] > code`).textContent; + const mergeCommit + = $(`.TimelineItem.js-details-container.Details a[href^="/${getRepo()!.nameWithOwner}/commit/" i] > code`).textContent; const tagName = await firstTag.get(mergeCommit); if (tagName) { @@ -57,9 +58,15 @@ async function init(signal: AbortSignal): Promise { addExistingTagLinkFooter(tagName, tagUrl); // PRs have a regular and a sticky header - observe('#partial-discussion-header relative-time', addExistingTagLinkToHeader.bind(undefined, tagName, tagUrl), {signal}); + observe('#partial-discussion-header relative-time', addExistingTagLinkToHeader.bind(undefined, tagName, tagUrl), { + signal, + }); } else { - void addReleaseBanner(<>This PR seems to be not yet released.); + void addReleaseBanner( + <> + This PR seems to be not yet released. + , + ); } } @@ -85,7 +92,9 @@ function addExistingTagLinkFooter(tagName: string, tagUrl: string): void { {createBanner({ icon: , - text: <>This pull request first appeared in {linkedTag}, + text: <> + This pull request first appeared in {linkedTag} + , classes: ['flash-success', 'rgh-bg-none'], })} diff --git a/source/features/comment-excess.tsx b/source/features/comment-excess.tsx index f4a9cbd5cf3a..149a04990e5e 100644 --- a/source/features/comment-excess.tsx +++ b/source/features/comment-excess.tsx @@ -45,8 +45,7 @@ function addIndicator(headerCommentCount: HTMLSpanElement): void { ); headerCommentCount.append(spacer); - headerCommentCount.after(link, - ); + headerCommentCount.after(link); } async function init(signal: AbortSignal): Promise { diff --git a/source/features/comments-time-machine-links.tsx b/source/features/comments-time-machine-links.tsx index 2fdaeaa1b709..0e0bf3481154 100644 --- a/source/features/comments-time-machine-links.tsx +++ b/source/features/comments-time-machine-links.tsx @@ -78,7 +78,9 @@ async function showTimeMachineBar(): Promise { ); await addNotice( - <>You can also {link} (), + <> + You can also {link} () + , ); } @@ -119,7 +121,8 @@ function addDropdownLink(menu: HTMLElement, timestamp: string): void { } function addDropdownLinkReact({delegateTarget: delegate}: DelegateEvent): void { - const timestamp = delegate.closest('[class^="Box"]')!.querySelector('relative-time[datetime]')!.attributes.datetime.value; + const timestamp + = delegate.closest('[class^="Box"]')!.querySelector('relative-time[datetime]')!.attributes.datetime.value; const menuItemList = $('[class^="prc-ActionList-ActionList"]'); const menuItem = $('[class^="prc-ActionList-ActionListItem"]', menuItemList).cloneNode(true); @@ -168,7 +171,12 @@ async function init(signal: AbortSignal): Promise { // [data-component="IconButton"] includes only React buttons // :not([id^="task-list-menu"]) excludes task list (Convert to issue/sub-issue, etc) menu buttons - delegate(`${commentSelector} button[data-component="IconButton"]:has(> .octicon-kebab-horizontal):not([id^="task-list-menu"])`, 'click', addDropdownLinkReact, {signal}); + delegate( + `${commentSelector} button[data-component="IconButton"]:has(> .octicon-kebab-horizontal):not([id^="task-list-menu"])`, + 'click', + addDropdownLinkReact, + {signal}, + ); observe( `${commentSelector} a[href^="${location.origin}"]:not(.${linkifiedUrlClass})`, diff --git a/source/features/conversation-activity-filter.tsx b/source/features/conversation-activity-filter.tsx index 6ebef5d83ddf..e7f1191592cc 100644 --- a/source/features/conversation-activity-filter.tsx +++ b/source/features/conversation-activity-filter.tsx @@ -148,7 +148,8 @@ function applyState(targetState: State): void { function createMenuItems(currentState: State): JSX.Element[] { return Object.entries(states).map(([itemState, label]) => (
  • - ); @@ -44,7 +45,10 @@ async function addConversationBanner(newCommentBox: HTMLElement): Promise {createBanner({ classes: ['rgh-bg-none'], icon: , - text: <>{getResolvedText(closingDate)} If you want to say something helpful, you can leave a {button}. Do not report issues here., + text: <> + {getResolvedText(closingDate)} If you want to say something helpful, you can leave a {button}.{' '} + Do not report issues here. + , })} ); @@ -53,10 +57,14 @@ async function addConversationBanner(newCommentBox: HTMLElement): Promise } function init(signal: AbortSignal): void | false { - observe([ - '#issuecomment-new:has(file-attachment)', - '[data-testid="comment-composer"]', - ], addConversationBanner, {signal}); + observe( + [ + '#issuecomment-new:has(file-attachment)', + '[data-testid="comment-composer"]', + ], + addConversationBanner, + {signal}, + ); } void features.add(import.meta.url, { diff --git a/source/features/rgh-token-user.tsx b/source/features/rgh-token-user.tsx index dae030075237..c9efeb14e798 100644 --- a/source/features/rgh-token-user.tsx +++ b/source/features/rgh-token-user.tsx @@ -24,9 +24,8 @@ async function verify(header: HTMLButtonElement): Promise {
    - Write API calls are blocked because your {' '} - Refined GitHub token - {' '} belongs to {currentTokenUser}, not {currentWebUser}. + Write API calls are blocked because your Refined GitHub token{' '} + belongs to {currentTokenUser}, not {currentWebUser}.
    , ); diff --git a/source/features/same-page-links.tsx b/source/features/same-page-links.tsx index a888d8f4ee0b..3b7bcc732241 100644 --- a/source/features/same-page-links.tsx +++ b/source/features/same-page-links.tsx @@ -8,14 +8,18 @@ function fix(button: HTMLAnchorElement): void { } function init(signal: AbortSignal): void { - observe([ - 'a[target="_blank"][class^="ClosedEvent-module__closerLink"]', // "Closing issue" link - 'a[target="_blank"][class^="LinkedPullRequest-module__pullRequestLink"]', // Linked PR links in issue headers - 'a[target="_blank"][class^="prc-ActionList-ActionListContent"][aria-keyshortcuts="#"]', // Linked PR links in issue header menu - 'ul[data-testid="issue-viewer-linked-pr-container"] a[target="_blank"]', // Linked PR and release links on issue sidebar - 'a[target="_blank"][class^="ReferencedEventInner-module__commitHashLink"]', // Commit linkbacks on issue timeline - 'div[data-testid="list-row-linked-pull-requests"] > a[target="_blank"]', // Linked PRs on issue list - ], fix, {signal}); + observe( + [ + 'a[target="_blank"][class^="ClosedEvent-module__closerLink"]', // "Closing issue" link + 'a[target="_blank"][class^="LinkedPullRequest-module__pullRequestLink"]', // Linked PR links in issue headers + 'a[target="_blank"][class^="prc-ActionList-ActionListContent"][aria-keyshortcuts="#"]', // Linked PR links in issue header menu + 'ul[data-testid="issue-viewer-linked-pr-container"] a[target="_blank"]', // Linked PR and release links on issue sidebar + 'a[target="_blank"][class^="ReferencedEventInner-module__commitHashLink"]', // Commit linkbacks on issue timeline + 'div[data-testid="list-row-linked-pull-requests"] > a[target="_blank"]', // Linked PRs on issue list + ], + fix, + {signal}, + ); } void features.add(import.meta.url, { diff --git a/source/features/select-notifications.tsx b/source/features/select-notifications.tsx index c03e2557e623..9b81eca41462 100644 --- a/source/features/select-notifications.tsx +++ b/source/features/select-notifications.tsx @@ -19,11 +19,17 @@ import {$, $$} from 'select-dom/strict.js'; import features from '../feature-manager.js'; import {botLinksNotificationSelectors} from '../github-helpers/selectors.js'; +import {is} from '../helpers/css-selectors.js'; import onetime from '../helpers/onetime.js'; import observe from '../helpers/selector-observer.js'; -const prIcons = ':is(.octicon-git-pull-request, .octicon-git-pull-request-closed, .octicon-git-pull-request-draft, .octicon-git-merge)'; -const issueIcons = ':is(.octicon-issue-opened, .octicon-issue-closed, .octicon-skip)'; +const prIcons = is( + '.octicon-git-pull-request', + '.octicon-git-pull-request-closed', + '.octicon-git-pull-request-draft', + '.octicon-git-merge', +); +const issueIcons = is('.octicon-issue-opened', '.octicon-issue-closed', '.octicon-skip'); const filters = { 'Pull requests': prIcons, Issues: issueIcons, @@ -166,7 +172,12 @@ function init(signal: AbortSignal): void { observe('input.js-notifications-mark-all-prompt', addDropdown, {signal}); // Close the dropdown when one of the toolbar buttons is clicked - delegate(['.js-notifications-mark-selected-actions > *', '.rgh-open-selected-button'], 'click', closeDropdown, {signal}); + delegate( + ['.js-notifications-mark-selected-actions > *', '.rgh-open-selected-button'], + 'click', + closeDropdown, + {signal}, + ); } void features.add(import.meta.url, { diff --git a/source/features/show-names.tsx b/source/features/show-names.tsx index cc14433bf77c..28ca9930e8d1 100644 --- a/source/features/show-names.tsx +++ b/source/features/show-names.tsx @@ -29,18 +29,20 @@ function createElement(element: HTMLAnchorElement, fullName: string): JSX.Elemen ); - if (element.matches([ - '[data-testid="avatar-link"]', // Commment on React-based views - '[data-testid="issue-body-header-author"]', - '.feed-item-content *', - // PR event: - // - https://github.com/refined-github/refined-github/pull/8970#event-22710755292 - // - https://github.com/refined-github/refined-github/pull/8970#event-22710646301 - // `readable-title-change-events` adds gap to rename events - '.TimelineItem-body:not(:has(> del.markdown-title)) > *', - // Reference event: https://github.com/refined-github/refined-github/pull/9041#ref-issue-4028015976 - '.TimelineItem-body > div > *', - ])) { + if ( + element.matches([ + '[data-testid="avatar-link"]', // Commment on React-based views + '[data-testid="issue-body-header-author"]', + '.feed-item-content *', + // PR event: + // - https://github.com/refined-github/refined-github/pull/8970#event-22710755292 + // - https://github.com/refined-github/refined-github/pull/8970#event-22710646301 + // `readable-title-change-events` adds gap to rename events + '.TimelineItem-body:not(:has(> del.markdown-title)) > *', + // Reference event: https://github.com/refined-github/refined-github/pull/9041#ref-issue-4028015976 + '.TimelineItem-body > div > *', + ]) + ) { nameElement.classList.add('ml-1'); } else if ( element.matches( @@ -92,9 +94,7 @@ async function updateLinks(found: HTMLAnchorElement[]): Promise { } const names = await api.v4( - [...users.keys()].map(username => - api.escapeKey(username) + `: user(login: "${username}") {name}`, - ).join(','), + [...users.keys()].map(username => api.escapeKey(username) + `: user(login: "${username}") {name}`).join(','), ); for (const [username, elements] of users) { diff --git a/source/features/show-open-prs-of-forks.tsx b/source/features/show-open-prs-of-forks.tsx index fd7ab5d6435c..d0d545ef52c7 100644 --- a/source/features/show-open-prs-of-forks.tsx +++ b/source/features/show-open-prs-of-forks.tsx @@ -65,7 +65,9 @@ async function initHeadHint(): Promise { $(`[data-hovercard-type="repository"][href="/${getForkedRepo()!}"]`).after( // The class is used by `quick-fork-deletion` - <> with {getLinkCopy(count)}, + <> + with {getLinkCopy(count)} + , ); } @@ -77,7 +79,8 @@ async function initDeleteHint(): Promise { $('details-dialog[aria-label*="Delete"] .Box-body p:first-child').after(

    - It will also abandon your {getLinkCopy(count)} in {getForkedRepo()!} and you’ll no longer be able to edit {count === 1 ? 'it' : 'them'}. + It will also abandon your {getLinkCopy(count)} in {getForkedRepo()!}{' '} + and you’ll no longer be able to edit {count === 1 ? 'it' : 'them'}.

    , ); } diff --git a/source/features/status-subscription.tsx b/source/features/status-subscription.tsx index 3164d93aca07..ecd0d079c07b 100644 --- a/source/features/status-subscription.tsx +++ b/source/features/status-subscription.tsx @@ -91,7 +91,6 @@ function addButton(subscriptionButton: HTMLButtonElement): void { Status , - // Always submitted, but ignored unless the value is `subscribe_to_custom_notifications` // Keep outside BtnGroup , @@ -112,7 +111,8 @@ function addButton(subscriptionButton: HTMLButtonElement): void { const githubApiBaseHeaders = new Headers({ accept: 'application/json', 'github-verified-fetch': 'true', - 'x-github-client-version': 'Refined GitHub. Please address https://github.com/orgs/community/discussions/132506#discussioncomment-11294985', + 'x-github-client-version': + 'Refined GitHub. Please address https://github.com/orgs/community/discussions/132506#discussioncomment-11294985', credentials: 'include', }); @@ -157,13 +157,11 @@ async function updateIssueSubscriptionStatus(targetStatus: SubscriptionStatus, i }, }; - const response = await fetch('/_graphql', - { - headers: githubApiBaseHeaders, - method: 'POST', - body: JSON.stringify(body), - }, - ); + const response = await fetch('/_graphql', { + headers: githubApiBaseHeaders, + method: 'POST', + body: JSON.stringify(body), + }); if (!response.ok) { throw new Error('Failed to update the issue subscription status'); } diff --git a/source/features/suggest-commit-title-limit.tsx b/source/features/suggest-commit-title-limit.tsx index 14d7ba89e92f..6968851fedb2 100644 --- a/source/features/suggest-commit-title-limit.tsx +++ b/source/features/suggest-commit-title-limit.tsx @@ -38,11 +38,16 @@ async function init(signal: AbortSignal): Promise { onCommitTitleUpdate(validateCommitTitle, signal); - delegate([ - ...currentPrTitleSelectors, - 'input#pull_request_title', // Old `isCompare` - TODO: Remove after August 2026 - 'input#issue_title', // Old `isPR` view - TODO: Remove after July 2026 - ], 'input', async ({delegateTarget}) => validatePrTitle(delegateTarget as HTMLInputElement), {signal, passive: true}); + delegate( + [ + ...currentPrTitleSelectors, + 'input#pull_request_title', // Old `isCompare` - TODO: Remove after August 2026 + 'input#issue_title', // Old `isPR` view - TODO: Remove after July 2026 + ], + 'input', + async ({delegateTarget}) => validatePrTitle(delegateTarget as HTMLInputElement), + {signal, passive: true}, + ); // `isPR` - input is added to the DOM when user enters editing mode and removed when they exit it // `isCompare` - input is re-rendered when previously entered title is restored observe(currentPrTitleSelectors, validatePrTitle, {signal}); diff --git a/source/features/sync-pr-commit-title.tsx b/source/features/sync-pr-commit-title.tsx index 077f23c2fb1b..5d3fb9e19c15 100644 --- a/source/features/sync-pr-commit-title.tsx +++ b/source/features/sync-pr-commit-title.tsx @@ -41,11 +41,13 @@ function createCommitTitle(): string { function needsSubmission(): boolean { const mergeButton = $optional(confirmMergeButton); const textContent = mergeButton?.textContent?.trim(); - if (!textContent || ![ - 'Confirm squash and merge', - 'Confirm auto-merge (squash)', - 'Confirm bypass rules and merge (squash)', - ].includes(textContent)) { + if ( + !textContent || ![ + 'Confirm squash and merge', + 'Confirm auto-merge (squash)', + 'Confirm bypass rules and merge (squash)', + ].includes(textContent) + ) { return false; } @@ -54,7 +56,9 @@ function needsSubmission(): boolean { } function getUi(): HTMLElement { - const cancelButton = ; + const cancelButton = ; return $optional('.rgh-sync-pr-commit-title-note') ?? (

    The title of this PR will be updated to match this title. {cancelButton} @@ -101,10 +105,14 @@ function disableSubmission(): void { function init(signal: AbortSignal): void { // PR title -> Commit title field observe(commitTitleFieldSelector, updateCommitTitle, {signal}); // On panel open - observe([ - 'h1[class^="prc-PageHeader-Title"]', - '.gh-header-title', // Old view - TODO: Remove after July 2026 - ], updateCommitTitle, {signal}); // On PR title change + observe( + [ + 'h1[class^="prc-PageHeader-Title"]', + '.gh-header-title', // Old view - TODO: Remove after July 2026 + ], + updateCommitTitle, + {signal}, + ); // On PR title change // Commit title field -> toggle checkbox visibility onCommitTitleUpdate(updateUi, signal); diff --git a/source/features/table-input.tsx b/source/features/table-input.tsx index 53aa5f730d0a..7661e0cd5f49 100644 --- a/source/features/table-input.tsx +++ b/source/features/table-input.tsx @@ -23,7 +23,6 @@ function addTable({delegateTarget: square}: DelegateEvent\n' - // on its own line // "1 space" indents without causing unwanted Markdown code blocks that 4 spaces would cause : '\n' + ' \n'.repeat(columns); diff --git a/source/features/tag-changes-link.tsx b/source/features/tag-changes-link.tsx index a7d9eea6b17a..c440acaa080b 100644 --- a/source/features/tag-changes-link.tsx +++ b/source/features/tag-changes-link.tsx @@ -115,7 +115,11 @@ async function init(): Promise { ); // The page of a tag without a release still uses the old layout #5037 - if (pageDetect.isEnterprise() || pageDetect.isTags() || (pageDetect.isSingleReleaseOrTag() && elementExists('.release'))) { + if ( + pageDetect.isEnterprise() + || pageDetect.isTags() + || (pageDetect.isSingleReleaseOrTag() && elementExists('.release')) + ) { lastLink.after(

  • {compareLink} diff --git a/source/features/tags-on-commits-list.tsx b/source/features/tags-on-commits-list.tsx index 9acb2706f18b..43307b589f4a 100644 --- a/source/features/tags-on-commits-list.tsx +++ b/source/features/tags-on-commits-list.tsx @@ -79,7 +79,8 @@ async function getTags(lastCommit: string, after?: string): Promise } const lastTag = nodes.at(-1)!.target; - const lastTagIsYounger = new Date(repository.object.committedDate) < new Date(isTagTarget(lastTag) ? lastTag.tagger.date : lastTag.committedDate); + const lastTagIsYounger = new Date(repository.object.committedDate) + < new Date(isTagTarget(lastTag) ? lastTag.tagger.date : lastTag.committedDate); // If the last tag is newer than last commit on the page, then not all commits are accounted for, keep looking if (lastTagIsYounger && repository.refs.pageInfo.hasNextPage) { diff --git a/source/features/toggle-everything-with-alt.tsx b/source/features/toggle-everything-with-alt.tsx index f662cee16f17..5a5a1e96d889 100644 --- a/source/features/toggle-everything-with-alt.tsx +++ b/source/features/toggle-everything-with-alt.tsx @@ -50,7 +50,9 @@ function init(signal: AbortSignal): void { delegate(commitMessageSelector, 'click', clickAll(commitMessageSelector), {signal}); //
    elements in issue/PR comment Markdown content - delegate('.TimelineItem-body[id] .markdown-body details > summary', 'click', clickAll(markdownCommentSelector), {signal}); + delegate('.TimelineItem-body[id] .markdown-body details > summary', 'click', clickAll(markdownCommentSelector), { + signal, + }); // "Add suggestion to batch" buttons in PR files delegate(addSuggestionToBatchSelector, 'click', clickAll(addSuggestionToBatchSelector), {signal, capture: true}); diff --git a/source/features/unread-anywhere.tsx b/source/features/unread-anywhere.tsx index 92a24242ab65..aa1cbf7d2376 100644 --- a/source/features/unread-anywhere.tsx +++ b/source/features/unread-anywhere.tsx @@ -79,7 +79,6 @@ async function addButton(nativeLink: HTMLAnchorElement): Promise { onClick={openUnreadNotifications} // Show pointer cursor even when disabled style={{width: 14, cursor: 'pointer'}} - // JSX swallows \n if you skip {''} aria-label={'Open unread notifications\nHotkey: g u'} > diff --git a/source/features/unreleased-commits.tsx b/source/features/unreleased-commits.tsx index 199c0e7bfe8c..26b287043a7b 100644 --- a/source/features/unreleased-commits.tsx +++ b/source/features/unreleased-commits.tsx @@ -1,30 +1,27 @@ import React from 'dom-chef'; -import {CachedFunction} from 'webext-storage-cache'; import * as pageDetect from 'github-url-detection'; import PlusIcon from 'octicons-plain-react/Plus'; import TagIcon from 'octicons-plain-react/Tag'; import {elementExists} from 'select-dom'; import {$optional} from 'select-dom/strict.js'; +import {CachedFunction} from 'webext-storage-cache'; import features from '../feature-manager.js'; -import observe from '../helpers/selector-observer.js'; import api from '../github-helpers/api.js'; +import getDefaultBranch from '../github-helpers/get-default-branch.js'; +import {userHasPushAccess} from '../github-helpers/get-user-permission.js'; +import {expectToken} from '../github-helpers/github-token.js'; +import {groupButtons} from '../github-helpers/group-buttons.js'; import { - buildRepoUrl, - cacheByRepo, - getLatestVersionTag, - getRepo, + buildRepoUrl, cacheByRepo, getLatestVersionTag, getRepo, } from '../github-helpers/index.js'; import isDefaultBranch from '../github-helpers/is-default-branch.js'; -import pluralize from '../helpers/pluralize.js'; import {branchSelector} from '../github-helpers/selectors.js'; -import getPublishRepoState from './unreleased-commits.gql'; -import getDefaultBranch from '../github-helpers/get-default-branch.js'; import abbreviateString from '../helpers/abbreviate-string.js'; import {wrapAll} from '../helpers/dom-utils.js'; -import {groupButtons} from '../github-helpers/group-buttons.js'; -import {expectToken} from '../github-helpers/github-token.js'; -import {userHasPushAccess} from '../github-helpers/get-user-permission.js'; +import pluralize from '../helpers/pluralize.js'; +import observe from '../helpers/selector-observer.js'; +import getPublishRepoState from './unreleased-commits.gql'; type RepoPublishState = { latestTag: string | false; @@ -63,7 +60,9 @@ const repoPublishState = new CachedFunction('tag-ahead-by', { // https://github.com/refined-github/refined-github/issues/6094 const latestTag = getLatestVersionTag([...tags.keys()]); const latestTagOid = tags.get(latestTag)!; - const aheadBy = repository.defaultBranchRef.target.history.nodes.findIndex((node: AnyObject) => node.oid === latestTagOid); + const aheadBy = repository.defaultBranchRef.target.history.nodes.findIndex((node: AnyObject) => + node.oid === latestTagOid, + ); return { latestTag, @@ -79,10 +78,9 @@ async function createLink( latestTag: string, aheadBy: number, ): Promise { - const commitCount - = aheadBy === undeterminableAheadBy - ? 'More than 20 unreleased commits' - : pluralize(aheadBy, '$$ unreleased commit'); + const commitCount = aheadBy === undeterminableAheadBy + ? 'More than 20 unreleased commits' + : pluralize(aheadBy, '$$ unreleased commit'); const label = `${commitCount}\nsince ${abbreviateString(latestTag, 30)}`; return ( @@ -92,7 +90,8 @@ async function createLink( aria-label={label} > - {aheadBy === undeterminableAheadBy || +{aheadBy}} + {' '} + {aheadBy === undeterminableAheadBy || +{aheadBy}} ); } diff --git a/source/features/update-pr-from-base-branch.tsx b/source/features/update-pr-from-base-branch.tsx index ca7d4eeda576..dc2574961253 100644 --- a/source/features/update-pr-from-base-branch.tsx +++ b/source/features/update-pr-from-base-branch.tsx @@ -101,46 +101,46 @@ const updateButtonClass = 'rgh-update-pr-from-base-branch'; function createButton(): JSX.Element { return ( -
    - { - Object.entries(updateMethods).map(([method, label]) => { - const buttonId = crypto.randomUUID(); - const tooltipId = crypto.randomUUID(); - return ( -
    - - -
    - ); - }) - } + + + +
    + ); + })} ); } -const nativeUpdateButtonSelector = '[aria-label="Conflicts"] [class^="MergeBoxSectionHeader-module__wrapper"] [data-component="buttonContent"]'; +const nativeUpdateButtonSelector + = '[aria-label="Conflicts"] [class^="MergeBoxSectionHeader-module__wrapper"] [data-component="buttonContent"]'; function canNativelyUpdate(): boolean { const nativeButton = $optional(nativeUpdateButtonSelector); diff --git a/source/features/useful-not-found-page.tsx b/source/features/useful-not-found-page.tsx index a97d7c3c0849..5179928f9b34 100644 --- a/source/features/useful-not-found-page.tsx +++ b/source/features/useful-not-found-page.tsx @@ -200,14 +200,16 @@ async function initPrCommitOnce(): Promise { const blankSlateParagraph = await elementReady('.blankslate:has(> .octicon-telescope) p', {waitForChildren: false}); blankSlateParagraph!.after( -

    You can also try to view the detached standalone commit.

    , +

    + You can also try to view the detached standalone commit. +

    , ); } async function initRepoFile(signal: AbortSignal): Promise { await expectToken(); observe('#repos-header-breadcrumb-wide-heading + ol a', crossIfNonExistent, {signal}); - observe('main div[data-testid="eror-404-description"]', showGitObjectHistoryOnRepo, {signal}); // "eror" as misspelled by GitHub + observe('main div[data-testid="eror-404-description"]', showGitObjectHistoryOnRepo, {signal}); // "eror" as misspelled by GitHub } void features.add(import.meta.url, { diff --git a/source/features/vertical-front-matter.tsx b/source/features/vertical-front-matter.tsx index 7524aa0e11f5..4a8a51ec1645 100644 --- a/source/features/vertical-front-matter.tsx +++ b/source/features/vertical-front-matter.tsx @@ -8,7 +8,8 @@ import features from '../feature-manager.js'; import observe from '../helpers/selector-observer.js'; // https://github.com/github/markup/blob/cd01f9ec87c86ce5a7c70188a74ef40fc4669c5b/lib/github/markup/markdown.rb#L34 -const hasFrontMatter = (): boolean => pageDetect.isSingleFile() && /\.(?:mdx?|mkdn?|mdwn|mdown|markdown|litcoffee)$/.test(location.pathname); +const hasFrontMatter = (): boolean => + pageDetect.isSingleFile() && /\.(?:mdx?|mkdn?|mdwn|mdown|markdown|litcoffee)$/.test(location.pathname); function transpose(table: HTMLElement): void { const rows = $$(':scope > tbody > tr', table); diff --git a/source/features/visit-tag.tsx b/source/features/visit-tag.tsx index 6b6116c4e8f8..a67d8ea4e5fa 100644 --- a/source/features/visit-tag.tsx +++ b/source/features/visit-tag.tsx @@ -11,13 +11,15 @@ import {wrapAll} from '../helpers/dom-utils.js'; import observe from '../helpers/selector-observer.js'; async function addLink(branchSelector: HTMLButtonElement): Promise { - if (elementExists([ - // If the branch picker is open, do nothing #7491 - '#selectPanel', + if ( + elementExists([ + // If the branch picker is open, do nothing #7491 + '#selectPanel', - // React view deduplication https://github.com/refined-github/refined-github/issues/7601 - '.rgh-visit-tag', - ])) { + // React view deduplication https://github.com/refined-github/refined-github/issues/7601 + '.rgh-visit-tag', + ]) + ) { return; } diff --git a/source/features/warn-pr-from-master.tsx b/source/features/warn-pr-from-master.tsx index f91003d3bdf4..81b626336c00 100644 --- a/source/features/warn-pr-from-master.tsx +++ b/source/features/warn-pr-from-master.tsx @@ -9,7 +9,14 @@ import observe from '../helpers/selector-observer.js'; async function addWarning(anchor: HTMLElement): Promise { anchor.before(
    - Note: Creating a PR from the default branch is an anti-pattern. + Note: Creating a PR from the default branch is an{' '} + + anti-pattern + .
    , ); } diff --git a/source/features/warning-for-disallow-edits.tsx b/source/features/warning-for-disallow-edits.tsx index cbe95ab7b4a1..3aee3ee2e5d2 100644 --- a/source/features/warning-for-disallow-edits.tsx +++ b/source/features/warning-for-disallow-edits.tsx @@ -9,7 +9,8 @@ import attachElement from '../helpers/attach-element.js'; const getWarning = (): React.JSX.Element => (
    - Note: Maintainers may require changes. It's easier and faster to allow them to make direct changes before merging. + Note:{' '} + Maintainers may require changes. It's easier and faster to allow them to make direct changes before merging.
    ); diff --git a/source/github-events/on-commit-title-update.ts b/source/github-events/on-commit-title-update.ts index 920979c91254..ffa83ba9c782 100644 --- a/source/github-events/on-commit-title-update.ts +++ b/source/github-events/on-commit-title-update.ts @@ -9,7 +9,10 @@ const fieldSelector = [ '#commit-summary-input', ]; -export default function onCommitTitleUpdate(callback: DelegateEventHandler, signal: AbortSignal): void { +export default function onCommitTitleUpdate( + callback: DelegateEventHandler, + signal: AbortSignal, +): void { // For immediate user input delegate(fieldSelector, 'input', callback, {signal}); } diff --git a/source/github-helpers/api.tsx b/source/github-helpers/api.tsx index c9a69bddcb8d..597bf96cfa76 100644 --- a/source/github-helpers/api.tsx +++ b/source/github-helpers/api.tsx @@ -322,7 +322,17 @@ async function getError(apiResponse: JsonObject): Promise const error = new RefinedGitHubApiError( 'Your organization requires a specific type of token.', ); - error.richMessage = <>Your organization requires a specific type of token. Fix
; + error.richMessage = <> + Your organization requires a specific type of token.{' '} + + Fix
 + + ; return error; } diff --git a/source/github-helpers/bugs-label.ts b/source/github-helpers/bugs-label.ts index 56819ffb319a..649453c7da36 100644 --- a/source/github-helpers/bugs-label.ts +++ b/source/github-helpers/bugs-label.ts @@ -1,4 +1,5 @@ -const supportedLabels = /^(?:bug|bug-?fix|confirmed-bug|(?:category|type|kind|triage|issue)[:/-]bug|(?::[\w-]+:|\p{Emoji})bug)$/iu; +const supportedLabels + = /^(?:bug|bug-?fix|confirmed-bug|(?:category|type|kind|triage|issue)[:/-]bug|(?::[\w-]+:|\p{Emoji})bug)$/iu; export default function isBugLabel(label: string): boolean { return supportedLabels.test(label.replaceAll(/\s/g, '')); } diff --git a/source/github-helpers/create-dropdown-item.tsx b/source/github-helpers/create-dropdown-item.tsx index 9e198f9068ee..4f1be05b6805 100644 --- a/source/github-helpers/create-dropdown-item.tsx +++ b/source/github-helpers/create-dropdown-item.tsx @@ -38,7 +38,6 @@ export default function createDropdownItem({ {label} -
  • ); } diff --git a/source/github-helpers/get-current-git-ref.test.ts b/source/github-helpers/get-current-git-ref.test.ts index 3f5ca37bb705..71e6e49771b8 100644 --- a/source/github-helpers/get-current-git-ref.test.ts +++ b/source/github-helpers/get-current-git-ref.test.ts @@ -7,91 +7,142 @@ import getCurrentGitRef, {getGitRef} from './get-current-git-ref.js'; // The titles supplied here listed here are real, not guessed, except the error tester test('getGitRef', () => { // Error testing - assert.equal(getGitRef( - '/', - 'some page title', - ), undefined, 'It should never throw with valid input'); - assert.throws(() => getGitRef( - 'https://github.com', - 'github.com', - )); + assert.equal( + getGitRef( + '/', + 'some page title', + ), + undefined, + 'It should never throw with valid input', + ); + assert.throws(() => + getGitRef( + 'https://github.com', + 'github.com', + ), + ); // Root - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint', - 'typescript-eslint/typescript-eslint: Monorepo for all the tooling which enables ESLint to support TypeScript', - ), undefined); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/tree/chore/lerna-4', - 'typescript-eslint/typescript-eslint at chore/lerna-4', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint', + 'typescript-eslint/typescript-eslint: Monorepo for all the tooling which enables ESLint to support TypeScript', + ), + undefined, + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/tree/chore/lerna-4', + 'typescript-eslint/typescript-eslint at chore/lerna-4', + ), + 'chore/lerna-4', + ); // Sub folder - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/tree/master/docs', - 'typescript-eslint/docs at master · typescript-eslint/typescript-eslint', - ), 'master'); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs', - 'typescript-eslint/docs at chore/lerna-4 · typescript-eslint/typescript-eslint', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/tree/master/docs', + 'typescript-eslint/docs at master · typescript-eslint/typescript-eslint', + ), + 'master', + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs', + 'typescript-eslint/docs at chore/lerna-4 · typescript-eslint/typescript-eslint', + ), + 'chore/lerna-4', + ); // Sub sub folder - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/tree/master/docs/getting-started', - 'typescript-eslint/docs/getting-started at master · typescript-eslint/typescript-eslint', - ), 'master'); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs/getting-started', - 'typescript-eslint/docs/getting-started at chore/lerna-4 · typescript-eslint/typescript-eslint', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/tree/master/docs/getting-started', + 'typescript-eslint/docs/getting-started at master · typescript-eslint/typescript-eslint', + ), + 'master', + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs/getting-started', + 'typescript-eslint/docs/getting-started at chore/lerna-4 · typescript-eslint/typescript-eslint', + ), + 'chore/lerna-4', + ); // File - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/README.md', - 'typescript-eslint/README.md at master · typescript-eslint/typescript-eslint', - ), 'master'); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/blob/chore/lerna-4/docs/getting-started/README.md', - 'typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/README.md', + 'typescript-eslint/README.md at master · typescript-eslint/typescript-eslint', + ), + 'master', + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/blob/chore/lerna-4/docs/getting-started/README.md', + 'typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', + ), + 'chore/lerna-4', + ); // Editing file - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/edit/master/docs/getting-started/README.md', - 'Editing typescript-eslint/README.md at master · typescript-eslint/typescript-eslint', - ), 'master'); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/edit/chore/lerna-4/docs/getting-started/README.md', - 'Editing typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/edit/master/docs/getting-started/README.md', + 'Editing typescript-eslint/README.md at master · typescript-eslint/typescript-eslint', + ), + 'master', + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/edit/chore/lerna-4/docs/getting-started/README.md', + 'Editing typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', + ), + 'chore/lerna-4', + ); // Blame - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/blame/master/docs/getting-started/README.md', - 'typescript-eslint/docs/getting-started/README.md at master · typescript-eslint/typescript-eslint', - ), 'master'); - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/blame/chore/lerna-4/docs/getting-started/README.md', - 'typescript-eslint/docs/getting-started/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', - ), 'chore/lerna-4'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/blame/master/docs/getting-started/README.md', + 'typescript-eslint/docs/getting-started/README.md at master · typescript-eslint/typescript-eslint', + ), + 'master', + ); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/blame/chore/lerna-4/docs/getting-started/README.md', + 'typescript-eslint/docs/getting-started/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint', + ), + 'chore/lerna-4', + ); // Single commit - assert.equal(getGitRef( - '/typescript-eslint/typescript-eslint/commit/795fd1c529ee58e97283c9ddf8463703517b50ab', - 'chore: add markdownlint (#1889) · typescript-eslint/typescript-eslint@795fd1c', - ), '795fd1c529ee58e97283c9ddf8463703517b50ab'); + assert.equal( + getGitRef( + '/typescript-eslint/typescript-eslint/commit/795fd1c529ee58e97283c9ddf8463703517b50ab', + 'chore: add markdownlint (#1889) · typescript-eslint/typescript-eslint@795fd1c', + ), + '795fd1c529ee58e97283c9ddf8463703517b50ab', + ); // Branch includes period - assert.equal(getGitRef( - '/anggrayudi/SimpleStorage/tree/release/0.8.0', - 'anggrayudi/SimpleStorage at release/0.8.0', - ), 'release/0.8.0'); + assert.equal( + getGitRef( + '/anggrayudi/SimpleStorage/tree/release/0.8.0', + 'anggrayudi/SimpleStorage at release/0.8.0', + ), + 'release/0.8.0', + ); - assert.equal(getGitRef( - '/ksh-code/repository/tree/h.l.o.o', - 'ksh-code/repository at h.l.o.o', - ), 'h.l.o.o'); + assert.equal( + getGitRef( + '/ksh-code/repository/tree/h.l.o.o', + 'ksh-code/repository at h.l.o.o', + ), + 'h.l.o.o', + ); }); // The titles supplied here listed here are real, not guessed, except the error tester diff --git a/source/github-helpers/get-user-avatar.ts b/source/github-helpers/get-user-avatar.ts index 3fedda38d201..a8f63a8bf9fc 100644 --- a/source/github-helpers/get-user-avatar.ts +++ b/source/github-helpers/get-user-avatar.ts @@ -25,6 +25,6 @@ export default function getUserAvatar(username: string, size: number): string | const url = pageDetect.isEnterprise() ? `/${cleanName}.png` : `https://avatars.githubusercontent.com/${cleanName}`; - // Why use a 2x size: https://github.com/refined-github/refined-github/pull/4973#discussion_r735133613 + // Why use a 2x size: https://github.com/refined-github/refined-github/pull/4973#discussion_r735133613 return url + `?size=${size * 2}`; } diff --git a/source/github-helpers/github-file-url.test.ts b/source/github-helpers/github-file-url.test.ts index b45bef993924..f0c627dbfac9 100644 --- a/source/github-helpers/github-file-url.test.ts +++ b/source/github-helpers/github-file-url.test.ts @@ -67,14 +67,25 @@ test('change filePath', () => { }); test('get filePath from search', () => { - const url = new GitHubFileUrl('https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670?after=f23b687b3b89aa95a76193722cdfeff740646670+34&path%5B%5D=source&path%5B%5D=features&path%5B%5D=release-download-count.tsx'); + const url = new GitHubFileUrl( + 'https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670?after=f23b687b3b89aa95a76193722cdfeff740646670+34&path%5B%5D=source&path%5B%5D=features&path%5B%5D=release-download-count.tsx', + ); assert.equal(url.user, 'yakov116'); assert.equal(url.repository, 'refined-github'); assert.equal(url.route, 'commits'); assert.equal(url.branch, 'f23b687b3b89aa95a76193722cdfeff740646670'); assert.equal(url.filePath, 'source/features/release-download-count.tsx'); - assert.equal(url.pathname, '/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx'); - assert.equal(url.href, 'https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx?after=f23b687b3b89aa95a76193722cdfeff740646670+34'); + assert.equal( + url.pathname, + '/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx', + ); + assert.equal( + url.href, + 'https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx?after=f23b687b3b89aa95a76193722cdfeff740646670+34', + ); assert.equal(url.search, '?after=f23b687b3b89aa95a76193722cdfeff740646670+34'); - assert.equal(String(url), 'https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx?after=f23b687b3b89aa95a76193722cdfeff740646670+34'); + assert.equal( + String(url), + 'https://github.com/yakov116/refined-github/commits/f23b687b3b89aa95a76193722cdfeff740646670/source/features/release-download-count.tsx?after=f23b687b3b89aa95a76193722cdfeff740646670+34', + ); }); diff --git a/source/github-helpers/github-file-url.ts b/source/github-helpers/github-file-url.ts index 370d086986a2..65c9748fc323 100644 --- a/source/github-helpers/github-file-url.ts +++ b/source/github-helpers/github-file-url.ts @@ -55,7 +55,9 @@ export default class GitHubFileUrl { for (const [index, section] of currentBranchSections.entries()) { if (ambiguousReference[index] !== section) { - console.warn(`The supplied path (${ambiguousReference.join('/')}) is ambiguous (current reference is \`${currentBranch}\`)`); + console.warn( + `The supplied path (${ambiguousReference.join('/')}) is ambiguous (current reference is \`${currentBranch}\`)`, + ); return {branch, filePath}; } } @@ -67,7 +69,10 @@ export default class GitHubFileUrl { } get pathname(): string { - return `/${this.user}/${this.repository}/${this.route}/${this.branch}/${this.filePath}`.replaceAll(/(?:(?:undefined)?\/)+$/g, ''); + return `/${this.user}/${this.repository}/${this.route}/${this.branch}/${this.filePath}`.replaceAll( + /(?:(?:undefined)?\/)+$/g, + '', + ); } set pathname(pathname: string) { diff --git a/source/github-helpers/github-token.ts b/source/github-helpers/github-token.ts index eb01acc63aa4..fdcd2ea73d63 100644 --- a/source/github-helpers/github-token.ts +++ b/source/github-helpers/github-token.ts @@ -121,6 +121,10 @@ export async function expectTokenScope(scope: string): Promise { const {scopes: tokenScopes} = await getTokenInfo(api, token); if (!tokenScopes.includes(scope)) { - throw new Error('The token you provided does not have ' + (tokenScopes.length > 0 ? `the \`${scope}\` scope. It only includes \`${tokenScopes.join(', ')}\`.` : 'any scope. You can change the scope of your token at https://github.com/settings/tokens')); + throw new Error( + 'The token you provided does not have ' + (tokenScopes.length > 0 + ? `the \`${scope}\` scope. It only includes \`${tokenScopes.join(', ')}\`.` + : 'any scope. You can change the scope of your token at https://github.com/settings/tokens'), + ); } } diff --git a/source/github-helpers/hotkey.tsx b/source/github-helpers/hotkey.tsx index 27212ed7c522..572749acb8cb 100644 --- a/source/github-helpers/hotkey.tsx +++ b/source/github-helpers/hotkey.tsx @@ -2,7 +2,11 @@ import React from 'dom-chef'; import {isMac} from './index.js'; -export function registerHotkey(hotkey: string, functionOrUrl: VoidFunction | string, {signal}: SignalAsOptions = {}): void { +export function registerHotkey( + hotkey: string, + functionOrUrl: VoidFunction | string, + {signal}: SignalAsOptions = {}, +): void { const element = typeof functionOrUrl === 'string' ? - + + , ); diff --git a/source/helpers/clean-commit-message.test.ts b/source/helpers/clean-commit-message.test.ts index 9d9a7f00fa7e..a4d1eb2dac36 100644 --- a/source/helpers/clean-commit-message.test.ts +++ b/source/helpers/clean-commit-message.test.ts @@ -14,39 +14,68 @@ test('cleanCommitMessage', () => { multi-line `)); - assert.equal(cleanCommitMessage(` + assert.equal( + cleanCommitMessage(` Some stuff happened ${coauthors[0]} - `), coauthors[0], 'Should preserve just the co-authors'); + `), + coauthors[0], + 'Should preserve just the co-authors', + ); - assert.equal(cleanCommitMessage(` + assert.equal( + cleanCommitMessage(` Some stuff happened ${coauthors[0]} Fixes #112345 ${coauthors[2]} - `), coauthors[0] + '\n' + coauthors[2], 'Should preserve multiple co-authors'); + `), + coauthors[0] + '\n' + coauthors[2], + 'Should preserve multiple co-authors', + ); - assert.equal(cleanCommitMessage(` + assert.equal( + cleanCommitMessage(` Some stuff happened ${coauthors[0]} More stuff ${coauthors[1]} - `), coauthors[0], 'Should de-duplicate inconsistent co-authored-by casing'); + `), + coauthors[0], + 'Should de-duplicate inconsistent co-authored-by casing', + ); - assert.isEmpty(cleanCommitMessage(` + assert.isEmpty( + cleanCommitMessage(` Fixes #1345 - `), 'Should drop closing keywords'); + `), + 'Should drop closing keywords', + ); - assert.equal(cleanCommitMessage(` + assert.equal( + cleanCommitMessage( + ` Fixes #1345 - `, true), 'Fixes #1345', 'Should keep closing keywords when asked'); - assert.equal(cleanCommitMessage(` + `, + true, + ), + 'Fixes #1345', + 'Should keep closing keywords when asked', + ); + assert.equal( + cleanCommitMessage( + ` Fixes #1 ${coauthors[0]} closes https://github.com/refined-github/refined-github/pull/6328 - `, true), [ - coauthors[0], - 'Fixes #1', - 'closes https://github.com/refined-github/refined-github/pull/6328', - ].join('\n'), 'Should keep multiple closing keywords'); + `, + true, + ), + [ + coauthors[0], + 'Fixes #1', + 'closes https://github.com/refined-github/refined-github/pull/6328', + ].join('\n'), + 'Should keep multiple closing keywords', + ); }); diff --git a/source/helpers/conventional-commits.test.ts b/source/helpers/conventional-commits.test.ts index 7332edd70fcb..1535d75cb36e 100644 --- a/source/helpers/conventional-commits.test.ts +++ b/source/helpers/conventional-commits.test.ts @@ -59,7 +59,7 @@ test('parseConventionalCommit', () => { "type": "Feature", } `); - expect(parseConventionalCommit(('feat: Commit (message)'))).toMatchInlineSnapshot(` + expect(parseConventionalCommit('feat: Commit (message)')).toMatchInlineSnapshot(` { "raw": "feat: ", "rawType": "feat", @@ -139,7 +139,7 @@ test('parseConventionalCommit support upper case types', () => { "type": "Feature", } `); - expect(parseConventionalCommit(('Feat: Commit (message)'))).toMatchInlineSnapshot(` + expect(parseConventionalCommit('Feat: Commit (message)')).toMatchInlineSnapshot(` { "raw": "Feat: ", "rawType": "Feat", diff --git a/source/helpers/dom-utils.ts b/source/helpers/dom-utils.ts index 5a42556f23df..5290e6ce6795 100644 --- a/source/helpers/dom-utils.ts +++ b/source/helpers/dom-utils.ts @@ -52,13 +52,15 @@ export const wrapAll = (wrapper: Wrapper, ...targets: A return wrapper; }; -export const isEditable = (node: unknown): boolean => node instanceof HTMLTextAreaElement +export const isEditable = (node: unknown): boolean => + node instanceof HTMLTextAreaElement || node instanceof HTMLInputElement || (node instanceof HTMLElement && node.isContentEditable); -export const frame = async (): Promise => new Promise(resolve => { - requestAnimationFrame(resolve); -}); +export const frame = async (): Promise => + new Promise(resolve => { + requestAnimationFrame(resolve); + }); export const highlightTab = (tabElement: Element): void => { tabElement.classList.add('selected'); diff --git a/source/helpers/errors.ts b/source/helpers/errors.ts index 63fa1a263eb1..134a93187091 100644 --- a/source/helpers/errors.ts +++ b/source/helpers/errors.ts @@ -11,8 +11,10 @@ export function disableErrorLogging(): void { const {version} = chrome.runtime.getManifest(); -const fineGrainedTokenSuggestion = 'Please use a GitHub App, OAuth App, or a personal access token with fine-grained permissions.'; -const preferredMessage = 'Refined GitHub does not support per-organization fine-grained tokens. https://github.com/refined-github/refined-github/wiki/Security'; +const fineGrainedTokenSuggestion + = 'Please use a GitHub App, OAuth App, or a personal access token with fine-grained permissions.'; +const preferredMessage + = 'Refined GitHub does not support per-organization fine-grained tokens. https://github.com/refined-github/refined-github/wiki/Security'; // Reads from path like assets/features/NAME.js export function parseFeatureNameFromStack(stack: string = new Error('stack').stack!): FeatureId | undefined { @@ -67,11 +69,14 @@ export function logError(error: Error): void { newIssueUrl.searchParams.set('template', '1_bug_report.yml'); newIssueUrl.searchParams.set('title', id ? `\`${id}\`: ${message}` : message); newIssueUrl.searchParams.set('repro', location.href); - newIssueUrl.searchParams.set('description', [ - '```', - String(error instanceof Error ? error.stack! : error).trim(), - '```', - ].join('\n')); + newIssueUrl.searchParams.set( + 'description', + [ + '```', + String(error instanceof Error ? error.stack! : error).trim(), + '```', + ].join('\n'), + ); // Don't change this to `throw Error` because Firefox doesn't show extensions' errors in the console console.group(`❌ Refined GitHub: ${id ?? 'global'}`); // Safari supports only one parameter diff --git a/source/helpers/feature-utils.test.ts b/source/helpers/feature-utils.test.ts index 2fb6599bc7b9..6ab974ed2695 100644 --- a/source/helpers/feature-utils.test.ts +++ b/source/helpers/feature-utils.test.ts @@ -12,40 +12,64 @@ test('shouldFeatureRun', async () => { assert.isTrue(await shouldFeatureRun({}), 'A lack of conditions should mean "run everywhere"'); - assert.isFalse(await shouldFeatureRun({ - asLongAs: yesNo, - }), 'Every `asLongAs` should be true to run'); - - assert.isFalse(await shouldFeatureRun({ - asLongAs: yesNo, - include: [yes], - }), 'Every `asLongAs` should be true to run, regardless of `include`'); - - assert.isFalse(await shouldFeatureRun({ - include: noNo, - }), 'At least one `include` should be true to run'); - - assert.isTrue(await shouldFeatureRun({ - include: yesNo, - }), 'If one `include` is true, then it should run'); - - assert.isFalse(await shouldFeatureRun({ - exclude: yesNo, - }), 'If any `exclude` is true, then it should not run'); - - assert.isFalse(await shouldFeatureRun({ - include: [yes], - exclude: yesNo, - }), 'If any `exclude` is true, then it should not run, regardless of `include`'); - - assert.isFalse(await shouldFeatureRun({ - asLongAs: [yes], - exclude: yesNo, - }), 'If any `exclude` is true, then it should not run, regardless of `asLongAs`'); - - assert.isFalse(await shouldFeatureRun({ - asLongAs: [yes], - include: yesYes, - exclude: yesNo, - }), 'If any `exclude` is true, then it should not run, regardless of `asLongAs` and `include`'); + assert.isFalse( + await shouldFeatureRun({ + asLongAs: yesNo, + }), + 'Every `asLongAs` should be true to run', + ); + + assert.isFalse( + await shouldFeatureRun({ + asLongAs: yesNo, + include: [yes], + }), + 'Every `asLongAs` should be true to run, regardless of `include`', + ); + + assert.isFalse( + await shouldFeatureRun({ + include: noNo, + }), + 'At least one `include` should be true to run', + ); + + assert.isTrue( + await shouldFeatureRun({ + include: yesNo, + }), + 'If one `include` is true, then it should run', + ); + + assert.isFalse( + await shouldFeatureRun({ + exclude: yesNo, + }), + 'If any `exclude` is true, then it should not run', + ); + + assert.isFalse( + await shouldFeatureRun({ + include: [yes], + exclude: yesNo, + }), + 'If any `exclude` is true, then it should not run, regardless of `include`', + ); + + assert.isFalse( + await shouldFeatureRun({ + asLongAs: [yes], + exclude: yesNo, + }), + 'If any `exclude` is true, then it should not run, regardless of `asLongAs`', + ); + + assert.isFalse( + await shouldFeatureRun({ + asLongAs: [yes], + include: yesYes, + exclude: yesNo, + }), + 'If any `exclude` is true, then it should not run, regardless of `asLongAs` and `include`', + ); }); diff --git a/source/helpers/feature-utils.ts b/source/helpers/feature-utils.ts index 49b804740266..c3530e131f27 100644 --- a/source/helpers/feature-utils.ts +++ b/source/helpers/feature-utils.ts @@ -19,7 +19,9 @@ export function isFeaturePrivate(id: string): boolean { } // Safari iOS 17.6 has the key, but it does nothing -export const doesBrowserActionOpenOptions = !globalThis.chrome?.contextMenus || navigator.platform === 'iPhone' || navigator.platform === 'iPad'; +export const doesBrowserActionOpenOptions = !globalThis.chrome?.contextMenus + || navigator.platform === 'iPhone' + || navigator.platform === 'iPad'; export async function shouldFeatureRun({ /** Every condition must be true */ diff --git a/source/helpers/fetch-dom.ts b/source/helpers/fetch-dom.ts index e5a1bfd27639..c16351d33f13 100644 --- a/source/helpers/fetch-dom.ts +++ b/source/helpers/fetch-dom.ts @@ -5,7 +5,10 @@ import type {ParseSelector} from 'typed-query-selector/parser.js'; import {log} from './feature-helpers.js'; async function fetchDom(url: string): Promise; -async function fetchDom>(url: string, selector: Selector): Promise; +async function fetchDom< + Selector extends string, + ElementType extends HTMLElement = ParseSelector, +>(url: string, selector: Selector): Promise; async function fetchDom(url: string, selector?: string): Promise { log.http(url); const absoluteUrl = new URL(url, location.origin).href; // Firefox `fetch`es from the content script, so relative URLs fail diff --git a/source/helpers/onetime.ts b/source/helpers/onetime.ts index ba86a2e8818c..6eb55e6efbbe 100644 --- a/source/helpers/onetime.ts +++ b/source/helpers/onetime.ts @@ -1,5 +1,7 @@ const notRun = Symbol('false'); -export default function onetime(function_: (...arguments_: ArgumentsType) => ReturnType): (...arguments_: ArgumentsType) => ReturnType { +export default function onetime( + function_: (...arguments_: ArgumentsType) => ReturnType, +): (...arguments_: ArgumentsType) => ReturnType { let returnValue: ReturnType | typeof notRun = notRun; return function (this: unknown, ...arguments_: ArgumentsType): ReturnType { if (returnValue !== notRun) { diff --git a/source/helpers/preserve-scroll.ts b/source/helpers/preserve-scroll.ts index 7333eaa54183..9e5bbda3e1c7 100644 --- a/source/helpers/preserve-scroll.ts +++ b/source/helpers/preserve-scroll.ts @@ -1,4 +1,6 @@ -export default function preserveScroll(anchor: Element = document.elementFromPoint(innerWidth / 2, innerHeight / 2)!): VoidFunction { +export default function preserveScroll( + anchor: Element = document.elementFromPoint(innerWidth / 2, innerHeight / 2)!, +): VoidFunction { const originalPosition = anchor.getBoundingClientRect().top; /** diff --git a/source/options.tsx b/source/options.tsx index 20539e75c7ef..db012e3d2aa0 100644 --- a/source/options.tsx +++ b/source/options.tsx @@ -69,7 +69,9 @@ function updateRateLink(): void { return; } - $('a#rate-link').href = isFirefox() ? 'https://addons.mozilla.org/firefox/addon/refined-github-' : 'https://apps.apple.com/app/id1519867270?action=write-review'; + $('a#rate-link').href = isFirefox() + ? 'https://addons.mozilla.org/firefox/addon/refined-github-' + : 'https://apps.apple.com/app/id1519867270?action=write-review'; } function isEnterprise(): boolean { @@ -87,10 +89,9 @@ function getExclusions(): string | void { } async function showStoredCssHotfixes(): Promise { - $('#hotfixes-field').textContent - = getExclusions() - ?? await styleHotfixes.getCached(version) - ?? 'No CSS found in cache.'; + $('#hotfixes-field').textContent = getExclusions() + ?? await styleHotfixes.getCached(version) + ?? 'No CSS found in cache.'; } async function fetchHotfixes(event: MouseEvent): Promise { @@ -98,10 +99,9 @@ async function fetchHotfixes(event: MouseEvent): Promise { button.disabled = true; try { // Style - $('#hotfixes-field').textContent - = getExclusions() - ?? await styleHotfixes.getFresh(version) - ?? 'No hotfixes needed for this version! 🎉'; + $('#hotfixes-field').textContent = getExclusions() + ?? await styleHotfixes.getFresh(version) + ?? 'No hotfixes needed for this version! 🎉'; // Broken features const storage = await brokenFeatures.getFresh(); diff --git a/source/options/feature-list.tsx b/source/options/feature-list.tsx index 19459c8f4fd0..5dba74387a35 100644 --- a/source/options/feature-list.tsx +++ b/source/options/feature-list.tsx @@ -29,7 +29,7 @@ async function markLocalHotfixes(): Promise { input.disabled = true; input.removeAttribute('name'); $(`.feature-name[for="${feature}"]`).after( - (Disabled due to {createRghIssueLink(relatedIssue)}), + {' '}(Disabled due to {createRghIssueLink(relatedIssue)}), ); } } @@ -40,8 +40,7 @@ function buildFeatureCheckbox({id, description, screenshot}: FeatureMeta): HTMLE
    - - {' '} + {' '} source @@ -114,9 +113,10 @@ function updateOffCount(): void { export default async function initFeatureList(): Promise { // Generate list - $('.js-features').append(...featuresMeta - .filter(feature => importedFeatures.includes(feature.id)) - .map(feature => buildFeatureCheckbox(feature)), + $('.js-features').append( + ...featuresMeta + .filter(feature => importedFeatures.includes(feature.id)) + .map(feature => buildFeatureCheckbox(feature)), ); // Add notice for features disabled via hotfix diff --git a/source/options/header.svelte b/source/options/header.svelte index cf978ab06a54..4d4541c4c0dc 100644 --- a/source/options/header.svelte +++ b/source/options/header.svelte @@ -7,7 +7,6 @@ }} /> -