-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial blog structure with routing, navigation, and components
- Loading branch information
1 parent
ce45107
commit 9f0a25f
Showing
22 changed files
with
2,673 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
.vscode | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
# env files | ||
.env | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { notFound } from 'next/navigation' | ||
import { CustomMDX } from 'app/components/mdx' | ||
import { formatDate, getBlogPosts } from 'app/blog/utils' | ||
import { baseUrl } from 'app/sitemap' | ||
|
||
export async function generateStaticParams() { | ||
let posts = getBlogPosts() | ||
|
||
return posts.map((post) => ({ | ||
slug: post.slug, | ||
})) | ||
} | ||
|
||
export function generateMetadata({ params }) { | ||
let post = getBlogPosts().find((post) => post.slug === params.slug) | ||
if (!post) { | ||
return | ||
} | ||
|
||
let { | ||
title, | ||
publishedAt: publishedTime, | ||
summary: description, | ||
image, | ||
} = post.metadata | ||
let ogImage = image | ||
? image | ||
: `${baseUrl}/og?title=${encodeURIComponent(title)}` | ||
|
||
return { | ||
title, | ||
description, | ||
openGraph: { | ||
title, | ||
description, | ||
type: 'article', | ||
publishedTime, | ||
url: `${baseUrl}/blog/${post.slug}`, | ||
images: [ | ||
{ | ||
url: ogImage, | ||
}, | ||
], | ||
}, | ||
twitter: { | ||
card: 'summary_large_image', | ||
title, | ||
description, | ||
images: [ogImage], | ||
}, | ||
} | ||
} | ||
|
||
export default function Blog({ params }) { | ||
let post = getBlogPosts().find((post) => post.slug === params.slug) | ||
|
||
if (!post) { | ||
notFound() | ||
} | ||
|
||
return ( | ||
<section> | ||
<script | ||
type="application/ld+json" | ||
suppressHydrationWarning | ||
dangerouslySetInnerHTML={{ | ||
__html: JSON.stringify({ | ||
'@context': 'https://schema.org', | ||
'@type': 'BlogPosting', | ||
headline: post.metadata.title, | ||
datePublished: post.metadata.publishedAt, | ||
dateModified: post.metadata.publishedAt, | ||
description: post.metadata.summary, | ||
image: post.metadata.image | ||
? `${baseUrl}${post.metadata.image}` | ||
: `/og?title=${encodeURIComponent(post.metadata.title)}`, | ||
url: `${baseUrl}/blog/${post.slug}`, | ||
author: { | ||
'@type': 'Person', | ||
name: 'My Portfolio', | ||
}, | ||
}), | ||
}} | ||
/> | ||
<h1 className="title font-semibold text-2xl tracking-tighter"> | ||
{post.metadata.title} | ||
</h1> | ||
<div className="flex justify-between items-center mt-2 mb-8 text-sm"> | ||
<p className="text-sm text-neutral-600 dark:text-neutral-400"> | ||
{formatDate(post.metadata.publishedAt)} | ||
</p> | ||
</div> | ||
<article className="prose"> | ||
<CustomMDX source={post.content} /> | ||
</article> | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { BlogPosts } from "app/components/posts" | ||
|
||
export const metadata = { | ||
title: "Blog", | ||
description: "Read my blog.", | ||
} | ||
|
||
export default function Page() { | ||
return ( | ||
<section> | ||
<h1 className="font-semibold text-2xl mb-8 tracking-tighter"> | ||
Luca Protelli | ||
</h1> | ||
<BlogPosts /> | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
--- | ||
title: 'Spaces vs. Tabs: The Indentation Debate Continues' | ||
publishedAt: '2024-04-08' | ||
summary: 'Explore the enduring debate between using spaces and tabs for code indentation, and why this choice matters more than you might think.' | ||
--- | ||
|
||
The debate between using spaces and tabs for indentation in coding may seem trivial to the uninitiated, but it is a topic that continues to inspire passionate discussions among developers. This seemingly minor choice can affect code readability, maintenance, and even team dynamics. | ||
|
||
Let's delve into the arguments for both sides and consider why this debate remains relevant in the software development world. | ||
|
||
## The Case for Spaces | ||
|
||
Advocates for using spaces argue that it ensures consistent code appearance across different editors, tools, and platforms. Because a space is a universally recognized character with a consistent width, code indented with spaces will look the same no matter where it's viewed. This consistency is crucial for maintaining readability and avoiding formatting issues when code is shared between team members or published online. | ||
|
||
Additionally, some programming languages and style guides explicitly recommend spaces for indentation, suggesting a certain number of spaces (often two or four) per indentation level. Adhering to these recommendations can be essential for projects that aim for best practices in code quality and readability. | ||
|
||
## The Case for Tabs | ||
|
||
On the other side of the debate, proponents of tabs highlight the flexibility that tabs offer. Because the width of a tab can be adjusted in most text editors, individual developers can choose how much indentation they prefer to see, making the code more accessible and comfortable to read on a personal level. This adaptability can be particularly beneficial in teams with diverse preferences regarding code layout. | ||
|
||
Tabs also have the advantage of semantic meaning. A tab is explicitly meant to represent indentation, whereas a space is used for many purposes within code. This distinction can make automated parsing and manipulation of code simpler, as tools can more easily recognize and adjust indentation levels without confusing them with spaces used for alignment. | ||
|
||
## Hybrid Approaches and Team Dynamics | ||
|
||
The debate often extends into discussions about hybrid approaches, where teams might use tabs for indentation and spaces for alignment within lines, attempting to combine the best of both worlds. However, such strategies require clear team agreements and disciplined adherence to coding standards to prevent formatting chaos. | ||
|
||
Ultimately, the choice between spaces and tabs often comes down to team consensus and project guidelines. In environments where collaboration and code sharing are common, agreeing on a standard that everyone follows is more important than the individual preferences of spaces versus tabs. Modern development tools and linters can help enforce these standards, making the choice less about technical limitations and more about team dynamics and coding philosophy. | ||
|
||
## Conclusion | ||
|
||
While the spaces vs. tabs debate might not have a one-size-fits-all answer, it underscores the importance of consistency, readability, and team collaboration in software development. Whether a team chooses spaces, tabs, or a hybrid approach, the key is to make a conscious choice that serves the project's needs and to adhere to it throughout the codebase. As with many aspects of coding, communication and agreement among team members are paramount to navigating this classic programming debate. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import fs from 'fs' | ||
import path from 'path' | ||
|
||
type Metadata = { | ||
title: string | ||
publishedAt: string | ||
summary: string | ||
image?: string | ||
} | ||
|
||
function parseFrontmatter(fileContent: string) { | ||
let frontmatterRegex = /---\s*([\s\S]*?)\s*---/ | ||
let match = frontmatterRegex.exec(fileContent) | ||
let frontMatterBlock = match![1] | ||
let content = fileContent.replace(frontmatterRegex, '').trim() | ||
let frontMatterLines = frontMatterBlock.trim().split('\n') | ||
let metadata: Partial<Metadata> = {} | ||
|
||
frontMatterLines.forEach((line) => { | ||
let [key, ...valueArr] = line.split(': ') | ||
let value = valueArr.join(': ').trim() | ||
value = value.replace(/^['"](.*)['"]$/, '$1') // Remove quotes | ||
metadata[key.trim() as keyof Metadata] = value | ||
}) | ||
|
||
return { metadata: metadata as Metadata, content } | ||
} | ||
|
||
function getMDXFiles(dir) { | ||
return fs.readdirSync(dir).filter((file) => path.extname(file) === '.mdx') | ||
} | ||
|
||
function readMDXFile(filePath) { | ||
let rawContent = fs.readFileSync(filePath, 'utf-8') | ||
return parseFrontmatter(rawContent) | ||
} | ||
|
||
function getMDXData(dir) { | ||
let mdxFiles = getMDXFiles(dir) | ||
return mdxFiles.map((file) => { | ||
let { metadata, content } = readMDXFile(path.join(dir, file)) | ||
let slug = path.basename(file, path.extname(file)) | ||
|
||
return { | ||
metadata, | ||
slug, | ||
content, | ||
} | ||
}) | ||
} | ||
|
||
export function getBlogPosts() { | ||
return getMDXData(path.join(process.cwd(), 'app', 'blog', 'posts')) | ||
} | ||
|
||
export function formatDate(date: string, includeRelative = false) { | ||
let currentDate = new Date() | ||
if (!date.includes('T')) { | ||
date = `${date}T00:00:00` | ||
} | ||
let targetDate = new Date(date) | ||
|
||
let yearsAgo = currentDate.getFullYear() - targetDate.getFullYear() | ||
let monthsAgo = currentDate.getMonth() - targetDate.getMonth() | ||
let daysAgo = currentDate.getDate() - targetDate.getDate() | ||
|
||
let formattedDate = '' | ||
|
||
if (yearsAgo > 0) { | ||
formattedDate = `${yearsAgo}y ago` | ||
} else if (monthsAgo > 0) { | ||
formattedDate = `${monthsAgo}mo ago` | ||
} else if (daysAgo > 0) { | ||
formattedDate = `${daysAgo}d ago` | ||
} else { | ||
formattedDate = 'Today' | ||
} | ||
|
||
let fullDate = targetDate.toLocaleString('en-us', { | ||
month: 'long', | ||
day: 'numeric', | ||
year: 'numeric', | ||
}) | ||
|
||
if (!includeRelative) { | ||
return fullDate | ||
} | ||
|
||
return `${fullDate} (${formattedDate})` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
function ArrowIcon() { | ||
return ( | ||
<svg | ||
width="12" | ||
height="12" | ||
viewBox="0 0 12 12" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M2.07102 11.3494L0.963068 10.2415L9.2017 1.98864H2.83807L2.85227 0.454545H11.8438V9.46023H10.2955L10.3097 3.09659L2.07102 11.3494Z" | ||
fill="currentColor" | ||
/> | ||
</svg> | ||
) | ||
} | ||
|
||
export default function Footer() { | ||
return ( | ||
<footer className="mb-16"> | ||
<ul className="font-sm mt-8 flex flex-col space-x-0 space-y-2 text-neutral-600 md:flex-row md:space-x-4 md:space-y-0 dark:text-neutral-300"> | ||
<li> | ||
<a | ||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100" | ||
rel="noopener noreferrer" | ||
target="_blank" | ||
href="/rss" | ||
> | ||
<ArrowIcon /> | ||
<p className="ml-2 h-7">rss</p> | ||
</a> | ||
</li> | ||
<li> | ||
<a | ||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100" | ||
rel="noopener noreferrer" | ||
target="_blank" | ||
href="https://github.com/lucaprotelli" | ||
> | ||
<ArrowIcon /> | ||
<p className="ml-2 h-7">github</p> | ||
</a> | ||
</li> | ||
<li> | ||
<a | ||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100" | ||
rel="noopener noreferrer" | ||
target="_blank" | ||
href="https://github.com/lucaprotelli/portfolio" | ||
> | ||
<ArrowIcon /> | ||
<p className="ml-2 h-7">view source</p> | ||
</a> | ||
</li> | ||
</ul> | ||
<p className="mt-8 text-neutral-600 dark:text-neutral-300"> | ||
© {new Date().getFullYear()} MIT Licensed | ||
</p> | ||
</footer> | ||
) | ||
} |
Oops, something went wrong.