Reputation: 15032
Ok, I'm trying to create a CLI tool for my own use, basically it just parses standard output of hcitool (which reports on surrounding bluetooth devices).
The tool can be found here: https://github.com/lu4/hcitool-reader
The tool is expected to be run using ts-node
(ts-node allows running TypeScript code on the fly).
My package works fine when installed globally from a local disk using following command:
bash> npm i -g /path/to/local/disk/hcitool-reader/repository
The tool can be validated (after it is installed) by executing:
bash> hcitool-reader
However if I remove old version and install the same code from NPM:
bash> npm uninstall -g hcitool-reader && npm i -g hcitool-reader
The package starts throwing node.js syntax exceptions pointing out that typescript syntax is wrong (which is a sign that ts-node wasn't registered properly).
bash> hcitool-reader
Trying to register ts-node with tsconfig.json found at:
/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader/tsconfig.json
/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader/src/index.ts:1
import 'reflect-metadata';
^^^^^^^^^^^^^^^^^^
SyntaxError: Unexpected string
at Module._compile (internal/modules/cjs/loader.js:811:22)
at Module._extensions..js (internal/modules/cjs/loader.js:879:10)
at Object.require.extensions.<computed> [as .ts] (/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader/node_modules/ts-node/src/index.ts:465:14)
at Module.load (internal/modules/cjs/loader.js:731:32)
at Function.Module._load (internal/modules/cjs/loader.js:644:12)
at Module.require (internal/modules/cjs/loader.js:771:19)
at require (internal/modules/cjs/helpers.js:68:18)
at Object.<anonymous> (/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader/bootstrap.js:15:20)
at Module._compile (internal/modules/cjs/loader.js:868:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:879:10)
The weird thing about this error is that the code stops working only when it is located NPM's global package folder, in my case:
/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader
in all other cases, from all other locations the code works fine.
Q: What's wrong with ts-node?
EDIT
It seems to me that it is a ts-node
issue I've created an issue in their repo, waiting for some comments from ts-node
team
Upvotes: 0
Views: 2487
Reputation: 113906
It's because you've written your bin/launch.js
script to run with node
, not ts-node
. The first line of your script is:
#!/usr/bin/env node
// ^^ launch with node.js
If you want to launch it with ts-node you should change it to:
#!/usr/bin/env ts-node
The first line of your script is what's commonly known as the sh-bang line (there are lots of different spellings for this). It is sh/csh compatible syntax that's been inherited by most common shell languages: bash/ksh/tcsh/tcl/node.
Strictly speaking it is not javascript syntax and should cause a syntax error but node.js specifically tolerates it if it's the first line of code. It causes the js file to be interpreted as a polyglot source (source code valid in more than one programming language).
The shell (bash/ksh/tcsh etc.) assumes that all scripts are written in the shell's own language (bash for bash, ksh for ksh etc.). The sh-bang syntax actually causes your file to be valid shell script source files. In all common unix shells the sh-bang command means:
Eval this string then treat the rest of this file as a comment then pass this file as the last argument to the string being eval'd.
So if your file's first line is:
#! /usr/bin/env wget https://stackoverflow.com/questions/57600624
The script will download this page.
The /usr/bin/env
part is to run the env
command which loads your current user environment then execute the rest of the line. The reason we run env
is because the sh-bang syntax by default doesn't load your user environment which means you need to pass the absolute path of node
or ts-node
which may break if you run the script on different distros (ubuntu vs redhat) or if you install node
or ts-node
different ways (apt-get vs nvm). So calling env
first makes sure your $PATH
environment variable is set up correctly and in all Unix/Unix-like systems env
is always installed in /usr/bin
.
NPM was not and is still not designed to specifically be a node package manager. Yes it has a lot of useful features supporting node.js (some like node_modules is even hardcoded in node.js) but it actually doesn't care what language your software is written in. It is a package manager for your OS, just like apt
and yum
(or brew
for you Mac users). As such it does not have node specific support for running global executables - it just depends on what your OS/shell already supports. In this case it depends on the sh-bang line.
When you install a global script npm
does not use the start
command in package.json. It runs your script directly. This is because it's not a node-specific package manager so what it installs could be a Python script or a shell script or a binary executable written in assembly. This is why you need to make sure your "bin"
script is normally executable by your OS.
Normally executable means that if you do this:
./bin/launch.js
then your OS can execute it just like all other programs on your system: node
, apt-get
, git
etc. Not normal would be something like:
ts-node ./bin/launch.js
or:
java -jar ./minecraft.jar
Upvotes: 2