Noir::TreeSitterKotlinKtorRouteExtractor
Tree-sitter-backed Ktor DSL route extractor.
Walks the canonical Ktor server idiom:
routing {
get("/x") { ... }
route("/api") {
post("/items") { val item = call.receive < Item > () }
}
authenticate("auth-jwt") {
get("/profile") { ... }
}
}
Recognises:
- Verb DSL calls —
get/post/put/delete/patch/head/options/querywith a string-literal path argument and a trailing lambda body. route("/x") { ... }blocks contributing to the path prefix.authenticate("realm") { ... }blocks acting as transparent wrappers (no prefix change). Tagging is handled elsewhere; we just descend so wrapped routes are still discovered.routing { ... }andapplication.routing { ... }entry points.- Type-safe
@Resourcerouting —get<VideoStream> { },resource<Login> { post { } },method(HttpMethod.Get) { handle<T> }— with cross-file parent-path composition. - Inside each verb's lambda body:
call.receive<T>()→bodyparameter typedTasjsoncall.parameters["name"]→nameparameter asquerycall.request.headers["name"]→nameparameter asheader
Not covered yet:
install(plugin) { ... }plugin scoping that affects routing.
Constants
API-documentation DSL blocks. They hang off a route
(get("/x") { }.describe { ... }) or nest inside one, and they
describe an endpoint rather than declaring one — no route is ever
registered inside them.
They have to be skipped because the parameter sub-DSL names a query
parameter with query("name") { ... }, which is exactly the shape of
the QUERY verb call (RFC 10008): a name, a trailing lambda, inside the
routing scope. Without this, every documented query parameter became a
QUERY route named after the parameter — query("chunk_size") in Ktor's
own httpbin sample surfaced as QUERY /chunk_size.
Only the lambda is skipped, never the receiver: the block is chained onto the very route it documents, so dropping the whole call would take the real route with it.
Pass-through DSL calls — descend into their lambda body without
changing the path prefix. routing is the entry point;
authenticate wraps a sub-tree behind an auth realm; the
remaining names cover the common Ktor scoping helpers. install
is handled separately (see walk) because only install(Routing)
contributes routes — every other plugin config block must NOT be
walked as routing.
Instance methods
Resolve each raw resource to its full URL path and query parameters.
The parent is the first primary-constructor property whose type is
itself a resource (root: Root → /api); the child path joins onto it.
Returns a map keyed by BOTH the dotted lexical name (Articles.New)
and the bare simple name (TagsResource) so get<...> references
resolve either way.
Collect every @Resource("path")-annotated class/object in a file
(including nested ones, with their dotted lexical name). The
analyzer gathers these across the whole project before composing
full paths, since a resource's parent is often declared in another
module (Ktor's KMP commonMain resource definitions).