npm-generated command not calling node - node.js

I'm trying to create a node.js command line utility and having an issue with wrapper that npm generates. My super-simple demo is a package.json file:
{
"name": "demo-cli",
"version": "0.0.1",
"bin": { "demo": "bin/demo-cli.js" }
}
And the demo-cli.js file:
console.log('DEMO WORKED');
The issue is when I install the module using npm install -g while in the project directory the wrapper it generates does not call node with the file as the parameter. This is the generated contents of demo.cmd:
:: Created by npm, please don't edit manually.
"%~dp0\.\node_modules\demo-cli\bin\demo-cli.js" %*
The unix version has the same issue. Looking at another globally installed CLI I can see that node is being tested for and called as expected.
:: Created by npm, please don't edit manually.
#IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\.\node_modules\mocha\bin\mocha" %*
) ELSE (
node "%~dp0\.\node_modules\mocha\bin\mocha" %*
)
What is the secret sauce needed for npm to generate the proper script file? I've tried everything I could find or think of, including removing the .js extension, preferring global, specifying the node version...no luck.
I'm using node v0.6.18 and npm v1.1.21 on Windows 7 x64.

Try putting
#!/usr/bin/env node
At the top of your demo-cli.js file.
On windows, npm looks for this shebang line when creating the .cmd wrapper. See cmd-shim.js in the npm source for more info.

Related

node 6.11.3 NODE_EXE not found in IntelliJ

I've updated my project to use node 6.11.3. When I now try to start a npm-script by using the Run-configurations provided from IntelliJ, I always receive the following error:
Error: Failed to replace env in config: ${NODE_EXE}
Important: This error appears only if I start npm from within my project. If I use the windows cmd, the error doesn't appear.
What could possibly have changed between node 6.11.2 and 6.11.3? Because with the prior version, everything worked fine.
A workaround for me is to add the NODE_EXE variable to my run configuration, but In my opinion, that shouldn't be needed, because it worked in 6.11.2 too.
Looks as if you have ${NODE_EXE} variable set in one of your npmrc files (see https://docs.npmjs.com/files/npmrc#files), and it can't be properly expanded for some reason when you run your script in the IDE.
is the issue specific to certain project?
how many npm versions do you have installed? Please check that npm chosen in Node.js Interpreters dialog is the same as you use in cmd shell?
please create an env.js file with console.log(process.env) and try running it via npm ("env" : "node env.js") in both cmd console and WebStorm - what is the result?

How to have separate build:bundle for linux and windows for my node project?

I am very new to node.js and npm. I was trying to setup an open source project locally on my windows and I got this error
'NODE_ENV' is not recognized as an internal or external command,
operable program or batch file.
So I edited build:bundle in package.json, SET NODE_ENV=xyz from NODE_ENV=xyz. It installed the project properly then.
My question is, Is there a way to tell npm to use SET NODE_ENV=xyz on windows and NODE_ENV=xyz on linux/mac, so that I can create a fix without breaking the entire project.
Install and use the package named cross-env
$ npm install --save-dev cross-env
Run scripts that set and use environment variables across platforms
You can then use one syntax, so the build:bundle script will be something like this:
...
"scripts": {
"build:bundle": "cross-env NODE_ENV=xyz ...",
...
}

Specify the path to node when running executable npm moduels

I'm fairly new to node and npm so imagine this might be the way I'm trying to use it. My understanding so far and questions are as follows.
When installing modules with npm if they have executables they are created in node_moadules/.bin
For example npm install istanbul creates
node_moadules/.bin/istanbul.cmd with the following contents
#IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\istanbul\lib\cli.js" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\istanbul\lib\cli.js" %*
)
This seems to be roughly similar for other npm modules installed.
1) When would there be a node.exe in the bin directory (%~dp0 is the path of the batch file - so node_moadules/.bin)? When using npm istanbul it will use the above script but I don't understand when it would use the IF block, it seems it would always go into the ELSE which uses node on the path to run it. This will work as intended when I have node installed and on my path. However this would seem to be a problem if you don't have it on your path or need to use a specific version in a project (different from the version on your path).
2) I can fix this by manually editing the above script to point to the specific node version but don't want to have to do this for all module executables installed with npm. Is there a way to configure this in npm so it uses a specific path to node when it generates these executable files?
A bit more background
We're using the gradle-node-plugin in our build so we don't have to manage node and npm on our build machines, this does it for us. However this means we don't have node on the path.
The plugin allows me to install npm modules but there is no clear way to then run the executables, using the version of node unpacked by the plugin, without manually editing the generated executable scripts as in 2), or instead of calling npm istanbul calling something like this:
<path_to>\node <path_to>\node_modules\istanbul\lib\cli.js
This also doesn't seem right as I'm hardcoding to the cli.js executable path within the module which could presumably change in future versions of that module.
If anyone could explain this, or tell me where the specific part of the docs that do are that would be helpful. I've tried to search but probably not using the right terms.

How to run TypeScript files from command line?

I'm having a surprisingly hard time finding an answer to this. With plain Node.JS, you can run any js file with node path/to/file.js, with CoffeeScript it's coffee hello.coffee and ES6 has babel-node hello.js. How do I do the same with Typescript?
My project has a tsconfig.json which is used by Webpack/ts-loader to build a nice little bundle for the browser. I have a need for a build step run from the console before that, though, that would use some of the .ts files used in the project to generate a schema, but I can't seem to be able to run a single Typescript file without compiling the whole project.
How do I do the same with Typescript
You can leave tsc running in watch mode using tsc -w -p . and it will generate .js files for you in a live fashion, so you can run node foo.js like normal
TS Node
There is ts-node : https://github.com/TypeStrong/ts-node that will compile the code on the fly and run it through node 🌹
npx ts-node src/foo.ts
Run the below commands and install the required packages globally:
npm install -g ts-node typescript '#types/node'
Now run the following command to execute a typescript file:
ts-node typescript-file.ts
We have following steps:
First you need to install typescript
npm install -g typescript
Create one file helloworld.ts
function hello(person){
return "Hello, " + person;
}
let user = "Aamod Tiwari";
const result = hello(user);
console.log("Result", result)
Open command prompt and type the following command
tsc helloworld.ts
Again run the command
node helloworld.js
Result will display on console
To add to #Aamod answer above, If you want to use one command line to compile and run your code, you can use the following:
Windows:
tsc main.ts | node main.js
Linux / macOS:
tsc main.ts && node main.js
Edit: May 2022
ts-node now has an --esm flag use it.
Old Answer:
None of the other answers discuss how to run a TypeScript script that uses modules, and especially modern ES Modules.
First off, ts-node doesn't work in that scenario, as of March 2020. So we'll settle for tsc followed by node.
Second, TypeScript still can't output .mjs files. So we'll settle for .js files and "type": "module" in package.json.
Third, you want clean import lines, without specifying the .js extension (which would be confusing in .ts files):
import { Lib } from './Lib';
Well, that's non-trivial. Node requires specifying extensions on imports, unless you use the experimental-specifier-resolution=node flag. In this case, it would enable Node to look for Lib.js or Lib/index.js when you only specify ./Lib on the import line.
Fourth, there's still a snag: if you have a different main filename than index.js in your package, Node won't find it.
Transpiling makes things a lot messier than running vanilla Node.
Here's a sample repo with a modern TypeScript project structure, generating ES Module code.
I created #digitak/esrun, a thin wrapper around esbuild and that executes a TypeScript file almost instantly. esrun was made because I was disappointed with ts-node: too slow, and just didn't work most of the time.
Advantages of esrun over ts-node include:
very fast (uses esbuild),
can import ESM as well as CJS (just use the libraries of your choice and esrun will work out of the box),
there is an included watch mode, run your script with the --watch option and any change to your entry file or any of its dependencies will re-trigger the result
you can use esrun in inspect mode to use the DevTools console instead of your terminal console.
After installing, just run:
npx #digitak/esrun file.ts
Just helpful information - here is newest TypeScript / JavaScript runtime Deno.
It was created by the creator of node Ryan Dahl, based on what he would do differently if he could start fresh.
You can also try tsx.
tsx is a CLI command (alternative to node) for seamlessly running TypeScript, its build upon esbuild so its very fast.
https://github.com/esbuild-kit/tsx
Example:
npx tsx ./script.ts
As of May 2022 ts-node does support es modules
npx ts-node --esm file.ts
you will likely need to add "type": "module", to your package.json. And some of the imports might be wonky unless you turn on experimental-specifier-resolution=node
npmjs.com/package/ts-node#commonjs-vs-native-ecmascript-modules
For linux / mac you can add the ts-node-script shebang.
Install typescript / ts-node globally (see 1 below for non global install):
npm install ts-node typescript --save-dev --global
Add this as the first line in your .ts file:
#!/usr/bin/env ts-node-script
Then make the file executable:
$ chmod +x ./your-file.ts
You can then run the file directly from the command line:
$ ./your-file.ts
Notes:
1 For non global install you can install local to your project
npm install ts-node typescript --save-dev
and add the relative path to the shebang script eg:
#!/usr/bin/env ./node_modules/.bin/ts-node-script
2 Support for shebangs was officially added in ts-node v8.9.0.
Like Zeeshan Ahmad's answer, I also think ts-node is the way to go. I would also add a shebang and make it executable, so you can just run it directly.
Install typescript and ts-node globally:
npm install -g ts-node typescript
or
yarn global add ts-node typescript
Create a file hello with this content:
#!/usr/bin/env ts-node-script
import * as os from 'os'
function hello(name: string) {
return 'Hello, ' + name
}
const user = os.userInfo().username
console.log(`Result: ${hello(user)}`)
As you can see, line one has the shebang for ts-node
Run directly by just executing the file
$ ./hello
Result: Hello, root
Some notes:
This does not seem to work with ES modules, as Dan Dascalescu has pointed out.
See this issue discussing the best way to make a command line script with package linking, provided by Kaspar Etter. I have improved the shebang accordingly
Update 2020-04-06: Some changes after great input in the comments: Update shebang to use ts-node-script instead of ts-node, link to issues in ts-node.
Write yourself a simple bash wrapper may helps.
#!/bin/bash
npx tsc $1 && node ${1%%.ts}
For environments such as Webstorm where the node command cannot be changed to ts-node or npx:
npm install ts-node typescript (Install dependencies)
node --require ts-node/register src/foo.ts (Add --require ts-node/register to "Node parameters")
This answer may be premature, but deno supports running both TS and JS out of the box.
Based on your development environment, moving to Deno (and learning about it) might be too much, but hopefully this answer helps someone in the future.
Just in case anyone is insane like me and wants to just run typescript script as though it was a .js script, you can try this. I've written a hacky script that appears to execute the .ts script using node.
#!/usr/bin/env bash
NODEPATH="$HOME/.nvm/versions/node/v8.11.3/bin" # set path to your node/tsc
export TSC="$NODEPATH/tsc"
export NODE="$NODEPATH/node"
TSCFILE=$1 # only parameter is the name of the ts file you created.
function show_usage() {
echo "ts2node [ts file]"
exit 0
}
if [ "$TSCFILE" == "" ]
then
show_usage;
fi
JSFILE="$(echo $TSCFILE|cut -d"." -f 1).js"
$TSC $TSCFILE && $NODE $JSFILE
You can do this or write your own but essentially, it creates the .js file and then uses node to run it like so:
# tsrun myscript.ts
Simple. Just make sure your script only has one "." else you'll need to change your JSFILE in a different way than what I've shown.
Install ts-node node module globally.
Create node runtime configuration (for IDE) or use node in command line to run below file js file (The path is for windows, but you can do it for linux as well)
~\AppData\Roaming\npm\node_modules\ts-node\dist\bin.js
Give your ts file path as a command line argument.
Run Or Debug as you like.
Create your TypeScript file (ex. app.ts)
npm i -D typescript ts-node -> to install the dev dependencies local
npx nodemon app.ts
Using nodemon, automatically recompile app.ts every time you change the file
Here is the command
tsc index.ts --outDir .temp && node .temp/index.js && rm -rf .temp
<<<<<<<<< Compile >>>>>>>>> <<<<<<< Run >>>>>>> << Clean >>
There is also an option to run code directly from the CLI, not the *.ts file itself.
It's perfectly described in the ts-node manual.
As a first step, install ts-node globally via npm, yarn, or whatever you like.
...and now just use ts-node -e 'console.log("Hello, world!")' (you may also add the -p flag for printing code)
This little command is perfect for checking, does everything installed fine. And for finding some other error, relevant with tsconfig.json options.
We can run it using nodemon as well
nodemon ./filepath/filename.ts
This question was posted in 2015. In 2018, node recognizes both .js and .ts. So, running node file.ts will also run.

NPM: module installation for a command line node tool?

I have a command line tool written in node. I'd like to:
Have the app be able to load its dependencies and work. Currently, after npm install -g <somemodule> that module is still not available. Things didn't used to work this way.
Not have to run npm link on every folder, as I have read in the NPM 1.0 docs. The above docs also talks about $PATH, which seems unrelated to the topic as I care about node modules, not binaries.
How can/should a node command line tool handle its dependencies so that the command line tool can run from any directory?
You can add following in the main file of your node.js app, assuming your file name is node-binary.js.
#! /usr/bin/env node
// your app code
console.log('TEST node binary');
And, in package.json file you need to specify which is the entry point of your app
...
"preferGlobal": "true",
"bin": {
"node-binary": "node-binary.js"
},
...
and run the command npm link in the app directory. You should now be able to use node-binary command from any directory.
Hope that helps... :)

Resources