Next.jsを使用してGoogle Chromeの拡張機能を作るとレイアウトとルーティングができるので結構楽に拡張の開発ができる。

# はじめに
@yushimatenjinです。Next.jsアドベントカレンダー4日目ですね。
Next.jsを使うと簡単にGoogle Chromeの拡張機能が作れるんじゃないかなと思ったら簡単に作ることが出来たのでその紹介を今回しようかなと思います。

このときに得た知見をもとに書いています。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Google Chrome 78にアップデートされたところ、今まであった、現在開いているタブ以外を閉じる機能がなくなっていて作業がはかどらなかったため、Chrome Extensionsを作ってみました。<br><br>アイコンを押すとすべてのタブを閉じるだけです　↓ダウンロード<a href="https://t.co/oBt2S62m9F">https://t.co/oBt2S62m9F</a> <a href="https://t.co/cqKiW1l3Zw">pic.twitter.com/cqKiW1l3Zw</a></p>&mdash; はが (@Mxcn3) <a href="https://twitter.com/Mxcn3/status/1190479518785404929?ref_src=twsrc%5Etfw">November 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# Chrome ExtensionをNext.jsで作ってみた

こちらが先日作成した、Next.jsで作ったChrome拡張を作るためのリポジトリです。
[https://github.com/yushimatenjin/next-js-chrome-extension-starterkit](https://github.com/yushimatenjin/next-js-chrome-extension-starterkit)

# Next.jsのはじめ方

Next.jsを始めるには、`React`の使える環境がある方でしたら簡単に始められます。

### 1. 必要なパッケージをインストール

小さい構成で始めるにはこれだけで大丈夫です。

```bash
yarn init
yarn add react react-dom next
```

### 2.Pagesディレクトリを作る


```bash
mkdir pages
```

### pages/index.jsを作成 & 書き換える

```jsx:pages/index.js
import React from 'react'

export default () => <div>Hello, World!</div>
```

これだけ書くだけで`/`にアクセスしたときに`Hello, World`を表示するページを作ることが出来ます。

```terminal
.
├── package.json
├── pages
│   └── index.js
└── yarn.lock
```

### 起動をするためのスクリプトをpackage.jsonに追加する

```package.json
  ...

   "scripts": {
      "dev": "next",
      "start": "next start",
      "build": "next build",
      "export": "next build && next export"
   }
  ...

```

### 起動する

index.jsの内容を表示するためには先程設定した`yarn dev`のコマンドを打つことで`localhost:3000`にアクセスできるようになります。
<img width="317" alt="ScreenShot 23.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/72e77647-d4bd-c7db-654c-f82bc418ea0c.jpeg">

```
yarn dev
```

これがNext.jsで最小限に起動をする方法になります。開発環境で起動する場合には`yarn dev`、ビルド&起動する場合には`yarn build && yarn start`でサーバーが立ち上がります。

ChromeのExtensionを作る場合には`export`を使用して静的なページとして出力をします。

# Chrome Extensionを作る

ChromeのExtensionを作るためにいくつのファイルを追加&変更します。

### 追加するフォルダ & 追加するファイル
1. extensions  // フォルダ
2. extensions/manifest.json // 拡張機能の詳細を記述するファイル

manifest.jsonは[こちら](https://developer.chrome.com/extensions/devguide)を参考にして記述をしていきます。

例

```manifest.json
{
    "name": "extension",
    "description": "extension",
    "version": "1.0.0",
    "browser_action": {
      "default_popup": "./dist/index.html"
    },
    "permissions": [
      "bookmarks",
      "tabs",
      "activeTab"
    ],
    "manifest_version": 2
  }
```
### 変更するファイル

3. package.json


##### 変更前
```package.json
   ....
  "scripts": {
    ...
    "export": "next build && next export"
  },
   ...
```

##### 変更後

`export`に`-o`オプションを付けて拡張機能の出力先を変更します。

```package.json
   ....
  "scripts": {
    ...
    "export": "next build && next export -o extensions/dist"
  },
   ...
```

 
この３つの新しく変更を加えた状態の現在のディレクトリ構成はこのような状態となっております。


```terminal
.
├── README.md
├── extensions
│   ├── dist
│   └── manifest.json
├── package.json
├── pages
│   └── index.js
└── yarn.lock
```


ちなみに上記の設定を行ったものがこのリポジトリとなりますので、このリポジトリをクローンをするとすぐに開発をし始めることが出来ます。
  
[next-js-chrome-extension-starterkit](https://github.com/yushimatenjin/next-js-chrome-extension-starterkit)


こちらのリポジトリは`tsx`ファイルを追加したため少しファイルの構成が増えています。`Next.js`ではTypeScriptを扱おうとするとある程度必要なファイルを自動的にファイルを追加してくれます。


### Chromeの拡張としてエクスポート

<img width="317" alt="ScreenShot 23.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/898dbd93-f833-c824-73cd-7500aa177e0f.jpeg">

この状態で起動をすると、白い背景にHello, World!と表示されていると思います。この状態のまま拡張にしてみましょう。

```bash
yarn export 
```

出力先をextension/distにpackage.jsonで設定しているので、静的なファイルが生成されます。

### エクスポートされた拡張を読み込む

<img width="732" alt="ScreenShot 26.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/6e6415cf-fcb0-7716-daee-ea328ec4bf04.jpeg">

1. エクスポートされたChromeの拡張機能を読み込むためにはGoogle Chromeの`設定`　→ `その他ツール` → `拡張機能`を選択


<img width="325" alt="ScreenShot 28.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/7ddb8889-5484-62a1-a2aa-4f07e2598dea.jpeg">

2. パッケージ化されていない拡張を読み込む

<img width="910" alt="ScreenShot 27.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/694272c1-ecd0-8f82-3427-b66bd1d415c2.jpeg">

3. `プロジェクト` → extensions ファイルを選択します


これで拡張機能を読み込むことが出来ます。


##### Chrome拡張を作ることが出来た(やったー！）
<img width="89" alt="ScreenShot 30.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/a05bc525-d923-8c2e-fefd-4fbd29bf70ee.jpeg">


#### ルーティングを追加する

Next.jsの特徴として静的サイトのルーティングを簡単に作れるので、新しいページを追加してみます。

Next.jsの`Link`は可能ですが、URLの指定を.htmlまで記述する必要があります。開発環境では必要ないのでページのパスを取得するための関数を追加します。

```constants/page.js
const Pages = () => {
    const suffix = process.env.NODE_ENV === "development" ? "" : ".html"
    const data = {
        index: `index${suffix}`,
        about: `about${suffix}`
    }
    return data
}

export default Pages

```

トップページから飛ぶ先のページ名`about`を追加します。

```jsx:pages/about.js
import React from "react";
import Link from "next/link";
import Page from "../constants/page";

const About = () => {
  return (
    <div>
      このExtensionは@mxcn3が作成しました。
      <Link href={Page().index}>
        <a>トップページへ</a>
      </Link>
    </div>
  );
};

export default About;

```

トップページからページ名 `about`に飛べるようにします。

```jsx:pages/index.js
import React from "react";
import Link from "next/link";
import Page from "../constants/page";

const Index = () => {
  return (
    <div>
      Hello World
      <Link href={Page().about}>
        <a>この拡張について</a>
      </Link>
    </div>
  );
};

export default Index;

```


```
yarn export
```
を再度実行することで拡張機能が更新されます。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257048/d1547b72-f383-aefb-7d96-c5a4063560a3.gif)


#### ページのサイズを変更する

ページのサイズをインラインで変更します。

あまり推奨されていないやり方みたいですが便利上インラインでスタイを変更します。
[https://reactjs.org/docs/dom-elements.html#style](https://reactjs.org/docs/dom-elements.html#style)


```jsx:pages/index.js
import React from "react";
import Link from "next/link";
import Page from "../constants/page";

const Index = () => {
  return (
    //   ページの大きさを変更
    <div
      style={{
        width: 400,
        height: 400
      }}
    >
      Hello World
      <Link href={Page().about}>
        <a>この拡張について</a>
      </Link>
      {/* iframeを追加 */}
      <iframe
        style={{
          width: "100%",
          height: "100%",
          margin: 0,
          border: "none"
        }}
        src="https://playcanv.as/p/8NZ92jAY/"
      />
    </div>
  );
};

export default Index;
```

![output2](https://user-images.githubusercontent.com/39250588/70077357-8d9a6500-15f8-11ea-9b59-1668b644d1bb.gif)


このような形で表示されます。

#### まとめ

Next.jsはReactを使用して記述ができるので、Next.jsでGoogle Chromeの拡張機能を書くことができれば比較的作りやすいのではないでしょうか。Next.jsを使用したChrome拡張の記事などをあまり見なかったので記事にさせていただきました。