搭建一套支持TS的Node运行环境

前言

前几天遇到一个批量处理文件的需求,需要用node来实现,由于第一次接触它,没啥经验,又想写TS,于是就搭建了这么一套环境,期间也踩了挺多坑。

本文就跟大家分享下我的实现过程,欢迎各位感兴趣的开发者阅读本文。

环境搭建

首先我们创建一个空项目,在项目根目录创建package.json文件。在文件中填写项目的基础信息,如下所示:

{
  "name": "ts-node-utils",
  "version": "1.0.0",
  "description": "一些用ts写的可执行node脚本",
  "private": false,
  "license": "MIT"
}

上述代码中,各个字段的含义:

  • name 项目名
  • version 版本号
  • description 项目的描述
  • private 项目是否为私有状态
  • license 项目所采用的开源协议

按照自己的实际情况填写即可。

注意:你也可以使用yarn或者npm来初始化一个项目,在初始化过程中会提示你填写上述信息,命令为: yarn init | npm init

安装依赖

开源社区中有一个名为ts-node的库,它可以运行时解析ts,执行node的API,读完它的文档后,我们知道了在项目中安装它的方法,如下所示:

npm install -D typescript | yarn add typescript -D
npm install -D ts-node | yarn add ts-node -D
npm install -D tslib @types/node | yarn add tslib @types/node -D

上述命令中,我们安装了typescript,ts-node,tslib,@types/node这四个包,上述代码中的|是或者的意思,提供了npm的安装方法和yarn的安装方法,根据自己的实际需求执行对应的命令即可。

创建TS配置文件

在项目根目录创建tsconfig.json文件,具体的配置请移步tsconfig配置,我的配置文件如下所示:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "outDir": "./output",
    "types": [
      "webpack-env"
    ],
    "declaration": true,// 是否生成声明文件
    "declarationDir": "dist/type",// 声明文件打包的位置
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

编写测试代码

接下来,我们引入几个node的api来测试下上面的配置,看看能否正常运行。

如下所示,我在项目根目录创建了handle-themes-file文件夹,并在文件夹内部创建了lib文件夹和main.ts文件。

我们在lib文件夹下创建HandleThemes.ts文件,在这里编写一个获取文件夹下所有文件的方法,代码如下所示:

import { readdir } from "fs";

export default class HandleThemes {
  public getFolderFiles(path: string): void {
    readdir(path, (errStatus, fileList) => {
      if (errStatus !== null) {
        console.log("文件读取失败, 错误原因: ", errStatus);
        return;
      }
      console.log("文件读取成功", fileList);
    });
  }
}

最后,我们在main.ts下导入HandleThemes.ts,实例化后,调用getFolderFiles方法,如下所示:

import HandleThemes from "./lib/HandleThemes";

const handles = new HandleThemes();
handles.getFolderFiles("/Users/likai/Desktop/测试文件夹");

运行报错

在ts-node的文档中,我们知道了在终端/命令行进入我们的项目根目录,执行ts-node xxx.ts就能执行了,此处我们运行的文件是main.ts文件,那么要执行的命令就为:

ts-node handle-themes-file/main.ts

然而,事情并没我们现象的那么顺利,命令执行后,会看到如下所示的报错:

image-20210814145833539
(node:65039) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/likai/Documents/WebProject/ts-node-utils/handle-themes-file/main.ts:1
import HandleThemes from "./lib/HandleThemes";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:979:16)
    at Module._compile (internal/modules/cjs/loader.js:1027:27)
    at Module.m._compile (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:1311:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:1314:12)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at main (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/bin.ts:331:12)
    at Object.<anonymous> (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/bin.ts:482:3)

修改项目类型声明

看报错提示,让在package.json中添加一个type类型为module的字段,那么我们就声明下,如下所示:

{
  "type": "module"
}

当我再次运行时,它又换了新的报错。

image-20210814150542095
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/likai/Documents/WebProject/ts-node-utils/handle-themes-file/main.ts
    at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:71:15)
    at Loader.getFormat (internal/modules/esm/loader.js:102:42)
    at Loader.getModuleJob (internal/modules/esm/loader.js:231:31)
    at async Loader.import (internal/modules/esm/loader.js:165:17)
    at async Object.loadESM (internal/process/esm_loader.js:68:5) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

百度这个报错时,基本上就一篇文章抄来抄去的

image-20210814150819838

这篇文章说是因为找不到导入的模块,需要在导入时添加文件的后缀名,且需要把ts后缀换成js,我跟着操作后,报错依然存在。

查阅官方文档,更换执行命令

我又看了一圈官方文档,说是让用node --loader ts-node/esm来执行

image-20210814152034219

于是,我就换了这个命令,结果又换了新错误。

image-20210814152131588
(node:65419) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:693
    return new TSError(diagnosticText, diagnosticCodes);
           ^
TSError: ⨯ Unable to compile TypeScript:
handle-themes-file/lib/HandleThemes.ts:1:25 - error TS2307: Cannot find module 'fs' or its corresponding type declarations.

1 import { readdir } from "fs";
                          ~~~~
handle-themes-file/lib/HandleThemes.ts:5:20 - error TS7006: Parameter 'errStatus' implicitly has an 'any' type.

5     readdir(path, (errStatus, fileList) => {
                     ~~~~~~~~~
handle-themes-file/lib/HandleThemes.ts:5:31 - error TS7006: Parameter 'fileList' implicitly has an 'any' type.

5     readdir(path, (errStatus, fileList) => {
                                ~~~~~~~~

    at createTSError (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:693:12)
    at reportTSError (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:697:19)
    at getOutput (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:884:36)
    at Object.compile (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/index.ts:1186:30)
    at /Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/src/esm.ts:146:38
    at Generator.next (<anonymous>)
    at /Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/dist/esm.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/dist/esm.js:4:12)
    at transformSource (/Users/likai/Documents/WebProject/ts-node-utils/node_modules/ts-node/dist/esm.js:88:16) {
  diagnosticText: "\x1B[96mhandle-themes-file/lib/HandleThemes.ts\x1B[0m:\x1B[93m1\x1B[0m:\x1B[93m25\x1B[0m - \x1B[91merror\x1B[0m\x1B[90m TS2307: \x1B[0mCannot find module 'fs' or its corresponding type declarations.\n" +
    '\n' +
    '\x1B[7m1\x1B[0m import { readdir } from "fs";\n' +
    '\x1B[7m \x1B[0m \x1B[91m                        ~~~~\x1B[0m\n' +
    "\x1B[96mhandle-themes-file/lib/HandleThemes.ts\x1B[0m:\x1B[93m5\x1B[0m:\x1B[93m20\x1B[0m - \x1B[91merror\x1B[0m\x1B[90m TS7006: \x1B[0mParameter 'errStatus' implicitly has an 'any' type.\n" +
    '\n' +
    '\x1B[7m5\x1B[0m     readdir(path, (errStatus, fileList) => {\n' +
    '\x1B[7m \x1B[0m \x1B[91m                   ~~~~~~~~~\x1B[0m\n' +
    "\x1B[96mhandle-themes-file/lib/HandleThemes.ts\x1B[0m:\x1B[93m5\x1B[0m:\x1B[93m31\x1B[0m - \x1B[91merror\x1B[0m\x1B[90m TS7006: \x1B[0mParameter 'fileList' implicitly has an 'any' type.\n" +
    '\n' +
    '\x1B[7m5\x1B[0m     readdir(path, (errStatus, fileList) => {\n' +
    '\x1B[7m \x1B[0m \x1B[91m                              ~~~~~~~~\x1B[0m\n',
  diagnosticCodes: [ 2307, 7006, 7006 ]
}

正确的解决方案

折腾到这里,我已经用尽自己所能去找解决方案了,仍然没解决,只好去寻求网友的帮助,最后在@皮的很的帮助下,解决了这个问题。

在他的帮助下,我才知道,原来要改tsconfig.json的配置才行😂。

image-20210814154002027
image-20210814153733411

要改的地方有2处,如下所示:

{
  "compilerOptions": {
      "module": "CommonJS",
      "types": [
        "node"
        ]
  }
}

做完上述配置后,我们把刚才在package.json修改的项目类型删掉,以及在导入时添加的js后缀也一起删掉。

最后在终端执行ts-node handle-themes-file/main.ts,成功执行。

image-20210814154507894

添加运行变量

每次都要进入终端,敲一边命令才能执行ts文件,这太麻烦了,我希望的是可以在编辑器中点一下就能运行当前可视区域的ts文件。

在WebStorm中是支持这个操作的,只需简单的配置即可,步骤如下:

  • 在package.json中配置一条脚本运行命令
{
   "ts-node": "ts-node"
}
  • 打开Run/Debug Configurations面板
image-20210814155153643

在弹出的面板中,添加一条执行命令。

image-20210814155236491
image-20210814155306162

填写命令名称、执行脚本、环境变量,最后点OK即可完成配置。

image-20210814155508125

配置完成后,我们就可以通过点击工具栏的 运行图标 来运行了。

image-20210814155714093

如果你没玩过webstorm,可以移步我的另一篇文章:合理使用WebStorm-环境配置篇,亲自上手体验一波。

项目地址

本文创建的项目,GitHub地址为:ts-node-utils

项目中还加入了其他的一些规范代码的东西,如果你对此感兴趣,请移步:

写在最后

至此,文章就分享完毕了。

我是神奇的程序员,一位前端开发工程师。

如果你对我感兴趣,请移步我的个人网站,进一步了解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345