【Chrome Extension】Manifest V3だとService WorkerでDOMParserが現状使えないので、jsdomをインストールする

そもそもなんでService WorkerでDomParseが使えないの?

そういう仕様だからと言ってしまうとそれまでだけど、以下にもできないことは明記されていた。

https://developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/

ただ、以下見ていると、DOMParserが使えないのはChrome自体のバグっぽい?
ちょっと待てば直るかもしれないな。タスクの優先度あげる?的な話もしているし。

https://bugs.chromium.org/p/chromium/issues/detail?id=1056354

https://bugs.chromium.org/p/chromium/issues/detail?id=1186804

サービスワーカーは関連するドキュメントを持たないため、documentParser のような DOM API にアクセスできません。この API は、JS でドキュメントの文字列を DOM または XML オブジェクト ツリーに変換するスクリプトを可能にします。このギャップは、エクステンションがこの機能を回復するためにサードパーティのライブラリを含めるか、ドキュメントにアクセスするために厄介でエラーを起こしやすい回避策を使用しなければならないことを意味します。このような状況は、拡張機能の開発者にとって驚きと混乱をもたらします。というのも、ブラウザ自体がこのような機能をすでに持っており、拡張機能のサービスワーカーに公開されていないだけだからです。

jsdomをnpm install

もともと、以下のサイトを参考にするばすぐに解決すると思ってました。

ただ、jsdomをnpm installしたところ以下のようなエラーが出た。

ERROR in ./node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js
Module not found: Error: Can't resolve 'child_process' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\jsdom\lib\jsdom\living\xhr'
@ ./node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js 4:22-46
@ ./node_modules/jsdom/lib/jsdom/living/generated/XMLHttpRequest.js
@ ./node_modules/jsdom/lib/jsdom/living/interfaces.js
@ ./node_modules/jsdom/lib/jsdom/browser/Window.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/jsdom/lib/api.js
Module not found: Error: Can't resolve 'fs' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\jsdom\lib'
@ ./node_modules/jsdom/lib/api.js 3:11-24
@ ./src/background.ts

ERROR in ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
Module not found: Error: Can't resolve 'fs' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\jsdom\lib\jsdom\browser\resources'
@ ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js 2:11-24
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js
Module not found: Error: Can't resolve 'fs' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\jsdom\lib\jsdom\living\xhr'
@ ./node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js 2:11-24
@ ./node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js
@ ./node_modules/jsdom/lib/jsdom/living/generated/XMLHttpRequest.js
@ ./node_modules/jsdom/lib/jsdom/living/interfaces.js
@ ./node_modules/jsdom/lib/jsdom/browser/Window.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/http-proxy-agent/dist/agent.js
Module not found: Error: Can't resolve 'net' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\http-proxy-agent\dist'
@ ./node_modules/http-proxy-agent/dist/agent.js 15:30-44
@ ./node_modules/http-proxy-agent/dist/index.js
@ ./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
@ ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/https-proxy-agent/dist/agent.js
Module not found: Error: Can't resolve 'net' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\https-proxy-agent\dist'
@ ./node_modules/https-proxy-agent/dist/agent.js 15:30-44
@ ./node_modules/https-proxy-agent/dist/index.js
@ ./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
@ ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/http-proxy-agent/dist/agent.js
Module not found: Error: Can't resolve 'tls' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\http-proxy-agent\dist'
@ ./node_modules/http-proxy-agent/dist/agent.js 16:30-44
@ ./node_modules/http-proxy-agent/dist/index.js
@ ./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
@ ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

ERROR in ./node_modules/https-proxy-agent/dist/agent.js
Module not found: Error: Can't resolve 'tls' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\https-proxy-agent\dist'
@ ./node_modules/https-proxy-agent/dist/agent.js 16:30-44
@ ./node_modules/https-proxy-agent/dist/index.js
@ ./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
@ ./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
@ ./node_modules/jsdom/lib/api.js
@ ./src/background.ts

とにかく、エラー解決してビルドを通す方法

とにかくと書いたのは、この方法が正しいかはわからないからです。
webpackの設定項目nodeを読んでみると以下のように書いてある。

これらのオプションは、Node.jsの特定のグローバルやモジュールをポリフィルまたはモックするかどうかを設定します。これにより、もともとNode.js環境のために書かれたコードを、ブラウザなどの他の環境でも実行できるようになります。

頑張って解釈すると、今回は使用するjsdomは、Node.jsのために提供さてれいるライブラリで (っというのも、ブラウザだとDOMParser使えるし) 、ブラウザなどの他の環境(今回はChromeのService Worker上)で実行できるように必要があり、このオプションを使う必要があります。っと勝手に解釈した。

っで、その設定が以下になります。

module.exports = {
  node: {
    child_process: "empty",
    fs: "empty", // if unable to resolve "fs"
    net: "empty",
    tls: "empty",
  },
};

これでビルドは通るようになりました。
ただ、勝手に解釈したので、問題は本当にこの修正で問題ないのかというところ。
実際にこれらemptyに設定したライブラリを使うときはエラーが発生するんじゃないかなーと思ったりしてます。

ハマり記録。いろんなやり方を試して失敗してを繰り返しました。

ググってると、以下発見

https://github.com/jsdom/jsdom/issues/1812

その中に書いてあるWebpackの設定をいじる方法があることを知りました。
Webpackの設定を以下のように変更。

module.exports = {
  target: 'node',
  entry: {
    content: './src/content.ts',
    background: './src/background.ts',
    options: './src/options.ts',
  },
以下略

この状態で再度npm run devしてビルドするとまだエラーが出る。

WARNING in ./node_modules/ws/lib/buffer-util.js
Module not found: Error: Can't resolve 'bufferutil' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/buffer-util.js
 @ ./node_modules/ws/lib/receiver.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts

WARNING in ./node_modules/ws/lib/validation.js
Module not found: Error: Can't resolve 'utf-8-validate' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/validation.js
 @ ./node_modules/ws/lib/receiver.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts

ERROR in ./node_modules/jsdom/lib/jsdom/utils.js
Module not found: Error: Can't resolve 'canvas' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\jsdom\lib\jsdom'
 @ ./node_modules/jsdom/lib/jsdom/utils.js 154:2-27 160:17-34
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts
npm ERR! code 2
npm ERR! path C:\code\chrome-extension\chaimet-ts-extension
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c webpack --config webpack.dev.js

これも参考サイトが見つかる。

https://github.com/jsdom/jsdom/issues/3042

要は、この依存しているライブラリを再度npm installしてやれば解決しそう?

npm install canvas
だけど、やはりエラーが出る。
WARNING in ./node_modules/ws/lib/buffer-util.js
Module not found: Error: Can't resolve 'bufferutil' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/buffer-util.js
 @ ./node_modules/ws/lib/websocket.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts

WARNING in ./node_modules/ws/lib/validation.js
Module not found: Error: Can't resolve 'utf-8-validate' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/validation.js
 @ ./node_modules/ws/lib/receiver.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts

ERROR in ./node_modules/canvas/build/Release/canvas.node 1:2
Module parse failed: Unexpected character '�' (1:2)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
 @ ./node_modules/canvas/lib/bindings.js 3:17-56
 @ ./node_modules/canvas/index.js
 @ ./node_modules/jsdom/lib/jsdom/utils.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts
npm ERR! code 2
npm ERR! path C:\code\chrome-extension\chaimet-ts-extension
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c webpack --config webpack.dev.js

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\xiaot\AppData\Local\npm-cache\_logs\2021-09-30T21_59_24_963Z-debug.log

先ほどの参考サイトをもう少し見ていると、同じようなエラーが出てる人がいた。それに対する解決策として、webpackの設定を変更するというのがあった。
実際に設定を入れてみる。以下17行目。

  plugins: [
    new CleanWebpackPlugin(),
    new CopyPlugin({
      patterns: [
        { from: 'src/manifest.json' },
        {
          from: 'src/assets',
          globOptions: {
            ignore: [
              '**/stylesheets/**',
            ],
          },
        },
        { from: 'src/options.html' },
      ]
    }),
    new webpack.IgnorePlugin(/canvas/, /jsdom$/),
  ],

再度npm run dev

PS C:\code\chrome-extension\chaimet-ts-extension> npm run dev

> dev
> webpack --config webpack.dev.js

C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\cli.js:93
                                throw err;
                                ^

ReferenceError: webpack is not defined
    at Object.<anonymous> (C:\code\chrome-extension\chaimet-ts-extension\webpack.common.js:27:9)
    at Module._compile (C:\code\chrome-extension\chaimet-ts-extension\node_modules\v8-compile-cache\v8-compile-cache.js:192:30)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1120:10)
    at Module.load (node:internal/modules/cjs/loader:971:32)
    at Function.Module._load (node:internal/modules/cjs/loader:812:14)
    at Module.require (node:internal/modules/cjs/loader:995:19)
    at require (C:\code\chrome-extension\chaimet-ts-extension\node_modules\v8-compile-cache\v8-compile-cache.js:159:20)        
    at Object.<anonymous> (C:\code\chrome-extension\chaimet-ts-extension\webpack.dev.js:3:16)
    at Module._compile (C:\code\chrome-extension\chaimet-ts-extension\node_modules\v8-compile-cache\v8-compile-cache.js:192:30)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1120:10)
    at Module.load (node:internal/modules/cjs/loader:971:32)
    at Function.Module._load (node:internal/modules/cjs/loader:812:14)
    at Module.require (node:internal/modules/cjs/loader:995:19)
    at require (C:\code\chrome-extension\chaimet-ts-extension\node_modules\v8-compile-cache\v8-compile-cache.js:159:20)
    at WEBPACK_OPTIONS (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\utils\convert-argv.js:114:13)
    at requireConfig (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\utils\convert-argv.js:116:6)
    at C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\utils\convert-argv.js:123:17
    at Array.forEach (<anonymous>)
    at module.exports (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\utils\convert-argv.js:121:15)
    at C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\cli.js:71:45
    at Object.parse (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\node_modules\yargs\yargs.js:576:18)
    at C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\cli.js:49:8
    at Object.<anonymous> (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack-cli\bin\cli.js:366:3)
    at Module._compile (node:internal/modules/cjs/loader:1091:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1120:10)
    at Module.load (node:internal/modules/cjs/loader:971:32)
    at Function.Module._load (node:internal/modules/cjs/loader:812:14)
    at Module.require (node:internal/modules/cjs/loader:995:19)
    at require (node:internal/modules/cjs/helpers:92:18)
    at Object.<anonymous> (C:\code\chrome-extension\chaimet-ts-extension\node_modules\webpack\bin\webpack.js:156:2)
    at Module._compile (node:internal/modules/cjs/loader:1091:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1120:10)
    at Module.load (node:internal/modules/cjs/loader:971:32)
    at Function.Module._load (node:internal/modules/cjs/loader:812:14)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
    at node:internal/main/run_main_module:17:47
npm ERR! code 1
npm ERR! path C:\code\chrome-extension\chaimet-ts-extension
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c webpack --config webpack.dev.js

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\xiaot\AppData\Local\npm-cache\_logs\2021-09-30T22_05_47_614Z-debug.log

再度以下にいきつく。

https://stackoverflow.com/questions/31592819/referenceerror-webpack-is-not-defined

ちゃんとwebpackをimportできてないから発生してるようだ。

npm run devするとwarningはでるものの、エラーはなくなってビルドできるようになった。

しかし、拡張機能をインストールすると、またエラーが出た。

npm install require –save-dev

https://stackoverflow.com/questions/10759130/require-js-in-a-chrome-extension-define-is-not-defined/10772309

結果、これも変わらず。どうすればいいんだぁ。。

その他のエラー

warningが出ていた件を解消していく

WARNING in ./node_modules/ws/lib/buffer-util.js
Module not found: Error: Can't resolve 'bufferutil' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/buffer-util.js
 @ ./node_modules/ws/lib/receiver.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts
 
WARNING in ./node_modules/ws/lib/validation.js
Module not found: Error: Can't resolve 'utf-8-validate' in 'C:\code\chrome-extension\chaimet-ts-extension\node_modules\ws\lib'
 @ ./node_modules/ws/lib/validation.js
 @ ./node_modules/ws/lib/receiver.js
 @ ./node_modules/ws/index.js
 @ ./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
 @ ./node_modules/jsdom/lib/jsdom/browser/Window.js
 @ ./node_modules/jsdom/lib/api.js
 @ ./src/background.ts

こちらを参考にした。

https://github.com/websockets/ws/issues/1126

以下のモジュールを入れて設定を変えればOKのようだ。

https://www.npmjs.com/package/webpack-node-externals

npm install webpack-node-externals –save-dev

webpackの設定を修正

const nodeExternals = require('webpack-node-externals');
...
module.exports = {
    ...
    target: 'node', // in order to ignore built-in modules like path, fs, etc.
    externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
    ...
};

これでビルド時にエラーはでなくなった。

しかし、Chrome拡張機能をインストールするとエラーが出て死ぬ。

まとめ

今回、とりあえずビルドを通すというところで頑張ってみましたが、正直これでよかったのかまったくわからない。
いろんなサイトみても何が正解かよくわからないし、公式のドキュメント読んでもよくわからなかった。

なんか意地でビルド通してましたけど、自分は別段Service Worker上でこの実装をしないといけないわけでもなかったのです。なので完全にブログ記事用のネタになりました。