ClojureScript compiler can't find namespace/file in same directory - clojurescript

lein cljsbuild is having trouble finding a namespace/file that's defined right alongside another namespace/file unless I make sure they're compiled in a certain order.
My directory layout looks like this:
project/
project.clj
src/
cljs/
contempo/
common/
defs.cljs
view/
core.cljs
navigation.cljs
navigation.cljs has some stuff to build Om components for navigating around the page, and core.cljs is the main entry point for this particular page. navigation.cljs starts with:
(ns contempo.view.navigation (:require ...))
core.cljs starts with:
(ns contempo.view.core (:require [contempo.view.navigation :as navigation]))
When I run lein cljsbuild, I get:
solace:Groov jfischer$ lein cljsbuild auto
Compiling ClojureScript.
Compiling "war/view/js/app.js" from ["src/cljs/contempo/common" "src/cljs/contempo/view"]...
Compiling "war/view/js/app.js" failed.
clojure.lang.ExceptionInfo: failed compiling file:src/cljs/contempo/view/core.cljs
... snipped stacktrace ...
Caused by: clojure.lang.ExceptionInfo: No such namespace: contempo.view.navigation at line 1 src/cljs/contempo/view/core.cljs
I can get it to work by removing all references to contempo.view.navigation from core.cljs, running lein cljsbuild auto and letting the compile finish, then putting them back and saving (so cljsbuild picks up the changes), but that's silly and shouldn't be necessary.
My project.clj looks like:
(defproject contempo "0.0.0-SNAPSHOT"
:description "Experimenting with ClojureScript and Om"
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/clojurescript "0.0-2740"]
[org.clojure/core.async "0.1.346.0-17112a-alpha"]
[org.omcljs/om "0.8.7"]]
:plugins [[lein-cljsbuild "1.0.4"]]
:clean-targets ^{:protect false} ["war/view/js/app.js"
"war/view/js/out"]
:cljsbuild {:builds [{:id "view-dev"
:source-paths ["src/cljs/contempo/common"
"src/cljs/contempo/view"]
:compiler {:output-to "war/view/js/app.js"
:output-dir "war/view/js/out"
:optimizations :none
:cache-analysis true
:source-map true}}]})
Is there anything obvious I'm doing wrong? This looks pretty similar to every ClojureScript project I've looked at.
Update: Tiny skeleton project that shows the error is up here: namespace-error-demo.zip

The issue ended up being: I wasn't obeying the rules for how namespaces are resolved.
With source paths of src/cljs/contempo/common and src/cljs/contempo/view, if I required the contempo.view.whatever namespace, the compiler would look for it at src/cljs/contempo/common/contempo/view/whatever.cljs and src/cljs/contempo/view/contempo/view/whatever.cljs.
I had to use src/cljs as the source directory. What I wanted to pull off (leaving out code for a given page that didn't need it) was kind of silly (since it'd be pulling in all of ClojureScript anyway) and is now properly addressed thanks to proper Google Closure Module support in ClojureScript.

I was having this same problem all of today. In my case, the root cause was having .cljs files with "-" in the name. I only discovered this was the problem after switching to 0.0-3030, which provides better error messages for the strict file path to namespace conventions that more recent versions of the cljs compiler have.
You might want to try changing :source-paths to ["src/cljs"]

Related

How to author agnostic JavaScript library in ClojureScript?

Let's say I have a cljs file containing the following:
(ns foo)
(defn add [x y]
(+ x y))
and wish to make this available as a JavaScript library to non-ClojureScript devs (primarily focused on node.js). I can do this:
clj -m cljs.main -c foo
But the problem is that the output is geared towards google closure's module system (e.g. goog.require). I can set the target to none with the -t flag (as opposed to browser or node), and that... doesn't fix this. Setting it to node also doesn't fix the issue: no index.js (it's called main like in Java), no module.exports = blah blah. Seems like it's geared towards standalone, full node apps, rather than libraries.
I understand that ClojureScript uses google closure for it's own sub-modules, and I'm not necessarily looking to get rid of all of that (I'm not sure you could). And I get that es2015 native JavaScript modules are out because of their static nature.
I could massage the output by hand or by script to play nice with the npm ecosystem, but I'm surprised that there's no compiler option that can actually output a npm-friendly module. Or is there? Am I just reading --help wrong?
This assumes that you already have a working installation of ClojureScript and Node.js
Given:
math101
|-- package.json
|-- src
| `-- com
| `-- example
| `-- math.cljs
package.json
{
"name": "math101",
"version": "1.0.0",
"main": "dist/index.js",
"license": "MIT",
"devDependencies": {
"shadow-cljs": "^2.8.52",
"source-map-support": "^0.5.13"
}
}
Notes:
dist/index.js - This will contain our ClojureScript code converted to JavaScript
shadow-cljs - The build (and dependency management) tool that we need
source-map-support - Required to run ClojureScript on Node.js
📣Please make sure you have installed these two NPM dependencies before you proceed further.
math.cljs
(ns com.example.math)
(defn add [x y]
(+ x y))
1. Setup the build tool
At the root of math101 run yarn shadow-cljs init.
This will create a file called shadow-cljs.edn with some default settings:
;; shadow-cljs configuration
{:source-paths
["src/dev"
"src/main"
"src/test"]
:dependencies
[]
:builds
{}}
Let's make some changes.
First you don't need that many source paths for this:
{:source-paths
["src"]
:dependencies
[]
:builds
{}}
Then let's add a build configuration:
;; shadow-cljs configuration
{:source-paths
["src"]
:dependencies
[]
:builds
{:math101 {:target :node-library
:output-to "dist/index.js"
:exports-var com.example.math/add}}}
Notes:
:math101 - This the build id that we will use later
:target :node-library - This tells shadow-cljs that you intend to author a library
:output-to "dist/index.js" - The code you intend to publish
:exports-var com.example.math/add - "Fully qualified name" of the function you intend to publish. This will produce a default export.
Additional notes:
The :target :node-library emits code that can be used (via require) as a standard node library, and is useful for publishing your code for re-use as a compiled Javascript artifact.
Source
There is a :npm-module target available but so far :node-library has checked all the boxes for me.
2. Let's build this!
Run yarn shadow-cljs compile math101.
The first time you run this, shadow-cljs will download a bunch of stuff. Eventually it will finish and when it does...
$ node
> var add = require('./dist')
> add(40, 2)
42
✨✨✨
Need to export more than just one function? No problemo.
Let's add subtract:
(ns com.example.math)
(defn add [x y]
(+ x y))
(defn subtract [x y]
(- x y))
And now let's update our build config:
;; shadow-cljs configuration
{:source-paths
["src"]
:dependencies
[]
:builds
{:math101 {:target :node-library
:output-to "dist/index.js"
:exports {:add com.example.math/add
:subtract com.example.math/subtract}}}}
Run yarn shadow-cljs compile math101 again and when it finishes:
$ node
> var math101 = require('./dist')
> math101.add(40, 2)
42
> math101.subtract(44, 2)
42
✨✨✨✨✨✨
ADDENDUM
This is only intended to get you started. shadow-cljs compile generates non-production code (i.e. it's not minified, dead code is not removed AFAIK and Google Closure hasn't run any optimisation yet).
The command for generating production-ready code is shadow-cljs release but you may want to tweak your build config before.
I would highly recommend that you invest time reading up the shadow-cljs documentation. It is an amazing tool.
shadow-cljs supports output in a CommonJS format via :target :npm-module which does support exactly what you are asking for. Node and other JS tools (eg. webpack) can consume separate namespaces independently. The default CLJS tools do not support this mode.
ClojureScript however is very much written with the assumption that your whole program will be optimized by the Closure Compiler. This makes it less than ideal for writing libraries to be included in other builds. Each "library" built this way will contain its own version of cljs.core and therefore will be pretty large to begin and including 2 libraries built this way is a recipe for disaster since they won't be compatible with each other.

Sharing code between separate scripts (outputs)

I have a project which is not intended to be a single page application, and I'm trying to figure out the best way to share code for common functionality between different script outputs.
For example I may have a stores.js target, and a items.js target built from cljs/stores and cljs/items respectively, but both have some overlapping functionality (say tables with editable cells).
So far the solutions I've come up with are either to symlink common file(s) into both directories that implement the functionality, which I find aesthetically displeasing, but should actually work fine, or to extract the common functionality into a separate package and make the project depend on it. That however involves either requiring developers to setup both repositories and install the dependency locally, or setup a maven server.
This answer from 2012 seems to suggest I should simply do away with multiple targets and instead initialize the functionality I want on the correct page, but it seems to me that having the functionality for the entire site in a single file would make the resulting javascript much larger than it needs to be, increasing the page load time, especially given that I will have more than 2 pages.
Is there a better way?
OK, it turns out the solution is pretty simple, and actually should have been clear from the cljsbuild docs, but I wasn't seeing it. It took trying to reply to #phtrivier for me to actually see what I was missing.
The solution is to use multiple source paths in my project.clj file. So a simple example would look like so:
{:builds [{:source-paths ["src/cljs/items"
"src/cljs/shared"]
:compiler {:output-to "resources/public/js/items.js"
:optimizations :simple
:pretty-print true}
:jar true}
{:source-paths ["src/cljs/stores"
"src/cljs/shared"]
:compiler {:output-to "resources/public/js/stores.js"
:optimizations :simple
:pretty-print true}}]}
from there it's simple:
src/cljs/shared/shared.cljs
(ns shared)
(defn common []
"Hello World")
src/cljs/items/items.cljs
(ns items
(:require [shared :refer [common]]))
(.log js/console (common))
src/cljs/stores/stores.cljs
(ns stores
(:require [shared :refer [common]]))
(js/alert (common))
items.js and stores.js will be their own separate files both containing the shared code.

How can I get the newest build of clojurescript working with lein-cljsbuild (without forking)?

I'm happily using lein cljsbuild right now to compile my clojurescript but I'm noticing that the clojurescript library is changing quite rapidly and cljsbuild has fallen behind. Is there an option that we can put into project.clj that compiles against the newest clojurescript build?
I believe you can just add it as a dependency in your project.clj, e.g. [org.clojure/clojurescript "0.0-1835"].
Bigger example:
(defproject cljs-tests "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-1835"]]
:plugins [[lein-cljsbuild "0.3.2"]]
:cljsbuild ...)

Compiling external JS files with Cljsbuild in ClojureScript

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.

Clojurescript libraries - goog.require could not find

New to clojurescript, and working through the "Modern CLJS" tutorial here.
It instructs to pull in domina by adding it to the project.clj:
:dependencies [[org.clojure/clojure "1.4.0"]
[compojure "1.1.5"]
[domina "1.0.0"]]
And then use it in your script by referencing it in the ns form
(ns cljstut.login
(:use [domina :only [by-id value]]))
However, when I actually run this in a browser, I see the following in the console log.
goog.require could not find: domina
Seems like I'm missing some declaration somewhere? But as a newb, this whole build process is rather opaque. Any troubleshooting tips are greatly appreciated.
Dependencies in your project.clj file and your namespace declarations look fine.
If you are building your clojurescript code using lein cljsbuild auto, you will need to restart that process. I believe it should automatically pull in new dependencies after a restart.
If that doesn't work, or you aren't using lein cljsbuild auto, try running the lein deps command inside your project's folder manually - it will fetch all missing dependencies from remote repositories.
Also, make sure you have saved your project.clj file and try running lein clean and lein cljsbuild clean.
I'm in the process of updating modern-cljs to the latest versions of the used libs and lein plugins. At the moment I updated the series up to the 7th tutorial of the series. You should now be safe by updating to domina 1.0.2-SNAPSHOT into project.cljs :dependencies and to lein-cljsbuild 0.3.0 into project.cljs :plugins.
Mimmo
Just want to add that a typo in the namespace would trigger the same error message:
goog.require could not find: domina
Example (note the missing "j" in modern-cljs):
(ns modern-cls.login
(:use [domina :only [by-id value]]))
I was using a different project name thus a different namespace just to be brave.
It bit me in the ass when I copy-pasted from the guide and the error had me puzzled for a while :)
I ran into this same issue while working through the "Modern CLJS" tutorial. My problem was inadvertently adding the "domina" dependency to the :plugins section of project.clj rather than the :dependencies section.
I got past this error message by putting lines in the right order in the index.html file.
Correct order:
<script src="js/expanding_gases.js" type="text/javascript"></script>
<script type="text/javascript">goog.require("expanding_gases.flow_gases");</script>
Incorrect order:
<script type="text/javascript">goog.require("expanding_gases.flow_gases");</script>
<script src="js/expanding_gases.js" type="text/javascript"></script>
By the way a good read for understanding 'goog' messages is here:
https://github.com/clojure/clojurescript/wiki/Quick-Start