mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-21 00:21:35 +02:00
chore: update official roadmap endpoint (#8628)
* chore: update official roadmap endpoint * fix: variable typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -261,9 +261,6 @@ importers:
|
|||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^3.2.0
|
specifier: ^3.2.0
|
||||||
version: 3.2.0
|
version: 3.2.0
|
||||||
tinykeys:
|
|
||||||
specifier: ^3.0.0
|
|
||||||
version: 3.0.0
|
|
||||||
unified:
|
unified:
|
||||||
specifier: ^11.0.5
|
specifier: ^11.0.5
|
||||||
version: 11.0.5
|
version: 11.0.5
|
||||||
@@ -3485,9 +3482,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
|
resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
tinykeys@3.0.0:
|
|
||||||
resolution: {integrity: sha512-nazawuGv5zx6MuDfDY0rmfXjuOGhD5XU2z0GLURQ1nzl0RUe9OuCJq+0u8xxJZINHe+mr7nw8PWYYZ9WhMFujw==}
|
|
||||||
|
|
||||||
tiptap-markdown@0.8.10:
|
tiptap-markdown@0.8.10:
|
||||||
resolution: {integrity: sha512-iDVkR2BjAqkTDtFX0h94yVvE2AihCXlF0Q7RIXSJPRSR5I0PA1TMuAg6FHFpmqTn4tPxJ0by0CK7PUMlnFLGEQ==}
|
resolution: {integrity: sha512-iDVkR2BjAqkTDtFX0h94yVvE2AihCXlF0Q7RIXSJPRSR5I0PA1TMuAg6FHFpmqTn4tPxJ0by0CK7PUMlnFLGEQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -7306,8 +7300,6 @@ snapshots:
|
|||||||
fdir: 6.4.4(picomatch@4.0.2)
|
fdir: 6.4.4(picomatch@4.0.2)
|
||||||
picomatch: 4.0.2
|
picomatch: 4.0.2
|
||||||
|
|
||||||
tinykeys@3.0.0: {}
|
|
||||||
|
|
||||||
tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)):
|
tiptap-markdown@0.8.10(@tiptap/core@2.12.0(@tiptap/pm@2.12.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
'@tiptap/core': 2.12.0(@tiptap/pm@2.12.0)
|
||||||
|
@@ -8,6 +8,7 @@ import { slugify } from '../src/lib/slugger';
|
|||||||
import { markdownToHtml } from '../src/lib/markdown';
|
import { markdownToHtml } from '../src/lib/markdown';
|
||||||
import { HTMLElement, parse } from 'node-html-parser';
|
import { HTMLElement, parse } from 'node-html-parser';
|
||||||
import { htmlToMarkdown } from '../src/lib/html';
|
import { htmlToMarkdown } from '../src/lib/html';
|
||||||
|
import { httpGet } from '../src/lib/http';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@@ -53,13 +54,16 @@ if (!stats || !stats.isDirectory()) {
|
|||||||
for (const roadmapId of editorRoadmapIds) {
|
for (const roadmapId of editorRoadmapIds) {
|
||||||
console.log(`🚀 Starting ${roadmapId}`);
|
console.log(`🚀 Starting ${roadmapId}`);
|
||||||
|
|
||||||
const roadmapDir = path.join(
|
const { response: data, error } = await httpGet(
|
||||||
ROADMAP_CONTENT_DIR,
|
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${roadmapId}`,
|
||||||
roadmapId,
|
|
||||||
`${roadmapId}.json`,
|
|
||||||
);
|
);
|
||||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
|
||||||
let { nodes } = JSON.parse(roadmapContent) as {
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { nodes } = data as {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
};
|
};
|
||||||
nodes = nodes.filter(
|
nodes = nodes.filter(
|
||||||
@@ -97,11 +101,11 @@ for (const roadmapId of editorRoadmapIds) {
|
|||||||
> = {};
|
> = {};
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
const ndoeDirPatterWithoutExt = `${slugify(node.data.label)}@${node.id}`;
|
const nodeDirPatternWithoutExt = `${slugify(node?.data?.label as string)}@${node.id}`;
|
||||||
const nodeDirPattern = `${ndoeDirPatterWithoutExt}.md`;
|
const nodeDirPattern = `${nodeDirPatternWithoutExt}.md`;
|
||||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||||
contentMap[nodeDirPattern] = {
|
contentMap[nodeDirPattern] = {
|
||||||
title: node.data.label,
|
title: node?.data?.label as string,
|
||||||
description: '',
|
description: '',
|
||||||
links: [],
|
links: [],
|
||||||
};
|
};
|
||||||
@@ -169,7 +173,7 @@ for (const roadmapId of editorRoadmapIds) {
|
|||||||
const description = htmlToMarkdown(htmlStringWithoutLinks);
|
const description = htmlToMarkdown(htmlStringWithoutLinks);
|
||||||
|
|
||||||
contentMap[node.id] = {
|
contentMap[node.id] = {
|
||||||
title: node.data.label,
|
title: node.data.label as string,
|
||||||
description,
|
description,
|
||||||
links: listLinks,
|
links: listLinks,
|
||||||
};
|
};
|
||||||
|
@@ -7,6 +7,7 @@ import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
|||||||
import { slugify } from '../src/lib/slugger';
|
import { slugify } from '../src/lib/slugger';
|
||||||
import OpenAI from 'openai';
|
import OpenAI from 'openai';
|
||||||
import { runPromisesInBatchSequentially } from '../src/lib/promise';
|
import { runPromisesInBatchSequentially } from '../src/lib/promise';
|
||||||
|
import { httpGet } from '../src/lib/http';
|
||||||
|
|
||||||
// ERROR: `__dirname` is not defined in ES module scope
|
// ERROR: `__dirname` is not defined in ES module scope
|
||||||
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
||||||
@@ -50,13 +51,16 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roadmapDir = path.join(
|
const { response: roadmapContent, error } = await httpGet(
|
||||||
ROADMAP_CONTENT_DIR,
|
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${roadmapId}`,
|
||||||
roadmapId,
|
|
||||||
`${roadmapId}.json`,
|
|
||||||
);
|
);
|
||||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
|
||||||
let { nodes, edges } = JSON.parse(roadmapContent) as {
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let { nodes, edges } = roadmapContent as {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
};
|
};
|
||||||
@@ -138,7 +142,7 @@ function writeTopicContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
||||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
const nodeDirPattern = `${slugify(node?.data?.label as string)}@${node.id}.md`;
|
||||||
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
if (!roadmapContentFiles.includes(nodeDirPattern)) {
|
||||||
console.log(`Missing file for: ${nodeDirPattern}`);
|
console.log(`Missing file for: ${nodeDirPattern}`);
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +156,7 @@ async function writeNodeContent(node: Node & { parentTitle?: string }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const topic = node.data.label;
|
const topic = node.data.label as string;
|
||||||
const parentTopic = node.parentTitle;
|
const parentTopic = node.parentTitle;
|
||||||
|
|
||||||
console.log(`⏳ Generating content for ${topic}...`);
|
console.log(`⏳ Generating content for ${topic}...`);
|
||||||
|
@@ -5,6 +5,7 @@ import type { Node } from '@roadmapsh/editor';
|
|||||||
import matter from 'gray-matter';
|
import matter from 'gray-matter';
|
||||||
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
|
||||||
import { slugify } from '../src/lib/slugger';
|
import { slugify } from '../src/lib/slugger';
|
||||||
|
import { httpGet } from '../src/lib/http';
|
||||||
|
|
||||||
// ERROR: `__dirname` is not defined in ES module scope
|
// ERROR: `__dirname` is not defined in ES module scope
|
||||||
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
// https://iamwebwiz.medium.com/how-to-fix-dirname-is-not-defined-in-es-module-scope-34d94a86694d
|
||||||
@@ -48,13 +49,16 @@ if (roadmapFrontmatter.renderer !== 'editor') {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roadmapDir = path.join(
|
const { response: roadmapContent, error } = await httpGet(
|
||||||
ROADMAP_CONTENT_DIR,
|
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${roadmapId}`,
|
||||||
roadmapId,
|
|
||||||
`${roadmapId}.json`,
|
|
||||||
);
|
);
|
||||||
const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
|
|
||||||
let { nodes } = JSON.parse(roadmapContent) as {
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let { nodes } = roadmapContent as {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
};
|
};
|
||||||
nodes = nodes.filter(
|
nodes = nodes.filter(
|
||||||
@@ -73,7 +77,7 @@ const roadmapContentFiles = await fs.readdir(roadmapContentDir, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
nodes.forEach(async (node, index) => {
|
nodes.forEach(async (node, index) => {
|
||||||
const nodeDirPattern = `${slugify(node.data.label)}@${node.id}.md`;
|
const nodeDirPattern = `${slugify(node.data.label as string)}@${node.id}.md`;
|
||||||
if (roadmapContentFiles.includes(nodeDirPattern)) {
|
if (roadmapContentFiles.includes(nodeDirPattern)) {
|
||||||
console.log(`Skipping ${nodeDirPattern}`);
|
console.log(`Skipping ${nodeDirPattern}`);
|
||||||
return;
|
return;
|
||||||
|
@@ -36,7 +36,9 @@ export function EditorRoadmap(props: EditorRoadmapProps) {
|
|||||||
|
|
||||||
const { response, error } = await httpGet<
|
const { response, error } = await httpGet<
|
||||||
Omit<RoadmapRendererProps, 'resourceId'>
|
Omit<RoadmapRendererProps, 'resourceId'>
|
||||||
>(`/${switchRoadmapId || resourceId}.json`);
|
>(
|
||||||
|
`${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${switchRoadmapId || resourceId}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@@ -62,15 +62,6 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [renderer, setRenderer] = useState<PageType['renderer']>('balsamiq');
|
const [renderer, setRenderer] = useState<PageType['renderer']>('balsamiq');
|
||||||
|
|
||||||
let resourceJsonUrl = import.meta.env.DEV
|
|
||||||
? 'http://localhost:3000'
|
|
||||||
: 'https://roadmap.sh';
|
|
||||||
if (resourceType === 'roadmap') {
|
|
||||||
resourceJsonUrl += `/${resourceId}.json`;
|
|
||||||
} else {
|
|
||||||
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getMemberProgress(
|
async function getMemberProgress(
|
||||||
teamId: string,
|
teamId: string,
|
||||||
memberId: string,
|
memberId: string,
|
||||||
@@ -92,7 +83,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderResource(jsonUrl: string) {
|
async function renderResource() {
|
||||||
const page = await getResourceMeta(resourceType, resourceId);
|
const page = await getResourceMeta(resourceType, resourceId);
|
||||||
if (!page) {
|
if (!page) {
|
||||||
toast.error('Resource not found');
|
toast.error('Resource not found');
|
||||||
@@ -102,11 +93,22 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
const renderer = page.renderer || 'balsamiq';
|
const renderer = page.renderer || 'balsamiq';
|
||||||
setRenderer(renderer);
|
setRenderer(renderer);
|
||||||
|
|
||||||
const res = await fetch(jsonUrl, {});
|
let resourceJsonUrl = import.meta.env.DEV
|
||||||
|
? 'http://localhost:3000'
|
||||||
|
: 'https://roadmap.sh';
|
||||||
|
if (resourceType === 'roadmap' && renderer === 'balsamiq') {
|
||||||
|
resourceJsonUrl += `/${resourceId}.json`;
|
||||||
|
} else if (resourceType === 'roadmap' && renderer === 'editor') {
|
||||||
|
resourceJsonUrl = `${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${resourceId}`;
|
||||||
|
} else {
|
||||||
|
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(resourceJsonUrl, {});
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
const svg =
|
const svg =
|
||||||
renderer === 'editor'
|
renderer === 'editor'
|
||||||
? await renderFlowJSON(json as any)
|
? await renderFlowJSON(json)
|
||||||
: await wireframeJSONToSVG(json, {
|
: await wireframeJSONToSVG(json, {
|
||||||
fontURL: '/fonts/balsamiq.woff2',
|
fontURL: '/fonts/balsamiq.woff2',
|
||||||
});
|
});
|
||||||
@@ -129,19 +131,13 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (!containerEl.current || !resourceId || !resourceType || !teamId) {
|
||||||
!containerEl.current ||
|
|
||||||
!resourceJsonUrl ||
|
|
||||||
!resourceId ||
|
|
||||||
!resourceType ||
|
|
||||||
!teamId
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
Promise.all([
|
Promise.all([
|
||||||
renderResource(resourceJsonUrl),
|
renderResource(),
|
||||||
getMemberProgress(teamId, member._id, resourceType, resourceId),
|
getMemberProgress(teamId, member._id, resourceType, resourceId),
|
||||||
])
|
])
|
||||||
.then(([_, memberProgress = {}]) => {
|
.then(([_, memberProgress = {}]) => {
|
||||||
@@ -276,7 +272,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
}, [member]);
|
}, [member]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed left-0 right-0 top-0 z-100 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
|
<div className="fixed top-0 right-0 left-0 z-100 h-full items-center justify-center overflow-x-hidden overflow-y-auto overscroll-contain bg-black/50">
|
||||||
<div
|
<div
|
||||||
id={renderer === 'editor' ? undefined : 'customized-roadmap'}
|
id={renderer === 'editor' ? undefined : 'customized-roadmap'}
|
||||||
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
|
className="relative mx-auto h-full w-full max-w-4xl p-4 md:h-auto"
|
||||||
@@ -304,14 +300,14 @@ export function MemberProgressModal(props: ProgressMapProps) {
|
|||||||
<div className="flex w-full justify-center">
|
<div className="flex w-full justify-center">
|
||||||
<Spinner
|
<Spinner
|
||||||
isDualRing={false}
|
isDualRing={false}
|
||||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden ${
|
className={`absolute top-3 right-2.5 ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden ${
|
||||||
isCurrentUser ? 'hover:bg-gray-800' : 'hover:bg-gray-100'
|
isCurrentUser ? 'hover:bg-gray-800' : 'hover:bg-gray-100'
|
||||||
}`}
|
}`}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
@@ -65,8 +65,10 @@ export function UserProgressModal(props: ProgressMapProps) {
|
|||||||
let resourceJsonUrl = import.meta.env.DEV
|
let resourceJsonUrl = import.meta.env.DEV
|
||||||
? 'http://localhost:3000'
|
? 'http://localhost:3000'
|
||||||
: 'https://roadmap.sh';
|
: 'https://roadmap.sh';
|
||||||
if (resourceType === 'roadmap') {
|
if (resourceType === 'roadmap' && renderer === 'balsamiq') {
|
||||||
resourceJsonUrl += `/${resourceId}.json`;
|
resourceJsonUrl += `/${resourceId}.json`;
|
||||||
|
} else if (resourceType === 'roadmap' && renderer === 'editor') {
|
||||||
|
resourceJsonUrl = `${import.meta.env.PUBLIC_API_URL}/v1-official-roadmap/${resourceId}`;
|
||||||
} else {
|
} else {
|
||||||
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
resourceJsonUrl += `/best-practices/${resourceId}.json`;
|
||||||
}
|
}
|
||||||
|
@@ -86,7 +86,7 @@ export function UserProfileRoadmapRenderer(
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-white',
|
'bg-white',
|
||||||
isCustomResource ? 'w-full' : 'container relative max-w-[1000px]!',
|
isCustomResource ? 'w-full' : 'relative container max-w-[1000px]!',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isCustomResource ? (
|
{isCustomResource ? (
|
||||||
@@ -136,7 +136,7 @@ export function UserProfileRoadmapRenderer(
|
|||||||
<div className="flex w-full justify-center">
|
<div className="flex w-full justify-center">
|
||||||
<Spinner
|
<Spinner
|
||||||
isDualRing={false}
|
isDualRing={false}
|
||||||
className="mb-4 mt-2 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
className="mt-2 mb-4 h-4 w-4 animate-spin fill-blue-600 text-gray-200 sm:h-8 sm:w-8"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
23
src/queries/official-roadmap.ts
Normal file
23
src/queries/official-roadmap.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
import { httpGet } from '../lib/query-http';
|
||||||
|
|
||||||
|
export interface OfficialRoadmapDocument {
|
||||||
|
_id: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
slug: string;
|
||||||
|
nodes: any[];
|
||||||
|
edges: any[];
|
||||||
|
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function officialRoadmapOptions(slug: string) {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: ['official-roadmap', slug],
|
||||||
|
queryFn: () => {
|
||||||
|
return httpGet<OfficialRoadmapDocument>(`/v1-official-roadmap/${slug}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user