# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

`@ezez/utils` is a lightweight utility library designed as a fast, minimal alternative to Lodash. The library focuses on covering typical use cases while maintaining small bundle sizes and avoiding over-engineering. Each utility function is implemented in a single file with co-located tests following the pattern `functionName.ts` and `functionName.spec.ts`.

## Development Commands

### Testing
- `pnpm test` - Run all tests with Jest
- `pnpm test -- <filename>` - Run a single test file (e.g., `pnpm test -- pick.spec.ts`)
- `NODE_ENV=test jest <filepath>` - Run specific test file directly

### Building
- `pnpm run compile` - Build both ESM and CJS outputs (runs both compile:esm and compile:cjs)
- `pnpm run compile:esm` - Build ES modules to `esm/` directory
- `pnpm run compile:cjs` - Build CommonJS modules to `dist/` directory
- `pnpm run typecheck` - Run TypeScript type checking without emitting files

### Code Quality
- `pnpm run lint` - Lint source code with ezlint
- `pnpm run lint:fix` - Auto-fix linting issues

### Documentation
- `pnpm run docs` - Generate TypeDoc documentation to `docs/` directory

### Pre-publish
- `pnpm run prepublishOnly` - Runs lint, test, and docs (automatically runs before publishing)

## Architecture

### Dual Build System

The library supports both ESM and CommonJS through a dual compilation strategy:

1. **ESM build** (`esm/`): Uses `tsconfig.esm.json`, compiles to ES2015 modules with target ES2022
2. **CJS build** (`dist/`): Uses `tsconfig.cjs.json`, compiles to CommonJS modules
3. **Post-processing**: Both builds use `resolve-tspaths` to resolve TypeScript path aliases
4. **CJS package.json injection**: The CJS build adds a `package.json` with `{"type": "commonjs"}` to the `dist/` directory to ensure proper module resolution

The package.json exports field correctly maps:
- CommonJS: `./dist/index.js`
- ESM: `./esm/index.js`
- Types: `./esm/index.d.ts`

### Code Organization

- **Flat structure**: All utility functions live directly in `src/` (except `serializeToBuffer` which has a subdirectory)
- **Co-located tests**: Each function has its spec file in the same directory (e.g., `pick.ts` + `pick.spec.ts`)
- **Single export per file**: Each utility file exports one main function
- **Central index**: `src/index.ts` re-exports all utilities

### TypeScript Configuration

The project uses strict TypeScript settings:
- `strict: true` with all strictness flags enabled
- `noUncheckedIndexedAccess: true` - ensures index access returns `T | undefined`
- `exactOptionalPropertyTypes: true` - distinguishes between `undefined` and missing properties
- `noImplicitReturns: true` - requires all code paths to return a value
- Target: ES2022 with ES2015 modules (for ESM)

### Testing Setup

- **Jest** with Babel transformation
- Spec files excluded from TypeScript compilation (`tsconfig.json` excludes `src/**/*.spec.ts`)
- Bootstrap file: `test/bootstrap.cjs`
- Babel config: `test/babel.config.cjs`
- Module mapping handles `.js` extensions for ESM compatibility

## Coding Patterns

### Function Structure

Each utility follows this pattern:
1. JSDoc comment with description, parameters, examples, and return value
2. Type-safe function signature with generics where appropriate
3. Input validation and early returns for edge cases
4. Implementation focusing on the typical use case
5. Named export (not default)

Example from `pick.ts`:
```typescript
/**
 * Returns a new object with given properties copied from a source object.
 *
 * @param {Object} object - source object
 * @param {Array.<string>} props - properties to copy
 * @example
 * pick({ name: "Jack", age: 69 }, ["age", "title"]);
 * // { age: 69 }
 * @returns {Object} - new object with given properties
 */
const pick = <T extends object, K extends keyof T>(
    object: T | null, props: K[],
): ReturnType => {
    // validation
    // implementation
};

export { pick };
```

### TypeScript Patterns

- Use `@ts-expect-error` for known type system limitations with explanatory comments
- Prefer explicit return types on all functions (per 4.6.0 changelog goal)
- Use generics for type-safe object manipulation
- Handle null/undefined inputs gracefully

### Testing Patterns

- Use `must` assertion library (not Jest expect)
- Test typical use cases first
- Test edge cases (null, undefined, empty arrays/objects)
- Test TypeScript types where relevant

## Adding New Utilities

When adding a new utility function:

1. Create `functionName.ts` in `src/` with the implementation
2. Create `functionName.spec.ts` in `src/` with comprehensive tests
3. Add export to `src/index.ts` following alphabetical order
4. Update README.md's "Supported methods" section
5. Run `pnpm test` to verify tests pass
6. Run `pnpm run compile` to ensure builds work
7. Run `pnpm run docs` to update documentation

## Important Notes

- **Philosophy**: Cover typical use cases, not every edge case. Keep implementations simple and focused.
- **Bundle size**: This is a primary concern. Avoid unnecessary dependencies and complexity.
- **Not a polyfill**: Don't add functions that already exist in modern JavaScript (like `map`, `forEach`, etc.)
- **Lodash compatibility**: Not required. Similar behavior is fine, but perfect compatibility is not a goal.
- **Deprecation strategy**: Old functions are deprecated (like `match` deprecated in favor of `hasProps`/`assertProps`) but not removed.
