Skip to content

fix: discover generated controllers by metadata - #688

Merged
kang-heewon merged 1 commit into
trunkfrom
fix-663-codegen-controller-discovery
Jun 14, 2026
Merged

fix: discover generated controllers by metadata#688
kang-heewon merged 1 commit into
trunkfrom
fix-663-codegen-controller-discovery

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary

OpenAPI and RPC codegen now discover exported REST controllers from controller metadata instead of importing every class declaration by name, so co-located DTO/helper classes are ignored and controller globs fail clearly when no exported controller is found.

Fixes #663

변경 사항

  • Added shared controller-constructor discovery in @croco/protocols-core using REST controller metadata.
  • Refactored OpenAPI and RPC CLI controller loading to compile matched files, import each emitted module once, and filter exported controllers through the shared helper.
  • Added regression coverage for unexported DTO/helper classes, exported non-controller classes, matched files with no controllers, and empty globs with no controllers.
  • Added a patch changeset for @croco/protocols-core, @croco/openapi-spec, and @croco/rpc-codegen.

Verification

  • pnpm --filter @croco/protocols-core exec vitest run src/tests/controllerDiscovery.spec.ts
  • pnpm --filter @croco/openapi-spec exec vitest run src/tests/loadControllers.spec.ts
  • pnpm --filter @croco/rpc-codegen exec vitest run src/tests/loadRoutes.spec.ts
  • pnpm --filter @croco/openapi-spec test
  • pnpm --filter @croco/rpc-codegen test
  • pnpm --filter @croco/protocols-core test
  • pnpm --filter @croco/protocols-core typecheck
  • pnpm --filter @croco/openapi-spec typecheck
  • pnpm --filter @croco/rpc-codegen typecheck
  • pnpm --filter @croco/protocols-core build
  • pnpm --filter @croco/openapi-spec build
  • pnpm --filter @croco/rpc-codegen build
  • pnpm check
  • pnpm typecheck
  • pnpm test
  • pnpm build
  • pnpm exec changeset status --since origin/trunk
  • pre-commit: oxfmt, oxlint
  • pre-push: auto-changeset, test, typecheck

Self-review

  • Correctness/regression: [codegen] Filter controller classes before importing generated modules #663 acceptance is covered by tests that prove helper/DTO classes are ignored, exported non-controller classes are skipped, and both matched-no-controller and empty-glob inputs fail with a clear diagnostic.
  • API/security/compatibility: route extraction and generated output formats are unchanged for real controllers; the intentional behavior change is that codegen now rejects inputs with no exported REST controllers instead of silently succeeding or importing non-controller classes.
  • Maintainability/minimality: shared discovery lives with protocol metadata, both codegen CLIs use the same helper, and the diff stays limited to loader extraction, focused tests, and release metadata.

Risk

Users who passed empty or non-controller-only globs to codegen will now receive a hard diagnostic and must point the generator at exported @Controller classes.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • REST 컨트롤러 자동 탐지 기능이 OpenAPI 및 RPC 코드생성에 추가되었습니다.
  • 개선 사항

    • 메타데이터 기반 컨트롤러 발견으로 코드생성이 더욱 안정적으로 동작하도록 개선되었습니다.
    • 동일 파일 내 헬퍼 클래스가 자동으로 필터링되도록 처리됩니다.
  • 테스트

    • 컨트롤러 발견 및 라우트 로딩 로직에 대한 테스트 커버리지가 추가되었습니다.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kang-heewon, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 35 minutes and 52 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c9d5ae48-0d42-4e88-a756-f4bc87bc370b

📥 Commits

Reviewing files that changed from the base of the PR and between 2ba4ecc and 880217b.

📒 Files selected for processing (10)
  • .changeset/codegen-controller-discovery.md
  • packages/openapi-spec/src/cli.ts
  • packages/openapi-spec/src/libs/loadControllers.ts
  • packages/openapi-spec/src/tests/loadControllers.spec.ts
  • packages/protocols-core/src/index.ts
  • packages/protocols-core/src/libs/controllerDiscovery.ts
  • packages/protocols-core/src/tests/controllerDiscovery.spec.ts
  • packages/rpc-codegen/src/cli.ts
  • packages/rpc-codegen/src/libs/loadRoutes.ts
  • packages/rpc-codegen/src/tests/loadRoutes.spec.ts
📝 Walkthrough

Walkthrough

protocols-coreisControllerConstructordiscoverControllerConstructors 유틸을 추가하여 reflect-metadata 기반으로 컨트롤러 생성자를 식별한다. openapi-specrpc-codegen CLI에 인라인으로 존재하던 컨트롤러/라우트 로딩 구현을 각각 libs/loadControllers, libs/loadRoutes 모듈로 분리하고 공유 발견 로직을 사용하도록 리팩터링한다.

Changes

메타데이터 기반 컨트롤러 발견 및 CLI 로딩 로직 분리

Layer / File(s) Summary
protocols-core: 컨트롤러 발견 유틸 및 공개 exports
packages/protocols-core/src/libs/controllerDiscovery.ts, packages/protocols-core/src/index.ts, packages/protocols-core/src/tests/controllerDiscovery.spec.ts
isControllerConstructor(REST_CONTROLLER_KEY 메타데이터 검사)와 discoverControllerConstructors(Set 중복 제거 수집)를 구현하고 Constructor 타입과 함께 index.ts에서 re-export한다. 테스트는 컨트롤러/비컨트롤러 판별 및 혼합 모듈 exports 필터링을 검증한다.
openapi-spec: loadControllers libs 모듈 추출
packages/openapi-spec/src/cli.ts, packages/openapi-spec/src/libs/loadControllers.ts, packages/openapi-spec/src/tests/loadControllers.spec.ts
cli.ts에서 인라인 구현을 제거하고 ./libs/loadControllers로 위임한다. loadControllers.ts는 ts-morph로 glob 매칭 파일을 임시 디렉터리에 컴파일 후 discoverControllerConstructors로 컨트롤러만 선별하며 finally 블록에서 정리한다. 테스트는 헬퍼 클래스 무시, 컨트롤러 없음 에러, 매칭 파일 없음 에러 세 가지 시나리오를 검증한다.
rpc-codegen: loadRoutes libs 모듈 추출
packages/rpc-codegen/src/cli.ts, packages/rpc-codegen/src/libs/loadRoutes.ts, packages/rpc-codegen/src/tests/loadRoutes.spec.ts
cli.ts에서 인라인 구현을 제거하고 ./libs/loadRoutes로 위임한다. loadRoutes.ts는 동일 패턴으로 컴파일·발견·extractRouteIR 집계를 수행하여 RouteIR[]를 반환한다. 테스트는 openapi-spec과 동일한 세 가지 시나리오를 검증한다.
Changeset
.changeset/codegen-controller-discovery.md
3개 패키지의 patch 버전 범위와 변경 내용 요약을 담은 changeset 문서를 추가한다.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (openapi-spec / rpc-codegen)
  participant Loader as loadControllers / loadRoutes
  participant Project as ts-morph Project
  participant TmpDir as 임시 emit 디렉터리
  participant DC as discoverControllerConstructors
  participant Meta as reflect-metadata

  CLI->>Loader: loadControllers(glob) / loadRoutes(glob)
  Loader->>Project: addSourceFilesFromGlob(glob)
  Project-->>Loader: SourceFile[]
  Loader->>TmpDir: emit → .js 파일 작성
  loop 각 emitted 모듈
    Loader->>TmpDir: import(file://*.js)
    TmpDir-->>Loader: moduleExports
    Loader->>DC: discoverControllerConstructors(moduleExports)
    DC->>Meta: Reflect.getMetadata(REST_CONTROLLER_KEY, fn)
    Meta-->>DC: 메타데이터 유무
    DC-->>Loader: Constructor[]
  end
  Loader-->>CLI: Controller[] / RouteIR[]
  Loader->>TmpDir: rm(tmpDir) [finally]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 '생성된 컨트롤러를 메타데이터로 발견'이라는 핵심 변경사항을 정확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR이 issue #663의 모든 요구사항을 충족합니다. 메타데이터 기반 컨트롤러 발견 구현, 공유 가능한 발견 로직, 포괄적인 테스트 커버리지가 모두 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 issue #663의 범위 내에서 이루어졌으며, 컨트롤러 발견 메커니즘 개선에만 집중되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-663-codegen-controller-discovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown

📊 Benchmark Results

❌ Some benchmarks failed

Benchmark p75 Threshold Baseline vs Baseline Status
CrocoApp benchmarks 3.0μs - - - ⚠️
EventBusConfig.start (10 handlers) 1.3μs - 9.0μs -85.8%
EventPublisher.publishNow single event 1.5μs - - - ⚠️
DefaultHandlerResolver.resolve × 10 0.1μs - 0.2μs -60.0%
Container.get singleton (cold) 0.9μs - 0.5μs +80.4%
Container.register × 50 components 12.5μs - 8.0μs +55.7%
Container.validate (50 components) 29.3μs - 25.0μs +17.1%
Container.get singleton (warm) 0.4μs - 0.3μs +43.7%
TelemetryRuntime benchmarks 2.2μs - 2.0μs +8.7%

Updated: 2026-06-14T13:14:01.685Z · Commit: 9b45a4a

@kang-heewon
kang-heewon force-pushed the fix-663-codegen-controller-discovery branch 2 times, most recently from 9cf9fbe to 2ba4ecc Compare June 14, 2026 12:46
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/openapi-spec/src/libs/loadControllers.ts`:
- Around line 48-52: Replace the generic Error instance being thrown when no
REST controllers are found (in the block checking if controllers.length === 0)
with an appropriate Problem subclass. Instead of throwing new Error with the
message about no exported REST controllers found for the glob pattern,
instantiate and throw the appropriate Problem subclass that handles this
scenario, ensuring compliance with RFC 7807 Problem-based error handling
guidelines.
- Around line 23-27: Replace the generic Error being thrown in the
sourceFiles.length === 0 check with an appropriate Problem subclass that
conforms to RFC 7807 Problem-based error handling. Import the suitable Problem
subclass at the top of the file, then instantiate and throw that Problem
subclass instead of the generic Error, while preserving the existing error
message about no exported REST controllers being found for the given glob
pattern.
- Line 8: Replace the `Controller` type definition that currently uses
`Function` with the `Constructor` type imported from `@croco/protocols-core`.
This change improves type safety and consistency. After making this change,
locate the double type assertion `as unknown as Controller` at lines 42-44 and
remove it, since the proper `Constructor` type will eliminate the need for the
intermediate `unknown` type assertion.

In `@packages/openapi-spec/src/tests/loadControllers.spec.ts`:
- Around line 9-10: Add definite assignment assertions (!) to the test instance
variables declared in the describe block. Modify both tempRoot and sourceDir
variable declarations to include the ! operator after the variable name and
before the type annotation, so they read as let tempRoot!: string and let
sourceDir!: string. This follows the coding guideline requirement for test
instance variables declared in describe blocks.

In `@packages/rpc-codegen/src/libs/loadRoutes.ts`:
- Around line 69-83: The getCommonSourceDir function incorrectly handles
absolute paths on Windows by checking if commonDir starts with path.sep and
prepending it if it doesn't. This fails on Windows where absolute paths start
with drive letters (e.g., C:\project) rather than a separator, resulting in
malformed paths like \C:\project. Replace the path.sep check with
path.isAbsolute(commonDir) which correctly identifies absolute paths across
platforms regardless of whether they start with path.sep. This will prevent
incorrectly prepending a separator to paths that are already absolute.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dc2e40fe-fa5f-424b-b2ce-7fd3b6207798

📥 Commits

Reviewing files that changed from the base of the PR and between 6c159a3 and 2ba4ecc.

📒 Files selected for processing (10)
  • .changeset/codegen-controller-discovery.md
  • packages/openapi-spec/src/cli.ts
  • packages/openapi-spec/src/libs/loadControllers.ts
  • packages/openapi-spec/src/tests/loadControllers.spec.ts
  • packages/protocols-core/src/index.ts
  • packages/protocols-core/src/libs/controllerDiscovery.ts
  • packages/protocols-core/src/tests/controllerDiscovery.spec.ts
  • packages/rpc-codegen/src/cli.ts
  • packages/rpc-codegen/src/libs/loadRoutes.ts
  • packages/rpc-codegen/src/tests/loadRoutes.spec.ts

Comment thread packages/openapi-spec/src/libs/loadControllers.ts Outdated
Comment thread packages/openapi-spec/src/libs/loadControllers.ts
Comment thread packages/openapi-spec/src/libs/loadControllers.ts
Comment thread packages/openapi-spec/src/tests/loadControllers.spec.ts Outdated
Comment thread packages/rpc-codegen/src/libs/loadRoutes.ts
@kang-heewon
kang-heewon force-pushed the fix-663-codegen-controller-discovery branch from 2ba4ecc to 851d1cf Compare June 14, 2026 12:57
@kang-heewon
kang-heewon force-pushed the fix-663-codegen-controller-discovery branch from 851d1cf to 880217b Compare June 14, 2026 13:10
@kang-heewon
kang-heewon merged commit 2631037 into trunk Jun 14, 2026
7 of 8 checks passed
@kang-heewon
kang-heewon deleted the fix-663-codegen-controller-discovery branch June 14, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[codegen] Filter controller classes before importing generated modules

1 participant