0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Chrome拡張開発時の躓き箇所のメモ3点

Posted at

Chrome用のブラウザ拡張を開発するのに create-chrome-ext (の vue-ts テンプレート)をボイラープレートとして使用していたときの躓き箇所の備忘録になります。

DockerのNode.jsを利用するとHMRが動作しない

最初はホストPC(Windows)にインストールした Node.js(20.x) を使用していたのですが、途中で複数台のPCで開発できるよう Docker を使った環境構築を行うよう方針転換しました。

Dockerを使った環境構築について

複雑なことはせずにviteのデフォルトのポート(5173)のポートフォワーディングを行うくらいのコンテナ環境を作成しています。

compose.yml
services:
  app:
    build:
      context: ./
      dockerfile: ./.docker/app/Dockerfile
    volumes:
      - .:/app:cached
    ports:
      - '5173:5173'
    tty: true
  • compose.yml 作成
.docker/app/Dockerfile
FROM node:20-slim
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

ENV TZ=Asia/Tokyo
RUN { \
        echo "alias ll='ls -l --color=auto'"; \
    } >> /root/.bashrc

WORKDIR /app
  • compose.yml に記載した Dockerfile を作成
  • 今回は WSL2 配下ではなく WindowsPC のボリューム内にプロジェクトを作っているため ホストとコンテナで UID=1000,GID=1000 に揃えるための USER命令(USER node)はなし
vite.config.ts

import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import vue from '@vitejs/plugin-vue'
import manifest from './src/manifest'
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from '@primevue/auto-import-resolver'

export default defineConfig(({ mode }) => {
  const production = mode === 'production'

  return {
+    server: {
+      host: true,
+      port: 5173,
+      hmr: {
+        port: 5173,
+      },
+    },
    build: {
      cssCodeSplit: true,
      emptyOutDir: true,
      outDir: 'build',
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/chunk-[hash].js',
        },
      },
    },
    plugins: [
      crx({ manifest }),
      vue(),
      Components({
        resolvers: [PrimeVueResolver()],
      }),
    ],
  }
})
  • devモードで起動するときのviteにブラウザから到達できるよう server.hostserver.portserver.hmr.port などの設定を実施

ホストPCの Node.js を使用していた頃はサイドパネルのvueファイルを保管時のHMRが正常に動作していました。
Dockerを使用したコンテナ環境ではエディタでファイルを編集した際のhmr updateの出力が行われず、実際にHMRも行われなくなってしまいました。また、サイドパネルを開いた状態で開発者ツールを開いてみてもコンソールにエラーらしいエラーは出ていませんでした。

こちらの件、HMRに関するエラーらしいエラーも出ていなく原因がよくわかっていませんが、以下のように server.watch.usePolling: true を追記することで動作するようになりました。

vite.config.ts
import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import vue from '@vitejs/plugin-vue'
import manifest from './src/manifest'
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from '@primevue/auto-import-resolver'

export default defineConfig(({ mode }) => {
  const production = mode === 'production'

  return {
    server: {
      host: true,
      port: 5173,
+      watch: {
+        usePolling: true,
+      },
      hmr: {
        port: 5173,
      },
    },
    build: {
      cssCodeSplit: true,
      emptyOutDir: true,
      outDir: 'build',
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/chunk-[hash].js',
        },
      },
    },
    plugins: [
      crx({ manifest }),
      vue(),
      Components({
        resolvers: [PrimeVueResolver()],
      }),
    ],
  }
})

image.png

manualChunkを設定するとdevサーバーが起動しない

UIフレームワークや自動インポートのためのプラグインを導入していった結果として、デフォルトの閾値(500KB)を超過するチャンクが発生するようになりました。

build実行時のログ詳細
> docker compose exec app npm run build

> [some-ext]@0.0.1 build
> vue-tsc --noEmit && vite build    

vite v5.4.1 building for production...
✓ 209 modules transformed.
The emitted file "img/logo-16.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-48.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-34.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-128.png" overwrites a previously emitted file of the same name.
build/assets/index.ts-loader-CvVIyBbC.js    0.34 kB
build/sidepanel.html                        0.55 kB │ gzip:   0.33 kB
build/img/logo-16.png                       0.73 kB
build/manifest.json                         1.09 kB │ gzip:   0.65 kB
build/.vite/manifest.json                   1.87 kB │ gzip:   0.45 kB
build/img/logo-34.png                       2.04 kB
build/img/logo-48.png                       3.11 kB
build/img/logo-128.png                      7.80 kB
build/assets/primeicons-C6QP2o4f.woff2     35.15 kB
build/assets/primeicons-MpK4pl85.ttf       84.98 kB
build/assets/primeicons-WjwUDZjB.woff      85.06 kB
build/assets/primeicons-DMOk5skT.eot       85.16 kB
build/assets/primeicons-Dr5RGzOO.svg      342.53 kB │ gzip: 105.26 kB
build/assets/sidepanel-6d3Q9_62.css       350.17 kB │ gzip:  36.56 kB
build/assets/chunk-CPt-ohte.js              0.62 kB │ gzip:   0.26 kB
build/assets/chunk-CrPRWnrr.js              2.23 kB │ gzip:   1.10 kB
build/assets/chunk-D__Qv3Tb.js            722.55 kB │ gzip: 163.71 kB

(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 5.53s

image.png


これを解消すべく以下のような manualChunk の設定を施しました。
ちなみにチャンクの分割対象を視覚化するための Bundle Analyzer もこのタイミングで導入しました。

vite.config.ts
import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import vue from '@vitejs/plugin-vue'
import manifest from './src/manifest'
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from '@primevue/auto-import-resolver'
+import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig(({ mode }) => {
  const production = mode === 'production'

  return {
    server: {
      host: true,
      port: 5173,
      watch: {
        usePolling: true,
      },
      hmr: {
        port: 5173,
      },
    },
    build: {
      cssCodeSplit: true,
      emptyOutDir: true,
      outDir: 'build',
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/chunk-[hash].js',
+          manualChunks: {
+            vue: ['vue'],
+            datatable: ['primevue/datatable'],
+          },
        },
+        plugins: [visualizer()],
      },
    },
    plugins: [
      crx({ manifest }),
      vue(),
      Components({
        resolvers: [PrimeVueResolver()],
      }),
    ],
  }
})

image.png


上記設定を施した後のビルド結果が以下

build実行時のログ詳細
> docker compose exec app npm run build

> [some-ext]@0.0.1 build
> vue-tsc --noEmit && vite build

vite v5.4.1 building for production...
✓ 209 modules transformed.
The emitted file "img/logo-34.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-16.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-48.png" overwrites a previously emitted file of the same name.
The emitted file "img/logo-128.png" overwrites a previously emitted file of the same name.
build/assets/index.ts-loader-CvVIyBbC.js    0.34 kB
build/sidepanel.html                        0.70 kB │ gzip:   0.35 kB
build/img/logo-16.png                       0.73 kB
build/manifest.json                         1.09 kB │ gzip:   0.65 kB
build/img/logo-34.png                       2.04 kB
build/.vite/manifest.json                   2.16 kB │ gzip:   0.51 kB
build/img/logo-48.png                       3.11 kB
build/img/logo-128.png                      7.80 kB
build/assets/primeicons-C6QP2o4f.woff2     35.15 kB
build/assets/primeicons-MpK4pl85.ttf       84.98 kB
build/assets/primeicons-WjwUDZjB.woff      85.06 kB
build/assets/primeicons-DMOk5skT.eot       85.16 kB
build/assets/primeicons-Dr5RGzOO.svg      342.53 kB │ gzip: 105.26 kB
build/assets/sidepanel-6d3Q9_62.css       350.17 kB │ gzip:  36.56 kB
build/assets/chunk-CPt-ohte.js              0.62 kB │ gzip:   0.26 kB
build/assets/chunk-CrPRWnrr.js              2.23 kB │ gzip:   1.10 kB
build/assets/chunk-DilsI5ti.js             67.85 kB │ gzip:  27.28 kB
build/assets/chunk-F0L3Wnrr.js            200.27 kB │ gzip:  38.53 kB
build/assets/chunk-kB2xJbjL.js            454.92 kB │ gzip: 100.18 kB
✓ built in 5.48s

image.png

vueとprimevue/datatableについてチャンクを分割したことにより、チャンクの数が2個増え500KBを超えるものが無くなりました


build時のログとして表示されていたワーニングメッセージも抑制されるようになりよかったよかったと思いきや、その後の開発の続きを行う際のdevサーバー起動時に以下のようなエラーが出てサーバーが即落ちするようになってしまってました。:sob:

> docker compose exec app npm run dev

> [some-ext]@0.0.1 dev
> vite

  VITE v5.4.1  ready in 8149 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://172.20.0.2:5173/
  ➜  press h + enter to show help
Error: Could not resolve entry module (vue).
    at error (file:///app/node_modules/rollup/dist/es/shared/rollup.js:1858:30)
    at ModuleLoader.loadEntryModule (file:///app/node_modules/rollup/dist/es/shared/rollup.js:22369:20)
    at async Promise.all (index 2)
    at async Promise.all (index 0) {
  code: 'UNRESOLVED_ENTRY'
}

こちらの件、エラーメッセージや manualChunk について調べてもこれと言って解決策が見つけられませんでした。
ということで正しい対応か微妙ですが vite.config.ts の先頭に定義されているproductionビルドかを判定するproduction変数を元にbuild時のみ manualChunk の設定を有効化されるように修正しました。

vite.config.ts
import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import vue from '@vitejs/plugin-vue'
import manifest from './src/manifest'
import Components from 'unplugin-vue-components/vite'
import { PrimeVueResolver } from '@primevue/auto-import-resolver'

export default defineConfig(({ mode }) => {
  const production = mode === 'production'

  return {
    server: {
      host: true,
      port: 5173,
      watch: {
        usePolling: true,
      },
      hmr: {
        port: 5173,
      },
    },
    build: {
      cssCodeSplit: true,
      emptyOutDir: true,
      outDir: 'build',
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/chunk-[hash].js',
-          manualChunks: {
-            vue: ['vue'],
-            datatable: ['primevue/datatable'],
-          },
+          manualChunks: production
+            ? {
+                vue: ['vue'],
+                datatable: ['primevue/datatable'],
+              }
+            : undefined,
        },
      },
    },
    plugins: [
      crx({ manifest }),
      vue(),
      Components({
        resolvers: [PrimeVueResolver()],
      }),
    ],
  }
})

ストアに公開したブラウザ拡張のインストールでエラー

サイドパネルやタブに関するパーミッションを利用しているため審査に1~2日ほど掛かりましたが、無事に公開できました:relaxed:
そして動作確認のため、自分でブラウザ拡張をインストールしようとしてみたところ以下のようなエラーが出てしまいました。。:sob:

image.png

ダウンロード エラー: 画像をデコードできませんでした: logo-128.png

拡張機能内に含まれるロゴ画像(logo-128.png)はPhotoshop(CS6)で自作しWeb用に書き出したものでこれと言って特別なことをした覚えもありません。。
先に結論を書くとzipを作成する際に使用していたgulpをおもむろに4→5に移行していたことが原因でした。

以下の記事や公式のリリースノートが参考になりました:bow:

原因特定に至るまでの調査内容
  1. Chromeのデベロッパーダッシュボードにアップしたエラーが出ているバージョンの main.crx をダウンロード
    image.png

  2. ダウンロードした main.crxmain.zip にリネームして展開

  3. 展開した中身のlogo-128.pngファイルを確認し確かに破損していることを確認

  4. 自分の開発環境内に残っていたアップロード元のzipファイルを展開し、中身のlogo-128.pngファイルが破損していることも確認
    image.png

  5. このことからChromeのデベロッパーダッシュボードにアップロードしたタイミングで破損したわけではなく開発環境で作成したzipファイルに原因があるという問題の切り分けが可能となった

  6. zip化のためのコードはnpm-scriptsから呼び出し可能なスクリプト化(src/zip.js)されており、主にgulpを使用したシンプルなコードとなっていた

  7. そういえばgulp4について脆弱性のアラートが上がってたからgulp5に更新したな?と思い出した

  8. gulp5 + png などでググり自身のアップデートが不具合の原因であったことが判明。。


Chromeのブラウザ拡張開発のボイラープレートとして使用している create-chrome-ext を元に作成したプロジェクトについてnpmで管理するパッケージ周りのバージョンが古くなってしまっているものがあり、その中にgulp: ^4.0.2 も含まれていました。
これを深く考えず npm install -D gulp@latest で最新の^5.0.0にアップデートだけしたのがエラーの原因でした。

create-chrome-ext を使用してブラウザ拡張のプロジェクトを作成したときの出力

以下は vanilla-ts テンプレートを使用したプロジェクトを作成したときの出力です

>npm create chrome-ext

> npx
> create-chrome-ext

√ Project name: ... .
√ Author: ... no one
√ Framework: » vanilla
√ Language: » vanilla-ts

Scaffolding project in H:\workspace_browser_ext\test-project...

Done. Now run:

  npm install
  npm run dev

   Suggest you next step:
    1. cd
    2. Run npm install
    3. Open chrome://extensions/ in your browser
    4. Check the box for Developer mode in the top right.
    5. Click the Load unpacked extension button.
    6. Select the build/ directory that was created.

------------------------------
>npm install
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and 
powerful.
npm warn deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm warn deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated q@1.5.1: You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.
npm warn deprecated
npm warn deprecated (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
npm warn deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm warn deprecated chokidar@2.1.8: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm warn deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm warn deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead

added 457 packages, and audited 458 packages in 23s

44 packages are looking for funding
  run `npm fund` for details

11 vulnerabilities (7 moderate, 4 high)

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
gulp: ^4.0.2 を使用していた頃の脆弱性レポート
>npm audit
# npm audit report

braces  <3.0.3
Severity: high
Uncontrolled resource consumption in braces - https://github.com/advisories/GHSA-grv7-fg5c-xmjg
fix available via `npm audit fix --force`
Will install gulp@5.0.0, which is a breaking change
node_modules/braces
  chokidar  1.3.0 - 2.1.8
  Depends on vulnerable versions of anymatch
  Depends on vulnerable versions of braces
  Depends on vulnerable versions of readdirp
  node_modules/chokidar
    glob-watcher  5.0.0 - 5.0.5
    Depends on vulnerable versions of anymatch
    Depends on vulnerable versions of chokidar
    node_modules/glob-watcher
      gulp  4.0.0 - 4.0.2
      Depends on vulnerable versions of glob-watcher
      Depends on vulnerable versions of gulp-cli
      node_modules/gulp
  micromatch  <=4.0.7
  Depends on vulnerable versions of braces
  node_modules/anymatch/node_modules/micromatch
  node_modules/findup-sync/node_modules/micromatch
  node_modules/matchdep/node_modules/micromatch
  node_modules/readdirp/node_modules/micromatch
    anymatch  1.2.0 - 2.0.0
    Depends on vulnerable versions of micromatch
    node_modules/anymatch
    findup-sync  0.4.0 - 3.0.0
    Depends on vulnerable versions of micromatch
    node_modules/findup-sync
    node_modules/matchdep/node_modules/findup-sync
      liftoff  2.2.3 - 3.1.0
      Depends on vulnerable versions of findup-sync
      node_modules/liftoff
        gulp-cli  1.3.0 - 2.3.0
        Depends on vulnerable versions of liftoff
        Depends on vulnerable versions of matchdep
        node_modules/gulp-cli
      matchdep  >=1.0.1
      Depends on vulnerable versions of findup-sync
      Depends on vulnerable versions of micromatch
      node_modules/matchdep
    readdirp  2.2.0 - 2.2.1
    Depends on vulnerable versions of micromatch
    node_modules/readdirp


11 vulnerabilities (7 moderate, 4 high)

To address all issues (including breaking changes), run:
  npm audit fix --force

gulpを使用したzip化のスクリプトを修正

gulp5ではデフォルトでsrcに指定するGlobパターンのパスに含まれるストリーム形式のファイルについてUTF-8へのエンコードを行うとのこと。
Viteのプロダクションビルド(npm run build)によって build ディレクトリに出来上がったファイル一式をソースとした zip を作成するコードとなっているため、src のオプションとして encoding: false を追加することで解決できました。

src/zip.js
import gulp from 'gulp'
import zip from 'gulp-zip'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const manifest = require('../build/manifest.json')

gulp
-  .src('build/**')
+  .src('build/**', { encoding: false })
  .pipe(zip(`${manifest.name.replaceAll(' ', '-')}-${manifest.version}.zip`))
  .pipe(gulp.dest('package'))

参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?