|
1 | 1 | # Agent Guidelines |
2 | 2 |
|
| 3 | +Guidelines for AI coding agents working in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Small npm package (`@uscreen.de/id-generator`) that generates unique, sortable IDs |
| 8 | +using ULID with optional prefixes. Ships raw ES module JavaScript with separate |
| 9 | +TypeScript declarations -- no build step. |
| 10 | + |
| 11 | +- **Runtime**: Node.js >= 18 |
| 12 | +- **Module system**: ES modules (`"type": "module"` in package.json) |
| 13 | +- **Package manager**: pnpm (version managed via Corepack, see `packageManager` field) |
| 14 | +- **Single production dependency**: `ulid` |
| 15 | + |
3 | 16 | ## Commands |
4 | 17 |
|
5 | | -- **Test**: `pnpm test` (all tests), `pnpm test:cov` (with coverage), `pnpm test:ci` (CI mode) |
6 | | -- **Lint**: `pnpm lint` (check), `pnpm lint:fix` (fix issues) |
7 | | -- **Pre-publish**: `pnpm prepublishOnly` (runs lint + test) |
| 18 | +### Testing |
| 19 | + |
| 20 | +```bash |
| 21 | +pnpm test # Run all tests with c8 coverage (spec reporter) |
| 22 | +pnpm test:cov # Run tests with HTML + text + lcov coverage reports |
| 23 | +pnpm test:ci # Run tests without coverage (CI mode, fast) |
| 24 | +``` |
| 25 | + |
| 26 | +Run a single test by name using Node.js test runner `--test-name-pattern`: |
| 27 | + |
| 28 | +```bash |
| 29 | +node --test --test-name-pattern="with prefix" test.js |
| 30 | +``` |
| 31 | + |
| 32 | +Run tests matching a file pattern (not applicable here since there is only `test.js`): |
| 33 | + |
| 34 | +```bash |
| 35 | +node --test test.js |
| 36 | +``` |
| 37 | + |
| 38 | +The test runner is Node.js built-in (`node:test`). There is no separate test |
| 39 | +framework. Tests live in a single file: `test.js`. |
| 40 | + |
| 41 | +### Linting |
| 42 | + |
| 43 | +```bash |
| 44 | +pnpm lint # Check for lint errors |
| 45 | +pnpm lint:fix # Auto-fix lint errors (includes formatting) |
| 46 | +``` |
| 47 | + |
| 48 | +ESLint handles both linting and formatting (no Prettier). Always run `pnpm lint:fix` |
| 49 | +after making changes. |
| 50 | + |
| 51 | +### Pre-publish |
| 52 | + |
| 53 | +```bash |
| 54 | +pnpm prepublishOnly # Runs lint + test (must pass before npm publish) |
| 55 | +``` |
8 | 56 |
|
9 | 57 | ## Code Style |
10 | 58 |
|
11 | | -- **ESLint**: Uses @antfu/eslint-config with formatters enabled |
12 | | -- **Comma dangle**: Never use trailing commas |
13 | | -- **Curly braces**: Multi-line and consistent |
14 | | -- **Console**: Console statements allowed |
15 | | -- **Imports**: ES modules only (`import`/`export`) |
16 | | -- **Node built-ins**: Use `node:` prefix (e.g., `node:assert/strict`) |
| 59 | +### Formatting (enforced by ESLint) |
| 60 | + |
| 61 | +- **No trailing commas** -- `style/comma-dangle: ['error', 'never']` |
| 62 | +- **Single quotes** for strings (antfu default) |
| 63 | +- **No semicolons** (antfu default) |
| 64 | +- **2-space indentation** (antfu default) |
| 65 | +- **Curly braces**: required for multi-line blocks, must be consistent within an |
| 66 | + if/else chain -- `curly: ['error', 'multi-line', 'consistent']` |
| 67 | +- **Console**: `console.log` and similar are allowed (`no-console: off`) |
| 68 | + |
| 69 | +### Imports |
| 70 | + |
| 71 | +- ES modules only -- use `import`/`export`, never `require()` |
| 72 | +- Node.js built-ins MUST use the `node:` prefix: |
| 73 | + ```js |
| 74 | + import assert from 'node:assert/strict' |
| 75 | + import { describe, test } from 'node:test' |
| 76 | + ``` |
| 77 | +- Group imports: external packages first, then local modules, separated by a |
| 78 | + blank line: |
| 79 | + ```js |
| 80 | + import { distance } from 'fastest-levenshtein' |
| 81 | + |
| 82 | + import { id } from './index.js' |
| 83 | + ``` |
| 84 | +- Always include `.js` extension in relative imports |
17 | 85 |
|
18 | | -## Patterns |
| 86 | +### Functions |
19 | 87 |
|
20 | | -- **Functions**: Export named functions, use arrow functions for internal logic |
21 | | -- **Testing**: Node.js built-in test runner with `describe`/`test` structure |
22 | | -- **Types**: TypeScript declarations in separate `.d.ts` files |
23 | | -- **Dependencies**: Use exact versions, minimal deps (only `ulid` in production) |
24 | | -- **Error handling**: Use `assert` for testing, prefer explicit checks |
25 | | -- **Comments**: JSDoc for public APIs, minimal inline comments |
| 88 | +- Use `export const name = (...) => { }` for public API functions (arrow functions) |
| 89 | +- The `antfu/top-level-function` rule is disabled -- arrow functions are fine at |
| 90 | + top level |
| 91 | +- Create aliases by re-exporting: `export const generateId = id` |
| 92 | + |
| 93 | +### Types |
| 94 | + |
| 95 | +- TypeScript declarations live in separate `.d.ts` files (not inline JSDoc types) |
| 96 | +- The `.d.ts` files are excluded from ESLint via `eslint.config.js` |
| 97 | +- Use `export declare function` syntax in declaration files |
| 98 | +- Keep type declarations in sync with the JS implementation |
| 99 | + |
| 100 | +### Naming Conventions |
| 101 | + |
| 102 | +- **Functions/variables**: camelCase (`id`, `generateId`, `monotonicFactory`) |
| 103 | +- **Constants**: camelCase (not UPPER_SNAKE_CASE) |
| 104 | +- **File names**: lowercase, no special casing (`index.js`, `test.js`) |
| 105 | +- **Test descriptions**: descriptive strings starting with the module name |
| 106 | + (e.g., `'id generator with prefix'`) |
| 107 | + |
| 108 | +### Comments |
| 109 | + |
| 110 | +- JSDoc `/** */` blocks for public API functions (brief, one-liner descriptions) |
| 111 | +- Minimal inline comments -- only when intent is non-obvious |
| 112 | +- Section separator comments for logical groupings (e.g., `/** alias exports */`) |
| 113 | + |
| 114 | +### Error Handling |
| 115 | + |
| 116 | +- Use `node:assert/strict` for test assertions (`assert.ok`, `assert.strictEqual`) |
| 117 | +- Prefer explicit boolean checks over truthy/falsy where possible |
| 118 | +- No try/catch in this codebase -- the library functions are synchronous and |
| 119 | + do not throw under normal usage |
| 120 | + |
| 121 | +## Testing Patterns |
| 122 | + |
| 123 | +Tests use Node.js built-in test runner with `describe`/`test` blocks: |
| 124 | + |
| 125 | +```js |
| 126 | +import assert from 'node:assert/strict' |
| 127 | +import { describe, test } from 'node:test' |
| 128 | + |
| 129 | +describe('feature name', async () => { |
| 130 | + test('specific behavior', async () => { |
| 131 | + // arrange |
| 132 | + const result = someFunction() |
| 133 | + // assert |
| 134 | + assert.ok(result) |
| 135 | + assert.strictEqual(result, expected) |
| 136 | + }) |
| 137 | +}) |
| 138 | +``` |
| 139 | + |
| 140 | +Key patterns observed in `test.js`: |
| 141 | +- All test callbacks are `async` functions |
| 142 | +- `describe` callback is also `async` |
| 143 | +- Use `scheduler.wait(ms)` from `node:timers/promises` for timing-sensitive tests |
| 144 | +- Use third-party `fastest-levenshtein` for distance assertions on uniqueness |
| 145 | +- Tests verify: correctness, edge cases, sortability, and performance (100k IDs) |
26 | 146 |
|
27 | 147 | ## File Structure |
28 | 148 |
|
29 | | -- `index.js` - Main implementation (ES modules) |
30 | | -- `index.d.ts` - TypeScript declarations |
31 | | -- `test.js` - All tests using Node.js test runner |
32 | | -- Package manager: pnpm (Node.js 18+) |
| 149 | +``` |
| 150 | +index.js Main implementation (ES module, ~18 lines) |
| 151 | +index.d.ts TypeScript type declarations |
| 152 | +test.js All tests (Node.js test runner) |
| 153 | +eslint.config.js ESLint flat config (antfu + custom rules) |
| 154 | +package.json Package metadata, scripts, dependencies |
| 155 | +.nvmrc Node.js version for local dev (24) |
| 156 | +.github/ |
| 157 | + workflows/ |
| 158 | + ci.yml CI pipeline (Node 20/22/24, lint, test, coverage) |
| 159 | + codeql.yml GitHub CodeQL security scanning |
| 160 | + dependabot.yml Automated dependency updates |
| 161 | +``` |
| 162 | + |
| 163 | +## CI |
| 164 | + |
| 165 | +- Tests run on Node.js 20, 22, and 24 |
| 166 | +- Linting runs on Node 20+ |
| 167 | +- Coverage (c8) generated on Node 22, uploaded to Codecov |
| 168 | +- Uses Corepack for pnpm version management |
| 169 | +- `pnpm install --frozen-lockfile` in CI (lockfile must be up to date) |
| 170 | + |
| 171 | +## Dependencies Policy |
| 172 | + |
| 173 | +- Keep production dependencies minimal (currently only `ulid`) |
| 174 | +- Dev dependencies: ESLint ecosystem (`@antfu/eslint-config`, `eslint`, |
| 175 | + `eslint-plugin-format`), coverage (`c8`), test utilities (`fastest-levenshtein`) |
| 176 | +- Use caret ranges (`^`) for version specifiers |
| 177 | +- Dependabot groups: `dev-dependencies` and `production-dependencies` |
| 178 | + |
| 179 | +## Common Pitfalls |
| 180 | + |
| 181 | +- Do NOT add trailing commas -- the linter will reject them |
| 182 | +- Do NOT use `require()` -- this is an ES module package |
| 183 | +- Do NOT forget the `node:` prefix on Node.js built-in imports |
| 184 | +- Do NOT add semicolons -- antfu config enforces no-semicolons |
| 185 | +- Always include `.js` extension in relative import paths |
| 186 | +- Run `pnpm lint:fix` before committing to ensure formatting is correct |
0 commit comments