mirror of
https://github.com/smyalygames/anthonyberg-website.git
synced 2025-05-18 06:04:12 +02:00
feat(build): update to nextra v4
This commit is contained in:
parent
c5c576cbcb
commit
5080bbff7c
3
.gitignore
vendored
3
.gitignore
vendored
@ -35,5 +35,8 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# nextra
|
||||
_pagefind/
|
||||
|
||||
# IDEA folder
|
||||
/.idea/
|
||||
|
@ -2,13 +2,15 @@ import nextra from 'nextra'
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const withNextra = nextra({
|
||||
theme: 'nextra-theme-blog',
|
||||
themeConfig: './src/theme.config.tsx',
|
||||
// optional: add `unstable_staticImage: true` to enable Nextra's auto image import
|
||||
// ... Other Nextra config options
|
||||
search: false
|
||||
})
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// any configs you need
|
||||
turbopack: {
|
||||
resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.json'],
|
||||
},
|
||||
}
|
||||
|
||||
export default withNextra(nextConfig)
|
||||
|
21
package.json
21
package.json
@ -6,9 +6,10 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
"start": "next start",
|
||||
"postbuild": "pagefind --site .next/server/app --output-path public/_pagefind"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.x",
|
||||
@ -16,16 +17,18 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"gray-matter": "^4.0.3",
|
||||
"next": "^15.0.2",
|
||||
"nextra": "^3.2.0",
|
||||
"nextra-theme-blog": "^3.2.0",
|
||||
"next": "^15.3.1",
|
||||
"nextra": "^4.2.17",
|
||||
"nextra-theme-blog": "^4.2.17",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.8.4",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"typescript": "^5.6.3"
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^22.14.1",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"pagefind": "^1.3.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
2606
pnpm-lock.yaml
generated
2606
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
30
src/app/[[...mdxPath]]/page.tsx
Normal file
30
src/app/[[...mdxPath]]/page.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { generateStaticParamsFor, importPage } from 'nextra/pages'
|
||||
import { useMDXComponents as getMDXComponents } from '../../mdx-components'
|
||||
import { MDXComponents } from "mdx/types";
|
||||
|
||||
export const generateStaticParams = generateStaticParamsFor('mdxPath')
|
||||
|
||||
type Props = {
|
||||
params: Promise<{
|
||||
mdxPath: string[]
|
||||
}>
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: Props) {
|
||||
const params = await props.params
|
||||
const { metadata } = await importPage(params.mdxPath)
|
||||
return metadata
|
||||
}
|
||||
|
||||
const Wrapper = getMDXComponents().wrapper
|
||||
|
||||
export default async function Page(props: Props) {
|
||||
const params = await props.params
|
||||
const result = await importPage(params.mdxPath)
|
||||
const { default: MDXContent, toc, metadata } = result
|
||||
return (
|
||||
<Wrapper toc={toc} metadata={metadata}>
|
||||
<MDXContent {...props} params={params} />
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
55
src/app/layout.tsx
Normal file
55
src/app/layout.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Layout, Navbar, ThemeSwitch } from 'nextra-theme-blog';
|
||||
import { Head, Search } from 'nextra/components';
|
||||
import { getPageMap } from 'nextra/page-map';
|
||||
import 'nextra-theme-blog/style.css';
|
||||
import React from 'react';
|
||||
import { Metadata } from 'next';
|
||||
import Footer from '@/components/footer'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'Anthony Berg\'s Portfolio',
|
||||
template: '%s | Anthony Berg',
|
||||
},
|
||||
description: 'My own personal portfolio including for my own projects related to computer science',
|
||||
openGraph: {
|
||||
url: 'https://anthonyberg.io',
|
||||
siteName: 'Anthony Berg\'s Website',
|
||||
locale: 'en_GB',
|
||||
type: 'website',
|
||||
images: [{
|
||||
url: '/images/kgxtunnel.jpg',
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
const YEAR = new Date().getFullYear()
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
dir="ltr"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<Head backgroundColor={{dark: '#0f172a', light: '#fefce8'}} />
|
||||
<body>
|
||||
<Layout>
|
||||
<Navbar pageMap={await getPageMap()}>
|
||||
{/*<Search placeholder={"Search..."}/>*/}
|
||||
<ThemeSwitch />
|
||||
</Navbar>
|
||||
|
||||
{children}
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
18
src/app/posts/get-posts.ts
Normal file
18
src/app/posts/get-posts.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { normalizePages } from 'nextra/normalize-pages'
|
||||
import { getPageMap } from 'nextra/page-map'
|
||||
|
||||
export async function getPosts() {
|
||||
const { directories } = normalizePages({
|
||||
list: await getPageMap('/posts'),
|
||||
route: '/posts'
|
||||
})
|
||||
return directories
|
||||
.filter(post => post.name !== 'index')
|
||||
.sort((a, b) => (new Date(b.frontMatter.date)).valueOf() - (new Date(a.frontMatter.date)).valueOf())
|
||||
}
|
||||
|
||||
export async function getTags() {
|
||||
const posts = await getPosts()
|
||||
const tags = posts.flatMap(post => post.frontMatter.tags)
|
||||
return tags
|
||||
}
|
36
src/app/posts/page.tsx
Normal file
36
src/app/posts/page.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import Link from 'next/link'
|
||||
import { PostCard } from 'nextra-theme-blog'
|
||||
import { getPosts, getTags } from './get-posts'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Posts'
|
||||
}
|
||||
|
||||
export default async function PostsPage() {
|
||||
const tags = await getTags()
|
||||
const posts = await getPosts()
|
||||
const allTags: Record<string, number> = Object.create(null)
|
||||
|
||||
for (const tag of tags) {
|
||||
allTags[tag] ??= 0
|
||||
allTags[tag] += 1
|
||||
}
|
||||
return (
|
||||
<div data-pagefind-ignore="all">
|
||||
<h1>{metadata.title}</h1>
|
||||
<div
|
||||
className="not-prose"
|
||||
style={{ display: 'flex', flexWrap: 'wrap', gap: '.5rem' }}
|
||||
>
|
||||
{Object.entries(allTags).map(([tag, count]) => (
|
||||
<Link key={tag} href={`/tags/${tag}`} className="nextra-tag">
|
||||
{tag} ({count})
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{posts.map(post => (
|
||||
<PostCard key={post.route} post={post} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
39
src/app/rss.xml/route.ts
Normal file
39
src/app/rss.xml/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { getPosts } from '../posts/get-posts'
|
||||
|
||||
|
||||
const CONFIG = {
|
||||
title: 'Anthony\'s Blog',
|
||||
siteUrl: 'https://anthonyberg.io',
|
||||
description: 'Anthony\'s latest blog posts about tech related things.',
|
||||
lang: 'en-gb'
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const allPosts = await getPosts()
|
||||
const posts = allPosts
|
||||
.map(
|
||||
post => ` <item>
|
||||
<title>${post.title}</title>
|
||||
<description>${post.frontMatter.description}</description>
|
||||
<link>${CONFIG.siteUrl}${post.route}</link>
|
||||
<pubDate>${new Date(post.frontMatter.date).toUTCString()}</pubDate>
|
||||
</item>`
|
||||
)
|
||||
.join('\n')
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>${CONFIG.title}</title>
|
||||
<link>${CONFIG.siteUrl}</link>
|
||||
<description>${CONFIG.description}</description>
|
||||
<language>${CONFIG.lang}</language>
|
||||
${posts}
|
||||
</channel>
|
||||
</rss>`
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/rss+xml'
|
||||
}
|
||||
})
|
||||
}
|
33
src/app/tags/[tag]/page.tsx
Normal file
33
src/app/tags/[tag]/page.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { PostCard } from 'nextra-theme-blog'
|
||||
import { getPosts, getTags } from '../../posts/get-posts'
|
||||
|
||||
|
||||
export async function generateMetadata(props: { params: any }) {
|
||||
const params = await props.params
|
||||
return {
|
||||
title: `Posts Tagged with “${decodeURIComponent(params.tag)}”`
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const allTags = await getTags()
|
||||
return [...new Set(allTags)].map(tag => ({ tag }))
|
||||
}
|
||||
|
||||
export default async function TagPage(props: { params: any }) {
|
||||
const params = await props.params
|
||||
const { title } = await generateMetadata({ params })
|
||||
const posts = await getPosts()
|
||||
return (
|
||||
<>
|
||||
<h1>{title}</h1>
|
||||
{posts
|
||||
.filter(post =>
|
||||
post.frontMatter.tags.includes(decodeURIComponent(params.tag))
|
||||
)
|
||||
.map(post => (
|
||||
<PostCard key={post.route} post={post} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
29
src/components/footer.tsx
Normal file
29
src/components/footer.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { Footer as NXFooter } from 'nextra-theme-blog'
|
||||
|
||||
function Footer() {
|
||||
const YEAR = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<NXFooter>
|
||||
<small>
|
||||
<Link href={"https://github.com/smyalygames/anthonyberg-website/blob/main/LICENSE"}>
|
||||
<time>{YEAR}</time>
|
||||
© Anthony Berg.</Link>
|
||||
<a href={"https://github.com/smyalygames/anthonyberg-website/"}>GitHub Repository</a>
|
||||
</small>
|
||||
<style jsx>{`
|
||||
footer {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
a {
|
||||
float: right;
|
||||
}
|
||||
`}</style>
|
||||
</NXFooter>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
@ -1,3 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import styles from './theme-image.module.css'
|
||||
import Image, { ImageProps } from 'next/image'
|
||||
|
||||
|
24
src/content/_meta.ts
Normal file
24
src/content/_meta.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { MetaRecord } from 'nextra';
|
||||
|
||||
const meta: MetaRecord = {
|
||||
index: {
|
||||
title: 'Home',
|
||||
type: 'page',
|
||||
},
|
||||
projects: {
|
||||
title: 'Projects',
|
||||
type: 'page',
|
||||
},
|
||||
posts: {
|
||||
title: 'Blog',
|
||||
type: 'page'
|
||||
},
|
||||
veganenumbers: {
|
||||
display: 'hidden',
|
||||
theme: {
|
||||
sidebar: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default meta;
|
@ -1,22 +1,18 @@
|
||||
---
|
||||
type: page
|
||||
title: Anthony Berg
|
||||
date: 2024-10-05
|
||||
---
|
||||
|
||||
import {ThemeImage} from '@/components/theme-image'
|
||||
|
||||
# Anthony Berg
|
||||
import { ThemeImage } from '@/components/theme-image'
|
||||
|
||||
Hey, welcome to my website! :D
|
||||
|
||||
I'm Anthony, and I enjoy working with computers, be it from developing on my own projects,
|
||||
or just using Linux on my PCs or setting up my own personal servers.
|
||||
|
||||
For my day to day life, I am currently in my 1<sup>st</sup> year of my Master's at OsloMet, studying
|
||||
For my day-to-day life, I am currently in my 1<sup>st</sup> year of my Master's at OsloMet, studying
|
||||
Applied Computer and Information Technology, specialising in Mathematical Modelling and Scientific
|
||||
Computing.
|
||||
I have also completed BSc Computer Science at Newcastle University.
|
||||
I have also completed B.Sc. Computer Science at Newcastle University.
|
||||
|
||||
I have a few open source projects that I have worked on that you can find in my [portfolio](/projects).
|
||||
|
@ -1,13 +1,9 @@
|
||||
---
|
||||
type: page
|
||||
title: Projects
|
||||
date: 2023-11-14
|
||||
---
|
||||
|
||||
import Image from "next/image";
|
||||
|
||||
# Projects
|
||||
|
||||
Here are some of my projects that I have worked on
|
||||
|
||||
|
13
src/mdx-components.ts
Normal file
13
src/mdx-components.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useMDXComponents as getThemeComponents } from 'nextra-theme-blog';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
|
||||
// Get the default MDX components
|
||||
const themeComponents = getThemeComponents();
|
||||
|
||||
// Merge components
|
||||
export function useMDXComponents(components?: MDXComponents) {
|
||||
return {
|
||||
...themeComponents,
|
||||
...components
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import type { AppProps } from 'next/app'
|
||||
import '../styles/main.css'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
type: posts
|
||||
title: Posts
|
||||
date: 2023-11-14
|
||||
---
|
||||
|
||||
# Posts
|
||||
|
||||
## This is currently under construction
|
@ -1,13 +0,0 @@
|
||||
---
|
||||
type: tag
|
||||
title: Tagged Posts
|
||||
---
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const TagName = () => {
|
||||
const { tag } = useRouter().query
|
||||
return tag || null
|
||||
}
|
||||
|
||||
# Posts Tagged with “<TagName/>”
|
@ -1,57 +0,0 @@
|
||||
import Link from "next/link";
|
||||
|
||||
const YEAR = new Date().getFullYear()
|
||||
|
||||
const websiteMeta = {
|
||||
title: 'Anthony Berg\'s Portfolio',
|
||||
description: 'My own personal portfolio including for my own projects related to computer science',
|
||||
image: '/images/kgxtunnel.jpg',
|
||||
}
|
||||
|
||||
export default {
|
||||
footer: (
|
||||
<footer>
|
||||
<small>
|
||||
<Link href={"https://github.com/smyalygames/anthonyberg-website/blob/main/LICENSE"}><time>{YEAR}</time> © Anthony Berg.</Link>
|
||||
<a href={"https://github.com/smyalygames/anthonyberg-website/"}>GitHub Repository</a>
|
||||
</small>
|
||||
<style jsx>{`
|
||||
footer {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
a {
|
||||
float: right;
|
||||
}
|
||||
`}</style>
|
||||
</footer>
|
||||
),
|
||||
head: ({ title, meta } : { title: string; meta: any }) => (
|
||||
<>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Inter-roman.latin.var.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
{meta.description && (
|
||||
<meta name="description" content={meta.description}/>
|
||||
)}
|
||||
{meta.tag && <meta name="keywords" content={meta.tag}/>}
|
||||
{meta.author && <meta name="author" content={meta.author}/>}
|
||||
|
||||
{/*og meta*/}
|
||||
<meta name="robots" content="follow, index"/>
|
||||
<meta name="description" content={websiteMeta.description}/>
|
||||
<meta property="og:site_name" content={websiteMeta.title}/>
|
||||
<meta property="og:description" content={websiteMeta.description}/>
|
||||
<meta property="og:title" content={websiteMeta.title}/>
|
||||
<meta property="og:image" content={websiteMeta.image}/>
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
{/*<meta name="twitter:site" content="@yourname" />*/}
|
||||
<meta name="twitter:title" content={websiteMeta.title}/>
|
||||
<meta name="twitter:description" content={websiteMeta.description}/>
|
||||
<meta name="twitter:image" content={websiteMeta.image}/>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"target": "es2020",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@ -9,15 +13,29 @@
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/components/*": ["./src/components/*"]
|
||||
}
|
||||
"@/components/*": [
|
||||
"./src/components/*"
|
||||
]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user