Skip to content

Commit 0bdd6e6

Browse files
docs: add AGENTS.md / docs/architecture.md (#464)
hopefully make agents a bit better at contributing to the codebase, and go off the rails trying to run tests etc less 🙏 --------- Co-authored-by: Junie <junie@jetbrains.com>
1 parent d0e8945 commit 0bdd6e6

2 files changed

Lines changed: 159 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Agent Guide
2+
3+
This document provides essential information for AI agents and developers working on the `@nahkies/openapi-code-generator` monorepo.
4+
5+
## Project Overview
6+
7+
This is a monorepo managed with `pnpm`. It produces a CLI tool for generating high-quality TypeScript client SDKs and server scaffolding from OpenAPI 3.0, 3.1, and TypeSpec specifications.
8+
9+
## Critical Constraints
10+
11+
- **Execution**: NEVER use `npx`. ALWAYS use `pnpm run <script>` or `pnpm exec <command>`.
12+
- **Read-Only Directories**:
13+
- `integration-tests-definitions/`: Contains source specifications.
14+
- `integration-tests/*/src/generated/`: Contains generated code.
15+
- `tsconfig.tsbuildinfo`: Compiler state files.
16+
- **Generated Files**:
17+
- `src/core/schemas/openapi-3.0-specification-validator.js`
18+
- `src/core/schemas/openapi-3.1-specification-validator.js`
19+
- (These are generated by `ajv` during the build process).
20+
21+
## Development Workflow
22+
23+
### Environment
24+
- **Node.js**: >= 22.x (CI tests on 22.x and 24.x).
25+
- **Package Manager**: `pnpm` (Corepack enabled).
26+
27+
### Common Commands
28+
- **Install**: `pnpm install`
29+
- **Build**: `pnpm build` (generates validators, bundles packages, and runs `tsc -b`).
30+
- **Lint/Format**: `pnpm lint` (uses Biome). For CI-style checks, use `pnpm ci-lint`.
31+
- **Unit Tests**: `pnpm test` (uses Jest). For coverage, use `pnpm ci-test`.
32+
- **Integration Tests**:
33+
- Generate: `pnpm integration:generate`
34+
- Validate/Build: `pnpm integration:validate`
35+
- **E2E Tests**:
36+
- Generate: `pnpm e2e:generate`
37+
- Validate: `pnpm e2e:validate`
38+
- **Full Pipeline**: `pnpm ci-pipeline` (runs all checks locally).
39+
40+
## Testing Standards
41+
42+
- **Unit Testing**: Tests are co-located with source code (`.spec.ts`).
43+
- **Jest Globals**: You MUST explicitly import Jest globals in test files:
44+
```typescript
45+
import {describe, expect, it} from "@jest/globals"
46+
```
47+
- **Regression**: Always ensure `pnpm integration:validate` passes after changes to generation logic.
48+
49+
## Project Structure
50+
51+
For a detailed overview of the system's design, internal patterns (such as Builders and the Intermediate Representation), and data flows, please refer to [docs/architecture.md](docs/architecture.md).
52+
53+
- `packages/*`: Implementation of the generator and its runtimes.
54+
- `integration-tests-definitions`: OpenAPI/TypeSpec definitions for testing.
55+
- `integration-tests`: Generated code from the above definitions.
56+
- `scripts/`: Maintenance and CI/CD scripts.
57+
- `e2e/`: End-to-end tests.

docs/architecture.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Architecture
2+
3+
This document describes the high-level architecture, package structure, and common patterns of the `@nahkies/openapi-code-generator` monorepo.
4+
5+
## High-Level Overview
6+
7+
`@nahkies/openapi-code-generator` is a monorepo that produces a CLI tool for generating TypeScript client SDKs and server scaffolding from OpenAPI 3.0, 3.1, and TypeSpec specifications.
8+
9+
The system follows a typical compiler/generator pipeline:
10+
1. **Loading**: Specifications are loaded and resolved (including remote/local references).
11+
2. **Validation**: Loaded specifications are validated against their respective schemas using pre-compiled AJV validators.
12+
3. **Normalization**: The specification is transformed into a normalized Intermediate Representation (IR).
13+
4. **Building**: Template-specific builders transform the IR into code structures (types, schemas, clients/routers).
14+
5. **Emission**: The generated structures are formatted and written to the file system.
15+
16+
## Package Structure
17+
18+
- **`packages/openapi-code-generator`**: The core package containing the CLI, loader logic, IR normalization, and code generation templates.
19+
- **`packages/typescript-common-runtime`**: Shares common types (e.g., `Res`, `StatusCode`) and utilities (e.g., query parsing, body validation) used by all generated TypeScript code.
20+
- **`packages/typescript-*-runtime`**: Runtime-specific support packages.
21+
- `typescript-axios-runtime`: Provides utilities for Axios clients.
22+
- `typescript-fetch-runtime`: Provides `AbstractFetchClient`.
23+
- `typescript-express-runtime`: Provides utilities for routing and response handling in Express.
24+
- `typescript-koa-runtime`: Provides utilities for routing and response handling in Koa.
25+
- **`integration-tests`**: Validates the generator by running it against real-world specifications (Github, Stripe, etc.) and ensuring the output compiles.
26+
- **`e2e`**: End-to-end tests for the CLI and generated code.
27+
28+
## Core Patterns
29+
30+
### Adaptor Pattern
31+
The generator uses adaptors to abstract environment-specific logic, allowing it to run in both Node.js and the browser (playground).
32+
- **`IFsAdaptor`**: Abstraction for file system operations (`NodeFsAdaptor`, `WebFsAdaptor`).
33+
- **`IFormatter`**: Abstraction for code formatting (`TypescriptFormatterPrettier`, `TypescriptFormatterBiome`).
34+
35+
### Builder Pattern
36+
Complex code generation is managed through builders that accumulate state and then produce a `CompilationUnit`.
37+
- **`TypeBuilder`**: Generates TypeScript type definitions.
38+
- **`SchemaBuilder`**: Generates runtime validation schemas (Joi or Zod).
39+
- **`ClientBuilder`**: (e.g., `TypescriptFetchClientBuilder`) Generates client-side SDK code.
40+
- **`RouterBuilder` / `ServerBuilder`**: (e.g., `ExpressRouterBuilder`, `ExpressServerBuilder`) Generates server-side scaffolding and routing.
41+
42+
### Compilation Units
43+
A `CompilationUnit` (defined in `packages/openapi-code-generator/src/typescript/common/compilation-units.ts`) represents a single file to be emitted. It encapsulates:
44+
- The target filename.
45+
- An `ImportBuilder` to manage required imports without duplicates.
46+
- The raw string of code.
47+
48+
### Intermediate Representation (IR)
49+
The `Input` class (in `src/core/input.ts`) serves as the IR. It takes a raw loader and provides normalized access to operations, schemas, and servers. This decoupling ensures that templates don't need to handle OpenAPI version differences or complex reference resolution.
50+
51+
## Data Flow
52+
53+
```mermaid
54+
graph TD
55+
Spec[OpenAPI/TypeSpec Spec] --> Loader[OpenapiLoader]
56+
Loader --> Validator[OpenapiValidator]
57+
Validator --> Input[Input / IR]
58+
Input --> Generator[Template Generator]
59+
Generator --> Builders[Builders: Type, Schema, Client, Router]
60+
Builders --> Units[CompilationUnits]
61+
Units --> Emitter[TypescriptEmitter]
62+
Emitter --> Formatter[Formatter: Biome/Prettier]
63+
Formatter --> FS[Filesystem]
64+
```
65+
66+
## Important Invariants & Conventions
67+
68+
### Invariants
69+
- **No `npx`**: Always use `pnpm run` or `pnpm exec`.
70+
- **Read-Only Generation**: Never manually edit files in `src/generated/` directories; they are overwritten during generation.
71+
- **Jest Globals**: Always explicitly import Jest globals (`describe`, `it`, `expect`) from `@jest/globals`.
72+
73+
### Conventions
74+
- **Co-location**: Unit tests (`*.spec.ts`) are co-located with the source code they test. Use explicit jest imports (`import {describe, it, expect} from "@jest/globals"`).
75+
- **Import Extensions**: Use `.ts` extensions in imports (e.g., `import {foo} from "./foo.ts"`) to support ESM.
76+
- **Dependency Migration**: Prefer `pnpm` workspace references (e.g., `"@nahkies/typescript-common-runtime": "workspace:*"`).
77+
78+
## How to add a new feature
79+
80+
1. **Identify the Stage**: Determine if the change belongs in the `Loader`, `Input` (IR), or a specific `Generator`/`Builder`.
81+
2. **Update IR (if needed)**: If the feature requires new data from the OpenAPI spec, update `src/core/openapi-types-normalized.ts` and the normalization logic in `src/core/input.ts`.
82+
3. **Update Builders**: Implement the code generation logic in the relevant builders (e.g., `ClientOperationBuilder`).
83+
4. **Add Unit Tests**: Add a `.spec.ts` next to the modified file.
84+
5. **Add Integration Spec**: If the feature is a new OpenAPI concept, add a minimal reproduction spec to `integration-tests-definitions`.
85+
6. **Run Pipeline**:
86+
- `pnpm build`
87+
- `pnpm integration:generate`
88+
- `pnpm integration:validate`
89+
7. **Documentation**: Update the external documentation in `packages/documentation` if user-facing.
90+
91+
## Common Gotchas
92+
93+
- **Cyclic Dependencies**: When generating schemas (Joi/Zod), the generator must handle cyclic references in the OpenAPI spec. Check `SchemaBuilder`'s handling of `Reference`. For Zod, we have multiple versions (`zod-v3`, `zod-v4`) as Zod v4 handles lazy schemas differently.
94+
- **Filename Casing**: The generator supports different filename conventions. Ensure your builders respect `config.filenameConvention`.
95+
- **ESM vs CJS**: The project targets ESM. Relative imports must include file extensions.
96+
- **Generated Schema Validators**: `src/core/schemas/openapi-*-specification-validator.js` are generated. If you update the meta-schemas, you must run the build script to refresh them.
97+
98+
## Anti-Patterns
99+
100+
- **Direct Spec Access**: Avoid accessing the raw `Loader` or `loader.entryPoint` from within template builders. Use the normalized `Input` (IR) instead.
101+
- **Hardcoded Filenames**: Avoid hardcoding filenames in builders; use the provided config and `ImportBuilder` to manage paths.
102+
- **Complex Logic in Templates**: `*.generator.ts` files should be orchestrators. Move complex logic into dedicated `Builder` classes (e.g. `ClientOperationBuilder` or `SchemaBuilder`).

0 commit comments

Comments
 (0)