133 lines
6.5 KiB
Markdown
133 lines
6.5 KiB
Markdown
# AGENTS.md
|
||
|
||
Guidance for AI coding agents working in this repository.
|
||
|
||
## Project overview
|
||
|
||
LNReader is a free, open-source light novel reader for Android, built with
|
||
React Native (Expo) and TypeScript. Content is fetched at runtime by
|
||
JavaScript "plugins" (per-source scrapers) rather than bundled with the app.
|
||
Plugin _requests_ live in a separate repo, [lnreader-plugins](https://github.com/lnreader/lnreader-plugins);
|
||
this repo is the reader app itself.
|
||
|
||
## Setup
|
||
|
||
```bash
|
||
pnpm install
|
||
```
|
||
|
||
- Node >= 22.11.0, pnpm (see `packageManager` in `package.json`, currently pnpm@11.20.0).
|
||
- This is a React Native/Expo app targeting Android; there is no web or iOS
|
||
build target maintained here. Building/running the app requires an Android
|
||
SDK and a device/emulator, which is generally not available in an agent
|
||
sandbox — prefer running lint, type-check, and tests to verify changes.
|
||
- See `CONTRIBUTING.md` (and `CONTRIBUTING-NIX.md` for a Nix-based dev shell)
|
||
for full local Android setup instructions.
|
||
|
||
## Commands
|
||
|
||
Run these from the repo root:
|
||
|
||
```bash
|
||
pnpm run lint # eslint ./src
|
||
pnpm run lint:fix # eslint --fix
|
||
pnpm run format:check # prettier --check
|
||
pnpm run format # prettier --write
|
||
pnpm run type-check # tsc --noEmit
|
||
pnpm run check # format:check + lint + type-check (run before finishing any change)
|
||
pnpm test # jest (all projects)
|
||
pnpm run test:rn # jest --selectProjects rn (React Native/component/hook tests)
|
||
pnpm run test:db # jest --selectProjects db (database/query tests, node env)
|
||
pnpm run test:watch
|
||
pnpm run test:coverage
|
||
```
|
||
|
||
Always run `pnpm run check` (or at minimum lint + type-check + the relevant
|
||
test project) before considering a change complete. A pre-commit hook
|
||
(`husky` + `lint-staged`) runs prettier/eslint on staged files, and a
|
||
pre-push hook runs `type-check`, so failures here will also block commits/pushes.
|
||
|
||
## Code style
|
||
|
||
- TypeScript, functional React components, hooks-based state.
|
||
- Formatting is enforced by Prettier (`.prettierrc.js`) — don't hand-format,
|
||
run `pnpm run format` instead.
|
||
- ESLint config is `eslint.config.js` (flat config, based on `eslint-config-expo`).
|
||
Notable rules: `no-console` is an error (use the project's logging/toast
|
||
utilities, not `console.*`), `prefer-const`, `no-var`, and several
|
||
`react-hooks/*` correctness rules are enforced.
|
||
- Use the `@alias/*` path aliases defined in `tsconfig.json` (and mirrored in
|
||
`jest.config.js`) instead of deep relative imports, e.g. `@components`,
|
||
`@hooks`, `@database/*`, `@screens/*`, `@services/*`, `@utils/*`,
|
||
`@api/*`, `@theme/*`, `@plugins/*`, `@navigators/*`, `@i18n/*`, `@type/*`.
|
||
- Don't add comments that just restate what the code does; only comment
|
||
non-obvious "why" (workarounds, invariants, native/platform quirks).
|
||
|
||
## Localization (i18n)
|
||
|
||
- `src/i18n/languages/en/strings.json` is the source-of-truth string file —
|
||
it's the only locale you should hand-edit. Every other folder under
|
||
`src/i18n/languages/*` is a translation synced automatically from Crowdin
|
||
(see `crowdin.yml`); don't edit or "fix" translated strings in those files.
|
||
- After adding/changing keys in `en/strings.json`, run
|
||
`pnpm run generate:string-types` to regenerate the associated TypeScript types.
|
||
|
||
## Repository layout
|
||
|
||
- `src/api` – external API clients (drive/cloud backup providers, etc.)
|
||
- `src/components` – shared/reusable UI components
|
||
- `src/database` – SQLite (op-sqlite/Drizzle) schema, queries, and migrations
|
||
helpers; `src/database/queries/*Queries.ts` are the main data-access layer
|
||
- `src/hooks` – shared and "persisted" (MMKV-backed) React hooks
|
||
- `src/i18n` – translated strings (see Crowdin integration in `crowdin.yml`)
|
||
- `src/navigators` – React Navigation stack/tab setup
|
||
- `src/plugins` – plugin manager and plugin type definitions (not the plugin
|
||
source scrapers themselves, which live in the separate lnreader-plugins repo)
|
||
- `src/screens` – feature screens (library, browse, novel, reader, settings, etc.)
|
||
- `src/services` – background tasks, downloads, backups, trackers, EPUB export, updates
|
||
- `src/theme` – Material3 theming
|
||
- `src/utils` – general utilities
|
||
- `drizzle/` – generated SQL migrations (see `pnpm run generate:db-migration`)
|
||
- `modules/` – native modules (Nitro modules: epub, tts, file, zip-archive, etc.)
|
||
- `test/` – Jest global mocks and the shared `test-utils.tsx` render helpers
|
||
- `scripts/` – Node scripts for env file generation, release packaging, etc.
|
||
|
||
## Testing notes
|
||
|
||
- Don't add tests for the sake of coverage. Write tests for user-facing
|
||
interactions (screens, components a user directly touches) and for core
|
||
complex logic (parsing, database queries, sync/backup, chapter/novel
|
||
matching, etc.), not for trivial passthroughs, simple type/prop plumbing,
|
||
or generated/boilerplate code.
|
||
- Tests live in `__tests__` directories next to the code under test and use
|
||
Jest + `@testing-library/react-native`.
|
||
- Jest is split into two projects (see `jest.config.js`):
|
||
- `db` — pure logic/query tests under `src/database`, run in a Node environment.
|
||
- `rn` — component/hook/integration tests under `src/`, run with the `jest-expo` preset.
|
||
- Import the shared render helpers from `@test-utils` (`render`, `renderNovel`,
|
||
`AllTheProviders`) rather than using `@testing-library/react-native`'s
|
||
`render` directly, so providers (theme, gesture handler, safe area, novel
|
||
context) are set up consistently.
|
||
- Mock at module level with `jest.mock(...)`, not inside `beforeEach` — see
|
||
`TESTING.md` for the reasoning and common pitfalls (ESM transform errors,
|
||
`mockReturnValue` failures, test isolation).
|
||
- Global mocks for native modules and database queries live in `test/mocks/`;
|
||
hook-specific mocks live in `src/hooks/__mocks__` and `src/hooks/__tests__/mocks.ts`.
|
||
|
||
## Database changes
|
||
|
||
- Schema lives under `src/database/schema`; the ORM is Drizzle
|
||
(`drizzle-orm`, config in `drizzle.config.ts`).
|
||
- After changing the schema, generate a migration rather than hand-writing
|
||
SQL: `pnpm run generate:db-migration`. Do not edit files under `drizzle/`
|
||
by hand.
|
||
|
||
## PR / commit conventions
|
||
|
||
- Keep changes scoped and consistent with existing patterns in the touched
|
||
directory; prefer editing existing files over introducing new abstractions.
|
||
- Match existing code style; don't reformat unrelated code.
|
||
- If you are an AI agent opening a PR or making a commit, attribute it as
|
||
such (e.g. a "Generated with <agent/tool name>" note in the commit
|
||
message/PR description, and a co-author trailer if your tool supports one).
|