import path from 'node:path' import fs from 'fs-extra' import fg from 'fast-glob' import YAML from 'js-yaml' import type { Quiz, QuizMetaInfo } from './types' import { defaultLocale, supportedLocales } from './locales' export async function loadFile(filepath: string) { if (fs.existsSync(filepath)) return await fs.readFile(filepath, 'utf-8') return undefined } export async function loadLocaleVariations( filepath: string, preprocessor: (s: string) => T = s => s as any as T, ) { const { ext, dir, name } = path.parse(filepath) const data: Record = {} for (const locale of supportedLocales) { const file = preprocessor(await loadFile(path.join(dir, `${name}.${locale}${ext}`)) || '') if (file) data[locale] = file } if (!data[defaultLocale]) { // default version const file = preprocessor(await loadFile(filepath) || '') if (file) data[defaultLocale] = file } return data } export function readmeCleanUp(text: string) { return text .replace(/[\s\S]*/, '') .replace(/[\s\S]*/, '') .trim() } export function loadInfo(s: string): Partial | undefined { const object = YAML.load(s) as any if (!object) return undefined const arrayKeys = ['tags', 'related'] for (const key of arrayKeys) { if (object[key]) { object[key] = (object[key] || '') .toString() .split(',') .map((i: string) => i.trim()) .filter(Boolean) } else { object[key] = undefined } } return object } export const QUIZ_ROOT = path.resolve(__dirname, '../questions') export async function loadQuizzes(): Promise { const folders = await fg('{0..9}*-*', { onlyDirectories: true, cwd: QUIZ_ROOT, }) const quizzes = await Promise.all( folders.map(async dir => loadQuiz(dir)), ) return quizzes } export async function loadQuiz(dir: string): Promise { return { no: Number(dir.replace(/^(\d+)-.*/, '$1')), difficulty: dir.replace(/^\d+-(.+?)-.*$/, '$1') as any, path: dir, info: await loadLocaleVariations(path.join(QUIZ_ROOT, dir, 'info.yml'), loadInfo), readme: await loadLocaleVariations(path.join(QUIZ_ROOT, dir, 'README.md'), readmeCleanUp), template: await loadFile(path.join(QUIZ_ROOT, dir, 'template.ts')) || '', tests: await loadFile(path.join(QUIZ_ROOT, dir, 'test-cases.ts')), } } export async function loadQuizByNo(no: number | string) { const folders = await fg(`${no}-*`, { onlyDirectories: true, cwd: QUIZ_ROOT, }) if (folders.length) return await loadQuiz(folders[0]) return undefined } export function resolveInfo(quiz: Quiz, locale: string = defaultLocale) { const info = { ...quiz.info[defaultLocale], ...quiz.info[locale] } info.tags = quiz.info[locale]?.tags || quiz.info[defaultLocale]?.tags || [] info.related = quiz.info[locale]?.related || quiz.info[defaultLocale]?.related || [] if (typeof info.tags === 'string') // @ts-expect-error info.tags = info.tags.split(',').map(i => i.trim()).filter(Boolean) return info as QuizMetaInfo }