Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClojureScript module #19

Open
devth opened this issue Apr 17, 2014 · 3 comments
Open

ClojureScript module #19

devth opened this issue Apr 17, 2014 · 3 comments

Comments

@devth
Copy link

devth commented Apr 17, 2014

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

@radhikalism
Copy link
Collaborator

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
Copy link
Owner

magnars commented Apr 17, 2014

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

Let me add a few points tho:

  • The core of Optimus is an engine that makes asset first-class, providing features for hooking in custom asset loaders and transforming those assets. It also comes with transformations and loaders that help out with Web Performance. I would not call transpiling of other languages an optimisation in this context. So a clojurescript asset loader should be a separate module.

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

  • 👍 The ClojureScript feels first-class. There's no separate process needed to transform the code. You don't have to explain to new contributors how to set up autocompiling of your cljs-files. While developing it feels like you're just writing ClojureScript and serving it to the browser.
  • 👎 Since ClojureScript uses the Google Closure Compiler, it can take a few seconds to compile. lein-cljsbuild starts compiling the instant you save your file. Optimus would only start compiling once a request comes in. This would make your pages painfully slow in development. The asset loader would certainly have to do some tricks with caching to speed this up.
  • 👎 The compiled assets from Optimus are not available anywhere other than from the server process. I'm not entirely sure how testing works in ClojureScript, but I would think they need to be compiled to JavaScript before tests are run. Since you can't point your test framework to a directory of compiled cljs, you might end up with doing double work to get tests running?

Any thoughts on this?

@radhikalism
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants