node-fetchをwebpackすると実行できない・・・

##やりたかったこと
シンプルにとあるWebサイトをスクレイピングして、情報を取得したかった。
過去にもスクレイパーを作成したことはあるのだが、その時リクエストを送信するのにrequestモジュールを使用していたが、どうやらパッケージが2020年2月に廃止されたらしい…
そこで見つけたのが「node-fetch」。
試しにこれを使って指定したページのHTML要素を取得したい。

##使ってみる
###TypeScriptをインストール

```
yarn add typescript
```
詳しい環境構築はこの記事の本筋からズレるため省略させていただきます。

###node-fetchをインストール

```
yarn add node-fetch
```

ここでは詳しい使い方などは省略させていただきます。
[詳しくはこちらを](https://www.npmjs.com/package/node-fetch)

###サンプルコードを作成

```typescript:index.ts
import fetch from 'node-fetch';

const url: string = "https://qiita.com/";

fetch(url)
  .then(res => res.text())
  .then(body => console.log(body));
```

これでHTMLが取得できるはず。

###補足
JavaScriptで動的に生成されている要素を取得したい場合、ヘッドレスブラウザが必要になるようです。
この記事の本筋と異なるため、こちらも省略させていただきます。
（今回スクレイピングするサイトには必要なかったので詳しく調べてないです…ごめんなさい）

###実行してみる
ts-nodeを使用して実行してみる。

```json:package.json
"scripts": {
  "start": "ts-node ./index.ts",
  "build": "webpack"
}
```
実行

```
$ npm run start

<!DOCTYPE html><html><head><meta charset="utf-8" />
<title>Qiita</title><meta content="Qiitaは、プログラマのための技術情報共有サービスです。 プログラミングに関するTips、ノウハウ、メモを簡単に記録 &amp;amp; 公開することができます。" name="description" />
<meta content="width=device-width,initial-scale=1,shrink-to-fit=no" name="viewport" />
<meta content="#55c500" name="theme-color" />
<meta content="XWpkTG32-_C4joZoJ_UsmDUi-zaH-hcrjF6ZC_FoFbk" name="google-site-verification" />
<link href="/manifest.json" rel="manifest" />
<link href="/opensearch.xml" rel="search" title="Qiita" type="application/opensearchdescription+xml" />
<meta name="csrf-param" content="authenticity_token" />

....省略

```
うん。取れた。


##webpack
たったこれだけなのでJavaScriptで書いても良かったのですが、今回作るプロダクトではTypeScriptを使いたかった。
というわけでwebpackでトランスパイルします。


###まずはwebpackのツールをインストール

```
yarn add -D webpack webpack-cli ts-loader
```

webpack.config.jsを作成します。

```javascript:webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './index.ts',
  output: {
      path: path.join(__dirname, "dist"),
      filename: "index.js"
  },
  module: {
    rules: [{
      test: /\.ts$/,
      use: [
        {loader: 'ts-loader'}
      ]
    }]
  },
  resolve: {
      modules: [
      "node_modules",
      ],
      extensions: [ '.ts', '.js', 'json' ]
  }
};
```

###webpackして実行

```
$ node ./dist/index.js

webpack:///./node_modules/node-fetch/browser.js?:11
        throw new Error('unable to locate global object');
        ^

Error: unable to locate global object
    at getGlobal (webpack:///./node_modules/node-fetch/browser.js?:11:8)
    at eval (webpack:///./node_modules/node-fetch/browser.js?:14:14)
    at Object../node_modules/node-fetch/browser.js (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:97:1)
    at __webpack_require__ (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:20:30)
    at eval (webpack:///./src/index.ts?:26:36)
    at Object../src/index.ts (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:109:1)
    at __webpack_require__ (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:20:30)
    at /Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:84:18
    at Object.<anonymous> (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:87:10)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
```
あれ？トランスパイルしたらエラーでた…

##解決方法
調査した結果、以下の方法で解決しました。

```javascript:webpack.config.js
// 省略

target: "node",   //この行を追加
module: {
    rules: [{
      test: /\.ts$/,
      use: [
        {loader: 'ts-loader'}
      ]
    }]
  },

....
```
どうやら、webpackでバンドルするときにtargetをnodeに指定しないとbrowserオブジェクトが取得できないようです。
省略しますが、webpack.config.jsを上記のように修正後、webpack→実行すると意図した結果が得られました。

targetをnodeに指定することで、Node.js環境で実行できるようにコンパイルしてくれるようです。
参考→[webpackドキュメント](https://webpack.js.org/concepts/targets/)


##追記
ちなみに、axiosでやってみても同じことが起こりました。
フロントでaxiosを使用していたときには、targetを指定しなくても意図した挙動をしてくれていたのですが、サーバーサイド（Node.js環境）で使用するには必要なようです。
いい勉強になりました。