Nuxt.jsからPuppeteerが実行できない TypeError: Cannot read property 'on' of undefined

# 現象

単体のCLIから起動すると成功するコードを、NuxtのserverMiddlewareとしてリクエスト経由で起動すると`page.goto`が動かない。

# 実行環境

* Windows7
* Node.js 11.0.0 (に上げたが変わらず)
* Puppeteer 1.9.0 (および`@next`も同様)
* Nuxt.js 2.2.0


# エラー出力


```
TypeError: Cannot read property 'on' of undefined
    at Function.addEventListener (~\node_modules\puppeteer\lib\helper.js:165:13)
    at new NavigatorWatcher (~\node_modules\puppeteer\lib\FrameManager.js:1146:14)
    at FrameManager.navigateFrame (~\node_modules\puppeteer\lib\FrameManager.js:75:21)
    at Frame.goto (~\node_modules\puppeteer\lib\FrameManager.js:404:37)
    at Frame.<anonymous> (~\node_modules\puppeteer\lib\helper.js:145:23)
    at Page.goto (~\node_modules\puppeteer\lib\Page.js:579:49)
    at Page.<anonymous> (~\node_modules\puppeteer\lib\helper.js:145:23)
(中略: 呼び出し元スクリプト)
    at process.internalTickCallback (internal/process/next_tick.js:77:7)
```

# エラー箇所の原因

`puppeteer\lib\Connection.js`
の

```js
  /**
   * @param {!CDPSession} session
   * @return {!Connection}
   */
  static fromSession(session) {
    let connection = session._connection;
    // TODO(lushnikov): move to flatten protocol to avoid this.
    while (connection instanceof CDPSession)
      connection = connection._connection;
    return connection;
  }
```

このループ文が、Nuxt経由の場合は、connectionがConnectionのインスタンスにも関わらず条件がtrueとなり掘り続けて、undefinedまで潜って終わるため。

`console.log`で見るとConnectionなのに`instanceof CDPSession`が通る理由はprototypeに明るくないのでよくわからない…

コードの意味を汲み取れないが、潜り続ける問題を場当たり的に回避したコードを書くと、Nuxt経由でもうまく動いた。

```js
  /**
   * @param {!CDPSession} session
   * @return {!Connection}
   */
  static fromSession(session) {
    let connection = session._connection;
    // TODO(lushnikov): move to flatten protocol to avoid this.
    while (connection instanceof CDPSession) {
      if (connection._connection) {
        connection = connection._connection;
      } else {
        break;
      }
    }
    return connection;
  }
```

これで少なくとも返される値はundefinedではなくなる。

***

でもこれじゃnode_modulesのコードをいじるのでデプロイできないんですよね。

# コード例

```js:api.js(NuxtでserverMiddlewareとして動かすスクリプト)
module.exports = async (req, res, next) => {
   const puppeteer = require('puppeteer')
   const browser = await puppeteer.launch({ headless: true, })
   const page = await browser.newPage()
   await page.goto('https://www.google.com/')
}
```

```nuxt.config.js
const serveStatic = require('serve-static')

module.exports = {
    serverMiddleware: [{
        path: '/api', handler: '~/api.js'
    },]
}
```

nuxt で `http://localhost:3000` に立て`http://localhost:3000/api`にアクセスすると、api.jsが実行される。

# 調査

* [Using esm (ES6 modules) causes failure. · Issue #3382 · GoogleChrome/puppeteer](https://github.com/GoogleChrome/puppeteer/issues/3382)

エラー的に似てそうなのはこれなのだが関係ないと考えている。
ただ、Nuxtが裏でどう取りまとめているのかを理解していないのでなにか無理解が原因の可能性もあるのではと思っている。
zero configurationで動くのは本当にすごい[^1]がエラーが出たときにこまることを痛感。


# どうする？

expressとpuppeteerの連携はいくつか見かけたので、Nuxtからexpressに乗り換えるかserverMiddlewareをexpressにしてそこからpuppeteer起動でなんとかならないかと考えている。

しかしすごく遠回りなのでモチベーションがまったく出ない。

***

# 追記

serverMiddlewareをexpressにしたが、こちらでも同様のエラーが出た。
そのままexpressのコードでexpress単体でサーバーを立てリクエストしたら成功したので…

nuxtとpuppeteerを使う場合はexpressをメインでnuxtをミドルウェアとして実装するのが正解っぽい。

* [【Node.js】express + Nuxt.jsでシンプルなテンプレートを作った - Qiita](https://qiita.com/seapolis/items/118aec7aa41ac82a6b46)
* [マイクロサービスを作るのに、nuxt.jsが最高だった話 - Qiita](https://qiita.com/tkow/items/c869e7a69665ddc7305d)

こっちの方法を調べて移植してみる。

# 追記の追記

expressメインで動かしました。

```
components/
express.js
node_modules/
package.json
package-lock.json
pages/
store/
```

こういう構成で、基本はNuxtのもの。

```express.js
const express = require('express')
const { Nuxt, Builder } = require('nuxt')
const puppeteer = require('puppeteer')


// Create express instnace
const app = express()

const bodyParser = require('body-parser')
app.use(bodyParser.json())


const config = require('./nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
const nuxt = new Nuxt(config)

if (config.dev) {
    const builder = new Builder(nuxt)
    builder.build()
}

app.post('/api',  async (req, res) => {
    console.log(req.body.id)
   const browser = await puppeteer.launch({ headless: true, })
   const page = await browser.newPage()
   await page.goto('https://www.google.com/')
    res.send('success')
})

// 後ろに持ってこないとexpressのルーティングが効かない？
app.use(nuxt.render)

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Example app listening at http://%s:%s', host, port);
  });
```

ルーティングルールをまだ理解しきれてないが、望む動作は成功。
リクエストのparamを使いたい場合は、ちゃんとbodyParserを設定する。

`app.use(nuxt.render)`
を先に持ってくると`/api`が404になってしまった。

[^1]: `serverMiddleware`を使わなければ`nuxt.config.js`も不要