0
2

ts-nodeでサクッとWEBサイトのTypeScriptやNode.jsサンプルを動かすの巻

Last updated at Posted at 2023-11-16

はじめに

以前、サイトに掲載されているサンプルJavaプログラムをサクっと動かせるとしてjshellの記事を投稿しましたが、今回はTypeScriptをサクっと動かせるツールとしてts-nodeの記事を書きました。

ts-nodeをREPLモードとtsファイル指定モードで設定を変えないといけないのがちょっと今一な記事となっています。だれか改善策を知っている方がいたら是非ご教示下さい。

独り言:ts-nodeをREPLで使う人って少ないのかなー。

バージョン情報

  • Nodejs:20.x
  • TypeScript: 5.2.2
  • ts-node: 10.9.1

環境構築

Node.js

Node.jsインストール

ここからインストーラダウンロードしてインストール。

npm initコマンドでpackage.jsonを作成する

npm init実行サンプル
npm
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (ts-node) ts-node-verify-project
version: (1.0.0)
description: ts-node検証プロジェクト
entry point: (index.js)                                                                                                                                                                                                                  
test command:                                                                                                                                                                                                                            
git repository:                                                                                                                                                                                                                          
keywords:                                                                                                                                                                                                                                
author:                                                                                                                                                                                                                                  
license: (ISC)                                                                                                                                                                                                                           
About to write to C:\ts-node\package.json:

{
  "name": "ts-node-verify-project",
  "version": "1.0.0",
  "description": "ts-node検証プロジェクト",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) yes

TypeScript

TypeScriptインストール

npm install -D typescript

TypeScript初期設定

tsc --init

「tsconfig.json」ファイルが作成されるので以下の箇所を修正する。

"target": "es2016"
"module": "commonjs"
// "moduleResolution": "node10"

"target": "es2022"  // top-level awaitが使いたいから
"module": "Node16"
"moduleResolution": "Node16"    // コメントアウトも外す

Node.jsのTypeScript用定義をインストール

npm install -D @types/node

ts-node

ts-nodeをインストール

npm install -D ts-node

ts-nodeをREPLモードで使ってみる

残念ならが設定をREPLモードとtsファイル指定モードで変える必要がある。
詳細は発生したトラブルを参照。

REPLはNode.jsのREPLがベースとなっているので、こちらも参照するとよいかもしれません。

設定

tsconfig.json

{
  "compilerOptions": {
    たくさんの設定
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS",                    
      "allowImportingTsExtensions": true,     
    }
  }

pakages.json

typeの設定は不要。
ただし、自作のモジュールをimportしない場合は、削除しなくてもOK。
自作のモジュールとは、別tsファイルでexportするモジュールのこと。

//  "type": "module"    // ある場合は削除

起動

./node_modules/.bin/ts-node --esm 
// or ./node_modules/.bin/ts-node-esm 

ts-nodeを使ってみる

では、さっそく。

./node_modules/.bin/ts-node --esm
>

">"と表示され、入力待ち状態になる。

基本

> let i = 1
undefined

> i
1

> console.log(i)
1
undefined

> .type i
let i: number

> function add(a: number, b:number): number { return a + b }
undefined

> console.log(add(1, 2))
3
undefined

入力するたびに「undefined」と表示されるが気にしない。
jshellみないに変数一覧とかのコマンドはなさそうだ。
Node.jsのREPLのコマンドと同じコマンドは使える。
と思ったら.clearが正常に動作しない。。。これが原因か。。。.exitして再起動かな。

awaitも使えるよ

>  await Promise.resolve(123)
123

fsなんかのNode.jsコアモジュールはimportしなくても使えた

> let csvData = fs.readFileSync('sample1.csv', 'utf-8');
undefined
> console.log(csvData)
id1,data1
id2,data2
undefined
>

fsのpromise版の場合は、こんな感じ。

> let csvData2 = await fs.promises.readFile('sample1.csv', 'utf-8');
undefined
> console.log(csvData2)
id1,data1
id2,data2
undefined
>

自作モジュールimportも出来るぞ

ディレクトリ構成

C:/ts-node
│  package-lock.json
│  package.json
│  sample1.csv
│  tsconfig.json
│  
├─node_modules
│  
└─src
        main.ts
        Classes.ts
Classes.ts
Classes.ts
import { format as dateFormat } from 'date-fns';

export class Util {
  static getToday(): string {
    return dateFormat(new Date(), 'yyyy/MM/dd');
  }
}

export class Person {
  private _name : string;
  constructor(name : string) {
    this._name = name;
  }
  get name(): string {
    return this._name;
  }
}
> import { Person, Util } from './src/Classes.ts'
undefined

> let p = new Person('SP')
undefined

> p.name
'SP'

ライブラリも使えるぞ

今回は日付操作のライブラリ「date-fns」を使用。

date-fnsをインストール

npm i -D date-fns
> import { format as dateFormat } from 'date-fns'
undefined
> console.log(dateFormat(new Date(), 'yyyy/MM/dd'))
2023/11/15
undefined 

終了

> .exit

または、Ctrl+Cを2度。

ts-node tsファイル起動モード

tsconfig.json

"module": "CommonJS"などのts-nodeの設定は不要。

{
  "compilerOptions": {
    たくさんの設定
  },
// ts-nodeの設定は不要。   // あったら削除する
//  "ts-node": {
//    "compilerOptions": {
//      "module": "CommonJS"
//    }
  }  

pakages.json

typeを"module"設定する。

"type": "module" 

起動

node --no-warnings=ExperimentalWarning --loader ts-node/esm .\src\main.ts
main.ts
import { Person, Util } from './Classes.js';

// Personインスタンス生成
const person = new Person('AOKI');
console.log(person.name);

// 本日日付
console.log(Util.getToday());

発生したトラブル

その1

let i = 1などのステートメントを入力すると

export {};
^^^^^^
Uncaught SyntaxError: Unexpected token 'export'

とエラーが表示される。

解決策:tsconfig.jsonに"module": "CommonJS"を追加する。
解決ソース

{
  "compilerOptions": {
    たくさんの設定
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  }  
}

その2

その1の対応した状態でドキュメントに書かれている通り

./node_modules/.bin/ts-node --esm

で起動し自作モジュールをimportしてインスタンス生成するとエラーになる。

./node_modules/.bin/ts-node --esm
// or ./node_modules/.bin/ts-node-esm

> import { Person, Util } from './src/Classes.ts';
<repl>.ts:4:30 - error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
4 import { Person, Util } from './src/Classes.ts';

その8で解決。

その3

その2のimportをClasses**.ts**ではなく、Classes.jsにしたらimportは成功したがインスタンス生成でエラーになる。

// お、importは成功した!!
import { Person, Util } from './src/Classes.js'
undefined

// インスタンス生成
const p = new Person('SP')

node:internal/modules/cjs/loader:1051
  throw err;
  ^
Uncaught Error: Cannot find module './src/Classes.js'

その4

起動方法を変えて、node --loader ts-node/esmで起動したらワーニングが発生した。

node --loader ts-node/esm

Welcome to Node.js v20.9.0.
Type ".help" for more information.
> (node:34136) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)

ワーニングが発生しないように対応。

node --no-warnings=ExperimentalWarning --loader ts-node/esm
Welcome to Node.js v20.9.0.
Type ".help" for more information.

追記

ワーニングを握りつぶさないで、ちゃんと指示通りに起動したほうがいいですね。

node --import "data:text/javascript,import { register } from 'node:module'; import { pathToFileURL } from 'node:url'; register('ts-node/esm', pathToFileURL('./'));" .\src\main.ts

その5

その4対応で自作モジュールをimportしたらエラーになった。

node --no-warnings=ExperimentalWarning --loader ts-node/esm

>import { Person, Util } from './src/Classes.ts'
import { Person, Util } from './src/Classes.ts'
^^^^^^
Uncaught:
SyntaxError: Cannot use import statement inside the Node.js REPL, alternatively use dynamic import: const { Person, Util } = await import("./src/Classes.ts");

エラーの指示通りimportの形式を変更したが別のエラーになった。

> const { Person, Util } = await import("./src/Classes.ts");
Uncaught ReferenceError: exports is not defined in ES module scope

その6

その4対応でtsファイル指定して起動するとエラーになった。

node --no-warnings=ExperimentalWarning --loader ts-node/esm src/main.ts

file:/src/main.ts:2
Object.defineProperty(exports, "__esModule", { value: true });
                      ^
ReferenceError: exports is not defined in ES module scope

その7

その1の対応をやめて、その4の対応で起動し、import形式はconst await import形式したら
import、インスタンスの生成はうまくいったが、REPLがtsモードになっていない。。。

node --no-warnings=ExperimentalWarning --loader ts-node/esm

// import成功!
> const { Person, Util } = await import("./src/Classes.ts");
undefined

// インスタンス生成も成功!
> const p = new Person('SP')
undefined

> p.name
'SP'

// tsモードになっていない!!。だめだ。。
> let i:number = 1
let i:number = 1
     ^
Uncaught SyntaxError: Unexpected token ':'

その8

ts-nodeのREPLをtsモードで起動するには ./node_modules/.bin/ts-node --esmで起動するしかなさそうだ。
しかしimportしたら、やはりエラーになった(その2と同じ)。

> import { Person, Util } from './src/Classes.ts'
<repl>.ts:4:30 - error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.

4 import { Person, Util } from './src/Classes.ts'

これは、tsconfig.jsonにallowImportingTsExtensionsを追加したら、解決した。

{
  "compilerOptions": {
    たくさんの設定
  },
  "ts-node": {
    "esm": true,
    "compilerOptions": {
      "module": "CommonJS",    // これもあわせて「その1」解決策のために追加
      "allowImportingTsExtensions": true,
    }
  }  
}

その9

その8の対策をしたらimportはうまくいったが、インスタンス作成でエラーになった。

./node_modules/.bin/ts-node --esm 

> import { Person, Util } from './src/Classes.ts'
undefined

> const p2 = new Person('SP')
Uncaught:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\myfolder\ts-node\src\Classes.ts 

package.jsonの"type": "module"を削除したらうまくいった。

tsconfingで"module": "CommonJS"設定しているからかなー。

トラブル解決策

残念ながらts-nodeをREPLモードで起動する場合とmain.tsファイルなどを指定して起動する場合で、設定を変える必要がある。

ts-node REPLモード

tsconfig.json

{
  "compilerOptions": {
    たくさんの設定
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS",                    // その1対策
      "allowImportingTsExtensions": true,      // その8対策
    }
  }

pakages.json

typeの設定は不要。

//  "type": "module"    // その9対策

起動

./node_modules/.bin/ts-node --esm 
// or ./node_modules/.bin/ts-node-esm 

ts-node tsファイル起動モード

tsconfig.json

"module": "CommonJS"などのts-nodeの設定は不要。

{
  "compilerOptions": {
    たくさんの設定
  },
// ts-nodeの設定は不要。   // その7で行った対策
//  "ts-node": {
//    "compilerOptions": {
//      "module": "CommonJS"
//    }
  }  

pakages.json

typeを"module"設定する。

"type": "module" 

起動

node --no-warnings=ExperimentalWarning --loader ts-node/esm .\src\main.ts
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2