Add new article
All checks were successful
Deploy / build-and-publish (push) Successful in 1m22s
Deploy / deploy (push) Successful in 17s

This commit is contained in:
2024-10-04 21:54:42 +03:00
parent 50954731fd
commit b9f3b389a1
10 changed files with 312 additions and 55 deletions

78
package-lock.json generated
View File

@@ -1140,6 +1140,45 @@
"unist-util-visit": "^2.0.3" "unist-util-visit": "^2.0.3"
} }
}, },
"node_modules/@mavrin/remark-typograf/node_modules/unist-util-is": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz",
"integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==",
"dev": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/@mavrin/remark-typograf/node_modules/unist-util-visit": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
"integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
"dev": true,
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0",
"unist-util-visit-parents": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/@mavrin/remark-typograf/node_modules/unist-util-visit-parents": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
"integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
"dev": true,
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4773,16 +4812,6 @@
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
"dev": true "dev": true
}, },
"node_modules/unist-util-is": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz",
"integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==",
"dev": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-stringify-position": { "node_modules/unist-util-stringify-position": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
@@ -4796,35 +4825,6 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/unist-util-visit": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
"integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
"dev": true,
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0",
"unist-util-visit-parents": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-visit-parents": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
"integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
"dev": true,
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unplugin": { "node_modules/unplugin": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz",

View File

@@ -0,0 +1,9 @@
<script>
export let metadata;
</script>
<div class="text-xs">
{metadata.date}
</div>
<h1>{metadata.title}</h1>

View File

@@ -5,7 +5,6 @@
import {page} from '$app/stores'; import {page} from '$app/stores';
export let homepage: boolean = false export let homepage: boolean = false
export let title: string | null = null
let nav: HTMLElement; let nav: HTMLElement;
@@ -55,9 +54,9 @@
{/if} {/if}
</div> </div>
{#if title} <h1 class="text-2xl">
<h1 class="text-2xl">{title}</h1> <slot name="title"></slot>
{/if} </h1>
{#if !$page.url.pathname.startsWith('/blog')} {#if !$page.url.pathname.startsWith('/blog')}
<button <button

View File

@@ -3,7 +3,11 @@ import "./prism-one-dark.css";
import Navbar from "$lib/components/Navbar.svelte"; import Navbar from "$lib/components/Navbar.svelte";
</script> </script>
<Navbar title="Блог"></Navbar> <Navbar>
<div slot="title">
<a href="/blog">Блог</a>
</div>
</Navbar>
<article class="px-2 py-8 md:px-8 max-w-[980px] mx-auto text-lg"> <article class="px-2 py-8 md:px-8 max-w-[980px] mx-auto text-lg">
<slot /> <slot />
@@ -11,7 +15,7 @@ import Navbar from "$lib/components/Navbar.svelte";
<style lang="postcss"> <style lang="postcss">
:global(h1) { :global(h1) {
@apply text-3xl my-6; @apply text-5xl font-semibold my-6;
} }
:global(h2) { :global(h2) {
@@ -26,6 +30,10 @@ import Navbar from "$lib/components/Navbar.svelte";
@apply text-xl my-3; @apply text-xl my-3;
} }
:global(pre) {
max-width: calc(100vw - 16px);
}
:global(pre, code) { :global(pre, code) {
@apply rounded-lg; @apply rounded-lg;
} }

View File

@@ -1,6 +1,11 @@
import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx" import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx"
import {metadata as thisBlogMetadata} from "./this-blog/+page.svx"
const posts = [ const posts = [
{
href: '/blog/this-blog',
...thisBlogMetadata
},
{ {
href: '/blog/html-in-css', href: '/blog/html-in-css',
...htmlInCssMetadata ...htmlInCssMetadata

View File

@@ -3,9 +3,18 @@
</script> </script>
<section class="px-2 md:px-8 mb-16 max-w-[1280px] mx-auto"> <section class="px-2 md:px-8 mb-16 max-w-[1280px] mx-auto">
<h2 class="text-4xl mb-10 md:text-5xl">
Статьи
</h2>
<div class="flex flex-col gap-14">
{#each data.posts as post} {#each data.posts as post}
<a href={post.href}> <a
<h2 class="text-3xl mb-4 md:text-4xl">{post.title}</h2> href={post.href}
>
<div class="text-sm">{post.date}</div>
<h4 class="text-2xl md:text-3xl">{post.title}</h4>
</a> </a>
{/each} {/each}
</div>
</section> </section>

View File

@@ -3,11 +3,11 @@ title: "HTML in CSS: как сократить HTML-код и писать то
date: "2024-08-01" date: "2024-08-01"
--- ---
<div class="text-xs"> <script>
{date} import ArticleTitle from "$lib/components/ArticleTitle.svelte"
</div> </script>
# {title} <ArticleTitle metadata={metadata}/>
## О проекте ## О проекте

View File

@@ -0,0 +1,207 @@
---
title: "Как сделан этот блог?"
date: "2024-10-04"
---
<script>
import ArticleTitle from "$lib/components/ArticleTitle.svelte"
import CustomTabs from "./CustomTabs.svelte"
</script>
<ArticleTitle metadata={metadata} />
## Предисловие
Существует такой удивительный формат под названием MDX. Он представляет из себя смесь JSX и Markdown,
отсюда и MDX.
Он удобен тем, что на нем легко писать форматированный текст, но при этом не надо отказываться от возможности писать сложные
интерактивные компоненты, поскольку их можно вставить как обычный React JSX прямо в тело MDX файла.
<div class='grid md:grid-cols-2 gap-4'>
<div>
```markdown
### Пример статьи
<CustomTabs>
<CustomTab
value={1}
>
Да, можно **прямо тут** форматировать!
</CustomTab>
<CustomTab
value={2}
>
Или оставлять [ссылки](https://kopyl.dev)
привычным способом
</CustomTab>
</CustomTabs>
...
```
</div>
<div class='my-2 p-4'>
<h3 class='text-2xl'>Пример статьи</h3>
<CustomTabs>
<div slot="1">
Да, можно **прямо тут** форматировать!
</div>
<div slot="2">
Или оставлять [ссылки](https://kopyl.dev) привычным способом
</div>
</CustomTabs>
</div>
</div>
Есть целый ряд фреймворков, которые позволяют писать на нем, а для особо ушлых, конечно, есть и варианты без фреймворка.
MDX, в конце концов, просто компилируется в обычный React JSX, так что прикрутив этот компилятор MDX к обычному React проекту,
можно начать писать в нем MDX.
## А как все таки сделан этот блог?
Ведь эта страница, как и остальные на этом сайте, написана на Svelte!
К счастью для меня и остальных фанатов MDX, есть подобная альтернатива, интегрирующая Markdown и Svelte: <a href="https://github.com/pngwn/MDsveX" target="_blank">MDsveX</a>.
Хоть название и не лучшее, возможности не уступают оригиналу.
Самое главное, что у SVX (я буду так его называть), есть возможность передавать в компилятор <a href="https://github.com/remarkjs/remark" target="_blank">remark</a> и <a href="https://github.com/rehypejs/rehype" target="_blank">rehype</a> плагины.
## Создание проекта
### 1. Инициализация проекта
```bash
npm create svelte@latest my-blog
cd my-blog
```
Инициализируем проект на SvelteKit и переходим в каталог с ним.
### 2. Установка зависимостей
```bash
npm i -D mdsvex @mavrin/remark-typograf
```
Ставим наши зависимости, про вторую чуть позже.
### 3. Настройка конфигов
```js
// 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 -- в начале файла напишем блок выделенный минусами.
```markdown
---
title: "Как сделан этот блог?"
date: "2024-10-02"
---
```
Теперь эти поля доступны как переменные как в документе, так и для экспорта внутри объекта `metadata`.
```markdown
---
title: "Как сделан этот блог?"
date: "2024-10-02"
---
<h1>{title}</h1>
```
### 5. Создание списка статей
Теперь создадим файл `+page.server.ts` в каталоге `src/routes/blog`. Он будет предоставлять странице список всех статей.
```typescript
// 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` используем переданный список.
```html
// 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>
```
Готово!
## Наслаждаемся результатами
В нашем блоге настроен <a href="https://github.com/typograf/typograf" target="_blank">типограф</a>, что позволяет нам не заботиться о переносах строк в неположенных местах,
он заполняет текст неразрывными пробелами где надо.
Помимо этого, он заменяет `"` на `»`, выносит кавычки за пределы ссылки, заменяет `...` на `…`, превращает `--` в `—`.
Это только наиболее часто встречающиеся исправления, помимо них еще огромное количество исправлений и замен.

View File

@@ -0,0 +1,18 @@
<script lang="ts">
let firstTabActive = true;
</script>
<div class="p-8 border rounded-t-lg rounded-br-lg mt-8">
{#if firstTabActive}
<slot name="1"></slot>
{:else}
<slot name="2"></slot>
{/if}
</div>
<button
on:click={() => firstTabActive = !firstTabActive}
class="p-4 border rounded-b-lg bg-slate-800 hover:bg-slate-700"
>
Сменить таб
</button>

View File

@@ -9,7 +9,9 @@ const config = {
preprocess: [ preprocess: [
vitePreprocess(), vitePreprocess(),
mdsvex({ mdsvex({
remarkPlugins: [remarkTypograf] remarkPlugins: [
remarkTypograf,
],
}), }),
], ],
kit: { kit: {