refactor: monorepo setup with lerna (#243)

This commit is contained in:
Daniel Lautzenheiser
2022-12-15 13:44:49 -05:00
committed by GitHub
parent dac29fbf3c
commit 504d95c34f
706 changed files with 16571 additions and 142 deletions

View File

@ -0,0 +1,82 @@
import '../styles/globals.css';
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Prism from 'prismjs';
import { useEffect, useMemo, useState } from 'react';
import ColorModeContext from '../components/context/ColorModeContext';
import homepageData from '../lib/homepage';
import useCreateTheme from '../styles/theme';
import type { PaletteMode } from '@mui/material';
import type { AppProps } from 'next/app';
require('prismjs/components/prism-javascript');
require('prismjs/components/prism-typescript');
require('prismjs/components/prism-css');
require('prismjs/components/prism-jsx');
require('prismjs/components/prism-tsx');
require('prismjs/components/prism-yaml');
require('prismjs/components/prism-json');
require('prismjs/components/prism-toml');
require('prismjs/components/prism-markup-templating');
require('prismjs/components/prism-handlebars');
require('prismjs/components/prism-markdown');
function MyApp({ Component, pageProps }: AppProps) {
const [mode, setMode] = useState<PaletteMode>('dark');
const colorMode = useMemo(
() => ({
// The dark mode switch would invoke this method
toggleColorMode: () => {
const newMode = mode === 'light' ? 'dark' : 'light';
setMode(newMode);
localStorage.setItem('palette-mode', newMode);
},
}),
[mode],
);
useEffect(() => {
setMode(localStorage?.getItem('palette-mode') === 'light' ? 'light' : 'dark');
}, []);
const { asPath } = useRouter();
useEffect(() => {
Prism.highlightAll();
}, [asPath]);
// Update the theme only if the mode changes
const theme = useCreateTheme(mode);
return (
<>
<Head>
<meta charSet="utf-8" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png" />
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, viewport-fit=cover"
/>
<meta name="theme-color" content="#2e3034" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="description" content={homepageData.title} />
</Head>
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</ColorModeContext.Provider>
</>
);
}
export default MyApp;

View File

@ -0,0 +1,13 @@
import { Head, Html, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@ -0,0 +1,105 @@
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import CommunitySection from '../components/community/CommunitySection';
import Container from '../components/layout/Container';
import Page from '../components/layout/Page';
import communityData from '../lib/community';
import { getDocsMenuStaticProps } from '../lib/docs';
import type { DocsMenuProps } from '../lib/docs';
const StyledCommunityContent = styled('div')(
({ theme }) => `
width: 100%;
padding-top: 72px;
min-height: calc(100vh - 72px);
display: flex;
flex-direction: column;
gap: 80px;
${theme.breakpoints.between('md', 'lg')} {
padding-top: 48px;
gap: 56px
}
${theme.breakpoints.down('md')} {
padding-top: 32px;
gap: 40px
}
`,
);
const StyledTitle = styled('div')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 16px;
${theme.breakpoints.down('lg')} {
gap: 8px
}
`,
);
const StyledCommunityLinks = styled('section')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
padding: 64px 0 32px;
flex-grow: 1;
${theme.breakpoints.between('md', 'lg')} {
padding: 48px 0 32px;
}
${theme.breakpoints.down('md')} {
padding: 40px 0 32px;
}
`,
);
const StyledCommunityLinksContent = styled('div')`
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 40px;
`;
const Community = ({ docsGroups, searchablePages }: DocsMenuProps) => {
return (
<Page url="/community" docsGroups={docsGroups} searchablePages={searchablePages} fullWidth>
<StyledCommunityContent>
<Container>
<StyledTitle>
<Typography variant="h1" color="primary">
{communityData.title}
</Typography>
<Typography variant="h2" color="text.primary">
{communityData.subtitle}
</Typography>
</StyledTitle>
</Container>
<StyledCommunityLinks>
<Container>
<StyledCommunityLinksContent>
{communityData.sections.map(section => (
<CommunitySection key={section.title} section={section} />
))}
</StyledCommunityLinksContent>
</Container>
</StyledCommunityLinks>
</StyledCommunityContent>
</Page>
);
};
export default Community;
export const getStaticProps = getDocsMenuStaticProps;

View File

@ -0,0 +1,179 @@
import Alert from '@mui/material/Alert';
import { styled, useTheme } from '@mui/material/styles';
import { MDXRemote } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';
import remarkGfm from 'remark-gfm';
import Anchor from '../../components/docs/components/Anchor';
import Blockquote from '../../components/docs/components/Blockquote';
import CodeTabs from '../../components/docs/components/CodeTabs';
import Header1 from '../../components/docs/components/headers/Header1';
import Header2 from '../../components/docs/components/headers/Header2';
import Header3 from '../../components/docs/components/headers/Header3';
import Header4 from '../../components/docs/components/headers/Header4';
import Header5 from '../../components/docs/components/headers/Header5';
import Header6 from '../../components/docs/components/headers/Header6';
import DocsTable from '../../components/docs/components/table/Table';
import TableBody from '../../components/docs/components/table/TableBody';
import TableBodyCell from '../../components/docs/components/table/TableBodyCell';
import TableHead from '../../components/docs/components/table/TableHead';
import TableHeaderCell from '../../components/docs/components/table/TableHeaderCell';
import DocsContent from '../../components/docs/DocsContent';
import DocsLeftNav from '../../components/docs/DocsLeftNav';
import DocsRightNav from '../../components/docs/DocsRightNav';
import Page from '../../components/layout/Page';
import { fetchDocsContent, getSearchablePages } from '../../lib/docs';
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
import type { GetStaticPaths, GetStaticProps } from 'next/types';
import type { DocsGroup, DocsPage, SearchablePage } from '../../interface';
const StyledDocsView = styled('div')(
({ theme }) => `
display: grid;
grid-template-columns: calc(100% - 240px) 240px;
margin-left: 280px;
width: calc(100% - 280px);
padding-top: 16px;
${theme.breakpoints.down('lg')} {
margin-left: 0;
padding-top: 24px;
width: 100vw;
}
${theme.breakpoints.down('md')} {
grid-template-columns: 1fr;
}
`,
);
const StyledDocsContentWrapper = styled('main')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
margin-bottom: 40px;
${theme.breakpoints.between('md', 'lg')} {
width: calc(100vw - 250px);
}
${theme.breakpoints.down('lg')} {
margin-bottom: 32px;
}
${theme.breakpoints.down('md')} {
width: 100vw;
}
`,
);
interface DocsProps {
docsGroups: DocsGroup[];
searchablePages: SearchablePage[];
title: string;
slug: string;
description?: string;
source: MDXRemoteSerializeResult;
}
const Docs = ({
docsGroups,
searchablePages,
title,
slug,
description = '',
source,
}: DocsProps) => {
const theme = useTheme();
return (
<Page
title={title}
url={`/docs/${slug}`}
description={description}
docsGroups={docsGroups}
searchablePages={searchablePages}
fullWidth
>
<DocsLeftNav docsGroups={docsGroups} />
<StyledDocsView className={theme.palette.mode}>
<StyledDocsContentWrapper>
<DocsContent>
<Header1>{title}</Header1>
<MDXRemote
{...source}
components={{
h1: Header1,
h2: Header2,
h3: Header3,
h4: Header4,
h5: Header5,
h6: Header6,
blockquote: Blockquote,
table: DocsTable,
thead: TableHead,
tbody: TableBody,
th: TableHeaderCell,
td: TableBodyCell,
a: Anchor,
CodeTabs,
Alert,
}}
/>
</DocsContent>
</StyledDocsContentWrapper>
<DocsRightNav />
</StyledDocsView>
</Page>
);
};
export default Docs;
export const getStaticPaths: GetStaticPaths = async () => {
const paths = fetchDocsContent()[0].map(docs => `/docs/${docs.data.slug}`);
return {
paths,
fallback: false,
};
};
const buildSlugToDocsContent = (docsContents: DocsPage[]) => {
const hash: Record<string, DocsPage> = {};
docsContents.forEach(docs => (hash[docs.data.slug] = docs));
return hash;
};
let slugToDocsContent = buildSlugToDocsContent(fetchDocsContent()[0]);
let docsGroups = fetchDocsContent()[1];
export const getStaticProps: GetStaticProps = async ({ params }): Promise<{ props: DocsProps }> => {
const slug = params?.doc as string;
if (process.env.NODE_ENV === 'development') {
slugToDocsContent = buildSlugToDocsContent(fetchDocsContent()[0]);
docsGroups = fetchDocsContent()[1];
}
const { content, data } = slugToDocsContent[slug];
const source = await serialize(content, {
mdxOptions: {
remarkPlugins: [remarkGfm],
},
});
return {
props: {
docsGroups,
searchablePages: getSearchablePages(),
title: data.title,
slug: data.slug,
description: '',
source,
},
};
};

View File

@ -0,0 +1,451 @@
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import CardActionArea from '@mui/material/CardActionArea';
import CardContent from '@mui/material/CardContent';
import Chip from '@mui/material/Chip';
import { styled, useTheme } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import Image from 'next/image';
import Link from 'next/link';
import DateDisplay from '../components/DateDisplay';
import Container from '../components/layout/Container';
import Page from '../components/layout/Page';
import config from '../lib/config';
import { getDocsMenuStaticProps } from '../lib/docs';
import homepageData from '../lib/homepage';
import releases from '../lib/releases';
import type { DocsMenuProps } from '../lib/docs';
const StyledHomagePageContent = styled('div')(
({ theme }) => `
width: 100%;
padding-top: 72px;
display: flex;
flex-direction: column;
gap: 88px;
align-items: center;
${theme.breakpoints.down('md')} {
padding-top: 32px;
gap: 0;
}
`,
);
const StyledIntroSection = styled('section')`
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
`;
const StyledIntroSectionContent = styled('div')`
width: 100%;
display: flex;
flex-direction: column;
gap: 16px;
align-items: flex-start;
`;
const StyledOverviewSection = styled('section')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
${theme.breakpoints.down('md')} {
margin-top: 64px;
}
`,
);
const StyledOverviewSectionContent = styled('div')(
({ theme }) => `
width: 100%;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 64px;
${theme.breakpoints.down('md')} {
grid-template-columns: 1fr;
gap: 24px;
}
`,
);
const StyledOverviewList = styled('div')`
display: flex;
flex-direction: column;
gap: 16px;
`;
const StyledOverview = styled('div')`
display: flex;
flex-direction: column;
gap: 8px;
`;
const StyledImageWrapper = styled('div')`
width: 100%;
position: relative;
`;
const StyledCallToActionSection = styled('section')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
height: 0;
overflow: visible;
z-index: 1;
${theme.breakpoints.down('md')} {
height: auto;
margin-top: 64px;
}
`,
);
const StyledCallToActionContainer = styled('div')(
({ theme }) => `
max-width: 1280px;
width: 100%;
padding: 0 40px;
display: flex;
flex-direction: column;
align-items: center;
${theme.breakpoints.down('md')} {
padding: 0;
}
`,
);
const StyledCallToActionCard = styled(Card)(
({ theme }) => `
width: 80%;
${theme.breakpoints.down('md')} {
width: 100%;
}
`,
);
const StyledCallToActionCardContent = styled(CardContent)(
({ theme }) => `
display: flex;
align-items: flex-start;
padding: 24px 40px;
line-height: 30px;
gap: 24px;
${theme.breakpoints.down('md')} {
flex-direction: column;
}
`,
);
const StyledCallToActionText = styled('div')`
flex-grow: 1;
`;
const StyledReleasesSection = styled('section')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background: ${theme.palette.mode === 'light' ? '#dddee2' : '#242424'};
padding: 64px 0;
${theme.breakpoints.down('md')} {
padding: 48px 0;
}
`,
);
const StyledReleasesSectionContent = styled('div')(
({ theme }) => `
width: 100%;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 48px;
${theme.breakpoints.down('md')} {
grid-template-columns: 1fr;
}
`,
);
const StyledReleaseCardContent = styled(CardContent)`
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
`;
const StyledFeaturesSection = styled('section')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 80px;
${theme.breakpoints.down('md')} {
height: auto;
margin-top: 48px;
}
`,
);
const StyledFeaturesSectionIntro = styled('div')(
({ theme }) => `
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 32px 0 104px;
${theme.breakpoints.down('md')} {
padding: 32px 0 48px;
}
`,
);
const StyledFeaturesSectionContent = styled('div')(
({ theme }) => `
width: 100%;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 48px;
${theme.breakpoints.down('md')} {
grid-template-columns: 1fr;
}
`,
);
const StyledFeature = styled('div')`
width: 100%;
position: relative;
display: flex;
flex-direction: column;
gap: 8px;
`;
const StyledFeatureText = styled('div')`
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 16px;
`;
const Home = ({ docsGroups, searchablePages }: DocsMenuProps) => {
const theme = useTheme();
return (
<Page url="/" docsGroups={docsGroups} searchablePages={searchablePages} fullWidth>
<StyledHomagePageContent>
<StyledIntroSection>
<Container>
<StyledIntroSectionContent>
<Typography variant="h1" color="primary">
{homepageData.title}
</Typography>
<Typography variant="h2" color="text.primary">
{homepageData.subtitle}
</Typography>
<Button
component={Link}
href={homepageData.get_started.url}
variant="contained"
size="large"
>
{homepageData.get_started.title}
</Button>
</StyledIntroSectionContent>
</Container>
</StyledIntroSection>
<StyledOverviewSection>
<Container>
<StyledOverviewSectionContent>
<StyledOverviewList>
{homepageData.overviews.map(overview => (
<StyledOverview key={overview.title}>
<Typography variant="h3" color="text.primary">
{overview.title}
</Typography>
<Typography variant="body1" color="text.secondary">
{overview.description}
</Typography>
</StyledOverview>
))}
</StyledOverviewList>
<StyledImageWrapper>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src="/img/screenshot-editor.webp" />
</StyledImageWrapper>
</StyledOverviewSectionContent>
</Container>
</StyledOverviewSection>
<StyledCallToActionSection>
<StyledCallToActionContainer>
<StyledCallToActionCard raised>
<StyledCallToActionCardContent>
<StyledCallToActionText>
<Typography variant="subtitle1" color="text.primary" component="strong">
{homepageData.call_to_action.title}
</Typography>
&nbsp;
<Typography variant="body1" color="text.secondary" component="span">
{homepageData.call_to_action.subtitle}
</Typography>
</StyledCallToActionText>
<Button
component={Link}
href={homepageData.call_to_action.url}
variant="contained"
size="large"
sx={{ width: '188px', whiteSpace: 'nowrap' }}
>
{homepageData.call_to_action.button_text}
</Button>
</StyledCallToActionCardContent>
</StyledCallToActionCard>
</StyledCallToActionContainer>
</StyledCallToActionSection>
<StyledReleasesSection>
<Container>
<StyledReleasesSectionContent>
{[...Array(3)].map((_, index) => {
if (index >= releases.length) {
return null;
}
const release = releases[index];
return (
<CardActionArea
key={release.version}
href={`${config.repo_url}/releases/tag/${release.version}`}
>
<StyledReleaseCardContent>
<Typography
variant="subtitle1"
color="text.primary"
sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}
>
<>
<Chip label={release.version} color="primary" />
<DateDisplay date={release.date} format="MMMM dd, yyyy" />
</>
</Typography>
<Typography variant="body2" color="text.secondary">
{release.description}
</Typography>
</StyledReleaseCardContent>
</CardActionArea>
);
})}
</StyledReleasesSectionContent>
</Container>
</StyledReleasesSection>
<StyledFeaturesSection>
<Container>
<StyledFeaturesSectionIntro>
<Typography
variant="h2"
color="text.primary"
sx={{
display: 'flex',
alignItems: 'center',
gap: '8px',
[theme.breakpoints.down('md')]: {
textAlign: 'center',
},
}}
>
{homepageData.features_intro.title}
</Typography>
<Typography
variant="subtitle1"
component="div"
color="text.secondary"
sx={{
textAlign: 'center',
[theme.breakpoints.down('md')]: {
textAlign: 'center',
marginTop: '24px',
},
}}
>
{homepageData.features_intro.subtitle1} {homepageData.features_intro.subtitle2}
</Typography>
</StyledFeaturesSectionIntro>
<StyledFeaturesSectionContent>
{homepageData.features.map(feature => (
<StyledFeature key={feature.title}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={feature.image} width="100%" height="auto" />
<StyledFeatureText>
<Typography
variant="subtitle1"
color="text.primary"
sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}
>
{feature.title}
</Typography>
<Typography variant="body2" color="text.secondary">
{feature.description}
</Typography>
</StyledFeatureText>
</StyledFeature>
))}
</StyledFeaturesSectionContent>
</Container>
</StyledFeaturesSection>
<footer>
{theme.palette.mode === 'light' ? (
<a
key="netlify-logo-light"
href="https://www.netlify.com"
target="_blank"
rel="noreferrer"
>
<Image
width={114}
height={51}
src="/img/netlify-color-bg.svg"
alt="Deploys by Netlify"
/>
</a>
) : (
<a
key="netlify-logo-dark"
href="https://www.netlify.com"
target="_blank"
rel="noreferrer"
>
<Image
width={114}
height={51}
src="/img/netlify-color-accent.svg"
alt="Deploys by Netlify"
/>
</a>
)}
</footer>
</StyledHomagePageContent>
</Page>
);
};
export default Home;
export const getStaticProps = getDocsMenuStaticProps;