mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-31 04:59:50 +02:00
Add json-ld schema to frontend/backend roadmaps
This commit is contained in:
@@ -16,6 +16,7 @@ export interface Props {
|
||||
noIndex?: boolean;
|
||||
permalink?: string;
|
||||
sponsor?: SponsorType;
|
||||
jsonLd?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -25,14 +26,13 @@ const {
|
||||
noIndex = false,
|
||||
permalink = '',
|
||||
sponsor,
|
||||
jsonLd,
|
||||
} = Astro.props;
|
||||
|
||||
// Remove trailing slashes to consider the page as canonical
|
||||
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink}`;
|
||||
|
||||
const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${
|
||||
import.meta.env.GITHUB_SHA
|
||||
}`;
|
||||
const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${import.meta.env.GITHUB_SHA}`;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
@@ -69,43 +69,23 @@ const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${
|
||||
|
||||
<meta name='mobile-web-app-capable' content='yes' />
|
||||
<meta name='apple-mobile-web-app-capable' content='yes' />
|
||||
<meta
|
||||
name='apple-mobile-web-app-status-bar-style'
|
||||
content='black-translucent'
|
||||
/>
|
||||
<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />
|
||||
<meta name='apple-mobile-web-app-title' content='roadmap.sh' />
|
||||
<meta name='application-name' content='roadmap.sh' />
|
||||
|
||||
<link
|
||||
rel='apple-touch-icon'
|
||||
sizes='180x180'
|
||||
href='/manifest/apple-touch-icon.png'
|
||||
/>
|
||||
<link rel='apple-touch-icon' sizes='180x180' href='/manifest/apple-touch-icon.png' />
|
||||
<meta name='msapplication-TileColor' content='#101010' />
|
||||
<meta name='theme-color' content='#848a9a' />
|
||||
|
||||
<link rel='manifest' href='/manifest/manifest.json' />
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='32x32'
|
||||
href='/manifest/icon32.png'
|
||||
/>
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='16x16'
|
||||
href='/manifest/icon16.png'
|
||||
/>
|
||||
<link
|
||||
rel='shortcut icon'
|
||||
href='/manifest/favicon.ico'
|
||||
type='image/x-icon'
|
||||
/>
|
||||
<link rel='icon' type='image/png' sizes='32x32' href='/manifest/icon32.png' />
|
||||
<link rel='icon' type='image/png' sizes='16x16' href='/manifest/icon16.png' />
|
||||
<link rel='shortcut icon' href='/manifest/favicon.ico' type='image/x-icon' />
|
||||
|
||||
<link rel='icon' href='/manifest/favicon.ico' type='image/x-icon' />
|
||||
|
||||
<slot name='after-header' />
|
||||
{jsonLd && <script type='application/ld+json' set:html={JSON.stringify(jsonLd)} />}
|
||||
</head>
|
||||
<body>
|
||||
<YouTubeBanner />
|
||||
|
40
src/lib/jsonld-schema.ts
Normal file
40
src/lib/jsonld-schema.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
type ArticleSchemaProps = {
|
||||
url: string;
|
||||
headline: string;
|
||||
description: string;
|
||||
imageUrl: string;
|
||||
datePublished: string;
|
||||
dateModified: string;
|
||||
};
|
||||
|
||||
export function generateArticleSchema(article: ArticleSchemaProps) {
|
||||
const { url, headline, description, imageUrl, datePublished, dateModified } =
|
||||
article;
|
||||
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
mainEntityOfPage: {
|
||||
'@type': 'WebPage',
|
||||
'@id': url,
|
||||
},
|
||||
headline: headline,
|
||||
description: description,
|
||||
image: imageUrl,
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Kamran Ahmed',
|
||||
url: 'https://twitter.com/kamranahmedse',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'roadmap.sh',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://roadmap.sh/images/brand-square.png',
|
||||
},
|
||||
},
|
||||
datePublished: datePublished,
|
||||
dateModified: dateModified,
|
||||
};
|
||||
}
|
@@ -22,6 +22,13 @@ export interface RoadmapFrontmatter {
|
||||
description: string;
|
||||
keywords: string[];
|
||||
};
|
||||
schema?: {
|
||||
headline: string;
|
||||
description: string;
|
||||
datePublished: string;
|
||||
dateModified: string;
|
||||
imageUrl: string;
|
||||
};
|
||||
relatedRoadmaps: string[];
|
||||
sitemap: {
|
||||
priority: number;
|
||||
@@ -46,12 +53,9 @@ function roadmapPathToId(filePath: string): string {
|
||||
* @returns string[] Array of roadmap IDs
|
||||
*/
|
||||
export async function getRoadmapIds() {
|
||||
const roadmapFiles = await import.meta.glob<RoadmapFileType>(
|
||||
'/src/roadmaps/*/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
const roadmapFiles = await import.meta.glob<RoadmapFileType>('/src/roadmaps/*/*.md', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
return Object.keys(roadmapFiles).map(roadmapPathToId);
|
||||
}
|
||||
@@ -62,15 +66,10 @@ export async function getRoadmapIds() {
|
||||
* @param tag Tag assigned to roadmap
|
||||
* @returns Promisified RoadmapFileType[]
|
||||
*/
|
||||
export async function getRoadmapsByTag(
|
||||
tag: string
|
||||
): Promise<RoadmapFileType[]> {
|
||||
const roadmapFilesMap = await import.meta.glob<RoadmapFileType>(
|
||||
'/src/roadmaps/*/*.md',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
export async function getRoadmapsByTag(tag: string): Promise<RoadmapFileType[]> {
|
||||
const roadmapFilesMap = await import.meta.glob<RoadmapFileType>('/src/roadmaps/*/*.md', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
const roadmapFiles = Object.values(roadmapFilesMap);
|
||||
const filteredRoadmaps = roadmapFiles
|
||||
@@ -80,7 +79,5 @@ export async function getRoadmapsByTag(
|
||||
id: roadmapPathToId(roadmapFile.file),
|
||||
}));
|
||||
|
||||
return filteredRoadmaps.sort(
|
||||
(a, b) => a.frontmatter.order - b.frontmatter.order
|
||||
);
|
||||
return filteredRoadmaps.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
---
|
||||
import CaptchaScripts from '../../components/Captcha/CaptchaScripts.astro';
|
||||
import FAQs from '../../components/FAQs.astro';
|
||||
import InteractiveRoadmap from '../../components/InteractiveRoadmap/InteractiveRoadmap.astro';
|
||||
import MarkdownRoadmap from '../../components/MarkdownRoadmap.astro';
|
||||
import RoadmapHeader from '../../components/RoadmapHeader.astro';
|
||||
import UpcomingRoadmap from '../../components/UpcomingRoadmap.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { generateArticleSchema } from '../../lib/jsonld-schema';
|
||||
import { getRoadmapIds, RoadmapFrontmatter } from '../../lib/roadmap';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
@@ -22,8 +22,21 @@ interface Params extends Record<string, string | undefined> {
|
||||
|
||||
const { roadmapId } = Astro.params as Params;
|
||||
const roadmapFile = await import(`../../roadmaps/${roadmapId}/${roadmapId}.md`);
|
||||
const questions = await import (`../../roadmaps/${roadmapId}/faqs.astro`);
|
||||
const questions = await import(`../../roadmaps/${roadmapId}/faqs.astro`);
|
||||
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||
|
||||
let articleSchema;
|
||||
if (roadmapData.schema) {
|
||||
const roadmapSchema = roadmapData.schema;
|
||||
articleSchema = generateArticleSchema({
|
||||
url: `https://roadmap.sh/${roadmapId}`,
|
||||
headline: roadmapSchema.headline,
|
||||
description: roadmapSchema.description,
|
||||
datePublished: roadmapSchema.datePublished,
|
||||
dateModified: roadmapSchema.dateModified,
|
||||
imageUrl: roadmapSchema.imageUrl,
|
||||
});
|
||||
}
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
@@ -33,6 +46,7 @@ const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||
keywords={roadmapData.seo.keywords}
|
||||
sponsor={roadmapData.sponsor}
|
||||
noIndex={roadmapData.isUpcoming}
|
||||
jsonLd={articleSchema}
|
||||
>
|
||||
<RoadmapHeader
|
||||
description={roadmapData.description}
|
||||
@@ -55,10 +69,7 @@ const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||
|
||||
{
|
||||
!roadmapData.isUpcoming && !roadmapData.jsonUrl && (
|
||||
<MarkdownRoadmap
|
||||
roadmapId={roadmapId}
|
||||
description={roadmapData.description}
|
||||
>
|
||||
<MarkdownRoadmap roadmapId={roadmapId} description={roadmapData.description}>
|
||||
<roadmapFile.Content />
|
||||
</MarkdownRoadmap>
|
||||
)
|
||||
|
@@ -20,6 +20,12 @@ sponsor:
|
||||
category: "SponsorClick"
|
||||
action: "Ambassador Redirect"
|
||||
label: "Clicked Ambassador Link"
|
||||
schema:
|
||||
headline: "Backend Developer Roadmap"
|
||||
description: "Learn how to become a Backend Developer with this interactive step by step guide in 2023. We also have resources and short descriptions attached to the roadmap items so you can get everything you want to learn in one place."
|
||||
imageUrl: "https://roadmap.sh/roadmaps/backend.png"
|
||||
datePublished: "2023-01-05"
|
||||
dateModified: "2023-01-20"
|
||||
seo:
|
||||
title: "Backend Developer Roadmap"
|
||||
description: "Learn to become a modern backend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern backend development."
|
||||
|
@@ -10,6 +10,12 @@ hasTopics: true
|
||||
dimensions:
|
||||
width: 968
|
||||
height: 2734.48
|
||||
schema:
|
||||
headline: "Frontend Developer Roadmap"
|
||||
description: "Learn how to become a Frontend Developer with this interactive step by step guide in 2023. We also have resources and short descriptions attached to the roadmap items so you can get everything you want to learn in one place."
|
||||
imageUrl: "https://roadmap.sh/roadmaps/frontend.png"
|
||||
datePublished: "2023-01-05"
|
||||
dateModified: "2023-01-20"
|
||||
seo:
|
||||
title: "Frontend Developer Roadmap"
|
||||
description: "Learn to become a modern frontend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern frontend development."
|
||||
|
Reference in New Issue
Block a user