Как сделать блог на Svelte + MDX?
Предисловие
Существует такой удивительный формат под названием MDX. Он представляет из себя смесь JSX и Markdown, отсюда и MDX.
Он удобен тем, что на нем легко писать форматированный текст, но при этом не надо отказываться от возможности писать сложные интерактивные компоненты, поскольку их можно вставить как обычный React JSX прямо в тело MDX файла.
### Пример статьи
<CustomTabs>
<CustomTab
value={1}
>
Да, можно **прямо тут** форматировать!
</CustomTab>
<CustomTab
value={2}
>
Или оставлять [ссылки](https://kopyl.dev)
привычным способом
</CustomTab>
</CustomTabs>
...
Пример статьи
Да, можно прямо тут форматировать!
Есть целый ряд фреймворков, которые позволяют писать на нем, а для особо ушлых, конечно, есть и варианты без фреймворка. MDX, в конце концов, просто компилируется в обычный React JSX, так что прикрутив этот компилятор MDX к обычному React проекту, можно начать писать в нем MDX.
А как все-таки сделан этот блог?
Ведь эта страница, как и остальные на этом сайте, написана на Svelte!
К счастью для меня и остальных фанатов MDX, есть подобная альтернатива, интегрирующая Markdown и Svelte: MDsveX.
Хоть название и не лучшее, возможности не уступают оригиналу. Самое главное, что у SVX (я буду так его называть), есть возможность передавать в компилятор remark и rehype плагины.
Создание проекта
1. Инициализация проекта
npm create svelte@latest my-blog
cd my-blog
Инициализируем проект на SvelteKit и переходим в каталог с ним.
2. Установка зависимостей
npm i -D mdsvex @mavrin/remark-typograf
Ставим наши зависимости, про вторую чуть позже.
3. Настройка конфигов
// svelte.config.js
import adapter from "@sveltejs/adapter-auto";
import {vitePreprocess} from "@sveltejs/vite-plugin-svelte";
import {mdsvex} from "mdsvex";
import remarkTypograf from "@mavrin/remark-typograf";
const config = {
extensions: [
".svelte",
".svx"
],
preprocess: [
vitePreprocess(),
mdsvex({
remarkPlugins: [remarkTypograf]
})
],
kit: {
adapter: adapter()
}
};
export default config;
Отличается от стандартного конфига лишь тем, что мы добавили mdsvex
в preprocess
и закинули .svx
в extensions
.
4. Создание страницы
Создадим каталог src/routes/blog
, в нем будут все наши статьи.
Чтобы создать страницу, создадим в этом каталоге каталог с произвольным именем this-blog
и в нем файл +page.svx
.
В нем можно писать как будто это обычный .svelte
файл, но весь Markdown будет на выходе форматирован.
Помимо этого SVX поддерживает Frontmatter — в начале файла напишем блок выделенный минусами.
---
title: "Как сделан этот блог?"
date: "2024-10-02"
---
Теперь эти поля доступны как переменные как в документе, так и для экспорта внутри объекта metadata
.
---
title: "Как сделан этот блог?"
date: "2024-10-02"
---
<h1>{title}</h1>
5. Создание списка статей
Теперь создадим файл +page.server.ts
в каталоге src/routes/blog
. Он будет предоставлять странице список всех статей.
// src/routes/blog/+page.server.ts
import {metadata as thisBlogMetadata} from "./this-blog/+page.svx"
const posts = [
{
href: "/blog/this-blog",
...thisBlogMetadata
}
]
export const load = async () => {
return {
posts
}
}
А в файл +page.svelte
используем переданный список.
// src/routes/blog/+page.svelte
<script>
export let data;
</script>
<h2>
Статьи
</h2>
<div class="flex flex-col gap-14">
{#each data.posts as post}
<a
href={post.href}
>
<div class="text-sm">{post.date}</div>
<h4 class="text-2xl md:text-3xl">{post.title}</h4>
</a>
{/each}
</div>
Готово!
Наслаждаемся результатами
В нашем блоге настроен типограф, что позволяет нам не заботиться о переносах строк в неположенных местах,
он заполняет текст неразрывными пробелами где надо.
Помимо этого, он заменяет "
на »
, выносит кавычки за пределы ссылки, заменяет ...
на …
, превращает --
в —
.
Это только наиболее часто встречающиеся исправления, помимо них еще огромное количество исправлений и замен.