Generate a new Hoplon project. Run the following command on a terminal:
boot -d boot/new new -t hoplon -n error-recovery
Change your directory to the project:
cd error-recovery
And run:
boot dev
After the code is compiled open your browser and go to http://localhost:8000. You should see "Hello Hoplon!" in blue.
Let's make some mistake Open the file src/index.cljs.hl
inside the project directory, in your favorite editor. You should see the code below:
(page"index.html")(html(head(link:href"app.css":rel"stylesheet":type"text/css"))(body(h1"Hello, Hoplon!")))
In ClojureScript you will get errros if you don't close your forms. Remove one of the parentheses in the end of the (h1 "Hello, Hoplon!")))
(page"index.html")(html(head(link:href"app.css":rel"stylesheet":type"text/css"))(body(h1"Hello, Hoplon!"))
When you save the file you will get the following message on the terminal running boot dev
.
Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: ERROR: EOF while reading, starting at line 3 and column 1 at file hoplon/app_pages/_index_DOT_html.cljs, line 8, column 1
data: {:type :reader-exception,
:line 8,
:column 1,
:file "hoplon/app_pages/_index_DOT_html.cljs",
:from :boot-cljs}
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: EOF while reading, starting at line 3 and column 1
data: {:type :reader-exception,
:line 8,
:column 1,
:file
"/home/y/.dotfiles/dotfiles/boot/cache/tmp/home/y/repos/hoplon/hoplon-project/eb4/6qp7pj/hoplon/app_pages/_index_DOT_html.cljs"}
clojure.core/ex-info/invokeStatic core.clj: 4617
clojure.core/ex-info core.clj: 4617
clojure.tools.reader.reader-types/reader-error/invokeStatic reader_types.clj: 389
clojure.tools.reader.reader-types/reader-error reader_types.clj: 385
...
clojure.tools.reader/read-delimited/invokeStatic reader.clj: 201
clojure.tools.reader/read-delimited reader.clj: 190
clojure.tools.reader/read-list/invokeStatic reader.clj: 210
clojure.tools.reader/read-list reader.clj: 206
clojure.tools.reader/read*/invokeStatic reader.clj: 916
clojure.tools.reader/read* reader.clj: 897
clojure.tools.reader/read/invokeStatic reader.clj: 965
clojure.tools.reader/read reader.clj: 942
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316$fn__2317.invoke analyzer.cljc: 3112
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316.invoke analyzer.cljc: 3105
...
clojure.core/seq/invokeStatic core.clj: 137
clojure.core/seq core.clj: 137
cljs.compiler$emit_source.invokeStatic compiler.cljc: 1255
cljs.compiler$emit_source.invoke compiler.cljc: 1237
cljs.compiler$compile_file_STAR_$fn__3623.invoke compiler.cljc: 1328
cljs.compiler$with_core_cljs.invokeStatic compiler.cljc: 1159
cljs.compiler$with_core_cljs.invoke compiler.cljc: 1150
cljs.compiler$compile_file_STAR_.invokeStatic compiler.cljc: 1317
cljs.compiler$compile_file_STAR_.invoke compiler.cljc: 1313
cljs.compiler$compile_file$fn__3646.invoke compiler.cljc: 1398
cljs.compiler$compile_file.invokeStatic compiler.cljc: 1376
cljs.compiler$compile_file.invoke compiler.cljc: 1356
cljs.closure/compile-file/invokeStatic closure.clj: 432
cljs.closure/compile-file closure.clj: 423
cljs.closure/eval5411/fn closure.clj: 499
cljs.closure/eval5347/fn/G closure.clj: 389
cljs.closure/eval5417/fn closure.clj: 508
cljs.closure/eval5347/fn/G closure.clj: 389
cljs.closure/compile-sources/iter/fn closure.clj: 829
...
clojure.core/next/invokeStatic core.clj: 64
clojure.core/dorun/invokeStatic core.clj: 3033
clojure.core/doall/invokeStatic core.clj: 3039
clojure.core/doall core.clj: 3039
cljs.closure/compile-sources/invokeStatic closure.clj: 825
cljs.closure/compile-sources closure.clj: 814
cljs.closure/build/invokeStatic closure.clj: 1945
cljs.closure/build closure.clj: 1883
cljs.build.api/build/invokeStatic api.clj: 199
cljs.build.api/build api.clj: 187
adzerk.boot-cljs.impl/compile-cljs/invokeStatic impl.clj: 89
adzerk.boot-cljs.impl/compile-cljs impl.clj: 66
...
clojure.core/apply/invokeStatic core.clj: 646
clojure.core/apply core.clj: 641
boot.pod/eval-fn-call pod.clj: 328
boot.pod/call-in* pod.clj: 379
...
boot.pod/call-in* pod.clj: 382
adzerk.boot-cljs/compile/invokeStatic boot_cljs.clj: 71
adzerk.boot-cljs/compile boot_cljs.clj: 59
adzerk.boot-cljs/compile-1/fn boot_cljs.clj: 126
clojure.core/binding-conveyor-fn/fn core.clj: 1938
...
Elapsed time: 0.211 sec
While this is not a very nice message it sure tells you that you have a problem. But this gets better. If you look at your browser window there is something there for you too:
The important part of the error displayed on the terminal shows at your browser too.
The error is ERROR: EOF while reading, starting at line 3 and column 1 at file hoplon/app_pages/_index_DOT_html.cljs, line 8, column 1
. An EOF
usually means that the file ended but the form was incomplete. So you probably have an open parenthesis without a closing one. starting at line 3 and column 1
is the location of the open parenthesis that was not closed.
Wait a minute. We didn't close the parenthesis from the (h1
form. Well, the reader has no way to know that. The parentheses are closed in the order reversed, so the last opened one is closed first. In the end there is one parenthesis that was not closed and that parenthesis was opened on line 3, column 1.
You can now fix the src/index.cljs.hl
adding the removed parenthesis back.
Also if you close a form that was not opened you will get errors. Add one extra closing parenthesis in the end of the (ns "index.html")))
(page "index.html"))
(html
(head
(link :href "app.css" :rel "stylesheet" :type"text/css"))
(body
(h1 "Hello, Hoplon!")))
When you save the file you will get the following message on the terminal running boot dev
.
Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: ERROR: Unmatched delimiter ) at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598
data: {:type :reader-exception,
:line 1,
:column 1598,
:file "hoplon/app_pages/_index_DOT_html.cljs",
:from :boot-cljs}
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: Unmatched delimiter )
data: {:type :reader-exception,
:line 1,
:column 1598,
:file
"/home/y/.dotfiles/dotfiles/boot/cache/tmp/home/y/repos/hoplon/hoplon-project/dvg/6qp7pj/hoplon/app_pages/_index_DOT_html.cljs"}
clojure.core/ex-info/invokeStatic core.clj: 4617
clojure.core/ex-info core.clj: 4617
clojure.tools.reader.reader-types/reader-error/invokeStatic reader_types.clj: 389
clojure.tools.reader.reader-types/reader-error reader_types.clj: 385
...
clojure.tools.reader/read-unmatched-delimiter/invokeStatic reader.clj: 76
clojure.tools.reader/read-unmatched-delimiter reader.clj: 74
clojure.tools.reader/read*/invokeStatic reader.clj: 916
clojure.tools.reader/read* reader.clj: 897
clojure.tools.reader/read/invokeStatic reader.clj: 965
clojure.tools.reader/read reader.clj: 942
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316$fn__2317.invoke analyzer.cljc: 3112
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316.invoke analyzer.cljc: 3105
...
clojure.core/seq/invokeStatic core.clj: 137
clojure.core/seq core.clj: 137
cljs.compiler$emit_source.invokeStatic compiler.cljc: 1255
cljs.compiler$emit_source.invoke compiler.cljc: 1237
cljs.compiler$compile_file_STAR_$fn__3623.invoke compiler.cljc: 1328
cljs.compiler$with_core_cljs.invokeStatic compiler.cljc: 1159
cljs.compiler$with_core_cljs.invoke compiler.cljc: 1150
cljs.compiler$compile_file_STAR_.invokeStatic compiler.cljc: 1317
cljs.compiler$compile_file_STAR_.invoke compiler.cljc: 1313
cljs.compiler$compile_file$fn__3646.invoke compiler.cljc: 1398
cljs.compiler$compile_file.invokeStatic compiler.cljc: 1376
cljs.compiler$compile_file.invoke compiler.cljc: 1356
cljs.closure/compile-file/invokeStatic closure.clj: 432
cljs.closure/compile-file closure.clj: 423
cljs.closure/eval5411/fn closure.clj: 499
cljs.closure/eval5347/fn/G closure.clj: 389
cljs.closure/eval5417/fn closure.clj: 508
cljs.closure/eval5347/fn/G closure.clj: 389
cljs.closure/compile-sources/iter/fn closure.clj: 829
...
clojure.core/next/invokeStatic core.clj: 64
clojure.core/dorun/invokeStatic core.clj: 3033
clojure.core/doall/invokeStatic core.clj: 3039
clojure.core/doall core.clj: 3039
cljs.closure/compile-sources/invokeStatic closure.clj: 825
cljs.closure/compile-sources closure.clj: 814
cljs.closure/build/invokeStatic closure.clj: 1945
cljs.closure/build closure.clj: 1883
cljs.build.api/build/invokeStatic api.clj: 199
cljs.build.api/build api.clj: 187
adzerk.boot-cljs.impl/compile-cljs/invokeStatic impl.clj: 89
adzerk.boot-cljs.impl/compile-cljs impl.clj: 66
...
clojure.core/apply/invokeStatic core.clj: 646
clojure.core/apply core.clj: 641
boot.pod/eval-fn-call pod.clj: 328
boot.pod/call-in* pod.clj: 379
...
boot.pod/call-in* pod.clj: 382
adzerk.boot-cljs/compile/invokeStatic boot_cljs.clj: 71
adzerk.boot-cljs/compile boot_cljs.clj: 59
adzerk.boot-cljs/compile-1/fn boot_cljs.clj: 126
clojure.core/binding-conveyor-fn/fn core.clj: 1938
...
Elapsed time: 0.385 sec
While this is not a very nice message it sure tells you that you have a problem. But this gets better. If you look at your browser window there is something there for you too:
The important part of the error displayed on the terminal shows at your browser too.
The error is ERROR: Unmatched delimiter ) at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598
. An Unmatched delimiter )
is a straightforward message at least. It means that the there is a closing )
without one being opened.starting at line 1 and column 1598
is the location of the open parenthesis that was not closed. The column number seems like an error but during the expansion of the page
macro the ns
form that is generated is:
(ns hoplon.app-pages._index_DOT_html (:require[javelin.core :refer[->Cell input? cell cell? destroy-cell! lift lens? set-formula! cell-doseq* deref* set-cell! lens formula? alts! dosync* cell-map formula]][hoplon.core :refer[form audio input menuitem hgroup do! timeout $text base h1 set-attributes! embed h3 body keygen progress main cite on-page-load object i p nav ruby check-val! a menu blockquote img $comment span track data u dl select html thead del eventsource fieldset aside figure figcaption q on! bdi append-child! video address caption dd rp hr tbody table acronym frame applet html-var add-initfn! pre ul dir replace-child! html-time html-map sup dfn sub mark script big button wbr insert-before! strong li dt rtc frameset td tr section th optgroup bust-cache iframe remove-child! legend em kbd spliced article isindex abbr command prerendering? <!-- source output set-styles! basefont route-cell header datalist tfoot s ins footer title h5 canvas param font div option ensure-kids! summary samp center small style textarea loop-tpl* strike h4 tt head ol details col label rt when-dom h6 link page-load colgroup meter html-meta static-elements text-val! bdo --> b code dialog noframes do-watch noscript safe-nth h2 area br]])(:require-macros[javelin.core :refer[with-let mx2 dosync cell= set-cell!= prop-cell cell-doseq defc cell-let-1 defc= macroexpand-all mx cell-let]][hoplon.core :refer[text elem+ cache-key with-timeout defelem+ when-tpl static sexp defelem elem def-values if-tpl cond-tpl with-page-load for-tpl with-dom case-tpl loop-tpl with-interval with-init!]]))
So the extra closing parenthesis is at column 1598.
The error will be the same except by the character itself if you close a ]
or a }
that is not open. So the message in this case would be ERROR: Unmatched delimiter ] at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598
or ERROR: Unmatched delimiter } at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598
.
You can now fix the src/index.cljs.hl
removing the extra parenthesis.
Another common error is using a variable not declared yet In ClojureScript variables have to be declared before they are used. Or you can mistype a variable.
Let's edit src/index.cljs.hl
replacing (h1
with (h7
. h7
doesn't exist so it's not declared. What happens?
On the browser you will see:
On the terminal you can see the same warning:
Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
WARNING: Use of undeclared Var hoplon.app-pages._index_DOT_html/h7 at line 7 hoplon/app_pages/_index_DOT_html.cljs
Elapsed time: 0.302 sec
Let's explain the error. Use of undeclared Var
describes the problem. So a variable that has not being declared was used. Next is the variable itself: hoplon.app-pages._index_DOT_html/h7
. Well, where did that come from?
When you see a variable or a keyword in ClojureScript they may be qualified or unqualified. Qualified variables are in the form namespace/variable-name
and unqualified ones are simply variable-name
. We will discuss namespaces in more depth later but for now you need to know that hoplon.app-pages._index_DOT_html
is a namespace and h7
is a variable name.
The namespace hoplon.app-pages._index_DOT_html
was generated by Hoplon when we used the page
macro in (page "index.html")
. So the rule is that a namespace will be generated with the format hoplon.app-pages.page-name_DOT_html
. In our case page-name
is index
.
By default namespaces in .hl
files have all variables from ClojureScript core, Hoplon core and Javelin core refered. This means that we can use then unqualified. But when we use a variable not declared on those namespaces the compiler complains saying that it could not find the variable on the namespace that's trying to use it. That's why the message is Use of undeclared Var hoplon.app-pages._index_DOT_html/h7
.
Next we have at line 7 hoplon/app_pages/_index_DOT_html.cljs
. That says the line where the variable was used and the file name. In ClojureScript there is a correspondence between namespaces names and file paths. The original index.cljs.hl
file is transformed by boot-hoplon
in the hoplon/app_pages/_index_DOT_html.cljs
file with the namespace hoplon.app-pages._index_DOT_html
as we saw before.
You can now fix the src/index.cljs.hl
replacing h7
with h1
.
We will understand a bit more about ClojureScript itself.