Compiling external JS files with Cljsbuild in ClojureScript - leiningen

I'm trying to compile some JS libraries that we have with lein-cljsbuild to integrate them in our ClojureScript code base. First I added some goog.provide in top of each file, and the files are hierarchically organised in a directory tree according to their namespace (like in Java). That is namespace a.b.c is in src-js/libs/a/b/c.js
I have put the JS files in the root directory of the projects in src-js/libs, and I have the following :compiler options for lein-cljsbuild:
{:id "prod",
:source-paths ["src-cljs" "src-js"]
:compiler
{:pretty-print false,
:libs ["libs/"]
:output-to "resources/public/js/compiled-app.js",
:optimizations :simple}}
None of the JS files get compiled into the compiled-app file. What's wrong?
I also tried to put them in resources/closure-js/libs without success.
I'm using lein-cljsbuild 0.3.0.

First, unlike what is suggested in some texts, you do not need to include your private closure library locations in any classpath configuration statement in your project.clj. So unless the "src/js" directory included in your "source-paths:" statement is for some other purpose, you can remove it.
Second, the only thing to add to your project.clj, for the sake of bringing in your private closure code, is the "libs:" reference you have made; BUT unlike what you have entered, that reference must be to a specific *.js file (or files) and not merely a directory. So if the library you want to used is in a file named test.js and that resides in the /src/js directory, your libs: entry would be: "src/js/test.js". See the cljs-build release notes if you want to use that plugin's default :libs directory option.
Third, (and it looks like you know this already, but this is what tripped me up) if you are using a browser-backed REPL (repl-listen option of cljsbuild), you still will not be able to load/reference/use your private library assets from that REPL until you include a :require statement somewhere in the source for your compiled-app.js (e.g. "(ns testing (:require [myprivatelib]))" ), THEN you must re-compile (lein cljsbuild once) and reload your browser page with a link to compiled-app.js. This brings in that code base. Otherwise, your browser REPL will just keep insisting that the namespace provided in your closure library is not defined.
I hope this helps.

Related

How to add shadow cljs watcher on js directory in a re-frame app?

I have created a basic re-frame app using the template lein new re-frame my-proj. This particular project is interfacing with a framework (ecsy) that requires some ES6 modules and ES6 classes e.g code that is generated by the user, not simply called from cljs. Since Clojurescript does not currently generate ES6 code, I have created some wrapper ES6 modules in my project from which I plan to call into cljs code.
After much futzing, I have discovered that it's not necessary to turn these js wrapper modules into full-blown npm modules under 'node_modules', but rather I can simply put them in a sub-directory of my project e.g resources/libs and then add this directory to :js-options in shadow-cljs.edn:
{:lein true
:nrepl {:port 8777}
:builds {:app {:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "/js/compiled"
:modules {:app {:init-fn re-pure-ecs-simple.core/init
:preloads [devtools.preload]}}
:devtools {:http-root "resources/public"
:http-port 8280}
;;add this
:js-options {:js-package-dirs ["node_modules" "resources/libs"]}}}}
So everything works fine now, but the only problem is if I edit any of the js files in 'resources/public' the lein.bat dev compiler doesn't detect the changes. I can go in and make a mock change to a '.cljs' file, which does cause the compiler to re-compile, but it still doesn't pick up on the changes made to the '.js' file (or '.mjs' file). I have to kill, via ctrl-c, the compiler and re-start it to get the change propagated. Unfortunately, this takes about 15 seconds to compile since it's a full compile.
I tried adding 'resources/libs' to my 'project.clj':
:source-paths ["src/clj" "src/cljs" "resources/libs"]
to no avail.
I also tried deleting the compiled js files from <my_proj-dir>/resources/public/js/compiled/cljs-runtime:
rm 'module$node_modules$systems.js' 'module$node_modules$systems.js.map'
In this case, the compiler does re-generate the files (upon doing a mock .cljs change), but it still uses the prior version e.g. it must be using a cached version.
Is there a way I can add a watcher to this js directory so I can do incremental builds? There's obviously a watcher on the 'src/cljs' directory already. I have consulted the shadow-cljs user gd. but honestly, it's a little overwhelming.
You can follow the directions for requiring local .js in the User's Guide.
Basically you put the .js files into the same folder as your .cljs file and then just require it via (:require ["./foo.js" :as foo]). No additional config required.

Use a different React version with clojurescript react libraries (reagent,om,rum,quiescent)

How can I use a different React version with Reagent, Om, Rum, Quiescent or Brutha?
Self answer since this is asked frequently:
First you have to tell Leiningen to exclude the cljsjs/react dependencies:
[rum "0.6.0" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
If you have other dependencies pulling in cljsjs/react you can use a global exclusion:
:exclusions [[cljsjs/react] [cljsjs/react-dom]]
Next you have to satisfy the compiler since it won't find the namespaces cljsjs.react and cljsjs.react.dom. For this create two files that hold these namespaces in your source directory. For instance
- src/cljsjs/react.cljs
- src/cljsjs/react/dom.cljs
Both only need the namespace declaration and can otherwise be empty (ns cljsjs.react).
Now you can include any React version you'd like manually in your HTML file with a normal <script> tag.
Alternative:
You can also use foreign-libs compiler option.

How can I handle html files in Luminus which aren't in "resources"?

I have this:
(defn about-page []
(layout/render "about.html" {:title "About"}))
But since I have moved the directory "templates" from "resources" to the root directory and on a server I might put it yet in another place, it doesn't work. I did it because I don't want the html templates to be embedded in the output jar.
So how can I make the code work, how can I get access to my html files in "templates" then?
And the same question for static images, css, js: I put them in the root directory for now, so they aren't in "resources". They're in "public" folder. However, when I refer to them as "public/css/css1.css", they aren't getting found, that is, the path localhost:3000/public/css/css1.css doesn't exist.
How can I tell Luminus where my statics are located now?
Templates location
Selmer's documentation describes how to change the location of the templates:
By default the templates are located relative to the ClassLoader URL.
If you'd like to set a custom location for the templates, you can use
selmer.parser/set-resource-path! to do that:
(selmer.parser/set-resource-path! "/var/html/templates/")
It's also
possible to set the root template path in a location relative to the
resource path of the application:
(set-resource-path! (clojure.java.io/resource "META-INF/foo/templates"))
This allows the templates to be refrerenced
using include and extends tags without having to specify the full
path.
To reset the resource path back to the default simply pass it a nil:
(selmer.parser/set-resource-path! nil)
The application will then look
for templates at this location. This can be useful if you're deploying
the application as a jar and would like to be able to modify the HTML
without having to redeploy it.
As you want your templates to be reload when you change them you should also remember that Selmer caches them:
When rendering files Selmer will cache the compiled template. A
recompile will be triggered if the last modified timestamp of the file
changes. Note that changes in files referenced by the template will
not trigger a recompile. This means that if your template extends or
includes other templates you must touch the file that's being rendered
for changes to take effect.
Alternatively you can turn caching on and off using
(selmer.parser/cache-on!) and (selmer.parser/cache-off!) respectively.
Assets location
Handling of static resources is configured using site-defaults in your <app>.middleware namespace. You need to configure its' :static entry to use :files instead:
(-> site-defaults
(assoc :static {:files "/var/www/html"}))
and you need to copy files from resources/public directory to that location.

atom packages, clojurescript, google closure and dependency management

I'm writing some atom (the editor) package with ClojureScript. And i faced dependency load issue.
When compiled ClojureScript produces file like this (main.js):
goog.addDependency("base.js", ['goog'], []);
goog.addDependency("../cljs/core.js", ['cljs.core'], ...)
goog.addDependency("../clojure/browser/event.js", ...)
Obviously, ClojureScript heavily depends on Google Closure dependency management.
But, to be able to use Google Closure i need to include goog/base.js file.
The only way that i found is to add to goog/base.js:
module.exports = goog
and add to main.js:
require('./goog/base.js')
This is very bad approach, because these files are generated - so they can be overridden.
Also, release compilation will not include these lines.
The question is how can i use both these dependency systems?
Or is it possible to use ClojureScript w/o Google Closure?
Please advice, thanks!
If you set your ClojureScript :optimizations to something other than :none (e.g., :whitespace) then the resulting .js file will include the Google Closure code inlined and you won't have to reference it separately.
(Note that this means you might not be able to use a main function in your ClojureScript code, but you can just put a call to your main function somewhere at the toplevel.)

Variable file include path in Flash Pro when build an android app using Air

As far as I know, the only way to include a folder in an Air package (in Flash Pro, not Builder) is through the GUI:
Publish Settings > Player Settings > General Tab > Included Files
... but it's kind of a huge hassle to manually add and remove folders over and over again if you have to publish the app with a dozen different versions of the content, you know?
Is there a way to conditionally include folders based on text somewhere - the app config xml, maybe? or something else that will be checked when the APK is compiled? That way I could just copy and paste in the folder name to switch which asset folder is being included, instead of going through the whole process inside of the flash IDE?
This idea might possibly be of some use although it could require some management due to possibly meaning you have duplicated assets.
I typically store all my assets inside an 'includes' folder/directory and just add this to the Settings > Include Files pane.
I also have folders which store the target specific includes, such as 'IOS' and 'ANDROID', (but aren't added to the Include Files pane).
When publishing, I swap out the contents of the includes folder. So it's just a case of deleting the existing contents if the includes folder and copy/pasting from the relevant source folder into the includes folder.
Hope that makes sense.
There are a couple of things you could do, though they're not 100% solutions per se.
1) Use conditional compilation:
Depending on how your imports/includes are set up, you could change it to always be the same file, but change the code/embed inside depending on a compile time constant. You can add a compile constant by going to File > Publish Paramaters > Flash (tab) > Parameters > Configure Constants:
Your constants would be in the form CONFIG::debug or FOO::bar, then in code, you wrap your code like this:
CONFIG::debug
{
// code is only included if CONFIG::debug is true at compile time
}
Then, by flipping constants, you can include or exclude blocks of code. There's a bit more detail here: http://divillysausages.com/blog/as3_conditional_compilation
NOTE: this is much easier to do in an IDE like FlashDevelop.
2) Use an external IDE
Taking the FlashDevelop example, when building for mobile, a number of .bat files are created. Depending on the environment vars set, you can include/exclude folders as you wish. I do this to include different assets depending on if it's a desktop, Android, or iOS build.
3) Use the Flex SDK command line or ANT
With either of these, you can specify a config.xml file for the files that you want to include. External IDEs (like FD) use the command line directly, so you can almost copy/paste the command if necessary.
For ANT, it's not my strongest point, but you can find out a bit more here: http://charlespatricknewman.com/blog/?p=325