feat: add getRoutes method to list registered routes by bjohansebas · Pull Request #174 · pillarjs/router

@jeffsee55 @vercel

This adds support for an experimental builder that does app
introspection on the Express instance. It can used with an env var
`VERCEL_EXPERIMENTAL_EXPRESS_BUILD=1`. It provides route-level o11y so
when you're looking at your logs you can see the routes served

<img width="261" height="306" alt="Screenshot 2025-09-19 at 3 32 30 PM"
src="https://github.com/user-attachments/assets/47d01c66-6bf4-4229-8339-fd75625c8a02"
/>

Key differences:
- No longer calls into the `@vercel/node` builder. Uses `rolldown`
instead.
  	- It does NOT check types at this time.
- It still doesn't bundle, it just transforms the files in-place. And
doesn't process `node_modules` aside from using NFT for them, as we do
in the node builder.
- Using rolldown mostly because what we need to do here has diverged
from the `@vercel/node` logic sufficiently and starting from scratch is
a bit easier for now. And it had some wins over `esbuild` for being able
to avoid bundling.
- It has the added benefit of working with tsconfig paths, missing
extensions on relative imports, and code that mixes esm/cjs syntax.
- NFT is run on the compiled result instead of source, it wasn't
resolving tsconfig path imports like `@/my-lib` or imports with missing
file extensions, but running it against the compiled code seems to work
nicely.
- Writes result to the filesystem, whereas with the node builder
everything is in memory until `writeBuildResult`.
- Invokes the compiled code with a monkey-patched version of express
that emits app metadata like routes, static folders and view folders.
- Uses the build output version 2, there's some messy code in the build
command to handle this because the `@vercel/express` package currently
uses version 3
- Does not support `vc dev` at the moment. Use `srvx`!

### Explanation
The gist of the POC is that we need to extract info from the express
instance, so we compile the source code, monkey-patch express with a
wrapper function that exports the app's metadata, invoke the code with a
child process and then use the metadata add routes to the build output
and then clean up the monkey-patch code.

NOTE: the introspection logic is basic, it doesn't process sub-routes.
There's some work to the [Express
router](pillarjs/router#174) that will make this
easier for v5, and for v4 the logic will look different but AFAIK it's
easier to do there.

### Before we can really use this as the main path forward
We need to validate this approach, it's kind of a hack but does give us
some nice improvements. The change to using rolldown is arguably not
necessary, we might want to continue to use the node builder logic to
reduce friction.

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>