How to run a command line tool from node.js application - javascript

The title of my question is how to run a command line tool from a node.js application because I think an answer here will apply to all command line utilities installable from npm. I have seen questions related to running command line from node.js, but they don't seem to be working for my situation. Specifically I am trying to run a node command line utility similar to npm (in how it is used, but not its function) called tilemantle.
tilemantle's documentation shows installing tilemantle globally and running the program from the command line.
What I would like to do is install tilemantle locally as a part of a npm project using npm install tilemantle --save and then run tilemantle from inside my project.
I've tried `tilemantle = require('tilemantle'), but the index.js file in the tilemantle project is empty, so I think this won't help with anything.
I tried the project node-cmd
const cmd = require('node-cmd');
cmd.run('./node_modules/tilemantle/bin/tilemantle', 'http://localhost:5000/layer/{z}/{x}/{y}/tile.png', '-z 0-11', '--delay=100ms', '--point=37.819895,-122.478674', '--buffer=100mi'
This doesn't throw any errors, but it also just doesn't work.
I also tried child processes
const child_process = require('child_process');
child_process.exec('./node_modules/tilemantle/bin/tilemantle', 'http://localhost:5000/layer/{z}/{x}/{y}/tile.png, -z 0-11 --delay=100ms --point=37.819895,-122.478674 --buffer=100mi'
This also doesn't throw any errors, but it also doesn't work.
Is there a way to get this working, so that I can run tilemantle from inside my program and not need to install it globally?
Update
I can get tilemantle to run from my terminal with
node './node_modules/tilemantle/bin/tilemantle', 'http://localhost:5000/layer/{z}/{x}/{y}/tile.png', '--delay=100ms', '--point=37.819895,-122.478674', '--buffer=100mi', '-z 0-11'
If I run the following as suggested by jkwok
child_process.spawn('tilemantle', ['http://myhost.com/{z}/{x}/{y}.png',
'--point=44.523333,-109.057222', '--buffer=12mi', '-z', '10-14'],
{ stdio: 'inherit' });
I am getting spawn tilemantle ENOENT and if I replace tilemantle with ./node_modules/tilemantle/bin/tilemantle.js I get spawn UNKNOWN
Based on jfriend00's answer it sounds like I need to actually be spawning node, so I tried the following
child_process.spawn('node', ['./node_modules/tilemantle/bin/tilemantle.js', 'http://myhost.com/{z}/{x}/{y}.png', '--point=44.523333,-109.057222', '--buffer=12mi', '-z', '10-14'], { stdio: 'inherit' });
Which gives me the error spawn node ENOENT which seems strange since I can run it from my terminal and I checked my path variable and C:\Program Files\nodejs is on my path.
Just to check I tried running the following with a full path to node.
child_process.spawn('c:/program files/nodejs/node.exe', ['./node_modules/tilemantle/bin/tilemantle.js', 'http://myhost.com/{z}/{x}/{y}.png', '--point=44.523333,-109.057222', '--buffer=12mi', '-z', '10-14'], { stdio: 'inherit' });
which runs without the ENOENT error, but again it is failing silently and is just not warming up my tile server.
I am running Windows 10 x64 with Node 6.11.0

You can install any executable locally and then run it with child_process. To do so, you just have to figure out what the exact path is to the executable and pass that path to the child_process.exec() or child_process.spawn() call.
What it looks like you ultimately want to run is a command line that does:
node <somepath>/tilemantle.js
When you install on windows, it will do most of that for you if you run:
node_modules\.bin\tilemantle.cmd
If you want to run the js file directly (e.g. on other platforms), then you need to run:
node node_modules/tilemantle/bin/tilemantle.js
Note, with child_process, you have to specify the actual executable which in this case is "node" itself and then the path to the js file that you wish to run.
This, of course, all assumes that node is in your path so the system can find it. If it is not in your path, then you will have to use the full path to the node executable also.

It looks like you are trying to capture the output of tilemantle from running a file rather than from the command line. If so, I did the following and got it to work.
Installed tilemantle and child_process locally into a new npm project as you did, and added the following into a file in that project.
// test.js file
var spawn = require('child_process').spawn;
spawn('tilemantle', ['http://myhost.com/{z}/{x}/{y}.png', '--
point=44.523333,-109.057222', '--buffer=12mi', '-z', '10-14'], { stdio: 'inherit' });
Run it using node test.js inside the project.
I tried a bunch of the other options in this post but could only get the above one to print the progress along with other output. Hope this helps!

Many command line utilities come with a "programmatic" API. Unfortunately, tilemantle does not, which is why you are unable to require that module in your code.
You can, however, easily access a locally installed version of the CLI from npm scripts. I don't know anything about tilemantle, so I'll provide an example using the tldr command line tool. In your package.json:
{
"name": "my-lib",
"version": "1.0.0",
"scripts": {
"test": "tldr curl"
},
"dependencies": {
"tldr": "^2.0.1"
}
}
You can now run npm test from the terminal in your project as an alias for tldr curl.
Per this post, you can use the global-npm package to run npm scripts from your code:
const npm = require('global-npm')
npm.load({}, (err) => {
if (err) throw err
npm.commands.run(['test'])
})
And voila, you can now run the locally installed CLI programmatically(ish), even though it has not offered an API for that.

Related

Pass command line arguments to NodeJS script from NPM script

I have a main script (publish-all.js) from which I want to invoke the npm publish script of an Angular project, which also has a sub-script (publish.js that does general stuff (creating folders, copying files, moving folders...) after ng build.
I need to pass some environment variables to that second script.
I am using shelljs to run unix-like commands.
I tried using:
npm run publish -- VERSION=${productVersion} DESTDIR=${productDestinationPath}
From publish-all.js where productVersion and productDestinationPath are constants declared above that line, and which invokes the following script from the package.json:
"publish": "ng build --prod && node ./scripts/publish.js"
But the actual command line I get is
ng build --prod && node ./scripts/publish.js "VERSION=value" "DESTDIR=value"
Finally, in my publish.js script I tried getting those variables the following way:
let version = process.env.VERSION;
let destinationPath = process.env.DESTDIR;
But I get undefined values.
What am I doing wrong? Is the a better way of doing all this?
Should I maybe use process.argv instead??
I am using this strategy because it is what I were told to do, but I would like to know if there is a less confusing way.รง
EDIT 2021-07-13
I tried using export (with shelljs, since I am in Windows and using powershell) but I am getting an exception.
I have the following code in publish-all.js now:
shelljs.exec(`export VERSION=${productVersion}`);
shelljs.exec(`export DESTDIR=${productDestinationPath}`);
shelljs.exec('npm run publish');
And in the publish.js script from the ANGULAR project:
version = process.env.VERSION;
destinationPath = process.env.DESTDIR;
Though it does not get to publish.js. It gets stuck in the shelljs.exec('npm run publish'), with the following exception:
I had to hide the project folder because of privacy policies, but it is a subfolder inside the folder where I am executing publish-all.js.
Environmental variables go BEFORE the command. So, instead of passing them after you can add them BEFORE:
VERSION=${productVersion} DESTDIR=${productDestinationPath} npm run publish
Or,
You can export the variables first then run the script:
export VERSION=${productVersion}
export DESTDIR=${productDestinationPath}
npm run publish

uglifyjs-folder remove console.log & alert from minified files

I am minifying multiple files in a folder using uglifyjs-folder in npm package.json like :
"uglifyjs": "uglifyjs-folder js -eyo build/js"
It is working as intended & minify all files in folder.
I want to remove any console.log & alert while minify but not able to find any option with uglifyjs-folderhttps://www.npmjs.com/package/uglifyjs-folder
Please help.
Short Answer
Unfortunately, uglifyjs-folder does not provide an option to silence the logs.
Solution
You could consider writing a nodejs utility script which utilizes shelljs to:
Invoke the uglifyjs-folder command via the shelljs exec() method.
Prevent logging to console by utilizing the exec() methods silent option.
The following steps further explain how this can be achieved:
Install
Firstly, cd to your project directory and install/add shelljs by running:
npm i -D shelljs
node script
Create a nodejs utility script as follows. Lets name the file: run-uglifyjs-silently.js.
var path = require('path');
var shell = require('shelljs');
var uglifyjsPath = path.normalize('./node_modules/.bin/uglifyjs-folder');
shell.exec(uglifyjsPath + ' js -eyo build/js', { silent: true });
Note: We execute uglifyjs-folder directly from the local ./node_modules/.bin/ directory and utilize path.normalize() for cross-platform purposes.
package.json
Configure the uglifyjs script inside package.json as follows:
{
...
"scripts": {
"uglifyjs": "node run-uglifyjs-silently"
...
},
...
}
Running
Run the script as per normal via the command line. For example:
npm run uglifyjs
Or, for less logging to the console, add the npm run --silent or shorthand equivalent -s option/flag. For example:
npm run uglifyjs -s
Notes:
The example gist above assumes that run-uglifyjs-silently.js is saved at the top-level of your project directory, (i.e. Where package.json resides).
Tip: You could always store run-uglifyjs-silently.js in a hidden directory named .scripts at the top level of your project directory. In which case you'll need to redefine your script in package.json as follows:
{
...
"scripts": {
"uglifyjs": "node .scripts/run-uglifyjs-silently"
...
},
...
}
uglify-folder (in 2021, now?) supports passing in terser configs like so:
$ uglify-folder --config-file uglifyjs.config.json ...other options...
and with uglifyjs.config.json:
{
"compress": {
"drop_console": true
}
}
And all options available here from the API reference.

What does the init command do?

Several Node.js packages have the following two steps as their starting point (just using Jasmine as an example):
npm install --save-dev jasmine
./node_modules/.bin/jasmine init
The first statement is straightforward, but I could not for the life of me figure out what the second statement does under the hood. The Jasmine docs only say that it initializes it (I am searching for something more technical).
./node_modules/.bin/jasmine looks like this:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../jasmine/bin/jasmine.js" "$#"
ret=$?
else
node "$basedir/../jasmine/bin/jasmine.js" "$#"
ret=$?
fi
exit $ret
In case helpful, I did this to clone and install the package locally:
mkdir testProj : Creates new project folder
code testProj --add : Adds folder to workspace
cd testProj
npm init : Creates package.json
npm install [--save-dev] : Installs dependencies
./node_modules/.bin/ init : What does this do, specifically?
Short: npx init
Any pointers/documentation explaining that init would be deeply appreciated.
Edit:
Just to clarify, I know what init does (clear from testing and the Jasmine documentation), I just do not understand how it does it. I am basically trying to find out why init is needed behind the script name when running it from the CLI, and where the init code is located.
I managed to finally solve this myself. If anyone comes across this in the future, the following is the explanation of ./node_modules/.bin/jasmine init.
./node_modules/.bin/jasmine init is executed from the command line
This runs the jasmine Unix script in ./node_modules/.bin/ (init argument is not used yet)
The script resolves path to jasmine.js (./node_modules/jasmine/bin/jasmine.js) and runs it
Jasmine.js contains this code: var Command = require('../lib/command.js')
Jasmine.js creates a new instance of the Command object (command) and executes: command.run(jasmine, process.argv.slice(2));
process.argv is an array of all arguments given when starting the application from command line. Recalling the command, one sees that slice(2) equals init
The run function inside command.js launches initJasmine by having mapped init to initJasmine at the very top
Finally, initJasmine makes the directory spec and all its contents
Hope that helps someone else in the future.
jasmine init initializes a basic folder structure for Jasmine by creating a spec directory and configuration json for you. You can look for more details here https://github.com/jasmine/jasmine-npm

Trouble importing from module after mix a compile

I'm in the process of re-writing my electron app with ES6, using Laravel Mix to compile the app JS and SASS. Now the main process loads up the render process fine. Once that happens my app.js loads up and that's where I have my issues. So I do:
import { remote } from 'electron';
Which causes this error in console:
Uncaught Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
Now i've tried reinstalling electron, even though electron works when the main process fires up to begin with. The line refers to this in the compiled js:
/* WEBPACK VAR INJECTION */(function(__dirname) {var fs = __webpack_require__(8)
var path = __webpack_require__(9)
var pathFile = path.join(__dirname, 'path.txt')
if (fs.existsSync(pathFile)) {
module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
} else {
throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')
}
I'm not really sure what's going on, any advice or information would be a great help!
Thanks
Edit: I've tried running it with --verbose:
/Library/Caches/com.apple.xbs/Sources/AppleGVA/AppleGVA-10.1.16/Sources/Slices/Driver/AVD_loader.cpp: failed to get a service for display 3
2017-06-13 16:10:42.383 Electron Helper[47106:766924] Couldn't set selectedTextBackgroundColor from default ()
Most probably source of problem is that path.txt doesn't exists.
path.txt is generated while installing electron from npm. If you are not seeing any error while installing electron that means, errors are getting suppressed.
Troubleshoot: Check if node_modules/electron/path.txt exist. If not, then you have got a problem.
Solution:
Note: If on Windows, use native CMD instead of Git Bash
Try to install electron manually after npm install by executing following script
cd node_modules/electron && node install.js
This may take up a while, since it is going to download electron's full package.

npm config parameter on windows

I previously asked (and got a great answer for) this question: named parameter to npm run script
As mentioned in 1 of the comments to the correct answer, the $npm_config_variable does not work on windows. I'm looking for a cross platform way to run this script as npm run say-hello --greeting='hello'.
Is there any way to check for the OS in npm scripts and use %% instead of $?
Use minimist library. Its realy easy to access command line params with it and it works in every environment.
var argv = require('minimist')(process.argv.slice(2));
var param = argv.param;
// package.json snippet
"scripts": {
"start": "app --param=value"
}

Categories