diff --git a/src/components/VideoHeader.astro b/src/components/VideoHeader.astro
index 1d3717414..321908983 100644
--- a/src/components/VideoHeader.astro
+++ b/src/components/VideoHeader.astro
@@ -1,6 +1,5 @@
---
import type { VideoFileType } from '../lib/video';
-import YouTubeAlert from './YouTubeAlert.astro';
export interface Props {
video: VideoFileType;
@@ -16,15 +15,15 @@ const { frontmatter, author } = video;
class='hidden items-center justify-start text-gray-400 sm:flex sm:justify-center'
>
- {author.frontmatter.name}
+ {author.data.name}
·
Illustrated Video
diff --git a/src/content/config.ts b/src/content/config.ts
index d5ba9159d..3f4e01b12 100644
--- a/src/content/config.ts
+++ b/src/content/config.ts
@@ -1,9 +1,11 @@
import { authorCollection } from './author';
import { guideCollection } from './guide';
+import { projectCollection } from './project';
import { questionGroupCollection } from './question-group';
export const collections = {
authors: authorCollection,
guides: guideCollection,
'question-groups': questionGroupCollection,
+ projects: projectCollection,
};
diff --git a/src/content/project.ts b/src/content/project.ts
new file mode 100644
index 000000000..08c368571
--- /dev/null
+++ b/src/content/project.ts
@@ -0,0 +1,28 @@
+import { defineCollection, z } from 'astro:content';
+
+export const projectDifficulties = [
+ 'beginner',
+ 'intermediate',
+ 'advanced',
+] as const;
+export type ProjectDifficultyType = (typeof projectDifficulties)[number];
+
+export const projectCollection = defineCollection({
+ type: 'content',
+ schema: z.object({
+ title: z.string(),
+ description: z.string(),
+ isNew: z.boolean(),
+ sort: z.number(),
+ difficulty: z.enum(projectDifficulties),
+ nature: z.string(),
+ skills: z.array(z.string()),
+ seo: z.object({
+ title: z.string(),
+ description: z.string(),
+ keywords: z.array(z.string()),
+ ogImageUrl: z.string().optional(),
+ }),
+ roadmapIds: z.array(z.string()),
+ }),
+});
diff --git a/src/data/projects/accessible-form-ui.md b/src/content/projects/accessible-form-ui.md
similarity index 100%
rename from src/data/projects/accessible-form-ui.md
rename to src/content/projects/accessible-form-ui.md
diff --git a/src/data/projects/accordion.md b/src/content/projects/accordion.md
similarity index 100%
rename from src/data/projects/accordion.md
rename to src/content/projects/accordion.md
diff --git a/src/data/projects/basic-dockerfile.md b/src/content/projects/basic-dockerfile.md
similarity index 100%
rename from src/data/projects/basic-dockerfile.md
rename to src/content/projects/basic-dockerfile.md
diff --git a/src/data/projects/basic-html-website.md b/src/content/projects/basic-html-website.md
similarity index 100%
rename from src/data/projects/basic-html-website.md
rename to src/content/projects/basic-html-website.md
diff --git a/src/data/projects/blogging-platform-api.md b/src/content/projects/blogging-platform-api.md
similarity index 100%
rename from src/data/projects/blogging-platform-api.md
rename to src/content/projects/blogging-platform-api.md
diff --git a/src/data/projects/broadcast-server.md b/src/content/projects/broadcast-server.md
similarity index 100%
rename from src/data/projects/broadcast-server.md
rename to src/content/projects/broadcast-server.md
diff --git a/src/data/projects/caching-server.md b/src/content/projects/caching-server.md
similarity index 100%
rename from src/data/projects/caching-server.md
rename to src/content/projects/caching-server.md
diff --git a/src/data/projects/changelog-component.md b/src/content/projects/changelog-component.md
similarity index 100%
rename from src/data/projects/changelog-component.md
rename to src/content/projects/changelog-component.md
diff --git a/src/data/projects/cookie-consent.md b/src/content/projects/cookie-consent.md
similarity index 100%
rename from src/data/projects/cookie-consent.md
rename to src/content/projects/cookie-consent.md
diff --git a/src/data/projects/custom-dropdown.md b/src/content/projects/custom-dropdown.md
similarity index 100%
rename from src/data/projects/custom-dropdown.md
rename to src/content/projects/custom-dropdown.md
diff --git a/src/data/projects/database-backup-utility.md b/src/content/projects/database-backup-utility.md
similarity index 100%
rename from src/data/projects/database-backup-utility.md
rename to src/content/projects/database-backup-utility.md
diff --git a/src/data/projects/datepicker-ui.md b/src/content/projects/datepicker-ui.md
similarity index 100%
rename from src/data/projects/datepicker-ui.md
rename to src/content/projects/datepicker-ui.md
diff --git a/src/data/projects/ecommerce-api.md b/src/content/projects/ecommerce-api.md
similarity index 100%
rename from src/data/projects/ecommerce-api.md
rename to src/content/projects/ecommerce-api.md
diff --git a/src/data/projects/expense-tracker-api.md b/src/content/projects/expense-tracker-api.md
similarity index 100%
rename from src/data/projects/expense-tracker-api.md
rename to src/content/projects/expense-tracker-api.md
diff --git a/src/data/projects/expense-tracker.md b/src/content/projects/expense-tracker.md
similarity index 100%
rename from src/data/projects/expense-tracker.md
rename to src/content/projects/expense-tracker.md
diff --git a/src/data/projects/fitness-workout-tracker.md b/src/content/projects/fitness-workout-tracker.md
similarity index 100%
rename from src/data/projects/fitness-workout-tracker.md
rename to src/content/projects/fitness-workout-tracker.md
diff --git a/src/data/projects/github-random-repo.md b/src/content/projects/github-random-repo.md
similarity index 100%
rename from src/data/projects/github-random-repo.md
rename to src/content/projects/github-random-repo.md
diff --git a/src/data/projects/github-user-activity.md b/src/content/projects/github-user-activity.md
similarity index 100%
rename from src/data/projects/github-user-activity.md
rename to src/content/projects/github-user-activity.md
diff --git a/src/data/projects/image-grid.md b/src/content/projects/image-grid.md
similarity index 100%
rename from src/data/projects/image-grid.md
rename to src/content/projects/image-grid.md
diff --git a/src/data/projects/image-processing-service.md b/src/content/projects/image-processing-service.md
similarity index 100%
rename from src/data/projects/image-processing-service.md
rename to src/content/projects/image-processing-service.md
diff --git a/src/data/projects/log-archive-tool.md b/src/content/projects/log-archive-tool.md
similarity index 100%
rename from src/data/projects/log-archive-tool.md
rename to src/content/projects/log-archive-tool.md
diff --git a/src/data/projects/markdown-note-taking-app.md b/src/content/projects/markdown-note-taking-app.md
similarity index 100%
rename from src/data/projects/markdown-note-taking-app.md
rename to src/content/projects/markdown-note-taking-app.md
diff --git a/src/data/projects/movie-reservation-system.md b/src/content/projects/movie-reservation-system.md
similarity index 100%
rename from src/data/projects/movie-reservation-system.md
rename to src/content/projects/movie-reservation-system.md
diff --git a/src/data/projects/number-guessing-game.md b/src/content/projects/number-guessing-game.md
similarity index 100%
rename from src/data/projects/number-guessing-game.md
rename to src/content/projects/number-guessing-game.md
diff --git a/src/data/projects/personal-blog.md b/src/content/projects/personal-blog.md
similarity index 100%
rename from src/data/projects/personal-blog.md
rename to src/content/projects/personal-blog.md
diff --git a/src/data/projects/portfolio-website.md b/src/content/projects/portfolio-website.md
similarity index 100%
rename from src/data/projects/portfolio-website.md
rename to src/content/projects/portfolio-website.md
diff --git a/src/data/projects/realtime-leaderboard-system.md b/src/content/projects/realtime-leaderboard-system.md
similarity index 100%
rename from src/data/projects/realtime-leaderboard-system.md
rename to src/content/projects/realtime-leaderboard-system.md
diff --git a/src/data/projects/reddit-client.md b/src/content/projects/reddit-client.md
similarity index 100%
rename from src/data/projects/reddit-client.md
rename to src/content/projects/reddit-client.md
diff --git a/src/data/projects/restricted-textarea.md b/src/content/projects/restricted-textarea.md
similarity index 100%
rename from src/data/projects/restricted-textarea.md
rename to src/content/projects/restricted-textarea.md
diff --git a/src/data/projects/scalable-ecommerce-platform.md b/src/content/projects/scalable-ecommerce-platform.md
similarity index 100%
rename from src/data/projects/scalable-ecommerce-platform.md
rename to src/content/projects/scalable-ecommerce-platform.md
diff --git a/src/data/projects/simple-tabs.md b/src/content/projects/simple-tabs.md
similarity index 100%
rename from src/data/projects/simple-tabs.md
rename to src/content/projects/simple-tabs.md
diff --git a/src/data/projects/single-page-cv.md b/src/content/projects/single-page-cv.md
similarity index 100%
rename from src/data/projects/single-page-cv.md
rename to src/content/projects/single-page-cv.md
diff --git a/src/data/projects/task-tracker-js.md b/src/content/projects/task-tracker-js.md
similarity index 100%
rename from src/data/projects/task-tracker-js.md
rename to src/content/projects/task-tracker-js.md
diff --git a/src/data/projects/task-tracker.md b/src/content/projects/task-tracker.md
similarity index 100%
rename from src/data/projects/task-tracker.md
rename to src/content/projects/task-tracker.md
diff --git a/src/data/projects/temperature-converter.md b/src/content/projects/temperature-converter.md
similarity index 82%
rename from src/data/projects/temperature-converter.md
rename to src/content/projects/temperature-converter.md
index c142baac7..9a43312b4 100644
--- a/src/data/projects/temperature-converter.md
+++ b/src/content/projects/temperature-converter.md
@@ -11,14 +11,14 @@ skills:
- JavaScript
- DOM Manipulation
seo:
- - title: Build a Temperature Converter with JavaScript
- - description: Learn how to create an interactive temperature converter that converts between Celsius, Fahrenheit, and Kelvin using JavaScript.
- - keywords:
+ title: Build a Temperature Converter with JavaScript
+ description: Learn how to create an interactive temperature converter that converts between Celsius, Fahrenheit, and Kelvin using JavaScript.
+ keywords:
- 'temperature converter'
- 'javascript project'
- 'unit conversion'
- 'html and css'
-roadmapIds:
+roadmapIds:
- 'frontend'
---
@@ -30,4 +30,4 @@ Here is a mockup of what the temperature converter might look like:
[![Temperature Converter](https://assets.roadmap.sh/guest/temperature-converter-8omel.png)](https://assets.roadmap.sh/guest/temperature-converter-8omel.png)
-This project will help you gain experience with handling user input, conditionally enabling form elements, and performing simple calculations using JavaScript.
\ No newline at end of file
+This project will help you gain experience with handling user input, conditionally enabling form elements, and performing simple calculations using JavaScript.
diff --git a/src/data/projects/testimonial-cards.md b/src/content/projects/testimonial-cards.md
similarity index 100%
rename from src/data/projects/testimonial-cards.md
rename to src/content/projects/testimonial-cards.md
diff --git a/src/data/projects/todo-list-api.md b/src/content/projects/todo-list-api.md
similarity index 100%
rename from src/data/projects/todo-list-api.md
rename to src/content/projects/todo-list-api.md
diff --git a/src/data/projects/tooltip-ui.md b/src/content/projects/tooltip-ui.md
similarity index 100%
rename from src/data/projects/tooltip-ui.md
rename to src/content/projects/tooltip-ui.md
diff --git a/src/data/projects/unit-converter.md b/src/content/projects/unit-converter.md
similarity index 100%
rename from src/data/projects/unit-converter.md
rename to src/content/projects/unit-converter.md
diff --git a/src/data/projects/url-shortening-service.md b/src/content/projects/url-shortening-service.md
similarity index 100%
rename from src/data/projects/url-shortening-service.md
rename to src/content/projects/url-shortening-service.md
diff --git a/src/data/projects/weather-api-wrapper-service.md b/src/content/projects/weather-api-wrapper-service.md
similarity index 100%
rename from src/data/projects/weather-api-wrapper-service.md
rename to src/content/projects/weather-api-wrapper-service.md
diff --git a/src/lib/project.ts b/src/lib/project.ts
index 011f829f7..6a693f453 100644
--- a/src/lib/project.ts
+++ b/src/lib/project.ts
@@ -1,54 +1,17 @@
-import type { MarkdownFileType } from './file';
+import { getCollection, type CollectionEntry } from 'astro:content';
import { getRoadmapById, type RoadmapFileType } from './roadmap.ts';
-export const projectDifficulties = [
- 'beginner',
- 'intermediate',
- 'advanced',
-] as const;
-export type ProjectDifficultyType = (typeof projectDifficulties)[number];
-
-export interface ProjectFrontmatter {
- title: string;
- description: string;
- isNew: boolean;
- sort: number;
- difficulty: ProjectDifficultyType;
- nature: string;
- skills: string[];
- seo: {
- title: string;
- description: string;
- keywords: string[];
- ogImageUrl: string;
- };
- roadmapIds: string[];
-}
-
-export type ProjectFileType = MarkdownFileType
& {
- id: string;
+export type ProjectFileType = CollectionEntry<'projects'> & {
roadmaps: RoadmapFileType[];
};
-/**
- * Generates id from the given project file
- * @param filePath Markdown file path
- *
- * @returns unique project identifier
- */
-function projectPathToId(filePath: string): string {
- const fileName = filePath.split('/').pop() || '';
-
- return fileName.replace('.md', '');
-}
-
export async function getProjectsByRoadmapId(
roadmapId: string,
): Promise {
const projects = await getAllProjects();
return projects.filter((project) =>
- project.frontmatter?.roadmapIds?.includes(roadmapId),
+ project.data?.roadmapIds?.includes(roadmapId),
);
}
@@ -63,26 +26,20 @@ export async function getAllProjects(): Promise {
return tempProjects;
}
- const projects = import.meta.glob(
- '/src/data/projects/*.md',
- {
- eager: true,
- },
- );
-
- tempProjects = Object.values(projects).map((projectFile) => ({
- ...projectFile,
- id: projectPathToId(projectFile.file),
- }));
-
+ tempProjects = await getCollection('projects');
return tempProjects;
}
export async function getProjectById(
groupId: string,
): Promise {
- const project = await import(`../data/projects/${groupId}.md`);
- const roadmapIds = project.frontmatter.roadmapIds || [];
+ const projects = await getAllProjects();
+ const project = projects.find((project) => project.slug === groupId);
+ if (!project) {
+ throw new Error(`Project not found with id: ${groupId}`);
+ }
+
+ const roadmapIds = project.data.roadmapIds || [];
const roadmaps = await Promise.all(
roadmapIds.map((roadmapId: string) => getRoadmapById(roadmapId)),
);
@@ -90,7 +47,6 @@ export async function getProjectById(
return {
...project,
roadmaps: roadmaps,
- id: projectPathToId(project.file),
};
}
@@ -101,7 +57,7 @@ export async function getRoadmapsProjects(): Promise<
const roadmapsProjects: Record = {};
projects.forEach((project) => {
- project.frontmatter.roadmapIds.forEach((roadmapId) => {
+ project.data.roadmapIds.forEach((roadmapId) => {
if (!roadmapsProjects[roadmapId]) {
roadmapsProjects[roadmapId] = [];
}
diff --git a/src/lib/video.ts b/src/lib/video.ts
index d34e27ab4..e0bba39e1 100644
--- a/src/lib/video.ts
+++ b/src/lib/video.ts
@@ -44,7 +44,7 @@ export async function getVideosByAuthor(
): Promise {
const allVideos = await getAllVideos();
- return allVideos.filter((video) => video.author?.id === authorId);
+ return allVideos.filter((video) => video.author?.slug === authorId);
}
/**
@@ -63,7 +63,7 @@ export async function getAllVideos(): Promise {
...videoFile,
id: videoPathToId(videoFile.file),
author: allAuthors.find(
- (author) => author.id === videoFile.frontmatter.authorId,
+ (author) => author.slug === videoFile.frontmatter.authorId,
)!,
}));
diff --git a/src/pages/[roadmapId]/projects.astro b/src/pages/[roadmapId]/projects.astro
index 78ed00e9b..061d4e85c 100644
--- a/src/pages/[roadmapId]/projects.astro
+++ b/src/pages/[roadmapId]/projects.astro
@@ -52,7 +52,7 @@ const nounTitle =
const seoDescription = `Seeking ${nounTitle.toLowerCase()} projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`;
const projects = await getProjectsByRoadmapId(roadmapId);
-const projectIds = projects.map((project) => project.id);
+const projectIds = projects.map((project) => project.slug);
const projectApiClient = projectApi(Astro);
const { response: userCounts } =
diff --git a/src/pages/authors/[authorId].astro b/src/pages/authors/[authorId].astro
index 18947fc91..943e760c1 100644
--- a/src/pages/authors/[authorId].astro
+++ b/src/pages/authors/[authorId].astro
@@ -32,7 +32,7 @@ const videos = await getVideosByAuthor(authorId);
---
({
text: questionGroup.data.briefTitle,
- url: `/questions/${questionGroup.id}`,
+ url: `/questions/${questionGroup.slug}`,
isNew: questionGroup.data.isNew,
}))}
/>
diff --git a/src/pages/pages.json.ts b/src/pages/pages.json.ts
index 16703b170..2996e7ea3 100644
--- a/src/pages/pages.json.ts
+++ b/src/pages/pages.json.ts
@@ -34,16 +34,16 @@ export async function GET() {
group: 'Best Practices',
})),
...questionGroups.map((questionGroup) => ({
- id: questionGroup.id,
- url: `/questions/${questionGroup.id}`,
+ id: questionGroup.slug,
+ url: `/questions/${questionGroup.slug}`,
title: questionGroup.data.briefTitle,
group: 'Questions',
})),
...guides.map((guide) => ({
- id: guide.id,
+ id: guide.slug,
url: guide.data.excludedBySlug
? guide.data.excludedBySlug
- : `/guides/${guide.id}`,
+ : `/guides/${guide.slug}`,
title: guide.data.title,
description: guide.data.description,
authorId: guide.data.authorId,
@@ -56,10 +56,10 @@ export async function GET() {
group: 'Videos',
})),
...projects.map((project) => ({
- id: project.id,
- url: `/projects/${project.id}`,
- title: project.frontmatter.title,
- description: project.frontmatter.description,
+ id: project.slug,
+ url: `/projects/${project.slug}`,
+ title: project.data.title,
+ description: project.data.description,
group: 'Projects',
})),
]),
diff --git a/src/pages/projects/[projectId]/index.astro b/src/pages/projects/[projectId]/index.astro
index 7a712a729..0c80b7eb5 100644
--- a/src/pages/projects/[projectId]/index.astro
+++ b/src/pages/projects/[projectId]/index.astro
@@ -1,11 +1,7 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import { Badge } from '../../../components/Badge';
-import {
- getAllProjects,
- getProjectById,
- type ProjectFrontmatter,
-} from '../../../lib/project';
+import { getAllProjects, getProjectById } from '../../../lib/project';
import AstroIcon from '../../../components/AstroIcon.astro';
import { ProjectStepper } from '../../../components/Projects/StatusStepper/ProjectStepper';
import { ProjectTabs } from '../../../components/Projects/ProjectTabs';
@@ -14,7 +10,7 @@ export async function getStaticPaths() {
const projects = await getAllProjects();
return projects
- .map((project) => project.id)
+ .map((project) => project.slug)
.map((projectId) => ({
params: { projectId },
}));
@@ -27,7 +23,8 @@ interface Params extends Record {
const { projectId } = Astro.params as Params;
const project = await getProjectById(projectId);
-const projectData = project.frontmatter as ProjectFrontmatter;
+const projectData = project.data;
+const { Content } = await project.render();
let jsonLdSchema: any[] = [];
@@ -49,7 +46,11 @@ const parentRoadmapId = projectData?.roadmapIds?.[0] || '';
>
-
+
project.id)
+ .map((project) => project.slug)
.map((projectId) => ({
params: { projectId },
}));
@@ -26,7 +22,7 @@ interface Params extends Record {
const { projectId } = Astro.params as Params;
const project = await getProjectById(projectId);
-const projectData = project.frontmatter as ProjectFrontmatter;
+const projectData = project.data;
let jsonLdSchema: any[] = [];
diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro
index f0cfe6774..5732cd1f2 100644
--- a/src/pages/projects/index.astro
+++ b/src/pages/projects/index.astro
@@ -12,7 +12,7 @@ const allRoadmapIds = Object.keys(roadmapProjects);
const allRoadmaps = await getRoadmapsByIds(allRoadmapIds);
const enrichedRoadmaps = allRoadmaps.map((roadmap) => {
const projects = (roadmapProjects[roadmap.id] || []).sort((a, b) => {
- return a.frontmatter.sort - b.frontmatter.sort;
+ return a.data.sort - b.data.sort;
});
return {
@@ -25,7 +25,7 @@ const enrichedRoadmaps = allRoadmaps.map((roadmap) => {
const projectIds = allRoadmapIds
.map((id) => roadmapProjects[id])
.flat()
- .map((project) => project.id);
+ .map((project) => project.slug);
const projectApiClient = projectApi(Astro);
const { response: userCounts } =
await projectApiClient.listProjectsUserCount(projectIds);
diff --git a/src/pages/questions/[questionGroupId].astro b/src/pages/questions/[questionGroupId].astro
index 6ea62c146..0d53541ec 100644
--- a/src/pages/questions/[questionGroupId].astro
+++ b/src/pages/questions/[questionGroupId].astro
@@ -34,7 +34,7 @@ const { data: frontmatter } = questionGroup;
briefTitle={frontmatter.briefTitle}
description={frontmatter.seo.description}
keywords={frontmatter.seo.keywords}
- permalink={`/questions/${questionGroup.id}`}
+ permalink={`/questions/${questionGroup.slug}`}
>
{
!frontmatter.authorId && (