Run main process as child_process - node.js

I am having troubles with getting child_process working with Atom Electron. First of all, I am using the pre-compiled binary app that you can download from Electron's website:
I am using the usual pre-compiled binary on Mac OS X.
In myapp.app/Contents/Resources I made a folder app as described.
I added a brief package.json inside it, setting index.js as main script.
Now, if I add to index.js the following snippet:
'use strict';
var electron = require('electron');
var app = electron.app;
const BrowserWindow = electron.BrowserWindow;
var mainWindow;
function createWindow () {
mainWindow = new BrowserWindow({width: 800, height: 600});
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.webContents.openDevTools();
mainWindow.on('closed', function()
{
mainWindow = null;
});
}
app.on('ready', createWindow);
app.on('window-all-closed', function () {
if (process.platform !== 'darwin')
{
app.quit();
}
});
app.on('activate', function () {
if (mainWindow === null)
{
createWindow();
}
});
(that is basically the example code to get things working) everything works fine. I get the window and I can get it to do basically anything.
Now, for reasons related to updates, I am in need to slightly change this paradigm. What I would need is to be able to perform several tasks from index.js without any need to do any gui operation (it should be some sort of daemon) and then to start some child.js script as a child_process from index.js. child.js should be able to open windows and all the rest.
So here was my naive try. I just cut and pasted the example snippet above in child.js, then edited index.js into the following:
var child_process = require('child_process');
var my_child = child_process.fork(__dirname + '/child.js');
Quite minimal, right? Hoped it would work, but it didn't. When I double click on my pretty app, nothing happens. I bet I am doing something wrong in a trivial way, but I wouldn't be able to tell what.
Update 1 I moved this out of my package so that I could get console.logs. child.js dies with an error at require('electron'): it doesn't seem to be able to find it.
Update 2: I listed the environment variables in child.js and noticed a ATOM_SHELL_INTERNAL_RUN_AS_NODE = 1. I thought I should turn that to 0, but nothing changed.

In Electron child_process.fork() will spawn the child with the ATOM_SHELL_INTERNAL_RUN_AS_NODE environment variable set, which means that no Chromium features will be available in the child process (so no Electron built-in modules), all you'll get is the vanilla Node runtime plus a patched fs that can read inside ASAR files. If you look at the code for child_process.fork() you'll see there's no way to override the value of ATOM_SHELL_INTERNAL_RUN_AS_NODE. If you can, use child_process.spawn() instead, failing that I guess you could try opening an issue and asking if the Electron team will be open to modifying the current behavior.

Related

Make async node script run forever

I searched a lot, but have not found the specific solution to my problem yet.
I have an async function that polls a database as node script that runs forever when using node 12, but in v14 the implementation changed and it closes immediately after running once.
(async function pollDatabase() {
const db = new Db();
return async.forever(async function (pollFn) {
const query = "SELECT * FROM templates WHERE data->>'isProcessed'='0' LIMIT 1;";
const templates = await db.query(query);
if (!templates || !templates.length) {
setTimeout(pollFn, 1000);
return;
}
await doSomethingWithTemplateAndReExecuteThisFunction(templates[0], pollFn);
});
})();
The weird thing is that for example an express server just stays running, but I have not figured out yet how that works. I was not planning to convert this background script to a server. Any thoughts on what would be the best way to make this run forever as some kind of background task? At the moment it is a docker container containing just this script.
I have solved it myself in the end. I don't know exactly why it happened with the switch to Node 14, but the combination of an outdated pg-promise and bluebird library seems to have been the culprit. I guess it probably has to do with the way Promises are now handled in Node 14.
Old versions:
pg-promise#3.2.6 and bluebird#2.10.2
New versions:
pg-promise#^10.9.4 with the native Promise implementation

VSCode node js debugger stuck at the structured-stack file

Currently, I'm running a node js program with the debugger.
the program uses a lot of promises and using a library called p-limit
and I put the debugging config to skip node internal and node modules.
"skipFiles": [
"<node_internals>/**/*.js",
"${workspaceFolder}/node_modules/**/*.js",
]
It's a long running program, it eats cpu and network a lot.
Then I notice the program doesn't eat CPU and network anymore.
So I assume it's stuck and hit the pause button
Somehow the program went stuck at the file called stuctured-stack
(function() {
Error.prepareStackTrace = function(err, trace) {
err.stack = trace;
};
Error.stackTraceLimit = Infinity;
return function structuredStack() {
return new Error().stack;
};
})()
there's no error thrown and it seems not doing anything
What does this mean?
Edit
Added a screenshot:

Electron app installed but doesn't appear in start menu

I've created an app using Electron, and bundled it in an .exe file with electron-builder.
When I run the generated executable, the application starts with the default installation GIF used by electron-builder, as expected.
After the GIF finishes, the app restarts and works properly. It even appears in control panel's programs list.
However, if I look for it in the start menu applications, it isn't there (and searching it by its name only returns the aforementioned .exe installer).
Because of this, once the app is closed, the only way to open it back is running again the installer.
Why does this happen? Is there any way to make it appear with the other programs?
The electron-builder installer (and electron-windows-installer) use Squirrel for handling the installation. Squirrel launches your application on install with arguments that you need to handle. An example can be found on the windows installer github docs
Handling Squirrel Events
Squirrel will spawn your app with command line flags on first run, updates, and uninstalls. it is very important that your app handle these events as early as possible, and quit immediately after handling them. Squirrel will give your app a short amount of time (~15sec) to apply these operations and quit.
The electron-squirrel-startup module will handle the most common events for you, such as managing desktop shortcuts. Just add the following to the top of your main.js and you're good to go:
if (require('electron-squirrel-startup')) return;
You should handle these events in your app's main entry point with something such as:
const app = require('app');
// this should be placed at top of main.js to handle setup events quickly
if (handleSquirrelEvent()) {
// squirrel event handled and app will exit in 1000ms, so don't do anything else
return;
}
function handleSquirrelEvent() {
if (process.argv.length === 1) {
return false;
}
const ChildProcess = require('child_process');
const path = require('path');
const appFolder = path.resolve(process.execPath, '..');
const rootAtomFolder = path.resolve(appFolder, '..');
const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
const exeName = path.basename(process.execPath);
const spawn = function(command, args) {
let spawnedProcess, error;
try {
spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
} catch (error) {}
return spawnedProcess;
};
const spawnUpdate = function(args) {
return spawn(updateDotExe, args);
};
const squirrelEvent = process.argv[1];
switch (squirrelEvent) {
case '--squirrel-install':
case '--squirrel-updated':
// Optionally do things such as:
// - Add your .exe to the PATH
// - Write to the registry for things like file associations and
// explorer context menus
// Install desktop and start menu shortcuts
spawnUpdate(['--createShortcut', exeName]);
setTimeout(app.quit, 1000);
return true;
case '--squirrel-uninstall':
// Undo anything you did in the --squirrel-install and
// --squirrel-updated handlers
// Remove desktop and start menu shortcuts
spawnUpdate(['--removeShortcut', exeName]);
setTimeout(app.quit, 1000);
return true;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
}
};
Notice that the first time the installer launches your app, your app will see a --squirrel-firstrun flag. This allows you to do things like showing up a splash screen or presenting a settings UI. Another thing to be aware of is that, since the app is spawned by squirrel and squirrel acquires a file lock during installation, you won't be able to successfully check for app updates till a few seconds later when squirrel releases the lock.
In this example you can see it run Update.exe (a squirrel executable) with the argument --create-shortcut that adds start menu and desktop shortcuts.
It's 2021 and I am still having a very similar problem.
My app installs correctly and with the script above it also successfully adds a Desktop link to my app. BUT: There is no Shortcut being added to the Windows Start Menu.
With the script above this should also be added to the Start Menu, right?
One comment above says:
// Install desktop and start menu shortcuts
spawnUpdate(['--createShortcut', exeName]);
What am I missing? Any hint highly appreciated...
For me it helped to add icon and setupIcon to the package.json file, where the makers, are configured. Before my app didnt show up in the Start menu, and with the maker config as below, it does. I am not sure why though.
"makers": [
{
"name": "#electron-forge/maker-squirrel",
"config": {
"name": "cellmonitor",
"icon": "favicon.ico",
"setupIcon": "favicon.ico"
}
}
]

Execute node command from UI

I am not very much familiar with nodejs but, I need some guidance in my task. Any help would be appreciated.
I have nodejs file which runs from command line.
filename arguments and that do some operation whatever arguments I have passed.
Now, I have html page and different options to select different operation. Based on selection, I can pass my parameters to any file. that can be any local node js file which calls my another nodejs file internally. Is that possible ? I am not sure about what would be my approach !
I always have to run different command from terminal to execute different task. so, my goal is to reduce that overhead. I can select options from UI and do operations through nodejs file.
I was bored so I decided to try to answer this even though I'm not totally sure it's what you're asking. If you mean you just need to run a node script from a node web app and you normally run that script from the terminal, just require your script and run it programmatically.
Let's pretend this script you run looks like this:
// myscript.js
var task = process.argv[2];
if (!task) {
console.log('Please provide a task.');
return;
}
switch (task.toLowerCase()) {
case 'task1':
console.log('Performed Task 1');
break;
case 'task2':
console.log('Performed Task 2');
break;
default:
console.log('Unrecognized task.');
break;
}
With that you'd normally do something like:
$ node myscript task1
Instead you could modify the script to look like this:
// Define our task logic as functions attached to exports.
// This allows our script to be required by other node apps.
exports.task1 = function () {
console.log('Performed Task 1');
};
exports.task2 = function () {
console.log('Performed Task 2');
};
// If process.argv has more than 2 items then we know
// this is running from the terminal and the third item
// is the task we want to run :)
if (process.argv.length > 2) {
var task = process.argv[2];
if (!task) {
console.error('Please provide a task.');
return;
}
// Check the 3rd command line argument. If it matches a
// task name, invoke the related task function.
if (exports.hasOwnProperty(task)) {
exports[task]();
} else {
console.error('Unrecognized task.');
}
}
Now you can run it from the terminal the same way:
$ node myscript task1
Or you can require it from an application, including a web application:
// app.js
var taskScript = require('./myscript.js');
taskScript.task1();
taskScript.task2();
Click the animated gif for a larger smoother version. Just remember that if a user invokes your task script from your web app via a button or something, the script will be running on the web server and not the user's local machine. That should be obvious but I thought I'd remind you anyway :)
EDIT
I already did the video so I'm not going to redo it, but I just discovered module.parent. The parent property is only populated if your script was loaded from another script via require. This is a better way to test if your script is being run directly from the terminal or not. The way I did it might have problems if you pass an argument in when you start your app.js file, such as --debug. It would try to run a task called "--debug" and then print out "Unrecognized task." to the console when you start your app.
I suggest changing this:
if (process.argv.length > 2) {
To this:
if (!module.parent) {
Reference: Can I know, in node.js, if my script is being run directly or being loaded by another script?

Execute shell command in foreground from node.js

I'm working on a Node.js CLI script that, as part of its duties, will sometimes need to take a large block of input text from the user. Right now, I'm just using the very basic readline.prompt(). I'd like some better editing capabilities. Rather than reinvent the wheel, I figure I could just do as crontab -e or visudo do and have the script launch a text editor that writes data to a temporary file, and read from that file once it exits. I've tried some things from the child_process library, but they all launch applications in the background, and don't give them control of stdin or the cursor. In my case, I need an application like vim or nano to take up the entire console while running. Is there a way to do this, or am I out of luck?
Note: This is for an internal project that will run on a single machine and whose source will likely never see the light of day. Hackish workarounds are welcome, assuming there's not an existing package to do what I need.
Have you set the stdio option of child_process.spawn to inherit?
This way, the child process will use same stdin and stdout as the top node process.
This code works for me (node v4.3.2):
'use strict';
var fs = require('fs');
var child_process = require('child_process');
var file = '/tmp/node-editor-test';
fs.writeFile(file, "Node Editor", function () {
var child = child_process.spawn('vim', [file], {stdio: 'inherit'});
child.on('close', function () {
fs.readFile(file, 'utf8', function (err, text) {
console.log('File content is now', text);
});
});
});

Resources