TypeORMのMigrationでCannot use import statement outside a moduleエラーの対処法

## TL;DR
あまり綺麗ではないですが、これで解決しました。
`typeorm`コマンドを使用せず、直接`ts-node`で`typeorm`を呼び出します。

```bash
$ ts-node ./node_modules/typeorm/cli.js migration:generate -n 'User'
```

NestJSを使用しているときにこのエラーに遭遇した場合は、ページ下部の[ormconfig.jsonの書き換え](#手順3)を試してみてください。

## よくみたらドキュメントに書いてあった...

* [TypeORM > If entities files are in typescript](https://typeorm.io/#/using-cli/if-entities-files-are-in-typescript)

ドキュメントの推奨している方法だと、、、

### STEP 1
ts-nodeをグローバルにインストール

```bash
$ npm install -g ts-node
```

### STEP 2
`package.json`にスクリプトを書き加える

```json
"scripts": {
    ...
    "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js"    
}
```

### STEP 3
そしたら、こんな感じで実行する
`--`は必要なので注意！

```sh
$ npm run typeorm migration:generate -- -n UserMigration
```


## 遭遇したエラー内容

```
$ npx typeorm init
$ npm install
$ typeorm migration:generate -n UserMigration

Error during migration generation:
/Users/michinosuke/archive/web-app/typeorm-playground/src/entity/role.ts:1
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:984:16)
    at Module._compile (internal/modules/cjs/loader.js:1032:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
    at Module.load (internal/modules/cjs/loader.js:933:32)
    at Function.Module._load (internal/modules/cjs/loader.js:774:14)
    at Module.require (internal/modules/cjs/loader.js:957:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at /usr/local/lib/node_modules/typeorm/util/DirectoryExportedClassesLoader.js:42:39
    at Array.map (<anonymous>)
    at Object.importClassesFromDirectories (/usr/local/lib/node_modules/typeorm/util/DirectoryExportedClassesLoader.js:42:10)
```



## 上のやつで解決しないとき
一応、他の可能性も考えてみます。

### ts-nodeも直接パスを指定する

`ts-node`がグローバルにインストールされていない場合は、これで解決するかも。

```
$ ./node_modules/.bin/ts-node ./node_modules/typeorm/cli.js migration:generate -n 'User'
```

### 環境変数をつける

* `TS_NODE_PROJECT`で`tsconfig.json`のパスを渡します。
* `TS_NODE_TRANSPILE_ONLY`に`true`を指定して、TypeScriptの`transpileModule`を使ってみる。

> **transpileModuleについての参考**
>
> * [ts-node Typechecking](https://typestrong.org/ts-node/docs/options/)
> * [TypeScriptコンパイラとそのAPI](https://m-hiyama-memo.hatenablog.jp/entry/20171222/1513910292)

```bash
TS_NODE_PROJECT=tsconfig.json TS_NODE_TRANSPILE_ONLY=true ./node_modules/.bin/ts-node ./node_modules/typeorm/cli.js migration:generate -n 'User'
```

### tsconfig.jsonを書き換える

`compilerOptions`の`module`が`commonjs`以外になっているときは、`commonjs`に変更してみてください。

```json:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs"
  }
}
```


### さらに他の解決法
一旦TypeScriptで`.ts`ファイルを`.js`にトランスパイルしちゃう方法です。綺麗な解決策ではありませんが、ほぼ確実に解決できると思います。

もしあなたがNestJSなどを使用してこのエラーに遭遇している場合、下の`手順3`の`ormconfig.json`の書き換えだけで解決する可能性が高いです。

#### 手順1

`tsconfig.json`の`compilerOptions`の`skipLibCheck`に`true`を指定します。
あまりいい方法じゃないんですが、これを指定しないとトランスパイルにエラーが大量に出ます。
> 参考: [ERROR TS1086: An accessor cannot be declared in an ambient context](https://stackoverflow.com/questions/67087529/error-ts1086-an-accessor-cannot-be-declared-in-an-ambient-context)

```json
{
   "compilerOptions": {
      "skipLibCheck": true
   }
}
```

#### 手順2

トランスパイルします。

```sh
$ tsc
```

> 上のコマンドが打てなかったら、TypeScriptをグローバルインストールしてください。

> ```sh
$ npm i -g typescript
```

実行できたら、プロジェクトルートに`build`ディレクトリが作成されて、`js`ファイルがたくさん入ってるはずです。

#### 手順3
`ormconfig.json`を以下のように書き換えます。
* `src/` → `build/`
* `.ts` → `.js`

Before

```json
{
   "type": "sqlite",
   "database": "database.sqlite",
   "synchronize": true,
   "logging": false,
   "entities": [
      "src/entity/**/*.ts"
   ],
   "migrations": [
      "src/migration/**/*.ts"
   ],
   "subscribers": [
      "src/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir": "src/entity",
      "migrationsDir": "src/migration",
      "subscribersDir": "src/subscriber"
   }
}
```

After

```json
{
   "type": "sqlite",
   "database": "database.sqlite",
   "synchronize": true,
   "logging": false,
   "entities": [
      "build/entity/**/*.js"
   ],
   "migrations": [
      "build/migration/**/*.js"
   ],
   "subscribers": [
      "build/subscriber/**/*.js"
   ],
   "cli": {
      "entitiesDir": "build/entity",
      "migrationsDir": "build/migration",
      "subscribersDir": "build/subscriber"
   }
}
```

#### 手順4
`node`で`typeorm`を呼び出します。

```sh
$ node ./node_modules/typeorm/cli.js migration:generate -n 'User'
```


## 参考
* [migration:generate throws a SyntaxError with clean project template #5087](https://github.com/typeorm/typeorm/issues/5087)
* [同じようなissue一覧](https://github.com/typeorm/typeorm/issues?q=Cannot+use+import+statement+outside+a+module)