Roadmap to becoming a developer in 2022
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
4.9 KiB

import type { MarkdownFileType } from './file';
import slugify from 'slugify';
import { getAllAuthors, type AuthorFileType } from './author.ts';
import { getAllGuides } from './guide.ts';
interface RawQuestionGroupFrontmatter {
order: number;
briefTitle: string;
briefDescription: string;
title: string;
description: string;
isNew: boolean;
authorId?: string;
date?: string;
seo: {
title: string;
description: string;
ogImageUrl?: string;
keywords: string[];
};
relatedTitle?: string;
relatedGuidesId?: string;
sitemap: {
priority: number;
changefreq: string;
};
questions: {
question: string;
answer: string;
topics: string[];
}[];
}
type RawQuestionGroupFileType =
MarkdownFileType<RawQuestionGroupFrontmatter> & {
id: string;
};
export type QuestionType = {
id: string;
question: string;
answer: string;
isLongAnswer: boolean;
topics?: string[];
};
export type QuestionGroupType = RawQuestionGroupFileType & {
questions: QuestionType[];
allTopics: string[];
author?: AuthorFileType;
relatedGuides?: Record<string, string>;
};
/**
* Gets all the best practice files
*
* @returns Promisified BestPracticeFileType[]
*/
export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> {
const questionGroupFilesMap = import.meta.glob<RawQuestionGroupFileType>(
`/src/data/question-groups/*/*.md`,
{
eager: true,
},
);
const answerFilesMap = import.meta.glob<string>(
// get the files inside /src/data/question-groups/[ignore]/content/*.md
`/src/data/question-groups/*/content/*.md`,
{
eager: true,
12 months ago
query: '?raw',
},
);
const allAuthors = await getAllAuthors();
const allGuides = await getAllGuides();
return Object.values(questionGroupFilesMap)
.map((questionGroupFile) => {
const fileParts = questionGroupFile?.file?.split('/');
const [questionGroupDir, questionGroupFileName] = fileParts?.slice(-2);
const questionGroupFileId = questionGroupFileName?.replace('.md', '');
const formattedAnswers: QuestionType[] =
questionGroupFile.frontmatter.questions.map((qa) => {
const questionText = qa.question;
let answerText = qa.answer;
let isLongAnswer = false;
if (answerText.endsWith('.md')) {
const answerFilePath = `/src/data/question-groups/${questionGroupDir}/content/${answerText}`;
answerText =
(answerFilesMap[answerFilePath] as any)?.default ||
answerFilesMap[answerFilePath] ||
`File missing: ${answerFilePath}`;
isLongAnswer = true;
}
return {
id: slugify(questionText, { lower: true }),
question: questionText,
answer: answerText,
topics: qa.topics,
isLongAnswer,
};
});
const uniqueTopics = formattedAnswers
.flatMap((answer) => answer.topics || [])
.filter((topic) => topic)
.reduce((acc, topic) => {
if (!acc.includes(topic)) {
acc.push(topic);
}
return acc;
}, [] as string[]);
const relatedGuides = questionGroupFile.frontmatter.relatedGuidesId
? allGuides
.filter(
(guide) =>
guide.id === questionGroupFile.frontmatter.relatedGuidesId,
)
.reduce(
(acc, guide) => {
acc[guide.frontmatter.title] = `/guides/${guide.id}`;
return acc;
},
{} as Record<string, string>,
)
: undefined;
return {
...questionGroupFile,
id: questionGroupFileId,
questions: formattedAnswers,
allTopics: uniqueTopics,
author: allAuthors.find(
(author) => author.id === questionGroupFile.frontmatter.authorId,
)!,
relatedGuides,
};
})
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
}
feat: profile pages, custom roadmap pages and SSR (#5494) * Update * Add stats and health endpoints * Add pre-render * fix: redirect to the error page * Fix generate-renderer issue * Rename * Fix best practice topics not loading * Handle SSR for static pages * Refactor faqs * Refactor best practices * Fix absolute import * Fix stats * Add custom roadmap page * Minor UI change * feat: custom roadmap slug routes (#4987) * feat: replace roadmap slug * fix: remove roadmap slug * feat: username route * fix: user public page * feat: show roadmap progress * feat: update public profile * fix: replace with toast * feat: user public profile page * feat: implement profile form * feat: implement user profile roadmap page * refactor: remove logs * fix: increase progress gap * fix: remove title margin * fix: breakpoint for roadmaps * Update dependencies * Upgrade dependencies * fix: improper avatars * fix: heatmap focus * wip: remove `getStaticPaths` * fix: add disable props * wip * feat: add email icon * fix: update pnpm lock * fix: implement author page * Fix beginner roadmaps not working * Changes to form * Refactor profile and form * Refactor public profile form * Rearrange sidebar items * Update UI for public form * Minor text update * Refactor public profile form * Error page for user * Revamp UI for profile page * Add public profile page * Fix vite warnings * Add private profile banner * feat: on blur check username * Update fetch depth * Add error detail * Use hybrid mode of rendering * Do not pre-render stats pages * Update deployment workflow * Update deployment workflow --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com>
10 months ago
export async function getQuestionGroupById(id: string) {
const questionGroups = await getAllQuestionGroups();
return questionGroups.find((group) => group.id === id);
}
export async function getQuestionGroupsByIds(
ids: string[],
): Promise<{ id: string; title: string; description: string }[]> {
if (!ids?.length) {
return [];
}
const questionGroupFilesMap = import.meta.glob<
MarkdownFileType<RawQuestionGroupFrontmatter>
>(`/src/data/question-groups/*/*.md`, {
eager: true,
});
return Object.values(questionGroupFilesMap)
.map((group) => {
const fileId = group?.file?.split('/')?.pop()?.replace('.md', '');
const frontmatter = group.frontmatter;
return {
id: fileId!,
title: frontmatter.briefTitle,
description: `${frontmatter.questions.length} Questions`,
};
})
.filter((group) => {
return ids.includes(group.id);
});
}