|
1 | 1 | import reservedNames from 'github-reserved-names/reserved-names.json' with {type: 'json'}; |
2 | 2 | import {addTests} from './collector.ts'; |
3 | | - |
4 | | -// Selector helpers with typed-query-selector validation. |
5 | | -// The generic parameter allows type override when selector inference isn't specific enough (e.g., attribute-only selectors) |
6 | | -const $ = <E extends Element = Element>(selector: string) => document.querySelector<E>(selector); |
7 | | -const exists = (selector: string): boolean => Boolean(document.querySelector(selector)); |
| 3 | +import type {StrictlyParseSelector} from './strict-types.ts'; |
| 4 | + |
| 5 | +// Selector helpers with typed-query-selector strict validation |
| 6 | +// Overload 1: Strict validation with type inference from selector |
| 7 | +function $<S extends string, E extends StrictlyParseSelector<S>>( |
| 8 | + selector: S, |
| 9 | +): [E] extends [never] ? never : E | undefined; |
| 10 | +// Overload 2: Allow explicit type override when inference isn't specific enough |
| 11 | +function $<E extends Element>(selector: string): E | undefined; |
| 12 | +// Implementation |
| 13 | +// eslint-disable-next-line @typescript-eslint/no-unsafe-return -- Return type is inferred from overloads |
| 14 | +function $(selector: string) { |
| 15 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- Return type validated by strict typing |
| 16 | + return document.querySelector(selector); |
| 17 | +} |
| 18 | + |
| 19 | +// @ts-expect-error -- E is inferred by TypeScript automatically, not used explicitly |
| 20 | +function exists<S extends string, E extends StrictlyParseSelector<S>>(selector: S): boolean { |
| 21 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- querySelector return type validated by strict typing |
| 22 | + const element = document.querySelector(selector); |
| 23 | + return Boolean(element); |
| 24 | +} |
8 | 25 |
|
9 | 26 | const combinedTestOnly = ['combinedTestOnly']; // To be used only to skip tests of combined functions, i.e. isPageA() || isPageB() |
10 | 27 |
|
@@ -297,6 +314,7 @@ TEST: addTests('isQuickPR', [ |
297 | 314 | 'https://github.com/sindresorhus/refined-github/compare/test-branch?quick_pull=1', |
298 | 315 | ]); |
299 | 316 |
|
| 317 | +// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call -- Strict validation may mark complex selectors as potentially invalid |
300 | 318 | const getStateLabel = (): string | undefined => $([ |
301 | 319 | '.State', // Old view |
302 | 320 | // React versions |
@@ -402,6 +420,7 @@ export const isEmptyRepo = (): boolean => exists('[aria-label="Cannot fork becau |
402 | 420 |
|
403 | 421 | export const isPublicRepo = (): boolean => exists('meta[name="octolytics-dimension-repository_public"][content="true"]'); |
404 | 422 |
|
| 423 | +// eslint-disable-next-line @typescript-eslint/no-unsafe-call -- Strict validation may mark selectors with child combinators as potentially invalid |
405 | 424 | export const isArchivedRepo = (): boolean => Boolean(isRepo() && $('main > .flash-warn')?.textContent!.includes('archived')); |
406 | 425 |
|
407 | 426 | export const isBlank = (): boolean => exists('main .blankslate:not([hidden] .blankslate)'); |
@@ -861,6 +880,7 @@ TEST: addTests('isNewRepoTemplate', [ |
861 | 880 | ]); |
862 | 881 |
|
863 | 882 | /** Get the logged-in user’s username */ |
| 883 | +// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call -- getAttribute is safe |
864 | 884 | const getLoggedInUser = (): string | undefined => $('meta[name="user-login"]')?.getAttribute('content') ?? undefined; |
865 | 885 |
|
866 | 886 | /** Drop all redundant slashes */ |
|
0 commit comments