chore(lint): apply the main eslint config to vue files (#39545)

Vue files only got `eslint-plugin-vue` rules, so stylistic and
TypeScript rules never ran on them, and `import-x` could not see cycles
between `.ts` and `.vue` files.

- Apply the main ESLint config to `.vue` files
- Let `import-x` parse `.vue` files, which surfaced a cycle between
`repo-findfile.ts` and `RepoFileSearch.vue`
- Fix the resulting lint issues

`vue-eslint-parser` is added as a direct dependency because `import-x`
resolves parsers by package name, see
https://github.com/un-ts/eslint-plugin-import-x/issues/381.
This commit is contained in:
silverwind authored and GitHub committed 2026-10-03 00:47:13 +00:00
1 parent 986b0bcae0
commit 99573bde0e
12 files changed
+115 -112

No files matched your search

+24 -21
View File
@@ -2,18 +2,19 @@
import {ref, computed, watch, nextTick, useTemplateRef, onMounted, onUnmounted, type ShallowRef} from 'vue';
import {generateElemId} from '../utils/dom.ts';
import {GET} from '../modules/fetch.ts';
import {filterRepoFilesWeighted} from '../features/repo-findfile.ts';
import {pathEscapeSegments} from '../utils/url.ts';
import SvgIcon from './SvgIcon.vue';
import {throttle} from '../utils/func.ts';
import type {RepoFilesFilter} from '../features/repo-findfile.ts';
const props = defineProps({
repoLink: { type: String, required: true },
currentRefNameSubURL: { type: String, required: true },
treeListUrl: { type: String, required: true },
noResultsText: { type: String, required: true },
placeholder: { type: String, required: true },
});
const props = defineProps<{
repoLink: string;
currentRefNameSubURL: string;
treeListUrl: string;
noResultsText: string;
placeholder: string;
filterFiles: RepoFilesFilter;
}>();
const refElemInput = useTemplateRef('searchInput') as Readonly<ShallowRef<HTMLInputElement>>;
const refElemPopup = useTemplateRef('searchPopup') as Readonly<ShallowRef<HTMLDivElement>>;
@@ -28,7 +29,7 @@ const showPopup = computed(() => searchQuery.value.length > 0);
const filteredFiles = computed(() => {
if (!searchQuery.value) return [];
return filterRepoFilesWeighted(allFiles.value, searchQuery.value);
return props.filterFiles(allFiles.value, searchQuery.value);
});
const applySearchQuery = throttle(() => {
@@ -52,16 +53,17 @@ const handleKeyDown = (e: KeyboardEvent) => {
if (!searchQuery.value || filteredFiles.value.length === 0) return;
const handleSelectedItem = (idx: number) => {
e.preventDefault();
selectedIndex.value = idx;
const el = refElemPopup.value.querySelector(`.file-search-results > :nth-child(${idx+1} of .item)`);
el?.scrollIntoView({ block: 'nearest', behavior: 'instant' });
const el = refElemPopup.value.querySelector(`.file-search-results > :nth-child(${idx + 1} of .item)`);
el?.scrollIntoView({block: 'nearest', behavior: 'instant'});
};
if (e.key === 'ArrowDown') {
e.preventDefault();
handleSelectedItem(Math.min(selectedIndex.value + 1, filteredFiles.value.length - 1));
} else if (e.key === 'ArrowUp') {
handleSelectedItem(Math.max(selectedIndex.value - 1, 0))
e.preventDefault();
handleSelectedItem(Math.max(selectedIndex.value - 1, 0));
} else if (e.key === 'Enter') {
e.preventDefault();
const selectedFile = filteredFiles.value[selectedIndex.value];
@@ -71,11 +73,10 @@ const handleKeyDown = (e: KeyboardEvent) => {
}
};
const clearSearch = () => {
function clearSearch() {
searchQuery.value = '';
refElemInput.value.value = '';
};
}
const handleClickOutside = (e: MouseEvent) => {
if (!searchQuery.value) return;
@@ -85,7 +86,7 @@ const handleClickOutside = (e: MouseEvent) => {
if (!clickInside) clearSearch();
};
const loadFileListForSearch = async () => {
async function loadFileListForSearch() {
if (hasLoadedFileList.value || isLoadingFileList.value) return;
isLoadingFileList.value = true;
@@ -96,11 +97,11 @@ const loadFileListForSearch = async () => {
} finally {
isLoadingFileList.value = false;
}
};
}
function handleSearchResultClick(filePath: string) {
clearSearch();
window.location.href = `${props.repoLink}/src/${pathEscapeSegments(props.currentRefNameSubURL)}/${pathEscapeSegments(filePath)}`;
window.location.assign(`${props.repoLink}/src/${pathEscapeSegments(props.currentRefNameSubURL)}/${pathEscapeSegments(filePath)}`);
}
const updatePosition = () => {
@@ -121,17 +122,19 @@ const updatePosition = () => {
}
};
const resizeObserver = new ResizeObserver(updatePosition);
onMounted(() => {
const searchPopupId = generateElemId('file-search-popup-');
refElemPopup.value.setAttribute('id', searchPopupId);
refElemInput.value.setAttribute('aria-controls', searchPopupId);
document.addEventListener('click', handleClickOutside);
window.addEventListener('resize', updatePosition);
resizeObserver.observe(document.documentElement);
});
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside);
window.removeEventListener('resize', updatePosition);
resizeObserver.disconnect();
});
// Position search results below the input