Update to eslint 10 (#36925)

- Enable a few more rules, fix issues. The 2 `value` issues are
false-positives.
- Add exact types for `window.pageData` and
`window.notificationSettings`.
- peerDependencyRules for eslint-plugin-github unrestricted, the plugin
works in v10, but does not declare compatibility, pending
https://github.com/github/eslint-plugin-github/issues/680.
- Added
[eslint-plugin-de-morgan](https://github.com/azat-io/eslint-plugin-de-morgan),
no violations.

---------

Signed-off-by: silverwind <me@silverwind.io>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
silverwind
2026-03-23 08:49:25 +01:00
committed by GitHub
parent 4ba90207cf
commit ae0bc0222a
16 changed files with 494 additions and 261 deletions
+62 -13
View File
@@ -3,14 +3,30 @@ import {nextTick, defineComponent} from 'vue';
import {SvgIcon} from '../svg.ts';
import {GET} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import type {SvgName} from '../svg.ts';
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
type DashboardRepo = {
id: number,
link: string,
full_name: string,
archived: boolean,
fork: boolean,
mirror: boolean,
template: boolean,
private: boolean,
internal: boolean,
latest_commit_status_state?: CommitStatus,
latest_commit_status_state_link?: string,
locale_latest_commit_status_state?: string,
};
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
type CommitStatusMap = {
[status in CommitStatus]: {
name: string,
name: SvgName,
color: string,
};
};
@@ -38,8 +54,8 @@ export default defineComponent({
return {
tab,
repos: [],
reposTotalCount: null,
repos: [] as DashboardRepo[],
reposTotalCount: null as number | null,
reposFilter,
archivedFilter,
privateFilter,
@@ -48,7 +64,7 @@ export default defineComponent({
searchQuery,
isLoading: false,
staticPrefix: assetUrlPrefix,
counts: {},
counts: {} as Record<string, number>,
repoTypes: {
all: {
searchMode: '',
@@ -65,16 +81,49 @@ export default defineComponent({
collaborative: {
searchMode: 'collaborative',
},
},
textArchivedFilterTitles: {},
textPrivateFilterTitles: {},
organizations: [],
} as Record<string, {searchMode: string}>,
textArchivedFilterTitles: {} as Record<string, string>,
textPrivateFilterTitles: {} as Record<string, string>,
organizations: [] as Array<{name: string, full_name: string, num_repos: number, org_visibility: string}>,
isOrganization: true,
canCreateOrganization: false,
organizationsTotalCount: 0,
organizationId: 0,
searchLimit: 0,
uid: 0,
teamId: 0,
isMirrorsEnabled: false,
isStarsEnabled: false,
canCreateMigrations: false,
textNoOrg: '',
textNoRepo: '',
textRepository: '',
textOrganization: '',
textMyRepos: '',
textNewRepo: '',
textSearchRepos: '',
textFilter: '',
textShowArchived: '',
textShowPrivate: '',
textShowBothArchivedUnarchived: '',
textShowOnlyUnarchived: '',
textShowOnlyArchived: '',
textShowBothPrivatePublic: '',
textShowOnlyPublic: '',
textShowOnlyPrivate: '',
textAll: '',
textSources: '',
textForks: '',
textMirrors: '',
textCollaborative: '',
textFirstPage: '',
textPreviousPage: '',
textNextPage: '',
textLastPage: '',
textMyOrgs: '',
textNewOrg: '',
textOrgVisibilityLimited: '',
textOrgVisibilityPrivate: '',
subUrl: appSubUrl,
...pageData.dashboardRepoList,
activeIndex: -1, // don't select anything at load, first cursor down will select
@@ -250,7 +299,7 @@ export default defineComponent({
nextTick(() => {
// MDN: If there's no focused element, this is the Document.body or Document.documentElement.
if ((document.activeElement === document.body || document.activeElement === document.documentElement)) {
this.$refs.search.focus({preventScroll: true});
(this.$refs.search as HTMLInputElement).focus({preventScroll: true});
}
});
}
@@ -283,7 +332,7 @@ export default defineComponent({
}
},
repoIcon(repo: any) {
repoIcon(repo: DashboardRepo) {
if (repo.fork) {
return 'octicon-repo-forked';
} else if (repo.mirror) {
@@ -435,7 +484,7 @@ export default defineComponent({
<svg-icon name="octicon-archive" :size="16"/>
</div>
</a>
<a class="tw-flex tw-items-center" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link || null" :data-tooltip-content="repo.locale_latest_commit_status_state">
<a class="tw-flex tw-items-center" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link || undefined" :data-tooltip-content="repo.locale_latest_commit_status_state">
<!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
<svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class="'tw-ml-2 commit-status icon ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
</a>
@@ -5,7 +5,7 @@ import {toggleElem} from '../utils/dom.ts';
const {pageData} = window.config;
const mergeForm = pageData.pullRequestMergeForm;
const mergeForm = pageData.pullRequestMergeForm!;
const mergeTitleFieldValue = shallowRef('');
const mergeMessageFieldValue = shallowRef('');
+1 -1
View File
@@ -49,7 +49,7 @@ defineProps<{
const isLoading = shallowRef(false);
const errorText = shallowRef('');
const repoLink = pageData.repoLink;
const repoLink = pageData.repoLink!;
const data = shallowRef<DayData[]>([]);
onMounted(() => {
+1 -1
View File
@@ -46,7 +46,7 @@ defineProps<{
const isLoading = shallowRef(false);
const errorText = shallowRef('');
const repoLink = pageData.repoLink;
const repoLink = pageData.repoLink!;
const data = ref<DayData[]>([]);
onMounted(() => {
+1 -1
View File
@@ -11,7 +11,7 @@ async function initInputCitationValue(citationCopyApa: HTMLButtonElement, citati
import(/* webpackChunkName: "citation-js-bibtex" */'@citation-js/plugin-bibtex'),
import(/* webpackChunkName: "citation-js-csl" */'@citation-js/plugin-csl'),
]);
const {citationFileContent} = pageData;
const citationFileContent = pageData.citationFileContent!;
const config = plugins.config.get('@bibtex');
config.constants.fieldTypes.doi = ['field', 'literal'];
config.constants.fieldTypes.version = ['field', 'literal'];
+1 -1
View File
@@ -99,7 +99,7 @@ export async function submitFormFetchAction(formEl: HTMLFormElement, opts: Submi
if (formMethod.toLowerCase() === 'get') {
const params = new URLSearchParams();
for (const [key, value] of formData) {
params.append(key, value.toString());
params.append(key, value as string);
}
const pos = reqUrl.indexOf('?');
if (pos !== -1) {
+5 -4
View File
@@ -3,7 +3,8 @@ import {setFileFolding} from './file-fold.ts';
import {POST} from '../modules/fetch.ts';
const {pageData} = window.config;
const prReview = pageData.prReview || {};
// it is undefined on most pages, fortunately, when it is accessed by the related functions, it exists
const prReview = pageData.prReview!;
const viewedStyleClass = 'viewed-file-checked-form';
const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found
const expandFilesBtnSelector = '#expand-files-btn';
@@ -13,11 +14,11 @@ const collapseFilesBtnSelector = '#collapse-files-btn';
// The data used will be window.config.pageData.prReview.numberOf{Viewed}Files
function refreshViewedFilesSummary() {
const viewedFilesProgress = document.querySelector('#viewed-files-summary')!;
viewedFilesProgress.setAttribute('value', prReview.numberOfViewedFiles);
viewedFilesProgress.setAttribute('value', String(prReview.numberOfViewedFiles));
const summaryLabel = document.querySelector<HTMLElement>('#viewed-files-summary-label')!;
summaryLabel.textContent = summaryLabel.getAttribute('data-text-changed-template')!
.replace('%[1]d', prReview.numberOfViewedFiles)
.replace('%[2]d', prReview.numberOfFiles);
.replace('%[1]d', String(prReview.numberOfViewedFiles))
.replace('%[2]d', String(prReview.numberOfFiles));
}
// Initializes a listener for all children of the given html element
+1 -1
View File
@@ -7,7 +7,7 @@ export function initRepositorySearch() {
const params = new URLSearchParams();
for (const [key, value] of new FormData(repositorySearchForm).entries()) {
params.set(key, value.toString());
params.set(key, value as string);
}
if ((e.target as HTMLInputElement).name === 'clear-filter') {
params.delete('archived');
+24 -2
View File
@@ -26,8 +26,30 @@ interface Window {
assetUrlPrefix: string,
runModeIsProd: boolean,
customEmojis: Record<string, string>,
pageData: Record<string, any>,
notificationSettings: Record<string, any>,
pageData: Record<string, any> & {
adminUserListSearchForm?: {
SortType: string,
StatusFilterMap: Record<string, string>,
},
citationFileContent?: string,
prReview?: {
numberOfFiles: number,
numberOfViewedFiles: number,
},
DiffFileTree?: import('./modules/diff-file.ts').DiffFileTreeData,
FolderIcon?: string,
FolderOpenIcon?: string,
repoLink?: string,
repoActivityTopAuthors?: any[],
pullRequestMergeForm?: Record<string, any>,
dashboardRepoList?: Record<string, any>,
},
notificationSettings: {
MinTimeout: number,
TimeoutStep: number,
MaxTimeout: number,
EventSourceUpdateTime: number,
},
enableTimeTracking: boolean,
mermaidMaxSourceCharacters: number,
i18n: Record<string, string>,
+2 -2
View File
@@ -17,7 +17,7 @@ export type DiffTreeEntry = {
ParentEntry?: DiffTreeEntry,
};
type DiffFileTreeData = {
export type DiffFileTreeData = {
TreeRoot: DiffTreeEntry,
};
@@ -33,7 +33,7 @@ type DiffFileTree = {
let diffTreeStoreReactive: Reactive<DiffFileTree>;
export function diffTreeStore() {
if (!diffTreeStoreReactive) {
diffTreeStoreReactive = reactiveDiffTreeStore(pageData.DiffFileTree, pageData.FolderIcon, pageData.FolderOpenIcon);
diffTreeStoreReactive = reactiveDiffTreeStore(pageData.DiffFileTree!, pageData.FolderIcon!, pageData.FolderOpenIcon!);
}
return diffTreeStoreReactive;
}
+6 -6
View File
@@ -112,8 +112,8 @@ export function blobToDataURI(blob: Blob): Promise<string> {
reject(new Error('blobToDataURI: FileReader error'));
});
reader.readAsDataURL(blob);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
}
@@ -135,16 +135,16 @@ export function convertImage(blob: Blob, mime: string): Promise<Blob> {
if (!(blob instanceof Blob)) return reject(new Error('convertImage: toBlob failed'));
resolve(blob);
}, mime);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
img.addEventListener('error', () => {
reject(new Error('convertImage: image failed to load'));
});
img.src = await blobToDataURI(blob);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
}
+1 -1
View File
@@ -8,7 +8,7 @@ window.config = {
runModeIsProd: true,
customEmojis: {},
pageData: {},
notificationSettings: {},
notificationSettings: {MinTimeout: 0, TimeoutStep: 0, MaxTimeout: 0, EventSourceUpdateTime: 0},
enableTimeTracking: true,
mermaidMaxSourceCharacters: 5000,
i18n: {},