I have a project that is using Jasmine to test the JavaScript. I am trying to switch to using ClojureScript for the front end. My project.clj is like
(defproject myproject "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript"0.0-1889"]
[org.clojure/google-closure-library-third-party "0.0-2029"]
[domina "1.0.0"]
[hiccups "0.2.0"]]
:plugins [[lein-cljsbuild "0.3.3"]]
:cljsbuild {
:builds [{
:source-paths ["src/clojurescript"]
:compiler {
:output-to "public/javascripts/main.js"
:optimizations :whitespace
:pretty-print true}}
{
:source-paths ["spec/clojurescript"]
:compiler {
:output-to "spec/javascripts/mainSpec.js"
:optimizations :whitespace
:pretty-print true}}]})
So all the .cljs files in src/clojurescript get compiled to main.js and all the .cljs in spec/clojurescript get compiled to mainSpec.js.
When I load the Jasmine page, both the .js files are loaded but the tests aren't run.
In the console I get an Error: Namespace "goog.debug.Error" already declared.
Both the .js files have the same ~30k lines of google closure code at the top which is causing the error. If I delete this code from mainSpec.js it runs fine.
Is there any way to tell cljsbuild to leave this code off the spec file?
As Jared314 and Zubair pointed out, the problem you're encountering is caused by trying to include two clojurescript compilation outputs in the same page. Clojurescript/Google Closure expect to do a 'whole-world' compile, that is, the compiler expects that all of the code for the entire page is passed to the compiler so that it can optimise it, rename functions, and ultimately spit out a single javascript file. It's not designed to produce multiple output files that work together.
The 'correct' way to solve your problem is to produce two outputs that are used in isolation: a main.js file for running your application, and a spec.js file that include all the code in main plus the code in spec for testing. You can do this by setting up your project something like this:
:cljsbuild {
:builds [{
:source-paths ["src/clojurescript"]
:compiler {:output-to "public/javascripts/main.js"}}
{
:source-paths ["src/clojurescript" "spec/clojurescript"]
:compiler {:output-to "spec/javascripts/spec.js"}}]})
Your jasmine page should refer to spec.js but not main.js - referring to both is the cause of your error.
The issue is both builds are compiled with the Google Closure library included, hence the "already declared" error. You can try using :optimizations :advanced, in the :compiler options, to reduce, or eliminate, the duplicate code.
But, if you still run into the same issue, you might want to compile the src and spec together for the mainSpec.js build.
Related
In my ClojureScript code I am requiring a JavaScript module called seedrandom which is in the node_modules folder, like this:
(ns something.core
(:require ["seedrandom" :as rnd]))
(js/console.log (.quick (rnd "x")))
According to the seedrandom documentation it is intended for both nodejs and the browser, and I've previously included and used it successfully in ClojureScript code via a <script> tag, confirming it works in the browser.
Running this cljs file in lumo on the command line works well and outputs a deterministically random number.
When I try to use this same cljs file in my Reagent frontend project I see the following error:
Compiling build :app to "public/js/app.js" from ["src" "env/dev/cljs"]...
events.js:183
throw er; // Unhandled 'error' event
^
Error: module not found: "crypto" from file /home/chrism/dev/something/node_modules/seedrandom/seedrandom.js
at onresolve (/home/chrism/dev/something/node_modules/#cljs-oss/module-deps/index.js:181:30)
...
Inside seedrandom.js we see the following:
// When in node.js, try using crypto package for autoseeding.
try {
nodecrypto = require('crypto');
} catch (ex) {}
Clearly this code is intended to ignore the built-in nodejs crypto module when running in the browser. The problem, as far as I can tell, is that the ClojureScript compiler does not know that - it sees that require('crypto') and tries to pull it into the compilation phase, but can't find it because it's a nodejs built-in.
Is there some way I can tell the compiler to ignore that particular require? Or can I shim the 'crypto' module somehow? What is the cleanest way to solve this?
Note: I have previously experienced this same issue with JavaScript modules which check for the fs node module. Hope we can find a general solution to use again in future. Thanks!
Relevant versions: [org.clojure/clojurescript "1.10.520"] and [reagent "0.8.1"].
This answer is related, asking a similar question from the perspective of Google Closure, which ClojureScript uses, but I'm looking for an answer I can use specifically with cljs.
Recently started playing with Clojurescript in Spacemacs(emacs 26.1). I am using figwheel, I usually make a change in the *.cljs file and then test the results in the function in the REPL. I recently watched a video where someone evaluated the function in the file without having to go to the REPL.
I tried to do the same using cider in spacemacs, The problem is it evaluates but does not display the results.
Below snippet is from config in my project.clj
:profiles {:dev {:source-paths ["src" "env/dev/clj"]
:dependencies [[binaryage/devtools "0.9.10"]
[figwheel-sidecar "0.5.16"]
[nrepl "0.4.4"]
[cider/piggieback "0.3.9"]]}})
Snippet from cljs file.
(defn tes []
(inc 1234))
(prn (tes))
(tes)
I tried evaluating (tes) and (prn (tes)), it evaluates but does not print anything in the echo area.
I have cider setup in spacemacs and cider REPL running during the execution. Executing the function in cider REPL by navigating to the namespace works.
Is there anything I am missing here in order to get it working in terms of setup.
You probaly need cider-nrepl.Put the following in your ~/.lein/profiles.clj file.
{:repl {:plugins [[cider/cider-nrepl "0.18.0"]]}}
I am trying to test lodash, and apparently the following line won't work in the REPL:
import curry from 'lodash/curry';
See, e.g., babel-node es6 "Modules aren't supported in the REPL"
Why does babel-node not support module loading in the REPL?
Is there a way that I can pre-load a module like lodash into babel-node? (e.g. via a command line option or a configuration file)
If not, is there another way of evaluating ES6 with lodash preloaded?
So far, I've tried the online REPL at https://babeljs.io/repl/, and evaluation in the Console in Firefox. None worked.
import surely won't work, because the package should be installed first and bundled in order to be imported, which isn't the duty of Babel REPL.
Lodash is already loaded and used in Babel REPL, it can be used in REPL as _ global:
const { curry } = _;
If the question isn't limited to Lodash, the quickest way to get third-party library in REPL is to load the script manually. And since Babel website has jQuery loaded (as almost any website), the shortest way to do this is jQuery.getScript, in console:
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js');
After that Lodash _ global becomes available in REPL.
The whole code can be wrapped with callback to skip console part:
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js', () => {
console.log(_.VERSION);
});
babel-node REPL doesn't support imports, as the links in the original post say. Instead, require should be used, like anywhere else in Node.
var { curry } = require('lodash')
This requires to have lodash installed in node_modules that exists in current working directory.
The project.clj of my clojurescript code specifies :repl-options {:init-ns my-project.core} and I start figwheel via start-figwheel!. In the figwheel documentation it says that
;; you can also just call (ra/start-figwheel!)
;; and figwheel will do its best to get your config from the
;; project.clj or a figwheel.edn file`
But when figwheel starts, it puts me into the cljs.user namespace. How can I make figwheel pick up this option?
My figwheel.clj looks as follows:
(require '[figwheel-sidecar.repl :as r]
'[figwheel-sidecar.repl-api :as ra])
(ra/start-figwheel!
{:figwheel-options {}
:build-ids ["dev"]
:all-builds
[{:id "dev"
:figwheel {:devcards true}
:source-paths ["src"]
:compiler {:main 'my-project.core
:asset-path "js"
:output-to "resources/public/js/main.js"
:output-dir "resources/public/js"
:verbose true}}]})
(ra/cljs-repl)
I am basically asking this question from Google groups.
start-figwheel! only starts figwheel logic. Regarding your finding that:
;; you can also just call (ra/start-figwheel!)
;; and figwheel will do its best to get your config from the
;; project.clj or a figwheel.edn file`
It indeed finds its config but it's only :figwheel submap of project.clj.
:repl-options are used by REPL when it starts. It seems that figwheel-sidecar.repl-api/cljs-repl doesn't allow specifying REPL options and it doesn't read them from project.clj.
You might try to play with starting lein repl where project.clj's :repl-options use :init option to provide the code you want to execute instead of a standalone script.
I am trying to use figwheel in my ClojureScript build.
It works with lein cljsbuild auto already, but I have to put :optimisations :whitespace.
Otherwise I get a message in the browser :
Uncaught ReferenceError: goog is not defined
However figwheel require :optimisations :none to run. Here is the part of my leiningen file :
:cljsbuild {
:builds
[{:id "dev"
:source-paths ["src/cljs"]
:figwheel { :websocket-host "localhost"
;;:on-jsload "example.core/fig-reload"
:autoload true
:heads-up-display true
:load-warninged-code true
;;:url-rewriter "example.core/fig-url-rewrite"
}
:compiler {;; :main
:output-to "resources/public/js/gdb/gdb.js"
:output-dir "resources/public/js/gdb/cljsbuild-dev"
;;:asset-path "js/out"
:optimizations :none
:source-map "resources/public/js/gdb/gdb.js.map"
:pretty-print true}}]}
What is missing for me to get the missing dependencies ?
It turns out this is a classic case of RTFM.
The answer was in the ClojureScript quickstart guide.
Specifically, I had to add a :main field, as specified in the Less Boilerplate section :
:main "example.core"
Nothing jumps out as being obviously wrong or missing. However, lein is pretty powerful in the degree it lets you set things to fit your personal taste/workflow, so it is hard to spot things if the approach is signficantly different.
When I run into these types of problems, I find using the standard templates provided by many libraries or projects really useful. My recommendation would be to run
lein new figwheel ft -- --reagent
This will setup a basic project called ft (in this case also with reagent - there is another option for om or you could leave all of this out for a bare bones default. See the figwheel repo on github for more details. This will provide a good working lein figwheel setup which hyou can use as a guide.