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
2026-10-03 02:47:13 +02:00
committed by GitHub
parent 986b0bcae0
commit 99573bde0e
12 changed files with 115 additions and 112 deletions
+12 -10
View File
@@ -17,6 +17,7 @@ import unescapedHtmlLiteral from './tools/eslint-rules/unescaped-html-literal.ts
const jsExts = ['js', 'mjs', 'cjs'] as const;
const tsExts = ['ts', 'mts', 'cts'] as const;
const vueExts = ['vue'] as const;
const restrictedGlobals = [
{name: 'localStorage', message: 'Use `modules/user-settings.ts` instead.'},
@@ -36,7 +37,7 @@ export default defineConfig([
'public/assets/js',
]),
{
files: [`**/*.{${[...jsExts, ...tsExts].join(',')}}`],
files: [`**/*.{${[...jsExts, ...tsExts, ...vueExts].join(',')}}`],
ignores: ['dist/*'],
languageOptions: {
ecmaVersion: 'latest',
@@ -48,7 +49,9 @@ export default defineConfig([
ecmaFeatures: {
impliedStrict: true,
},
parser: typescriptParser,
project: true,
extraFileExtensions: vueExts.map((ext) => `.${ext}`),
},
},
linterOptions: {
@@ -66,8 +69,11 @@ export default defineConfig([
wc,
},
settings: {
'import-x/extensions': [...jsExts, ...tsExts].map((ext) => `.${ext}`),
'import-x/parsers': {'@typescript-eslint/parser': [...jsExts, ...tsExts].map((ext) => `.${ext}`)},
'import-x/extensions': [...jsExts, ...tsExts, ...vueExts].map((ext) => `.${ext}`),
'import-x/parsers': {
'@typescript-eslint/parser': [...jsExts, ...tsExts].map((ext) => `.${ext}`),
'vue-eslint-parser': vueExts.map((ext) => `.${ext}`),
},
'import-x/resolver': {'typescript': true},
},
rules: {
@@ -958,7 +964,7 @@ export default defineConfig([
'unicorn/prefer-iterable-in-constructor': [2],
'unicorn/prefer-iterator-concat': [0], // too opinionated
'unicorn/prefer-iterator-helpers': [0],
'unicorn/prefer-iterator-to-array': [2],
'unicorn/prefer-iterator-to-array': [0], // Iterator#toArray requires ES2025
'unicorn/prefer-iterator-to-array-at-end': [2],
'unicorn/prefer-iterator-zip': [0],
'unicorn/prefer-json-import': [0],
@@ -1096,17 +1102,13 @@ export default defineConfig([
},
},
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: {
parser: '@typescript-eslint/parser',
},
},
files: vueExts.map((ext) => `**/*.${ext}`),
extends: [
vue.configs['flat/recommended'],
vueScopedCss.configs.recommended,
],
rules: {
'@typescript-eslint/no-redundant-type-constituents': [0], // types imported from .vue files resolve to any via typescript-eslint's *.vue shim
'vue/attributes-order': [0],
'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}],
'vue/max-attributes-per-line': [0],
+1
View File
@@ -109,6 +109,7 @@
"typescript": "6.0.3",
"typescript-eslint": "8.70.1",
"vitest": "5.0.1",
"vue-eslint-parser": "10.4.0",
"vue-tsc": "3.3.11"
}
}
+3
View File
@@ -467,6 +467,9 @@ importers:
vitest:
specifier: 5.0.1
version: 5.0.1(@types/node@26.6.2)(@vitest/browser-playwright@5.0.1)(@vitest/ui@5.0.1)(happy-dom@20.11.2)(jsdom@20.0.3(supports-color@10.2.2))(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
vue-eslint-parser:
specifier: 10.4.0
version: 10.4.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
vue-tsc:
specifier: 3.3.11
version: 3.3.11(typescript@6.0.3)
+18 -28
View File
@@ -13,18 +13,13 @@ import {toggleFullScreen} from '../utils.ts';
import {localUserSettings} from '../modules/user-settings.ts';
import type {ActionsArtifact, ActionsJob, ActionsRun, ActionsStatus} from '../modules/gitea-actions.ts';
import {AnsiLineRenderer} from '../render/ansi.ts';
import {
type ActionRunViewStore,
createLogLineMessage,
type LogLine,
type LogLineCommand,
parseLogLineCommand,
} from './ActionRunView.ts';
import {createLogLineMessage, parseLogLineCommand} from './ActionRunView.ts';
import type {ActionRunViewStore, LogLine, LogLineCommand} from './ActionRunView.ts';
function isLogElementInViewport(el: Element, {extraViewPortHeight}={extraViewPortHeight: 0}): boolean {
function isLogElementInViewport(el: Element, {extraViewPortHeight} = {extraViewPortHeight: 0}): boolean {
const rect = el.getBoundingClientRect();
// only check whether bottom is in viewport, because the log element can be a log group which is usually tall
return 0 <= rect.bottom && rect.bottom <= window.innerHeight + extraViewPortHeight;
return rect.bottom >= 0 && rect.bottom <= window.innerHeight + extraViewPortHeight;
}
export type ActionRunJobViewLocale = {
@@ -42,14 +37,14 @@ type Step = {
summary: string,
duration: string,
status: ActionsStatus,
}
};
type JobStepState = {
cursor: string|null,
cursor: string | null,
expanded: boolean,
manuallyCollapsed: boolean, // whether the user manually collapsed the step, used to avoid auto-expanding it again
firstLogTime?: number, // the step's first log line time, what "Show seconds" counts from
}
};
// one ANSI renderer per step, so an unterminated color carries between that step's lines only
const stepAnsiRenderers: AnsiLineRenderer[] = [];
@@ -59,7 +54,7 @@ type StepContainerElement = HTMLElement & {
// then the following batches of logs should still use the same group (active logs container).
// maybe it can be refactored to decouple from the HTML element in the future.
_stepLogsActiveContainer?: HTMLElement;
}
};
type LocaleStorageOptions = {
autoScroll: boolean;
@@ -176,8 +171,8 @@ onMounted(async () => {
}, 0);
});
intervalID = setInterval(() => void loadJob(), 1000);
void hashChangeListener();
intervalID = setInterval(() => loadJob(), 1000);
hashChangeListener();
window.addEventListener('hashchange', hashChangeListener);
});
@@ -256,7 +251,7 @@ async function copyStepOutput(event: MouseEvent, stepIndex: number) {
function toggleStepLogs(idx: number) {
currentJobStepsStates.value[idx].expanded = !currentJobStepsStates.value[idx].expanded;
if (currentJobStepsStates.value[idx].expanded) {
void loadJobForce(); // try to load the data immediately instead of waiting for next timer interval
loadJobForce(); // try to load the data immediately instead of waiting for next timer interval
} else if (currentJob.value.steps[idx].status === 'running') {
currentJobStepsStates.value[idx].manuallyCollapsed = true;
}
@@ -293,19 +288,14 @@ function shouldAutoScroll(stepIndex: number): boolean {
function appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) {
for (const line of logLines) {
const cmd = parseLogLineCommand(line);
switch (cmd?.name) {
case 'hidden':
continue;
case 'group':
beginLogGroup(stepIndex, startTime, line, cmd);
continue;
case 'endgroup':
endLogGroup(stepIndex);
continue;
if (cmd?.name === 'group') {
beginLogGroup(stepIndex, startTime, line, cmd);
} else if (cmd?.name === 'endgroup') {
endLogGroup(stepIndex);
} else if (cmd?.name !== 'hidden') {
// the active logs container may change during the loop, for example: entering and leaving a group
getActiveLogsContainer(stepIndex).append(createLogLine(stepIndex, startTime, line, cmd));
}
// the active logs container may change during the loop, for example: entering and leaving a group
const el = getActiveLogsContainer(stepIndex);
el.append(createLogLine(stepIndex, startTime, line, cmd));
}
}
+17 -16
View File
@@ -148,8 +148,8 @@ const activeIndex = shallowRef(-1); // don't select anything at load, first curs
const elSearch = useTemplateRef('elSearch') as Readonly<ShallowRef<HTMLInputElement>>;
const countsKey = computed(() => `${reposFilter.value}:${archivedFilter.value}:${privateFilter.value}`);
const showMoreReposLink = computed(() => repos.value.length > 0 && repos.value.length < repoTypeCount.value);
const repoTypeCount = computed(() => counts.value[countsKey.value]);
const showMoreReposLink = computed(() => repos.value.length > 0 && repos.value.length < repoTypeCount.value);
const checkboxArchivedFilterTitle = computed(() => textArchivedFilterTitles.get(archivedFilter.value));
const checkboxArchivedFilterProps = computed(() => ({checked: archivedFilter.value === 'archived', indeterminate: archivedFilter.value === 'both'}));
const checkboxPrivateFilterTitle = computed(() => textPrivateFilterTitles.get(privateFilter.value));
@@ -218,7 +218,7 @@ function updateHistory() {
if (page.value === 1) {
params.delete('repo-search-page');
} else {
params.set('repo-search-page', `${page.value}`);
params.set('repo-search-page', String(page.value));
}
const queryString = params.toString();
@@ -275,22 +275,23 @@ async function searchRepos() {
let response: Response, json: {data: WebSearchRepo[]};
try {
const firstLoad = reposTotalCount.value === null;
const loadTotalCount = async () => {
if (reposTotalCount.value) return;
const totalCountResponse = await GET(`${subUrl}/repo/search?count_only=1&uid=${uid}&team_id=${teamId}&q=&page=1&mode=`);
reposTotalCount.value = parseInt(totalCountResponse.headers.get('X-Total-Count') ?? '0');
if (firstLoad && reposTotalCount.value) {
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)) {
elSearch.value.focus({preventScroll: true});
}
});
}
};
// independent of the search, so both requests go out together
const totalCountSearchURL = `${subUrl}/repo/search?count_only=1&uid=${uid}&team_id=${teamId}&q=&page=1&mode=`;
const totalCountRequest = reposTotalCount.value ? null : GET(totalCountSearchURL);
const searchRequest = GET(searchedURL);
searchRequest.catch(() => {}); // awaited below, marked handled in case the count throws first
if (totalCountRequest) {
reposTotalCount.value = parseInt((await totalCountRequest).headers.get('X-Total-Count') ?? '0');
}
if (firstLoad && reposTotalCount.value) {
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)) {
elSearch.value.focus({preventScroll: true});
}
});
}
const [totalCountResult] = await Promise.allSettled([loadTotalCount(), searchRequest]); // a failed search must not show the empty state before the count is known
if (totalCountResult.status === 'rejected') throw totalCountResult.reason;
response = await searchRequest;
json = await response.json();
} catch {
+3 -3
View File
@@ -12,13 +12,13 @@ type Commit = {
committer_or_author_name: string,
time: string,
short_sha: string,
}
};
type CommitListResult = {
commits: Array<Commit>,
last_review_commit_sha: string,
locale: Record<string, string>,
}
};
const elRoot = useTemplateRef('elRoot') as Readonly<ShallowRef<HTMLDivElement>>;
const elExpandBtn = useTemplateRef('elExpandBtn') as Readonly<ShallowRef<HTMLButtonElement>>;
@@ -154,7 +154,7 @@ async function fetchCommits() {
commits.value.push(...results.commits);
commits.value.reverse();
lastReviewCommitSha.value = results.last_review_commit_sha || null;
if (lastReviewCommitSha.value && !commits.value.some((x) => x.id === lastReviewCommitSha.value)) {
if (lastReviewCommitSha.value && commits.value.every((x) => x.id !== lastReviewCommitSha.value)) {
// the lastReviewCommit is not available (probably due to a force push)
// reset the last review commit sha
lastReviewCommitSha.value = null;
+21 -21
View File
@@ -2,6 +2,7 @@
import {computed, nextTick, onBeforeUnmount, onMounted, shallowRef, useTemplateRef, watch, type ShallowRef} from 'vue';
import SvgIcon from './SvgIcon.vue';
import {showErrorToast} from '../modules/toast.ts';
import {errorMessage} from '../modules/errors.ts';
import {GET} from '../modules/fetch.ts';
import {pathEscapeSegments} from '../utils/url.ts';
import {queryElemChildren} from '../utils/dom.ts';
@@ -17,7 +18,7 @@ type ListItem = {
type SelectedTab = 'branches' | 'tags';
type TabLoadingStates = Record<SelectedTab, '' | 'loading' | 'done'>
type TabLoadingStates = Record<SelectedTab, '' | 'loading' | 'done'>;
const props = defineProps<{
elRoot: HTMLElement;
@@ -65,6 +66,14 @@ const enableFeed = props.elRoot.getAttribute('data-enable-feed') === 'true';
const searchFieldPlaceholder = computed(() => selectedTab.value === 'branches' ? textFilterBranch : textFilterTag);
const showCreateNewRef = computed(() => {
if (!allowCreateNewRef || !searchTerm.value) {
return false;
}
// FIXME: not quite right here, it mixes "branch" and "tag" names
return allItems.value.every((item: ListItem) => item.refShortName !== searchTerm.value);
});
const filteredItems = computed<ListItem[]>(() => {
const searchTermLower = searchTerm.value.toLowerCase();
const items = allItems.value.filter((item: ListItem) => {
@@ -74,8 +83,7 @@ const filteredItems = computed<ListItem[]>(() => {
return item.refShortName.toLowerCase().includes(searchTermLower);
});
// TODO: fix this anti-pattern: side-effects-in-computed-properties
activeItemIndex.value = !items.length && showCreateNewRef.value ? 0 : -1; // eslint-disable-line vue/no-side-effects-in-computed-properties
activeItemIndex.value = !items.length && showCreateNewRef.value ? 0 : -1; // eslint-disable-line vue/no-side-effects-in-computed-properties -- TODO: move this side effect out of the computed
return items;
});
@@ -84,14 +92,6 @@ const showNoResults = computed(() => {
return !filteredItems.value.length && !showCreateNewRef.value;
});
const showCreateNewRef = computed(() => {
if (!allowCreateNewRef || !searchTerm.value) {
return false;
}
// FIXME: not quite right here, it mixes "branch" and "tag" names
return !allItems.value.some((item: ListItem) => item.refShortName === searchTerm.value);
});
const createNewRefFormActionUrl = computed(() => {
return `${currentRepoLink}/branches/_new/${currentRefType.value}/${pathEscapeSegments(currentRefShortName.value)}`;
});
@@ -127,15 +127,15 @@ function selectItem(item: ListItem) {
currentRefType.value = item.refType;
currentRefShortName.value = item.refShortName;
elDropdown.value.closest('form')!.action = refFormActionTemplate
.replace('{RepoLink}', currentRepoLink)
.replace('{RefType}', pathEscapeSegments(item.refType))
.replace('{RefShortName}', pathEscapeSegments(item.refShortName));
.replace('{RepoLink}', () => currentRepoLink)
.replace('{RefType}', () => pathEscapeSegments(item.refType))
.replace('{RefShortName}', () => pathEscapeSegments(item.refShortName));
} else {
window.location.href = refLinkTemplate
.replace('{RepoLink}', currentRepoLink)
.replace('{RefType}', pathEscapeSegments(item.refType))
.replace('{RefShortName}', pathEscapeSegments(item.refShortName))
.replace('{TreePath}', pathEscapeSegments(currentTreePath));
window.location.assign(refLinkTemplate
.replace('{RepoLink}', () => currentRepoLink)
.replace('{RefType}', () => pathEscapeSegments(item.refType))
.replace('{RefShortName}', () => pathEscapeSegments(item.refShortName))
.replace('{TreePath}', () => pathEscapeSegments(currentTreePath)));
}
}
@@ -174,7 +174,7 @@ function keydown(e: KeyboardEvent) {
return;
}
activeItemIndex.value = nextIndex;
getActiveItem()!.scrollIntoView({block: 'nearest'});
getActiveItem().scrollIntoView({block: 'nearest'});
} else if (e.key === 'Enter') {
e.preventDefault();
getActiveItem()?.click();
@@ -208,7 +208,7 @@ async function loadTabItems() {
tabLoadingStates.value = {...tabLoadingStates.value, [tab]: 'done'};
} catch (e) {
tabLoadingStates.value = {...tabLoadingStates.value, [tab]: ''};
showErrorToast(`Network error when fetching items for ${tab}, error: ${e}`);
showErrorToast(`Network error when fetching items for ${tab}, error: ${errorMessage(e)}`);
console.error(e);
}
}
+8 -8
View File
@@ -46,13 +46,13 @@ const customEventListener: Plugin = {
};
type LineOptions = ChartOptions<'line'> & {
plugins?: {
customEventListener?: {
chartType: ChartType;
onDoubleClick: (args: {chart: Chart}, reset: boolean) => void;
};
};
}
plugins?: {
customEventListener?: {
chartType: ChartType;
onDoubleClick: (args: {chart: Chart}, reset: boolean) => void;
};
};
};
Chart.register(
Title,
@@ -157,7 +157,7 @@ async function fetchGraphData() {
const data: ContributorsData = await response.json();
const {total, ...other} = data;
// below line might be deleted if we are sure go produces map always sorted by keys
const totalWeeks = Object.fromEntries(Object.entries(total.weeks).sort());
const totalWeeks = Object.fromEntries(Object.entries(total.weeks).sort(([a], [b]) => Number(a) - Number(b)));
const weekValues = Object.values(totalWeeks);
xAxisStart = weekValues[0].week;
+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
+1 -1
View File
@@ -3,7 +3,7 @@ import ViewFileTreeItem from './ViewFileTreeItem.vue';
import {onMounted, useTemplateRef, type ShallowRef} from 'vue';
import {createViewFileTreeStore} from './ViewFileTreeStore.ts';
const elRoot = useTemplateRef('elRoot') as Readonly<ShallowRef<HTMLDivElement>>;;
const elRoot = useTemplateRef('elRoot') as Readonly<ShallowRef<HTMLDivElement>>;
const props = defineProps({
repoLink: {type: String, required: true},
+4 -4
View File
@@ -30,12 +30,12 @@ export type WorkflowGraphLocale = {
graphResetView: string,
};
interface StoredState {
type StoredState = {
scale: number;
translateX: number;
translateY: number;
timestamp: number;
}
};
const props = defineProps<{
store: ActionRunViewStore;
@@ -233,14 +233,14 @@ function isJobLinked(job: ActionsJob) {
}
function onNodeClick(job: GraphNode | ActionsJob, event: MouseEvent) {
const target = 'jobs' in job ? job.jobs[0]! : job;
const target = 'jobs' in job ? job.jobs[0] : job;
if (!isJobLinked(target)) return;
const link = `${props.runLink}/jobs/${target.id}`;
if (event.ctrlKey || event.metaKey) {
window.open(link, '_blank');
return;
}
window.location.href = link;
window.location.assign(link);
}
</script>
+3
View File
@@ -67,6 +67,8 @@ export function filterRepoFilesWeighted(files: Array<string>, filter: string) {
return filterResult;
}
export type RepoFilesFilter = typeof filterRepoFilesWeighted;
export function initRepoFileSearch() {
registerGlobalInitFunc('initRepoFileSearch', async (el) => {
const {default: RepoFileSearch} = await import('../components/RepoFileSearch.vue');
@@ -76,6 +78,7 @@ export function initRepoFileSearch() {
treeListUrl: el.getAttribute('data-tree-list-url'),
noResultsText: el.getAttribute('data-no-results-text'),
placeholder: el.getAttribute('data-placeholder'),
filterFiles: filterRepoFilesWeighted,
}).mount(el);
});
}