I’m working on a feature for Mirage, that will make some Mirage code available to Node so that it can be used in an Express middleware.
I’d like some advice on how best to write the Broccoli code.
At a high level, here’s my current thinking on what needs to happen:
Read code from the host app’s /mirage directory, turn it from ES6 into CJS
Read code from Mirage’s source, turn it into CJS
require() code from those places in the serverMiddleware() hook, instantiate a new Mirage server with config from the host app, and go from there
I’m trying to start out with just #2 – ignoring the host app for now, and just getting the Mirage server code written to CJS in a place where I can require() it from the serverMiddleware() hook.
What’s the right way to think about this?
In my experimenting so far, I followed some other addons and used Rollup via broccoli-rollup to read in ember-cli-mirage/addon/server.js (which is sort of the entry point for Mirage), and wrote it to CJS.
Where I’m getting confused is, where I can/should write this CJS file so that I can reliably “get” to it in serverMiddleware().
In general, since serverMiddleware doesn’t get a tree I feel confused about how that hook can interact with files created at build-time. I also sometimes reach for something like a .then method on my broccoli trees, but don’t know if that’s possible or a recommended pattern. (In other words, once I write my CJS file, if I could .then() and require() it, I could throw some instance data onto this and access it like that from serverMiddleware. Feels hacky, but it could get me unstuck.)
There is an old PR to Mirage that does something similar, and uses broccoli-builder to make a new Builder. Then it uses builder.build().then() to add some routes to Express.
Is that a good approach? I was sort of under the impression that index.js should be mainly concerned with trees and Broccoli plugins, and that making a new Builder was more low-level and wouldn’t fit nicely into the Ember CLI pipeline.
I think the proper place for the files would be the tmp directory if you want them cleaned up when the server is turned off. You can use broccoli-persistent-filter or broccoli-caching-writer if you want them cached between builds as well.
If you want them not cleaned up and visible to the user, then I don’t think there are any conventions. Maybe a dist/ or generated/ directory inside mirage/ so users can figure out that it can be deleted and get recreated?
Interesting, I wasn’t aware of this. Thanks for the pointer!
I’m playing around with it and it seems to be working with most of my imports. But I’m having trouble when importing from my addon’s top-level:
// relative path works
import Db from './db';
// absolute path starting with 'ember-cli-mirage' doesn't work
import { toCollectionName, toInternalCollectionName } from 'ember-cli-mirage/utils/normalize-name';
Any insight on why this might be happening? Is there some more magic happening by the time these import statements are run, that adds ember-cli-mirage to a lookup path or something?
For an ember app in the browser it’s not non-standard, lots of things work that way. But yeah, it’s one of the semantic differences you encounter as soon as you try resolving your stuff in both node and the browser.
It surprised me the first time I noticed because it seems like node modules should be able to resolve themselves, but alas they cannot.
Gotcha, thanks. Feels good learning about this stuff, peeling back the curtain. Wonder if there’s some comprehensive material I could read/watch, or if I should just keep poking around?
Fwiw the actual changes needed in Mirage weren’t bad, the absolute path is most useful under /tests and I was able to keep those there (since I don’t need those atm in Node).