scripts
Folders and files
Name | Name | Last commit date | ||
---|---|---|---|---|
parent directory.. | ||||
import path from 'path' import fs from 'fs-extra' import { defaultLocale, f, SupportedLocale, supportedLocales, t } from './locales' import { loadQuizes, resolveInfo } from './loader' import { toAnswerShort, toNearborREADME, toPlayShort, toQuizREADME, toSolutionsShort } from './toUrl' import { Quiz, QuizMetaInfo } from './types' const DifficultyColors: Record<string, string> = { warm: 'teal', easy: '7aad0c', medium: 'd9901a', hard: 'de3d37', extreme: 'b11b8d', } const DifficultyRank = [ 'warm', 'easy', 'medium', 'hard', 'extreme', ] function escapeHtml(unsafe: string) { return unsafe .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } function toBadgeURL(label: string, text: string, color: string, args = '') { return `https://img.shields.io/badge/${encodeURIComponent(label.replace(/-/g, '--'))}-${encodeURIComponent(text.replace(/-/g, '--'))}-${color}${args}` } function toBadge(label: string, text: string, color: string, args = '') { return `<img src="${toBadgeURL(label, text, color, args)}" alt="${text}"/>` } export function toBadgeLink(url: string, label: string, text: string, color: string, args = '') { return `<a href="${url}" target="_blank">${toBadge(label, text, color, args)}</a> ` } function toAuthorInfo(author: Partial<QuizMetaInfo['author']> = {}) { return `by ${author.name}${author.github ? ` <a href="https://github.com/${author.github}" target="_blank">@${author.github}</a>` : ''}` } function toDifficultyBadge(difficulty: string, locale: SupportedLocale) { return toBadge('', t(locale, `difficulty.${difficulty}`), DifficultyColors[difficulty]) } function toDifficultyBadgeInverted(difficulty: string, locale: SupportedLocale, count: number) { return toBadge(t(locale, `difficulty.${difficulty}`), count.toString(), DifficultyColors[difficulty]) } function quizToBadge(quiz: Quiz, locale: string, absolute = false) { return toBadgeLink( toQuizREADME(quiz, locale, absolute), '', `${quiz.no}・${quiz.info[locale]?.title || quiz.info[defaultLocale]?.title}`, DifficultyColors[quiz.difficulty], ) } function quizNoToBadges(ids: (string|number)[], quizes: Quiz[], locale: string, absolute = false) { return ids .map(i => quizes.find(q => q.no === Number(i))) .filter(Boolean) .map(i => quizToBadge(i!, locale, absolute)) .join(' ') } function getAllTags(quizes: Quiz[], locale: string) { const set = new Set<string>() for (const quiz of quizes) { const info = resolveInfo(quiz, locale) for (const tag of (info?.tags || [])) set.add(tag as string) } return Array.from(set).sort() } function getQuizesByTag(quizes: Quiz[], locale: string, tag: string) { return quizes.filter((quiz) => { const info = resolveInfo(quiz, locale) return !!info.tags?.includes(tag) }) } async function insertInfoReadme(filepath: string, quiz: Quiz, locale: SupportedLocale, quizes: Quiz[]) { if (!fs.existsSync(filepath)) return let text = await fs.readFile(filepath, 'utf-8') /* eslint-disable prefer-template */ if (!text.match(/<!--info-header-start-->[\s\S]*<!--info-header-end-->/)) text = `<!--info-header-start--><!--info-header-end-->\n\n${text}` if (!text.match(/<!--info-footer-start-->[\s\S]*<!--info-footer-end-->/)) text = `${text}\n\n<!--info-footer-start--><!--info-footer-end-->` const info = resolveInfo(quiz, locale) const availableLocales = supportedLocales.filter(l => l !== locale).filter(l => !!quiz.readme[l]) text = text .replace( /<!--info-header-start-->[\s\S]*<!--info-header-end-->/, '<!--info-header-start-->' + `<h1>${escapeHtml(info.title || '')} ${toDifficultyBadge(quiz.difficulty, locale)} ${(info.tags || []).map(i => toBadge('', `#${i}`, '999')).join(' ')}</h1>` + `<blockquote><p>${toAuthorInfo(info.author)}</p></blockquote>` + '<p>' + toBadgeLink(toPlayShort(quiz.no, locale), '', t(locale, 'badge.take-the-challenge'), '3178c6', '?logo=typescript&logoColor=white') + (availableLocales.length ? (' ' + availableLocales.map(l => toBadgeLink(toNearborREADME(quiz, l), '', t(l, 'display'), 'gray')).join(' ')) : '') + '</p>' + '<!--info-header-end-->', ) .replace( /<!--info-footer-start-->[\s\S]*<!--info-footer-end-->/, '<!--info-footer-start--><br>' + toBadgeLink(`../../${f('README', locale, 'md')}`, '', t(locale, 'badge.back'), 'grey') + toBadgeLink(toAnswerShort(quiz.no, locale), '', t(locale, 'badge.share-your-solutions'), 'teal') + toBadgeLink(toSolutionsShort(quiz.no), '', t(locale, 'badge.checkout-solutions'), 'de5a77', '?logo=awesome-lists&logoColor=white') + (Array.isArray(info.related) && info.related.length ? `<hr><h3>${t(locale, 'readme.related-challenges')}</h3>${quizNoToBadges(info.related, quizes, locale, true)}` : '') + '<!--info-footer-end-->', ) /* eslint-enable prefer-template */ await fs.writeFile(filepath, text, 'utf-8') } async function updateIndexREADME(quizes: Quiz[]) { // update index README for (const locale of supportedLocales) { const filepath = path.resolve(__dirname, '..', f('README', locale, 'md')) let challengesREADME = '' let prev = '' // Difficulty const quizesByDifficulty = [...quizes].sort((a, b) => DifficultyRank.indexOf(a.difficulty) - DifficultyRank.indexOf(b.difficulty)) for (const quiz of quizesByDifficulty) { if (prev !== quiz.difficulty) challengesREADME += `${prev ? '<br><br>' : ''}${toDifficultyBadgeInverted(quiz.difficulty, locale, quizesByDifficulty.filter(q => q.difficulty === quiz.difficulty).length)}<br>` challengesREADME += quizToBadge(quiz, locale) prev = quiz.difficulty } challengesREADME += '<br><details><summary>By Tags</summary><br><table><tbody>' const tags = getAllTags(quizes, locale) for (const tag of tags) { challengesREADME += `<tr><td>${toBadge('', `#${tag}`, '999')}</td><td>` getQuizesByTag(quizesByDifficulty, locale, tag) .forEach((quiz) => { challengesREADME += quizToBadge(quiz, locale) }) challengesREADME += '</td></tr>' } challengesREADME += '<tr><td><code> </code></td><td></td></tr>' challengesREADME += '</tbody></table></details>' let readme = await fs.readFile(filepath, 'utf-8') readme = readme.replace( /<!--challenges-start-->[\s\S]*<!--challenges-end-->/m, `<!--challenges-start-->\n${challengesREADME}\n<!--challenges-end-->`, ) await fs.writeFile(filepath, readme, 'utf-8') } } async function updateQuestionsREADME(quizes: Quiz[]) { const questionsDir = path.resolve(__dirname, '../questions') // update each questions' readme for (const quiz of quizes) { for (const locale of supportedLocales) { await insertInfoReadme( path.join( questionsDir, quiz.path, f('README', locale, 'md'), ), quiz, locale, quizes, ) } } } export async function updateREADMEs(type?: 'quiz' | 'index') { const quizes = await loadQuizes() quizes.sort((a, b) => a.no - b.no) if (type === 'quiz') { await updateQuestionsREADME(quizes) } else if (type === 'index') { await updateIndexREADME(quizes) } else { await Promise.all([ updateIndexREADME(quizes), updateQuestionsREADME(quizes), ]) } } updateREADMEs(process.argv.slice(2)[0] as any)