magnars / optimus

A Ring middleware for frontend performance optimization.
364 stars 23 forks source link

ClojureScript module #19

Open devth opened 10 years ago

devth commented 10 years ago

Would it make sense to have an optimus-clojurescript module or is there too much overlap with lein-cljsbuild?

radhikalism commented 10 years ago

An interesting question; I've been considering this with regard to CoffeeScript lately. In short, I think it might be a good idea to have modules for language transpilers. Longer, half-baked answer...

Firstly, it depends on whether you consider transpilation an optimization or not. If you're convinced that turning cljs/coffee/whatever into JS is a kind of optimization, then you already have a case: as the slogan goes, "optimize in production". You could argue that the ability to ship non-JS sources as-is can be a developer efficiency. Or you could argue that once you ship, say cljs code, it performs better when converted to JS for the browser (as opposed to not performing at all!).

However, I actually think Optimus solves a more general category of problems, of which optimization is one member. Optimus serves as a "representational transformer of web assets", as it offers opportunities for optimization, bundling, code generation, image manipulation, etc. All of these transformations turn some source asset data into an intermediate form, such that if the full set of source assets were transferred to a (hypothetically) capable & configured browser (e.g. one having a native cljs runtime), it would behave the same as if the full set of the transformed representations were sent to a normal browser. The benefits being potential convenience, control and performance improvements for real systems, if set up appropriately.

I think this justifies modules like optimus-less and optimus-jsx better than the first perspective.

With this approach, a key point is to use the late-binding nature of executing transformations in production -- i.e. at boot-time, before taking requests but after deployment. This allows the asset pipeline to respond to its particular deployment environment, such as to compute features, bundles, partial templates etc. on a site-, host-, or even time-specific basis, and still produce static, cacheable data.

The downside, as with all services with moving parts is perhaps increased risk regarding reproducibility.

If Optimus' exporting file generation is used instead, then there is less flexibility, and something like a cljs module would indeed overlap with cljsbuild. But I don't think there is any harm in that; better to have the option of using the late-bound module too.

I could be mistaken, of course. See what @magnars thinks.

magnars commented 10 years ago

I think that @arbscht summed up my thoughts way more eloquently than I could have.

Let me add a few points tho:

I see some advantages and disadvantages with serving your ClojureScript through Optimus:

Any thoughts on this?

radhikalism commented 10 years ago

I agree with the points @magnars makes. Some more thoughts on dealing with the disadvantages...

Perhaps the Closure compiler (or whatever transpiler) could be run along a fast path in development testing (i.e. configured to do minimal work, but produce a less-optimized payload), as with optimizations/none? I don't know if it would necessarily be prohibitively slow.

Otherwise, I wonder how realistically risky or difficult it would be to use cljsbuild instead of an Optimus module for development testing? In theory, they should produce equivalent output, and when unit testing, the SUT should be the application code and not the tooling anyway. (The JS engine, optimization strategy, etc, may already vary in testing vs production.)

A hybrid technique might be to generate pre-built files (using cljsbuild or export-assets), making them available for use as a cold-boot default, but replace it with an optimized payload when that eventually becomes ready at runtime. (Similar to JIT-like runtime optimizations elsewhere.)

If some approach not involving cljsbuild turns out to be perform acceptably, I expect one could easily adapt clojurescript.test to load sources over HTTP instead of the filesystem.

And yes, asset loader caching could also be nice to have. I think using a persist-able, shareable cache implementation with smart invalidation could be useful for other use cases too.

To me, this looks like it might just require experimentation to figure out feasibility.