【Nuxt.js】serverMiddlewareでエイリアスを効かせてimportする

# この記事について
[こちら](https://github.com/nuxt/nuxt.js/issues/4580)や[こちら](https://github.com/nuxt/nuxt.js/issues/7017)のIssuesように、Nuxt.jsのserverMiddleware側ではエイリアスが効かないようなので、ライブラリを使用してserverMiddleware側でもエイリアスを効かせられる方法をまとめました。

# 対象読者
* Nuxt.jsでserverMiddlewareの中でaliasを使用した実装を行う方
* Node.jsアプリケーションでaliasを使用したい方

# 環境
|内容|バージョン|
|---|---|
|node|16.14.2|
|nuxt|2.15.8|
|yarn|1.22.15|

# Nuxt.jsでエイリアス設定時のserverMiddlewareの挙動
以下のディレクトリ構造を想定します。

```
sampleProject
├server
| ├hello.ts
│ └index.ts
├common
│ └arrayUtil.ts
└front
  ├sample
  │ └hello.ts
  └plugins
    └sample.ts
```

<details><summary>「server/hello.ts」「common/arrayUtil.ts」「front/hello.ts」の中身</summary><div>

```server/hello.ts
export const sayHelloServer = () => {
  console.log('hello from server');
};
```

```common/arrayUtil.ts
export class ArrayUtil {
  public static isEmpty(values: undefined | null | unknown[]): boolean {
    return values === undefined || values === null || values.length === 0;
  }
};
```

```front/sample/hello.ts
export const sayHelloFront = () => {
  console.log('hello from front');
};
```
</div></details>

TypeScriptとしてのエイリアスは以下のように設定しております。

```js:tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@f/*": ["./front/*"],
      "@s/*": ["./server/*"],
      "@c/*": ["./common/*"]
    }
  }
}
```

tsconfig.jsonの記述のみでは、トランスパイル後のプログラム実行時にエイリアス解決ができないため、 `next.config.js` で、webpackを拡張してaliasの設定を行います。

```next.config.js
export default {
  srcDir: '~~/front',
  serverMiddleware: ['~~/server/'],
  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    extend(config) {
      config.resolve.alias['@f'] = path.resolve(__dirname, 'front');
      config.resolve.alias['@c'] = path.resolve(__dirname, 'common');
      config.resolve.alias['@s'] = path.resolve(__dirname, 'server');
    },
  },
};
```

以下のように、front/serverそれぞれでエイリアスを使用してimportを記述して実行してみると、frontでのimportは成功して、serverMiddlewareでのimportは失敗してしまいます。

（うまくいく）

```front/plugins/sample.ts
import { ArrayUtil } from '@c/arrayUtil';
import { sayHelloFront } from '@f/sample/hello';

console.log(ArrayUtil.isEmpty(null)); // true
sayHelloFront(); // hello from front
```

（serverMiddleware側は失敗する）

```server/index.ts
import express from 'express';
// Error: Cannot find module '@c/arrayUtil' のエラーが発生する
import { ArrayUtil } from '@c/arrayUtil';
import { sayHelloServer } from '@s/hello';

console.log(ArrayUtil.isEmpty(null));
sayHelloServer();

const app = express();
export default app;
```

# `module-alias` の使用
`module-alias` というライブラリを使用して、serverMiddleware側でもエイリアスを使用できるようにします。

```
yarn add module-alias
```

`package.json` に使用するエイリアスを記述します。

```js:package.json
{
  "scripts": {
    // 略
  },
  "dependencies": {
    // 略
  },
  "devDependencies": {
    // 略
  },
  // "_moduleAliases"に使用するエイリアスを記述
  "_moduleAliases": {
    "@s": "./server", // 「@s」を記述することでserverフォルダへのパスを表す
    "@c": "./common"
  }
}
```

エントリポイントで `module-alias/register` をインポートします。

```ts:index.ts
import 'module-alias/register';

import express from 'express';
// エイリアスを効かせて実行可能
import { ArrayUtil } from '@c/arrayUtil';
import { sayHelloServer } from '@s/hello';

console.log(ArrayUtil.isEmpty(null)); // true
sayHelloServer(); // hello from server

const app = express();
export default app;
```

`next.config.js` 側ではserverMiddlewareで使用するエイリアスは設定不要となるため、以下の記述となります。

```nuxt.config.js
export default {
  srcDir: '~~/front',
  serverMiddleware: ['~~/server/'],
  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    extend(config) {
      // frontディレクトリで使用するエイリアスのみを記述
      config.resolve.alias['@f'] = path.resolve(__dirname, 'front');
      config.resolve.alias['@c'] = path.resolve(__dirname, 'common');
    },
  },
};
```

# 参考
[Cannot use aliases inside serverMiddleware](https://github.com/nuxt/nuxt.js/issues/4580)
[Cannot resolve '@' in serverMiddleware](https://github.com/nuxt/nuxt.js/issues/7017)