mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
Merge branch 'release/1.0.3'
This commit is contained in:
commit
e2b98b5181
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "friday"
|
||||||
|
target-branch: "develop"
|
||||||
@ -26,11 +26,12 @@ import { defineConfig } from "astro/config";
|
|||||||
import { h } from "hastscript";
|
import { h } from "hastscript";
|
||||||
import { toString } from "hast-util-to-string";
|
import { toString } from "hast-util-to-string";
|
||||||
import { escape } from "html-escaper";
|
import { escape } from "html-escaper";
|
||||||
|
import { DEFAULT_LANGUAGE } from "./src/config";
|
||||||
import languages from "./src/i18n/languages";
|
import languages from "./src/i18n/languages";
|
||||||
|
|
||||||
const createAnchorLabel = (heading: string) => {
|
const createAnchorLabel = (heading: string) => {
|
||||||
const node = h("span.anchor-label", escape(heading));
|
const node = h("span.anchor-label", escape(heading));
|
||||||
node.properties["is:raw"] = true;
|
if (node.properties) node.properties["is:raw"] = true;
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export default defineConfig({
|
|||||||
solid(),
|
solid(),
|
||||||
sitemap({
|
sitemap({
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: "ru",
|
defaultLocale: DEFAULT_LANGUAGE,
|
||||||
locales: Object.fromEntries(Object.keys(languages).map((key) => [key, key])),
|
locales: Object.fromEntries(Object.keys(languages).map((key) => [key, key])),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -111,7 +112,4 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
experimental: {
|
|
||||||
integrations: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
1393
package-lock.json
generated
1393
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yarkov.tech",
|
"name": "yarkov.tech",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Personal website created with Astro.",
|
"description": "Personal website created with Astro.",
|
||||||
"author": {
|
"author": {
|
||||||
@ -19,20 +19,20 @@
|
|||||||
"lint": "eslint '**/*.{js,mjs,ts,tsx}'"
|
"lint": "eslint '**/*.{js,mjs,ts,tsx}'"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/image": "^0.3.7",
|
"@astrojs/image": "^0.7.1",
|
||||||
"@astrojs/mdx": "^0.10.3",
|
"@astrojs/mdx": "^0.11.1",
|
||||||
"@astrojs/rss": "^1.0.0",
|
"@astrojs/rss": "^1.0.1",
|
||||||
"@astrojs/sitemap": "^1.0.0",
|
"@astrojs/sitemap": "^1.0.0",
|
||||||
"@astrojs/solid-js": "^1.1.0",
|
"@astrojs/solid-js": "^1.1.0",
|
||||||
"@types/html-escaper": "^3.0.0",
|
"@types/html-escaper": "^3.0.0",
|
||||||
"@types/mdx": "^2.0.2",
|
"@types/mdx": "^2.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@typescript-eslint/parser": "^5.38.0",
|
||||||
"astro": "^1.1.2",
|
"astro": "^1.3.0",
|
||||||
"astro-compress": "^1.0.6",
|
"astro-compress": "^1.0.8",
|
||||||
"astro-icon": "^0.7.3",
|
"astro-icon": "^0.7.3",
|
||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.1",
|
||||||
"eslint-plugin-solid": "^0.7.1",
|
"eslint-plugin-solid": "^0.7.3",
|
||||||
"hast-util-to-string": "^2.0.0",
|
"hast-util-to-string": "^2.0.0",
|
||||||
"hastscript": "^7.0.2",
|
"hastscript": "^7.0.2",
|
||||||
"html-escaper": "^3.0.3",
|
"html-escaper": "^3.0.3",
|
||||||
@ -44,7 +44,7 @@
|
|||||||
"rehype-slug": "^5.0.1",
|
"rehype-slug": "^5.0.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"remark-smartypants": "^2.0.0",
|
"remark-smartypants": "^2.0.0",
|
||||||
"sass": "^1.54.7",
|
"sass": "^1.55.0",
|
||||||
"solid-js": "^1.5.3"
|
"solid-js": "^1.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { Image, Picture } from "@astrojs/image/components";
|
import { Picture } from "@astrojs/image/components";
|
||||||
import { createDateFormatter } from "@root/utils";
|
import { createDateFormatter } from "@root/utils";
|
||||||
|
import { isURL } from "@utils/string";
|
||||||
import ArticleComments from "@components/ArticleComments";
|
import ArticleComments from "@components/ArticleComments";
|
||||||
import "./ArticlePost.scss";
|
|
||||||
import TranslatedLink from "../TranslatedLink";
|
import TranslatedLink from "../TranslatedLink";
|
||||||
|
import "./ArticlePost.scss";
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
data: ArticleType;
|
data: ArticleType;
|
||||||
@ -44,7 +45,7 @@ const { title, coverImage, tags, author, published_at } = data;
|
|||||||
<figure>
|
<figure>
|
||||||
<Picture
|
<Picture
|
||||||
itemprop="image"
|
itemprop="image"
|
||||||
src={coverImage}
|
src={isURL(coverImage) ? coverImage : `../../../content/assets/images/${coverImage}`}
|
||||||
sizes="(max-width: 500px) 500px, 700px"
|
sizes="(max-width: 500px) 500px, 700px"
|
||||||
widths={[500, 700]}
|
widths={[500, 700]}
|
||||||
formats={["webp"]}
|
formats={["webp"]}
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
---
|
---
|
||||||
import { DEFAULT_LANGUAGE } from "@root/config";
|
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/config";
|
||||||
import { getLanguageFromURL } from "@root/utils";
|
import { getLanguageFromURL } from "@root/utils";
|
||||||
|
|
||||||
export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
|
export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
|
||||||
to: string;
|
to: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
let lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const { to, ...attrs } = Astro.props as Props;
|
const { to, ...attrs } = Astro.props as Props;
|
||||||
|
|
||||||
|
if (!SUPPORTED_LANGUAGES.includes(lang)) lang = DEFAULT_LANGUAGE;
|
||||||
|
|
||||||
const href = lang === DEFAULT_LANGUAGE ? to : `/${lang}${to}`;
|
const href = lang === DEFAULT_LANGUAGE ? to : `/${lang}${to}`;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -115,6 +115,18 @@ export const SITE_METADATA: SiteMetadataType = {
|
|||||||
demoUrl: undefined,
|
demoUrl: undefined,
|
||||||
articleUrl: undefined,
|
articleUrl: undefined,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: "serial",
|
||||||
|
name: "web-serial-example",
|
||||||
|
year: "2022",
|
||||||
|
description: {
|
||||||
|
ru: "Пример использования Web Serial API для взаимодействия с микроконтроллером через браузер.",
|
||||||
|
en: "An example of using the Web Serial API to interact with the microcontroller through a browser",
|
||||||
|
},
|
||||||
|
sourceUrl: "https://github.com/sergeyyarkov/avr128da48_web-serial-example",
|
||||||
|
demoUrl: "https://web-serial-example.netlify.app/",
|
||||||
|
articleUrl: undefined,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
skills: [
|
skills: [
|
||||||
{
|
{
|
||||||
@ -132,7 +144,6 @@ export const SITE_METADATA: SiteMetadataType = {
|
|||||||
name: "C Language",
|
name: "C Language",
|
||||||
},
|
},
|
||||||
{ icon: "mysql", name: "SQL" },
|
{ icon: "mysql", name: "SQL" },
|
||||||
{ icon: "graphql", name: "GraphQL" },
|
|
||||||
{
|
{
|
||||||
icon: "avr",
|
icon: "avr",
|
||||||
name: "AVR instruction set",
|
name: "AVR instruction set",
|
||||||
|
|||||||
1
src/icons/serial.svg
Normal file
1
src/icons/serial.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><path fill="#000" d="M5 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1Zm1.5 1.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0ZM8 10a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1Zm2.5-.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0ZM7 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1Zm2.5-.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0ZM11 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1ZM3.022 5a2 2 0 0 0-1.944 2.47l.725 3A2 2 0 0 0 3.747 12h8.519a2 2 0 0 0 1.944-1.53l.726-3A2 2 0 0 0 12.992 5h-9.97ZM2.05 7.235A1 1 0 0 1 3.022 6h9.97a1 1 0 0 1 .972 1.235l-.726 3a1 1 0 0 1-.972.765h-8.52a1 1 0 0 1-.971-.765l-.725-3Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 650 B |
60
src/lib/api.ts
Normal file
60
src/lib/api.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import type { AstroGlobal, MDXInstance } from "astro";
|
||||||
|
|
||||||
|
import { formatArticlesByLangs } from "@i18n/utils";
|
||||||
|
import { getLanguageFromURL } from "@root/utils";
|
||||||
|
import { DEFAULT_LANGUAGE } from "@root/config";
|
||||||
|
|
||||||
|
import * as utils from "@utils/index";
|
||||||
|
|
||||||
|
export function fetchTranslatedArticles(Astro: Readonly<AstroGlobal>): MDXInstance<ArticleType>[] {
|
||||||
|
const data = import.meta.glob<MDXInstance<ArticleType>>("../../content/articles/**/*.mdx", { eager: true });
|
||||||
|
const modules = formatArticlesByLangs(data);
|
||||||
|
const siteLang = utils.getLanguageFromURL(Astro.url.pathname);
|
||||||
|
const articles: MDXInstance<ArticleType>[] = [];
|
||||||
|
const push = (article: MDXInstance<ArticleType>, lang: LanguageKeys) => {
|
||||||
|
articles.push(article);
|
||||||
|
article.frontmatter.lang = lang;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const dirName of Object.keys(modules)) {
|
||||||
|
const article = modules[dirName];
|
||||||
|
const langs = Object.keys(article) as LanguageKeys[];
|
||||||
|
|
||||||
|
for (const lang of langs) {
|
||||||
|
if (lang !== siteLang) {
|
||||||
|
if (langs.length === 1) {
|
||||||
|
push(article[lang], lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!langs.includes(siteLang) && langs.length >= 2) {
|
||||||
|
const fallbackLang = langs[0];
|
||||||
|
const isPushed = articles.find(({ frontmatter }) => {
|
||||||
|
return frontmatter.title === article[fallbackLang].frontmatter.title;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isPushed) {
|
||||||
|
push(article[fallbackLang], langs[fallbackLang]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(article[lang], lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.getPublishedArticles(articles);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAboutMe(Astro: Readonly<AstroGlobal>): Promise<MDXInstance<{}>> {
|
||||||
|
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
|
let markdown: MDXInstance<{}> | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
markdown = (await import(`../../content/about/${lang}/index.mdx`)) as MDXInstance<{}>;
|
||||||
|
} catch (error) {
|
||||||
|
markdown = (await import(`../../content/about/${DEFAULT_LANGUAGE}/index.mdx`)) as MDXInstance<{}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import type { AstroGlobal, MDXInstance } from "astro";
|
|
||||||
import { formatArticlesByLangs } from "@i18n/utils";
|
|
||||||
import { getLanguageFromURL } from "@utils/url";
|
|
||||||
|
|
||||||
class ArticleService {
|
|
||||||
public getTranslatedArticles(Astro: Readonly<AstroGlobal>): MDXInstance<ArticleType>[] {
|
|
||||||
const data = import.meta.glob<MDXInstance<ArticleType>>("../../content/articles/**/*.mdx", {
|
|
||||||
eager: true,
|
|
||||||
});
|
|
||||||
const modules = formatArticlesByLangs(data);
|
|
||||||
const siteLang = getLanguageFromURL(Astro.url.pathname);
|
|
||||||
const articles: MDXInstance<ArticleType>[] = [];
|
|
||||||
const push = (article: MDXInstance<ArticleType>, lang: LanguageKeys) => {
|
|
||||||
articles.push(article);
|
|
||||||
article.frontmatter.lang = lang;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key of Object.keys(modules)) {
|
|
||||||
const langs = Object.keys(modules[key]) as LanguageKeys[];
|
|
||||||
for (const lang of langs) {
|
|
||||||
const isNonDefaultLanguage = !langs.includes(siteLang) && langs.length === 1;
|
|
||||||
const article = modules[key];
|
|
||||||
|
|
||||||
if (lang === siteLang) push(article[siteLang], siteLang);
|
|
||||||
if (isNonDefaultLanguage) push(article[lang], lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return articles.filter((article) => !article.frontmatter.draft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new ArticleService();
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { default as ArticleService } from "./ArticleService";
|
|
||||||
@ -1,26 +1,17 @@
|
|||||||
---
|
---
|
||||||
import type { MDXInstance } from "astro";
|
import { SITE_METADATA } from "@root/config";
|
||||||
|
|
||||||
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/config";
|
|
||||||
import PageLayout from "@layouts/default.astro";
|
import PageLayout from "@layouts/default.astro";
|
||||||
|
import { getLanguageFromURL } from "@root/utils";
|
||||||
|
import { useTranslation } from "@i18n/utils";
|
||||||
|
import * as API from "@root/lib/api";
|
||||||
import PageHeading from "@components/PageHeading";
|
import PageHeading from "@components/PageHeading";
|
||||||
import SkillsList from "@components/SkillsList";
|
import SkillsList from "@components/SkillsList";
|
||||||
import SkillsBlock from "@components/SkillsBlock";
|
import SkillsBlock from "@components/SkillsBlock";
|
||||||
|
|
||||||
import ArticleContent from "@components/ArticleContent";
|
import ArticleContent from "@components/ArticleContent";
|
||||||
import { getLanguageFromURL } from "@root/utils";
|
|
||||||
import { useTranslation } from "@i18n/utils";
|
|
||||||
|
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
let markdown: MDXInstance<{}> | undefined;
|
const data = await API.fetchAboutMe(Astro);
|
||||||
|
|
||||||
try {
|
|
||||||
markdown = (await import(`../../content/about/${lang}/index.mdx`)) as MDXInstance<{}>;
|
|
||||||
} catch (error) {
|
|
||||||
markdown = (await import(`../../content/about/${DEFAULT_LANGUAGE}/index.mdx`)) as MDXInstance<{}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { skills } = SITE_METADATA;
|
const { skills } = SITE_METADATA;
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -28,7 +19,7 @@ const { skills } = SITE_METADATA;
|
|||||||
<PageHeading heading={t("pageTitle.about")} />
|
<PageHeading heading={t("pageTitle.about")} />
|
||||||
<section>
|
<section>
|
||||||
<ArticleContent>
|
<ArticleContent>
|
||||||
{markdown && <markdown.Content />}
|
{data && <data.Content />}
|
||||||
</ArticleContent>
|
</ArticleContent>
|
||||||
<SkillsList>
|
<SkillsList>
|
||||||
{skills.map((block) => <SkillsBlock data={block} />)}
|
{skills.map((block) => <SkillsBlock data={block} />)}
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import ArticlesList from "@components/ArticlesList";
|
|||||||
import { transformArticles, formatToArticleBlocks } from "@utils/articles";
|
import { transformArticles, formatToArticleBlocks } from "@utils/articles";
|
||||||
import { getLanguageFromURL } from "@root/utils";
|
import { getLanguageFromURL } from "@root/utils";
|
||||||
import { useTranslation } from "@i18n/utils";
|
import { useTranslation } from "@i18n/utils";
|
||||||
import { ArticleService } from "@services/index";
|
import * as API from "@root/lib/api";
|
||||||
|
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const data = ArticleService.getTranslatedArticles(Astro);
|
const data = API.fetchTranslatedArticles(Astro);
|
||||||
const tags = new Set(data.map((article) => article.frontmatter.tags).flat());
|
const tags = new Set(data.map((article) => article.frontmatter.tags).flat());
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const articles = transformArticles(data);
|
const articles = transformArticles(data);
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import RecentArticles from "@components/RecentArticles";
|
|||||||
import { Splash, SplashHeading, SplashLinks, Link } from "@components/Splash";
|
import { Splash, SplashHeading, SplashLinks, Link } from "@components/Splash";
|
||||||
import { getLanguageFromURL } from "@root/utils";
|
import { getLanguageFromURL } from "@root/utils";
|
||||||
import { useTranslation } from "@i18n/utils";
|
import { useTranslation } from "@i18n/utils";
|
||||||
import { ArticleService } from "@services/index";
|
|
||||||
import UIString from "@components/UIString";
|
import UIString from "@components/UIString";
|
||||||
|
import * as API from "@root/lib/api";
|
||||||
|
|
||||||
const data = ArticleService.getTranslatedArticles(Astro);
|
const data = API.fetchTranslatedArticles(Astro);
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const articles = utils.transformArticles(data);
|
const articles = utils.transformArticles(data);
|
||||||
|
|||||||
@ -15,6 +15,10 @@ export const sortArtcilesByDate = (articles: ArticleType[]): ArticleType[] => {
|
|||||||
return articles.sort((a, b) => new Date(b.published_at).valueOf() - new Date(a.published_at).valueOf());
|
return articles.sort((a, b) => new Date(b.published_at).valueOf() - new Date(a.published_at).valueOf());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPublishedArticles = (articles: MDXInstance<ArticleType>[]) => {
|
||||||
|
return articles.filter((article) => !article.frontmatter.draft);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will distribute the array of articles by years.
|
* This function will distribute the array of articles by years.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -19,8 +19,7 @@
|
|||||||
"@stores/*": ["./src/stores/*"],
|
"@stores/*": ["./src/stores/*"],
|
||||||
"@hooks/*": ["./src/hooks/*"],
|
"@hooks/*": ["./src/hooks/*"],
|
||||||
"@i18n/*": ["./src/i18n/*"],
|
"@i18n/*": ["./src/i18n/*"],
|
||||||
"@images/*": ["./src/images/*"],
|
"@images/*": ["./src/images/*"]
|
||||||
"@services/*": ["./src/services/*"]
|
|
||||||
},
|
},
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"typeRoots": ["./node_modules/@types", "./src/types"]
|
"typeRoots": ["./node_modules/@types", "./src/types"]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user