Connecting to ClojureScript bREPL: clojure.browser.repl/connect throws TypeError in compiled JavaScript
Asked Answered
D

2

8

I'm trying to connect to a ClojureScript browser REPL, and I'm having trouble with clojure.browser.repl/connect. My compiled JavaScript throws a TypeError trying to call appendChild on a null object in the block of Google Closure code at the top. I'm following the instructions in ClojureScript: Up and Running (Chapter 9, p.78, available in the preview), and wondering if the tooling for this has changed since it was published.

I'm using Leiningen 2.0.0, Java 1.6.0_37, OS X 10.7.5, plus the dependencies in my project.clj:

(defproject brepl-hello "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [org.clojure/clojurescript "0.0-1552"]
                 [compojure "1.1.5"]
                 [ring/ring-jetty-adapter "1.1.8"]]
  :plugins [[lein-cljsbuild "0.3.0"]]
  :source-paths ["src/clj"]
  :cljsbuild {:builds [{
                :source-paths ["src/cljs"]
                :compiler {
                 :output-to "resources/public/brepl-hello.js"
                 :optimizations :whitespace 
                 :pretty-print true}}]})

Here's the only ClojureScript source file, src/cljs/brepl_hello/brepl-hello.cljs:

(ns brepl-hello
  (:require [clojure.browser.repl :as repl]))

(repl/connect "http://localhost:9000/repl")

This compiles to the file resources/public/brepl-hello.js, which I've inserted into index.html in the same directory:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <script type="text/javascript" src="brepl-hello.js"></script>
    </head>
    <body>
    </body>
</html>

I've been serving this on port 3000 with Ring/Jetty from the REPL or Python SimpleHTTPServer. When I open this page in Chrome, the dev console shows Uncaught TypeError: Cannot call method 'appendChild' of null, with a traceback to this if/else block in the Google Closure code at the top of the complied js file, where parentElm (passed in to the containing function as a parameter) is null.

if(goog.userAgent.GECKO || goog.userAgent.WEBKIT) {
    window.setTimeout(goog.bind(function() {
      parentElm.appendChild(iframeElm);
      iframeElm.src = peerUri.toString();
      goog.net.xpc.logger.info("peer iframe created (" + iframeId + ")")
    }, this), 1)
  }else {
    iframeElm.src = peerUri.toString();
    parentElm.appendChild(iframeElm);
    goog.net.xpc.logger.info("peer iframe created (" + iframeId + ")")
  }

This seems to be a problem with clojure.browser.repl/connect. Swapping out this line in the ClojureScript source for something like:

(ns brepl-hello
  (:require [clojure.browser.repl :as repl]))

(.write js/document "Hello World!")

Will compile and run in the browser just fine. I suspect something is misconfigured in my build settings or directory structure, or I'm making a noob mistake somewhere in all this. What's changed since the time the instructions I'm following were published? I found a couple references to this problem in the #clojure irc logs, but no solution.

Finally, here's an abbreviated directory tree for reference:

├── out
│   ├── cljs
│   │   ├── core.cljs
│   │   └── core.js
│   ├── clojure
│   │   └── browser
│   │       ├── event.cljs
│   │       ├── event.js
│   │       ├── net.cljs
│   │       ├── net.js
│   │       ├── repl.cljs
│   │       └── repl.js
│   └── goog
│       └── [...] 
├── pom.xml
├── project.clj
├── resources
│   └── public
│       ├── brepl-hello.js
│       └── index.html
├── src
│   ├── clj
│   │   └── brepl_hello
│   │       └── core.clj
│   └── cljs
│       └── brepl_hello
│           └── brepl-hello.cljs
└─── target
     ├── brepl-hello-0.1.0-SNAPSHOT.jar
     ├── classes
     ├── cljsbuild-compiler-0
     │   ├── brepl_hello
     │   │   └── brepl-hello.js
     │   ├── cljs
     │   │   ├── core.cljs
     │   │   └── core.js
     │   └── clojure
     │       └── browser
     │           ├── event.cljs
     │           ├── event.js
     │           ├── net.cljs
     │           ├── net.js
     │           ├── repl.cljs
     │           └── repl.js
     └── stale
         └── extract-native.dependencies
Detradetract answered 27/1, 2013 at 20:41 Comment(0)
A
6

Well, its open source and looking at the code it seems that document.body is null at the time the repl hidden iframe is being added to it (the connect call leads to this point).

You should do this connect call on dom ready or body on load and it should work fine.

Aguie answered 28/1, 2013 at 13:32 Comment(1)
You're right! This is a plain old uninitialized element. Oops.Detradetract
P
2

Take a look at:

https://github.com/magomimmo/modern-cljs/blob/master/doc/tutorial-02.md

or, for a better brepl experience, here

https://github.com/magomimmo/modern-cljs/blob/master/doc/tutorial-18.md

Pratique answered 13/9, 2013 at 15:37 Comment(1)
Great resources, thank you for putting them togetherYee

© 2022 - 2024 — McMap. All rights reserved.