首页 > 解决方案 > 为什么我全局安装的 npm 包不能调用 ts-node?

问题描述

好的,我正在尝试创建一个供我自己使用的 CLI 工具,基本上它只是解析 hcitool 的标准输出(报告周围的蓝牙设备)。

该工具可以在这里找到:https ://github.com/lu4/hcitool-reader

该工具预计将使用ts-node(ts-node 允许动态运行 TypeScript 代码)运行。

使用以下命令从本地磁盘全局安装时,我的包工作正常:

bash> npm i -g /path/to/local/disk/hcitool-reader/repository

可以通过执行以下命令来验证该工具(安装后):

bash> hcitool-reader

但是,如果我删除旧版本并从 NPM 安装相同的代码:

bash> npm uninstall -g hcitool-reader && npm i -g hcitool-reader

包开始抛出 node.js 语法异常,指出 typescript 语法错误(这是 ts-node 未正确注册的标志)。

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)

这个错误的奇怪之处在于代码仅在位于 NPM 的全局包文件夹时才停止工作,在我的例子中:

/home/pi/.config/versions/node/v12.8.0/lib/node_modules/hcitool-reader

在所有其他情况下,从所有其他位置代码都可以正常工作。

问: ts-node 出了什么问题?

编辑 在我看来,这是一个ts-node问题,我在他们的仓库中创建了一个问题,等待ts-node团队的一些评论

标签: node.jstypescriptnpmts-node

解决方案


这是因为您编写了bin/launch.js要运行的脚本node,而不是ts-node. 脚本的第一行是:

#!/usr/bin/env node
//              ^^ launch with node.js

如果你想用 ts-node 启动它,你应该把它改成:

#!/usr/bin/env ts-node

壳牌sh-bang线

脚本的第一行就是通常所说的sh-bang行(有很多不同的拼写)。它是 sh/csh 兼容的语法,已被大多数常见的 shell 语言继承:bash/ksh/tcsh/tcl/node。

严格来说,它不是 javascript 语法,应该会导致语法错误,但如果它是第一行代码,node.js 会特别容忍它。它会导致 js 文件被解释为多语言源(源代码在不止一种编程语言中有效)。

shell(bash/ksh/tcsh 等)假定所有脚本都是用 shell 自己的语言编写的(bash 代表 bash,ksh 代表 ksh 等)。sh-bang 语法实际上使您的文件成为有效的 shell 脚本源文件。在所有常见的 unix shell 中,sh-bang 命令意味着:

评估此字符串,然后将此文件的其余部分视为注释,然后将此文件作为最后一个参数传递给正在评估的字符串。

因此,如果您的文件的第一行是:

#! /usr/bin/env wget https://stackoverflow.com/questions/57600624

该脚本将下载此页面。

/usr/bin/env部分是运行env加载当前用户环境的命令,然后执行该行的其余部分。我们运行的原因env是因为默认情况下 sh-bang 语法不会加载您的用户环境,这意味着您需要传递绝对路径,node或者ts-node如果您在不同的发行版(ubuntu 与 redhat)上运行脚本,或者如果您安装nodets-node不同的方式(apt-get vs nvm)。因此,首先调用env确保您的$PATH环境变量设置正确,并且在所有 Unix/类 Unix 系统env始终安装在/usr/bin.

NPM 不是节点包管理器!

NPM 不是,也仍然不是专门设计为一个节点包管理器。是的,它有很多支持 node.js 的有用功能(有些像 node_modules 甚至在 node.js 中被硬编码)但它实际上并不关心你的软件是用什么语言编写的。它是你的操作系统的包管理器,就像aptyum(或brew为您的 Mac 用户)。因此,它对运行全局可执行文件没有特定于节点的支持 - 它仅取决于您的 OS/shell 已经支持的内容。在这种情况下,它取决于 sh-bang 线。

安装全局脚本时npm不要使用startpackage.json 中的命令。它直接运行您的脚本。这是因为它不是特定于节点的包管理器,因此它安装的可能是 Python 脚本或 shell 脚本或以汇编语言编写的二进制可执行文件。这就是为什么您需要确保您的"bin"脚本通常可以由您的操作系统执行。

通常可执行意味着如果你这样做:

./bin/launch.js

那么你的操作系统可以像你系统上的所有其他程序一样执行它:nodeapt-get等等git。不正常会是这样的:

ts-node ./bin/launch.js

或者:

java -jar ./minecraft.jar

推荐阅读