187

I have a script foo.js that contains some functions I want to play with in the REPL.

Is there a way to have node execute my script and then jump into a REPL with all the declared globals, like I can with python -i foo.py or ghci foo.hs?

hugomg
  • 68,213
  • 24
  • 160
  • 246

12 Answers12

233

There is still nothing built-in to provide the exact functionality you describe. However, an alternative to using require it to use the .load command within the REPL, like such:

.load foo.js

It loads the file in line by line just as if you had typed it in the REPL. Unlike require this pollutes the REPL history with the commands you loaded. However, it has the advantage of being repeatable because it is not cached like require.

Which is better for you will depend on your use case.


Edit: It has limited applicability because it does not work in strict mode, but three years later I have learned that if your script does not have 'use strict', you can use eval to load your script without polluting the REPL history:

var fs = require('fs');
eval(fs.readFileSync('foo.js').toString())
vossad01
  • 11,552
  • 8
  • 56
  • 109
  • What if I want to drop into the repl inside of an async callback? – Chet Aug 20 '17 at 20:07
  • 3
    @Chet You write up a new StackOverflow question if your question does not match an existing one :-) – vossad01 Aug 21 '17 at 20:34
  • @Chet you can .load another file with (async()=>{ more code })(); and it will share the same globals. – nurettin Sep 19 '18 at 11:53
  • 1
    Tip if you're on macOS (maybe others as well). You can type ".load " (note the space) into the REPL and drag/drop the file into the Terminal from the Finder to add the correct path to your command. This is handy if the files you're working with are several levels down. – jamesnotjim Jun 05 '19 at 16:00
  • I needed to declare `__dirname` to make it work. Also, I wrote the code into a shell script (for the sake of reusability). Otherwise, great solution! – Dave F Apr 07 '21 at 22:15
  • @vossad01 and then watch it get marked as a duplicate anyway. – James M. Lay Apr 20 '21 at 02:04
67

i always use this command

node -i -e "$(< yourScript.js)"

works exactly as in Python without any packages.

George Shalvashvili
  • 1,263
  • 13
  • 21
  • 2
    does anyone know how to get this working in windows cmd? I got it working in bash, but not windows. – Sharpiro Apr 19 '18 at 14:28
  • 1
    @Sharpiro: If you install Git then you have an option to install a mini-UNIX into your Windows PC. I mean Git's normal distribution for Windows. – Juan Lanus Sep 06 '18 at 18:51
  • The one thing that's annoying about this is Node.js will print the repl prompt and _then_ run your script, so any output gets stuck after the prompt. https://stackoverflow.com/a/45893494/3538165 doesn't have this problem but for that solution functions must be assigned explicitly to variables to end up in the repl namespace, so not very good either. – Resigned June 2023 Jun 07 '20 at 22:34
  • This is the correct answer to the question. Super helpful. Thanks @George – Vinn Jul 30 '22 at 17:08
13

I made Vorpal.js, which handles this problem by turning your node add into an interactive CLI. It supports a REPL extension, which drops you into a REPL within the context of your running app.

var vorpal = require('vorpal')();
var repl = require('vorpal-repl');

vorpal
  .delimiter('myapp>')
  .use(repl)
  .show()
  .parse(process.argv); 

Then you can run the app and it will drop into a REPL.

$ node myapp.js repl
myapp> repl: 
dthree
  • 19,847
  • 14
  • 77
  • 106
12

Another way is to define those functions as global.

global.helloWorld = function() { console.log("Hello World"); }

Then preload the file in the REPL as:

node -r ./file.js

Then the function helloWorld can be accessed directly in the REPL.

Ashish Chaudhary
  • 798
  • 5
  • 12
11

Here's a bash function version of George's answer:

noderepl() {
    FILE_CONTENTS="$(< $1 )"
    node -i -e "$FILE_CONTENTS"
}

If you put this in your ~/.bash_profile you can use it like an alias, i.e.:

noderepl foo.js
Eliot
  • 5,450
  • 3
  • 32
  • 30
  • 2
    I've been using this for months now, and in a transition to a new shell environment I lost some of my settings and had to track this back down again. So since I'm here, I thought I'd thank you for this really useful function. – Xaekai Feb 19 '19 at 02:44
8

I created replpad since I got tired of reloading the script repeatedly.

Simply install it via: npm install -g replpad

Then use it by running: replpad

If you want it to watch all files in the current and all subdirectories and pipe them into the repl when they change do: replpad .

Check out the videos on the site to get a better idea of how it works and learn about some other nice features that it has like these:

  • access core module docs in the repl via the dox() function that is added to every core function, i.e. fs.readdir.dox()
  • access user module readmes in the repl via the dox() function that is added to every module installed via npm, i.e. marked.dox()
  • access function's highlighted source code, info on where function was defined (file, linenumber) and function comments and/or jsdocs where possible via the src property that is added to every function, i.e. express.logger.src
  • scriptie-talkie support (see .talk command)
  • adds commands and keyboard shortcuts
  • vim key bindings
  • key map support
  • parens matching via match token plugin
  • appends code entered in repl back to file via keyboard shortcut or .append command

See: https://github.com/thlorenz/replpad

Rimian
  • 36,864
  • 16
  • 117
  • 117
Thorsten Lorenz
  • 11,781
  • 8
  • 52
  • 62
  • I had to `CXX=clang++ npm install replpad` to work around the error `g++: error: unrecognized command line option '-stdlib=libc++'` – ShadSterling Feb 11 '18 at 20:11
  • But then when I run it it fails with ```# # Fatal error in ../deps/v8/src/api.cc, line 1248 # Check failed: !value_obj->IsJSReceiver() || value_obj->IsTemplateInfo(). # Illegal instruction: 4``` – ShadSterling Feb 11 '18 at 20:16
7

Why not load the file into an interactive node repl?

node -h
-e, --eval script          evaluate script
-i, --interactive          always enter the REPL even if stdin

node -e 'var client = require("./build/main/index.js"); console.log("Use `client` in repl")' -i

Then you can add to package.json scripts

"repl": "node -e 'var client = require(\"./build/main/index.js\"); console.log(\"Use `client` in repl\")' -i",

tested using node v8.1.2

random-forest-cat
  • 33,652
  • 11
  • 120
  • 99
5

Currently you can't do that directly, but you can mylib = require('./foo.js') in the REPL. Remember methods are exported, not declared as globals.

Ricardo Tomasi
  • 34,573
  • 2
  • 55
  • 66
  • 1
    I find this preferable to `.load my_work.js`, despite requiring some additional `exports.working_var = ...` declarations, because the REPL barfs on some kinds of perfectly valid javascript, like multiline comments (at least with my `readline` configuration). – chbrown Mar 25 '15 at 22:22
4

replpad is cool, but for a quick and easy way to load a file into node, import its variables and start a repl, you can add the following code to the end of your .js file

if (require.main === module){
    (function() {
        var _context = require('repl').start({prompt: '$> '}).context;
        var scope = require('lexical-scope')(require('fs').readFileSync(__filename));
        for (var name in scope.locals[''] )
            _context[scope.locals[''][name]] = eval(scope.locals[''][name]);
        for (name in scope.globals.exported)
            _context[scope.globals.exported[name]] = eval(scope.globals.exported[name]);
    })();
}

Now if your file is src.js, running node src.js will start node, load the file, start a REPL, and copy all the objects declared as var at the top level as well as any exported globals. The if (require.main === module) ensures that this code will not be executed if src.js is included through a require statement. I fact, you can add any code you want to be excuted when you are running src.js standalone for debugging purposes inside the if statement.

yegodz
  • 5,031
  • 2
  • 17
  • 18
3

Another suggestion that I do not see here: try this little bit of code

#!/usr/bin/env node
'use strict';

const repl = require('repl');
const cli = repl.start({ replMode: repl.REPL_MODE_STRICT });
cli.context.foo = require('./foo'); // injects it into the repl

Then you can simply run this script and it will include foo as a variable

corvid
  • 10,733
  • 11
  • 61
  • 130
2

Old answer

type test.js|node -i

Will open the node REPL and type in all lines from test.js into REPL, but for some reason node will quit after file ends

Another problem is, that functions will not be hoisted.

Better answer

node -e require('repl').start({useGlobal:true}); -r ./test2.js

Then all globals declared without var within test2.js will be available in the REPL

not sure why var a in global scope will not be available

Community
  • 1
  • 1
Sam Washington
  • 626
  • 4
  • 12
  • 8
    Please [add](https://stackoverflow.com/posts/55347069/edit) some explanaton to your answer – mechnicov Mar 25 '19 at 22:21
  • Seems like `type` doesn't work in `bash`. For the better answer, seems like you can just do `node -r noderc.js` without starting the repl manually. But you have to assign to `global` directly in the script for functions/var. There should be a way to appropriate "vm.runInContext" in order to take more values from the script. – Gavin Haynes Sep 11 '20 at 16:52
  • `cat` is `type` in bash/posix – Sam Washington Dec 10 '20 at 05:05
-1

There is an Official Node.js REPL that supports also async methods

console.js

const repl = require('repl')
const mongoose = require('mongoose')

const run = async () => {
    await mongoose.connect(process.env.DB_URL, {
        useNewUrlParser: true,
        useUnifiedTopology: true
    })

    const r = repl.start(`(${process.env.NODE_EN}) ⚡️ `)
    r.context.User = require('./src/models/user.model')
    r.context.mongoose = mongoose
    console.log(`Ready `);
}

run()

Start the console:

NODE_OPTIONS=--experimental-repl-await node console.js

User model its exposed to console

await User.find({})

source

msroot
  • 817
  • 11
  • 13