🗼

babel-nodeとtscでdeno runみたいなことをNode.jsでしたい

2021/11/13に公開

はじめに

Denoを使っていて、ちょっとファイルを加工して画面に出す、みたいな1ファイルなTypeScriptを書いて deno run hoge.ts するのが便利でした。そういったことを、Node.jsでできないかな〜と思い、最小限でやってみました。

$ yarn init
yarn init v1.22.17
# ... ひたすらEnter

babel-node

Node.jsのCLIで、Babelのpresetやpluginを使ったコンパイルした上で実行するbabel-node
を入れます。

$ yarn add -D @babel/core @babel/node
$ yarn babel-node
# ...
babel >

TypeScriptからJavaScriptに変換する@babel/preset-typescriptと、新しい文法を使えるようにしてくれる@babel/preset-envを入れます。

yarn add -D @babel/preset-env @babel/preset-typescript

これらを .babelrc のpresetsに追加します。

.babelrc
{
  "presets": ["@babel/preset-env", "@babel/preset-typescript"]
}

ためしに実行してみましょう。以下のようなhello worldなスクリプトを用意しました。

index.ts
const greet = (msg: string) => {
  return `Hello, ${msg}!`;
}

console.log(greet(["World"]));

babel-nodeに --extensions ".ts" オプションをつけて実行します。

$ yarn babel-node --extensions ".ts" index.ts
# ...
Hello, World!
✨  Done in 0.98s.

無事に実行されました。greet 関数の引数である msg の型は string であるのに、Arrayを渡してもエラーになりませんでした。preset-typescriptは、変換のみを担うので、型が間違っていても実行できます。型チェックは別で行う必要があります。

tsc

TypeScriptのコンパイラであるtscをインストールします。

$ yarn add -D typescript

tscでは --noEmit オプションをつけることで、型チェックのみを行えます。先程のスクリプトを型チェックしてみると...

$ yarn tsc --noEmit index.ts
# ...
index.ts:5:19 - error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'string'.

5 console.log(greet(["World"]));
                    ~~~~~~~~~


Found 1 error.

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

無事に型エラーが検出されました。これをpreset-typescriptと組み合わせて、スクリプトの型チェックと実行を行います。

npm scripts

package.json のscriptsに、型チェックと実行を書いて行きます。

{
  ...
  "scripts": {
    "deno:typecheck": "tsc --noEmit",
    "deno:exec": "babel-node --extensions \".ts\""
  }
  ...
}

これら2つのscriptをまとめて実行するために、npm-run-allを入れます。

$ yarn add -D npm-run-all

scriptsに書き足します。npm-run-allでは -- でコマンド引数を受け取り {1} のようにして、他のscriptに渡せます。

{
  ...
  "scripts": {
    "deno:typecheck": "tsc --noEmit",
    "deno:exec": "babel-node --extensions \".ts\"",
    "deno-run": "npm-run-all -s \"deno:* {1}\" --"
  },
  ...
}

実行

準備が整ったので、先程のスクリプトを実行してみましょう。

$ yarn deno-run index.ts
# ...
index.ts:5:19 - error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'string'.

5 console.log(greet(["World"]));
                    ~~~~~~~~~


Found 1 error.

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
ERROR: "deno:typecheck index.ts" exited with 2.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

エラーが2個でちゃうのがアレですが、ちゃんと型チェックで止まりました。文字列を渡すようにスクリプトを修正します。

index.ts
const greet = (msg: string) => {
  return `Hello, ${msg}!`;
}

// console.log(greet(["World"]));
console.log(greet("World"));

無事に実行されます。

$ yarn deno-run index.ts
# ...
Hello, World!
✨  Done in 5.32s.

babel-nodeとtscで deno run コマンドが実現できました!!

まとめ

package.jsonと.babelrcは以下のようになりました。

package.json
{
  "name": "hoge",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "deno:typecheck": "tsc --noEmit",
    "deno:exec": "babel-node --extensions \".ts\"",
    "deno-run": "npm-run-all -s \"deno:* {1}\" --"
  },
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/node": "^7.16.0",
    "@babel/preset-env": "^7.16.0",
    "@babel/preset-typescript": "^7.16.0",
    "npm-run-all": "^4.1.5",
    "typescript": "^4.4.4"
  }
}
.babelrc
{
  "presets": ["@babel/preset-env", "@babel/preset-typescript"]
}

おわりに

ts-nodeを使えば良いのでは??...そのとおり!!

$ yarn add -D typescript ts-node
$ yarn ts-node index.ts
# ...
Hello, World!
✨  Done in 1.90s.

わけあってpreset-typescriptとtscでやりたかったんですわ...

Discussion