Making the _compiler itself_ provide an API enables things like LSP, which don't want to generate machine code at all. A traditional single-pass compiler usually can't accommodate this without re-plumbing.
The Eclipse Compiler for Java [1] is a notable exception, architected around incremental compilation, an API for “live” AST manipulation, and a layered non-batch approach to when to invoke various analysis steps.
The LSP for Java [2] used in eg. VSCode’s Java plugins, builds on this API.
But, no, I haven’t seen a generalized approach to this architecture discussed in literature.
I wrote 'multiple codegen architectures' instead of 'multiple architectures for codegen'.
As far as I have done in the toy compilers and seen the things in actual production ready compilers, the codegen is still very much tied to the one thing or the other rest llvm.
Nim has multiple backends and is relatively mature. It’s fairly readable as compilers go.
There’s also a new experimental rewrite of the Nim compiler called Nimony which targets a new intermediate called NIFC. That is intended to the be transformed to C, LLVM, JavaScript, etc.