はじめに

webpack & Babel を使って Chrome 拡張機能を良い感じに開発できるテンプレート(Hot Reload 付き)を作った。

TL;DR

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. リポジトリ
    2. テンプレート機能概要
      1. webpack & Babel でモダン JavaScript を利用可能
      2. Hot Reload 対応
      3. zip ファイルの作成
    3. (まだ)やれてないこと
    4. その他 細かい解説(兼 メモ)
      1. webpack
      2. Babel
      3. NPM Scripts
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.5
BuildVersion: 19F101

$ node -v
v12.7.0

$ npm -v
6.14.5

$ npx webpack -v
4.43.0

詳細

リポジトリ

17number/chrome-extension-template-webpack-babel

テンプレート機能概要

webpack & Babel でモダン JavaScript を利用可能

※どちらもそのまま書くとエラーになる

Hot Reload 対応

npm run watch で Watch モード、かつ Hot Reload

npm run watch 後に chrome://extensions から再読み込みする必要あり

zip ファイルの作成

npm run release [<version>] でリリースファイル(zip ファイル)の作成

1
2
3
4
5
$ npm run release
# output 'release/extension.zip`

$ npm run release v0.0.1
# output 'release/extension_v0.0.1.zip`

(まだ)やれてないこと

そのうちやる(と思う)

  • ESLint などによる Lint
  • Prettier などによる自動整形
  • Jest などによるテスト

その他 細かい解説(兼 メモ)

※ただ使うだけであればここは読み飛ばして OK

webpackBabel もふわっと知ってる程度の知識でスタートしたので、色々と勉強になった。

忘れないように「何をどうやっているのか」などについて簡単にメモしておく。あくまでも参考ページへのリンクや、簡単な説明のみに留めるので、細かい部分は公式ドキュメントを要確認。

なお、前述の通りほぼ素人からのスタートなので、誤りとか「それド基本では?」みたいな内容も多々あると思う。

webpack

webpack は分割されているファイルを良い感じにまとめてくれる(Bundle してくれる)ツール。

webpackとBabelの基本を理解する(1) ―webpack編― - Qiita が平易でわかりやすい。

CLI

参考: Command Line Interface | webpack

webpack コマンドでビルド(Bundle)

1
$ webpack

--config オプションで使用するコンフィグを指定可能。OSS で良く見かける npm run dev とか npm run prod などは、このコンフィグを切り替えて実行しているだけ(のことが多い)。

1
$ webpack --config <path-to-file>

--watch オプションを付けて実行することで、ファイル変更を検知したら即ビルドするようになる。

1
$ webpack --watch
エントリーファイル 指定/分割

参考: Entry Points | webpack

entry オプションで、起点となるファイルを指定できる。

1
2
3
module.exports = {
entry: './path/to/file.js',
};

複数指定する場合は Object 形式 で指定する。

1
2
3
4
5
6
module.exports = {
entry: {
content: './path/to/content.js',
background: './path/to/background.js',
},
};

なお、単一指定(entry: './file.js)だと dist/main.js に、Object 形式だと dist/<object key>.js にファイルが出力される。
※デフォルト(output オプション未指定)の場合

処理対象ファイル、処理内容の定義

参考:

起点は前章の entry で指定したので、「どんなファイル」に「どんな処理を適用するか」を module.rules で定義。

以下のようなイメージ。

  • scss ファイルなら css にコンパイル
  • js ファイルなら Babel でトランスパイル
  • etc

test で対象ファイルを指定し、use で適用処理を記述。

use は後ろから適用していくので、複数記述する場合には注意が必要。

webpack.common.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
}
],
},
{
test: /\.s[ac]ss$/i,
use: [
'style-loader', // 3rd
'css-loader', // 2nd
'sass-loader', // 1st
],
},
],
},
};
ビルドファイルのクリア

参考: Output Management | webpack

johnagan/clean-webpack-plugin を使うと、ビルド(Bundle)実行前に既存ファイルを削除してくれる。
(残骸が残ることによる意図しない動作を防ぐことができる)

webpack.common.js

1
2
3
4
5
6
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [
new CleanWebpackPlugin(),
],
};
設定ファイルの分割

参考:

公式ガイド にも記載があるが、survivejs/webpack-merge を使うことで、共通設定/開発環境設定/本番環境設定 などを分けることができる。

webpack.common.js

1
2
3
4
module.exports = {
entry: { /* 略 */ },
// ...,
};

webpack.dev.js

1
2
3
4
5
6
const { merge } = require('webpack-merge');
const common = './webpack.common.js';
module.exports = merge(common, {
mode: 'development',
// ...,
});

公式ガイド側の記述方法は古い部分があるので、survivejs/webpack-merge を見た方が良い。(2020/07/12 現在)

具体的には以下だとエラーになる。

1
2
3
4
5
const merge = require('webpack-merge');
// ↑は正しくは const { merge } = require('webpack-merge');
module.exports = merge(common, {
// ...,
});
静的ファイルなどのコピー

参考: CopyWebpackPlugin | webpack

webpack-contrib/copy-webpack-plugin を使うことでファイルのコピーが可能。

patterns でコピー対象や、コピー先、除外対象などを指定できる。

除外対象は globOptions 内の ignore で指定。

webpack.common.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
// src/manifest.json を出力先(デフォルト dist)にコピー
{ from: 'src/manifest.json' },
// src/assets 配下を出力先(デフォルト dist)にコピー
// ex: src/assets/baz -> dist/baz
// src/assets/foo/bar -> dist/foo/bar
{
from: 'src/assets',
globOptions: {
ignore: [
// scss は直接 JS ファイルで import しているため除外
'**/stylesheets/**',
],
},
},
]
}),
],
};
Sass/SCSS ファイルを使う

参考: sass-loader | webpack

以下をインストールして、webpack の設定を行うと Sass/SCSS ファイルを使うことができる

1
$ npm i -D sass sass-loader css-loader style-loader

webpack.xxxx.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
],
},
};

xxxx.js

1
import './path/to/scss.scss';
Hot Reload

参考:

rubenspgcavalcante/webpack-extension-reloader を使うことで、Chrome 拡張機能を Hot Reload できるようになる。

webpack.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const ExtensionReloader = require('webpack-extension-reloader');

module.exports = merge(common, {
plugins: [
new ExtensionReloader({
port: 9090,
reloadPage: true,
manifest: path.resolve(__dirname, 'src', 'manifest.json'),
entries: {
contentScript: [
'content',
],
background: [
'background',
],
extensionPage: [
'options',
],
},
}),
],
});

contentScript などには entry で指定したキーを設定する。
※自分は webpack.common.jsentry を定義している。

その他の詳細は How to use を参照。

Hot Reload を watch でのみ有効化

参考:

module.exports = {...}module.exports = (env, argv) => {...} にすることで、実行時オプションを参照できるようになる。
※公式だと Exporting a Function | Configuration TypesMode | webpack にさらっと書かれてる。

で、--watch(or -w)で実行すると argv{ watch: true, w: true } が追加される。

上記をもとに webpack.dev.js を以下のように修正。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const ExtensionReloader = require('webpack-extension-reloader');

module.exports = (env, argv) => {
return merge(
common,
{
plugins: [
(argv.watch || argv.w) ? (new ExtensionReloader({
port: 9090,
reloadPage: true,
manifest: path.resolve(__dirname, 'src', 'manifest.json'),
entries: {
contentScript: [
'content',
],
background: [
'background',
],
extensionPage: [
'options',
],
},
})) : false,
].filter(Boolean),
}
);
};
Terser による Minify

参考: TerserWebpackPlugin | webpack

webpack-contrib/terser-webpack-plugin

設定ファイルを分割して、本番向けの webpack.prod.js に定義。

webpack.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const TerserPlugin = require('terser-webpack-plugin');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
mode: 'production',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
exclude: /\/node_modules\//,
terserOptions: {
mangle: true,
// https://github.com/terser/terser#output-options
output: {
ascii_only: false,
},
},
}),
],
},
});

ascii_only: false にしておかないと、日本語などが \u1234 のようにエスケープされてしまい、リリース時の審査に通らないので注意。

その他細かい設定は本家 terser/terser も見た方が良い。

Babel

Babel は最新の書き方(構文)を使っているコードを、各ブラウザ(など)が理解できるコードに変換(トランスパイル)してくれるツール。

これも webpackとBabelの基本を理解する(2) ―Babel編― - Qiita がわかりやすい。

webpack から Babel を利用

参考: babel-loader | webpack

途中(処理対象ファイル、処理内容の定義) で出てきたが、babel/babel-loader を使うことで、webpack の流れで Babel によるトランスパイルを適用できる。

use 内の options で詳細設定しても良いし、babel.config.jsonbabel.config.js, .babelrc などの別ファイル に設定を切り出しても良い。
今回は babel.config.js として切り出している。

webpack.common.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
}
],
},
],
},
};

babel.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = function (api) {
api.cache(true);

const presets = [["@babel/preset-env", {
// NOTE: "usage" だと上手く動かなかった、詳細は未確認
// useBuiltIns: "entry",
// corejs: 3,
targets: {
chrome: "68",
firefox: "62",
},
}]];

return {
presets,
sourceType: "unambiguous",
};
}
Chrome 拡張機能として動作させるための設定

参考:

↑を参考に useBuiltIns: "usage" を使ってみたが、自分の環境ではエラーになった。

useBuiltIns: "entry" にしつつ、JS 側で import 'core-js' とすると動作したが、useBuiltIns 無しでも動作したため現時点ではどちらもコメントアウトしている。

NPM Scripts

圧縮

nfriedly/node-bestzip を使用。

引数でのバージョン指定に対応したかったので、release.js を作成し、npm run release で実行するようにしている。

まとめ

その他・メモ

このへんのやつもちゃんとやりたい

参考文献

※主に記事内で紹介したもの以外

webpack, Babel の基本理解に役立った。何も知らない状態で読んでもなんとなく理解できる。


Babel 周り。webpack で手一杯なのでちゃんと見れてない(というか webpack もまだまだ。。。


自分のテンプレ作る際にめっちゃ参考になった。まだ理解しきれてない箇所もあるので、他にも取り入れられそうなやつがあれば随時取り込む予定


公式ドキュメントなど


その他

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list