Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 18, 2021 05:25 pm GMT

Talk to your computer in Javascript: the repl console

Premise

I often want to run ad hoc Javascript commands that rely on npm packages and custom classes I've written that work with a database/filesystem or wrap common logic.

Node comes with repl (Read-Eval-Print Loop), and you can launch a simple repl console by just running node with no arguments - the commands for it are documented in e.g. https://nodejs.org/api/repl.html#repl_design_and_features. That's quite handy - but falls short of a full-featured interactive shell that has access to all necessary packages.

The solution

Luckily, repl is available in node as a package ( https://nodejs.org/api/repl.html#repl_repl ) - so all that's necessary is to write a small script that starts a repl instance and pulls in everything you need.

You'll need to inject all the packages you want to use interactively into the repl console via a launcher script. It's also handy to configure repl in the script, and I show some examples below:

/*  I often use this console with projects that rely on env vars,  and I use dotenv to pull them in.  I think this is quite common, so I'm including this  as an opinionated example on how  to make the repl console environment aware.*/require("dotenv").config()/*  I also often use the repl console with projects that run  across multiple environments: development/production/staging.  It's helpful to be reminded where the console is running  to avoid unfortunate mistakes.*/console.log(`Starting console - ${process.env.NODE_ENV}`)const repl = require("repl")const util = require("util")const startConsole = async () => {  /*    The lines below configure output formatting for repl.    W/o specifying any output options, you'd get    formatting like    > a = {a: {b: {c: {d: {e: {f: {g: {h: 1}}}}}}}}    { a: { b: { c: [Object] } } }    With these options, you'd get    > a = {a: {b: {c: {d: {e: {f: {g: {h: 1}}}}}}}}    { a: { b: { c: { d: { e: { f: { g: { h: 1 } } } } } } } }    Note these options are the same as the options passed to inspect    https://nodejs.org/api/util.html#util_util_inspect_object_options  */  util.inspect.defaultOptions.depth = 20  util.inspect.defaultOptions.colors = true  util.inspect.defaultOptions.getters = true  util.inspect.defaultOptions.compact = true  /*    repl is supposed to use util.inspect to format by default.    However, w/o explicitly specifying {writer: util.inspect},    I was not able to get the options above to be successfully applied    for eval results formatting. They _do_ get applied to    console.log formatting, though, in either case.    You may want to specify other options - see    https://nodejs.org/api/repl.html#repl_repl_start_options    for a comprehensive list - e.g. {prompt: "xyz>"} is a handy one.  */  const replServer = repl.start({writer: util.inspect})  /*    Pull in any number of modules here - these are the    modules that will be available to you in the repl instance.  */  const modules = ["util", "fs"]  modules.forEach((moduleName) => {    replServer.context[moduleName] = require(moduleName)  })  /*    This is not necessary in newer versions of node,    but in older versions I wasn't able to pull in    ad-hoc modules to a running repl instance w/o it.  */  replServer.context.require = require}startConsole()

The way I personally set it up is by having all the things my application cares about available as a single module defined in my application - including both npm packages and my own library/reusable code.

I use this single module in application code, scripts, background jobs, and also in the repl console - that way accessing functionality looks the same in all contexts, and I can easily memorize commands and have them at my fingertips.

My script ends up looking more like this:

require("dotenv").config()console.log(`Starting console - ${process.env.NODE_ENV}`)const repl = require("repl")const util = require("util")/*  This contains all the modules I want to pull in*/const lib = require("../lib.js")const startConsole = async () => {  /*    E.g. establish connections to various databases...  */  await lib.init()  util.inspect.defaultOptions.depth = 20  util.inspect.defaultOptions.colors = true  util.inspect.defaultOptions.getters = true  util.inspect.defaultOptions.compact = true  const replServer = repl.start({writer: util.inspect})  for(key of Object.keys(lib)) {    replServer.context[key] = lib[key]  }}startConsole()

Starting the console

I usually start the script through npm/yarn, via package.json:

...  "scripts": {    ...    "console": "node --experimental-repl-await ./scripts/console.js"    ...  },...

I love --experimental-repl-await (https://nodejs.org/api/cli.html#cli_experimental_repl_await - added in Node.js 10.0.0), and I hope it makes its way out of experimental soon. It allows awaiting on async commands in the repl console. Without it, working with promises is quite annoying.

After that's in, it's just yarn run console or npm run console.

Working with the console

yarn run console> console.log("Hello world")Hello worldundefined> moment = require('moment')> moment.now()1618688628781

Note how console.log("...") produces 2 lines as output. It performs its side effect of printing and returns a value - and repl will print the result of each expression it evaluates. For example, variable declarations return undefined, but variable assignments return the assigned value:

> let a = 1undefined> a = 22

That's handy to know if you want to skip printing the output of some expression.

In most cases, I tend to avoid using variable declarations in repl, since you can assign a variable without declaring it. The reason is that I often copy-paste sections of code from a text editor, and variable declarations are not re-runnable. In application code I'll usually use const, but in repl that locks you out from fixing mistakes, especially with e.g. function declarations.

> let a = 1undefined> let a = 1Uncaught SyntaxError: Identifier 'a' has already been declared> b = 11> b = 11

Persistent history

Repl supports bi-directional reverse-i-search similar to zsh. I.e. you can search back through your history by pressing ctrl+r (or ctrl+s to search forward) - which makes preserving history between runs potentially very worth it.

History is preserved in a file, so you'll need to choose where to store it. I store it in a .gitignored folder in my project. E.g. the default node.js repl console stores history by default, in your home folder in .node_repl_history ( https://nodejs.org/api/repl.html#repl_persistent_history ).

Here's the code for enabling persistent command history - the path is relative to the root of the project ( https://nodejs.org/api/repl.html#repl_replserver_setuphistory_historypath_callback ):

replServer.setupHistory("./no_commit/repl_history", () => {  console.log("Loaded history!")})

I add this at the end of the startConsole() function above, adding the environment as the filename suffix:

require("dotenv").config()console.log(`Starting console - ${process.env.NODE_ENV}`)const repl = require("repl")const lib = require("../index.js")const util = require("util")const startConsole = async () => {  await lib.init()  util.inspect.defaultOptions.depth = 20  util.inspect.defaultOptions.colors = true  util.inspect.defaultOptions.getters = true  util.inspect.defaultOptions.compact = true  const replServer = repl.start({    writer: util.inspect,    prompt: "> "  })  for(key of Object.keys(lib)) {    replServer.context[key] = lib[key]  }   const historyPath = `./no_commit/repl_history_${process.env.NODE_ENV}`  replServer.setupHistory(historyPath, () => {})}startConsole()

Conclusion

It's quite easy to set up an interactive Javascript shell based on Node's REPL module. It can be configured flexibly, have access to application logic, and any installed npm modules.

Unlike a Chrome console, it can be used to run arbitrary commands on your computer (or a remote computer), and not just for working with a particular application - hence the title of this article.


Original Link: https://dev.to/alexeydc/talk-to-your-computer-in-javascript-the-repl-console-4l4i

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To