embroider-build / embroider

Compiling Ember apps into spec-compliant, modern Javascript.
MIT License
339 stars 137 forks source link

will it be possible to generate a build that doesn't step on the require()/define() globals? #358

Open tomwayson opened 4 years ago

tomwayson commented 4 years ago

So glad to see Ember finally trying to modernize it's outdated build system, which to me has felt like it's Achilles's heel. I watched Ed's EmberATX talk and followed along w/ my own build of the super-rentals app, and I really like where this is headed so far.

However, I was hoping that ember's loader.js would not be used at all (i.e. only use webpack's built in loader), but I see that vendor.js still has it, and see that see that the stage 3 output references window.define(). The problem is that even though ember's loader.js has a noConflict mode, Ember does not expose any way to enable that, which makes it hard to integrate libraries that (for reasons, like using a hosted Dojo 1 build, don't ask) need to be able to define that global. This has been a long standing problem for (I'll admit) a small group in the ember community. There are multiple addons dedicated to dealing w/ this problem (that I'm aware of):

https://github.com/richardaiko/ember-derequire https://github.com/Esri/ember-cli-amd https://github.com/Esri/ember-esri-loader (which I maintain)

All of which do some AST parsing or regEx to parse the build output and replace define and require with some alternate (like enifed and eriuqer), which is a really fragile hack that has required lot's of tweaking for edge cases over the years.

Is there any opportunity with embroider to fix this issue at the framework level so we can ultimately get rid of these addons?

ef4 commented 4 years ago

For Embroider this is mostly about compatibility. A lot of code in the ecosystem expects loader.js to be present. But you're right that Embroider removes most of the reasons loader.js was needed in the first place. The hard part is just getting everything else off that pattern. Including Ember's own resolver.

It would be possible to do an Embroider mode with no loader.js today. It involves turning off code that right now protects runtime define and require from being interpreted by webpack. But to make each require actually resolvable at build time, we'd need some fairly expensive analysis of all the vendor scripts to try to find the corresponding define. And we'd probably need to patch Ember's resolver to do a more legible thing when it's requiring modules (which is probably impossible under a classic build, but actually possible under Embroider given that we have a stage where we can statically see all the modules that have ended up in the app's namespace).

So overall, this would be an interesting capability and I'm not opposed if somebody else wants to work on it, but it's not high on my priority list. Keeping loader.js around helps more than it hurts right now in most cases, because eliminating it goes against the prevailing way things work in the ecosystem. To change that, we need to shepherd everybody away from directly accessing AMD.

It would be nice if somebody wants to take on the task of writing a thorough deprecation RFC for warning and then removing define and require. The challenge though is that it needs to explain what people should do instead in all the common cases. We have compelling answers for a lot of them (often it's "use ember-auto-import instead of manually shimming things). More dynamic cases are harder but I think I've covered most of them with the Embroider macro system (which works in both Embroider and classic ember-cli builds).