chore: update

This commit is contained in:
Rick van Lieshout 2025-07-15 17:28:41 +02:00
parent c7ef1146b3
commit 7c93cd76d9
38 changed files with 6306 additions and 11016 deletions

View File

@ -1,4 +0,0 @@
*.*
!*.ts
!*.tsx
/public/

View File

@ -1,53 +0,0 @@
{
"env": {
"browser": true
},
"extends": [
"plugin:import/typescript",
"plugin:import/errors",
"airbnb-typescript",
"@mastermindzh/eslint-config",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": [
"react",
"jest",
"@typescript-eslint",
"simple-import-sort",
"prettier"
],
"rules": {
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"**/*.test.ts",
"**/*.test.tsx",
"**/internal/**/*.ts"
]
}
],
"@typescript-eslint/no-unused-vars": ["off"],
"@typescript-eslint/no-use-before-define": ["off"],
"@typescript-eslint/quotes": ["error", "double"],
"@typescript-eslint/naming-convention": ["error", {
"format": ["camelCase", "UPPER_CASE", "snake_case", "PascalCase"],
"leadingUnderscore": "allow",
"selector": "parameter"
}],
"react/static-property-placement": ["off"],
"react/prop-types": ["off"],
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"]
},
"settings": {
"import/resolver": {
"typescript": {}
}
}
}

View File

@ -26,8 +26,3 @@ jobs:
- name: Run tests
run: npm run test
# - name: Release
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: npm run semantic-release

3
.nvmrc
View File

@ -1 +1,2 @@
19.8.1
v22.12.0

View File

@ -1,3 +1,3 @@
module.exports = {
...require("@mastermindzh/prettier-config")
...require("@mastermindzh/prettier-config"),
};

View File

@ -9,13 +9,16 @@
"font-weight-notation": "named-where-possible",
"function-url-no-scheme-relative": true,
"function-url-quotes": "always",
"max-empty-lines": 1,
"no-descending-specificity": true,
"no-duplicate-selectors": true,
"order/order": ["custom-properties", "declarations"],
"order/order": [
[
"custom-properties",
"declarations"
]
],
"order/properties-alphabetical-order": true,
"property-no-unknown": [true, { "ignoreProperties": ["/^lost-/"] }],
"string-quotes": "double",
"value-keyword-case": "lower"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -66,4 +66,4 @@
"phone": "+31614436562"
}
}
}
}

View File

@ -102,7 +102,7 @@ First, let's add a script to the `package.json` that will do our commit for us:
```json
{
"scripts": {
"commit": "git-cz",
"commit": "git-cz"
}
}
```
@ -138,14 +138,7 @@ module.exports = {
"subject-full-stop": [2, "never", "."],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"type-enum": [
2,
"always",
[
"first type",
"second type",
],
],
"type-enum": [2, "always", ["first type", "second type"]],
},
prompt: {
questions: {
@ -155,7 +148,6 @@ module.exports = {
},
},
};
```
As you can see we have configured a whole bunch of things, but there are 2 things that matter for now:
@ -234,8 +226,7 @@ module.exports = {
emoji: "💎",
},
refactor: {
description:
"A code change that neither fixes a bug nor adds a feature",
description: "A code change that neither fixes a bug nor adds a feature",
title: "Code Refactoring",
emoji: "📦",
},
@ -274,12 +265,10 @@ module.exports = {
},
},
scope: {
description:
"What is the scope of this change (e.g. component or file name)",
description: "What is the scope of this change (e.g. component or file name)",
},
subject: {
description:
"Write a short, imperative tense description of the change",
description: "Write a short, imperative tense description of the change",
},
body: {
description: "Provide a longer description of the change",
@ -377,7 +366,7 @@ First, let's add some npm scripts again:
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major",
"release:major": "standard-version --release-as major"
}
}
```

View File

@ -95,11 +95,11 @@ Below you'll find a list of some of the biggest things I've changed.
- I added a Dark mode with a little [react-toggle](https://github.com/aaronshaf/react-toggle) to switch between light/dark.
- Blog posts now show a header that includes the "back to articles" button and my name.
- Added code block theming for both light and dark mode
!["A block of code in both the dark and light themes"](/media/prism_styles.png)
!["A block of code in both the dark and light themes"](/media/prism_styles.png)
- I added support for tables with a bit of styling around them (slight indent and row styling)
![a table](/media/table.png)
![a table](/media/table.png)
- I added the ability to quote others in a beautiful way
![a quote](/media/quote.png)
![a quote](/media/quote.png)
- Medium like image zooming (click any of the images above)
#### The posts query bug

View File

@ -14,7 +14,6 @@ description: "Flashing the LSI-9211 used to be way more difficult, luckily the E
socialImage: ./media/flash-result.jpg
---
I've been building a new storage-oriented server for a while now and have yet again decided to go with (3 +1 backup) LSI 9211 raid controllers.
The reason I keep going for these specific raid controllers is quite simple. The card can theoretically support (8x500MB) 4GB/s in throughput divided over 8 drives (2x SFF-8087) which is near the maximum for consumer hard drives.

View File

@ -5,14 +5,14 @@ date: 2025-05-04
template: "post"
category: "life"
tags:
- life
- grief
- memory
- loss
- trauma
- healing
- personal
- poetry
- life
- grief
- memory
- loss
- trauma
- healing
- personal
- poetry
coverImage: ./media/cover.png
---
@ -31,7 +31,7 @@ But then there's another kind.
The kind that settles into your bones. The kind that haunts you during the quiet hours. The kind that never finds a conclusion.
That kind is not absence. It's ***gone***. Irrevocable, final, cruel. A door slammed shut that you still find yourself knocking on, long after youve forgotten why you started.
That kind is not absence. It's **_gone_**. Irrevocable, final, cruel. A door slammed shut that you still find yourself knocking on, long after youve forgotten why you started.
It never opens. And still, you try the handle every now and then.
@ -39,13 +39,13 @@ It never opens. And still, you try the handle every now and then.
Losing a loved one doesnt stay in one room of your house though, it isn't one door that is locked. It leaks under doors and through cracks. You find it in the most unexpected places.
You're sipping coffee with someone, and they mention a trip they took. Suddenly, you're remembering the trip *you* never took. The one you'd planned. The one that cancer took from you.
You're sipping coffee with someone, and they mention a trip they took. Suddenly, you're remembering the trip _you_ never took. The one you'd planned. The one that cancer took from you.
They talk about a shared playlist. You remember the song you danced to in the rain, soaking wet but laughing anyway. The same song you now skip every time it comes on because it hurts too much
Someone laughs about chipped mugs. You think of the one she cracked and called "vintage," and how it's still at the back of your cabinet. Untouched. Sacred.
Its like walking through a museum where only *you* know whats behind the glass.
Its like walking through a museum where only _you_ know whats behind the glass.
> They talk, they laugh, the world goes on,<br />
> Yet I sit with shadows, from dusk to dawn. <br />
@ -56,9 +56,9 @@ Its like walking through a museum where only *you* know whats behind the g
<img src="/media/the-flashback-no-one-sees.png" alt="A foggy bathroom mirror with a red lipstick kiss at the center, surrounded by a shadowy, intimate atmosphere.">
</figure>
These are what I call *mind pops*. Short for Involuntary Autobiographical Memory Chains. I wrote a journal-like book once, and later a smaller one. I wanted to share them. I tried. But I was never brave enough to.
These are what I call _mind pops_. Short for Involuntary Autobiographical Memory Chains. I wrote a journal-like book once, and later a smaller one. I wanted to share them. I tried. But I was never brave enough to.
Mind pops come uninvited. And they come *hard*.
Mind pops come uninvited. And they come _hard_.
Here are some examples from my book:
- The eyeliner she never quite got even.
@ -77,7 +77,7 @@ Theyre not just memories. Theyre grenades. Quiet ones. You never know when
## Unfair doesnt even begin to cover it
We grow up believing that the world has some kind of order to it. That bad things happen, sure—but not *this* bad. Not *this* unfair.
We grow up believing that the world has some kind of order to it. That bad things happen, sure—but not _this_ bad. Not _this_ unfair.
You lose someone who made life feel infinite. You watch them disappear into the haze of hospital lights and soft-spoken specialists.
Cancer doesnt care if youre young. Or kind.<br />
@ -99,18 +99,18 @@ For a long time, no one knew. Most don't, still.
People see the version of you they expect: Smiling. Joking. Coping. Existing.<br />
But behind the eyes, youre unraveling. Constantly. Because grief doesnt pause.<br />
Not ***really***.
Not **_really_**.
Sharing this is hard. Not because I dont want to, but because it makes you so vulnerable. Because as you're writing or speaking, memories pop up uninvited. They derail your words. They choke your sentences. They just pop in... bastards...
And even when you manage to share, others often dont understand. They compare. They say things like “I lost someone too,” but its different. This kind of grief? This kind of loss? Its heavier. It cuts deeper. And hearing it treated like its the same as all the rest—*that* makes you angry.
And even when you manage to share, others often dont understand. They compare. They say things like “I lost someone too,” but its different. This kind of grief? This kind of loss? Its heavier. It cuts deeper. And hearing it treated like its the same as all the rest—_that_ makes you angry.
Heres something I once wrote, when I thought no one would ever read it:
> I'm surrounded by many figures, but still alone...<br />
>No one sees me, nor the pain behind my mask.<br />
>They see the smiling, happy guy I show them.<br />
>You can't blame them though, how can I let them see the darkness in my heart? <br />
> No one sees me, nor the pain behind my mask.<br />
> They see the smiling, happy guy I show them.<br />
> You can't blame them though, how can I let them see the darkness in my heart? <br />
> It would scare them, for it has been torn apart.
I didnt want to scare people. Or burden them. So I said nothing.<br />
@ -135,19 +135,19 @@ If I eat alone, at a restaurant, I order what she wouldve. (who do I kid, I s
And I wrote a song. Its unfinished though—like she was.
**🎵 (Refrain)**<br />
*A strand of blonde hair, as delicate as sun,*<br />
*A mystery found, in silence it's spun.*<br />
*But memory, like a fleeting breeze,*<br />
*Fades away as a haze, in the time we seize.*<br /><br />
_A strand of blonde hair, as delicate as sun,_<br />
_A mystery found, in silence it's spun._<br />
_But memory, like a fleeting breeze,_<br />
_Fades away as a haze, in the time we seize._<br /><br />
**🎶(Verse)**<br />
*Days pass like rustling leaves,*<br />
*Stories fade, like quiet thieves.*<br />
*How swiftly memory slips away,*<br />
*Like an old melody, lost in the fray.*<br />
_Days pass like rustling leaves,_<br />
_Stories fade, like quiet thieves._<br />
_How swiftly memory slips away,_<br />
_Like an old melody, lost in the fray._<br />
*A mysterious gate, in the labyrinth of the mind,*<br />
*Locked and hidden, what secrets behind?*
_A mysterious gate, in the labyrinth of the mind,_<br />
_Locked and hidden, what secrets behind?_
As I'm writing this blog however, I have a sudden urge to add sound to the lyrics, it'll still be unfinished, but a little farther along.
I used both a piano and a viola to give it a more interesting sound than just the piano.
@ -175,7 +175,7 @@ She once told me something that still echoes today:
> "You have to grieve me, for a while... and then you move on. <br />
> You find someone else to love. Someone who makes you feel something again. <br /> You deserve that."
I shook my head. *"I cant. I **wont**. Not because I dont want to feel again. But because abstinence, to me, feels like remembering you as you are. Eternal."* <br />
I shook my head. _"I cant. I **wont**. Not because I dont want to feel again. But because abstinence, to me, feels like remembering you as you are. Eternal."_ <br />
We both cried after that. For different reasons, I think.
### My words weren't true though
@ -192,7 +192,7 @@ I know grief isnt rational. It doesnt follow clean lines.<br />
And maybe thats what hurts most of all—knowing I tried, and still came back to the same place. <br />
Alone, but full of someone who isnt here.
One day... *maybe*.
One day... _maybe_.
## Why Im finally speaking
@ -227,12 +227,12 @@ Ive met people over the years whove carried this same grief. And some of t
On the edge.
Wondering if theres peace on the other side of absence.<br />
Let me say this clearly: I have never thought of taking that road for myself. If anything, I would *ask* for eternal life. Even with the grief.<br />
Let me say this clearly: I have never thought of taking that road for myself. If anything, I would _ask_ for eternal life. Even with the grief.<br />
But if you are someone who has stood on that edge:
**Dont. Please.**
The pain is real. The grief is heavy. But life—*even broken life*—has light in it still. And sometimes, all you need is one person willing to sit with you in the dark.
The pain is real. The grief is heavy. But life—_even broken life_—has light in it still. And sometimes, all you need is one person willing to sit with you in the dark.
Let me be that person for a moment. Just long enough to remind you: the door might be closed, but the room isnt empty.
@ -241,7 +241,7 @@ Whatever you do, don't pass on the pain.
## What help looks like
Over the past two years, Ive slowly—*achingly*—learned to live with it.
Over the past two years, Ive slowly—_achingly_—learned to live with it.
And honestly, I'm happier now than I've ever been since.
Ive had friends. Real ones. Some with the training to guide me through the murk. Others who simply sat there while I unraveled, trying their best.
@ -252,7 +252,7 @@ They had always been there, I just didn't allow them to be there for me before.
Ive learned that grief isnt a wall to climb or a puzzle to solve. Its a landscape.
You dont conquer it.<br />
You *walk it*. One aching step at a time.<br />
You _walk it_. One aching step at a time.<br />
And if you're lucky... you dont walk it alone.
### To those who held me together
@ -287,7 +287,7 @@ Still holding her memory, but not drowning in it.
## Before I finally sign off
This entire blog was something I never thought I could write.
And what comes next,this next part, is something I swore I *never* would share.
And what comes next,this next part, is something I swore I _never_ would share.
But here we are.
@ -311,7 +311,7 @@ We, humans, perceive the vast majority of other humans we encounter as bystander
This book will give you a small glimpse into my life. By the end, you might find that I'm more complex than you've thought till now. That there's another side to the happy, carefree guy I show the world for the majority of my time.
Truth is, that life hasn't always been easy. In fact, I'd say that after the turning point my life has never felt easy anymore. In fact, for at least 10 years now I've been living with prodigious amounts of a single emotion: bereavement.
Truth is, that life hasn't always been easy. In fact, I'd say that after the turning point my life has never felt easy anymore. In fact, for at least 10 years now I've been living with prodigious amounts of a single emotion: bereavement.
And though I've tried not to let the bereavement take over my life, or in fact make it known to others. it's always been there. I've effectively come to think of it as a zit. Constantly there, constantly annoying and once it's gone away it's only a matter of time before it shows up again somewhere else on your body.
@ -353,4 +353,4 @@ A happy one">
If youve read this far—thank you.
That alone means more than you know.
*- Rick, mastermindzh, lycan, xxxroosjexxx, and other alias' you might know me from.*
_- Rick, mastermindzh, lycan, xxxroosjexxx, and other alias' you might know me from._

View File

@ -44,7 +44,7 @@ One of the most satisfying aspects of IaC is the ability to automate even the sm
Setting up UptimeKuma is straightforward, you can simply use our old friend Docker:
```docker run -d --name uptime-kuma -v ./data/uptimekuma:/app/data -p 3001:3001 louislam/uptime-kuma```
`docker run -d --name uptime-kuma -v ./data/uptimekuma:/app/data -p 3001:3001 louislam/uptime-kuma`
And access the Dashboard by navigating to <http://localhost:3001> to configure your monitors.
But that isn't automatic enough for me, I like to put my things in compose files for home usage.
@ -65,8 +65,8 @@ labels:
This actually does 2 things:
- creates a group with the *key/id* `monitoring` and the name `Monitoring`
- Adds a monitor with the *key/id* `uptime_kuma` to UptimeKuma with the type `http`, name `Kuma status monitoring`, and url `http://${HOST_IP}:3001`
- creates a group with the _key/id_ `monitoring` and the name `Monitoring`
- Adds a monitor with the _key/id_ `uptime_kuma` to UptimeKuma with the type `http`, name `Kuma status monitoring`, and url `http://${HOST_IP}:3001`
Adding these labels, whilst AutoKuma is running and configured to pick up labels starting with `kuma` is enough for monitors to show up (after restarting the containers).
All in all, my `docker-compose.yml` file for both UptimeKuma and AutoKuma now looks like this:

65
eslint.config.js Normal file
View File

@ -0,0 +1,65 @@
const mastermindzhConfig = require("@mastermindzh/eslint-config");
const typescriptEslint = require("@typescript-eslint/eslint-plugin");
const typescriptParser = require("@typescript-eslint/parser");
const importPlugin = require("eslint-plugin-import");
const reactPlugin = require("eslint-plugin-react");
module.exports = [
// Global ignores (replaces .eslintignore)
{
ignores: [
"dist/**",
"build/**",
"public/**",
".cache/**",
"node_modules/**",
"*.config.js",
"*.config.ts",
],
},
...mastermindzhConfig,
{
// Only lint TypeScript files (matching your .eslintignore pattern)
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parser: typescriptParser,
parserOptions: {
project: "./tsconfig.json",
},
},
plugins: {
"@typescript-eslint": typescriptEslint,
import: importPlugin,
react: reactPlugin,
},
// Your project-specific rules
rules: {
// Import rules
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: ["**/*.test.ts", "**/*.test.tsx", "**/internal/**/*.ts"],
},
],
// TypeScript rules
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off",
// Use the correct rule name for quotes
quotes: ["error", "double"],
"@typescript-eslint/naming-convention": [
"error",
{
format: ["camelCase", "UPPER_CASE", "snake_case", "PascalCase"],
leadingUnderscore: "allow",
selector: "parameter",
},
],
// React rules
"react/static-property-placement": "off",
"react/prop-types": "off",
// Shadow rules
"no-shadow": "off",
"@typescript-eslint/no-shadow": "error",
},
},
];

View File

@ -1,18 +1,10 @@
import path from "path";
const templates = Object.freeze({
indexTemplate: path.resolve(
"./src/templates/IndexTemplate/IndexTemplate.tsx",
),
notFoundTemplate: path.resolve(
"./src/templates/NotFoundTemplate/NotFoundTemplate.tsx",
),
categoryTemplate: path.resolve(
"./src/templates/CategoryTemplate/CategoryTemplate.tsx",
),
categoriesTemplate: path.resolve(
"./src/templates/CategoriesTemplate/CategoriesTemplate.tsx",
),
indexTemplate: path.resolve("./src/templates/IndexTemplate/IndexTemplate.tsx"),
notFoundTemplate: path.resolve("./src/templates/NotFoundTemplate/NotFoundTemplate.tsx"),
categoryTemplate: path.resolve("./src/templates/CategoryTemplate/CategoryTemplate.tsx"),
categoriesTemplate: path.resolve("./src/templates/CategoriesTemplate/CategoriesTemplate.tsx"),
tagTemplate: path.resolve("./src/templates/TagTemplate/TagTemplate.tsx"),
tagsTemplate: path.resolve("./src/templates/TagsTemplate/TagsTemplate.tsx"),
pageTemplate: path.resolve("./src/templates/PageTemplate/PageTemplate.tsx"),

View File

@ -15,7 +15,7 @@ const onCreateWebpackConfig = (
...aliases,
[name]: path.resolve(target),
}),
{},
{}
),
},
});

View File

@ -22,10 +22,7 @@ const jestConfig: Config.InitialOptions = {
"identity-obj-proxy",
"^gatsby-page-utils/(.*)$": "gatsby-page-utils/$1",
"^gatsby-core-utils/(.*)$": "gatsby-core-utils/dist/$1",
"^gatsby-plugin-utils/(.*)$": [
"gatsby-plugin-utils/dist/$1",
"gatsby-plugin-utils/$1",
],
"^gatsby-plugin-utils/(.*)$": ["gatsby-plugin-utils/dist/$1", "gatsby-plugin-utils/$1"],
},
transform: { "^.+\\.[jt]sx?$": ["@swc/jest", swc] },
setupFiles: ["<rootDir>/internal/testing/jest-setup.ts"],

View File

@ -1,3 +1,3 @@
{
"MD033": false
"MD033": false
}

15818
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,11 @@
"commit": "git-cz",
"format": "npm run format:ts && npm run format:scss",
"format:scss": "stylelint \"src/**/*.scss\" --fix",
"format:ts": "eslint \"src\" --ext .tsx,.ts --fix && prettier --write .",
"format:ts": "eslint \"src/**/*.{ts,tsx}\" --fix && prettier --write .",
"lint": "npm run lint:ts && npm run lint:scss",
"lint:scss": "stylelint \"src/**/*.scss\"",
"lint:staged": "lint-staged",
"lint:ts": "eslint \"src\" --ext .tsx,.ts && prettier --check .",
"lint:ts": "eslint \"src/**/*.{ts,tsx}\" && prettier --check .",
"prepare": "husky install",
"release": "standard-version",
"release:major": "standard-version --release-as major",
@ -58,8 +58,8 @@
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"classnames": "^2.5.1",
"disqus-react": "^1.1.6",
"gatsby": "^5.14.4",
"disqus-react": "^1.1.7",
"gatsby": "^5.14.5",
"gatsby-link": "^5.14.1",
"gatsby-plugin-catch-links": "^5.14.0",
"gatsby-plugin-feed": "^5.14.0",
@ -84,7 +84,7 @@
"gatsby-source-filesystem": "^5.14.0",
"gatsby-transformer-remark": "^6.14.0",
"gatsby-transformer-sharp": "^5.14.0",
"prismjs": "^1.29.0",
"prismjs": "^1.30.0",
"react": "^18.3.1",
"react-cookie-consent": "^9.0.0",
"react-dom": "^18.3.1",
@ -93,69 +93,67 @@
"reading-time": "^1.5.0"
},
"devDependencies": {
"@commitlint/config-conventional": "^17.8.1",
"@commitlint/cz-commitlint": "^19.6.1",
"@jest/globals": "^29.7.0",
"@mastermindzh/eslint-config": "^1.0.2",
"@commitlint/config-conventional": "^19.8.1",
"@commitlint/cz-commitlint": "^19.8.1",
"@eslint/compat": "^1.3.1",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.31.0",
"@jest/globals": "^30.0.4",
"@mastermindzh/eslint-config": "^3.1.0",
"@mastermindzh/prettier-config": "^1.0.0",
"@semantic-release/exec": "6.0.3",
"@semantic-release/git": "10.0.1",
"@swc/core": "^1.10.4",
"@swc/jest": "^0.2.37",
"@swc/core": "^1.12.14",
"@swc/jest": "^0.2.39",
"@types/gatsby-transformer-remark": "^2.9.4",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.3",
"@types/node": "^24.0.14",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/react-helmet": "^6.1.6",
"@types/react-helmet": "^6.1.11",
"@types/react-test-renderer": "^18.3.1",
"@types/react-toggle": "^4.0.5",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"autoprefixer": "^10.4.20",
"browserslist": "^4.24.3",
"@typescript-eslint/eslint-plugin": "^8.37.0",
"@typescript-eslint/parser": "^8.37.0",
"autoprefixer": "^10.4.21",
"browserslist": "^4.25.1",
"codecov": "^3.8.3",
"commitizen": "^4.3.1",
"commitlint": "^19.6.1",
"concurrently": "^9.1.2",
"eslint": "^8.57.1",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^8.10.0",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^27.9.0",
"commitlint": "^19.8.1",
"concurrently": "^9.2.0",
"eslint": "^9.31.0",
"eslint-config-prettier": "^10.1.5",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.37.3",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-prettier": "^5.5.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^16.3.0",
"husky": "^8.0.3",
"identity-obj-proxy": "3.0.0",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest": "^30.0.4",
"jest-cli": "^30.0.4",
"jest-environment-jsdom": "^30.0.4",
"jest-svg-transformer": "^1.0.0",
"lint-staged": "^15.3.0",
"lint-staged": "^16.1.2",
"lost": "9.0.2",
"markdownlint": "^0.37.3",
"postcss": "^8.4.49",
"markdownlint": "^0.38.0",
"postcss": "^8.5.6",
"postcss-scss": "^4.0.9",
"prettier": "^2.8.8",
"prettier-plugin-packagejson": "^2.5.6",
"prettier": "^3.6.2",
"prettier-plugin-packagejson": "^2.5.18",
"react-test-renderer": "^18.3.1",
"rimraf": "^6.0.1",
"sass": "^1.83.0",
"sass": "^1.89.2",
"source-map-support": "^0.5.21",
"standard-version": "^9.5.0",
"stylelint": "^16.12.0",
"stylelint-config-recommended-scss": "^14.1.0",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.10.0",
"stylelint": "^16.21.1",
"stylelint-config-recommended-scss": "^15.0.1",
"stylelint-order": "^7.0.0",
"stylelint-scss": "^6.12.1",
"ts-node": "^10.9.2",
"typescript": "^5.7.2",
"typescript": "^5.8.3",
"unist-util-find": "3.0.0"
}
}

11
renovate.json Normal file
View File

@ -0,0 +1,11 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"ignoreDeps": [
"react",
"react-dom",
"@types/react",
"@types/react-dom",
"@types/react-test-renderer",
"react-test-renderer"
]
}

View File

@ -3,7 +3,6 @@ import React from "react";
import * as styles from "./Icon.module.scss";
import { ICONS } from "@/constants";
interface Props {
name: keyof typeof ICONS;
icon: {

View File

@ -1,11 +1,7 @@
import React, { FC } from "react";
import { graphql, StaticQuery } from "gatsby";
import {
GatsbyImage,
GatsbyImageProps,
IGatsbyImageData,
} from "gatsby-plugin-image";
import { GatsbyImage, GatsbyImageProps, IGatsbyImageData } from "gatsby-plugin-image";
import { FileSystemNode } from "gatsby-source-filesystem";
interface Props extends Omit<GatsbyImageProps, "image"> {
@ -28,9 +24,7 @@ const Image: FC<Props> = ({ path, ...rest }: Props) => (
<StaticQuery
query={graphql`
query {
images: allFile(
filter: { ext: { regex: "/png|jpg|jpeg|webp|tif|tiff/" } }
) {
images: allFile(filter: { ext: { regex: "/png|jpg|jpeg|webp|tif|tiff/" } }) {
edges {
node {
absolutePath

View File

@ -1,9 +1,9 @@
import React from "react";
import Helmet from "react-helmet";
import { useSiteMetadata } from "@/hooks";
import { CookieBar } from "../Cookiebar/CookieBar";
import * as styles from "./Layout.module.scss";
import { useSiteMetadata } from "@/hooks";
interface Props {
title: string;
@ -20,7 +20,6 @@ const Layout: React.FC<Props> = ({
description,
socialImage = "",
noIndex = false,
slug,
}: Props) => {
const { author, url } = useSiteMetadata();
const metaImage = socialImage || author.photo;

View File

@ -11,9 +11,7 @@ describe("Page", () => {
title: mocks.markdownRemark.frontmatter.title,
};
const tree = renderer
.create(<Page {...props}>{props.children}</Page>)
.toJSON();
const tree = renderer.create(<Page {...props}>{props.children}</Page>).toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@ -1,8 +1,8 @@
import React, { useEffect, useRef } from "react";
import type { Nullable } from "@/types";
import { Helmet } from "react-helmet";
import * as styles from "./Page.module.scss";
import type { Nullable } from "@/types";
interface Props {
title?: string;

View File

@ -1,8 +1,8 @@
import { PAGINATION } from "@/constants";
import classNames from "classnames";
import { Link } from "gatsby";
import React from "react";
import * as styles from "./Pagination.module.scss";
import { PAGINATION } from "@/constants";
type Props = {
prevPagePath: string;

View File

@ -1,6 +1,6 @@
import { useSiteMetadata } from "@/hooks";
import React from "react";
import * as styles from "./Author.module.scss";
import { useSiteMetadata } from "@/hooks";
const Author = () => {
const { author } = useSiteMetadata();
@ -13,6 +13,7 @@ const Author = () => {
<strong>{author.name}</strong>
</a>
{typeof window !== "undefined" ? (
// eslint-disable-next-line no-undef
<span className="showInPrintView"> {window ? `@ ${window.location}` : ""}</span>
) : (
""

View File

@ -1,8 +1,8 @@
import { Content } from "@/components/Post/Content";
import * as mocks from "@/mocks";
import { StaticQuery, useStaticQuery } from "gatsby";
import React from "react";
import renderer from "react-test-renderer";
import * as mocks from "@/mocks";
import { Content } from "@/components/Post/Content";
const mockedStaticQuery = StaticQuery as jest.Mock;
const mockedUseStaticQuery = useStaticQuery as jest.Mock;

View File

@ -1,4 +1,3 @@
import type { Node } from "@/types";
import React from "react";
import { Helmet } from "react-helmet";
import { useSiteMetadata } from "../../hooks";
@ -8,6 +7,7 @@ import { Content } from "./Content";
import { Meta } from "./Meta";
import * as styles from "./Post.module.scss";
import { Tags } from "./Tags";
import type { Node } from "@/types";
interface Props {
post: Node;

View File

@ -1,8 +1,8 @@
import { Image } from "@/components/Image";
import { Link, navigate } from "gatsby";
import React, { FunctionComponent } from "react";
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher";
import * as styles from "./PostHeader.module.scss";
import { Image } from "@/components/Image";
type Props = { author: { name: string; photo: string } };

View File

@ -5,7 +5,6 @@ import { Link } from "gatsby";
import * as styles from "./Author.module.scss";
import { Image } from "@/components/Image";
type Props = {
author: {
name: string;

View File

@ -1,4 +1,3 @@
import { useSiteMetadata } from "@/hooks";
import React from "react";
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher";
import { Author } from "./Author";
@ -6,6 +5,7 @@ import { Contacts } from "./Contacts";
import { Copyright } from "./Copyright";
import { Menu } from "./Menu";
import * as styles from "./Sidebar.module.scss";
import { useSiteMetadata } from "@/hooks";
type Props = {
isIndex?: boolean;

View File

@ -1,3 +1,4 @@
/* eslint-disable max-len */
const ICONS = {
twitter: {
path: "M25.312 6.375c-0.688 1-1.547 1.891-2.531 2.609 0.016 0.219 0.016 0.438 0.016 0.656 0 6.672-5.078 14.359-14.359 14.359-2.859 0-5.516-0.828-7.75-2.266 0.406 0.047 0.797 0.063 1.219 0.063 2.359 0 4.531-0.797 6.266-2.156-2.219-0.047-4.078-1.5-4.719-3.5 0.313 0.047 0.625 0.078 0.953 0.078 0.453 0 0.906-0.063 1.328-0.172-2.312-0.469-4.047-2.5-4.047-4.953v-0.063c0.672 0.375 1.453 0.609 2.281 0.641-1.359-0.906-2.25-2.453-2.25-4.203 0-0.938 0.25-1.797 0.688-2.547 2.484 3.062 6.219 5.063 10.406 5.281-0.078-0.375-0.125-0.766-0.125-1.156 0-2.781 2.25-5.047 5.047-5.047 1.453 0 2.766 0.609 3.687 1.594 1.141-0.219 2.234-0.641 3.203-1.219-0.375 1.172-1.172 2.156-2.219 2.781 1.016-0.109 2-0.391 2.906-0.781z",

View File

@ -10,20 +10,18 @@ interface CategoriesQueryResult {
}
const useCategoriesList = () => {
const { allMarkdownRemark } = useStaticQuery<CategoriesQueryResult>(
graphql`
query CategoriesListQuery {
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
) {
group(field: { frontmatter: { category: SELECT } }) {
fieldValue
totalCount
}
const { allMarkdownRemark } = useStaticQuery<CategoriesQueryResult>(graphql`
query CategoriesListQuery {
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
) {
group(field: { frontmatter: { category: SELECT } }) {
fieldValue
totalCount
}
}
`
);
}
`);
return allMarkdownRemark.group ?? [];
};

View File

@ -1,52 +1,50 @@
import { graphql, useStaticQuery } from "gatsby";
const useSiteMetadata = () => {
const { site } = useStaticQuery(
graphql`
query SiteMetaData {
site {
siteMetadata {
author {
bio
name
photo
contacts {
rss
line
email
weibo
gitlab
medium
github
twitter
codepen
youtube
facebook
linkedin
telegram
instagram
soundcloud
phone
}
const { site } = useStaticQuery(graphql`
query SiteMetaData {
site {
siteMetadata {
author {
bio
name
photo
contacts {
rss
line
email
weibo
gitlab
medium
github
twitter
codepen
youtube
facebook
linkedin
telegram
instagram
soundcloud
phone
}
menu {
path
label
}
legalMenu {
path
label
}
url
title
subtitle
copyright
disqusShortname
}
menu {
path
label
}
legalMenu {
path
label
}
url
title
subtitle
copyright
disqusShortname
}
}
`
);
}
`);
return site.siteMetadata;
};

View File

@ -10,20 +10,18 @@ interface TagsQueryResult {
}
const useTagsList = () => {
const { allMarkdownRemark } = useStaticQuery<TagsQueryResult>(
graphql`
query TagsListQuery {
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
) {
group(field: { frontmatter: { tags: SELECT } }) {
fieldValue
totalCount
}
const { allMarkdownRemark } = useStaticQuery<TagsQueryResult>(graphql`
query TagsListQuery {
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
) {
group(field: { frontmatter: { tags: SELECT } }) {
fieldValue
totalCount
}
}
`
);
}
`);
return allMarkdownRemark.group || [];
};

View File

@ -1,10 +1,10 @@
import React from "react";
import { Link } from "gatsby";
import { Layout } from "@/components/Layout";
import { Page } from "@/components/Page";
import { Sidebar } from "@/components/Sidebar";
import { useSiteMetadata } from "@/hooks";
import { Link } from "gatsby";
const NotFoundTemplate: React.FC = () => {
const { title, subtitle } = useSiteMetadata();