Resolve merge conflicts
This commit is contained in:
84
__tests__/path-map.spec.js
Normal file
84
__tests__/path-map.spec.js
Normal file
@ -0,0 +1,84 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const guides = require('../data/guides');
|
||||
const roadmaps = require('../data/roadmaps');
|
||||
|
||||
const {
|
||||
getPageRoutes,
|
||||
getGuideRoutes,
|
||||
getRoadmapRoutes
|
||||
} = require("../path-map");
|
||||
|
||||
describe("Build scripts tests", () => {
|
||||
test('it should generate valid pathmap for pages', () => {
|
||||
const pageRoutes = getPageRoutes();
|
||||
|
||||
expect(pageRoutes).toEqual({
|
||||
'/': { page: '/index' },
|
||||
'/about': { page: '/about' },
|
||||
'/privacy': { page: '/privacy' },
|
||||
'/terms': { page: '/terms' },
|
||||
'/guides': { page: '/guides/index' },
|
||||
'/roadmaps': { page: '/roadmaps' },
|
||||
});
|
||||
});
|
||||
|
||||
test('it should generate valid guides pathmap', () => {
|
||||
const expectedGuideRoutes = guides.reduce((acc, guide) => {
|
||||
const [,, slug] = guide.url.split('/');
|
||||
return {
|
||||
...acc,
|
||||
[guide.url]: {
|
||||
page: '/guides/[guide]',
|
||||
query: slug,
|
||||
}
|
||||
};
|
||||
}, {});
|
||||
|
||||
// Valid path map is generated
|
||||
expect(expectedGuideRoutes).toEqual(getGuideRoutes());
|
||||
|
||||
const pageFilePath = path.join(__dirname, '../pages/guides/[guide].js');
|
||||
const foundFilePath = fs.existsSync(pageFilePath) ? pageFilePath : '';
|
||||
|
||||
// Given page component exists
|
||||
expect(foundFilePath).toEqual(pageFilePath);
|
||||
});
|
||||
|
||||
test('it should have markdown file for each guide', () => {
|
||||
guides.forEach(guide => {
|
||||
const [,, slug] = guide.url.split('/');
|
||||
|
||||
const expectedFile = path.join(__dirname, `../data/guides/${slug}.md`);
|
||||
const foundFile = fs.existsSync(expectedFile) ? expectedFile : '';
|
||||
|
||||
expect(foundFile).toEqual(expectedFile);
|
||||
})
|
||||
});
|
||||
|
||||
test('it should generate valid roadmap routes', () => {
|
||||
const expectedPathMap = roadmaps.reduce((roadmapAcc, roadmap) => {
|
||||
// Routes for each of the versions of this roadmap
|
||||
const versionRoutes = roadmap.versions.reduce((versionAcc, version) => ({
|
||||
...versionAcc,
|
||||
[`${roadmap.url}/${version}`]: {
|
||||
page: '/[roadmap]/[version]',
|
||||
query: `${roadmap.url.split('/')[1]}/${version}`,
|
||||
}
|
||||
}), {});
|
||||
|
||||
// Route for the route roadmap itself
|
||||
return {
|
||||
...roadmapAcc,
|
||||
[roadmap.url]: {
|
||||
page: '/[roadmap]/index',
|
||||
query: roadmap.url.split('/')[1]
|
||||
},
|
||||
// Expected roadmap for versions
|
||||
...versionRoutes
|
||||
};
|
||||
}, {});
|
||||
|
||||
expect(getRoadmapRoutes()).toEqual(expectedPathMap);
|
||||
})
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import { FeaturedContentWrap } from './style';
|
||||
import guides from '../../data/guides';
|
||||
import GuideBlock from '../guide-block';
|
||||
import guides from 'data/guides';
|
||||
import GuideBlock from 'components/guide-block';
|
||||
|
||||
const FeaturedGuides = () => (
|
||||
<FeaturedContentWrap className="featured-content-wrap">
|
||||
@ -16,11 +16,11 @@ const FeaturedGuides = () => (
|
||||
{ guides
|
||||
.filter(({ featured }) => featured)
|
||||
.map(guide => (
|
||||
<GuideBlock guide={ guide } key={ guide.slug } />
|
||||
<GuideBlock guide={ guide } key={ guide.url } />
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</FeaturedContentWrap>
|
||||
);
|
||||
|
||||
export default FeaturedGuides;
|
||||
export default FeaturedGuides;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { FeaturedWrap } from './style';
|
||||
import FeaturedJourneys from './journeys';
|
||||
import FeaturedGuides from './guides';
|
||||
import FeaturedRoadmaps from './roadmaps';
|
||||
|
||||
@ -14,4 +13,4 @@ FeaturedContent.defaultProps = {
|
||||
className: '',
|
||||
};
|
||||
|
||||
export default FeaturedContent;
|
||||
export default FeaturedContent;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import { FeaturedContentWrap } from './style';
|
||||
import roadmaps from '../../data/roadmaps';
|
||||
import RoadmapBlock from '../roadmap-block';
|
||||
import roadmaps from 'data/roadmaps';
|
||||
import RoadmapBlock from 'components/roadmap-block';
|
||||
|
||||
const FeaturedRoadmaps = () => (
|
||||
<FeaturedContentWrap className="featured-content-wrap">
|
||||
@ -21,11 +21,11 @@ const FeaturedRoadmaps = () => (
|
||||
{ roadmaps
|
||||
.filter(({ featured }) => featured)
|
||||
.map(roadmap => (
|
||||
<RoadmapBlock roadmap={ roadmap } key={ roadmap.slug } />
|
||||
<RoadmapBlock roadmap={ roadmap } key={ roadmap.url } />
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</FeaturedContentWrap>
|
||||
);
|
||||
|
||||
export default FeaturedRoadmaps;
|
||||
export default FeaturedRoadmaps;
|
||||
|
@ -1,12 +1,14 @@
|
||||
import Link from 'next/link';
|
||||
import formatDate from 'date-fns/format'
|
||||
|
||||
import { Author, AuthorImage, AuthorName, BlockLink, BlockMeta, BlockSubtitle, BlockTitle, PublishDate } from './style';
|
||||
import { findByUsername } from '../../lib/author';
|
||||
import { findByUsername } from 'lib/author';
|
||||
|
||||
const GuideBlock = ({ guide }) => {
|
||||
const author = findByUsername(guide.author) || {};
|
||||
return (
|
||||
<div className="col-xl-4 col-lg-4 col-md-6 col-sm-12 col-12 grid-item-container">
|
||||
<Link href={ guide.slug } passHref>
|
||||
<Link href={ guide.url } passHref>
|
||||
<BlockLink>
|
||||
<BlockTitle>{ guide.title }</BlockTitle>
|
||||
<BlockSubtitle>{ guide.featuredDescription || guide.description }</BlockSubtitle>
|
||||
@ -15,7 +17,7 @@ const GuideBlock = ({ guide }) => {
|
||||
<AuthorImage src={ author.picture } />
|
||||
<AuthorName>{ author.name }</AuthorName>
|
||||
</Author>
|
||||
<PublishDate>{ guide.updatedAt }</PublishDate>
|
||||
<PublishDate>{ formatDate(new Date(guide.createdAt), 'MMMM d, yyyy') }</PublishDate>
|
||||
</BlockMeta>
|
||||
</BlockLink>
|
||||
</Link>
|
||||
@ -23,4 +25,4 @@ const GuideBlock = ({ guide }) => {
|
||||
)
|
||||
};
|
||||
|
||||
export default GuideBlock;
|
||||
export default GuideBlock;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import MdxComponents from '../mdx-components';
|
||||
import MdxComponents from 'components/mdx-components';
|
||||
import { GuideBodyWrap } from './style';
|
||||
|
||||
const GuideBody = (props) => (
|
||||
@ -10,4 +10,4 @@ const GuideBody = (props) => (
|
||||
</MDXProvider>
|
||||
);
|
||||
|
||||
export default GuideBody;
|
||||
export default GuideBody;
|
||||
|
@ -5,4 +5,5 @@ export const GuideBodyWrap = styled.div`
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
`;
|
||||
min-height: 300px;
|
||||
`;
|
||||
|
@ -1,22 +1,46 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFacebookSquare, faGithub, faHackerNewsSquare, faRedditSquare, faTwitter, faTwitterSquare } from '@fortawesome/free-brands-svg-icons'
|
||||
import { AuthorBio, AuthorImg, AuthorInfoWrap, AuthorMeta, ContributeIcon, FooterBg, FooterContainer, FooterWrap, ShareIcons, ShareWrap } from './style';
|
||||
import { getContributionUrl } from "lib/guide";
|
||||
import {
|
||||
getTwitterUrl,
|
||||
getTwitterShareUrl,
|
||||
getFacebookShareUrl,
|
||||
getRedditShareUrl,
|
||||
getHnShareUrl
|
||||
} from "lib/url";
|
||||
import {
|
||||
AuthorBio,
|
||||
AuthorImg,
|
||||
AuthorInfoWrap,
|
||||
AuthorMeta,
|
||||
ContributeIcon,
|
||||
FooterBg,
|
||||
FooterContainer,
|
||||
FooterWrap,
|
||||
ShareIcons,
|
||||
ShareWrap
|
||||
} from './style';
|
||||
|
||||
|
||||
const GuideFooter = (props) => (
|
||||
const GuideFooter = ({
|
||||
guide,
|
||||
guide: {
|
||||
author = {}
|
||||
} = {}
|
||||
}) => (
|
||||
<FooterWrap>
|
||||
<FooterBg className="border-top">
|
||||
<FooterContainer>
|
||||
<ShareWrap>
|
||||
<ContributeIcon>
|
||||
<a href="#">
|
||||
<a href={ getContributionUrl(guide) } target="_blank">
|
||||
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Improve this Guide </span>
|
||||
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Contribute </span>
|
||||
<FontAwesomeIcon icon={faGithub}/>
|
||||
</a>
|
||||
</ContributeIcon>
|
||||
<ContributeIcon hasMargins>
|
||||
<a href="#">
|
||||
<a href={ getTwitterUrl(author.twitter) } target="_blank">
|
||||
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Follow the author </span>
|
||||
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Author </span>
|
||||
<FontAwesomeIcon icon={faTwitter}/>
|
||||
@ -25,10 +49,18 @@ const GuideFooter = (props) => (
|
||||
<ShareIcons>
|
||||
<span className="d-none d-sm-none d-md-none d-lg-inline d-xl-inline">Help spread the word</span>
|
||||
<span className="d-inline d-sm-inline d-md-inline d-lg-none d-xl-none">Share</span>
|
||||
<a href="#"><FontAwesomeIcon icon={faTwitterSquare}/></a>
|
||||
<a href="#"><FontAwesomeIcon icon={faFacebookSquare}/></a>
|
||||
<a href="#"><FontAwesomeIcon icon={faRedditSquare}/></a>
|
||||
<a href="#"><FontAwesomeIcon icon={faHackerNewsSquare}/></a>
|
||||
<a href={ getTwitterShareUrl({ text: `${guide.title} by @${author.twitter}`, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faTwitterSquare}/>
|
||||
</a>
|
||||
<a href={ getFacebookShareUrl({ text: guide.title, url: guide.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={faFacebookSquare}/>
|
||||
</a>
|
||||
<a href={ getRedditShareUrl({ text: guide.title, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faRedditSquare}/>
|
||||
</a>
|
||||
<a href={ getHnShareUrl({ text: guide.title, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faHackerNewsSquare}/>
|
||||
</a>
|
||||
</ShareIcons>
|
||||
</ShareWrap>
|
||||
</FooterContainer>
|
||||
@ -37,10 +69,10 @@ const GuideFooter = (props) => (
|
||||
<FooterBg className="border-top">
|
||||
<FooterContainer>
|
||||
<AuthorInfoWrap>
|
||||
<AuthorImg src="/static/authors/kamranahmedse.jpeg" alt=""/>
|
||||
<AuthorImg src={ author.picture } alt={ author.name }/>
|
||||
<AuthorMeta>
|
||||
<h4><a href="#">Kamran Ahmed</a></h4>
|
||||
<AuthorBio>Lead engineer at Tajawal. Lover of all things web and opensource. Created roadmap.sh to help the confused ones.</AuthorBio>
|
||||
<h4><a href={ getTwitterUrl(author.twitter) } target="_blank">{ author.name }</a></h4>
|
||||
<AuthorBio>{ author.bio }</AuthorBio>
|
||||
</AuthorMeta>
|
||||
</AuthorInfoWrap>
|
||||
</FooterContainer>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import formatDate from 'date-fns/format'
|
||||
import {
|
||||
ActionItems,
|
||||
AuthorImage,
|
||||
@ -9,21 +10,28 @@ import {
|
||||
GuideTitle,
|
||||
HeaderWrap,
|
||||
} from './style';
|
||||
import { getContributionUrl } from "lib/guide";
|
||||
import { getTwitterUrl } from "lib/url";
|
||||
|
||||
const GuideHeader = (props) => (
|
||||
const GuideHeader = ({
|
||||
guide,
|
||||
guide: {
|
||||
author = {}
|
||||
} = {}
|
||||
}) => (
|
||||
<HeaderWrap className="border-bottom">
|
||||
<GuideMeta>
|
||||
<GuideAuthor href="https://github.com/kamranahmedse" target="_blank">
|
||||
<AuthorImage src="/static/authors/kamranahmedse.jpeg" />
|
||||
Kamran Ahmed
|
||||
<GuideAuthor href={ getTwitterUrl(author.twitter) } target="_blank">
|
||||
<AuthorImage src={ author.picture } />
|
||||
{ author.name }
|
||||
</GuideAuthor>
|
||||
·
|
||||
<GuideDate>Wednesday, October 9th 2019</GuideDate>
|
||||
<GuideDate>{ formatDate(new Date(guide.createdAt), 'EEEE, MMMM d yyyy') }</GuideDate>
|
||||
·
|
||||
<EditGuide href="#">Improve this Guide</EditGuide>
|
||||
<EditGuide href={ getContributionUrl(guide) } target="_blank">Improve this Guide</EditGuide>
|
||||
</GuideMeta>
|
||||
<GuideTitle>Design Patterns for Humans</GuideTitle>
|
||||
<GuideSubtitle>An ultra-simplified explanation to design patterns</GuideSubtitle>
|
||||
<GuideTitle>{ guide.title }</GuideTitle>
|
||||
<GuideSubtitle>{ guide.description }</GuideSubtitle>
|
||||
<ActionItems>
|
||||
</ActionItems>
|
||||
</HeaderWrap>
|
||||
|
@ -3,7 +3,7 @@ import { BlockLink, BlockSubtitle, BlockTitle } from './style';
|
||||
|
||||
const RoadmapBlock = ({ roadmap }) => (
|
||||
<div className="col-xl-4 col-lg-4 col-md-6 col-sm-12 col-12 grid-item-container">
|
||||
<Link href={ roadmap.slug } passHref>
|
||||
<Link href={ roadmap.url } passHref>
|
||||
<BlockLink>
|
||||
<BlockTitle>{ roadmap.title }</BlockTitle>
|
||||
<BlockSubtitle>{ roadmap.featuredDescription || roadmap.description }</BlockSubtitle>
|
||||
@ -12,4 +12,4 @@ const RoadmapBlock = ({ roadmap }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
export default RoadmapBlock;
|
||||
export default RoadmapBlock;
|
||||
|
@ -24,7 +24,7 @@ const RoadmapSummary = ({ roadmap }) => (
|
||||
<Description>{ roadmap.description }</Description>
|
||||
<VersionList className="border-bottom">
|
||||
{ (roadmap.versions || []).map(versionItem => (
|
||||
<Link href={ `${roadmap.slug}/${versionItem}` } passHref key={ versionItem }>
|
||||
<Link href={ `${roadmap.url}/${versionItem}` } passHref key={ versionItem }>
|
||||
<VersionLink className={ classNames({
|
||||
active: isActiveRoadmap(versionItem, roadmap.version),
|
||||
}) }>{ versionItem } Version</VersionLink>
|
||||
@ -40,4 +40,4 @@ const RoadmapSummary = ({ roadmap }) => (
|
||||
</SummaryContainer>
|
||||
);
|
||||
|
||||
export default RoadmapSummary;
|
||||
export default RoadmapSummary;
|
||||
|
@ -1,16 +1,34 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFacebookSquare, faRedditSquare, faTwitterSquare } from '@fortawesome/free-brands-svg-icons'
|
||||
|
||||
import { getFacebookShareUrl, getRedditShareUrl, getTwitterShareUrl } from "lib/url";
|
||||
import { ShareIcon, ShareIconsList, ShareWrap } from './style';
|
||||
|
||||
const ShareGuide = (props) => (
|
||||
const ShareGuide = ({
|
||||
guide,
|
||||
guide: {
|
||||
author = {}
|
||||
} = {}
|
||||
}) => (
|
||||
<ShareWrap>
|
||||
<ShareIconsList className="d-sm-none d-md-none d-lg-flex d-xl-flex">
|
||||
<ShareIcon><a href="#"><FontAwesomeIcon icon={ faTwitterSquare } /></a></ShareIcon>
|
||||
<ShareIcon><a href="#"><FontAwesomeIcon icon={ faFacebookSquare } /></a></ShareIcon>
|
||||
<ShareIcon><a href="#"><FontAwesomeIcon icon={ faRedditSquare } /></a></ShareIcon>
|
||||
<ShareIcon>
|
||||
<a href={ getTwitterShareUrl({ text: `${guide.title} by @${author.twitter}`, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={ faTwitterSquare } />
|
||||
</a>
|
||||
</ShareIcon>
|
||||
<ShareIcon>
|
||||
<a href={ getFacebookShareUrl({ text: guide.title, url: guide.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={ faFacebookSquare } />
|
||||
</a>
|
||||
</ShareIcon>
|
||||
<ShareIcon>
|
||||
<a href={ getRedditShareUrl({ text: guide.title, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={ faRedditSquare } />
|
||||
</a>
|
||||
</ShareIcon>
|
||||
</ShareIconsList>
|
||||
</ShareWrap>
|
||||
);
|
||||
|
||||
export default ShareGuide;
|
||||
export default ShareGuide;
|
||||
|
@ -2,16 +2,22 @@
|
||||
{
|
||||
"username": "kamranahmedse",
|
||||
"name": "Kamran Ahmed",
|
||||
"picture": "/static/authors/kamranahmedse.jpeg"
|
||||
"twitter": "kamranahmedse",
|
||||
"picture": "/static/authors/kamranahmedse.jpeg",
|
||||
"bio": "Lead engineer at Tajawal. Lover of all things web and opensource. Created roadmap.sh to help the confused ones."
|
||||
},
|
||||
{
|
||||
"username": "ziishaned",
|
||||
"name": "Zeeshan Ahmed",
|
||||
"picture": "/static/authors/ziishaned.png"
|
||||
"twitter": "ziishaned",
|
||||
"picture": "/static/authors/ziishaned.png",
|
||||
"bio": "Playing with computers ever since I started walking. Loves hiking, cycling, JavaScript, OpenSource, challenging myself and communities."
|
||||
},
|
||||
{
|
||||
"username": "idnan",
|
||||
"name": "Adnan Ahmed",
|
||||
"picture": "/static/authors/idnan.jpeg"
|
||||
"twitter": "idnan_se",
|
||||
"picture": "/static/authors/idnan.jpeg",
|
||||
"bio": "Playing with computers ever since I started walking. Loves hiking, cycling, JavaScript, OpenSource, challenging myself and communities."
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -2,55 +2,55 @@
|
||||
{
|
||||
"title": "Design Patterns for Humans",
|
||||
"description": "A language agnostic, ultra-simplified explanation to design patterns",
|
||||
"slug": "/guides/design-patterns-for-humans",
|
||||
"url": "/guides/design-patterns-for-humans",
|
||||
"featured": true,
|
||||
"author": "kamranahmedse",
|
||||
"createdAt": "June 12, 2019",
|
||||
"updatedAt": "June 12, 2019"
|
||||
"createdAt": "2019-01-23T17:00:00.860Z",
|
||||
"updatedAt": "2019-01-23T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"title": "Learn Regex",
|
||||
"description": "An easy to understand guide on regular expressions with real world examples",
|
||||
"slug": "/guides/learn-regex",
|
||||
"url": "/guides/learn-regex",
|
||||
"featured": true,
|
||||
"author": "ziishaned",
|
||||
"createdDate": "June 19, 2019",
|
||||
"updatedAt": "June 19, 2019"
|
||||
"createdAt": "2019-01-23T17:00:00.860Z",
|
||||
"updatedAt": "2019-01-23T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"title": "Bash Guide",
|
||||
"description": "Easy to understand guide for bash with real world usage examples.",
|
||||
"slug": "/guides/bash-guide",
|
||||
"url": "/guides/bash-guide",
|
||||
"featured": true,
|
||||
"author": "idnan",
|
||||
"createdAt": "May 18, 2018",
|
||||
"updatedAt": "May 18, 2018"
|
||||
"createdAt": "2019-01-23T17:00:00.860Z",
|
||||
"updatedAt": "2019-01-23T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"title": "DNS in One Picture",
|
||||
"description": "Quick illustrative guide on how a website is found on the internet.",
|
||||
"slug": "/guides/dns-in-one-picture",
|
||||
"url": "/guides/dns-in-one-picture",
|
||||
"featured": true,
|
||||
"author": "kamranahmedse",
|
||||
"createdAt": "May 11, 2018",
|
||||
"updatedAt": "May 11, 2018"
|
||||
"createdAt": "2019-11-01T12:00:00.860Z",
|
||||
"updatedAt": "2019-11-01T12:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"title": "Using React Hooks",
|
||||
"description": "Start using React hooks in your react applications today with this guide.",
|
||||
"slug": "/guides/using-react-hooks",
|
||||
"url": "/guides/using-react-hooks",
|
||||
"featured": true,
|
||||
"author": "kamranahmedse",
|
||||
"createdAt": "October 22, 2019",
|
||||
"updatedAt": "October 22, 2019"
|
||||
"createdAt": "2019-01-23T17:00:00.860Z",
|
||||
"updatedAt": "2019-01-23T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"title": "HTTP Caching",
|
||||
"description": "Everything you need to know about web caching",
|
||||
"slug": "/guides/http-caching",
|
||||
"url": "/guides/http-caching",
|
||||
"featured": true,
|
||||
"author": "kamranahmedse",
|
||||
"updatedAt": "November 01, 2019",
|
||||
"createdAt": "November 01, 2019"
|
||||
"updatedAt": "2019-10-09T12:00:00.860Z",
|
||||
"createdAt": "2019-01-23T17:00:00.860Z"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
0
data/guides/bash-guide.md
Normal file
0
data/guides/bash-guide.md
Normal file
0
data/guides/dns-in-one-picture.md
Normal file
0
data/guides/dns-in-one-picture.md
Normal file
0
data/guides/http-caching.md
Normal file
0
data/guides/http-caching.md
Normal file
0
data/guides/using-react-hooks.md
Normal file
0
data/guides/using-react-hooks.md
Normal file
@ -3,7 +3,7 @@
|
||||
"title": "Frontend Developer",
|
||||
"description": "Step by step guide to becoming a modern frontend developer",
|
||||
"featuredDescription": "Step by step guide to becoming a modern frontend developer in 2019",
|
||||
"slug": "/roadmaps/frontend",
|
||||
"url": "/frontend",
|
||||
"picture": "/static/roadmaps/{version}/frontend.png",
|
||||
"featured": true,
|
||||
"versions": [
|
||||
@ -16,7 +16,7 @@
|
||||
"title": "Backend Developer",
|
||||
"description": "Step by step guide to becoming a modern backend developer",
|
||||
"featuredDescription": "Step by step guide to becoming a modern backend developer in 2019",
|
||||
"slug": "/roadmaps/backend",
|
||||
"url": "/backend",
|
||||
"picture": "/static/roadmaps/{version}/backend.png",
|
||||
"featured": true,
|
||||
"versions": [
|
||||
@ -29,7 +29,7 @@
|
||||
"title": "DevOps Roadmap",
|
||||
"description": "Step by step guide for DevOps or any other Operations Role",
|
||||
"featuredDescription": "Step by step guide to become an SRE or for any operations role in 2019",
|
||||
"slug": "/roadmaps/devops",
|
||||
"url": "/devops",
|
||||
"picture": "/static/roadmaps/{version}/devops.png",
|
||||
"featured": true,
|
||||
"versions": [
|
||||
@ -38,4 +38,4 @@
|
||||
"2017"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
6
data/site.json
Normal file
6
data/site.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"twitter": "roadmapsh",
|
||||
"url": "https://roadmap.sh",
|
||||
"repoUrl": "https://github.com/kamranahmedse/roadmap-next",
|
||||
"dataUrl": "/tree/master/data"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import DefaultLayout from '../default';
|
||||
import PageHeader from '../../components/page-header';
|
||||
import PageFooter from '../../components/page-footer';
|
||||
import DefaultLayout from 'layouts/default';
|
||||
import PageHeader from 'components/page-header';
|
||||
import PageFooter from 'components/page-footer';
|
||||
|
||||
class GuideLayout extends React.Component {
|
||||
render() {
|
||||
@ -17,4 +17,4 @@ class GuideLayout extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default GuideLayout;
|
||||
export default GuideLayout;
|
||||
|
@ -1,3 +1,3 @@
|
||||
import authors from "../data/authors";
|
||||
import authors from "data/authors";
|
||||
|
||||
export const findByUsername = (username) => authors.find(author => author.username === username) || {};
|
||||
export const findByUsername = (username) => authors.find(author => author.username === username) || {};
|
||||
|
32
lib/guide.js
Normal file
32
lib/guide.js
Normal file
@ -0,0 +1,32 @@
|
||||
import guides from "data/guides";
|
||||
import authors from "data/authors";
|
||||
import siteConfig from "data/site";
|
||||
|
||||
|
||||
export const getRequestedGuide = req => {
|
||||
const guide = guides.find(guide => guide.url === req.url);
|
||||
if (!guide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We will use this URL format to find the relevant markdown
|
||||
// file inside the `/data` directory. For example `/guides/learn-regex`
|
||||
// has to have `/guides/learn-regex.md` file inside the `data` directory
|
||||
const path = guide.url.replace(/^\//, '');
|
||||
|
||||
try {
|
||||
return {
|
||||
...guide,
|
||||
author: authors.find(author => author.username === guide.author) || {},
|
||||
component: require(`../data/${path}.md`).default,
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getContributionUrl = (guide) => {
|
||||
return `${siteConfig.repoUrl}${siteConfig.dataUrl}${guide.url}.md`
|
||||
};
|
@ -1,25 +1,20 @@
|
||||
import roadmaps from "../data/roadmaps";
|
||||
import roadmaps from "data/roadmaps";
|
||||
|
||||
export const getRequestedRoadmap = req => {
|
||||
// Considering it a new roadmap URL e.g. `/roadmaps/frontend`
|
||||
const currentUrl = req.url.replace(/\/$/, '');
|
||||
// Considering it a legacy URL e.g. converting `/frontend` to `roadmap.sh/roadmaps/frontend`
|
||||
const legacyUrl = `/roadmaps${currentUrl}`;
|
||||
// Get the roadmap version out of the URL e.g. `/roadmaps/frontend/2019`
|
||||
const [foundVersion = ''] = currentUrl.match(/(\d+|latest)$/) || ['latest'];
|
||||
const foundVersionRegex = new RegExp(`\/?${foundVersion}$`);
|
||||
// Remove version from the URL because slugs in roadmaps list don't have versions
|
||||
const newUrlWithoutVersion = currentUrl.replace(foundVersionRegex, '');
|
||||
const legacyUrlWithoutVersion = legacyUrl.replace(foundVersionRegex, '');
|
||||
// Remove version from the URL because urls in roadmaps list don't have versions
|
||||
const urlWithoutVersion = currentUrl.replace(foundVersionRegex, '');
|
||||
|
||||
const urlToSlugList = [
|
||||
currentUrl,
|
||||
legacyUrl,
|
||||
newUrlWithoutVersion,
|
||||
legacyUrlWithoutVersion,
|
||||
urlWithoutVersion,
|
||||
];
|
||||
|
||||
const foundRoadmap = roadmaps.find(roadmap => urlToSlugList.includes(roadmap.slug));
|
||||
const foundRoadmap = roadmaps.find(roadmap => urlToSlugList.includes(roadmap.url));
|
||||
if (!foundRoadmap) {
|
||||
return null;
|
||||
}
|
||||
@ -29,4 +24,4 @@ export const getRequestedRoadmap = req => {
|
||||
version: foundVersion,
|
||||
picture: (foundRoadmap.picture || '').replace('{version}', foundVersion),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -6,9 +6,10 @@
|
||||
export const serverOnlyProps = (callback) => {
|
||||
return async (props) => {
|
||||
if (process.browser) {
|
||||
// noinspection ES6ModulesDependencies,JSUnresolvedVariable
|
||||
return __NEXT_DATA__.props.pageProps;
|
||||
}
|
||||
|
||||
return await callback(props)
|
||||
};
|
||||
};
|
||||
};
|
||||
|
50
lib/url.js
Normal file
50
lib/url.js
Normal file
@ -0,0 +1,50 @@
|
||||
import queryString from 'query-string';
|
||||
import siteConfig from 'data/site';
|
||||
|
||||
export const prefixHost = (url) => {
|
||||
return /^\//.test(url) ? `${siteConfig.url}${url}` : url;
|
||||
};
|
||||
|
||||
export const getTwitterUrl = (username) => {
|
||||
return `https://twitter.com/${username}`;
|
||||
};
|
||||
|
||||
export const getTwitterShareUrl = ({ text, url }) => {
|
||||
const urlToShare = `${prefixHost(url)}?${queryString.stringify({
|
||||
utm_source: 'roadmap.sh',
|
||||
utm_campaign: 'share',
|
||||
utm_medium: 'twitter',
|
||||
})}`;
|
||||
|
||||
return `https://twitter.com/intent/tweet?text=${text}&url=${encodeURI(urlToShare)}`;
|
||||
};
|
||||
|
||||
export const getFacebookShareUrl = ({ text, url }) => {
|
||||
const urlToShare = `${prefixHost(url)}?${queryString.stringify({
|
||||
utm_source: 'roadmap.sh',
|
||||
utm_campaign: 'share',
|
||||
utm_medium: 'facebook',
|
||||
})}`;
|
||||
|
||||
return `https://www.facebook.com/sharer/sharer.php?quote=${text}&u=${encodeURI(urlToShare)}`;
|
||||
};
|
||||
|
||||
export const getRedditShareUrl = ({ text, url }) => {
|
||||
const urlToShare = `${prefixHost(url)}?${queryString.stringify({
|
||||
utm_source: 'roadmap.sh',
|
||||
utm_campaign: 'share',
|
||||
utm_medium: 'reddit'
|
||||
})}`;
|
||||
|
||||
return `https://www.reddit.com/submit?title=${text}&url=${encodeURI(urlToShare)}`;
|
||||
};
|
||||
|
||||
export const getHnShareUrl = ({ text, url }) => {
|
||||
const urlToShare = `${prefixHost(url)}?${queryString.stringify({
|
||||
utm_source: 'roadmap.sh',
|
||||
utm_campaign: 'share',
|
||||
utm_medium: 'hn'
|
||||
})}`;
|
||||
|
||||
return `https://news.ycombinator.com/submitlink?t=${text}&u=${urlToShare}`;
|
||||
};
|
@ -1,6 +1,12 @@
|
||||
const path = require('path');
|
||||
const withSass = require('@zeit/next-sass');
|
||||
const withCSS = require('@zeit/next-css');
|
||||
const rehypePrism = require('@mapbox/rehype-prism')
|
||||
const rehypePrism = require('@mapbox/rehype-prism');
|
||||
const {
|
||||
getPageRoutes,
|
||||
getGuideRoutes,
|
||||
getRoadmapRoutes,
|
||||
} = require("./path-map");
|
||||
|
||||
const withMDX = require('@next/mdx')({
|
||||
extension: /\.(md|mdx)?$/,
|
||||
@ -11,20 +17,10 @@ const withMDX = require('@next/mdx')({
|
||||
|
||||
const options = {
|
||||
exportPathMap: () => {
|
||||
// @todo make it dynamic for pages, authors and guides
|
||||
return {
|
||||
'/': { page: '/' },
|
||||
'/about': { page: '/about' },
|
||||
'/privacy': { page: '/privacy' },
|
||||
'/terms': { page: '/terms' },
|
||||
'/roadmaps': { page: '/roadmaps' },
|
||||
'/guides': { page: '/guides' },
|
||||
'/frontend': { page: '/[fallback]', query: "frontend" },
|
||||
'/backend': { page: '/[fallback]', query: "backend" },
|
||||
'/devops': { page: '/[fallback]', query: "devops" },
|
||||
'/roadmaps/frontend': { page: '/roadmaps/[roadmap]', query: "frontend" },
|
||||
'/roadmaps/backend': { page: '/roadmaps/[roadmap]', query: "backend" },
|
||||
'/roadmaps/devops': { page: '/roadmaps/[roadmap]', query: "devops" },
|
||||
...getPageRoutes(),
|
||||
...getGuideRoutes(),
|
||||
...getRoadmapRoutes(),
|
||||
};
|
||||
},
|
||||
|
||||
@ -38,6 +34,8 @@ const options = {
|
||||
use: ['@svgr/webpack'],
|
||||
});
|
||||
|
||||
config.resolve.modules.push(path.resolve('./'));
|
||||
|
||||
// Allow loading images
|
||||
config.module.rules.push({
|
||||
test: /\.(png|jpg|gif|eot|ttf|woff|woff2)$/,
|
||||
@ -58,4 +56,4 @@ let nextConfig = withSass(options);
|
||||
nextConfig = withCSS(nextConfig);
|
||||
nextConfig = withMDX(nextConfig);
|
||||
|
||||
module.exports = nextConfig;
|
||||
module.exports = nextConfig;
|
||||
|
10
package.json
10
package.json
@ -7,7 +7,9 @@
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"html": "next export"
|
||||
"html": "next export",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.22",
|
||||
@ -24,16 +26,20 @@
|
||||
"autoprefixer": "^9.6.1",
|
||||
"bootstrap": "^4.3.1",
|
||||
"classnames": "^2.2.6",
|
||||
"date-fns": "^2.6.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"next": "^9.0.4",
|
||||
"node-sass": "^4.12.0",
|
||||
"postcss-css-variables": "^0.13.0",
|
||||
"prism-themes": "^1.3.0",
|
||||
"query-string": "^6.8.3",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"styled-components": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-styled-components": "^1.10.6"
|
||||
"babel-plugin-styled-components": "^1.10.6",
|
||||
"glob": "^7.1.5",
|
||||
"jest": "^24.9.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
import OldRoadmap from './index';
|
||||
|
||||
export default OldRoadmap;
|
@ -1,22 +0,0 @@
|
||||
import Error from 'next/error';
|
||||
import Roadmap from '../roadmaps/[roadmap]/index';
|
||||
import { serverOnlyProps } from '../../lib/server';
|
||||
import { getRequestedRoadmap } from '../../lib/roadmap';
|
||||
|
||||
// Fallback page to handle the old roadmap pages implementation
|
||||
const OldRoadmap = ({ roadmap }) => {
|
||||
if (roadmap) {
|
||||
return <Roadmap roadmap={ roadmap } />
|
||||
}
|
||||
|
||||
return <Error statusCode={ 404 } />;
|
||||
};
|
||||
|
||||
OldRoadmap.getInitialProps = serverOnlyProps(({ req }) => {
|
||||
return {
|
||||
roadmap: getRequestedRoadmap(req),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
export default OldRoadmap;
|
@ -1,3 +1,3 @@
|
||||
import Roadmap from './index';
|
||||
|
||||
export default Roadmap;
|
||||
export default Roadmap;
|
@ -1,10 +1,10 @@
|
||||
import Error from 'next/error';
|
||||
import DefaultLayout from '../../../layouts/default';
|
||||
import { serverOnlyProps } from '../../../lib/server';
|
||||
import PageHeader from '../../../components/page-header';
|
||||
import PageFooter from '../../../components/page-footer';
|
||||
import { getRequestedRoadmap } from '../../../lib/roadmap';
|
||||
import RoadmapSummary from '../../../components/roadmap-summary';
|
||||
import DefaultLayout from 'layouts/default';
|
||||
import PageHeader from 'components/page-header';
|
||||
import PageFooter from 'components/page-footer';
|
||||
import RoadmapSummary from 'components/roadmap-summary';
|
||||
import { serverOnlyProps } from 'lib/server';
|
||||
import { getRequestedRoadmap } from 'lib/roadmap';
|
||||
|
||||
const Roadmap = ({ roadmap }) => {
|
||||
if (!roadmap) {
|
||||
@ -26,4 +26,4 @@ Roadmap.getInitialProps = serverOnlyProps(({ req }) => {
|
||||
};
|
||||
});
|
||||
|
||||
export default Roadmap;
|
||||
export default Roadmap;
|
@ -1,8 +1,8 @@
|
||||
import AboutHeader from '../components/about-header/index';
|
||||
import PageFooter from '../components/page-footer/index';
|
||||
import PageHeader from '../components/page-header/index';
|
||||
import DefaultLayout from '../layouts/default/index';
|
||||
import FaqList from '../components/faq-list/index';
|
||||
import AboutHeader from 'components/about-header/index';
|
||||
import PageFooter from 'components/page-footer/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
import FaqList from 'components/faq-list/index';
|
||||
|
||||
const About = () => (
|
||||
<DefaultLayout>
|
||||
@ -13,4 +13,4 @@ const About = () => (
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default About;
|
||||
export default About;
|
||||
|
@ -1,33 +1,32 @@
|
||||
import GuideLayout from '../../layouts/guide';
|
||||
import { serverOnlyProps } from '../../lib/server';
|
||||
|
||||
import GuideHeader from '../../components/guide-header';
|
||||
import GuideContent from '../../data/guides/keep-it-clean.md';
|
||||
import GuideBody from '../../components/guide-body';
|
||||
import ShareGuide from '../../components/share-guide';
|
||||
import GuideFooter from '../../components/guide-footer';
|
||||
import Error from "next/error";
|
||||
import GuideLayout from 'layouts/guide';
|
||||
import { serverOnlyProps } from 'lib/server';
|
||||
import GuideHeader from 'components/guide-header';
|
||||
import GuideBody from 'components/guide-body';
|
||||
import ShareGuide from 'components/share-guide';
|
||||
import GuideFooter from 'components/guide-footer';
|
||||
import { getRequestedGuide } from "lib/guide";
|
||||
|
||||
const Guide = ({ guide }) => {
|
||||
if (!guide) {
|
||||
return <Error statusCode={404} />
|
||||
}
|
||||
|
||||
return (
|
||||
<GuideLayout>
|
||||
<GuideHeader/>
|
||||
<GuideHeader guide={ guide } />
|
||||
<GuideBody>
|
||||
<GuideContent/>
|
||||
<ShareGuide/>
|
||||
<guide.component />
|
||||
<ShareGuide guide={ guide }/>
|
||||
</GuideBody>
|
||||
<GuideFooter/>
|
||||
<GuideFooter guide={ guide } />
|
||||
</GuideLayout>
|
||||
);
|
||||
};
|
||||
|
||||
Guide.getInitialProps = serverOnlyProps(({ req }) => {
|
||||
// Remove URL chunk to make it a slug e.g. /guides/some-guide-item to become `some-guide-item
|
||||
const slug = req.url
|
||||
.replace(/^\/*?guides\/*?/, '/')
|
||||
.replace(/\/*$/, '');
|
||||
|
||||
return {
|
||||
slug,
|
||||
guide: getRequestedGuide(req)
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import DefaultLayout from '../../layouts/default/index';
|
||||
import PageHeader from '../../components/page-header/index';
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
|
||||
const Roadmap = () => (
|
||||
<DefaultLayout>
|
||||
@ -10,4 +10,4 @@ const Roadmap = () => (
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Roadmap;
|
||||
export default Roadmap;
|
||||
|
@ -1,16 +0,0 @@
|
||||
import FeaturedContent from '../components/featured-content/index';
|
||||
import HeroSection from '../components/hero-section/index';
|
||||
import PageFooter from '../components/page-footer/index';
|
||||
import PageHeader from '../components/page-header/index';
|
||||
import DefaultLayout from '../layouts/default/index';
|
||||
|
||||
const Home = (props) => (
|
||||
<DefaultLayout>
|
||||
<PageHeader />
|
||||
<HeroSection />
|
||||
<FeaturedContent />
|
||||
<PageFooter />
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Home;
|
@ -1,10 +1,16 @@
|
||||
import Home from './home';
|
||||
import DefaultLayout from '../layouts/default';
|
||||
import FeaturedContent from 'components/featured-content/index';
|
||||
import HeroSection from 'components/hero-section/index';
|
||||
import PageFooter from 'components/page-footer/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
|
||||
const Index = () => (
|
||||
const Home = (props) => (
|
||||
<DefaultLayout>
|
||||
<Home />
|
||||
<PageHeader />
|
||||
<HeroSection />
|
||||
<FeaturedContent />
|
||||
<PageFooter />
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Index;
|
||||
export default Home;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PageHeader from '../components/page-header/index';
|
||||
import PageFooter from '../components/page-footer/index';
|
||||
import { TosPage } from '../components/tos-page/index';
|
||||
import DefaultLayout from '../layouts/default/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
import PageFooter from 'components/page-footer/index';
|
||||
import { TosPage } from 'components/tos-page/index';
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
|
||||
const Privacy = () => (
|
||||
<DefaultLayout>
|
||||
@ -109,4 +109,4 @@ const Privacy = () => (
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Privacy;
|
||||
export default Privacy;
|
||||
|
13
pages/roadmaps.js
Normal file
13
pages/roadmaps.js
Normal file
@ -0,0 +1,13 @@
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
|
||||
const RoadmapsList = () => (
|
||||
<DefaultLayout>
|
||||
<PageHeader />
|
||||
<div className="container">
|
||||
<p>Show all roadmaps here</p>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default RoadmapsList;
|
@ -1,13 +0,0 @@
|
||||
import DefaultLayout from '../../layouts/default/index';
|
||||
import PageHeader from '../../components/page-header/index';
|
||||
|
||||
const Roadmap = () => (
|
||||
<DefaultLayout>
|
||||
<PageHeader />
|
||||
<div className="container">
|
||||
<p>Show all roadmaps here</p>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Roadmap;
|
@ -1,7 +1,7 @@
|
||||
import PageFooter from '../components/page-footer/index';
|
||||
import PageHeader from '../components/page-header/index';
|
||||
import { TosPage } from '../components/tos-page/index';
|
||||
import DefaultLayout from '../layouts/default/index';
|
||||
import PageFooter from 'components/page-footer/index';
|
||||
import PageHeader from 'components/page-header/index';
|
||||
import { TosPage } from 'components/tos-page/index';
|
||||
import DefaultLayout from 'layouts/default/index';
|
||||
|
||||
const Terms = () => (
|
||||
<DefaultLayout>
|
||||
@ -162,4 +162,4 @@ const Terms = () => (
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Terms;
|
||||
export default Terms;
|
||||
|
87
path-map.js
Normal file
87
path-map.js
Normal file
@ -0,0 +1,87 @@
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
|
||||
const guides = require('./data/guides.json');
|
||||
const roadmaps = require('./data/roadmaps');
|
||||
|
||||
const PAGES_PATH = path.join(__dirname, 'pages');
|
||||
|
||||
/**
|
||||
* Generate the page routes from the page files inside `/pages`
|
||||
* directory. Gives the format understood by next
|
||||
* {
|
||||
* '/slug': { page: '/path/to-file' }
|
||||
* }
|
||||
*/
|
||||
const getPageRoutes = () => {
|
||||
const files = glob.sync(`${PAGES_PATH}/**/*.js`, {
|
||||
ignore: [
|
||||
'**/_*.js', // private non-page files e.g. _document.js
|
||||
'**/[[]*[]].js', // Ignore dynamic pages i.e. `page/[something].js` files
|
||||
'**/[[]*[]]/*.js', // Ignore files inside dynamic pages i.e. `[something]/abc.js`
|
||||
]
|
||||
});
|
||||
|
||||
const pageRoutes = {};
|
||||
files.forEach(file => {
|
||||
const pageName = file.replace(PAGES_PATH, '').replace('.js', '');
|
||||
const pagePath = pageName.replace('/index', '') || '/';
|
||||
|
||||
pageRoutes[pagePath] = { page: `${pageName}` }
|
||||
});
|
||||
|
||||
return pageRoutes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates routes for guide pages
|
||||
* @returns {*}
|
||||
*/
|
||||
const getGuideRoutes = () => {
|
||||
return guides.reduce((acc, guide) => {
|
||||
const [, , slug] = guide.url.split('/');
|
||||
return {
|
||||
...acc,
|
||||
[guide.url]: {
|
||||
page: '/guides/[guide]',
|
||||
query: slug,
|
||||
}
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates routes for each of the roadmap and it's respective versions
|
||||
* @returns {*}
|
||||
*/
|
||||
const getRoadmapRoutes = () => {
|
||||
return roadmaps.reduce((roadmapRoutes, roadmap) => {
|
||||
const [, slug] = roadmap.url.split('/');
|
||||
|
||||
return {
|
||||
...roadmapRoutes,
|
||||
// Default roadmap path i.e. `{ '/frontend': { page: '/[roadmap]/index', query: 'frontend' }`
|
||||
[roadmap.url]: {
|
||||
page: '/[roadmap]/index',
|
||||
query: slug
|
||||
},
|
||||
// Route for each of the versions of this roadmap i.e.
|
||||
// `{ '/frontend/2019': { page: '/[roadmap]/[version]', query: 'frontend/2019' } }`
|
||||
...(roadmap.versions.reduce((versionRoutes, version) => {
|
||||
return {
|
||||
...versionRoutes,
|
||||
[`${roadmap.url}/${version}`]: {
|
||||
page: '/[roadmap]/[version]',
|
||||
query: `${slug}/${version}`
|
||||
}
|
||||
};
|
||||
}, {})),
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getPageRoutes,
|
||||
getGuideRoutes,
|
||||
getRoadmapRoutes,
|
||||
};
|
Reference in New Issue
Block a user