Node repl with async await - node.js

I would like to add support to async/await to node repl
Following this issue: https://github.com/nodejs/node/issues/8382
I've tried to use this one https://github.com/paulserraino/babel-repl but it is missing async await suppport
I would like to use this snippet
const awaitMatcher = /^(?:\s*(?:(?:let|var|const)\s)?\s*([^=]+)=\s*|^\s*)(await\s[\s\S]*)/;
const asyncWrapper = (code, binder) => {
let assign = binder ? `root.${binder} = ` : '';
return `(function(){ async function _wrap() { return ${assign}${code} } return _wrap();})()`;
};
// match & transform
const match = input.match(awaitMatcher);
if(match) {
input = `${asyncWrapper(match[2], match[1])}`;
}
How can I add this snippet to a custom eval on node repl?
Example in node repl:
> const user = await User.findOne();

As of node ^10, you can use the following flag when starting the repl:
node --experimental-repl-await
$ await myPromise()

There is the project https://github.com/ef4/async-repl:
$ async-repl
async> 1 + 2
3
async> 1 + await new Promise(r => setTimeout(() => r(2), 1000))
3
async> let x = 1 + await new Promise(r => setTimeout(() => r(2), 1000))
undefined
async> x
3
async>
Another option, slightly onerous to start but with a great UI, is to use the Chrome Devtools:
$ node --inspect -r esm
Debugger listening on ws://127.0.0.1:9229/b4fb341e-da9d-4276-986a-46bb81bdd989
For help see https://nodejs.org/en/docs/inspector
> Debugger attached.
(I am using the esm package here to allow Node to parse import statements.)
Then you go to chrome://inspect in Chrome and you will be able to connect to the node instance. Chrome Devtools has top-level await, great tab-completion etc.

The idea is to preprocess the command and wrap it in a async function if
there is an await syntax outside async function
this https://gist.github.com/princejwesley/a66d514d86ea174270210561c44b71ba is the final solution

Related

run onPrepare function synchronously in Jasmine, before executing specs

Hello stackoverflow community,
The task
I'm running Jasmine tests programmatically using jasmine.execute(). My task is to run onPrepare function, where I do some setup work like setting up reporters etc, and I need that function to be synchronous (has to be finished before running specs)
Approach #1
The first approach I tried was to just declare an async onPrepare function, which also includes the code for specifying reporters, and then do
await onPrepare();
await jasmine.execute();
Problem
In the result I get jasmine.getEnv() is not a function. I assume because getEnv() becomes available as .execute() is ran. So I understand this won't work
Approach #2
The next thing I tried was to create a helper file with my sync code, specify it in the config and run jasmine.execute();.
So, if simplified, I have
// conf.js
(async () => {
let Jasmine = require('jasmine');
let jasmine = new Jasmine();
let variables = require("./variables.json");
let {spawn} = require("child_process");
let childProcess = spawn(variables.webdriver);
console.log(`Webdriver started, pid: ${childProcess.pid}`);
jasmine.exitOnCompletion = false;
jasmine.loadConfig({
'spec_files': ['specs/*.spec.js'],
'helpers': ['on-jasmine-prepare.js'],
'stopSpecOnExpectationFailure': false,
'random': false,
})
const result = await jasmine.execute();
console.log('Test status:', result.overallStatus);
console.log('Closing library and webdriver process');
await library.close();
await childProcess.kill();
console.log('webdriver killed:', childProcess.killed);
})()
// on-jasmine-prepare.js
(async () => {
const {SpecReporter} = require("jasmine-spec-reporter");
const library = require("./library/library");
const variables = require("./variables.json");
const errorHandler = require("./modules/on-error-handler");
jasmine.getEnv().clearReporters();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3 * 60 * 1000;
jasmine.getEnv().addReporter(new SpecReporter({}))
global.library = new library.Library(variables.IP);
console.log('library instantiated')
await library.deleteSessions();
console.log('sessions deleted')
await library.launch(library.home);
console.log('home page launched')
jasmine.getEnv().addReporter(
errorHandler(library)
)
console.log('debugger reporter added' )
})();
Problem
The problem that I noticed that the helper file is executed asynchronously with the specs and I get a spec error before the helper function finishes (basically because of race condition). Below the output example, where you can see some console.log from onPrepare was ran after spec started
Webdriver started, pid: 40745
library instantiated
Jasmine started
sessions deleted
Example
✗ App is loaded (7 secs)
- Error: Session already exist
home page launched
debugger reporter added
The question
How do I run onPrepare function synchronously, before specs? Preferably natively (using only jasmine capabilities). Otherwise maybe using third party packages. I know it's possible because #protractor had this feature, however I couldn't back-engineer it
MacOS
node v16.13.2
jasmine v4.1.0
Thank you

Why webdriverio can't find element?

await this.browser.url(url);
await this.browser.pause(5000);
const link = await $('a');
It opens the URL but after a pause I get the following error:
TypeError: Cannot read property 'isDevTools' of undefined
how to fix this?
Just need to use $ as browser object funtion
const link = await this.browser.$('a');
For anyone else seeing this issue, I later realized I was importing the $ command from the package, which was not correct. It was added without my knowledge – I think VS Code auto-imported while writing more tests.
import $ from 'webdriverio/build/commands/browser/$'; <<-- Removed this and it worked fine.
describe('Connect Apps / Manage Apps', async () => {
before(async () => {
const elem = await $('.results');
await elem.waitForDisplayed(5000, true);
});

How to read a File which contains keywords in Nodejs

I have a Question ?
I have a File Which contains one keyword on each line (5000),
I'm developing a Scraper Using Puppeteer in Nodes Which will go to a website which has a Search Bar and in that Search bar I want to search Using the Keywords from that File, So Someone Please Guide me How to accomplish this ? am I Using the right tools ? I would be grateful .
You can use:
// yourVariable is the text data.
if(yourVariable.contains("What you are looking for.")){
// the code
}
If that is what you mean.
Otherwise, if you mean going to each 5000 lines in the text file, you can use nthline in an async function by running:
npm i nthline --save
in your console, then:
;(async () => {
const nthline = require('nthline'),
filePath = '/path/to/100-million-rows-file',
rowIndex = 42
console.log(await nthline(rowIndex, filePath))
})()
Or if you want to check if the line contains data:
;(async () => {
const nthline = require('nthline'),
filePath = '/path/to/100-million-rows-file',
rowIndex = 42
console.log(await nthline(rowIndex, filePath).contains("Data"))
})()

How to pass command line arguments to NodeJS launched from an executable script

How to set what would otherwise be command-line arguments to node for a NodeJS process run from a launcher script? (The sh/CMD scripts npm places into node_modules/.bin.)
Plenty of NodeJS libraries / frameworks come with their own runner script, e.g. zeit/micro or moleculer that's usually executed from a npm script. This presents a problem in development, since in my case I want to do the equivalent of:
node --inspect -r ts-node/register -r dotenv-safe/config src/index.ts
(Except, of course, that does nothing since index.ts just exports something for the runner to pick up.)
Is there some "clean", preferably generic (i.e. not specific to a given framework's runner exposing those command line params) way that I'm missing to do this, ideally one that works as a npm script? The only thing that seems like it would work would be for e.g. micro:
node-dev -r ts-node/register ./node_modules/micro-dev/bin/micro-dev.js ./src/index.ts
which is kind of a mouthful from the Redundant Department of Redundancy Department and seems to obviate the point of having those launcher scripts. (It also won't work if the runner spawns other Node processes, but that's not a problem I'm actually having.) I'd like to not have to duplicate what the launcher scripts are already doing. I'm also aware of npx having --node-arg but npx is a whole another can of worms. (On Windows it's five seconds of startup time and one spurious error message just to run a script I already have installed; it also won't find an already installed package if it can't find its .cmd launcher script, e.g. when using Docker to run the dev environment. In short I'd rather not use npx for this.)
To clear up the confusion that seems to crop up in the comments: I want to override the command line parameters that affect the behaviour of the NodeJS runtime itself executing the runner script, not pass parameters to the script itself or to my code. That is, the options listed here: https://nodejs.org/api/cli.html
One option is to write a little wrapper script that uses the current process execPath to run child_process.execFile.
So the sample here is to be able to do
node --expose-http2 --zero-fill-buffers -r ./some-module.js ./test.js
but not actually write that out, instead have wrap.js inject the args:
node ./wrap.js ./test.js
I tested running this via npm in a package.json, and it works fine. I tested that it was working by having some-module.js stick a value on the global object, and then logging it in test.js.
Files involved:
wrap.js
const child_process = require('child_process');
const nodeArgs = ['--expose-http2', '--zero-fill-buffers', '-r', './some-module.js'];
const runTarget = process.argv[2];
console.log('going to wrap', runTarget, 'with', nodeArgs);
const finalArgs = nodeArgs.concat(runTarget).concat(process.argv.slice(2));
const child = child_process.execFile(
process.execPath,
finalArgs,
{
env: process.env,
cwd: process.cwd(),
stdio: 'inherit'
}, (e, stdout, stderr) => {
console.log('process completed');
if (e) {
process.emit('uncaughtException', e);
}
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
and
some-module.js
global.testval = 2;
and
test.js
console.log('hi guys, did the wrap work?', global.testval)
EDIT: So upon further thought, this solution really only satisfies wrapping the initial runner. But most tools, such as mocha re-spawn a sub process which would then lose this effect. To really get the job done, you can proxy each of the child process calls and somewhat enforce that calls to spawn and such also include your args.
I rewrote the code to reflect this. Here's a new setup:
package.json
{
"scripts": {
"test": "node -r ./ensure-wrapped.js node_modules/mocha/$(npm view mocha bin.mocha) ./test.js"
},
"dependencies": {
"mocha": "^5.1.0"
}
}
ensure-wrapped.js
const child_process = require('child_process');
// up here we can require code or do whatever we want;
global.testvalue = 'hi there'
const customParams = ['--zero-fill-buffers'];
// the code below injects itself into any child process's spawn/fork/exec calls
// so that it propogates
const matchNodeRe = /((:?\s|^|\/)node(:?(:?\.exe)|(:?\.js)|(:?\s+)|$))/;
const ensureWrappedLocation = __filename;
const injectArgsAndAddToParamsIfPathMatchesNode = (cmd, args, params) => {
params.unshift(...customParams);
params.unshift(args);
if (!Array.isArray(args)) { // all child_proc functions do [] optionally, then other params
args = []
params.unshift(args);
}
if (!matchNodeRe.test(cmd)) {
return params;
}
args.unshift(ensureWrappedLocation);
args.unshift('-r');
return params;
}
child_process._exec = child_process.exec;
child_process.exec = (cmd, ...params) => {
// replace node.js node.exe or /path/to/node to inject -r ensure-wrapped.js ...args..
// leaves alone exec if it isn't calling node
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(cmd, ...params)
}
child_process._execFile = child_process.execFile;
child_process.execFile = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFile(path, ...params)
}
child_process._execFileSync = child_process.execFileSync;
child_process.execFileSync = (path, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(path, args, params);
return child_process._execFileSync(path, ...params);
}
child_process._execSync = child_process.execSync;
child_process.execSync = (cmd, ...params) => {
cmd = cmd.replace(matchNodeRe, '$1 -r ' + ensureWrappedLocation + ' ');
return child_process._exec(bin, ...args)
}
child_process._fork = child_process.fork;
child_process.fork = (module, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(process.execPath, args, params);
return child_process._fork(module, ...params);
}
child_process._spawn = child_process.spawn;
child_process.spawn = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawn(cmd, ...params)
}
child_process._spawnSync = child_process.spawnSync;
child_process.spawnSync = (cmd, args, ...params) => {
params = injectArgsAndAddToParamsIfPathMatchesNode(cmd, args, params);
return child_process._spawnSync(cmd, ...params);
}
test.js
describe('test', () => {
it('should have the global value pulled in by some-module.js', (done) => {
if (global.testvalue !== 'hi there') {
done(new Error('test value was not globally set'))
}
return done();
})
})
Please never put code like this into a node module that's published. modifying the global library functions is pretty bad.
Everything passed in the command line AFTER your nodejs application is parsed into an array called process.argv. So...
node myapp.js foo bar hello 5000
In your nodejs code...
const args = process.argv;
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);
would yield...
foo
bar
hello
5000
I didnt get clear scenario of your problem,but as your question title ,we can execute the any cmd command from nodejs using npm libraries like:
import Promise from 'bluebird'
import cmd from 'node-cmd'
const getAsync = Promise.promisify(cmd.get, { multiArgs: true, context: cmd })
getAsync('node -v').then(data => {
console.log('cmd data', data)
}).catch(err => {
console.log('cmd err', err)
})

How To Execute Windows Shell Commands (Cmd.exe) with Node JS

I would like to
C:\>ACommandThatGetsData > save.txt
But instead of parsing and saving the data in the console, I would like to do the above command with Node.JS
How to execute a shell command with Node.JS?
Use process.execPath():
process.execPath('/path/to/executable');
Update
I should have read the documentations better.
There is a Child Process Module which allows to execute a child process. You will need either child_process.exec, child_process.execFile or child_process.spawn. All of these are similar in use, but each has its own advantages. Which of them to use depends on your needs.
You could also try the node-cmd package:
const nodeCmd = require('node-cmd');
nodeCmd.get('dir', (err, data, stderr) => console.log(data));
On newer versions of the package, the syntax changed a little:
const nodeCmd = require('node-cmd');
nodeCmd.run('dir', (err, data, stderr) => console.log(data));
I know this question is old, but it helped me get to my solution using promises.
Also see: this question & answer
const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function runCommand(command) {
const { stdout, stderr, error } = await exec(command);
if(stderr){console.error('stderr:', stderr);}
if(error){console.error('error:', error);}
return stdout;
}
async function myFunction () {
// your code here building the command you wish to execute ...
const command = 'dir';
const result = await runCommand(command);
console.log("_result", result);
// your code here processing the result ...
}
// just calling myFunction() here so it runs when the file is loaded
myFunction();

Resources