Giter Club home page Giter Club logo

jay's Introduction

Jay Build Status npm

Supercharged JavaScript REPL ๐Ÿ˜Ž

Jay is a terminal-based JavaScript REPL focused on increasing prototyping speed and productivity. It's packed with modern REPL features, whilst also striving to maintain a familiar REPL vibe.

Here are the most important features that differentiate Jay from other REPL's:

  • require modules directly from the registry
  • Eager eval (requires node >= 12.3.0)
  • Top level await
  • Typeahead + dropdown menu-style completion

Plus some necessities:

  • Colored input
  • Bracket/quote pair completion
  • Fresh require
  • Full readline keybindings support
  • Lazy loaded built-in modules
  • _ variable

Why

Jay was created with two goals in mind:

  1. To bring modern JavaScript REPL features to the terminal, like eager eval, top level await and typeahead code completion
  2. To create a super quick environment for prototyping/running npm dependent JavaScript code.

It would probably make sense to split Jay into separate packages (just the REPL into one, and the smart require into another) in the future, to allow better reusability of the REPL components.

Examples

Basic web scraping

Let's say that for some reason we want to scrape all of the titles and links from the hacker news front page.

To accomplish that, we can use Jay (obviously), got (http client) and cheerio (jQuery-esque API for server-side).

Let's begin by running Jay and getting the necessary dependencies:

$ jay
> const got = require('got')
> const cheerio = require('cheerio')

Then, we download the page and load the HTML into cheerio:

> const {body} = await got('https://news.ycombinator.com')
> const $ = cheerio.load(body)

$ behaves pretty much like jQuery, we can use the following simple one-liner to get our result:

> $('a.storylink').map((i, el) => ({text: $(el).text(), link: $(el).attr('href')})).get()
[
	{
		text: 'National Park Typeface',
		link: 'https://nationalparktypeface.com'
	},
	...
]

After running the previous line, we can store the result in a named variable via _ - which caches the result of the last evaluation - as follows:

> const result = _
> result.length // 30

If you find an interesting example to put in this section, a PR is more than welcome!

Install

Jay expects itself to be installed globally:

$ npm install -g jay-repl

Then simply run it by typing jay in the terminal:

$ jay

Alternatively, Jay can be directly run with Node builtin npx:

$ npx -p jay-repl jay

FAQ

How does the smart require function work?

After pressing enter in the prompt, Jay parses the entered input into an AST using acorn and looks for all CallExpression's whose names are equal to require.

This triggers the "asker" system to ask the user whether they actually want to install a given require'd module. If the user decides to install the module, Jay starts a "global" npm install in its cache directory. If they don't, nothing happens and the evaluation will most likely result in an "module not found" error.

Either way, after all of the above is complete, the control is handed back to the evaluator which only now actually executes the entered line.

How does Jay's require differ from the normal one?

The normal require only looks for modules within two places:

  • locally, if the module id is prefixed with things like . or ../ - e.g. require('./index.js')
  • in node_modules - e.g. require('express')

Jay's require in addition to the above also looks within its global cache (but only if the local & node_modules resolutions fail). This, in addition to parsing the input and looking for require calls, allows for importing any module that's on the registry, automatically installing it if needed.

The require also is also a "fresh require".

What does "fresh require" mean?

The require function in Jay doesn't use the standard node's cache and always reads the files from disk upon importing them. Consider the following example:

Let's say we have a file called greet.js with the following contents:

module.exports = x => 'hello '.repeat(x)

Start up node's repl, require it, and we get the expected output:

$ node
> greet = require('./greet')
> greet(2)
// 'hello hello '

Now, without closing the session, we change the file into:

-module.exports = x => 'hello '.repeat(x)
+module.exports = x => 'hi '.repeat(x)

Requiring the file again will, unfortunately, not change the output:

> greet = require('./greet')
> greet(3)
// 'hello hello hello '

Jay, as beforementioned, doesn't cache modules. Repeating the steps yields the result we actually want in this case:

$ jay
$ echo "module.exports = x => 'hello '.repeat(x) > greet.js"
> greet = require('./greet')
> greet(2)
// 'hello hello '
$ sed -i 's/hello/hi/' greet.js
// (in the same Jay session)
> greet = require('./greet')
> greet(3)
// 'hi hi hi '

This also works analogically with modules in node_modules, Jay's cache, JSON files, etc.

Where does Jay store the cached modules?

Jay uses env-paths to determine the cache's location:

  • MacOS - ~/Library/Caches/jay-repl-nodejs/packages
  • Linux - ~/.cache/jay-repl-nodejs
  • Windows - ~/

You can see the exact location of the cache by simply running the following line in Jay:

> require('env-paths')('jay-repl').cache

Contributing

  1. Fork & clone the repository
  2. Start the Typescript watch script:
$ npm run build:watch
  1. Make your changes
  2. Try out your changes on your local build
$ node dist/cli.js
  1. Run the tests:
$ npm test
  1. Commit & PR!

This repository uses Git LFS for storing readme's gifs, if you want to view them locally you will need to install and set up the Git LFS extension on your machine.

License

MIT ยฉ nikersify

jay's People

Contributors

rsify avatar tomsquest avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jay's Issues

Evaluating `new Promise` crashes the program

Entering the following causes jay to exit immediately (without an error):

p = new Promise(() => {})

It works fine in node's REPL:

p = new Promise(() => {}) // => Promise { <pending> }

Possibly Related

Environment

  • jay: 0.2.2
  • node: v12.13.0
  • OS: Linux (Arch)

Add configuration option to disable pairing quotes and parenthesis pairing autocompletion

I find very uncomfortable to have forced auto-pairing completion.

An example that makes me make mistakes and/or type more instead of less:

Step 1: Date.parse("2019-07-31T22:13:54.697Z")
Step 2: now I want to check it, so I go to the beginning of the line, and type isNaN( and after the first parenthesis, jay autocompletes for me the second, leaving me with: isNaN()Date.parse("2019-07-31T22:13:54.697Z")

Now I have to delete the bad closing parenthesis. Some times is even worst since I forget about the autocompletion and go to the end to add the second one, and press ENTER, just to notice syntax is wrong.

Another way this results uncomfortable is when you paste something which gets auto-paired, causing bad syntax again.

I would vote to completely remove the feature, since I find it's not useful at all, but a configuration option to disable it would be ok too.

Error: Cannot find module 'fp-ts/lib/Either' (Linux, node 10)

Error: Error: Cannot find module 'fp-ts/lib/Either'

Stack:

$ node -v                                
v10.16.0

$ npm i -g jay-repl     
/home/tom/.nvm/versions/node/v10.16.0/bin/jay -> /home/tom/.nvm/versions/node/v10.16.0/lib/node_modules/jay-repl/dist/cli.js
npm WARN [email protected] requires a peer of fp-ts@^1.0.0 || ^2.0.0-rc.6 but none is installed. You must install peer dependencies yourself.

+ [email protected]
added 169 packages from 362 contributors in 2.182s

$ jay
internal/modules/cjs/loader.js:638
    throw err;
    ^

Error: Cannot find module 'fp-ts/lib/Either'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)
    at Function.Module._load (internal/modules/cjs/loader.js:562:25)
    at Module.require (internal/modules/cjs/loader.js:690:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (/home/tom/.nvm/versions/node/v10.16.0/lib/node_modules/jay-repl/node_modules/io-ts/lib/index.js:27:16)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)

jay doesn't create its own cache directory

Installing Jay on a fresh install and then running it yields:

Error: ENOENT: no such file or directory, open '/Users/peter/Library/Caches/jay-repl-nodejs/history'
    at Object.openSync (fs.js:447:3)
    at Object.createHistorian (/usr/local/lib/node_modules/jay-repl/dist/history.js:12:41)

mkdir /Users/peter/Library/Caches/jay-repl-nodejs resolved the situation, but I assume this should be happening without my intervention.

autocomplete new definitions just typed

jay looks promising, normally we will create new variables etc under jay but jay seems ignore them for autocomplete, it will be nice is jay can remember any new definitions in current session and provide autocomplete for them on the go, so I don't need retype "superlongvariable" after the first time I did "let superlongvarible", instead jay will help to autocomplete it

Doing {{}} crashes the program

I was just tinkering with JavaScript objects, and I decided to make an object {{}}. This caused the program to crash.

> {{}} 
(node:13519) UnhandledPromiseRejectionWarning: evalmachine.<anonymous>:1
({{}})
  ^

SyntaxError: Unexpected token '{'
    at new Script (vm.js:99:7)
    at Object.<anonymous> (/Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/eval.js:92:24)
    at Generator.next (<anonymous>)
    at /Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/eval.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/eval.js:3:12)
    at evaluate (/Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/eval.js:90:32)
    at /Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/cli.js:148:47
    at Generator.next (<anonymous>)
    at fulfilled (/Users/example/.npm/_npx/13516/lib/node_modules/jay-repl/dist/cli.js:5:58)
(node:13519) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:13519) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I tested this in the normal node repl, and it left undefined as expected.

> {{}}
undefined

Related: #24

Exit jay

Hi,

I'm loving jay, but have only just figured out how to exit via process.exit().

In my node repl it will respond to:

  • Ctrl + C
  • exit()

It'd be sweet if jay could do this too. I'm happy to have a look at making this work if you think it'd be accepted.

Sam

Running 'jay' hangs for ~3 minutes when running behind network proxy on Windows

Simply running jay from the console on Windows hangs for about 3 minutes before eventually displaying the node, npm, and jay-repl versions (and prompt). This only happens when behind a network proxy. I've done some investigation and the code that causes this to hang is the following line in cli.ts.

version('npm', execa.sync('npm', ['-v']).stdout)

It appears that this is caused by a "bug" in npm. Apparently, version 4.4.0 of npm added an update check feature. When running npm commands via child_process the network proxy is not observed and thus a network timeout occurs after 3 minutes.

A workaround for this is to set the NO_UPDATE_NOTIFIER environment variable to false. I tested the following code that resolves this issue:

function getNpmVersion() {
	const env = {
		...process.env,
		NO_UPDATE_NOTIFIER: "true",
	};

	const result = execa.sync('npm', ['-v'], { env });

	return result.stdout;
}

Could you fix this? I tried cloning the repo to do a pull request but I am getting errors doing that too (which also appear to be network proxy related!).

node v10.13.0
npm v6.4.1
jay-repl v0.2.2

Thanks,
Jonathan

Multiline commands aren't supported

It looks like I can't enter a multiline command in jay, e.g. a for loop:

> for (let applicant of applicants) { 
SyntaxError: Unexpected token (2:1)
> for (var i = 1; i < 10; i++) { 
SyntaxError: Unexpected token (2:1)

The same works fine in node REPL:

> const applicants = [1,2,3]
undefined
> for (let applicant of applicants) {
... console.log(applicant)
... }
1
2
3
undefined
> for (var i = 0; i < 3; i++) {
... console.log(i)
... }
0
1
2
undefined

Error: process.stdin.setRawMode does not exist

I can't start jay

When I first tried starting it, I got this:

$ npx -p jay-repl jay
npx: installed 175 in 19.921s
node v10.9.0 [email protected] [email protected]
jay is getting plugin support! Help us make the API suit you: https://github.com/nikersify/jay/pull/18
(node:20672) UnhandledPromiseRejectionWarning: Error: process.stdin.setRawMode does not exist
    at resolve (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\prompt.js:67:19)
    at new Promise (<anonymous>)
    at Object.promptLine [as default] (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\prompt.js:63:12)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\cli.js:112:66
    at Generator.next (<anonymous>)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\cli.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\cli.js:4:12)
    at processPrompt (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\cli.js:111:20)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\36004\node_modules\jay-repl\dist\cli.js:172:9
(node:20672) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:20672) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Thought that maybe it was because I was on an old version of Node, so I upgraded. Still getting the same error :(

$ npx -p jay-repl jay
npx: installed 175 in 15.339s
node v13.0.1 [email protected] [email protected]
jay is getting plugin support! Help us make the API suit you: https://github.com/nikersify/jay/pull/18
(node:35284) UnhandledPromiseRejectionWarning: Error: process.stdin.setRawMode does not exist
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\prompt.js:67:19
    at new Promise (<anonymous>)
    at Object.promptLine [as default] (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\prompt.js:63:12)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\cli.js:112:66
    at Generator.next (<anonymous>)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\cli.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\cli.js:4:12)
    at processPrompt (C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\cli.js:111:20)
    at C:\Users\tobbe\AppData\Roaming\npm-cache\_npx\27480\node_modules\jay-repl\dist\cli.js:172:9
(node:35284) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:35284) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Windows 10, git-bash

Requiring built-in modules throws an error

Not sure if I'm missing something here - requiring standard NPM modules works as expected (such as const axios = require('axios')) but any built-in module such as fs or querystring fails:

node v12.4.0 [email protected] [email protected]
Type `> jay.help()` in the prompt for more information.
> const fs = require('fs')
C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\moduler.js:158
            throw new Error(`\`${id}\` has an invalid \`package.json\` file`);
            ^

Error: `fs` has an invalid `package.json` file
    at C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\moduler.js:158:19
    at Left.getOrElseL (C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\fp-ts\lib\Either.js:127:16)
    at decodePkg (C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\moduler.js:157:13)
    at _resolve (C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\moduler.js:180:21)
    at _require (C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\moduler.js:191:39)
    at evalmachine.<anonymous>:1:12
    at Script.runInContext (vm.js:134:20)
    at Object.<anonymous> (C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\eval.js:94:41)
    at Generator.next (<anonymous>)
    at C:\Users\Chris\AppData\Local\Yarn\Data\global\node_modules\jay-repl\dist\eval.js:7:71
>

I'm on Windows 10 and installed jay globally with Yarn.

Support save context

after exist all previous work is gone. can you support some feature to save the context?

Command history persistence

Hi.
Does it store command history in a file anywhere? Node.js REPL, for example, saves the commands in a file at ~/.node_repl_history and loads it on the next invocation of a REPL, so you get a persistent history of the commands entered.

Support .exit

I've gotten accustomed to typing .exit in the Node REPL in order to exit. Frankly, I realize this is a bad habit compared to just using ctrl-d (many more keystrokes are required) but I was wondering if it would be possible for jay to support .exit.

image

failing to run jay in windows cmd

I installed globally and when I run the cmd jay I got the following error on windows.

....\system32>jay
...\jay-repl\dist\eval.js:75
template.console = new console_1.Console(process.stdout, process.stdin);
^

TypeError: Cannot set property console of # which has only a getter
at Object.createEvaluator (C:\Users\ARME-HYD-01\AppData\Roaming\npm\node_modules\jay-repl\dist\eval.js:75:22)
at main (C:\Users\ARME-HYD-01\AppData\Roaming\npm\node_modules\jay-repl\dist\cli.js:91:56)
at Object. (C:\Users\ARME-HYD-01\AppData\Roaming\npm\node_modules\jay-repl\dist\cli.js:160:1)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:191:16)

Feature Request: Allow usage via code

It would be nice if we could programmatically call the jay repl and also inject our own variables inside. Basically emulate the node repl library but with the features of Jay. Would this be possible?

Throws when starting

System

macOS 10.14.6 (18G84)
Node.js v10.16.0
yarn 1.17.3

Steps

  1. yarn global add jay-repl
  2. jay

Expected

REPL starts.

Actual

Error: ENOENT: no such file or directory, open '/Users/moeriki/Library/Caches/jay-repl-nodejs/history'
    at Object.openSync (fs.js:443:3)
    at Object.createHistorian (/Users/moeriki/.config/yarn/global/node_modules/jay-repl/dist/history.js:12:41)
    at main (/Users/moeriki/.config/yarn/global/node_modules/jay-repl/dist/cli.js:94:33)
    at Object.<anonymous> (/Users/moeriki/.config/yarn/global/node_modules/jay-repl/dist/cli.js:170:1)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.