node.jsclojurescriptboot-cljcljsbuild

How to define target env in compile time while building .cljs?


I want to compile my .cljs file for both browser and node.js environments, to get server side rendering. As I understand, there's no way to define cljs env in compile time with reader macro conditions like:

#?(:clj ...)
#?(:cljs ...)

so, I can't easily tell compiler to process something like #?(:cljs-node ...) in node.js env.

Second option I see here is to develop a macro file which will define env at compile time. But how to define that current build is targeting node.js? May be, I could pass some params somehow to compiler or get :target compiler param?

Here are my boot files:

application.cljs.edn:

{:require  [filemporium.client.core]
 :init-fns [filemporium.client.core/init]} 

application.node.cljs.edn:

{:require [filemporium.ssr.core]
 :init-fns [filemporium.ssr.core/-main]
 :compiler-options
 {:preamble ["include.js"]
  :target :nodejs
  :optimizations :simple}}

Solution

  • I am not aware of a public API to achive this. However, you might use cljs.env/*compiler* dynamic var in your macro to check the target platform (i.e. NodeJS vs browser) configured with :target in your :compiler-options and either emit or suppress the code wrapped in the macro:

    (defn- nodejs-target?
      []
      (= :nodejs (get-in @cljs.env/*compiler* [:options :target])))
    
    (defmacro code-for-nodejs
      [& body]
      (when (nodejs-target?)
        `(do ~@body)))
    
    (defmacro code-for-browser
      [& body]
      (when-not (nodejs-target?)
        `(do ~@body)))
    
    (code-for-nodejs
      (def my-variable "Compiled for nodejs")
      (println "Hello from nodejs"))
    
    (code-for-browser
      (def my-variable "Compiled for browser")
      (println "Hello from browser"))