class
Noir::JSRouteExtractor
- Noir::JSRouteExtractor
- Reference
- Object
Overview
JSRouteExtractor provides a unified interface for extracting routes from JavaScript files
Defined in:
miniparsers/js_route_extractor.crConstant Summary
-
BRACKET_ROUTE_CALL_PATTERN =
/\[\s*['"](?:get|post|put|delete|del|patch|options|head|all)['"]\s*\]\s*\(/i -
FLEXIBLE_ROUTE_CALL_PATTERN =
/\.(?:\s|\n|\r)*(?:get|post|put|delete|del|patch|options|head|all|route|register|use)(?:\s|\n|\r)*\(/i -
HTTP_SERVER_LIBRARY_MARKERS =
["from \"express\"", "from 'express'", "require(\"express\")", "require('express')", "from \"fastify\"", "from 'fastify'", "require(\"fastify\")", "require('fastify')", "from \"koa\"", "from 'koa'", "require(\"koa\")", "require('koa')", "from \"hono\"", "from 'hono'", "require(\"hono\")", "require('hono')", "from \"restify\"", "from 'restify'", "require(\"restify\")", "require('restify')", "from \"polka\"", "from 'polka'", "from \"h3\"", "from 'h3'", "from \"@nestjs/", "from '@nestjs/"] -
Real HTTP-server library imports. When any of these is present alongside a test-stub marker, the file is doing legitimate server work (e.g., spinning up a test instance of an Express app) and we still want to extract its routes.
-
PARSER_ROUTE_CALL_HINTS =
[".get(", ".post(", ".put(", ".delete(", ".patch(", ".options(", ".head(", ".all(", ".route(", ".register(", ".use(", ".get (", ".post (", ".put (", ".delete (", ".patch (", ".options (", ".head (", ".all (", ".route (", ".register (", ".use ("] -
Pre-filter for
.extract_routes: returns false whencontentcontains no shape the JS parser knows how to emit (any verb invocation pattern like.get(/.post(/... or Fastify/Restify.route(, plus Express-style mounts.use(which feed into the cross-file router prefix table). Substring-checking is millions of times cheaper than tokenizing the file. -
ROUTER_PREFIX_KEY =
Analyzer::Javascript::ExpressConstants::ROUTER_PREFIX_KEY -
Import constants for key generation
-
STRICT_TEST_PATH_MARKERS =
["/e2e/", "/cypress/", "/playwright/", "/__mocks__/", "/__tests__/", "/e2e-tests/", "/mirage/"] -
True when the file's route-shaped calls are almost certainly mock-server stubs (Ember pretender, MSW, nock, ...) rather than real route registrations. Two routes:
Path markers strict enough that the HTTP-server-import exemption shouldn't apply:
/e2e/,/cypress/,/playwright/,/__mocks__/,/__tests__/,/e2e-tests/,/mirage/. Real apps never park production handlers under any of these — even when the harness file imports express to spin up a faked service (Ghost'se2e/helpers/services/stripe/fake-stripe-server.tsis the canonical example). Keeping the exemption out of these paths catches the harness fakes without affecting legit backend code. -
TEST_STUB_FILENAME_MARKERS =
[".test.", ".spec.", "-spec.", "-test.", ".test-d."] -
Hard test-file markers: when the filename itself follows a ubiquitous test convention, the file practically never defines real routes. Skip these even when the file imports a real HTTP server lib — NestJS e2e tests routinely import
@nestjs/platform-expressfor type-only references, and supertest harnesses import the same modules they exercise. The supertestrequest(app).get(...)shape would otherwise ride the HTTP-server-import exemption straight back into the parser. -
TEST_STUB_LIBRARY_MARKERS =
["pretender", "miragejs", "ember-cli-mirage", "from \"msw\"", "from 'msw'", "from \"msw/", "from 'msw/", "require(\"msw\")", "require('msw')", "from \"nock\"", "from 'nock'", "require(\"nock\")", "require('nock')", "setupApplicationTest", "setupRenderingTest", "/// <reference types=\"cypress\" />", "from \"cypress\"", "from 'cypress'", "require(\"cypress\")", "require('cypress')", "from \"@playwright/test\"", "from '@playwright/test'", "from \"playwright\"", "from 'playwright'", "from \"supertest\"", "from 'supertest'", "require(\"supertest\")", "require('supertest')", "from \"axios\"", "from 'axios'", "require(\"axios\")", "require('axios')", "from \"purest\"", "from 'purest'", "require(\"purest\")", "require('purest')", "from \"got\"", "from 'got'", "require(\"got\")", "require('got')", "from \"ky\"", "from 'ky'", "require(\"ky\")", "require('ky')", "from \"superagent\"", "from 'superagent'", "require(\"superagent\")", "require('superagent')", "from \"node-fetch\"", "from 'node-fetch'", "require(\"node-fetch\")", "require('node-fetch')", "from \"ofetch\"", "from 'ofetch'", "require(\"ofetch\")", "require('ofetch')", "from \"undici\"", "from 'undici'", "require(\"undici\")", "require('undici')", "from \"request\"", "from 'request'", "require(\"request\")", "require('request')"] -
Test-fixture libraries whose API mimics route registration:
pretender/miragejsexposeserver.get("/x", ...), MSW and nock expose handler builders, sinon-via-faker likewise. When these libraries are imported, virtually every route-shaped call in the file is a stub, not a real registration. Substring match is enough — these tokens never appear in production HTTP server source under normal circumstances. -
TEST_STUB_PATH_MARKERS =
["-pretender.", "-pretenders.", ".pretender.", "-mirage.", ".mirage.", "/tests/helpers/", "/test/helpers/", "/tests/api/", "/__tests__/", "/test/integration/", "/tests/integration/", "/test/e2e/", "/tests/e2e/", "/cypress/", "/playwright/", "/e2e-tests/", "/e2e/", "/mirage/", "/__mocks__/", "/dist/", "/build/", "/.next/", "/.nuxt/", "/.output/", "/coverage/", "/vendor/", "/app/javascript/"] -
Path-level evidence that a file is a mock-server fixture. Pretender helpers in particular get a
helper/thisarg and callthis.get(...)/this.post(...)directly, so they have no library-name imports the content filter can hook on — fall back to the convention-based filename match.
Class Method Summary
- .attach_callees(endpoint : Endpoint, callees_by_route : Hash(String, Array(JSCalleeExtractor::Entry)), method : String, path : String, line : Int32)
- .extract_body_params(handler_body : String, endpoint : Endpoint)
- .extract_cookie_params(handler_body : String, endpoint : Endpoint)
- .extract_header_params(handler_body : String, endpoint : Endpoint)
- .extract_params_from_context(content : String, pattern : JSRoutePattern, endpoint : Endpoint)
- .extract_path_params(handler_body : String, endpoint : Endpoint)
- .extract_query_params(handler_body : String, endpoint : Endpoint)
- .extract_routes(file_path : String, content : String | Nil = nil, debug : Bool = false, *, include_callees : Bool = false, route_callees : Hash(String, Array(JSCalleeExtractor::Entry)) | Nil = nil) : Array(Endpoint)
-
.extract_static_paths(content : String) : Array(Hash(String, String))
Extract static path declarations from JavaScript content Returns array of hashes with static_path (URL prefix) and file_path (directory)
-
.find_matching_brace(content : String, open_brace_idx : Int32) : Int32 | Nil
Delegate to JSLiteralScanner for literal-aware brace matching
-
.find_matching_paren(content : String, open_paren_idx : Int32) : Int32 | Nil
Delegate to JSLiteralScanner for literal-aware paren matching
-
.normalize_http_method(method : String) : String
Normalize HTTP method names to standard format
- .route_call_candidate?(content : String) : Bool
-
.strip_js_comments(content : String) : String
Replace JS/TS comments with whitespace of the same shape.
-
.test_stub_only?(file_path : String, content : String) : Bool
- Filename markers fire unconditionally —
foo.test.tsis a test no matter what it imports.
- Filename markers fire unconditionally —
Class Method Detail
Extract static path declarations from JavaScript content Returns array of hashes with static_path (URL prefix) and file_path (directory)
Delegate to JSLiteralScanner for literal-aware brace matching
Delegate to JSLiteralScanner for literal-aware paren matching
Normalize HTTP method names to standard format
Replace JS/TS comments with whitespace of the same shape.
Preserves newlines and column offsets so downstream line/column
math (controller_start_line, regex .begin(0), etc.) stays
accurate. Comment bodies are blanked to spaces so a commented-
out decorator like // @Get('/old') never matches the route
regex.
- Filename markers fire unconditionally —
foo.test.tsis a test no matter what it imports.- Strict path markers also fire unconditionally —
e2e/,cypress/, etc. are dedicated test/mock trees that never contain production handlers, even when the harness file imports a server lib. - Library + the remaining directory markers honor an
exemption — if the file also imports a real HTTP server
lib (express, fastify, ...), keep it so legit test-server
harnesses (e.g. mattermost's
webhook_serve.js) keep their routes.
- Strict path markers also fire unconditionally —