Search code examples
javascriptnode.jsemacsecmascript-6read-eval-print-loop

REPL Driven Development in Node.js


In languages like Clojure and Scheme I do really enjoy writing code in REPL-driven mode, when you write a piece of code in an editor (Emacs in my case), send it to your REPL, play with it then go back to an editor, fix found issues and send code to REPL again.

I tried to do the same with Node.js, and it kind of works if I limit myself to usage of ES5 syntax only. But if I use ES6 features like const, let and class, I expectedly get errors on re-evaluation of my declarations:

> let foo = 1;
> let foo = 2;
TypeError: Identifier 'foo' has already been declared

Is there any Node.js REPL params, or maybe patched REPLs, or even some magical Emacs mode which will purge existing declarations when I re-evaluate my code? So that I will be able to write Node.js code this way without the need to constantly think about which syntax I am using and/or the need to restart REPL manually on each re-evaluation.


Solution

  • if you are using nodejs-repl, you can eval (M-:) the following code :

    (with-current-buffer "*nodejs*" 
      (setq kill-buffer-query-functions (delq 'process-kill-buffer-query-function kill-buffer-query-functions))
      (kill-process nil comint-ptyp) 
      (kill-buffer-and-window) 
      (run-with-timer 0.01 nil (lambda () (nodejs-repl))
    ))
    

    This is not an optimal solution, but here's how it works :

    • It evals emacs lisp code in the current buffer
    • It kills the function that ask you if you want to kill the window (confirmation in the minibuffer)
    • It stops the process that allows the communication between nodejs and emacs, so it kills nodejs process (comint-ptyp)
    • It re-runs nodejs-repl

    If you want to run it with another REPL, just change the buffer name and the command to 're-run'.

    Hope it helps,

    Best regards


    EDIT

    Here's a more suitable solution, add this to your .emacs or to the nodejs-repl.el :

    (defun nodejs-repl-restart ()
      "restart the nodejs REPL"
      (interactive)
      (defvar nodejs-repl-code
        (concat "process.stdout.columns = %d;" "require('repl').start('%s', null, null, true, false)"))
      (with-current-buffer "*nodejs*"
        (kill-process nil comint-ptyp)
        (run-with-timer 0.01 nil (lambda ()
                      (setq nodejs-repl-prompt-re (format nodejs-repl-prompt-re-format nodejs-repl-prompt nodejs-repl-prompt))
                      (with-current-buffer "*nodejs*"
                    (apply 'make-comint nodejs-repl-process-name nodejs-repl-command nil `("-e" ,(format nodejs-repl-code (window-width) nodejs-repl-prompt)))
                    (nodejs-repl-mode) (erase-buffer) ))))) 
    

    It almost does the same, but it doesn't kill the buffer and it erase it.