1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

Compare commits

...

6 Commits

27 changed files with 8423 additions and 21362 deletions

View File

@ -1,12 +1,5 @@
.vscode .*
.git !.npmrc
.env
build build
.next
coverage coverage
node_modules node_modules
tmp
temp
.DS_Store
.lighthouseci
.vercel

View File

@ -10,7 +10,6 @@
}, },
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"unicorn/prefer-node-protocol": "error",
"@next/next/no-img-element": "off" "@next/next/no-img-element": "off"
} }
} }

View File

@ -16,7 +16,7 @@ jobs:
language: ['javascript'] language: ['javascript']
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Initialize CodeQL' - name: 'Initialize CodeQL'
uses: 'github/codeql-action/init@v2' uses: 'github/codeql-action/init@v2'

View File

@ -10,10 +10,10 @@ jobs:
build: build:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'

View File

@ -10,10 +10,10 @@ jobs:
lint: lint:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
@ -30,8 +30,8 @@ jobs:
- name: 'lint:markdown' - name: 'lint:markdown'
run: 'npm run lint:markdown' run: 'npm run lint:markdown'
- name: 'lint:typescript' - name: 'lint:eslint'
run: 'npm run lint:typescript' run: 'npm run lint:eslint'
- name: 'lint:prettier' - name: 'lint:prettier'
run: 'npm run lint:prettier' run: 'npm run lint:prettier'
@ -40,8 +40,3 @@ jobs:
uses: 'dotenv-linter/action-dotenv-linter@v2' uses: 'dotenv-linter/action-dotenv-linter@v2'
with: with:
github_token: ${{ secrets.github_token }} github_token: ${{ secrets.github_token }}
- name: 'lint:docker'
uses: 'hadolint/hadolint-action@v1.6.0'
with:
dockerfile: './Dockerfile'

View File

@ -8,7 +8,7 @@ jobs:
release: release:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
@ -21,7 +21,7 @@ jobs:
git_commit_gpgsign: true git_commit_gpgsign: true
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'

View File

@ -10,10 +10,10 @@ jobs:
test-unit: test-unit:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
@ -27,10 +27,10 @@ jobs:
test-lighthouse: test-lighthouse:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'
@ -52,10 +52,10 @@ jobs:
test-e2e: test-e2e:
runs-on: 'ubuntu-latest' runs-on: 'ubuntu-latest'
steps: steps:
- uses: 'actions/checkout@v3.1.0' - uses: 'actions/checkout@v3.5.0'
- name: 'Use Node.js' - name: 'Use Node.js'
uses: 'actions/setup-node@v3.5.1' uses: 'actions/setup-node@v3.6.0'
with: with:
node-version: '18.x' node-version: '18.x'
cache: 'npm' cache: 'npm'

View File

@ -1,11 +1,5 @@
{ {
"config": {
"default": true,
"MD013": false,
"MD024": false,
"MD033": false,
"MD041": false
},
"globs": ["**/*.{md,mdx}"], "globs": ["**/*.{md,mdx}"],
"ignores": ["**/node_modules"] "ignores": ["**/node_modules"],
"customRules": ["markdownlint-rule-relative-links"]
} }

8
.markdownlint.json Normal file
View File

@ -0,0 +1,8 @@
{
"default": true,
"relative-links": true,
"extends": "markdownlint/style/prettier",
"MD024": false,
"MD033": false,
"MD041": false
}

View File

@ -6,5 +6,9 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true "source.fixAll": true
} },
"eslint.options": {
"ignorePath": ".gitignore"
},
"prettier.ignorePath": ".gitignore"
} }

View File

@ -1,21 +1,21 @@
FROM node:18.12.1 AS dependencies FROM node:18.15.0 AS builder-dependencies
WORKDIR /usr/src/app WORKDIR /usr/src/application
COPY ./package*.json ./ COPY ./package*.json ./
RUN npm install RUN npm install
FROM node:18.12.1 AS builder FROM node:18.15.0 AS builder
WORKDIR /usr/src/app WORKDIR /usr/src/application
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
COPY ./ ./ COPY ./ ./
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
RUN npm run build RUN npm run build
FROM node:18.12.1 AS runner FROM gcr.io/distroless/nodejs18-debian11:latest AS runner
WORKDIR /usr/src/app WORKDIR /usr/src/application
ENV NODE_ENV=production ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder /usr/src/app/.next/standalone ./ COPY --from=builder /usr/src/application/.next/standalone ./
COPY --from=builder /usr/src/app/.next/static ./.next/static COPY --from=builder /usr/src/application/.next/static ./.next/static
COPY --from=builder /usr/src/app/public ./public COPY --from=builder /usr/src/application/public ./public
COPY --from=builder /usr/src/app/locales ./locales COPY --from=builder /usr/src/application/locales ./locales
COPY --from=builder /usr/src/app/next.config.js ./next.config.js COPY --from=builder /usr/src/application/next.config.js ./next.config.js
CMD ["node", "server.js"] CMD ["./server.js"]

View File

@ -55,7 +55,7 @@ export const Language: React.FC = () => {
<ul <ul
data-cy='languages-list' data-cy='languages-list'
className={classNames( className={classNames(
'absolute top-14 z-10 mt-3 mr-4 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag', 'absolute top-14 z-10 mr-4 mt-3 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
{ hidden: hiddenMenu } { hidden: hiddenMenu }
)} )}
> >

View File

@ -29,7 +29,7 @@ export const SwitchTheme: React.FC = () => {
<div <div
data-cy='switch-theme-dark' data-cy='switch-theme-dark'
className={classNames( className={classNames(
'absolute top-0 bottom-0 left-[8px] mt-auto mb-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out', 'absolute bottom-0 left-[8px] top-0 mb-auto mt-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out',
{ {
'opacity-100': theme === 'dark', 'opacity-100': theme === 'dark',
'opacity-0': theme === 'light' 'opacity-0': theme === 'light'
@ -43,7 +43,7 @@ export const SwitchTheme: React.FC = () => {
<div <div
data-cy='switch-theme-light' data-cy='switch-theme-light'
className={classNames( className={classNames(
'absolute right-[10px] top-0 bottom-0 mt-auto mb-auto h-[10px] w-[10px] leading-[0]', 'absolute bottom-0 right-[10px] top-0 mb-auto mt-auto h-[10px] w-[10px] leading-[0]',
{ {
'opacity-100': theme === 'light', 'opacity-100': theme === 'light',
'opacity-0': theme === 'dark' 'opacity-0': theme === 'dark'

View File

@ -10,7 +10,7 @@ export const InterestItem: React.FC<InterestItemProps> = (props) => {
const { fontAwesomeIcon, title } = props const { fontAwesomeIcon, title } = props
return ( return (
<li className='interest-item my-2 mx-2 h-8 w-8' title={title}> <li className='interest-item mx-2 my-2 h-8 w-8' title={title}>
<FontAwesomeIcon <FontAwesomeIcon
className='block h-full w-full text-yellow dark:text-yellow-dark' className='block h-full w-full text-yellow dark:text-yellow-dark'
icon={fontAwesomeIcon} icon={fontAwesomeIcon}

View File

@ -4,7 +4,7 @@ export const ProfileDescriptionBottom: React.FC = () => {
const { t, lang } = useTranslation() const { t, lang } = useTranslation()
return ( return (
<p className='mt-8 mb-8 text-base font-normal text-gray dark:text-gray-dark'> <p className='mb-8 mt-8 text-base font-normal text-gray dark:text-gray-dark'>
{t('home:about.description-bottom')} {t('home:about.description-bottom')}
{lang === 'fr' && ( {lang === 'fr' && (
<> <>

View File

@ -12,7 +12,7 @@ export const ProfileItem: React.FC<ProfileItemProps> = (props) => {
<strong className='float-left block w-28 text-sm font-bold text-black dark:text-white'> <strong className='float-left block w-28 text-sm font-bold text-black dark:text-white'>
{title} {title}
</strong> </strong>
<span className='ml-0 mb-4 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'> <span className='mb-4 ml-0 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'>
{link != null ? ( {link != null ? (
<a <a
className='text-gray hover:underline dark:text-gray-dark' className='text-gray hover:underline dark:text-gray-dark'

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
export const RevealFade: React.FC<React.PropsWithChildren<{}>> = (props) => { export const RevealFade: React.FC<React.PropsWithChildren> = (props) => {
const { children } = props const { children } = props
const htmlElement = useRef<HTMLDivElement>(null) const htmlElement = useRef<HTMLDivElement>(null)

View File

@ -4,7 +4,7 @@ export const SectionHeading: React.FC<SectionHeadingProps> = (props) => {
const { children, ...rest } = props const { children, ...rest } = props
return ( return (
<h2 {...rest} className='mt-1 mb-3 text-center text-4xl font-semibold'> <h2 {...rest} className='mb-3 mt-1 text-center text-4xl font-semibold'>
{children} {children}
</h2> </h2>
) )

View File

@ -1,4 +1,4 @@
import { Footer } from 'components/Footer' import { Footer } from '@/components/Footer'
describe('<Footer />', () => { describe('<Footer />', () => {
it('should render with appropriate link tag version', () => { it('should render with appropriate link tag version', () => {

File diff suppressed because it is too large Load Diff

View File

@ -13,9 +13,9 @@
"modern-normalize": "1.1.0" "modern-normalize": "1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "18.11.11", "@types/node": "18.15.11",
"date-and-time": "2.4.1", "date-and-time": "2.4.3",
"vite": "3.2.5", "vite": "4.2.1",
"vite-plugin-html": "3.2.0" "vite-plugin-html": "3.2.0"
} }
} }

View File

@ -2,7 +2,7 @@ const nextPWA = require('next-pwa')({
disable: process.env.NODE_ENV !== 'production', disable: process.env.NODE_ENV !== 'production',
dest: 'public' dest: 'public'
}) })
const nextTranslate = require('next-translate') const nextTranslate = require('next-translate-plugin')
/** @type {import("next").NextConfig} */ /** @type {import("next").NextConfig} */
const nextConfig = { const nextConfig = {

27733
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "divlo", "name": "divlo",
"version": "2.5.4", "version": "2.5.6",
"private": true, "private": true,
"repository": { "repository": {
"type": "git", "type": "git",
@ -18,7 +18,7 @@
"lint:commit": "commitlint", "lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint-cli2", "lint:markdown": "markdownlint-cli2",
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"", "lint:eslint": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"",
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"", "lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"test:unit": "cypress run --component", "test:unit": "cypress run --component",
@ -32,67 +32,70 @@
"postinstall": "husky install" "postinstall": "husky install"
}, },
"dependencies": { "dependencies": {
"@fontsource/montserrat": "4.5.13", "@fontsource/montserrat": "4.5.14",
"@fortawesome/fontawesome-svg-core": "6.2.1", "@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-brands-svg-icons": "6.2.1", "@fortawesome/free-brands-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.2.1", "@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/react-fontawesome": "0.2.0", "@fortawesome/react-fontawesome": "0.2.0",
"@giscus/react": "2.2.4", "@giscus/react": "2.2.8",
"clsx": "1.2.1", "clsx": "1.2.1",
"date-and-time": "2.4.1", "date-and-time": "2.4.3",
"gray-matter": "4.0.3", "gray-matter": "4.0.3",
"html-react-parser": "3.0.4", "html-react-parser": "3.0.15",
"next": "13.0.6", "next": "13.2.4",
"next-mdx-remote": "4.2.0", "next-mdx-remote": "4.4.1",
"next-pwa": "5.6.0", "next-pwa": "5.6.0",
"next-themes": "0.2.1", "next-themes": "0.2.1",
"next-translate": "1.6.0", "next-translate": "2.0.4",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"read-pkg": "7.1.0", "read-pkg": "7.1.0",
"rehype-raw": "6.1.1", "rehype-raw": "6.1.1",
"rehype-slug": "5.1.0", "rehype-slug": "5.1.0",
"remark-gfm": "3.0.1", "remark-gfm": "3.0.1",
"sharp": "0.31.2", "sharp": "0.32.0",
"shiki": "0.11.1", "shiki": "0.14.1",
"unified": "10.1.2", "unified": "10.1.2",
"unist-util-visit": "4.1.1", "unist-util-visit": "4.1.2",
"universal-cookie": "4.0.4" "universal-cookie": "4.0.4"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "17.3.0", "@commitlint/cli": "17.5.1",
"@commitlint/config-conventional": "17.3.0", "@commitlint/config-conventional": "17.4.4",
"@lhci/cli": "0.10.0", "@lhci/cli": "0.11.0",
"@saithodev/semantic-release-backmerge": "2.1.2", "@saithodev/semantic-release-backmerge": "3.1.0",
"@semantic-release/git": "10.0.1", "@semantic-release/git": "10.0.1",
"@tailwindcss/typography": "0.5.8", "@tailwindcss/typography": "0.5.9",
"@types/node": "18.11.11", "@tsconfig/strictest": "2.0.0",
"@types/react": "18.0.26", "@types/node": "18.15.11",
"@types/react": "18.0.32",
"@types/unist": "2.0.6", "@types/unist": "2.0.6",
"@typescript-eslint/eslint-plugin": "5.46.0", "@typescript-eslint/eslint-plugin": "5.57.0",
"autoprefixer": "10.4.13", "autoprefixer": "10.4.14",
"cypress": "12.0.1", "cypress": "12.9.0",
"editorconfig-checker": "4.0.2", "editorconfig-checker": "5.0.1",
"eslint": "8.29.0", "eslint": "8.37.0",
"eslint-config-conventions": "6.0.0", "eslint-config-conventions": "8.0.0",
"eslint-config-next": "13.0.6", "eslint-config-next": "13.2.4",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.27.5",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "45.0.1", "eslint-plugin-unicorn": "46.0.0",
"html-w3c-validator": "1.2.1", "html-w3c-validator": "1.2.2",
"husky": "8.0.2", "husky": "8.0.3",
"jsonresume-theme-custom": "file:./jsonresume-theme-custom", "jsonresume-theme-custom": "file:./jsonresume-theme-custom",
"lint-staged": "13.1.0", "lint-staged": "13.2.0",
"markdownlint-cli2": "0.5.1", "markdownlint-cli2": "0.6.0",
"postcss": "8.4.19", "markdownlint-rule-relative-links": "1.1.2",
"prettier": "2.8.1", "next-translate-plugin": "2.0.4",
"prettier-plugin-tailwindcss": "0.2.0", "postcss": "8.4.21",
"semantic-release": "19.0.5", "prettier": "2.8.7",
"start-server-and-test": "1.15.1", "prettier-plugin-tailwindcss": "0.2.6",
"tailwindcss": "3.2.4", "semantic-release": "21.0.1",
"typescript": "4.9.4", "start-server-and-test": "2.0.0",
"vercel": "28.8.0" "tailwindcss": "3.3.1",
"typescript": "5.0.3",
"vercel": "28.18.3"
} }
} }

View File

@ -82,7 +82,7 @@ const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async ( export const getStaticProps: GetStaticProps<BlogPostPageProps> = async (
context context
) => { ) => {
const slug = context?.params?.slug const slug = context?.params?.['slug']
const { getPostBySlug } = await import('utils/blog') const { getPostBySlug } = await import('utils/blog')
const post = await getPostBySlug(slug) const post = await getPostBySlug(slug)
if (post == null || (post != null && !post.frontmatter.isPublished)) { if (post == null || (post != null && !post.frontmatter.isPublished)) {

View File

@ -1,25 +1,24 @@
{ {
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "node", "lib": ["dom", "dom.iterable", "ESNext"],
"allowJs": true, "allowJs": true,
"checkJs": true,
"jsx": "preserve",
"sourceMap": true,
"removeComments": true,
"noEmit": true,
"strict": true,
"types": ["cypress"],
"baseUrl": ".", "baseUrl": ".",
"esModuleInterop": true, "paths": {
"forceConsistentCasingInFileNames": true, "@/*": ["./*"]
"lib": ["dom", "dom.iterable", "esnext"], },
"skipLibCheck": true, "types": ["cypress"],
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "jsx": "preserve",
"incremental": true "incremental": true,
"exactOptionalPropertyTypes": false,
"verbatimModuleSyntax": false,
"isolatedModules": true
}, },
"exclude": ["dist", ".next", "out", "next.config.js"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] "exclude": ["node_modules"]
} }

View File

@ -38,6 +38,9 @@ export const getPosts = async (): Promise<PostMetadata[]> => {
const postsWithTime = await Promise.all( const postsWithTime = await Promise.all(
posts.map(async (postFilename) => { posts.map(async (postFilename) => {
const [slug, extension] = postFilename.split('.') const [slug, extension] = postFilename.split('.')
if (slug == null || extension == null) {
throw new Error('Invalid postFilename.')
}
const blogPostPath = path.join(POSTS_PATH, `${slug}.${extension}`) const blogPostPath = path.join(POSTS_PATH, `${slug}.${extension}`)
const blogPostContent = await fs.promises.readFile(blogPostPath, { const blogPostContent = await fs.promises.readFile(blogPostPath, {
encoding: 'utf8' encoding: 'utf8'