webpack3で作る開発環境(webpack + sass + autoprefixer)


# はじめに
この記事は以下のような方の手助けになるかもしれません。

- webpack3で開発環境を作りたいけどうまく作れない
- sass + autoprefixer をいれて開発したい
- ためしにwebpackを使って見たい

エンジニア歴が浅いので、あまり信用しすぎずにお願いします。
(もっとこうしたほうがいいよ！というアドバイスお待ちしております)


# ディレクトリ構造
```
.
├── package-lock.json
├── package.json
├── postcss.config.js
├── script
│   └── shell-scripts
│       └── release.sh
├── src
│   ├── assets
│   │   └── json
│   │       └── data.json
│   ├── index.html
│   ├── js
│   │   ├── config
│   │   │   ├── config.js
│   │   │   ├── development.js
│   │   │   └── production.js
│   │   ├── entry.js
│   │   └──module
│   │       └── sample.js
│   └── scss
│       ├── module
│       │   └── reset.scss
│       └── style.scss
└── webpack.config.js

```

# configファイル
webpack周りの設定のためのファイルは以下の2つです。

- webpack.config.js
- postcss.config.js

また、今回は以下の package を使用しています。

## package.json
```package.json
{
  "name": "web",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "rm -rf dest && NODE_ENV=development webpack-dev-server",
    "build": "rm -rf dest && NODE_ENV=production  webpack -p",
    "build:release": "rm -rf dest && sh ./scripts/shell-scripts/release.sh && NODE_ENV=production webpack -p"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^7.2.1",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "copy-webpack-plugin": "^4.2.3",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.2",
    "node-sass": "^4.7.2",
    "postcss-loader": "^2.0.9",
    "precss": "^2.0.0",
    "sass-loader": "^6.0.6",
    "style-loader": "^0.19.0",
    "url-loader": "^0.6.2",
    "webpack": "^3.10.0",
    "webpack-dev-server": "^2.9.7"
  }
}
```

## webpack.config.js
```webpack.config.js
const webpack           = require('webpack');
const path              = require('path');
const autoprefixer      = require('autoprefixer');
const precss            = require('precss');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const ENV = process.env.NODE_ENV;

module.exports = [
	{
		devtool: ENV === 'development' ? 'cheap-module-eval-source-map' : '',
		devServer: {
			contentBase: path.join(__dirname, 'dest'),
			port: 6500,
		},
		entry: {
			'bundle': path.join( __dirname, 'src', 'js', 'entry.js' )
		},
		output: {
			path: path.join( __dirname, 'dest', 'js' ),
			filename: '[name].js'
		},
		module: {
			loaders: [
				{
					test: /\.js$/,
					exclude: /node_modules/,
					loader: 'babel-loader',
					query: {
						presets: ['es2015']
					}
				}
			]
		},
		plugins: [
			new webpack.ProvidePlugin({
				'$' : 'jquery'
			}),
			new webpack.DefinePlugin({
				'NODE_ENV': JSON.stringify(ENV),
				'API_KEY' : JSON.stringify("KEY:XXX-XXX")
			}),
		]
	},
	{
		entry: {
			style: path.join( __dirname, 'src', 'scss', 'style.scss' )
		},
		output: {
			path: path.join( __dirname, 'dest', 'css' ),
			filename: '[name].css'
		},
		module: {
			rules: [
				{
					test: /\.scss$/,
					use: ExtractTextPlugin.extract({
						fallback: 'style-loader',
						use: ['css-loader', 'postcss-loader', 'sass-loader']
					}),
					exclude: /node_modules/
				},
			]
		},
		plugins: [
			new ExtractTextPlugin('style.css'),
			new CopyWebpackPlugin(
				[
					{
						from: path.join( __dirname, 'src',  'assets'),
						to:   path.join( __dirname, 'dest', 'assets')
					},
					{
						from: path.join( __dirname, 'src',  'index.html'),
						to  : path.join( __dirname, 'dest', 'index.html')
					}
				],
				{ ignore: [ '.DS_Store', '*.js', '*.jsx', '*.scss', '*.css' ] }
			),
		]

	}
]
```

webpackでsassをコンパイルしていい感じにするために、
そもそも設定を書き分けています。
javascriptをbundleするための設定と、sassやそのほかのアセットの
コピータスクなどを分けて書くことで
エントリーポイントを変に複雑にしなくていいので楽になりました。

もしreactなどを使う場合には`webpack.ProvidePlugin`に
reactやreactDomなどを追加すると便利に使えます。

webpack-dev-serverの設定も入っているので、
開発するときもコマンド一発でスタートできるので
この設定はかなり気に入っています。

## postcss.config.js
```postcss.config.js
module.exports = {
	plugins: {
		'autoprefixer': {
			browsers: ['last 2 versions', '> 2%']
		}
	}
}
```
pluginの設定です。webpack.config.jsの中に書きたかったのですが
うまくいかなかったので外に出して書きました。


# srcファイル

以下は完全に蛇足ですが、動くかどうかを試す際に入れていたサンプルコードです。

## src/js/entry.js
```entry.js
import Person from './module/sample.js';
import CONFIG from './config/config.js';

var human = new Person('nisshy');
human.sayHello();

```

entry.js で config やら module を読み込みます。
ES6の記法を試すためにconstructorを使って見ました。

## src/js/module/sample.js

```src/js/sample.js
class Person {
	constructor(name) {
		this.name = name;
	}
	sayHello() {
		console.log( "Hello, I'm " + this.getName() );
	}
	getName() {
		return this.name;
	}
}
export default Person

```
sample.jsでclassを定義してexport defaultしています。


## src/js/config/config.js
```config.js
const config = require( './' + NODE_ENV );

export default class Environment {
	static get API_PATH() {
		return config.API_PATH;
	}
	static get SITE_NAME(){
		return config.SITE_NAME;
	}
}

```

このconfigファイルは環境によって値を変えたいものを定義したいものを
読んでくるためのファイルです。
ここではテスト環境と本番環境でapiのpathが違うことを想定して、
`development`と`production`とで出し分けをしています。
(SITE_NAMEは単純にテストのために使って見ました)

webpackを実行する際に `NODE_ENV=development`を指定すると、
`require( './development ')`となり、developmentの設定を読んでくれます。
本番と開発を手作業で書き分けたりすると99.9%ミスを起こすので
初めからこんな感じでかき分けるようにしました。


## src/js/config/development.js
```development.js
const config = {
	API_PATH: 'https://fizzbuzz.com/api/',
	SITE_NAME: 'DEVELOPMENT SITE'
}

module.exports = config;
```

## src/js/config/production.js
```production.js
const config = {
	API_PATH: 'https://foobar.com/api/',
	SITE_NAME: 'PRODUCTION SITE'
}

module.exports = config;
```


# 最後に
少し余分な記述があったりしますが、開発を始める際のベースとしてのセットであれば
問題ないのかな？と思ってます。
個人的にはgulpよりもwebpackのほうがかっこいいなぁと思っているので
もう少し使い方を掘っていきたいと思います。