Skip to content
Request for comments on ClojureScript compiler interface updates
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


# Request for comments: a new CLJS compiler interface

ClojureScript is super rad and deserves rad third-party tooling.
This document outlines potential improvements in the ClojureScript compiler's public interface and the use cases that motivate them.
If you can think of something that one *should* be able to do with ClojureScript but cannot with the current implementation, please open up and issue or pull request with a description of your use case and what a public API would need to provide to support it.
Please attach questions directly to the corresponding line of this document at a specific commit.

Read Fogus's blog posts on this subject:

## Things I'd like to be able to do

+ Compile multi-namespace, custom-macro-using ClojureScript programs without touching the file system
+ Retrieve per-namespace errors/warnings as data (e.g., maps containing warning types, line numbers, and so on)
+ Retrieve per-namespace dependency information: "This namespace provides vars A, B, C and requires vars X, Y, Z from namespaces U, V, W"
+ Integrate ClojureScript with my own caching/memoization scheme, without having to worry about ClojureScript caching internally (if ClojureScript caches internally, then its invalidation scheme must be bulletproof)
+ Compile ClojureScript and JavaScript sources that are not on the classpath

## Would be nice to have, but may be too difficult

+ Append new sources to already compiled/optimized code; the newly emitted JavaScript should reference the previously compiled code rather than duplicating/overwriting existing dependencies.
+ Get source mappings between ClojureScript <-> Raw JS <-> Optimized JS

## The current implementation

Tooling is difficult to build on top of the ClojureScript compiler's current interface: `cljs.closure/build`.
This interface tangles together inputs (accepting files, directories, strings, URLs, lists, or vectors) and outputs (writing to disk or returning a string of JavaScript).
Furthermore, there is no interface for retrieving as data:

+ namespace dependency information
+ file AST (pre or post macro-transformed)
+ errors/warnings (about, e.g., references to undefined vars)

even though the compiler calculates all these things internally during the compilation process.
Finally, the current compiler architecture is tightly coupled together with mutable state and the disk: between the `cljs.closure`, `cljs.analyzer`, and `cljs.compiler` namespaces, there are:

+ 49 references to ``
+ 14 atoms
+ 15 dynamic vars

## A potential new interface

(Note: I'm not 100% convinced all of this is a good idea, but I'm writing it imperatively for clarity.)

All public API functions should live in a new namespace, `cljs.api`.

The interface should be split into two parts: "easy" and "simple".

The "easy" interface should be a single function of `[coll-of-cljs-strings, optsmap]` that returns a string of JavaScript.
It does not traverse the filesystem, nor does it write output to disk.
Any Clojure macros required by the ClojureScript source are assumed to already be defined in the appropriate JVM Clojure namespace.

The "simple" interface is Ring-style middleware on the scale of namespaces.
See `middleware.clj` for an example.
The user provides the source string for each namespace, which is packaged up as a "compilation map" (analogous to Ring's request/response maps).
The compilation map is run through multiple middlewares to read forms, analyze forms, emit JavaScript, and so on.

Once the user has all of his or her compilation maps in hand (including compilation maps from all libraries), they're sorted in dependency order, given to Google Closure to optimize, and the final JavaScript is returned as a string that the user can then write to disk.

See a potential implementation in `com.keminglabs.cljs-proposal.middleware`.
Something went wrong with that request. Please try again.