Skip to content

Commit ba1d0e6

Browse files
committed
Merge #31: Add Nautilus blog posts in SvelteKit
a03a7b0 Add Nautilus blog posts in SvelteKit (Graeme Byrne) Pull request description: ## Description: This PR for issue #29 migrates all existing blog posts from the current Nautilus blog to the new SvelteKit-based website. The blog posts have been structured to ensure proper rendering and SEO optimisation in SvelteKit. ## Tasks: ### 1. Export Blog Posts: - Retrieved all blog posts from the current Nautilus blog. - Ensured all relevant metadata (title, author, date, tags, etc.) was included. ### 2. Convert to SvelteKit Format: - Converted all blog posts to match the required SvelteKit format. ### 3. Integrate Blog System into SvelteKit: - Displayed blog posts, showing metadata like title, date, excerpt, etc. - Set up individual blog post pages for each entry. ### 4. Ensure Proper Styling: - Matched the new Nautilus design and applied relevant styling. - Ensured proper rendering in both light and dark modes. ACKs for top commit: josecelano: ACK a03a7b0 Tree-SHA512: 2983ac4dfa0cf6b42cbad3b6c5776b78fd6eaac44a232b45840bd467c42828a11ce5dd04875afaf33a8f0537a97f528cc639603d16491ed377b96bfb1969eac3
2 parents 8c2a1e0 + a03a7b0 commit ba1d0e6

File tree

108 files changed

+6118
-193
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+6118
-193
lines changed

package-lock.json

+482-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite dev",
8-
"build": "vite build",
8+
"build": "tsx scripts/generateMetadata.ts && vite build",
99
"preview": "vite preview",
1010
"prepare": "svelte-kit sync || echo ''",
1111
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
@@ -41,6 +41,7 @@
4141
"dependencies": {
4242
"@sveltejs/adapter-static": "^3.0.8",
4343
"dateformat": "^5.0.3",
44-
"qrcode": "^1.5.4"
44+
"qrcode": "^1.5.4",
45+
"tsx": "^4.19.3"
4546
}
4647
}

scripts/generateMetadata.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import glob from 'fast-glob';
4+
5+
const BLOG_DIR = 'src/routes/blog';
6+
const OUTPUT_FILE = 'static/blogMetadata.json';
7+
8+
type BlogMetadata = {
9+
title: string;
10+
date: string;
11+
coverImage: string;
12+
excerpt: string;
13+
slug: string;
14+
contributor: string;
15+
contributorSlug: string;
16+
tags: string[];
17+
};
18+
19+
async function generateMetadata() {
20+
const files = await glob(`${BLOG_DIR}/**/metadata.ts`);
21+
const metadataArray: (BlogMetadata & { slug: string })[] = [];
22+
23+
for (const file of files) {
24+
const slug = path.basename(path.dirname(file));
25+
26+
const module = await import(path.resolve(file));
27+
const metadata: BlogMetadata = module.metadata;
28+
29+
metadataArray.push({ ...metadata, slug });
30+
}
31+
32+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(metadataArray, null, 2));
33+
console.log(`✅ Metadata generated: ${OUTPUT_FILE}`);
34+
}
35+
36+
generateMetadata();

src/lib/components/atoms/ArticleCard.svelte

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44

55
<div class="article-wrapper">
66
<a href="/">
7-
<img
8-
src="images/posts/torrust-header.jpg"
9-
alt=""
10-
/>
7+
<img src="images/posts-cover-images/torrust-header.jpg" alt="" />
118
<div class="text">
129
<h3>Article title that can take as much space as needed</h3>
1310
<p>

src/lib/components/atoms/ProjectPreview.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<script lang="ts">
22
let { url } = $props();
3-
import QrCode from "./QrCode.svelte";
3+
import QrCode from './QrCode.svelte';
44
</script>
55

66
<div class="wrapper">
7-
<QrCode url={url} />
7+
<QrCode {url} />
88
</div>
99

1010
<style lang="scss">
+16-16
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<script>
2-
import { onMount } from "svelte";
3-
import QRCode from "qrcode";
4-
5-
let { url } = $props();
6-
let qrCodeDataUrl = $state('')
7-
8-
onMount(async () => {
9-
qrCodeDataUrl = await QRCode.toDataURL(url);
10-
});
2+
import { onMount } from 'svelte';
3+
import QRCode from 'qrcode';
4+
5+
let { url } = $props();
6+
let qrCodeDataUrl = $state('');
7+
8+
onMount(async () => {
9+
qrCodeDataUrl = await QRCode.toDataURL(url);
10+
});
1111
</script>
12-
12+
1313
{#if qrCodeDataUrl}
14-
<img src={qrCodeDataUrl} class="qr-code" alt="QR Code" />
14+
<img src={qrCodeDataUrl} class="qr-code" alt="QR Code" />
1515
{/if}
1616

1717
<style>
18-
.qr-code {
19-
width: 250px;
20-
height: 250px;
21-
object-fit: contain;
22-
}
18+
.qr-code {
19+
width: 250px;
20+
height: 250px;
21+
object-fit: contain;
22+
}
2323
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<script lang="ts">
2+
import Tag from '$lib/components/atoms/Tag.svelte';
3+
4+
let { post_data } = $props();
5+
</script>
6+
7+
<div class="card">
8+
<div class="image-container">
9+
<a href="/blog/{post_data.slug}">
10+
<img src={post_data.coverImage} alt={post_data.title} />
11+
</a>
12+
</div>
13+
<div class="content">
14+
<a href="/blog/{post_data.slug}">
15+
<h3 class="title">{post_data.title}</h3>
16+
</a>
17+
<p class="description">{post_data.excerpt}</p>
18+
<div class="tags">
19+
{#each post_data.tags as tag}
20+
<Tag {tag} />
21+
{/each}
22+
</div>
23+
</div>
24+
</div>
25+
26+
<style lang="scss">
27+
.card {
28+
width: 100%;
29+
overflow: hidden;
30+
background: var(--color--page-background);
31+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
32+
display: flex;
33+
flex-direction: column;
34+
height: 100%;
35+
border: 1px solid var(--color--border);
36+
}
37+
38+
.image-container img {
39+
width: 100%;
40+
height: 180px;
41+
object-fit: cover;
42+
}
43+
44+
.content {
45+
display: flex;
46+
flex-direction: column;
47+
padding: 15px;
48+
flex-grow: 1;
49+
}
50+
51+
.title {
52+
font-weight: bold;
53+
margin-bottom: 5px;
54+
font-size: 20px;
55+
color: var(--color--text);
56+
}
57+
58+
.description {
59+
color: #666;
60+
font-size: 0.9rem;
61+
line-height: 1.4;
62+
overflow: hidden;
63+
display: -webkit-box;
64+
-webkit-line-clamp: 2;
65+
-webkit-box-orient: vertical;
66+
text-overflow: ellipsis;
67+
margin-bottom: 1.5rem;
68+
}
69+
70+
.tags {
71+
margin-top: auto;
72+
display: flex;
73+
flex-wrap: wrap;
74+
gap: 5px;
75+
}
76+
</style>

src/lib/components/molecules/Post.svelte

+63-33
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
<script lang="ts">
2-
// re-add slug, contributor, contributorSlug and tags
3-
let { title, date, coverImage, children } = $props();
2+
let { title, date, contributor, contributorSlug, tags, coverImage, categories, children } =
3+
$props();
44
55
import { formatDate } from '$lib/utils/date';
6-
// import Image from '$lib/components/atoms/Image.svelte';
76
// import ShareButton from '$lib/components/singletons/ShareButton.svelte';
8-
// import Tag from '$lib/components/atoms/Tag.svelte';
97
</script>
108

119
<svelte:head>
@@ -14,34 +12,38 @@
1412

1513
<div class="container">
1614
<main>
17-
<article>
18-
<img src={coverImage} alt={title} />
19-
<div class="header">
20-
<!-- {#if tags.length}
15+
<img src={coverImage} alt={title} />
16+
<div class="header">
17+
<h1>{title}</h1>
18+
</div>
19+
<div class="layout">
20+
{@render children()}
21+
</div>
22+
<div class="author-info">
23+
<div>
24+
Published {formatDate(date)}
25+
{#if contributor}
26+
<p>
27+
By <a class="author" href={'/contributor/' + contributorSlug}>{contributor}</a>
28+
</p>
29+
{/if}
30+
</div>
31+
<div class="labels">
32+
{#if tags.length}
2133
<div class="tags">
22-
{#each tags as tag}
23-
<Tag {tag}>
24-
{tag}
25-
</Tag>
26-
{/each}
34+
<p>Tags:</p>
35+
<span>{tags.join(', ')}</span>
2736
</div>
28-
{/if} -->
29-
<h1>{title}</h1>
30-
<div class="note">
31-
<div>
32-
<!-- {#if contributor}
33-
<a class="author" href={'/contributor/' + contributorSlug}>{contributor}</a>
34-
-
35-
{/if} -->
36-
{formatDate(date)}
37+
{/if}
38+
39+
{#if categories.length}
40+
<div class="categories">
41+
<p>Categories:</p>
42+
<span>{categories.join(', ')}</span>
3743
</div>
38-
<!-- <ShareButton {slug} {title} /> -->
39-
</div>
44+
{/if}
4045
</div>
41-
<div class="layout">
42-
{@render children()}
43-
</div>
44-
</article>
46+
</div>
4547
</main>
4648
</div>
4749

@@ -59,7 +61,8 @@
5961
max-height: 880px;
6062
}
6163
62-
.header {
64+
.header,
65+
.author-info {
6366
margin-inline: 1.5rem;
6467
6568
@include bp.for-desktop-up {
@@ -68,17 +71,44 @@
6871
}
6972
}
7073
74+
.author-info {
75+
display: flex;
76+
flex-direction: column;
77+
margin-top: 2rem;
78+
padding-top: 1rem;
79+
border-top: 2px solid var(--color--border);
80+
}
81+
82+
.labels {
83+
display: flex;
84+
flex-direction: column;
85+
}
86+
87+
.tags {
88+
display: flex;
89+
flex-direction: row;
90+
}
91+
92+
.categories {
93+
display: flex;
94+
flex-direction: row;
95+
}
96+
97+
@include bp.for-tablet-portrait-up {
98+
.author-info {
99+
display: flex;
100+
flex-direction: row;
101+
justify-content: space-between;
102+
}
103+
}
104+
71105
h1 {
72106
margin-top: 4.5rem;
73107
font-size: 3rem;
74108
line-height: 1.2;
75109
color: var(--color--text);
76110
}
77111
78-
.note {
79-
color: var(--color--text-secondary);
80-
}
81-
82112
.layout {
83113
margin: 1.5rem;
84114

src/lib/data/metadata.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import type { BlogMetadata } from '$lib/utils/types';
4+
5+
let cachedMetadata: BlogMetadata[] = [];
6+
7+
export function getMetadata(): BlogMetadata[] {
8+
if (cachedMetadata.length === 0) {
9+
const metadataPath = path.resolve('static/blogMetadata.json');
10+
11+
if (!fs.existsSync(metadataPath)) {
12+
throw new Error("Metadata file not found. Run 'tsx scripts/generateMetadata.ts'.");
13+
}
14+
15+
cachedMetadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
16+
}
17+
18+
return cachedMetadata;
19+
}

0 commit comments

Comments
 (0)