Commit d843c51
Changed files (23)
src
components
integrations
layouts
pages
src/components/widgets/LocaleSwitcher.astro
@@ -20,22 +20,21 @@ const getLocaleUrl = (target: Locale) =>
getRelativeLocaleUrl(getPathWithoutLocale(currentPath), target) +
Astro.url.search;
---
+
<select
class="locale-switcher"
aria-label={globalI18n.language}
onchange="location.href = this.value"
>
- {
- availableLocales.map((target) => (
- <option
- value={getLocaleUrl(target)}
- selected={target === locale}
- lang={target}
- >
- {getLocaleName(getLocaleLang(target), target)}
- </option>
- ))
- }
+ {availableLocales.map((target) => (
+ <option
+ value={getLocaleUrl(target)}
+ selected={target === locale}
+ lang={target}
+ >
+ {getLocaleName(getLocaleLang(target), target)}
+ </option>
+ ))}
</select>
<style lang="scss" is:global>
src/components/widgets/PostsList.astro
@@ -16,23 +16,22 @@ const postUrl = (post) => {
return getRelativeLocaleUrl(slug, locale);
};
---
+
<nav class="posts-list">
<ol>
- {
- posts.map((post) => {
- const dt = Temporal.ZonedDateTime.from(post.data.date).toPlainDateTime();
- return (
- <li>
- <a href={postUrl(post)}>
- {post.data.title}
- </a>
- <time datetime={dt.toString()}>
- {dt.toPlainDate().toLocaleString(Astro.currentLocale || DEFAULT_LOCALE)}
- </time>
- </li>
- );
- })
- }
+ {posts.map((post) => {
+ const dt = Temporal.ZonedDateTime.from(post.data.date).toPlainDateTime();
+ return (
+ <li>
+ <a href={postUrl(post)}>{post.data.title}</a>
+ <time datetime={dt.toString()}>
+ {dt
+ .toPlainDate()
+ .toLocaleString(Astro.currentLocale || DEFAULT_LOCALE)}
+ </time>
+ </li>
+ );
+ })}
</ol>
</nav>
src/components/Header.astro
@@ -1,11 +1,9 @@
---
import { getIntlayer, type Locale } from "intlayer";
-
+import LocaleSwitcher from "@/components/widgets/LocaleSwitcher.astro";
import { SITE_CONFIG } from "@/config.ts";
import { getRelativeLocaleUrl } from "@/utils/url.ts";
-import LocaleSwitcher from "@/components/widgets/LocaleSwitcher.astro";
-
interface Props {
availableLocales: Locale[];
}
@@ -14,14 +12,26 @@ const { availableLocales } = Astro.props;
const locale = Astro.currentLocale! as Locale;
const globalI18n = getIntlayer("global", locale);
---
+
<header class="header">
- <a class="header-home"
- href={getRelativeLocaleUrl("/", locale)}>{SITE_CONFIG.title}</a>
+ <a class="header-home" href={getRelativeLocaleUrl("/", locale)}
+ >{SITE_CONFIG.title}</a
+ >
<nav>
<ul>
- <li><a href={getRelativeLocaleUrl("/posts", locale)}>{globalI18n.posts.title}</a></li>
- <li><a href={getRelativeLocaleUrl("/about", locale)}>{globalI18n.about}</a></li>
- <li><a href={getRelativeLocaleUrl("/links", locale)}>{globalI18n.friendLinks}</a></li>
+ <li>
+ <a href={getRelativeLocaleUrl("/posts", locale)}
+ >{globalI18n.posts.title}</a
+ >
+ </li>
+ <li>
+ <a href={getRelativeLocaleUrl("/about", locale)}>{globalI18n.about}</a>
+ </li>
+ <li>
+ <a href={getRelativeLocaleUrl("/links", locale)}
+ >{globalI18n.friendLinks}</a
+ >
+ </li>
</ul>
<LocaleSwitcher availableLocales={availableLocales} />
</nav>
src/integrations/extract-images.ts
@@ -28,18 +28,17 @@ const EXT_BY_MIME: Record<string, string> = {
const IMG_TAG = /<img\b[^>]*>/g;
const IMG_SRC = /src=(["'])(data:image\/[^"']+)\1/;
-function imageInfo(
- src: string,
-): { bytes: Buffer; filename: string } | null {
+function imageInfo(src: string): { bytes: Buffer; filename: string } | null {
const match = src.match(/^data:image\/([^;]+);base64,([A-Za-z0-9+/=]+)$/);
if (!match) return null;
const ext = EXT_BY_MIME[match[1]];
if (!ext) return null;
const bytes = Buffer.from(match[2], "base64");
- const hash = crypto.createHash("sha256").update(bytes).digest("hex").slice(
- 0,
- 12,
- );
+ const hash = crypto
+ .createHash("sha256")
+ .update(bytes)
+ .digest("hex")
+ .slice(0, 12);
return { bytes, filename: `${hash}.${ext}` };
}
@@ -118,7 +117,7 @@ export function extractImages(): AstroIntegration {
await fs.mkdir(outDir, { recursive: true });
await Promise.all(
[...written].map(([filename, bytes]) =>
- fs.writeFile(path.join(outDir, filename), bytes)
+ fs.writeFile(path.join(outDir, filename), bytes),
),
);
logger.info(
src/layouts/BaseLayout.astro
@@ -1,7 +1,8 @@
---
import { Font } from "astro:assets";
import { getIntlayer, type Locale } from "intlayer";
-
+import Footer from "@/components/Footer.astro";
+import Header from "@/components/Header.astro";
import { DEFAULT_LOCALE, SITE_CONFIG } from "@/config";
import {
getLocaleFeedUrl,
@@ -10,9 +11,6 @@ import {
url,
} from "@/utils/url.ts";
-import Footer from "@/components/Footer.astro";
-import Header from "@/components/Header.astro";
-
import "sanitize.css/sanitize.css";
import "@/styles/global.scss";
@@ -36,7 +34,7 @@ const {
} = Astro.props;
const pageLocales = SITE_CONFIG.locales.filter((candidate) =>
- availableLocales.includes(candidate)
+ availableLocales.includes(candidate),
);
if (!pageLocales.includes(DEFAULT_LOCALE)) {
@@ -45,57 +43,64 @@ if (!pageLocales.includes(DEFAULT_LOCALE)) {
);
}
-const getAbsoluteLocaleUrl = (
- path: string,
- locale: Locale,
-) => new URL(getRelativeLocaleUrl(path, locale), Astro.site).toString();
+const getAbsoluteLocaleUrl = (path: string, locale: Locale) =>
+ new URL(getRelativeLocaleUrl(path, locale), Astro.site).toString();
---
+
<!doctype html>
<html lang={locale}>
<head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width" />
- <meta name="generator" content={Astro.generator} />
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <meta name="generator" content={Astro.generator}>
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
- <link rel="icon" href="/favicon.ico" />
- <link rel="sitemap" href="/sitemap-index.xml" />
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
+ <link rel="icon" href="/favicon.ico">
+ <link rel="sitemap" href="/sitemap-index.xml">
<link
rel="alternate"
type="application/atom+xml"
hreflang={locale ?? DEFAULT_LOCALE}
title="Atom Feed"
href={getLocaleFeedUrl(locale ?? DEFAULT_LOCALE)}
- />
+ >
<link
rel="alternate"
type="application/atom+xml"
title="Atom Feed"
href={url("/feed.atom")}
- />
+ >
<title>{title}</title>
- <meta name="title" content={title} />
- <meta name="description" content={description} />
- <meta name="author" content={author} />
-
- {
- pageLocales.map((target) => (
- <link rel="alternate" hreflang={target}
- href={getAbsoluteLocaleUrl(getPathWithoutLocale(Astro.url.pathname), target)}
- >
- ))
- }
- <link rel="alternate" hreflang="x-default"
- href={getAbsoluteLocaleUrl(getPathWithoutLocale(Astro.url.pathname), DEFAULT_LOCALE)}
+ <meta name="title" content={title}>
+ <meta name="description" content={description}>
+ <meta name="author" content={author}>
+
+ {pageLocales.map((target) => (
+ <link
+ rel="alternate"
+ hreflang={target}
+ href={getAbsoluteLocaleUrl(
+ getPathWithoutLocale(Astro.url.pathname),
+ target,
+ )}
+ >
+ ))}
+ <link
+ rel="alternate"
+ hreflang="x-default"
+ href={getAbsoluteLocaleUrl(
+ getPathWithoutLocale(Astro.url.pathname),
+ DEFAULT_LOCALE,
+ )}
>
- <meta property="og:url" content={Astro.url} />
- <meta property="og:site_name" content={SITE_CONFIG.title} />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={description} />
- <meta property="og:locale" content={locale} />
- <meta property="og:type" content={ogType} />
+ <meta property="og:url" content={Astro.url}>
+ <meta property="og:site_name" content={SITE_CONFIG.title}>
+ <meta property="og:title" content={title}>
+ <meta property="og:description" content={description}>
+ <meta property="og:locale" content={locale}>
+ <meta property="og:type" content={ogType}>
<meta name="robots" content="noindex, nofollow">
src/pages/[...locale]/posts/[slug].astro
@@ -1,12 +1,11 @@
---
-import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getCollection, render } from "astro:content";
+import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { SITE_CONFIG } from "@/config.ts";
+import BaseLayout from "@/layouts/BaseLayout.astro";
import { getPostsLocaleMap } from "@/utils/content.ts";
import { getLocaleRouteParam } from "@/utils/url.ts";
-
-import BaseLayout from "@/layouts/BaseLayout.astro";
import "@/styles/article/index.scss";
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
@@ -16,21 +15,23 @@ export const getStaticPaths = (async () => {
const postsLocaleMap = getPostsLocaleMap(posts);
return Object.entries(postsLocaleMap).flatMap(([slug, localeMap]) => {
- const availableLocales = SITE_CONFIG.locales.filter((locale) =>
- localeMap[locale] !== undefined
+ const availableLocales = SITE_CONFIG.locales.filter(
+ (locale) => localeMap[locale] !== undefined,
);
- return SITE_CONFIG.locales.map((locale) => {
- const entry = localeMap[locale];
- if (entry === undefined) {
- return undefined;
- } else {
- return {
- params: { locale: getLocaleRouteParam(locale), slug },
- props: { entry, availableLocales },
- };
- }
- }).filter((i) => i !== undefined);
+ return SITE_CONFIG.locales
+ .map((locale) => {
+ const entry = localeMap[locale];
+ if (entry === undefined) {
+ return undefined;
+ } else {
+ return {
+ params: { locale: getLocaleRouteParam(locale), slug },
+ props: { entry, availableLocales },
+ };
+ }
+ })
+ .filter((i) => i !== undefined);
});
}) satisfies GetStaticPaths;
@@ -39,6 +40,7 @@ const { entry, availableLocales } = Astro.props;
const data = entry.data;
const { Content } = await render(entry);
---
+
<BaseLayout
title={data.title}
author={data.author}
@@ -50,11 +52,14 @@ const { Content } = await render(entry);
<Content />
<footer>
<small>
- <p>License: {
- typeof data.license === "string"
- ? data.license
- : <a href={data.license.link}>{data.license.name}</a>
- }</p>
+ <p>
+ License:
+ {typeof data.license === "string" ? (
+ data.license
+ ) : (
+ <a href={data.license.link}>{data.license.name}</a>
+ )}
+ </p>
</small>
</footer>
</article>
src/pages/[...locale]/posts/index.astro
@@ -1,15 +1,13 @@
---
-import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getCollection } from "astro:content";
+import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getIntlayer } from "intlayer";
-
+import PostsList from "@/components/widgets/PostsList.astro";
import { SITE_CONFIG } from "@/config.ts";
+import BaseLayout from "@/layouts/BaseLayout.astro";
import { getLocalePosts } from "@/utils/content.ts";
import { getLocaleRouteParam } from "@/utils/url.ts";
-import BaseLayout from "@/layouts/BaseLayout.astro";
-import PostsList from "@/components/widgets/PostsList.astro";
-
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
export const getStaticPaths = (() => {
@@ -22,8 +20,8 @@ const locale = Astro.currentLocale;
const globalI18n = getIntlayer("global", locale);
const posts = getLocalePosts(await getCollection("posts"), locale);
-const sortedPosts = posts.sort(
- (a, b) => Temporal.ZonedDateTime.compare(b.data.date, a.data.date),
+const sortedPosts = posts.sort((a, b) =>
+ Temporal.ZonedDateTime.compare(b.data.date, a.data.date),
);
const postsByYear = sortedPosts.reduce(
(acc, post) => {
@@ -37,18 +35,17 @@ const postsByYear = sortedPosts.reduce(
{} as Record<number, typeof posts>,
);
---
+
<BaseLayout title={globalI18n.posts.title}>
- <div style={{fontSize: "1.2rem"}}>
+ <div style={{ fontSize: "1.2rem" }}>
<h1>{globalI18n.posts.title}</h1>
- {
- Object.entries(postsByYear)
- .reverse()
- .map(([year, posts]) => (
- <section>
- <h2>{year}</h2>
- <PostsList posts={posts} />
- </section>
- ))
- }
+ {Object.entries(postsByYear)
+ .reverse()
+ .map(([year, posts]) => (
+ <section>
+ <h2>{year}</h2>
+ <PostsList posts={posts} />
+ </section>
+ ))}
</div>
</BaseLayout>
src/pages/[...locale]/about.astro
@@ -3,21 +3,22 @@ import type { GetStaticPaths } from "astro";
import { getIntlayer } from "intlayer";
import { SITE_CONFIG } from "@/config.ts";
+import BaseLayout from "@/layouts/BaseLayout.astro";
import { loadLocaleContent } from "@/utils/content.ts";
import { getLocaleRouteParam } from "@/utils/url.ts";
-
-import BaseLayout from "@/layouts/BaseLayout.astro";
import "@/styles/article/index.scss";
-export const getStaticPaths: GetStaticPaths = () => (SITE_CONFIG.locales.map(
- (locale) => ({ params: { locale: getLocaleRouteParam(locale) } }),
-));
+export const getStaticPaths: GetStaticPaths = () =>
+ SITE_CONFIG.locales.map((locale) => ({
+ params: { locale: getLocaleRouteParam(locale) },
+ }));
const locale = Astro.currentLocale;
const globalI18n = getIntlayer("global", locale);
const { Content: Article } = await loadLocaleContent("about", locale);
---
+
<BaseLayout title={globalI18n.about} ogType="article">
<article>
<h1>{globalI18n.about}</h1>
src/pages/[...locale]/index.astro
@@ -1,15 +1,13 @@
---
-import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getCollection } from "astro:content";
+import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getIntlayer } from "intlayer";
-
+import PostsList from "@/components/widgets/PostsList.astro";
import { SITE_CONFIG } from "@/config.ts";
+import BaseLayout from "@/layouts/BaseLayout.astro";
import { getLocalePosts, loadLocaleContent } from "@/utils/content.ts";
import { shuffle } from "@/utils/random.ts";
import { getLocaleRouteParam } from "@/utils/url.ts";
-
-import BaseLayout from "@/layouts/BaseLayout.astro";
-import PostsList from "@/components/widgets/PostsList.astro";
import "@/styles/article/index.scss";
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
@@ -26,16 +24,17 @@ const globalI18n = getIntlayer("global", locale);
const { Content: Article } = await loadLocaleContent("home", locale);
const posts = getLocalePosts(await getCollection("posts"), locale);
-const sortedPosts = posts.sort(
- (a, b) => Temporal.ZonedDateTime.compare(b.data.date, a.data.date),
+const sortedPosts = posts.sort((a, b) =>
+ Temporal.ZonedDateTime.compare(b.data.date, a.data.date),
);
---
+
<BaseLayout title="Kerplunk">
<article>
<h1>{SITE_CONFIG.title}</h1>
<Article />
</article>
- <div style={{fontSize: "1.2rem"}}>
+ <div style={{ fontSize: "1.2rem" }}>
<h2 id="recent-posts">{globalI18n.posts.recent}</h2>
<PostsList posts={sortedPosts.slice(0, 5)} />
<h2 id="recent-posts">{globalI18n.posts.random}</h2>
src/pages/404.astro
@@ -1,6 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
---
+
<BaseLayout>
<div>404 Not Found</div>
</BaseLayout>
src/pages/feed.[locale].atom.ts
@@ -1,5 +1,5 @@
-import type { APIRoute, GetStaticPaths } from "astro";
import { getCollection } from "astro:content";
+import type { APIRoute, GetStaticPaths } from "astro";
import type { Locale } from "intlayer";
import { SITE_CONFIG } from "@/config.ts";
@@ -17,7 +17,7 @@ export const GET: APIRoute = async ({ site, params }) => {
const origin = site.origin;
const locale = params.locale as Locale;
const posts = (await getCollection("posts")).filter((post) =>
- post.id.startsWith(`${locale}/`)
+ post.id.startsWith(`${locale}/`),
);
const feed = await createFeed({
id: new URL(getRelativeLocaleUrl("/", locale), origin).toString(),
src/pages/feed.atom.ts
@@ -1,5 +1,5 @@
-import type { APIRoute } from "astro";
import { getCollection } from "astro:content";
+import type { APIRoute } from "astro";
import { DEFAULT_LOCALE, SITE_CONFIG } from "@/config.ts";
import { createFeed, localePostUrl } from "@/utils/feed.ts";
src/utils/plugins/rewrite-images.ts
@@ -1,5 +1,5 @@
-import type { RehypePlugin } from "@astrojs/markdown-remark";
import { getImage } from "astro:assets";
+import type { RehypePlugin } from "@astrojs/markdown-remark";
import { visit } from "unist-util-visit";
import { ASSET_DIR, dataImageFilename } from "@/integrations/extract-images.ts";
@@ -33,9 +33,10 @@ function parseJsxString(literal: string | undefined): string {
// `kp.img` renders Astro `<Image>` JSX into a script element. Feed readers
// cannot execute it, so turn those scripts into plain `<img>` elements backed
// by the same image service the pages use.
-export function rehypeRewriteJsxImages(
- options: { site: string; width: number },
-): RehypePlugin {
+export function rehypeRewriteJsxImages(options: {
+ site: string;
+ width: number;
+}): RehypePlugin {
return () => async (tree) => {
const replacements: Replacement[] = [];
@@ -66,10 +67,7 @@ export function rehypeRewriteJsxImages(
type: "element",
tagName: "img",
properties: {
- src: new URL(
- image?.src ?? url(source),
- options.site,
- ).toString(),
+ src: new URL(image?.src ?? url(source), options.site).toString(),
alt: parseJsxString(jsx.match(JSX_IMAGE_ALT)?.[1]),
width: image?.attributes.width,
height: image?.attributes.height,
src/utils/content.ts
@@ -1,5 +1,5 @@
+import type { CollectionEntry } from "astro:content";
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
-import { type CollectionEntry } from "astro:content";
import { DEFAULT_LOCALE } from "@/config.ts";
@@ -17,9 +17,10 @@ export async function loadLocaleContent(
const hasFlatFile = Boolean(contents[`/src/contents/${name}.${locale}.typ`]);
const hasNestFile = Boolean(contents[`/src/contents/${name}/${locale}.typ`]);
- const resolvedLocale = (hasFlatFile || hasNestFile) ? locale : DEFAULT_LOCALE;
+ const resolvedLocale = hasFlatFile || hasNestFile ? locale : DEFAULT_LOCALE;
- const loadedFile = contents[`/src/contents/${name}.${resolvedLocale}.typ`] ||
+ const loadedFile =
+ contents[`/src/contents/${name}.${resolvedLocale}.typ`] ||
contents[`/src/contents/${name}/${resolvedLocale}.typ`];
if (!loadedFile) {
throw new Error(`Missing content: /src/contents/${name}.${locale}.typ`);
@@ -51,9 +52,9 @@ export function getLocalePosts(
locale: string = DEFAULT_LOCALE,
) {
const postsLocaleMap = getPostsLocaleMap(posts);
- return Object.values(postsLocaleMap).map((
- locales,
- ) => (locales[locale] || locales[DEFAULT_LOCALE]));
+ return Object.values(postsLocaleMap).map(
+ (locales) => locales[locale] || locales[DEFAULT_LOCALE],
+ );
}
export function getPostLocaleSlug(post: CollectionEntry<"posts">) {
src/utils/feed.ts
@@ -3,8 +3,6 @@ import { Feed } from "feed";
import { SITE_CONFIG } from "@/config.ts";
import { getPostLocaleSlug } from "@/utils/content.ts";
-import { getRelativeLocaleUrl } from "@/utils/url.ts";
-
import { rehypeAbsoluteLinks } from "@/utils/plugins/absolute-links.ts";
import { rehypeCleanEndnotes } from "@/utils/plugins/clean-endnotes.ts";
import { rehypeCleanProperties } from "@/utils/plugins/clean-properties.ts";
@@ -14,6 +12,7 @@ import {
rehypeRewriteJsxImages,
} from "@/utils/plugins/rewrite-images.ts";
import { renderTypstToHtml } from "@/utils/render-typst.ts";
+import { getRelativeLocaleUrl } from "@/utils/url.ts";
// Matches the `width` that `kp.img` passes to Astro's `<Image />`.
const FEED_IMAGE_WIDTH = 780;
@@ -60,7 +59,7 @@ export async function createFeed(data: FeedData): Promise<Feed> {
});
const posts = [...data.posts].sort((a, b) =>
- Temporal.ZonedDateTime.compare(b.data.date, a.data.date)
+ Temporal.ZonedDateTime.compare(b.data.date, a.data.date),
);
const items = await Promise.all(
@@ -71,9 +70,10 @@ export async function createFeed(data: FeedData): Promise<Feed> {
id: link,
link,
description: post.data.desc,
- content: typeof post.filePath === "string"
- ? await renderFeedContent(post.filePath, data.site)
- : undefined,
+ content:
+ typeof post.filePath === "string"
+ ? await renderFeedContent(post.filePath, data.site)
+ : undefined,
author: [{ name: post.data.author || SITE_CONFIG.author }],
date: new Date(
Temporal.ZonedDateTime.from(post.data.date).epochMilliseconds,
src/utils/random.ts
@@ -1,16 +1,13 @@
export function mulberry32(a: number) {
- return function () {
- let t = a += 0x6D2B79F5;
- t = Math.imul(t ^ t >>> 15, t | 1);
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
+ return () => {
+ let t = (a += 0x6d2b79f5);
+ t = Math.imul(t ^ (t >>> 15), t | 1);
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
-export function shuffle<T>(
- arr: readonly T[],
- generator = Math.random,
-): T[] {
+export function shuffle<T>(arr: readonly T[], generator = Math.random): T[] {
const a = arr.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(generator() * (i + 1));
src/utils/render-typst.ts
@@ -2,8 +2,8 @@ import { root } from "astro:config/server";
import { fileURLToPath } from "node:url";
import { NodeCompiler } from "@myriaddreamin/typst-ts-node-compiler";
-import { rehypeIt } from "astro-typst/dist/lib/rehype";
import type { RehypePlugins } from "astro";
+import { rehypeIt } from "astro-typst/dist/lib/rehype";
const $typst = NodeCompiler.create({
workspace: fileURLToPath(root),
src/config.ts
@@ -16,8 +16,8 @@ export const DEFAULT_LOCALE = SITE_CONFIG.locales[0];
const protocol = SITE_CONFIG.website.split("://")[0];
const host = SITE_CONFIG.website.split("://")[1].split("/")[0];
-const basePath = SITE_CONFIG.website.split(host).slice(1).join("/") ||
- undefined;
+const basePath =
+ SITE_CONFIG.website.split(host).slice(1).join("/") || undefined;
export const WEBSITE = {
protocol,
host,
src/content.config.ts
@@ -1,69 +1,73 @@
import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";
-import { type Locale } from "intlayer";
import { slug as slugify } from "github-slugger";
+import type { Locale } from "intlayer";
import { SITE_CONFIG } from "@/config.ts";
-const typCollection = (base: string) => (defineCollection({
- loader: glob({
- pattern: ["**/[^_]*.{typ,typst}", "!**/_*/**"],
- base: "./src/contents/" + base,
- generateId: ({ entry, data }) => {
- const slugFromData = data.slug as string | undefined;
- const filename = entry.split("/").at(-1);
- const isFlatEntry = (filename.split(".").length > 2) &&
- (SITE_CONFIG.locales.indexOf(filename.split(".").at(-2) as Locale) !==
- -1);
- const locale = filename.split(".").at(-2);
- const slugFromEntry = isFlatEntry
- ? filename.split(".").slice(0, -2).join("-")
- : entry.split("/").at(-2);
- return `${locale}/${slugify(slugFromData || slugFromEntry)}`;
- },
- }),
- schema: z.object({
- title: z.string(),
- slug: z.string().optional(),
- author: z.string().default(SITE_CONFIG.author),
- desc: z.any().optional(),
- date: z.string().transform((str) => {
- const match = str.match(
- /datetime\(\s*year:\s*(\d+),?\s*month:\s*(\d+),?\s*day:\s*(\d+)(?:,?\s*hour:\s*(\d+))?(?:,?\s*minute:\s*(\d+))?(?:,?\s*second:\s*(\d+))?,?\s*\)/,
- );
- if (match) {
- const year = parseInt(match[1], 10);
- const month = parseInt(match[2], 10);
- const day = parseInt(match[3], 10);
- const hour = match[4] ? parseInt(match[4], 10) : 0;
- const minute = match[5] ? parseInt(match[5], 10) : 0;
- const second = match[6] ? parseInt(match[6], 10) : 0;
-
- return Temporal.ZonedDateTime.from({
- year,
- month,
- day,
- hour,
- minute,
- second,
- timeZone: SITE_CONFIG.timeZone,
- }).toString();
- }
- return z.NEVER;
+const typCollection = (base: string) =>
+ defineCollection({
+ loader: glob({
+ pattern: ["**/[^_]*.{typ,typst}", "!**/_*/**"],
+ base: "./src/contents/" + base,
+ generateId: ({ entry, data }) => {
+ const slugFromData = data.slug as string | undefined;
+ const filename = entry.split("/").at(-1);
+ const isFlatEntry =
+ filename.split(".").length > 2 &&
+ SITE_CONFIG.locales.indexOf(filename.split(".").at(-2) as Locale) !==
+ -1;
+ const locale = filename.split(".").at(-2);
+ const slugFromEntry = isFlatEntry
+ ? filename.split(".").slice(0, -2).join("-")
+ : entry.split("/").at(-2);
+ return `${locale}/${slugify(slugFromData || slugFromEntry)}`;
+ },
}),
- license: z.xor([
- z.object({
- name: z.string(),
- link: z.url(),
+ schema: z.object({
+ title: z.string(),
+ slug: z.string().optional(),
+ author: z.string().default(SITE_CONFIG.author),
+ desc: z.any().optional(),
+ date: z.string().transform((str) => {
+ const match = str.match(
+ /datetime\(\s*year:\s*(\d+),?\s*month:\s*(\d+),?\s*day:\s*(\d+)(?:,?\s*hour:\s*(\d+))?(?:,?\s*minute:\s*(\d+))?(?:,?\s*second:\s*(\d+))?,?\s*\)/,
+ );
+ if (match) {
+ const year = parseInt(match[1], 10);
+ const month = parseInt(match[2], 10);
+ const day = parseInt(match[3], 10);
+ const hour = match[4] ? parseInt(match[4], 10) : 0;
+ const minute = match[5] ? parseInt(match[5], 10) : 0;
+ const second = match[6] ? parseInt(match[6], 10) : 0;
+
+ return Temporal.ZonedDateTime.from({
+ year,
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ timeZone: SITE_CONFIG.timeZone,
+ }).toString();
+ }
+ return z.NEVER;
}),
- z.string(),
- ]).default({
- name: "CC BY-NC-SA 4.0",
- link: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ license: z
+ .xor([
+ z.object({
+ name: z.string(),
+ link: z.url(),
+ }),
+ z.string(),
+ ])
+ .default({
+ name: "CC BY-NC-SA 4.0",
+ link: "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ }),
}),
- }),
-}));
+ });
export const collections = {
posts: typCollection("posts"),
astro.config.ts
@@ -1,6 +1,5 @@
-import { defineConfig, fontProviders } from "astro/config";
-
import sitemap from "@astrojs/sitemap";
+import { defineConfig, fontProviders } from "astro/config";
import { intlayer } from "astro-intlayer";
import { typst } from "astro-typst";
@@ -48,9 +47,7 @@ export default defineConfig({
// But astro-typst only read this entry for it's own hast process
// TODO: Replace with `markdown.processor = unified({rehypePlugins: [...]})`
// after astro-typst add Astro v7 style config support
- rehypePlugins: [
- rehypeCleanEndnotes,
- ],
+ rehypePlugins: [rehypeCleanEndnotes],
},
fonts: [
{
intlayer.config.ts
@@ -1,4 +1,4 @@
-import { type IntlayerConfig } from "intlayer";
+import type { IntlayerConfig } from "intlayer";
import { DEFAULT_LOCALE, SITE_CONFIG, WEBSITE } from "./src/config.ts";
tsconfig.json
@@ -1,19 +1,11 @@
{
"extends": "astro/tsconfigs/strict",
- "include": [
- ".astro/types.d.ts",
- ".intlayer/**/*.ts",
- "**/*"
- ],
- "exclude": [
- "dist"
- ],
+ "include": [".astro/types.d.ts", ".intlayer/**/*.ts", "**/*"],
+ "exclude": ["dist"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
- "@/*": [
- "./src/*"
- ]
+ "@/*": ["./src/*"]
}
}
}