feat: Blog catalog and tags #7
|
@ -33,7 +33,7 @@ npm run dev [-- --open]
|
||||||
### Formatting
|
### Formatting
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
npx run eslint
|
npx eslint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
|
@ -3,6 +3,7 @@ title: Placeholder 1
|
||||||
date: '2024-09-01'
|
date: '2024-09-01'
|
||||||
description: "This is a placeholder description"
|
description: "This is a placeholder description"
|
||||||
tags: ["a123", "ボースト"]
|
tags: ["a123", "ボースト"]
|
||||||
|
series: ["placeholder"]
|
||||||
---
|
---
|
||||||
# 1st Level Heading
|
# 1st Level Heading
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Placeholder 2
|
||||||
|
date: '2024-09-20'
|
||||||
|
description: "This is a placeholder description"
|
||||||
|
tags: ["a123"]
|
||||||
|
series: ["placeholder"]
|
||||||
|
---
|
||||||
|
## Content
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Placeholder 3
|
||||||
|
date: '2024-09-24'
|
||||||
|
description: "This is a placeholder description"
|
||||||
|
tags: []
|
||||||
|
series: ["placeholder"]
|
||||||
|
---
|
||||||
|
## Content
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: The Perfect Math Class
|
||||||
|
date: '2024-09-15'
|
||||||
|
tags: ["Cirno"]
|
||||||
|
---
|
||||||
|
# Content
|
|
@ -0,0 +1,11 @@
|
||||||
|
import type { Reroute } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
const translated: Record<string, string> = {
|
||||||
|
'/post': '/page/1',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reroute: Reroute = ({ url }) => {
|
||||||
|
if (url.pathname in translated) {
|
||||||
|
return translated[url.pathname];
|
||||||
|
}
|
||||||
|
};
|
|
@ -3,11 +3,15 @@
|
||||||
export let metadata : { title: string, description: string, tags: [string], date: Date };
|
export let metadata : { title: string, description: string, tags: [string], date: Date };
|
||||||
|
|
||||||
export let link: Option<string> = null;
|
export let link: Option<string> = null;
|
||||||
const date = metadata.date.toLocaleDateString();
|
const date = metadata.date.toISOString().slice(0,-14);
|
||||||
|
const series = metadata?.series || [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="post-heading">
|
<div id="post-heading">
|
||||||
<h2>
|
<h2>
|
||||||
|
{#each series as seriesTag}
|
||||||
|
<p class="series-tag">{seriesTag}</p>
|
||||||
|
{/each}
|
||||||
{#if link}
|
{#if link}
|
||||||
<a id="post-title" href={link}>{metadata.title}</a>
|
<a id="post-title" href={link}>{metadata.title}</a>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -25,7 +29,7 @@
|
||||||
<DividerVertical />
|
<DividerVertical />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<a href="/">{tag}</a>
|
<a href="/tag/{tag}">{tag}</a>
|
||||||
{/each}
|
{/each}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-l text-gray-500">{date}</p>
|
<p class="text-l text-gray-500">{date}</p>
|
||||||
|
@ -35,9 +39,12 @@
|
||||||
<style>
|
<style>
|
||||||
#post-heading {
|
#post-heading {
|
||||||
@apply flex flex-col;
|
@apply flex flex-col;
|
||||||
width: 100%;
|
margin: .5em 1em .5em 1em;
|
||||||
}
|
}
|
||||||
#post-title {
|
#post-title {
|
||||||
@apply text-3xl;
|
@apply text-3xl;
|
||||||
}
|
}
|
||||||
|
.series-tag {
|
||||||
|
color: theme('colors.sunglow.600');
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,41 @@
|
||||||
|
export async function getPosts(tag: string | null = null) {
|
||||||
|
const allPostFiles = import.meta.glob('$content/post/*.md');
|
||||||
|
const iterablePostFiles = Object.entries(allPostFiles);
|
||||||
|
|
||||||
|
let posts = await Promise.all(
|
||||||
|
iterablePostFiles.map(async ([pathMarkdown, resolver]) => {
|
||||||
|
const { metadata } = await resolver();
|
||||||
|
const pathPost = "/post/" + pathMarkdown.slice(pathMarkdown.lastIndexOf("/") + 1, -".md".length);
|
||||||
|
|
||||||
|
return {
|
||||||
|
meta: {
|
||||||
|
...metadata,
|
||||||
|
date: new Date(metadata.date),
|
||||||
|
},
|
||||||
|
path: pathPost
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (tag)
|
||||||
|
posts = posts.filter(obj => obj.meta.tags.includes(tag))
|
||||||
|
|
||||||
|
posts.sort((post1, post2) => {
|
||||||
|
const date1: Date = post1.meta.date;
|
||||||
|
const date2: Date = post2.meta.date;
|
||||||
|
return date2.getTime() - date1.getTime();
|
||||||
|
});
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTags() {
|
||||||
|
const allPostFiles = import.meta.glob('$content/post/*.md');
|
||||||
|
const iterablePostFiles = Object.entries(allPostFiles);
|
||||||
|
|
||||||
|
const allPosts: string[][] = await Promise.all(
|
||||||
|
iterablePostFiles.map(async ([_, resolver]) => {
|
||||||
|
const { metadata } = await resolver();
|
||||||
|
return metadata.tags;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return new Set(allPosts.flat());
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
export const routes: { route: string, name: string, inactive?: boolean }[] = [
|
export const routes: { route: string, name: string, disabled?: boolean }[] = [
|
||||||
{ route: "/", name: "Home" },
|
{ route: "/", name: "Home" },
|
||||||
{ route: "/post", name: "Blog" },
|
{ route: "/post", name: "Blog" },
|
||||||
{ route: "/art", name: "Art", inactive: true },
|
{ route: "/tag", name: "Tags" },
|
||||||
|
{ route: "/gallery", name: "Gallery", disabled: true },
|
||||||
{ route: "/archives", name: "Archives" },
|
{ route: "/archives", name: "Archives" },
|
||||||
];
|
];
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import Footer from "./Footer.svelte";
|
import Footer from "./Footer.svelte";
|
||||||
import Navbar from "./Navbar.svelte";
|
import Navbar from "./Navbar.svelte";
|
||||||
|
import PageTransition from "./PageTransition.svelte";
|
||||||
|
|
||||||
|
export let data : { url: string };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Homepage has its own navbar with animations -->
|
<!-- Homepage has its own navbar with animations -->
|
||||||
|
@ -12,24 +15,30 @@
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<main>
|
||||||
|
<PageTransition url={data.url}>
|
||||||
<slot />
|
<slot />
|
||||||
|
</PageTransition>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
<!-- On the home page -->
|
||||||
|
<PageTransition url={data.url}>
|
||||||
<slot />
|
<slot />
|
||||||
|
</PageTransition>
|
||||||
<Footer />
|
<Footer />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(.navbar-link) {
|
:global(.nav-link) {
|
||||||
@apply text-lg;
|
@apply text-lg;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-family: serif;
|
font-family: serif;
|
||||||
}
|
}
|
||||||
:global(p.navbar-link) {
|
:global(a.disabled-link) {
|
||||||
color: rgb(128,128,128);
|
color: rgb(128,128,128);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
:global(a) {
|
:global(a) {
|
||||||
color: theme('colors.eucalyptus.400');
|
color: theme('colors.eucalyptus.400');
|
||||||
|
@ -64,7 +73,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
:global(a.active-link) {
|
:global(a.current-link) {
|
||||||
color: theme('colors.magenta.500');
|
color: theme('colors.magenta.500');
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -97,7 +106,7 @@
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#content {
|
main {
|
||||||
width: min(100vw, max(50vw, 100vh));
|
width: min(100vw, max(50vw, 100vh));
|
||||||
grid-area: content;
|
grid-area: content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,9 @@ export const prerender = true;
|
||||||
|
|
||||||
// Prevent page 404 on refresh
|
// Prevent page 404 on refresh
|
||||||
export const trailingSlash = 'always';
|
export const trailingSlash = 'always';
|
||||||
|
|
||||||
|
export async function load({ url }) {
|
||||||
|
return {
|
||||||
|
url: url.pathname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,11 +53,7 @@
|
||||||
{#each routes as item}
|
{#each routes as item}
|
||||||
{#if item.route !== "/"}
|
{#if item.route !== "/"}
|
||||||
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
|
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
|
||||||
{#if item.inactive ?? false}
|
<a class="navbar-link" href={item.route} class:disabled-link={item.disabled}>{item.name}</a>
|
||||||
<p class="navbar-link">{item.name}</p>
|
|
||||||
{:else}
|
|
||||||
<a class="navbar-link" href={item.route}>{item.name}</a>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,26 +1,32 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { routes } from "$lib/sitemap.ts"
|
import { routes } from "$lib/sitemap.ts"
|
||||||
|
import { name } from '$content/metadata.json';
|
||||||
|
|
||||||
function isActiveLink(pathname, route) {
|
function isCurrentLink(pathname, route) {
|
||||||
return route != "/" && pathname.startsWith(route);
|
return route != "/" && pathname.startsWith(route)
|
||||||
|
|| route == "/post" && pathname.startsWith("/page");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="navbar" class="h-5 space-x-4">
|
<div id="navbar" class="h-5 space-x-4">
|
||||||
|
<p id="bio-name">{name}</p>
|
||||||
{#each routes as item}
|
{#each routes as item}
|
||||||
{#if item.inactive ?? false}
|
<a
|
||||||
<p class="navbar-link"
|
class="nav-link"
|
||||||
>{item.name}</p>
|
class:current-link={isCurrentLink($page.url.pathname, item.route)}
|
||||||
{:else}
|
class:disabled-link={item.disabled}
|
||||||
<a class="navbar-link"
|
|
||||||
class:active-link={isActiveLink($page.url.pathname, item.route)}
|
|
||||||
href={item.route}>{item.name}</a>
|
href={item.route}>{item.name}</a>
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
#bio-name {
|
||||||
|
font-family: serif;
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: normal;
|
||||||
|
color: theme('colors.java.800');
|
||||||
|
}
|
||||||
#navbar {
|
#navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { blur } from 'svelte/transition'
|
||||||
|
|
||||||
|
export let url: string
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#key url}
|
||||||
|
<div class="transition" in:blur>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.transition {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
export let data: PageData;
|
||||||
|
const { posts, pageN, maxPageN } = data;
|
||||||
|
import siteMetadata from '$content/metadata.json';
|
||||||
|
|
||||||
|
import { DoubleArrowLeft, DoubleArrowRight, ChevronLeft, ChevronRight } from 'svelte-radix';
|
||||||
|
import PostHeader from '$lib/components/PostHeader.svelte';
|
||||||
|
|
||||||
|
const disableLinkPrev = pageN == 1;
|
||||||
|
const disableLinkNext = pageN == maxPageN;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Page {pageN } | {siteMetadata.blogName}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<hr class="separator" />
|
||||||
|
<ul id="catalog" class="content">
|
||||||
|
{#each posts as post}
|
||||||
|
<li>
|
||||||
|
<PostHeader metadata={post.meta} link={post.path} />
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<hr class="separator" />
|
||||||
|
<div id="page-navigator">
|
||||||
|
<a
|
||||||
|
class="nav-link icon"
|
||||||
|
class:disabled-link={disableLinkPrev}
|
||||||
|
href="/page/1"><DoubleArrowLeft /></a>
|
||||||
|
<a
|
||||||
|
class="nav-link icon"
|
||||||
|
class:disabled-link={disableLinkPrev}
|
||||||
|
href="/page/{Math.max(1, pageN-1)}"><ChevronLeft /></a>
|
||||||
|
<div id="page-map">
|
||||||
|
<p id="page-num">{pageN}/{maxPageN}</p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
class="nav-link icon"
|
||||||
|
class:disabled-link={disableLinkNext}
|
||||||
|
href="/page/{Math.min(maxPageN, pageN+1)}"><ChevronRight /></a>
|
||||||
|
<a
|
||||||
|
class="nav-link icon"
|
||||||
|
class:disabled-link={disableLinkNext}
|
||||||
|
href="/page/{maxPageN}"><DoubleArrowRight /></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#page-navigator {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: horizontal;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 1em auto 1em auto;
|
||||||
|
width: 80%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#page-map {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#page-num {
|
||||||
|
color: rgb(128,128,128);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import { getPosts } from '$lib/posts';
|
||||||
|
import siteMetadata from '$content/metadata.json';
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ params }) => {
|
||||||
|
const pageN: number = +params.slug;
|
||||||
|
if (!pageN) throw error(404);
|
||||||
|
|
||||||
|
const posts = await getPosts();
|
||||||
|
|
||||||
|
const pageSize = siteMetadata?.pageSize || 3;
|
||||||
|
|
||||||
|
const maxPageN = Math.ceil(posts.length / pageSize);
|
||||||
|
if (pageN < 0 || pageN > maxPageN) throw error(404);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageN,
|
||||||
|
posts: posts.slice((pageN - 1) * pageSize, pageN * pageSize),
|
||||||
|
maxPageN,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,30 +1,7 @@
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
import { getPosts } from '$lib/posts';
|
||||||
|
|
||||||
export const load: PageLoad = async (_) => {
|
export const load: PageLoad = async (_) => {
|
||||||
const allPostFiles = import.meta.glob('$content/post/*.md');
|
const allPosts = await getPosts();
|
||||||
const iterablePostFiles = Object.entries(allPostFiles);
|
return { allPosts };
|
||||||
|
|
||||||
const allPosts = await Promise.all(
|
|
||||||
iterablePostFiles.map(async ([pathMarkdown, resolver]) => {
|
|
||||||
const { metadata } = await resolver();
|
|
||||||
const pathPost = pathMarkdown.slice(pathMarkdown.lastIndexOf("/") + 1, -".md".length);
|
|
||||||
|
|
||||||
return {
|
|
||||||
meta: {
|
|
||||||
...metadata,
|
|
||||||
date: new Date(metadata.date),
|
|
||||||
},
|
|
||||||
path: pathPost
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
allPosts.sort((post1, post2) => {
|
|
||||||
const date1: Date = post1.meta.date;
|
|
||||||
const date2: Date = post2.meta.date;
|
|
||||||
return date2.getTime() - date1.getTime();
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
allPosts
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
const { allPosts } = data;
|
const { allPosts } = data;
|
||||||
import siteMetadata from '$content/metadata.json';
|
import siteMetadata from '$content/metadata.json';
|
||||||
import Heading from './Heading.svelte';
|
import PostHeader from '$lib/components/PostHeader.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<ul id="catalog" class="content">
|
<ul id="catalog" class="content">
|
||||||
{#each allPosts as post}
|
{#each allPosts as post}
|
||||||
<li>
|
<li>
|
||||||
<Heading metadata={post.meta} link={post.path} />
|
<PostHeader metadata={post.meta} link={post.path} />
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
const { metadata, Content } = data;
|
const { metadata, Content } = data;
|
||||||
import siteMetadata from '$content/metadata.json';
|
import siteMetadata from '$content/metadata.json';
|
||||||
import Heading from '../Heading.svelte';
|
import PostHeader from '$lib/components/PostHeader.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{metadata.title} | {siteMetadata.blogName}</title>
|
<title>{metadata.title} | {siteMetadata.blogName}</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Heading metadata={metadata}/>
|
<PostHeader metadata={metadata}/>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<article class="prose lg:prose-xl dark:prose-invert p-4">
|
<article class="prose lg:prose-xl dark:prose-invert p-4">
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import { getTags } from '$lib/posts';
|
||||||
|
|
||||||
|
export const load: PageLoad = async (_) => {
|
||||||
|
const allTags = await getTags();
|
||||||
|
return { allTags };
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
export let data: PageData;
|
||||||
|
const { allTags } = data;
|
||||||
|
import siteMetadata from '$content/metadata.json';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Tags | {siteMetadata.blogName}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h1>Tags</h1>
|
||||||
|
|
||||||
|
<hr class="separator" />
|
||||||
|
<div id="catalog" class="content">
|
||||||
|
{#each allTags as tag}
|
||||||
|
<a class="tag" href="/tag/{tag}">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<hr class="separator" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tag {
|
||||||
|
margin-left: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
export let data: PageData;
|
||||||
|
const { name, posts } = data;
|
||||||
|
import siteMetadata from '$content/metadata.json';
|
||||||
|
import PostHeader from '$lib/components/PostHeader.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{name} | {siteMetadata.blogName}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h1>{name}</h1>
|
||||||
|
|
||||||
|
<hr class="separator" />
|
||||||
|
<ul id="catalog" class="content">
|
||||||
|
{#each posts as post}
|
||||||
|
<li>
|
||||||
|
<PostHeader metadata={post.meta} link={post.path} />
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<hr class="separator" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#catalog li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import { getPosts } from '$lib/posts';
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ params }) => {
|
||||||
|
const posts = await getPosts(params.slug);
|
||||||
|
return {
|
||||||
|
name: params.slug,
|
||||||
|
posts,
|
||||||
|
};
|
||||||
|
}
|
|
@ -40,6 +40,12 @@ const config = {
|
||||||
precompress: false,
|
precompress: false,
|
||||||
trailingSlash: 'always',
|
trailingSlash: 'always',
|
||||||
}),
|
}),
|
||||||
|
prerender: {
|
||||||
|
crawl: true,
|
||||||
|
entries: [
|
||||||
|
"/page/1/",
|
||||||
|
],
|
||||||
|
},
|
||||||
alias: {
|
alias: {
|
||||||
$content: contentDir,
|
$content: contentDir,
|
||||||
"@/*": "./*",
|
"@/*": "./*",
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
|
test('Navigate to blog post', async ({ page }) => {
|
||||||
|
await page.goto('/post');
|
||||||
|
await page.getByText('The Perfect Math Class').click();
|
||||||
|
await expect(page).toHaveURL("/post/the-perfect-math-class/");
|
||||||
|
});
|
Loading…
Reference in New Issue