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: 0 additions & 1 deletion .prettierignore

This file was deleted.

40 changes: 40 additions & 0 deletions .zed/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"language_servers": ["!eslint", "..."],
"code_actions_on_format": {
"source.action.useSortedAttributes.biome": true,
"source.action.useSortedKeys.biome": true,
"source.fixAll.biome": true,
},
"languages": {
"JavaScript": {
"formatter": {
"language_server": { "name": "biome" },
},
},
"TypeScript": {
"formatter": {
"language_server": { "name": "biome" },
},
},
"TSX": {
"formatter": {
"language_server": { "name": "biome" },
},
},
"JSON": {
"formatter": {
"language_server": { "name": "biome" },
},
},
"CSS": {
"formatter": {
"language_server": { "name": "biome" },
},
},
"YAML": {
"formatter": {
"language_server": { "name": "biome" },
},
},
},
}
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Petka is a Slovenian-language Wordle clone built with React Native / Expo. The b
```bash
pnpm install # Install dependencies
pnpm start # Start Expo dev server
pnpm run lint # ESLint via expo lint
pnpm run lint # Biome check
pnpm run typecheck # tsc --noEmit
pnpm run format # Prettier --write .
pnpm run format # Biome format --write .
pnpm test # Jest
pnpm test:coverage # Jest with coverage
pnpm test path/to/test.ts # Single test file
Expand Down Expand Up @@ -58,17 +58,17 @@ Each resource (users, puzzles, leaderboards, etc.) has its own folder with:

`convex/schema.ts` assembles all table definitions and their indexes. `convex/convex.config.ts` registers Convex components (crons, migrations, expo-push-notifications, presence). Never edit `convex/_generated/`.

Validators use `zod` via `convex-helpers` (`zodToConvex`, `zid` for typed document IDs).
Validators use `zod` (v4) via `convex-helpers/server/zod4` (`zodToConvex`, `zid` for typed document IDs).

## Code conventions

**Imports:** ordered `object → builtin → external → internal → parent → sibling → index`, with a blank line between groups. Internal paths use the `@/` alias (maps to `src/`). Type-only imports must use `import { type X }` syntax.
**Imports:** ordered `object → builtin → external → internal → parent → sibling → index`, with a blank line between groups. Internal paths use the `@/` alias (maps to `src/`). Type-only imports must use `import type { X }` syntax (also enforced by biome). For mixed imports use `import { Component, type ComponentProps }` syntax.

**JSX props:** `key` and `ref` come first (enforced by ESLint), remaining props alphabetically.
**JSX props:** all props sorted alphabetically (enforced by Biome `useSortedAttributes`).

**Unused vars:** prefix with `_` to suppress the lint rule (`_unusedVar`). Avoid doing that unless absolutely necessary.

**Prettier:** 120-char line width, single quotes, 2-space indent, trailing commas (ES5).
**Biome:** 120-char line width, single quotes, 2-space indent, trailing commas (ES5). Config in `biome.json`.

**Convex mutations:** always use `generateUseMutationHook` — it provides consistent loading state and PostHog error capture. Do not call `useMutation` directly in components.

Expand Down
2 changes: 1 addition & 1 deletion babel.config.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = function (api) {
module.exports = (api) => {
api.cache(true);

return {
Expand Down
31 changes: 31 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "includes": ["**", "!!**/dist", "!!convex/_generated", "!!.zed"] },
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 120
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": { "noNonNullAssertion": "off" },
"suspicious": { "noNonNullAssertedOptionalChain": "off" }
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5",
"semicolons": "always"
}
},
"assist": {
"enabled": true,
"actions": { "source": { "organizeImports": "on", "useSortedAttributes": "on" } }
}
}
8 changes: 4 additions & 4 deletions convex/leaderboards/queries.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { type NamedTableInfo, type Query } from 'convex/server';
import type { NamedTableInfo, Query } from 'convex/server';
import { ConvexError } from 'convex/values';
import { zid } from 'convex-helpers/server/zod4';
import { z } from 'zod';

import { internal } from '../_generated/api';
import { type DataModel, type Id } from '../_generated/dataModel';
import type { DataModel, Id } from '../_generated/dataModel';
import { generateRandomString, weekBounds, windowAround } from '../shared/helpers';
import { internalMutation, mutation, query } from '../shared/queries';
import { type User } from '../users/models';
import type { User } from '../users/models';

import {
createLeaderboardModel,
type LeaderboardWithScores,
leaderboardRange,
leaderboardType,
updateLeaderboardModel,
type LeaderboardWithScores,
} from './models';

export const list = query({
Expand Down
2 changes: 1 addition & 1 deletion convex/migrations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Migrations } from '@convex-dev/migrations';

import { components, internal } from './_generated/api.js';
import { type DataModel } from './_generated/dataModel.js';
import type { DataModel } from './_generated/dataModel.js';

export const migrations = new Migrations<DataModel>(components.migrations);
export const run = migrations.runner();
Expand Down
6 changes: 3 additions & 3 deletions convex/shared/queries.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { NoOp } from 'convex-helpers/server/customFunctions';
import { zCustomQuery, zCustomAction, zCustomMutation } from 'convex-helpers/server/zod4';
import { zCustomAction, zCustomMutation, zCustomQuery } from 'convex-helpers/server/zod4';

import {
query as baseQuery,
action as baseAction,
mutation as baseMutation,
internalMutation as baseInternalMutation,
mutation as baseMutation,
query as baseQuery,
} from '../_generated/server';

export const query = zCustomQuery(baseQuery, NoOp);
Expand Down
11 changes: 5 additions & 6 deletions db/seeders/dictionaryEntries/getDictionaryWords.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import fs, { readFile } from 'fs/promises';
import { createWriteStream } from 'fs';
import path from 'path';
import { createWriteStream } from 'node:fs';
import fs, { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as cheerio from 'cheerio';

import { fileURLToPath } from 'url';
import { XMLParser } from 'fast-xml-parser';

import { fetchDictionaryTermHtml } from './utils.js';
Expand Down Expand Up @@ -87,7 +86,7 @@ async function readAndParseXmlFiles(directoryPath) {
const isValid = await isValidDictionaryTerm(item.word);
if (isValid) {
console.log(`Writing word: ${item.word} with frequency: ${item.frequency}`);
writeStream.write(JSON.stringify(item) + '\n');
writeStream.write(`${JSON.stringify(item)}\n`);
} else {
console.warn('Found illegal word: ', item.word);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'node:fs';
import path from 'node:path';
import readline from 'node:readline';
import { fileURLToPath } from 'node:url';
import * as cheerio from 'cheerio';

import { fetchDictionaryTermHtml } from './utils.js';
Expand Down
59 changes: 0 additions & 59 deletions eslint.config.js

This file was deleted.

6 changes: 3 additions & 3 deletions jest.setup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-require-imports */
import { type Octicons } from '@expo/vector-icons';
import { type ComponentProps } from 'react';
import type { Octicons } from '@expo/vector-icons';
import type { ComponentProps } from 'react';
import { Text as MockText } from 'react-native';

jest.mock('@react-native-async-storage/async-storage', () =>
Expand All @@ -19,6 +18,7 @@ jest.mock('expo', () => ({

jest.mock('@expo/vector-icons', () => ({
...jest.requireActual('@expo/vector-icons'),
// biome-ignore lint/suspicious/noExplicitAny: Test mock, fine with any
Octicons: (props: ComponentProps<typeof Octicons> & { uniProps?: (theme: unknown) => any }) => {
const { defaultTheme } = jest.requireActual('./src/styles/themes');
const transformedProps = props.uniProps ? props.uniProps(defaultTheme) : {};
Expand Down
20 changes: 4 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"lint": "expo lint",
"lint": "biome check .",
"typecheck": "tsc --noEmit",
"format": "prettier --write .",
"format": "biome format --write .",
"prepare": "husky",
"test": "jest",
"test:coverage": "jest --coverage --silent"
},
"lint-staged": {
"*.{js,ts,tsx,jsx,json,css,md}": "prettier --write",
"*.{js,ts,tsx,jsx}": "eslint --fix"
"*.{js,ts,tsx,jsx,json}": "biome check --write --no-errors-on-unmatched"
},
"dependencies": {
"@convex-dev/crons": "^0.2.0",
Expand Down Expand Up @@ -86,29 +85,18 @@
},
"devDependencies": {
"@babel/core": "^7.28.3",
"@biomejs/biome": "2.4.8",
"@sentry/cli": "^2.53.0",
"@testing-library/react-native": "^13.3.3",
"@types/jest": "^29.5.14",
"@types/react": "~19.2.14",
"@typescript-eslint/eslint-plugin": "^8.57.1",
"@typescript-eslint/parser": "^8.57.1",
"@typescript-eslint/typescript-estree": "^8.57.1",
"@typescript-eslint/utils": "^8.57.1",
"cheerio": "^1.2.0",
"eslint": "^9.39.4",
"eslint-config-expo": "~55.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import-x": "^4.16.2",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-unused-imports": "^4.4.1",
"fast-xml-parser": "^5.5.6",
"husky": "^9.1.7",
"jest": "~29.7.0",
"jest-expo": "~55.0.10",
"lint-staged": "^16.4.0",
"metro-core": "^0.83.1",
"prettier": "^3.8.1",
"prettier-eslint": "^16.4.2",
"react-native-svg-transformer": "^1.5.1",
"ts-jest": "^29.4.6",
"typescript": "~5.9.3"
Expand Down
Loading
Loading