How do I print a new line to the terminal without a newline in Deno? In node.js I used to do:
process.stdout.write('hello, deno!')
Is this possible in Deno? Deno does not have the process module, and I could not find an equivalent option in https://doc.deno.land/builtin/stable.
I figured it out. Deno does not have node.js's process module but it has different functionality to replicate it. I was able to print to the terminal without a new line with:
const text = new TextEncoder().encode('Hello, deno!')
// asynchronously
await Deno.writeAll(Deno.stdout, text)
// or, sychronously
Deno.writeAllSync(Deno.stdout, text)
Documentation link: https://doc.deno.land/builtin/stable#Deno.writeAll
import { writeAllSync } from "https://deno.land/std/streams/conversion.ts";
const text = new TextEncoder().encode('Hello')
writeAllSync(Deno.stdout, text)
Deno.writeAllSync and Deno.writeAll are deprecated, it's recommended to use the package above instead.
Related
I want to get a value from input
const readline = require('node:readline/promises').createInterface({
input: process.stdin,
output: process.stdout
})
const value = await readline.question('enter :');
console.log(value)
readline.close()
And then I get an Error "SyntaxError: await is only valid in async functions and the top level bodies of modules"
On the other hand in the docs example:
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'process';
const rl = readline.createInterface({ input, output });
const answer = await rl.question(
'What do you think of Node.js? '
);
console.log(
`Thank you for your valuable feedback: ${answer}`
);
rl.close();
I copy this example and get the same error. I cant use readline synchronously. I want input a value, some variable will equals this value, and THEN it will console.log(). I dont want to use "Prompt" module. TY
Your issue is because you are trying to use top-level-await.
Now this is possible in modern nodejs (16+ I think), but you need to tell explicitly node to use modules, like your in your second code.
Try to upgrade nodeJS to the latest version 18, it should run fine.
(Also you may need to hint node on using modules explicitly, for that either name your file using the extension .mjs or specify "type": "module" in your package.json
Working example using .mjs and current node v18.6 the code is copy pasted from your question, no changes made.
Solved:
I had to add \r\n at program.stdin.write(data) (something like this program.stdin.write(data+'\r\n')) and it worked.
It seems that if i don't put \r\n, it doesn't triggers, its like typing in a line without pressing enter so it will never be processed.
===========================================================================
I need to access screen through child_process, but it doesn't works properly.
First I tried to access using spawn.
const {spawn} = require('child_process');
const program = spawn('screen',['-x User/Aplication']);
program.stdout.on('data',data=>{
//Something
})
function writeTo(data){
program.stdin.write(data);
}
But i got the error "Must be connected to a terminal error". After some research i found a solution, use script+spawn to make a pseudo-console.
const {spawn} = require('child_process');
const program = spawn('script',['/dev/null']);//Pseudo-console
program.stdin.write('screen -x User/Aplication');//I access to screen through the pseudo-console, and it works.
program.stdout.on('data',data=>{
//Something
})
function writeTo(data){
program.stdin.write(data);
}
But... when I try to use writeTo, it doesn't works.
writeTo('Some command here')//Does nothing.
And somehow, when I pipe my console input, it works!
process.stdin.pipe(program.stdin);
Then I type something in my console and it proxies properly to connected screen.
Issue: It doesn't proxies properly when using program.stdin.write, but somehow, it works when i pipe my console process.stdin.pipe(program.stdin)
Observation 1: I made a short echo-program and it worked with both program.stdin.write and process.stdin.pipe(program.stdin)
echo.js
process.stdin.on('data',data=>{
console.log(`[Input]${data}`);
})
main.js
const {spawn} = require('child_process');
const program = spawn('node',['echo.js']);
program.stdout.pipe(process.stdout);
function writeTo(data){
program.stdin.write(data);
}
writeTo('Test');//Output: [Input]Test
process.stdin.pipe(program.stdin);//I type 'something'. Output: [Input]something
Observation 2: When using script+screen and piping my console, program.stdin.write only 'buffers' and process.stdin.pipe loads that buffer and sends it with what i typed.
program.stdin.write('He');//screen receives nothing
program.stdin.write('llo');//screen receives nothing
process.stdin.pipe(program.stdin);//I type ' world!'. screen receives 'Hello world!'
it may not be the whole problem, but the second argument to spawn should have each argument in a separate array element.
const program = spawn('screen',['-x', 'User/Aplication']);
I had to add \r\n at program.stdin.write(data) (something like this program.stdin.write(data+'\r\n')) and it worked.
It seems that if i don't put \r\n, it doesn't triggers as a new line and it doesn't sends it, its like typing all commands in a line without pressing enter so it will never be processed.
I don't know much about security when it comes to Backend-JavaScript.
The only way I can imagine sanitizing the input is to use regex and replace all the unwanted characters, or split the whole string and filter/map the unwanted characters out, then join back together.
I'm also using the Oak module for Deno.
You can try out this https://deno.land/x/html_entities
According to lib documentation:
HTML validity and XSS attack prevention you can achieve from XmlEntities module.
import { XmlEntities } from "https://deno.land/x/html_entities#v1.0/mod.js";
XmlEntities.encode('<>"\'&©®'); // <>"'&©®
XmlEntities.encodeNonUTF('<>"\'&©®'); // <>"'&©®
XmlEntities.encodeNonASCII('<>"\'&©®'); // <>"\'&©®
XmlEntities.decode('<>"'&©®∆'); // <>"'&©®∆
you can reuse the sanitize npm package:
npm i -g sanitize
And then you can use it like:
import { createRequire } from "https://deno.land/std/node/module.ts";
const require = createRequire(import.meta.url);
const sanitizer = require("sanitizer");
name = sanitizer.value(name, "string");
surname = sanitizer.value(surname, "string");
or you can replace this library with another sanitization library
and you run your deno script with: deno run --allow-read --unstable script.ts
With jest I'm not able to test commander module functions that result in process exit.
For example, if I pass the --help option or an invalid parameter like -x (see below) process.exit or process.stdout.write are not called as they should looking at the commander sources.
import {Command} from "commander";
let mockExit: jest.SpyInstance;
let mockStdout: jest.SpyInstance;
beforeAll(() => {
mockExit = jest.spyOn(process, "exit").mockImplementation();
mockStdout = jest.spyOn(process.stdout, "write").mockImplementation();
});
afterAll(() => {
mockExit.mockRestore();
mockStdout.mockRestore();
});
test("Ask for help", () => {
// Setup
const save = JSON.parse(JSON.stringify(process.argv));
process.argv = ["--help"]; // Same setting it to "-x"
const program = new Command();
program
.option("-v, --verbose [level]", "verbose level")
.parse(process.argv);
expect(mockExit).toBeCalled();
// expect(mockStdout).toBeCalled();
// Cleanup
process.argv = save;
});
What is strange is that, from the behavior of other tests, process.argv is not restored after this one.
Tests are in typescript and passed through ts-jest.
Any ideas?
Thanks!
I suggest you use .exitOverride(), which is the approach Commander uses in its own tests. This means early "termination" is via a throw rather than exit.
https://github.com/tj/commander.js#override-exit-handling
The first problem though (from comments) is the arguments. Commander expects the parse arguments follow the conventions of node with argv[0] is the application and argv[1] is the script being run, with user parameters after that.
So instead of:
argsToParse = ["--help"];
something like:
argsToParse = ['node", "dummy.js", "--help"];
(No need to modify process.argv as such.)
I would like to be able to open Vim from node.js program running in the terminal, create some content, save and exit Vim, and then grab the contents of the file.
I'm trying to do something like this:
filename = '/tmp/tmpfile-' + process.pid
editor = process.env['EDITOR'] ? 'vi'
spawn editor, [filename], (err, stdout, stderr) ->
text = fs.readFileSync filename
console.log text
However, when this runs, it just hangs the terminal.
I've also tried it with exec and got the same result.
Update:
This is complicated by the fact that this process is launched from a command typed at a prompt with readline running. I completely extracted the relevant parts of my latest version out to a file. Here is it in its entirety:
{spawn} = require 'child_process'
fs = require 'fs'
tty = require 'tty'
rl = require 'readline'
cli = rl.createInterface process.stdin, process.stdout, null
cli.prompt()
filename = '/tmp/tmpfile-' + process.pid
proc = spawn 'vim', [filename]
#cli.pause()
process.stdin.resume()
indata = (c) ->
proc.stdin.write c
process.stdin.on 'data', indata
proc.stdout.on 'data', (c) ->
process.stdout.write c
proc.on 'exit', () ->
tty.setRawMode false
process.stdin.removeListener 'data', indata
# Grab content from the temporary file and display it
text = fs.readFile filename, (err, data) ->
throw err if err?
console.log data.toString()
# Try to resume readline prompt
cli.prompt()
The way it works as show above, is that it shows a prompt for a couple of seconds, and then launches in to Vim, but the TTY is messed up. I can edit, and save the file, and the contents are printed correctly. There is a bunch of junk printed to terminal on exit as well, and Readline functionality is broken afterward (no Up/Down arrow, no Tab completion).
If I uncomment the cli.pause() line, then the TTY is OK in Vim, but I'm stuck in insert mode, and the Esc key doesn't work. If I hit Ctrl-C it quits the child and parent process.
You can inherit stdio from the main process.
const child_process = require('child_process')
var editor = process.env.EDITOR || 'vi';
var child = child_process.spawn(editor, ['/tmp/somefile.txt'], {
stdio: 'inherit'
});
child.on('exit', function (e, code) {
console.log("finished");
});
More options here: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
Update: My answer applied at the time it was created, but for modern versions of Node, look at this other answer.
First off, your usage of spawn isn't correct. Here are the docs. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn
Your sample code makes it seem like you expect vim to automatically pop up and take over the terminal, but it won't. The important thing to remember is that even though you may spawn a process, it is up to you to make sure that the data from the process makes it through to your terminal for display.
In this case, you need to take data from stdin and send it to vim, and you need to take data output by vim and set it to your terminal, otherwise you won't see anything. You also need to set the tty into raw mode, otherwise node will intercept some of the key sequences, so vim will not behave properly.
Next, don't do readFileSync. If you come upon a case where you think you need to use a sync method, then chances are, you are doing something wrong.
Here's a quick example I put together. I can't vouch for it working in every single case, but it should cover most cases.
var tty = require('tty');
var child_process = require('child_process');
var fs = require('fs');
function spawnVim(file, cb) {
var vim = child_process.spawn( 'vim', [file])
function indata(c) {
vim.stdin.write(c);
}
function outdata(c) {
process.stdout.write(c);
}
process.stdin.resume();
process.stdin.on('data', indata);
vim.stdout.on('data', outdata);
tty.setRawMode(true);
vim.on('exit', function(code) {
tty.setRawMode(false);
process.stdin.pause();
process.stdin.removeListener('data', indata);
vim.stdout.removeListener('data', outdata);
cb(code);
});
}
var filename = '/tmp/somefile.txt';
spawnVim(filename, function(code) {
if (code == 0) {
fs.readFile(filename, function(err, data) {
if (!err) {
console.log(data.toString());
}
});
}
});
Update
I seeee. I don't think readline is as compatible with all of this as you would like unfortunately. The issue is that when you createInterface, node kind of assumes that it will have full control over that stream from that point forward. When we redirect that data to vim, readline is still there processing keypresses, but vim is also doing the same thing.
The only way around this that I see is to manually disable everything from the cli interface before you start vim.
Just before you spawn the process, we need to close the interface, and unfortunately manually remove the keypress listener because, at least at the moment, node does not remove it automatically.
process.stdin.removeAllListeners 'keypress'
cli.close()
tty.setRawMode true
Then in the process 'exit' callback, you will need to call createInterface again.
I tried to do something like this using Node's repl library - https://nodejs.org/api/repl.html - but nothing worked. I tried launching vscode and TextEdit, but on the Mac there didn't seem to be a way to wait for those programs to close. Using execSync with vim, nano, and micro all acted strangely or hung the terminal.
Finally I switched to using the readline library using the example given here https://nodejs.org/api/readline.html#readline_example_tiny_cli - and it worked using micro, e.g.
import { execSync } from 'child_process'
...
case 'edit':
const cmd = `micro foo.txt`
const result = execSync(cmd).toString()
console.log({ result })
break
It switches to micro in a Scratch buffer - hit ctrl-q when done, and it returns the buffer contents in result.