Форматтера у фронтенда не было: стиль держался вручную и успел разъехаться в 50 файлах. Ставим Prettier с настройками под уже сложившийся стиль (без точек с запятой, одинарные кавычки, ширина 100 — подобрана замером: при 100 расходится меньше файлов, чем при 96 или 110) и прогоняем его по коду. `src/routeTree.gen.ts` исключён — его переписывает плагин роутера. Чтобы форматирование больше не расходилось незаметно, добавлены проверки в CI: `csharpier check` для бэкенда (его отсутствие и позволило накопиться 79 неотформатированным файлам) и `prettier --check` для фронтенда. Версии форматтеров прибиты точно, без кареток: минорка меняет вывод и красит CI на файлах, которых никто не трогал. `.editorconfig` задаёт редакторам те же отступы и LF ещё до форматтера; значения совпадают с настройками csharpier и Prettier намеренно — оба его читают. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
2.1 KiB
TypeScript
46 lines
2.1 KiB
TypeScript
/**
|
|
* Сопоставление файла с шоу по названию: имя релиза (напр. «The.Simpsons.S33E01.WEBDL…») содержит
|
|
* оригинальное (или отображаемое) название шоу. Нормализуем обе строки до слов через пробел и ищем
|
|
* название как цельную последовательность слов; при нескольких совпадениях берём самое длинное
|
|
* («Star Trek Discovery» важнее «Star Trek»).
|
|
*/
|
|
|
|
type ShowNameRef = { id: string; name: string; originalName?: string | null }
|
|
|
|
/** Приводит строку к «словам через пробел»: буквы/цифры сохраняем, всё прочее — разделитель. */
|
|
function normalize(value: string): string {
|
|
return value
|
|
.toLowerCase()
|
|
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
|
.trim()
|
|
.replace(/\s+/g, ' ')
|
|
}
|
|
|
|
/** Минимальная длина названия-кандидата (в символах после нормализации) — отсекает шум. */
|
|
const MIN_CANDIDATE_LENGTH = 2
|
|
|
|
/**
|
|
* Возвращает id наиболее подходящего шоу для имени файла или undefined, если совпадений нет.
|
|
* Совпадением считается вхождение названия шоу как цельной последовательности слов в имя файла.
|
|
*/
|
|
export function matchShowByName(
|
|
fileName: string,
|
|
shows: readonly ShowNameRef[],
|
|
): string | undefined {
|
|
const haystack = ` ${normalize(fileName)} `
|
|
let best: { id: string; length: number } | undefined
|
|
|
|
for (const show of shows) {
|
|
for (const candidate of [show.originalName, show.name]) {
|
|
if (!candidate) continue
|
|
const norm = normalize(candidate)
|
|
if (norm.length < MIN_CANDIDATE_LENGTH) continue
|
|
if (haystack.includes(` ${norm} `) && norm.length > (best?.length ?? 0)) {
|
|
best = { id: show.id, length: norm.length }
|
|
}
|
|
}
|
|
}
|
|
|
|
return best?.id
|
|
}
|