Skip to content

Commit 66394ae

Browse files
mnahkiesjunie-agent
andcommitted
docs: architecture and agents documentation update
Co-authored-by: Junie <junie@jetbrains.com>
1 parent 993d5d6 commit 66394ae

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ This is a monorepo managed with `pnpm`. It produces a CLI tool for generating hi
4848

4949
## Project Structure
5050

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+
5153
- `packages/*`: Implementation of the generator and its runtimes.
5254
- `integration-tests-definitions`: OpenAPI/TypeSpec definitions for testing.
5355
- `integration-tests`: Generated code from the above definitions.

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)