Shadow-cljs: differences about nrepl and socket REPL?

Created on 2 Mar 2019  路  6Comments  路  Source: thheller/shadow-cljs

I asked about the API before and I got an function which I can use to send my code to runtime from my own code,

(shadow.cljs.devtools.api/repl :build-id)

It was based on Socket REPL. Actually I'm a little confused about the history about Socket REPL and nREPL. But I know that Socket REPL returns me only strings and nREPL returns me data in a structure.

So actually nREPL is the useful one I think. Now I want to send my code with nREPL. Is there any API I can use too?

Besides there was an option in my template shadow-cljs.edn which I think should pick me a random port, not sure if it's still working today...

 :socket-repl {:port 0}

but it's obviously sure that nREPL is running when I start the watch server..

shadow-cljs - nREPL server started on port 54004

Are the roles of Socket REPL and nREPL just different in the case of shadow-cljs?

All 6 comments

nREPL provides a way to eval code remotely among other things. That is pretty much the only thing it has in common with the Socket REPL. nREPL uses a completely different protocol so it can't really be compared with the Socket REPL. It is much more complex. I can't explain nREPL easily since I barely understand it myself.

See https://github.com/nrepl/nrepl

:socket-repl is only used to configure the Socket REPL, :nrepl configures nREPL. They are independent and don't affect each other.

Thanks.. but is there an API like this for nREPL?

(shadow.cljs.devtools.api/repl :build-id)

The shadow-cljs API doesn't change and remains exactly the same.

(shadow.cljs.devtools.api/repl :build-id)

So there might be something wrong... I have a shadow-cljs instance running:

=>> yarn watch
yarn run v1.13.0
$ shadow-cljs watch client
shadow-cljs - config: /Users/chen/repo/erigeron/composer-todolist/shadow-cljs.edn  cli version: 2.8.12  node: v11.6.0
shadow-cljs - updating dependencies
shadow-cljs - dependencies updated
[2019-03-03 12:23:58.917 - WARNING] TCP Port 9630 in use.
[2019-03-03 12:24:01.584 - WARNING] TCP Port 7000 in use.
shadow-cljs - HTTP server available at http://localhost:7001
shadow-cljs - server version: 2.8.12 running at http://localhost:9631
shadow-cljs - nREPL server started on port 49259
shadow-cljs - watching build :client
[:client] Configuring build.
[:client] Compiling ...
[:client] Build completed. (199 files, 27 compiled, 0 warnings, 7.52s)

And I got a nrepl-client running:

client = require('nrepl-client').connect port: 49259
client
  .once 'connect', () ->
    expr = '(shadow.cljs.devtools.api/repl :client)';
    client.eval expr, (err, result) ->
      console.log '%s => ', expr, err or result
      # client.end()
      setTimeout ->
        client.eval '(println (range 100))', (err, result) ->
          console.log 'result', err, result
        client.eval '(exists? js/window)', (err, result) ->
          console.log 'result', err, result
      , 1000

And it prints:

=>> coffee client.coffee
(shadow.cljs.devtools.api/repl :client) =>  [ { id: 'D27994C7-657B-451F-BE73-142F055B1AF8',
    out: 'To quit, type: :cljs/quit\n',
    session: '155cb6f5-d95b-464f-b9f2-97ab112f2558' },
  { id: 'D27994C7-657B-451F-BE73-142F055B1AF8',
    ns: 'cljs.user',
    session: '155cb6f5-d95b-464f-b9f2-97ab112f2558',
    value: '[:selected :client]' },
  { id: 'D27994C7-657B-451F-BE73-142F055B1AF8',
    session: '155cb6f5-d95b-464f-b9f2-97ab112f2558',
    status: [ 'done' ] } ]
result null [ { err:
     'Syntax error compiling at (REPL:1:1).\nUnable to resolve symbol: exists? in this context\n',
    id: '8914D014-2566-462F-AAF6-7C97EEFD024A',
    session: 'eaf13e73-4993-48bc-a1bf-b54e1efd3a95' },
  { ex: 'class clojure.lang.Compiler$CompilerException',
    id: '8914D014-2566-462F-AAF6-7C97EEFD024A',
    'root-ex': 'class clojure.lang.Compiler$CompilerException',
    session: 'eaf13e73-4993-48bc-a1bf-b54e1efd3a95',
    status: [ 'eval-error' ] } ]
result null [ { err:
     'Syntax error compiling at (REPL:1:1).\nUnable to resolve symbol: exists? in this context\n',
    id: '8914D014-2566-462F-AAF6-7C97EEFD024A',
    session: 'eaf13e73-4993-48bc-a1bf-b54e1efd3a95' },
  { ex: 'class clojure.lang.Compiler$CompilerException',
    id: '8914D014-2566-462F-AAF6-7C97EEFD024A',
    'root-ex': 'class clojure.lang.Compiler$CompilerException',
    session: 'eaf13e73-4993-48bc-a1bf-b54e1efd3a95',
    status: [ 'eval-error' ] },
  { id: '8914D014-2566-462F-AAF6-7C97EEFD024A',
    session: 'eaf13e73-4993-48bc-a1bf-b54e1efd3a95',
    status: [ 'done' ] } ]
result null [ { id: '8A3B2892-6845-4CA4-8CFC-2EC6421B3AB2',
    out:
     '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99)\n',
    session: 'fdfa1917-86cb-4168-a485-4f74910231f6' },
  { id: '8A3B2892-6845-4CA4-8CFC-2EC6421B3AB2',
    ns: 'shadow.user',
    session: 'fdfa1917-86cb-4168-a485-4f74910231f6',
    value: 'nil' },
  { id: '8A3B2892-6845-4CA4-8CFC-2EC6421B3AB2',
    session: 'fdfa1917-86cb-4168-a485-4f74910231f6',
    status: [ 'done' ] } ]

So the code I sent via nREPL is not running in the context of the webpage I was debugging. I must have misunderstood your APIs.

You misunderstood nREPL.

I don't know the exact client API but you are supposed to create a session (maybe via :clone?) and re-use that session for the following commands. Note how your example uses a couple different session ids? There should only be one, or maybe 2 in the beginning. Once you call (shadow.cljs.devtools.api/repl :foo) that session will be switched to CLJS and only that session. So if the client just creates a new session it will be in CLJ mode again and can't eval CLJS.

Turned out there are several steps required before I can call that API. The step involved:

  • create an nREPL connection with nrepl-client module.
  • call client.clone (err, result) -> to create a new session. also client.lsSessions (err, result) -> is the API to list sessions. Turned out the session used by API could be a temporary one. So we need to create one by ourselves.
  • call (shadow.cljs.devtools.api/repl :foo) in the created session with a namespace cljs.user.
  • finally we can eval code in the session with a namespace of the app, say app.main in my code...

Details of APIs in node-nrepl-client could be found on its README.

Was this page helpful?
0 / 5 - 0 ratings