Webpack4 + TypeScript + Sass + EJSの開発環境を構築する

Webpack4がリリースされたので、Webpack4をベースにTypeScript、Sass、EJSの開発環境を構築したので、備忘録として残しておきます。

  • OS X 10.13.3
  • Node.js 9.5.0

ディレクトリ構造

├── docs
│   ├── css
│   │   └── bundle.css
│   ├── images
│   │   ├── images
│   │   │   └── 01.jpg
│   │   └── 01.jpg
│   ├── js
│   │   └── bundle.js
│   └── index.html
├── src
│   ├── css
│   │   ├── main.scss
│   │   └── sub.scss
│   ├── images
│   │   └── 01.jpg
│   ├── js
│   │   ├── main.ts
│   │   └── sub.ts
│   ├── index.ejs
│   └── sub.ejs
├── package.json
├── postcss.config.js
├── tsconfig.json
└── webpack.config.js

src内の各ファイルをコンパイル及びバンドルし、docsに出力します。

パッケージのインストール

Webpack

$ npm install --save-dev webpack webpack-cli

Webpack4は既に正式リリースされており、バージョンを省略しても4.xがインストールされます。

TypeScript

$ npm install --save-dev typescript ts-loader

TypeScript本体と、TypeScriptをWebpackで扱うためのts-loaderをインストールします。

Sass

今回構築する環境では、Sassを以下の手順で処理します。

  1. Sass(SCSS)をコンパイル(sass-loader)
  2. prefixの付与(postcss-loader + autoprefixer
  3. .cssと画像を出力(extract-text-webpack-pluginfile-loader

必要となるパッケージをインストールします。

$ npm install --save-dev node-sass sass-loader postcss-loader autoprefixer css-loader file-loader style-loader extract-text-webpack-plugin@next

extract-text-webpack-pluginについては、このissueにもあるように、現在はベータ版をインストールする必要があるため、@nextを付しています。

EJS

$ npm install --save-dev ejs-compiled-loader html-webpack-plugin

ejs-loaderというloaderもありますが、この環境ではejs-compiled-loaderを使います。

設定ファイルの作成

package.json

  ...
  "scripts": {
    "webpack:prod": "webpack --mode production",
    "webpack:dev": "webpack --mode development"
  },
  ...

npm runで実行するスクリプトを定義しておきます。

webpack.config.js

const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: path.resolve(__dirname, './src/js/main.ts'),
  output: {
    path: path.resolve(__dirname, './docs'),
    filename: './js/bundle.js'
  },
  plugins: [
    new ExtractTextPlugin({
      filename: './css/bundle.css'
    }),
    new HtmlWebpackPlugin({
      filename: './index.html',
      template: './src/index.ejs'
    })
  ],
  resolve: {
    extensions: [
      '.ts',  // for ts-loader
      '.js'   // for style-loader
    ]
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader'
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            'css-loader',
            'postcss-loader',
            'sass-loader'
          ]
        })
      },
      {
        test: /\.(jpg|png|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: './images/[name].[ext]',
            outputPath: './',
            publicPath: path => '.' + path
          }
        }
      },
      {
        test: /\.ejs$/,
        use: 'ejs-compiled-loader'
      }
    ]
  }
}

エントリポイントはmain.tsで、この中でmain.scssや他の.tsファイルをimportします。

通常だとCSSもbundle.jsに含まれることとなりますが、extract-text-webpack-pluginにより、bundle.js内のCSS部分は削除され、別途.cssファイルが出力されます。

postcss.config.js

module.exports = {
  plugins: [
    require('autoprefixer')({
      browsers: 'last 5 versions'
    })
  ]
}

Autoprefixerの設定をしておきます。

tsconfig.json

{
  "compilerOptions": {
    "sourceMap": true,
    "target": "es5",
    "module": "es2015"
  }
}

TypeScriptの設定を行います。

ソースファイルの作成

TypeScript

main.ts

import '../css/main.scss'
import y from './sub'

const x: string = 'hoge'
console.log(x)

console.log(y)

ここで.scssimportします。

sub.ts

const y : number = 3
export default y

Sass

main.scss

@import './sub.scss';

body {
  background-color: #cfc;
  background-image: url('../images/01.jpg');

  & > p {
    color: #00f;
  }
}

sub.scss

div {
  display: flex;
  border: 1px solid;

  &.test {
    color: #fcf;
  }
}

EJS

index.ejs

<!DOCTYPE html>
<head>
<title>title</title>
</head>
<%- include src/sub %>

sub.ejs

<p><%- 'hoge' %></p>

Webpackの実行

$ npm run webpack:dev

エラーなくコンパイル・バンドルされていれば成功です。