A directory of route modules had an index file listing them, and the file was updated by hand every time somebody added one.
// eager: imported immediately
const modules = import.meta.globEager('./routes/*.js')
// lazy: each value is a function returning a promise
const lazy = import.meta.glob('./routes/*.js')
const routes = Object.entries(lazy).map(([path, load]) => ({
path: '/' + path.match(/./routes/(.*).js$/)[1],
component: load,
}))
The lazy form is what makes this worth using: each entry is a dynamic import, so Rollup code-splits per route without any manual configuration. It is a Vite feature rather than a standard, so it does not survive a move to another bundler — which is the argument against building anything structural on it. Deriving a route path from a filename with a regular expression is convenient and makes renaming a file a routing change, which is worth a comment.