CSSの自動インラインとメールを送れるHTMLメールの作成ツールをgulpで作った

## はじめに
この記事はZOZOテクノロジーズ #3 Advent Calendar 2019 16日目の記事です。
昨日は、@zt_takumi_ito さんのPowerShellでサーバアクセスするときのパスワード入力で楽するでした。

HTMLメールを作成する機会があり、調べてみると通常のコーディングと異なり下記の制約の下で作成する必要があることがわかりました。

- メーラーによってCSSの各種プロパティの対応状況が異なる
- スタイルは要素にインラインで記述する（外部読み込みやstyleタグによる記述はメーラーにより非対応）
- 実際にメールを送信して表示を確認する必要がある

一度作成して終わりであれば、スタイルをタグに直接書いて作成するのもありだと思いますが、
今後も運用が予想されたためクラスを使用してスタイルを適用させることでなるべく楽をしたいと考えました。

## 機能
そこで下記の機能を持つツールを作成しました。

- 外部ファイルで記述したCSSをインラインに自動変換してくれる
- HTMLやCSSに変更があればブラウザを自動更新する
- 作成した内容をメールで送って確認できるようにする
- 送信する対象のHTMLを指定できる
- 送信時のタイトルを指定できる

また、SASSを使えるようにしました。

## 作成したツールの詳細
webpackでの作成も考えましたが個人的な慣れやJSは使用しないことからgulpベースで作成しました。
なお作成したツールは[Github](https://github.com/yuki-tomioka/html_mail)に公開しています。

### バージョン
- gulp
  - CLI version: 2.2.0
  - Local version: 4.0.2
- node
  - 10.15.3

### ディレクトリ構成
```
root
  ├ src
  ├ ├ index.html
  ├ ├ css
  ├ └ sass
  ├ .env-sample
  ├ .gitignore
  ├ gulpfile.js
  └ package.json
```

### 使用モジュール
|モジュール名|主な利用機能|
|---|---|
|browser-sync|ファイルに変更があればブラウザを自動更新|
|gulp-sass|SASSを使えるように|
|gulp-inline-css|外部CSSをインラインに変更|
|gulp-mail|メール送信機構|
|minimist|コマンド引数を作成|
|node-env-file|環境変数の設定|

### 基本的な使い方
#### 作成編
1. リポジトリをクローンする
2. `npm ci`で各種モジュールをインストールする
3. `npx gulp`でツールを起動する
4. src配下にhtmlファイルを設置しテーブルコーディングをする
5. src/sass配下にscss記法でスタイルを記述

#### メール送信編
1. .env-sampleを.envにリネームして送信先、送信元の情報を記述する
2. `npx gulp mail`でメールを送信する

メールテンプレートとしてhtmlを複数作成している場合、`npx gulp mail --file hoge`とするとhoge.htmlの内容を送信できます。（デフォルトではindex.html）
また、メールのタイトルを指定したい場合、`npx gulp mail --subject fuga`とするとfugaがタイトルのメールとして送信されます。（デフォルトではhtml mail test）

### 設定ファイルの記述

#### .env-sampleの記述内容
Gmailのアカウントを使用することを前提としています。

```
# 送信先（複数はカンマ区切り、スペースを入れない）
mailTo=example@gmail.com,example_02@gmail.com

# 送信元
smtpUser=example_03@gmail.com
smtpPass=example_03_pass
smtpHost=smtp.gmail.com
smtpPort=465
```

#### gulpfile.jsの記述内容

```js
/**
 * ------------------------------------------------------------
 * 読み込み
 * ------------------------------------------------------------
 */
const gulp = require('gulp');
const sass = require('gulp-sass');
const inlineCss = require('gulp-inline-css');
const browserSync = require('browser-sync').create();
const mail = require('gulp-mail');
const env = require('node-env-file');
const minimist = require('minimist');

const src = './src/';
const dist = './dist/';

env('.env');
const mailTo = process.env.mailTo;
const smtpUser = process.env.smtpUser;
const smtpPass = process.env.smtpPass;
const smtpHost = process.env.smtpHost;
const smtpPort = process.env.smtpPort;

/**
 * ------------------------------------------------------------
 * 設定
 * ------------------------------------------------------------
 */
const sassOption = {
  outputStyle: 'expanded'
}

const inlineCssOption = {
  applyStyleTags: false,
  removeStyleTags: false,
  applyTableAttributes: true,
  removeHtmlSelectors: true
};

const browserSyncOption = {
  server: dist
};

const smtpInfo = {
  auth: {
    user: smtpUser,
    pass: smtpPass
  },
  host: smtpHost,
  secureConnection: true,
  port: smtpPort
};

const fileOptions = minimist(process.argv.slice(2), {
  string: 'file',
  default: {
    file: 'index'
  }
});
const file = fileOptions.file;

const subjectOptions = minimist(process.argv.slice(2), {
  string: 'subject',
  default: {
    subject: 'html mail test'
  }
});
const subject = subjectOptions.subject;

/**
 * ------------------------------------------------------------
 * タスク
 * ------------------------------------------------------------
 */
gulp.task('sass', () => {
  return gulp
    .src(src + 'sass/**/*.scss')
    .pipe(sass(sassOption))
    .pipe(gulp.dest(src + 'css/'));
});

gulp.task('inlineCss', () => {
  return gulp
    .src(src + '**/*.html')
    .pipe(inlineCss(inlineCssOption))
    .pipe(gulp.dest(dist))
});

gulp.task('serve', (done) => {
  browserSync.init(browserSyncOption);
  done();
});

gulp.task('mail', () => {
  return gulp
    .src(dist + file + '.html')
    .pipe(mail({
      subject: subject,
      to: [
        mailTo
      ],
      from: smtpUser,
      smtp: smtpInfo
    }));
});

/**
 * ------------------------------------------------------------
 * watchタスク
 * ------------------------------------------------------------
 */
gulp.task('watch', (done) => {
  const browserReload = (done) => {
    browserSync.reload();
    done();
  };
  gulp.watch(src + '**/*.scss', gulp.series('sass', 'inlineCss'));
  gulp.watch(src + '**/*.html', gulp.task('inlineCss'));
  gulp.watch(dist + '*', browserReload);
});


gulp.task('default', gulp.series('serve', 'watch'));
```

## 今後の課題
今後、ローカル環境にある画像を手軽に読み込ませる方法を模索しようと考えおります。
現状では画像のサイズのみを合わせてLorem Picsumで仮画像を配置する方法を取っております。

何かいい方法があれば教えていただけると幸いです。

## 参考サイト
- [gulpを使用してレスポンシブ Eメール デザイン作成の効率をあげる](https://www.mitsue.co.jp/knowledge/blog/frontend/201701/20_1208.html)
- [gulpタスクに.envで定義した環境変数を渡すならgulp-envでなくnode-env-fileを使う](https://dackdive.hateblo.jp/entry/2017/01/30/100000)
- [Gulpでコマンド実行時に引数を与えて処理を分岐させる方法](https://web-guided.com/716/)
- [HTMLメール用のCSSリセットとテンプレート、主要なメールクライアントのバグにも対応 -normalize.email.css](https://coliss.com/articles/build-websites/operation/css/css-reset-for-html-mail.html)