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に公開しています。

バージョン

  • 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のアカウントを使用することを前提としています。

# 送信先(複数はカンマ区切り、スペースを入れない)
[email protected],[email protected]

# 送信元
[email protected]
smtpPass=example_03_pass
smtpHost=smtp.gmail.com
smtpPort=465

gulpfile.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で仮画像を配置する方法を取っております。

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

参考サイト