Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell/packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ fumadocs
lokalise
nuqs
orpc
oxlint
radixui
rmiz
shiki
Expand Down
8 changes: 0 additions & 8 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ runs:
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'

- name: Build MDX
shell: bash
run: bun build:mdx

- name: Build Cosmos
shell: bash
run: bun cosmos:export

- name: Generate Next.js route types
shell: bash
run: bun typegen
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Configure environment
uses: ./.github/actions/setup

- name: Run ESLint
- name: Run Oxlint
run: bun lint

spellcheck:
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
- name: Configure environment
uses: ./.github/actions/setup

- name: Run Prettier format check
- name: Run Oxfmt format check
run: bun format:check

knip:
Expand Down
1 change: 0 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
engine-strict=true
save-exact=true
public-hoist-pattern[]=*eslint-plugin*
3 changes: 1 addition & 2 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"oxc.oxc-vscode",
"bradlc.vscode-tailwindcss",
"unifiedjs.vscode-mdx",
"streetsidesoftware.code-spell-checker",
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
"source.fixAll.oxc": "explicit"
},

"editor.defaultFormatter": "oxc.oxc-vscode",
"oxc.fmt.configPath": "oxfmt.config.ts",

"editor.formatOnPaste": true,
"editor.formatOnSave": true,

Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Use Bun for all local work.
- `bun dev`: start the app on `localhost:3000`
- `bun run build`: export Cosmos fixtures, then build production output
- `bun start`: run the production build locally
- `bun check`: run lint, typecheck, Prettier, Knip, i18n, and spellcheck
- `bun check`: run lint, typecheck, Oxfmt, Knip, i18n, and spellcheck
- `bun test:unit` or `bun test:unit:coverage`: run Vitest tests with optional coverage
- `bun test:e2e`: run Playwright end-to-end tests when the maintainer asks for them
- `bun db:migrate` and `bun db:seed`: prepare the local database
Expand All @@ -27,7 +27,7 @@ This version has breaking changes — APIs, conventions, and file structure may

## Coding Style & Naming Conventions

TypeScript is strict; prefer small typed helpers over `any`. Follow ESLint and Prettier defaults via `eslint.config.ts` and `prettier.config.js`; run `bun lint:fix` and `bun format` before opening a PR. Use the `@/` alias for `src/` imports and `~/` for `public/`. Components use PascalCase exports, utility files use kebab-case names such as `get-pathnames.ts`, and tests end in `.test.ts` or `.test.tsx`.
TypeScript is strict; prefer small typed helpers over `any`. Follow Oxlint and Oxfmt defaults via `oxlint.config.ts` and `oxfmt.config.js`; run `bun lint:fix` and `bun format` before opening a PR. Use the `@/` alias for `src/` imports and `~/` for `public/`. Components use PascalCase exports, utility files use kebab-case names such as `get-pathnames.ts`, and tests end in `.test.ts` or `.test.tsx`.

## Testing Guidelines

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ Welcome to my personal blog and portfolio website! This repository contains a mo

- Vitest for unit testing
- Playwright for E2E testing
- ESLint configuration
- Prettier code formatting
- Oxlint configuration
- Oxfmt code formatting
- Lefthook
- Conventional commit

Expand Down Expand Up @@ -203,9 +203,9 @@ bun analyze # Analyze bundle size

# Quality & Testing
bun check # Run all quality checks (lint, typecheck, format, etc.)
bun lint # Run ESLint
bun lint # Run Oxlint
bun typecheck # Run TypeScript type checking
bun format # Format code with Prettier
bun format # Format code with Oxfmt
bun knip # Find unused dependencies/exports
bun test:unit # Run unit tests
bun test:e2e # Run Playwright E2E tests
Expand Down
543 changes: 150 additions & 393 deletions bun.lock

Large diffs are not rendered by default.

41 changes: 0 additions & 41 deletions eslint.config.ts

This file was deleted.

4 changes: 2 additions & 2 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ pre-commit:
parallel: true
jobs:
- name: format
run: bun prettier --write --ignore-unknown "{staged_files}"
run: bun oxfmt --no-error-on-unmatched-pattern "{staged_files}"
stage_fixed: true

- name: lint
glob: '*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,d.ts,d.cjs,d.mts}'
run: bun eslint --fix "{staged_files}"
run: bun oxlint --fix "{staged_files}"
stage_fixed: true
3 changes: 3 additions & 0 deletions oxfmt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig } from '@nelsonlaidev/oxfmt-config'

export default defineConfig()
45 changes: 45 additions & 0 deletions oxlint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineConfig } from '@nelsonlaidev/oxlint-config'

export default defineConfig({
custom: {
tailwindcss: {
entryPoint: 'src/styles/globals.css',
ignore: ['not-prose', 'shiki', 'toaster'],
},
playwright: {
files: [`src/tests/e2e/**/*.test.{ts,tsx}`],
assertFunctionNames: ['a11y', 'checkAppliedTheme', 'checkStoredTheme'],
},
vitest: {
files: [`src/tests/unit/**/*.test.{ts,tsx}`],
},
},
config: {
overrides: [
{
files: ['src/components/ui/*.fixture.tsx'],
plugins: ['nextjs'],
rules: {
'nextjs/no-html-link-for-pages': 'off',
},
},
{
files: ['**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: '@tanstack/react-table',
importNames: ['useReactTable'],
message: 'Please use the custom hook from `@/hooks/use-react-table` instead.',
},
],
},
],
},
},
],
},
})
35 changes: 24 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"dev": "next dev -p 3000",
"email:dev": "email dev --port 3001 --dir ./src/emails/templates",
"email:export": "email export --dir ./src/emails/templates",
"format": "prettier --write .",
"format:check": "prettier --check .",
"format": "oxfmt",
"format:check": "oxfmt --check",
"knip": "knip-bun",
"lint": "eslint",
"lint:fix": "eslint --fix",
"lint": "oxlint",
"lint:fix": "oxlint --fix",
"lint:spell": "cspell .",
"prepare": "lefthook install",
"start": "next start -p 3000",
Expand All @@ -40,7 +40,7 @@
"test:unit:ui": "bun with-env vitest --ui",
"test:unit:watch": "bun with-env vitest watch",
"typecheck": "tsc --noEmit",
"typegen": "next typegen",
"typegen": "bun build:mdx && bun cosmos:export && next typegen",
"with-env": "bun --env-file=.env.local"
},
"dependencies": {
Expand Down Expand Up @@ -120,11 +120,14 @@
"@content-collections/next": "0.2.11",
"@cspell/dict-es-es": "3.0.8",
"@cspell/dict-pt-br": "2.4.2",
"@eslint-react/eslint-plugin": "4.0.2-beta.6",
"@lingual/i18n-check": "0.9.5",
"@nelsonlaidev/eslint-config": "4.1.0",
"@nelsonlaidev/prettier-config": "0.3.0",
"@nelsonlaidev/eslint-plugin": "0.2.4",
"@nelsonlaidev/oxfmt-config": "0.1.4",
"@nelsonlaidev/oxlint-config": "0.3.0",
"@nelsonlaidev/typescript-config": "1.2.1",
"@playwright/test": "1.61.0",
"@stylistic/eslint-plugin": "5.10.0",
"@tailwindcss/postcss": "4.3.1",
"@tailwindcss/typography": "0.5.20",
"@tanstack/react-query-devtools": "5.101.0",
Expand All @@ -146,13 +149,23 @@
"cspell": "10.0.1",
"discord-api-types": "0.38.48",
"drizzle-kit": "0.31.10",
"eslint": "10.5.0",
"eslint-plugin-better-tailwindcss": "4.3.2",
"eslint-plugin-command": "3.5.2",
"eslint-plugin-de-morgan": "2.1.1",
"eslint-plugin-import-zod": "1.2.1",
"eslint-plugin-playwright": "2.10.1",
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-regexp": "3.1.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-sonarjs": "4.0.2",
"execa": "9.6.1",
"fast-xml-validator": "1.1.1",
"jsdom": "29.1.1",
"knip": "6.17.1",
"lefthook": "2.1.9",
"prettier": "3.8.4",
"oxfmt": "0.56.0",
"oxlint": "1.71.0",
"oxlint-tsgolint": "0.23.0",
"react-cosmos": "7.3.0",
"react-cosmos-core": "7.3.0",
"react-cosmos-next": "7.3.0",
Expand All @@ -163,8 +176,8 @@
"typescript": "6.0.3",
"vitest": "4.1.9"
},
"packageManager": "bun@1.3.14",
"engines": {
"node": ">=24"
}
},
"packageManager": "bun@1.3.14"
}
3 changes: 0 additions & 3 deletions prettier.config.js

This file was deleted.

4 changes: 2 additions & 2 deletions src/app/[locale]/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ function Page(props: PageProps<'/[locale]'>) {
sameAs: [SITE_FACEBOOK_URL, SITE_INSTAGRAM_URL, SITE_X_URL, SITE_GITHUB_URL, SITE_YOUTUBE_URL],
},
dateCreated: '2022-02-01T00:00:00Z',
// eslint-disable-next-line @eslint-react/purity
// oxlint-disable-next-line @eslint-react/purity
dateModified: new Date().toISOString(),
// eslint-disable-next-line @eslint-react/purity
// oxlint-disable-next-line @eslint-react/purity
copyrightYear: new Date().getFullYear(),
inLanguage: locale,
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/[locale]/(unsubscribe)/unsubscribe/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function Page(props: PageProps<'/[locale]/unsubscribe'>) {
}

// Allow for future types
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
// oxlint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (data.type === 'comment_reply') {
return <UnsubscribeCommentReplyForm data={data} />
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/avatar/[id]/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function djb2(str: string) {
let hash = 5381
for (let i = 0; i < str.length; i++) {
// `i` is always in range due to the loop condition, so `codePointAt` will never return `undefined`.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
// oxlint-disable-next-line @typescript-eslint/no-non-null-assertion
hash = (hash << 5) + hash + str.codePointAt(i)!
}
return hash
Expand Down
9 changes: 7 additions & 2 deletions src/components/account/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,14 @@ function EditName(props: EditNameProps) {
void refetchSession()
})

function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
async function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
event.preventDefault()
void form.handleSubmit()

try {
await form.handleSubmit()
} catch (error) {
console.error('Failed to submit name update:', error)
}
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/comment-section/comment-code-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function CommentCodeBlock(props: CommentCodeBlockProps) {
return (
<CodeBlock data-lang={lang} title={title} className='shiki' figureClassName='my-2' scrollAreaClassName='max-h-120'>
{/* HTML of highlighted code is safely generated. */}
{/* eslint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml */}
{/* oxlint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml */}
{isHighlighted && codeHtml ? <code dangerouslySetInnerHTML={{ __html: codeHtml }} /> : <code>{code}</code>}
</CodeBlock>
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/comment-section/comment-reply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function CommentReply() {
placeholder={t('blog.comments.reply-to-comment')}
disabled={disabled}
// autoFocus is necessary because user is replying to a comment
// eslint-disable-next-line jsx-a11y/no-autofocus
// oxlint-disable-next-line jsx-a11y/no-autofocus
autoFocus
data-testid='comment-textarea-reply'
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/comment-section/comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function Comment(props: CommentProps) {
<TooltipTrigger>
<span>{formattedDate}</span>
</TooltipTrigger>
{/* eslint-disable-next-line @eslint-react/purity */}
{/* oxlint-disable-next-line @eslint-react/purity */}
<TooltipContent>{new Date(createdAt).toLocaleString()}</TooltipContent>
</Tooltip>
) : (
Expand Down
9 changes: 7 additions & 2 deletions src/components/guestbook/message-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ export function MessageBox(props: MessageBoxProps) {
toast.success(t('success.message-created'), { testId: 'guestbook-message-created-toast' })
})

function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
async function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
event.preventDefault()
void form.handleSubmit()

try {
await form.handleSubmit()
} catch (error) {
console.error('Failed to submit message:', error)
}
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/json-ld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export function JsonLd(props: JsonLdProps) {
const { json } = props

// We need to use `dangerouslySetInnerHTML` to insert the JSON-LD script tag.
// eslint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml
// oxlint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml
return <script type='application/ld+json' dangerouslySetInnerHTML={{ __html: JSON.stringify(json) }} />
}
Loading
Loading