module

Noir::TreeSitterAdonisJsExtractor

Tree-sitter-backed AdonisJS extractor.

AdonisJS exposes a Laravel-flavoured router via either Route (v5, @ioc:Adonis/Core/Route) or router (v6, @adonisjs/core/services/router). Both share the same shape:

Route.get('/users', 'UsersController.index')

Route.group(() => {
    Route.get('/posts', 'PostsController.index')
    Route.post('/posts', 'PostsController.store')
}).prefix('/api/v1').middleware('auth')

Route.resource('articles', 'ArticlesController').apiOnly()

The fluent modifiers (.prefix, .middleware, .as, .domain, .namespace, .where, .apiOnly, .only, .except) live on the result of the registration call. We walk the chain outermost-first so the modifiers can influence the prefix / resource action set before the inner registration emits.

Recognised:

  • Verb methods: .get, .post, .put, .delete, .patch, .options, .head, plus .any (fans out to GET / POST / PUT / DELETE / PATCH).
  • .group(callback) — walks the callback with the current accumulated prefix; subsequent .prefix(...) modifiers on the group result are folded in.
  • .resource(name, controller) — emits the five REST API routes (index / store / show / update / destroy). The .apiOnly() modifier is the default behaviour here; .only([...]) restricts to the listed actions; .except drops the listed ones.

Out of scope for this first cut:

  • Per-handler request-helper scanning. AdonisJS handlers receive ({ request, response, params }) — the dominant pattern is to point at a 'Controller.method' string, which would need cross-file resolution to scan.
  • .where('id', /\d+/) constraint annotations beyond stripping the modifier.
  • Domain / subdomain routing.

Constants

ANY_VERBS = ["GET", "POST", "PUT", "DELETE", "PATCH"]
GROUP_METHOD = "group"
HTTP_VERB_METHODS = {"get" => "GET", "post" => "POST", "put" => "PUT", "delete" => "DELETE", "patch" => "PATCH", "options" => "OPTIONS", "head" => "HEAD"}
RESOURCE_ACTIONS = {"index" => {"GET", ""}, "store" => {"POST", ""}, "show" => {"GET", "/:id"}, "update" => {"PUT", "/:id"}, "destroy" => {"DELETE", "/:id"}}
ROUTER_RECEIVERS = Set {"router", "Route"}

The only identifiers AdonisJS registers routes on: Route (v5, @ioc:Adonis/Core/Route) and router (v6, @adonisjs/core/services/router). Gating the verb/group/resource dispatch on the call chain's ROOT receiver being one of these stops every other .get/.delete/... call shaped like a route from becoming a phantom endpoint — env.get('STRIPE_SECRET_KEY'), session.get('plan'), a Lucid Model.query()...delete('*'), etc.

STRING_LITERAL_TYPES = Set {"string", "template_string"}

Route paths and controller references are written as either a quoted string or a backtick template literal — tree-sitter-javascript emits template_string for the latter. decode_string has always known how to unwrap both; only the call-site gates were rejecting backticks.

TRANSPARENT_MODIFIERS = Set {"middleware", "as", "domain", "where", "namespace", "apiOnly"}

Modifiers that don't change the path or verb — we just walk the receiver with the same prefix.

Instance methods

extract_routes(source : String, include_callees : Bool = false) : Array(Route)
Source

Nested types