mirror of
https://github.com/theoludwig/theoludwig.git
synced 2026-06-03 07:18:36 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
02296281f3
|
|||
|
332b1e69c2
|
|||
|
3ee53e84f6
|
|||
|
748eeb487c
|
@@ -22,21 +22,20 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: "pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061" # v4.2.0
|
||||
|
||||
- uses: "pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061" # v4.2.0
|
||||
- uses: "pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093" # v6.0.8
|
||||
|
||||
- name: "Setup Node.js"
|
||||
uses: "actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238" # v6.2.0
|
||||
uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6.4.0
|
||||
with:
|
||||
node-version: "24.13.1"
|
||||
node-version: "24.15.0"
|
||||
cache: "pnpm"
|
||||
- run: "node --version"
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
run: "pnpm clean-install"
|
||||
|
||||
- name: "Run Chromatic"
|
||||
uses: "chromaui/action@07791f8243f4cb2698bf4d00426baf4b2d1cb7e0" # latest
|
||||
uses: "chromaui/action@a200f3ba3ff81232c47ac7942347fb212b1a67dc" # latest
|
||||
with:
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
workingDir: "apps/storybook"
|
||||
|
||||
@@ -20,21 +20,21 @@ jobs:
|
||||
steps:
|
||||
- uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2
|
||||
|
||||
- uses: "pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061" # v4.2.0
|
||||
- uses: "pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093" # v6.0.8
|
||||
|
||||
- name: "Setup Node.js"
|
||||
uses: "actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238" # v6.2.0
|
||||
uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6.4.0
|
||||
with:
|
||||
node-version: "24.13.1"
|
||||
node-version: "24.15.0"
|
||||
cache: "pnpm"
|
||||
- run: "node --version"
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
run: "pnpm clean-install"
|
||||
|
||||
- name: "Install Playwright"
|
||||
run: "pnpm exec playwright install --with-deps"
|
||||
|
||||
- run: "node --run lint:editorconfig"
|
||||
- run: "node --run lint:markdown"
|
||||
- run: "node --run lint:turbo"
|
||||
# - run: "node --run lint:typescript" # already covered by oxlint
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": ["pnpm-lock.yaml"],
|
||||
"semi": false,
|
||||
"experimentalTailwindcss": {
|
||||
"sortTailwindcss": {
|
||||
"stylesheet": "./configs/config-tailwind/styles.css",
|
||||
"functions": ["classNames"]
|
||||
}
|
||||
|
||||
+274
-2
@@ -1,8 +1,168 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"extends": ["node_modules/eslint-config-conventions/.oxlintrc.json"],
|
||||
"options": {
|
||||
"typeAware": true,
|
||||
"typeCheck": true,
|
||||
"denyWarnings": true,
|
||||
"reportUnusedDisableDirectives": "error"
|
||||
},
|
||||
"env": {
|
||||
"builtin": true,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"shared-node-browser": true
|
||||
},
|
||||
"plugins": ["eslint", "typescript", "unicorn", "react", "oxc", "import", "promise"],
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"suspicious": "error",
|
||||
"pedantic": "error",
|
||||
"perf": "error"
|
||||
},
|
||||
"rules": {
|
||||
"import-x/extensions": [
|
||||
"no-await-in-loop": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-negated-condition": "off",
|
||||
"no-inline-comments": "off",
|
||||
"max-lines": "off",
|
||||
"max-depth": "off",
|
||||
"max-classes-per-file": "off",
|
||||
"max-lines-per-function": "off",
|
||||
"require-await": "off",
|
||||
"no-lonely-if": "off",
|
||||
"array-callback-return": "off",
|
||||
"no-shadow": "off",
|
||||
"no-throw-literal": "off",
|
||||
"consistent-return": "off",
|
||||
|
||||
"getter-return": "error",
|
||||
"no-undef": "error",
|
||||
"no-unreachable": "error",
|
||||
"use-isnan": [
|
||||
"error",
|
||||
{
|
||||
"enforceForSwitchCase": true,
|
||||
"enforceForIndexOf": true
|
||||
}
|
||||
],
|
||||
"valid-typeof": [
|
||||
"error",
|
||||
{
|
||||
"requireStringLiterals": true
|
||||
}
|
||||
],
|
||||
"default-param-last": "error",
|
||||
"default-case-last": "error",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"null": "ignore"
|
||||
}
|
||||
],
|
||||
"grouped-accessor-pairs": "error",
|
||||
"new-cap": [
|
||||
"error",
|
||||
{
|
||||
"newIsCap": true,
|
||||
"capIsNew": false,
|
||||
"properties": true
|
||||
}
|
||||
],
|
||||
"no-implicit-coercion": "error",
|
||||
"no-extra-boolean-cast": [
|
||||
"error",
|
||||
{
|
||||
"enforceForInnerExpressions": true
|
||||
}
|
||||
],
|
||||
"no-empty": [
|
||||
"error",
|
||||
{
|
||||
"allowEmptyCatch": true
|
||||
}
|
||||
],
|
||||
"no-multi-str": "error",
|
||||
"no-new-func": "error",
|
||||
"no-proto": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-else-return": [
|
||||
"error",
|
||||
{
|
||||
"allowElseIf": false
|
||||
}
|
||||
],
|
||||
"no-var": "error",
|
||||
"no-void": [
|
||||
"error",
|
||||
{
|
||||
"allowAsStatement": true
|
||||
}
|
||||
],
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "all"
|
||||
}
|
||||
],
|
||||
"prefer-object-has-own": "error",
|
||||
"yoda": "error",
|
||||
"curly": "error",
|
||||
"func-style": "error",
|
||||
"arrow-body-style": ["error", "always"],
|
||||
"object-shorthand": ["error", "properties"],
|
||||
|
||||
"promise/param-names": "error",
|
||||
"promise/no-nesting": "error",
|
||||
|
||||
"unicorn/catch-error-name": "error",
|
||||
"unicorn/consistent-date-clone": "error",
|
||||
"unicorn/error-message": "error",
|
||||
"unicorn/no-array-for-each": "error",
|
||||
"unicorn/no-array-method-this-argument": "error",
|
||||
"unicorn/no-document-cookie": "error",
|
||||
"unicorn/no-zero-fractions": "error",
|
||||
"unicorn/number-literal-case": "error",
|
||||
"unicorn/prefer-node-protocol": "error",
|
||||
"unicorn/throw-new-error": "error",
|
||||
"unicorn/require-array-join-separator": "error",
|
||||
"unicorn/prefer-number-properties": "error",
|
||||
"unicorn/prefer-modern-math-apis": "error",
|
||||
"unicorn/prefer-structured-clone": "error",
|
||||
"unicorn/consistent-existence-index-check": "error",
|
||||
"unicorn/no-array-reverse": "off",
|
||||
"unicorn/no-array-sort": "off",
|
||||
"unicorn/no-lonely-if": "off",
|
||||
"unicorn/prefer-string-replace-all": "off",
|
||||
"unicorn/prefer-at": "off",
|
||||
"unicorn/prefer-query-selector": "off",
|
||||
"unicorn/prefer-top-level-await": "off",
|
||||
"unicorn/prefer-string-slice": "off",
|
||||
"unicorn/no-immediate-mutation": "off",
|
||||
"unicorn/no-useless-undefined": "off",
|
||||
"unicorn/no-object-as-default-parameter": "off",
|
||||
|
||||
"react/no-array-index-key": "off",
|
||||
"react/no-unknown-property": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/iframe-missing-sandbox": "off",
|
||||
"react/self-closing-comp": "error",
|
||||
"react/jsx-boolean-value": "error",
|
||||
"react/jsx-no-target-blank": "off",
|
||||
"react/jsx-no-useless-fragment": "off",
|
||||
"react/no-unstable-nested-components": "off",
|
||||
|
||||
"import/no-webpack-loader-syntax": "error",
|
||||
"import/export": "error",
|
||||
"import/no-duplicates": "error",
|
||||
"import/no-named-default": "error",
|
||||
"import/no-anonymous-default-export": "error",
|
||||
"import/consistent-type-specifier-style": "error",
|
||||
"import/no-unassigned-import": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/max-dependencies": "off",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
@@ -11,6 +171,118 @@
|
||||
"js": "never",
|
||||
"jsx": "never"
|
||||
}
|
||||
],
|
||||
|
||||
"typescript/ban-types": "off",
|
||||
"typescript/no-unnecessary-type-arguments": "off",
|
||||
"typescript/no-unsafe-type-assertion": "off",
|
||||
"typescript/no-unsafe-member-access": "off",
|
||||
"typescript/no-confusing-void-expression": "off",
|
||||
"typescript/no-unsafe-assignment": "off",
|
||||
"typescript/no-misused-promises": "off",
|
||||
"typescript/return-await": ["error", "always"],
|
||||
"typescript/require-await": "off",
|
||||
"typescript/switch-exhaustiveness-check": "off",
|
||||
"typescript/ban-ts-comment": "off",
|
||||
"typescript/prefer-readonly-parameter-types": "off",
|
||||
"typescript/strict-void-return": "off",
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"args": "all",
|
||||
"argsIgnorePattern": "^_",
|
||||
"caughtErrors": "all",
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
"destructuredArrayIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false,
|
||||
"classes": false,
|
||||
"enums": false,
|
||||
"variables": false,
|
||||
"typedefs": false
|
||||
}
|
||||
],
|
||||
"no-redeclare": [
|
||||
"error",
|
||||
{
|
||||
"builtinGlobals": false
|
||||
}
|
||||
],
|
||||
"typescript/only-throw-error": "off",
|
||||
"no-unused-expressions": [
|
||||
"error",
|
||||
{
|
||||
"allowShortCircuit": true,
|
||||
"allowTernary": true,
|
||||
"allowTaggedTemplates": true
|
||||
}
|
||||
],
|
||||
"typescript/adjacent-overload-signatures": "error",
|
||||
"typescript/consistent-type-definitions": "error",
|
||||
"typescript/consistent-type-imports": "error",
|
||||
"typescript/explicit-member-accessibility": "error",
|
||||
"typescript/explicit-function-return-type": [
|
||||
"error",
|
||||
{
|
||||
"allowExpressions": true,
|
||||
"allowHigherOrderFunctions": true,
|
||||
"allowTypedFunctionExpressions": true,
|
||||
"allowDirectConstAssertionInArrowFunctions": true
|
||||
}
|
||||
],
|
||||
"typescript/no-extraneous-class": [
|
||||
"error",
|
||||
{
|
||||
"allowWithDecorator": true
|
||||
}
|
||||
],
|
||||
"typescript/no-floating-promises": [
|
||||
"error",
|
||||
{
|
||||
"allowForKnownSafeCalls": [
|
||||
{
|
||||
"from": "package",
|
||||
"name": ["test", "it", "suite", "describe"],
|
||||
"package": "node:test"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"typescript/no-non-null-assertion": "error",
|
||||
"typescript/no-this-alias": "error",
|
||||
"typescript/no-require-imports": "error",
|
||||
"typescript/prefer-function-type": "error",
|
||||
"typescript/prefer-find": "error",
|
||||
"typescript/prefer-nullish-coalescing": "off",
|
||||
"typescript/prefer-readonly": "error",
|
||||
"typescript/prefer-reduce-type-parameter": "error",
|
||||
"typescript/prefer-return-this-type": "error",
|
||||
"typescript/promise-function-async": "error",
|
||||
"typescript/require-array-sort-compare": "error",
|
||||
"typescript/restrict-plus-operands": [
|
||||
"error",
|
||||
{
|
||||
"skipCompoundAssignments": true
|
||||
}
|
||||
],
|
||||
"typescript/restrict-template-expressions": "error",
|
||||
"typescript/strict-boolean-expressions": [
|
||||
"error",
|
||||
{
|
||||
"allowString": false,
|
||||
"allowNumber": false,
|
||||
"allowNullableObject": false,
|
||||
"allowNullableBoolean": false,
|
||||
"allowNullableString": false,
|
||||
"allowNullableNumber": false,
|
||||
"allowAny": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -28,7 +28,7 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/) >= v24.0.0 [(`nvm install 24`)](https://nvm.sh)
|
||||
- [pnpm](https://pnpm.io/) [(`npm install --global corepack@0.34.6 && corepack enable`)](https://github.com/nodejs/corepack)
|
||||
- [pnpm](https://pnpm.io/) [(`npm install --global corepack@0.35.0 && corepack enable`)](https://github.com/nodejs/corepack)
|
||||
- [Docker](https://www.docker.com/)
|
||||
|
||||
### Installation
|
||||
@@ -45,7 +45,7 @@ cp .env.example .env
|
||||
cp apps/website/.env.example apps/website/.env
|
||||
|
||||
# Install dependencies
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm clean-install
|
||||
|
||||
# Install Playwright browser binaries and their dependencies (tests)
|
||||
pnpm exec playwright install --with-deps
|
||||
@@ -58,7 +58,6 @@ pnpm exec playwright install --with-deps
|
||||
node --run dev
|
||||
|
||||
# Lint
|
||||
node --run lint:editorconfig
|
||||
node --run lint:markdown
|
||||
node --run lint:turbo
|
||||
# node --run lint:typescript # already covered by oxlint
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
declare module "*.css"
|
||||
@@ -37,8 +37,8 @@ const preview: Preview = {
|
||||
controls: {
|
||||
disableSaveFromUI: true,
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /date$/i,
|
||||
color: /(background|color)$/iu,
|
||||
date: /date$/iu,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM node:24.13.1-slim@sha256:a81a03dd965b4052269a57fac857004022b522a4bf06e7a739e25e18bce45af2 AS node-pnpm
|
||||
FROM docker.io/node:24.15.0-slim@sha256:879b21aec4a1ad820c27ccd565e7c7ed955f24b92e6694556154f251e4bdb240 AS node-pnpm
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN npm install --global corepack@0.34.6 && corepack enable
|
||||
ENV PATH="$PNPM_HOME/bin:$PATH"
|
||||
RUN npm install --global corepack@0.35.0 && corepack enable
|
||||
ENV TURBO_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV DO_NOT_TRACK=1
|
||||
@@ -9,7 +9,7 @@ WORKDIR /usr/src/app
|
||||
|
||||
FROM node-pnpm AS builder
|
||||
COPY ./ ./
|
||||
RUN pnpm install --global turbo@2.8.9
|
||||
RUN pnpm install --global turbo@2.9.16
|
||||
RUN turbo prune @repo/website --docker
|
||||
|
||||
FROM node-pnpm AS installer
|
||||
@@ -18,7 +18,7 @@ ENV IS_STANDALONE=true
|
||||
COPY .gitignore .gitignore
|
||||
COPY --from=builder /usr/src/app/out/json/ ./
|
||||
COPY --from=builder /usr/src/app/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm clean-install
|
||||
COPY --from=builder /usr/src/app/out/full/ ./
|
||||
COPY turbo.json turbo.json
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ClassValue } from "clsx"
|
||||
import { clsx } from "clsx"
|
||||
import type { ClassValue } from "@repo/utils/clsx"
|
||||
import { clsx } from "@repo/utils/clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export const classNames = (...inputs: ClassValue[]): string => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/montserrat": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"@repo/utils": "workspace:*",
|
||||
"tailwind-merge": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -51,15 +51,15 @@
|
||||
@apply tracking-wider italic;
|
||||
}
|
||||
blockquote {
|
||||
@apply border-gray-lighter border-l-4 pl-3 italic;
|
||||
@apply border-l-4 border-gray-lighter pl-3 italic;
|
||||
}
|
||||
|
||||
kbd {
|
||||
@apply bg-gray-lighter rounded-md px-2 dark:text-black;
|
||||
@apply rounded-md bg-gray-lighter px-2 dark:text-black;
|
||||
}
|
||||
|
||||
mark {
|
||||
@apply bg-yellow rounded-md px-2;
|
||||
@apply rounded-md bg-yellow px-2;
|
||||
}
|
||||
|
||||
ol {
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background dark:bg-background-dark font-sans text-black dark:text-white;
|
||||
@apply bg-background font-sans text-black dark:bg-background-dark dark:text-white;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
@@ -101,7 +101,7 @@ body {
|
||||
}
|
||||
|
||||
.prose {
|
||||
@apply dark:text-gray-lighter !max-w-5xl scroll-smooth text-black;
|
||||
@apply !max-w-5xl scroll-smooth text-black dark:text-gray-lighter;
|
||||
}
|
||||
|
||||
.prose p {
|
||||
@@ -122,11 +122,11 @@ body {
|
||||
}
|
||||
|
||||
.prose a {
|
||||
@apply text-primary dark:text-primary-dark !font-semibold;
|
||||
@apply !font-semibold text-primary dark:text-primary-dark;
|
||||
}
|
||||
|
||||
.prose strong {
|
||||
@apply dark:text-gray-lighter text-black;
|
||||
@apply text-black dark:text-gray-lighter;
|
||||
}
|
||||
|
||||
.prose h2,
|
||||
|
||||
+2
-5
@@ -6,19 +6,16 @@
|
||||
"scripts": {
|
||||
"dev": "turbo run dev --parallel",
|
||||
"start": "turbo run start --parallel",
|
||||
"lint:editorconfig": "editorconfig-checker",
|
||||
"lint:markdown": "markdownlint-cli2",
|
||||
"lint:turbo": "turbo boundaries",
|
||||
"lint:typescript": "turbo run lint:typescript",
|
||||
"lint:oxlint": "turbo run typegen && oxlint . --type-aware --type-check --deny-warnings --report-unused-disable-directives",
|
||||
"lint:oxlint": "turbo run typegen && oxlint .",
|
||||
"lint:oxfmt": "oxfmt . --check",
|
||||
"oxfmt": "oxfmt .",
|
||||
"test": "turbo run test",
|
||||
"build": "turbo run build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"editorconfig-checker": "catalog:",
|
||||
"eslint-config-conventions": "catalog:",
|
||||
"markdownlint": "catalog:",
|
||||
"markdownlint-cli2": "catalog:",
|
||||
"markdownlint-rule-relative-links": "catalog:",
|
||||
@@ -32,5 +29,5 @@
|
||||
"engines": {
|
||||
"node": ">=24.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc"
|
||||
"packageManager": "pnpm@11.5.0+sha512.dbfcc4f81cf48597afd4bc391ffdf12c11f1a9fb83a395bfa6b0a2d9cc2fd8ffebafdb1ccbd529632153f793904c2615b7f09fe1a345473fd1c35845172a8eb1"
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export const BlogPostContent: React.FC<BlogPostContentProps> = async (props) =>
|
||||
},
|
||||
img: (properties) => {
|
||||
const { src = "", alt = "Blog Image" } = properties
|
||||
const source = src.replace("../../../apps/website/public/", "/")
|
||||
const source = (src as string).replace("../../../apps/website/public/", "/")
|
||||
return (
|
||||
<span className="flex flex-col items-center justify-center">
|
||||
<Image src={source} alt={alt} width={1000} height={1000} className="size-auto" />
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
declare module "*.css"
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { AbstractIntlMessages } from "next-intl"
|
||||
import { hasLocale } from "next-intl"
|
||||
import { getRequestConfig } from "next-intl/server"
|
||||
import { routing } from "./routing.ts"
|
||||
@@ -12,7 +11,7 @@ export default getRequestConfig(async ({ requestLocale }) => {
|
||||
|
||||
const userMessages = (await import(`./translations/${locale}.json`)).default
|
||||
const defaultMessages = (await import(`./translations/${LOCALE_DEFAULT}.json`)).default
|
||||
const messages = deepMerge<AbstractIntlMessages>(defaultMessages, userMessages)
|
||||
const messages = deepMerge(defaultMessages, userMessages)
|
||||
|
||||
return {
|
||||
locale,
|
||||
|
||||
@@ -98,12 +98,12 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
||||
<></>
|
||||
)}
|
||||
|
||||
{workExperience.summary != null ? (
|
||||
{workExperience.summary == null ? (
|
||||
<></>
|
||||
) : (
|
||||
<div className="mt-2">
|
||||
<p>{workExperience.summary}</p>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
|
||||
@@ -59,9 +59,9 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
|
||||
return (
|
||||
<NextLink className={classNames(buttonVariants({ variant, size }), className)} {...rest}>
|
||||
{leftIcon != null ? <span className="mr-2">{leftIcon}</span> : null}
|
||||
{leftIcon == null ? null : <span className="mr-2">{leftIcon}</span>}
|
||||
<span>{children}</span>
|
||||
{rightIcon != null ? <span className="ml-2">{rightIcon}</span> : null}
|
||||
{rightIcon == null ? null : <span className="ml-2">{rightIcon}</span>}
|
||||
|
||||
<Ripple color={rippleColor} />
|
||||
</NextLink>
|
||||
@@ -94,7 +94,7 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
disabled={isDisabled}
|
||||
{...rest}
|
||||
>
|
||||
{leftIconElement != null ? <span className="mr-2">{leftIconElement}</span> : null}
|
||||
{leftIconElement == null ? null : <span className="mr-2">{leftIconElement}</span>}
|
||||
<span>{children}</span>
|
||||
{rightIcon != null && !isLoading ? <span className="ml-2">{rightIcon}</span> : null}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const typographyVariants = (options?: { variant?: TypographyVariant }): string =
|
||||
export type TypographyProps<Component extends React.ElementType = "p"> = {
|
||||
as?: Component
|
||||
} & React.ComponentPropsWithoutRef<Component> & {
|
||||
className?: string
|
||||
variant?: TypographyVariant
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ export const AboutItem: React.FC<AboutItemProps> = (props) => {
|
||||
<li className="flex items-center justify-between sm:justify-start">
|
||||
<strong className="w-24 text-sm text-black lg:w-32 dark:text-white">{label}</strong>
|
||||
<span className="block text-sm font-normal text-black dark:text-gray-lighter">
|
||||
{link != null ? (
|
||||
{link == null ? (
|
||||
value
|
||||
) : (
|
||||
<a className="hover:underline" href={link}>
|
||||
{value}
|
||||
</a>
|
||||
) : (
|
||||
value
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./constants": "./src/constants.ts",
|
||||
"./clsx": "./src/clsx.ts",
|
||||
"./dates": "./src/dates.ts",
|
||||
"./objects": "./src/objects.ts",
|
||||
"./strings": "./src/strings.ts",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
type ClassDictionary = Record<string, unknown>
|
||||
|
||||
export type ClassValue = ClassDictionary | string | null | boolean | undefined
|
||||
|
||||
/**
|
||||
* Utility for constructing className strings conditionally.
|
||||
* @see https://github.com/lukeed/clsx
|
||||
*/
|
||||
export const clsx = (inputs: ClassValue[]): string => {
|
||||
let result = ""
|
||||
for (const input of inputs) {
|
||||
if (typeof input === "string") {
|
||||
if (input.length > 0) {
|
||||
result = result.length > 0 ? result + " " + input : input
|
||||
}
|
||||
} else if (typeof input === "object" && input !== null) {
|
||||
for (const key of Object.keys(input)) {
|
||||
if (input[key] != null && input[key] !== false && input[key] !== 0 && input[key] !== "") {
|
||||
result = result.length > 0 ? result + " " + key : key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import type { ClassValue } from "../clsx.ts"
|
||||
import { clsx } from "../clsx.ts"
|
||||
|
||||
describe("clsx", () => {
|
||||
describe("strings", () => {
|
||||
it("should return empty string for empty input", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [""]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "")
|
||||
})
|
||||
|
||||
it("should return the string as-is", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = ["foo"]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo")
|
||||
})
|
||||
|
||||
it("should join multiple strings with space", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = ["foo", "bar"]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo bar")
|
||||
})
|
||||
|
||||
it("should skip falsy values between strings", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = ["foo", false, "bar", null, "baz"]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo bar baz")
|
||||
})
|
||||
})
|
||||
|
||||
describe("objects", () => {
|
||||
it("should return empty string for empty object", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{}]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "")
|
||||
})
|
||||
|
||||
it("should include keys with truthy values", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{ foo: true, bar: false }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo")
|
||||
})
|
||||
|
||||
it("should exclude keys with falsy values (0, null, false)", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{ foo: 1, bar: 0, baz: 1 }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo baz")
|
||||
})
|
||||
|
||||
it("should join keys from multiple objects", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{ foo: 1 }, { bar: 2 }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo bar")
|
||||
})
|
||||
|
||||
it("should handle null between objects", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{ foo: 1 }, null, { baz: 1, bat: 0 }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo baz")
|
||||
})
|
||||
})
|
||||
|
||||
describe("mixed", () => {
|
||||
it("should handle strings and objects together", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = ["hello", { world: 1, push: true }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "hello world push")
|
||||
})
|
||||
|
||||
it("should ignore non-string/object values", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [undefined, "hello", null, true]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "hello")
|
||||
})
|
||||
|
||||
it("should return empty string with no arguments", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = []
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "")
|
||||
})
|
||||
|
||||
it("should not append empty string with separator after a non-empty token", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = ["foo", ""]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "foo")
|
||||
})
|
||||
|
||||
it("should exclude object keys whose value is null, undefined, or empty string", () => {
|
||||
// Arrange - Given
|
||||
const input: ClassValue[] = [{ a: null, b: undefined, c: "", d: "ok" }]
|
||||
|
||||
// Act - When
|
||||
const output = clsx(input)
|
||||
|
||||
// Assert - Then
|
||||
assert.strictEqual(output, "d")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -10,7 +10,7 @@ export const getPathnameWithoutLocale = (input: string): string => {
|
||||
const locale = LOCALES.find((locale) => {
|
||||
return input.startsWith(`/${locale}`)
|
||||
})
|
||||
const pathname = locale != null ? input.slice(locale.length + 1) : input
|
||||
const pathname = locale == null ? input : input.slice(locale.length + 1)
|
||||
if (pathname.length <= 0) {
|
||||
return `/${pathname}`
|
||||
}
|
||||
|
||||
Generated
+3630
-3095
File diff suppressed because it is too large
Load Diff
+37
-39
@@ -3,39 +3,46 @@ packages:
|
||||
- "configs/*"
|
||||
- "packages/*"
|
||||
|
||||
allowBuilds:
|
||||
"@swc/core": true
|
||||
"@parcel/watcher": true
|
||||
"sharp": true
|
||||
"unrs-resolver": true
|
||||
"core-js-pure": true
|
||||
"esbuild": true
|
||||
|
||||
minimumReleaseAge: 1440
|
||||
minimumReleaseAgeStrict: true
|
||||
minimumReleaseAgeIgnoreMissingTime: false
|
||||
|
||||
catalogMode: "strict"
|
||||
|
||||
catalog:
|
||||
# Turborepo
|
||||
"turbo": "2.8.9"
|
||||
|
||||
# TypeScript/Linting
|
||||
"typescript": "5.9.3"
|
||||
"@types/node": "25.2.3"
|
||||
"turbo": "2.9.16"
|
||||
"typescript": "6.0.3"
|
||||
"@types/node": "25.9.1"
|
||||
"@total-typescript/ts-reset": "0.6.1"
|
||||
"oxlint": "1.47.0"
|
||||
"oxlint-tsgolint": "0.12.2"
|
||||
"oxfmt": "0.32.0"
|
||||
"eslint-config-conventions": "21.2.0"
|
||||
"editorconfig-checker": "6.1.1"
|
||||
"oxlint": "1.67.0"
|
||||
"oxlint-tsgolint": "0.23.0"
|
||||
"oxfmt": "0.52.0"
|
||||
|
||||
# Utils
|
||||
"mime": "4.1.0"
|
||||
|
||||
# React.js/Next.js
|
||||
"next": "16.1.6"
|
||||
"next-intl": "4.8.3"
|
||||
"next": "16.2.6"
|
||||
"next-intl": "4.13.0"
|
||||
"next-themes": "0.4.6"
|
||||
"react": "19.2.4"
|
||||
"react-dom": "19.2.4"
|
||||
"@types/react": "19.2.14"
|
||||
"react": "19.2.6"
|
||||
"react-dom": "19.2.6"
|
||||
"@types/react": "19.2.15"
|
||||
"@types/react-dom": "19.2.3"
|
||||
"react-icons": "5.5.0"
|
||||
"react-icons": "5.6.0"
|
||||
|
||||
# Blog
|
||||
"@giscus/react": "3.1.0"
|
||||
"gray-matter": "4.0.3"
|
||||
"katex": "0.16.28"
|
||||
"katex": "0.17.0"
|
||||
"next-mdx-remote": "5.0.0"
|
||||
"@mdx-js/mdx": "3.1.1"
|
||||
"rehype-katex": "7.0.1"
|
||||
@@ -47,38 +54,29 @@ catalog:
|
||||
"@shikijs/rehype": "1.24.0"
|
||||
|
||||
# Markdown Lint
|
||||
"markdownlint-cli2": "0.21.0"
|
||||
"markdownlint-cli2": "0.22.1"
|
||||
"markdownlint": "0.40.0"
|
||||
"markdownlint-rule-relative-links": "5.0.1"
|
||||
"markdownlint-rule-relative-links": "5.1.0"
|
||||
|
||||
# Storybook
|
||||
"storybook": &storybook "10.2.8"
|
||||
"storybook": &storybook "10.4.1"
|
||||
"@storybook/addon-docs": *storybook
|
||||
"@storybook/addon-a11y": *storybook
|
||||
"@storybook/nextjs": *storybook
|
||||
"@storybook/addon-themes": *storybook
|
||||
"@storybook/test-runner": "0.24.2"
|
||||
"@chromatic-com/storybook": "5.0.1"
|
||||
"chromatic": "15.1.0"
|
||||
"@storybook/test-runner": "0.24.4"
|
||||
"@chromatic-com/storybook": "5.2.1"
|
||||
"chromatic": "16.10.0"
|
||||
|
||||
# Testing
|
||||
"playwright": &playwright "1.58.2"
|
||||
"playwright": &playwright "1.60.0"
|
||||
"@playwright/test": *playwright
|
||||
"start-server-and-test": "2.1.3"
|
||||
"start-server-and-test": "3.0.5"
|
||||
|
||||
# CSS
|
||||
"postcss": "8.5.6"
|
||||
"@tailwindcss/postcss": "4.1.18"
|
||||
"postcss": "8.5.15"
|
||||
"tailwindcss": &tailwindcss "4.3.0"
|
||||
"@tailwindcss/postcss": *tailwindcss
|
||||
"@tailwindcss/typography": "0.5.19"
|
||||
"tailwindcss": "4.1.18"
|
||||
"tailwind-merge": "3.4.1"
|
||||
"clsx": "2.1.1"
|
||||
"tailwind-merge": "3.6.0"
|
||||
"@fontsource/montserrat": "5.2.8"
|
||||
|
||||
allowBuilds:
|
||||
"@swc/core": true
|
||||
"@parcel/watcher": true
|
||||
"sharp": true
|
||||
"unrs-resolver": true
|
||||
"core-js-pure": true
|
||||
"esbuild": true
|
||||
|
||||
Reference in New Issue
Block a user