How can I make figwheel start with a custom initial namespace? - clojurescript

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.

Related

Failed to construct ‘HTMLElement’: Please use the ‘new’ operator

I am trying to use AWS Amplify Authentication lib in a re-frame app.
The lib provides a higher order component withAuthenticator which is supposed to wrap the main view of your app. I am trying to use reactify-component and adapt-react-class but unfortunately I get the following error:
Uncaught TypeError: Failed to construct ‘HTMLElement’: Please use the ‘new’ operator, this DOM object constructor cannot be called as a function.
(defn main-panel []
[:div
[:h1 "Hello" ]])
(def root-view
(reagent/adapt-react-class
(withAuthenticator
(reagent/reactify-component main-panel))))
(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(aws-config/configure)
(re-frame/dispatch-sync [::events/initialize-db])
(reagent/render [root-view]
(.getElementById js/document "app")))
Any help is appreciated
I had this issue with reagent + amplify.
Solved it with 2 changes, but I'm unsure if both are needed
#1 Change output of the google closure compiler to es6 (or higher). Amplify seems to use es6 features that Cannot be polyfilled.
This is for shadow-cljs (shadow-cljs.edn), but this should be possible for other build systems as well.
{:builds {:app {:compiler-options {:output-feature-set :es6}}}}
Disclaimer: I switched to shadow-cljs from lein-cljsbuild since I could not get lein-cljsbuild to respect the configuration for es6 output.
#2 Use functional components.
In reagent 1.0.0-alpha1 and up you can change the compiler to produce functional components by default.
(ns core
(:require
[reagent.core :as r]))
(def functional-compiler (r/create-compiler {:function-components true}))
(r/set-default-compiler! functional-compiler)
There are other ways to make functional components. Check the documentation if you don't like, or can't use, this approach.

Evaluate expression and display results in spacemacs

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"]]}}

ClojureScript cljsbuild doesn't load dependencies without optimisations

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.

Can I use arbitrary node modules from clojurescript?

Is it possible to use arbitrary node.js modules in a clojurescript project? If yes, how do I go about including them? If not, why not?
Yes, you can, there is nothing special about it:
(def fs (js/require "fs"))
(println (.readdirSync fs js/__dirname))
Be careful with the externs if you don't use optimizations none.
Edit: Does leiningen play with the various js package managers?:
Nope. Since the language does not have packages, it cannot know. You have to do js dependency management and lein deps too. There is a lein-npm and a lein-bower to help with integrating these two package managers.
Yes, since late 2017. With shadow-cljs or Lumo now it's not problem to import npm modules in ClojureScript code anymore.
(ns app.main
(:require ["dayjs" :as dayjs]
["shortid" :as shortid]
["lodash" :as lodash]
["lodash" :refer [isString]]))
Read this topic for details: Guide on how to use/import npm modules/packages in ClojureScript?
Since ClojureScript 1.9.854, there's better support to declare npm modules as dependencies and to require them from your namespaces.
In order to declare it as a dependency, you need to use the :npm-deps compiler option (together with the :install-deps one, if you want lein/bootto automatically install it).
:npm-deps is a map from keyword to string, where the keyword is the name of the dependency you would use to install it using npm and the string is the version of the dependency.
An example of what you could add to your project.clj (if you use lein-cljsbuild), in order to use left-pad:
:cljsbuild {:builds [{:id "prod"
:source-paths ["src"]
:compiler {:main left-pad-demo.core
:output-to "package/index.js"
:target :nodejs
:output-dir "target"
:optimizations :simple
:install-deps true
:npm-deps {:left-pad "1.2.0"}
:pretty-print true}}]})
And then, from your namespace, you can require it like so:
(ns left-pad-demo.core
(:require left-pad))
Or so:
(ns left-pad-demo.core
(:require ["left-pad" :as lp]))
A full working namespace could look like:
(ns left-pad-demo.core
(:require left-pad))
(defn -main [s length]
(console.log (left-pad s length)))
(set! *main-cli-fn* -main)
References:
Requiring Node.js modules from ClojureScript namespaces
ClojureScript is not an Island: Integrating Node Modules

Multiple ClojureScript files on same page

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.