Use hybrid mode of rendering

pull/5494/head
Kamran Ahmed 8 months ago
parent 1d04dd9970
commit 9d63306fc9
  1. 11
      astro.config.mjs
  2. 40
      src/pages/[roadmapId]/[...topicId].astro
  3. 26
      src/pages/[roadmapId]/index.astro
  4. 75
      src/pages/[roadmapId]/index.json.ts
  5. 17
      src/pages/authors/[authorId].astro
  6. 31
      src/pages/authors/[authorId].json.ts
  7. 26
      src/pages/best-practices/[bestPracticeId]/[...topicId].astro
  8. 30
      src/pages/best-practices/[bestPracticeId]/index.astro
  9. 52
      src/pages/best-practices/[bestPracticeId]/index.json.ts
  10. 27
      src/pages/g/[linkGroupId]/[linkId].astro
  11. 26
      src/pages/guides/[guideId].astro
  12. 24
      src/pages/questions/[questionGroupId].astro
  13. 2
      src/pages/r/[customRoadmapSlug].astro
  14. 12
      src/pages/u/[username].astro
  15. 2
      src/pages/v1-stats.json.ts
  16. 19
      src/pages/videos/[videoId].astro

@ -41,18 +41,11 @@ export default defineConfig({
],
],
},
// @FIXME:
// This should be "hybrid" but there is a bug in the current version of Astro
// that adds trailing slashes to the URLs when using "hybrid" mode.
// ----------------------------------------------
// https://github.com/withastro/astro/issues/7808
// ----------------------------------------------
// For now, we are using "server" mode and then using cloudfront to cache the
// pages and serve them as static.
output: 'server',
output: 'hybrid',
adapter: node({
mode: 'standalone',
}),
trailingSlash: 'never',
integrations: [
tailwind({
config: {

@ -1,22 +1,32 @@
---
import { getRoadmapTopicFiles } from '../../lib/roadmap-topic';
export const partial = true;
const { topicId, roadmapId } = Astro.params;
if (!topicId) {
return new Response();
import {
getRoadmapTopicFiles,
type RoadmapTopicFileType,
} from '../../lib/roadmap-topic';
export async function getStaticPaths() {
const topicPathMapping = await getRoadmapTopicFiles();
return Object.keys(topicPathMapping).map((topicSlug) => {
const topicDetails = topicPathMapping[topicSlug];
const roadmapId = topicDetails.roadmapId;
const topicId = topicSlug.replace(`/${roadmapId}/`, '');
return {
params: {
topicId,
roadmapId,
},
props: topicDetails,
};
});
}
const topicSlug = `/${roadmapId}/${topicId}`;
const topicPathMapping = await getRoadmapTopicFiles();
const topicDetails = topicPathMapping[topicSlug];
if (!topicDetails) {
return Astro.redirect('/404');
}
export const partial = true;
const { file } = topicDetails;
const { topicId } = Astro.params;
const { file, url, roadmapId, roadmap, heading } =
Astro.props as RoadmapTopicFileType;
const fileWithoutBasePath = file.file?.replace(/.+?\/src\/data/, '/src/data');
const gitHubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master${fileWithoutBasePath}`;

@ -13,25 +13,27 @@ import {
generateFAQSchema,
} from '../../lib/jsonld-schema';
import { getOpenGraphImageUrl } from '../../lib/open-graph';
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
import {
getRoadmapById,
type RoadmapFrontmatter,
getRoadmapFaqsById,
} from '../../lib/roadmap';
export async function getStaticPaths() {
const roadmapIds = await getRoadmapIds();
return roadmapIds.map((roadmapId) => ({
params: { roadmapId },
}));
}
interface Params extends Record<string, string | undefined> {
roadmapId: string;
}
const { roadmapId } = Astro.params as Params;
const roadmapFile = await getRoadmapById(roadmapId).catch(() => null);
if (!roadmapFile) {
return Astro.redirect('/404');
}
const roadmapFAQs = await getRoadmapFaqsById(roadmapId);
const roadmapFile = await import(
`../../data/roadmaps/${roadmapId}/${roadmapId}.md`
);
const { faqs: roadmapFAQs = [] } = await import(
`../../data/roadmaps/${roadmapId}/faqs.astro`
);
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
let jsonLdSchema = [];

@ -1,59 +1,30 @@
import type { APIRoute } from 'astro';
export const GET: APIRoute = async function ({ params, url, request, props }) {
const { roadmapId: fullRoadmapId } = params;
if (!fullRoadmapId) {
return new Response(
JSON.stringify({
data: null,
error: {
message: 'Roadmap not found',
},
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
},
);
}
// to account for `roadmap/roadmap-beginner.json` files
const roadmapId =
fullRoadmapId?.indexOf('-beginner') !== -1
? fullRoadmapId.replace('-beginner', '')
: fullRoadmapId;
export async function getStaticPaths() {
const roadmapJsons = import.meta.glob('/src/data/roadmaps/**/*.json', {
eager: true,
});
const fileName =
roadmapId === fullRoadmapId ? `${roadmapId}.json` : `${fullRoadmapId}.json`;
return Object.keys(roadmapJsons).map((filePath) => {
const roadmapId = filePath.split('/').pop()?.replace('.json', '');
const roadmapJson = roadmapJsons[filePath] as Record<string, any>;
try {
const roadmapJson = await import(
/* @vite-ignore */ `../../data/roadmaps/${roadmapId}/${fileName}`
);
return new Response(JSON.stringify(roadmapJson), {
status: 200,
headers: {
'Content-Type': 'application/json',
return {
params: {
roadmapId,
},
});
} catch (error) {
return new Response(
JSON.stringify({
data: null,
error: {
message: 'Roadmap not found',
detail: (error as any).message,
},
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
props: {
roadmapJson: roadmapJson?.default,
},
);
}
};
});
}
export const GET: APIRoute = async function ({ params, request, props }) {
return new Response(JSON.stringify(props.roadmapJson), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
};

@ -4,21 +4,22 @@ import AstroIcon from '../../components/AstroIcon.astro';
import { getGuidesByAuthor } from '../../lib/guide';
import { getVideosByAuthor } from '../../lib/video';
import GuideListItem from '../../components/GuideListItem.astro';
import { getAuthorById } from '../../lib/author';
import { getAuthorById, getAuthorIds } from '../../lib/author';
import VideoListItem from '../../components/VideoListItem.astro';
interface Params extends Record<string, string | undefined> {}
const { authorId } = Astro.params;
if (!authorId) {
return Astro.redirect('/404');
}
export async function getStaticPaths() {
const authorIds = await getAuthorIds();
const author = await getAuthorById(authorId);
if (!author) {
return Astro.redirect('/404');
return authorIds.map((authorId) => ({
params: { authorId },
}));
}
const { authorId } = Astro.params;
const author = await getAuthorById(authorId);
const authorFrontmatter = author.frontmatter;
const guides = await getGuidesByAuthor(authorId);

@ -1,20 +1,25 @@
import type { APIRoute } from 'astro';
import { getAuthorById } from '../../lib/author';
import { getAuthorById, getAuthorIds } from '../../lib/author';
export const GET: APIRoute = async function ({ params, request, props }) {
const { authorId } = params as { authorId: string };
export async function getStaticPaths() {
const authorIds = await getAuthorIds();
return await Promise.all(
authorIds.map(async (authorId) => {
const authorDetails = await getAuthorById(authorId);
const authorDetails = await getAuthorById(authorId);
if (!authorDetails) {
return new Response(JSON.stringify({ error: 'Not found' }), {
status: 404,
headers: {
'Content-Type': 'application/json',
},
});
}
return {
params: { authorId },
props: {
authorDetails: authorDetails?.frontmatter || {},
},
};
}),
);
}
return new Response(JSON.stringify(authorDetails?.frontmatter), {
export const GET: APIRoute = async function ({ params, request, props }) {
return new Response(JSON.stringify(props.authorDetails), {
status: 200,
headers: {
'Content-Type': 'application/json',

@ -1,16 +1,26 @@
---
import { getAllBestPracticeTopicFiles } from '../../../lib/best-practice-topic';
import type { BestPracticeTopicFileType } from '../../../lib/best-practice-topic';
const { topicId, bestPracticeId } = Astro.params;
if (!topicId) {
return new Response();
}
export async function getStaticPaths() {
const topicPathMapping = await getAllBestPracticeTopicFiles();
return Object.keys(topicPathMapping).map((topicSlug) => {
const topicDetails = topicPathMapping[topicSlug];
const bestPracticeId = topicDetails.bestPracticeId;
const topicId = topicSlug.replace(`/${bestPracticeId}/`, '');
const topicSlug = `/${bestPracticeId}/${topicId}`;
const topicPathMapping = await getAllBestPracticeTopicFiles();
return {
params: {
topicId,
bestPracticeId,
},
props: topicDetails,
};
});
}
const topicDetails = topicPathMapping[topicSlug];
const { file } = topicDetails;
const { file } = Astro.props as BestPracticeTopicFileType;
const fileWithoutBasePath = file.file?.replace(/.+?\/src\/data/, '/src/data');
const gitHubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master${fileWithoutBasePath}`;

@ -7,13 +7,24 @@ import { TopicDetail } from '../../../components/TopicDetail/TopicDetail';
import UpcomingForm from '../../../components/UpcomingForm.astro';
import BaseLayout from '../../../layouts/BaseLayout.astro';
import { UserProgressModal } from '../../../components/UserProgress/UserProgressModal';
import {
type BestPracticeFileType,
type BestPracticeFrontmatter,
getBestPracticeById,
} from '../../../lib/best-practice';
import { generateArticleSchema } from '../../../lib/jsonld-schema';
import { getOpenGraphImageUrl } from '../../../lib/open-graph';
import {
BestPracticeFileType,
BestPracticeFrontmatter,
getAllBestPractices,
} from '../../../lib/best-practice';
export async function getStaticPaths() {
const bestPractices = await getAllBestPractices();
return bestPractices.map((bestPractice: BestPracticeFileType) => ({
params: { bestPracticeId: bestPractice.id },
props: {
bestPractice: bestPractice,
},
}));
}
interface Params extends Record<string, string | undefined> {
bestPracticeId: string;
@ -24,14 +35,7 @@ interface Props {
}
const { bestPracticeId } = Astro.params as Params;
const bestPractice = await getBestPracticeById(bestPracticeId).catch(
() => null,
);
if (!bestPractice) {
return Astro.redirect('/404');
}
const { bestPractice } = Astro.props as Props;
const bestPracticeData = bestPractice.frontmatter as BestPracticeFrontmatter;
let jsonLdSchema = [];

@ -1,33 +1,33 @@
import type { APIRoute } from 'astro';
export const GET: APIRoute = async function ({ params, request, props }) {
const { bestPracticeId } = params;
export async function getStaticPaths() {
const bestPracticeJsons = await import.meta.glob(
'/src/data/best-practices/**/*.json',
{
eager: true,
},
);
try {
const roadmapJson = await import(
`../../../data/best-practices/${bestPracticeId}/${bestPracticeId}.json`
);
return Object.keys(bestPracticeJsons).map((filePath) => {
const bestPracticeId = filePath.split('/').pop()?.replace('.json', '');
const bestPracticeJson = bestPracticeJsons[filePath] as Record<string, any>;
return new Response(JSON.stringify(roadmapJson), {
status: 200,
headers: {
'Content-Type': 'application/json',
return {
params: {
bestPracticeId,
},
});
} catch (error) {
return new Response(
JSON.stringify({
data: null,
error: {
message: 'Best Practices not found',
},
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
props: {
bestPracticeJson: bestPracticeJson?.default,
},
);
}
};
});
}
export const GET: APIRoute = async function ({ params, request, props }) {
return new Response(JSON.stringify(props.bestPracticeJson), {
status: 200,
headers: {
'content-type': 'application/json',
},
});
};

@ -1,14 +1,31 @@
---
import BaseLayout from '../../../layouts/BaseLayout.astro';
import SkeletonLayout from '../../../layouts/SkeletonLayout.astro';
import { getLinkGroupById } from '../../../lib/link-group';
import { getAllLinkGroups } from '../../../lib/link-group';
const { linkId } = Astro.params;
const linkGroup = await getLinkGroupById(linkId!).catch(() => null);
if (!linkGroup) {
return Astro.redirect('/404');
export async function getStaticPaths() {
const linkGroups = await getAllLinkGroups();
return linkGroups.flatMap((linkGroup) => {
const linkGroupLinks = linkGroup.frontmatter;
return Object.keys(linkGroupLinks).map((slug) => {
return {
params: {
linkGroupId: linkGroup.id,
linkId: slug,
},
props: {
linkGroup,
},
};
});
});
}
const { linkId } = Astro.params;
const { linkGroup } = Astro.props;
const fullUrl = linkGroup.frontmatter[linkId!];
---

@ -2,23 +2,27 @@
import GuideContent from '../../components/Guide/GuideContent.astro';
import GuideHeader from '../../components/GuideHeader.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { getGuideById } from '../../lib/guide';
import { getAllGuides, type GuideFileType } from '../../lib/guide';
import { getOpenGraphImageUrl } from '../../lib/open-graph';
interface Params extends Record<string, string | undefined> {
guideId: string;
export interface Props {
guide: GuideFileType;
}
const { guideId } = Astro.params;
if (!guideId) {
return Astro.redirect('/404');
}
const guide = await getGuideById(guideId!).catch(() => null);
if (!guide) {
return Astro.redirect('/404');
export async function getStaticPaths() {
const guides = (await getAllGuides()).filter(
(guide) => !guide.frontmatter.excludedBySlug,
);
return guides.map((guide) => ({
params: { guideId: guide.id },
props: { guide },
}));
}
const { frontmatter: guideData } = guide;
const { guideId } = Astro.params;
const { guide } = Astro.props;
const { frontmatter: guideData, author } = guide;
const ogImageUrl = getOpenGraphImageUrl({
group: 'guides',

@ -6,16 +6,26 @@ import Footer from '../../components/Footer.astro';
import AstroIcon from '../../components/AstroIcon.astro';
import { QuestionsList } from '../../components/Questions/QuestionsList';
import { getQuestionGroupById } from '../../lib/question-group';
import {
getAllQuestionGroups,
type QuestionGroupType,
} from '../../lib/question-group';
const { questionGroupId } = Astro.params;
const questionGroup = await getQuestionGroupById(questionGroupId!).catch(
() => null,
);
if (!questionGroup) {
return Astro.redirect('/404');
export interface Props {
questionGroup: QuestionGroupType;
}
export async function getStaticPaths() {
const questionGroups = await getAllQuestionGroups();
return questionGroups.map((questionGroup) => {
return {
params: { questionGroupId: questionGroup.id },
props: { questionGroup },
};
});
}
const { questionGroup } = Astro.props;
const { frontmatter } = questionGroup;
---

@ -5,6 +5,8 @@ import { SkeletonRoadmapHeader } from '../../components/CustomRoadmap/SkeletonRo
import Loader from '../../components/Loader.astro';
import ProgressHelpPopup from '../../components/ProgressHelpPopup.astro';
export const prerender = false;
const { customRoadmapSlug } = Astro.params;
---

@ -1,10 +1,12 @@
---
import { FrownIcon } from 'lucide-react';
import { userApi } from '../../../api/user';
import AccountLayout from '../../../layouts/AccountLayout.astro';
import { UserPublicProfilePage } from '../../../components/UserPublicProfile/UserPublicProfilePage';
import OpenSourceBanner from '../../../components/OpenSourceBanner.astro';
import Footer from '../../../components/Footer.astro';
import { userApi } from '../../api/user';
import AccountLayout from '../../layouts/AccountLayout.astro';
import { UserPublicProfilePage } from '../../components/UserPublicProfile/UserPublicProfilePage';
import OpenSourceBanner from '../../components/OpenSourceBanner.astro';
import Footer from '../../components/Footer.astro';
export const prerender = false;
interface Params extends Record<string, string | undefined> {
username: string;

@ -1,7 +1,5 @@
import { execSync } from 'child_process';
export const prerender = true;
export async function GET() {
const commitHash = execSync('git rev-parse HEAD').toString().trim();
const commitDate = execSync('git log -1 --format=%cd').toString().trim();

@ -1,14 +1,23 @@
---
import VideoHeader from '../../components/VideoHeader.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { getVideoById } from '../../lib/video';
import { getAllVideos, VideoFileType } from '../../lib/video';
const { videoId } = Astro.params;
export interface Props {
video: VideoFileType;
}
export async function getStaticPaths() {
const videos = await getAllVideos();
const video = await getVideoById(videoId).catch(() => null);
if (!video) {
return Astro.redirect('/404');
return videos.map((video) => ({
params: { videoId: video.id },
props: { video },
}));
}
const { videoId } = Astro.params;
const { video } = Astro.props;
---
<BaseLayout

Loading…
Cancel
Save