Laravel Mixで作る良い感じのフロントエンドビルド環境

PHPのフレームワークLaravelに同梱されているビルドツールのLaravel Mixが、設定が楽ちんでSassやJs(ES2015記法)のビルドが簡単にできて良い感じだったので、少し手を加えて個人的にさらに良い感じの環境を作ってみました。

Laravel Mix自体はnpmのモジュールになっているので、単体でインストールしてLaravelを使わないプロジェクトでも使用可能です。

コードはGithubにあげてます。

https://github.com/KanDai/laravelmix-frontend

## 環境

- OS: macOS Sierra
- Node: 8.9.0

## やったこと

### ファイルをglobのワイルドカードで指定できるように

デフォルトではワイルドカードの指定が出来ないので、複数のファイルをビルドするときには下記のように書かないといけません。

```webpack.mix.js
mix
  .js('resources/assets/js/app.js', 'public/js')
  .js('resources/assets/js/app2.js', 'public/js')
  .sass('resources/assets/sass/app.scss', 'public/css')
  .sass('resources/assets/sass/app2.scss', 'public/css');
```

これだと、生成したいファイルが増えるたびに足していかなければいけないので、globをインストールしてワイルドカードでファイル指定できるようにしました。

```webpack.mix.js
const glob = require('glob')

// Compile all scss files directly under the sass directory
glob.sync('resources/sass/*.scss').map(function (file) {
  mix.sass(file, 'public/assets/css')
})

// Compile all js files directly under the js directory
glob.sync('resources/js/*.js').map(function (file) {
  mix.js(file, 'public/assets/js')
})
```

参考
https://qiita.com/katsunory/items/3585ab8072d74d234302


### SASSのglob importを使う

Sass(SCSS)もglob importを使ってこんな感じで書きたいところ。

```styles.scss
@import "base/*";
@import "modules/**/*";
```

webpack.mix.jsで `mix.webpackConfig` を呼んで、Webpackのオプションを拡張、 import-glob-loader を使う方法で動きました。

```webpack.mix.js
mix.webpackConfig({
    module: {
      rules: [
        { // Allow .scss files imported glob
          test: /\.scss/,
          loader: 'import-glob-loader'
        }
      ]
    }
  })
```

参考
https://qiita.com/kimama1997/items/f8f448ef6b14245e8a9d


### PostCSSのプラグインを追加

PostCSSで拡張できるようだったので、何個かPostCSSのプラグインを追加してます。

- CSS MQPacker → メディアクエリーの最適化
- CSS Declaration Sorter → プロパティをソート

```webpack.mix.js
mix.sass(file, 'public/assets/css')
    .options({
      processCssUrls: false,
      postCss: [
        require('css-mqpacker')(),
        require('css-declaration-sorter')({
          order: 'smacss'
        })
      ],
      autoprefixer: {
        options: {
          browsers: [
            'last 2 versions',
          ]
        }
      }
```

autoprefixerは元々入ってますがここで設定も出来ます。


### Prettierによるコード整形

最近話題のPrettierを導入してみました。

これもWebpackのオプションを拡張して `prettier-loader` を使っています。
prettierの設定もここに書いてます。jsでクォートはシングルクォート、行末のセミコロンをトル設定だけしてます。

```webpack.mix.js
mix.webpackConfig({
    module: {
      rules: [
        { // JavaScript Prettier Setting
          test: /\.js$/,
          loader: 'prettier-loader',
          options: { // Prettier Options https://prettier.io/docs/en/options.html
            singleQuote: true,
            semi: false
          }
        },
        { // Allow .scss files imported glob
          test: /\.scss/,
          loader: 'import-glob-loader'
        },
        { // Sass Prettier Setting
          test: /\.scss$/,
          loader: 'prettier-loader',
          options: {
            parser: "postcss"
          }
        },
      ]
    }
  })
```

その他、元々入ってるBrowserSyncの設定や、通知、ソースマップの設定などもしてます(詳しくはREADMEに記載)。

## コード全体

```webpack.mix.js
// import .env / plugin
require('dotenv').config()
const mix = require('laravel-mix')
const glob = require('glob')

// Compile all scss files directly under the sass directory
glob.sync('resources/sass/*.scss').map(function (file) {
  mix.sass(file, 'public/assets/css')
    .options({
      processCssUrls: false,
      postCss: [
        require('css-mqpacker')(),
        require('css-declaration-sorter')({
          order: 'smacss'
        })
      ],
      autoprefixer: {
        options: {
          browsers: [
            'last 2 versions',
          ]
        }
      }
    })
})

// Compile all js files directly under the js directory
glob.sync('resources/js/*.js').map(function (file) {
  mix.js(file, 'public/assets/js')
})

mix
  // Notification off
  .disableNotifications()

  // Setting browserSync
  .browserSync({
    // Using a vhost-based url
    // proxy: process.env.MIX_SENTRY_DSN_PUBLIC || 'http://localhost:8080',
    // Serve files from the public directory
    server: {
      baseDir: 'public',
      index: 'index.html'
    },
    port: 8080,
    proxy: false,
    // Watch files
    files: 'public/**/*'
  })

  // Added webpackConfig settings
  .webpackConfig({
    module: {
      rules: [
        { // JavaScript Prettier Setting
          test: /\.js$/,
          loader: 'prettier-loader',
          options: { // Prettier Options https://prettier.io/docs/en/options.html
            singleQuote: true,
            semi: false
          }
        },
        { // Allow .scss files imported glob
          test: /\.scss/,
          loader: 'import-glob-loader'
        },
        { // Sass Prettier Setting
          test: /\.scss$/,
          loader: 'prettier-loader',
          options: {
            parser: "postcss"
          }
        },
      ]
    }
  })

// Generate sourcemap only for development environment
if (!mix.inProduction()) {
  mix.sourceMaps()
}
```

実際、Laravel使ってるプロジェクトのフロントエンドで使ってますが、今のところ良い感じです。