読者です 読者をやめる 読者になる 読者になる

みかづきブログ その3

3ヶ月つづけてみました。

フロントエンドエンジニアがさくらVPS で Ubuntu + Nginx + Node.js + Express + webpackの環境を整えるまでの道のり - その6 Expressのディレクトリ調整とpm2の導入 😎

春ですね。^ ^

なにか新しいことをはじめたくなりますね。^ ^

前回はExpressの導入を行いました。
今回は、webpackを導入します。

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com




ゴールまでの道のり

  1. VPSサーバ借りる
  2. Ubuntu入れる
  3. Nginx入れる
  4. nodebrew、Node.js入れる
  5. nmp入れる
  6. git入れる
  7. Express入れる
  8. webpack入れる
  9. ES2015をES5に変換できるようにする
  10. PostCSSをCSSに変換できるようにする ← 前回はここまで
  11. Expressのディレクトリ構成変える ← 今回はここから
  12. pm2入れる
  13. サイトつくる



今回のリポジトリ

github.com



前回、前々回はちょっと寄り道をして、webpackの環境を整えました。
今回は、前回、前々回の設定をExpressに取り込んでいきたいと思います。

kimizuka.hatenablog.com

kimizuka.hatenablog.com



適当なディレクトリをつくって移動

mkdir webpack-sample-express
cd webpack-sample-express

ソース用のディレクトリと書き出し用のディレクトリをつくる

mkdir _src
mkdir _src/js
mkdir _src/css
mkdir public
mkdir public/js
mkdir public/css

package.jsonをつくる

vim package.json
package.json
{
  "name": "webpack-sample-express",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "pm2 start ./bin/www -i max",
    "pm2stop": "pm2 stop all",
    "pm2list": "pm2 list",
    "build": "webpack",
    "watch": "webpack --watch & node ./bin/www"
  },
  "babel": {
    "presets": [
      "es2015",
      "es2017"
    ]
  },
  "dependencies": {
    "autoprefixer": "^6.7.7",
    "babel-core": "^6.24.1",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2017": "^6.24.1",
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "css-loader": "^0.28.0",
    "debug": "~2.6.3",
    "express": "~4.15.2",
    "extract-text-webpack-plugin": "^2.1.0",
    "morgan": "~1.8.1",
    "pm2": "^2.4.4",
    "postcss-extend": "^1.0.5",
    "postcss-for": "^2.1.1",
    "postcss-loader": "^1.3.3",
    "postcss-nested": "^1.0.0",
    "postcss-simple-vars": "^3.1.0",
    "pug": "~2.0.0-beta11",
    "serve-favicon": "~2.4.2",
    "style-loader": "^0.16.1",
    "webpack": "^2.3.3"
  }
}

コンフィグファイルをつくる

vim webpack.config.babel.js
webpack.config.babel.js
import webpack from "webpack";
import ExtractTextPlugin from "extract-text-webpack-plugin";

module.exports = [{
  entry: {
    index : __dirname + "/_src/js/index.js"
  },
  output: {
    path: __dirname + "/public/js/",
    filename: "[name].bundle.js"
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: [
        /node_modules/
      ],
      use: [{
        loader: "babel-loader",
        options: {
          presets: [
            "es2015",
            "es2017"
          ]
        }
      }],
    }]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin()
  ]
},{
  entry: {
    index: __dirname + "/_src/css/index.css"
  },
  output: {
    path: __dirname + "/public/css/",
    filename: "[name].bundle.css"
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
        fallback: "style-loader",
        use: "css-loader?minimize!postcss-loader"
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin("[name].bundle.css")
  ]
}];

CSSのコンフィグファイルをつくる

vim postcss.config.js
postcss.config.js
module.exports = {
  plugins: [
    require("postcss-simple-vars")({
      silent: true
    }),
    require("postcss-nested"),
    require("postcss-for"),
    require("postcss-extend"),
    require("autoprefixer")({
      browsers: ["last 5 versions"]
    })
  ]
};

必要なモジュールをインストール

npm install

_src/js/index.jsを用意

vim _src/js/index.js
_src/js/index.js
import hello from "./hello.js";

hello.say();

_src/js/hello.jsを用意

vim _src/js/hello.js
_src/hello.js
export default class Hello {
  static say() {
    alert("HELLO.");
  }
}

_src/css/index.cssを用意

vim _src/css/index.css
_src/css/index.css
@charset "UTF-8";

$teal: #004D40;

%bg-teal {
  background: $teal;
}

body {
  @extend %bg-teal;

  display: flex;

  @for $i from 0 to 10 {
    [data-index=$i] {
      &:after {
        content: "$i";
      }
    }
  }
}

app.jsを用意

vim app.js
app.js
var express      = require("express");
var path         = require("path");
var favicon      = require("serve-favicon");
var logger       = require("morgan");
var cookieParser = require("cookie-parser");
var bodyParser   = require("body-parser");

var index = require("./routes/index");

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");

// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__dirname, "public", "favicon.ico")));
app.use(logger("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

app.use("/", index);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error("Not Found");
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;

bin/www を作成

mkdir bin
vim bin/www
bin/www
#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app   = require("../app");
var debug = require("debug")("app:server");
var http  = require("http");

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || "3000");
app.set("port", port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on("error", onError);
server.on("listening", onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== "listen") {
    throw error;
  }

  var bind = typeof port === "string"
    ? "Pipe " + port
    : "Port " + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(bind + " is already in use");
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === "string"
    ? "pipe " + addr
    : "port " + addr.port;
  debug("Listening on " + bind);
}

routes/index.js を作成

mkdir routes
vim routes/index.js
routes/index.js
var express = require("express");
var router  = express.Router();

/* GET home page. */
router.get("/", function(req, res, next) {
  res.render("index", {
    title: "Express"
  });
});

module.exports = router;

view を作成

mkdir views
vim views/layout.pug
views/layout.pug
pdoctype html
html
  head
    title= title
    link(rel='stylesheet', href='/css/index.bundle.css')
  body
    block content
    script(src="/js/index.bundle.js")
vim views/index.pug
views/index.pug
extends layout

block content
  h1= title
  p Welcome to #{title}
vim views/error.pug
views/error.pug
extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}

試しにビルド!

npm run build

最終的なファイル構成

webpack-sample-express/
 └ _src/
  └ js/
   └ hello.js
   └ index.js
  └ css/
   └ index.css
 └ app.js
 └ bin/
   └ www
 └ nodemodules/
   └ ...
   └ ...
 └ package.json
 └ postcss.config.js
 └ public/
   └ js/
    └ index.bundle.js
   └ css/
    └ index.bundle.css
 └ routes
   └ index.js
 └ views
   └ error.pug
   └ index.pug
   └ layout.pug
 └ webpack.config.babel.js

リポジトリ

github.com

リポジトリにプッシュ / リポジトリからプル

.sass-cache
node_modules

をignoreしてリポジトリにプッシュ。
リポジトリをcloneし、

npm start

IPアドレスの3000番にアクセスすると、

f:id:kimizuka:20170413211037p:plain

てってれー。
無事にExpressが起動しています。
しかもデーモン化しているのでずーっと動きます。

リバースプロキシの設定

しかし、せっかくなので3000番ではなく80番で動かしたいところ。
そこでリバースプロキシを設定します。

sudo vim /etc/nginx/conf.d/express.conf
express.conf
server {
        listen 80;
        server_name (さくらから割り当てられているドメイン);
        location / {
                proxy_pass http://127.0.0.1:3000;
        }
}
sudo nginx -s reload

で設定を反映。
これで、IPではなくドメインでアクセスしてみると、

f:id:kimizuka:20170413211037p:plain

てってれー。
無事にExpressが起動しています。
(IP直打ちだとNginxのデフォルト画面になりますが)



以上で長かった初期設定が終わりました。
あとは思う存分サイトをつくるだけです。

webpackを導入してPostCSSをCSSに変換してCSSファイルを書き出せるようにする。 😎 (フロントエンドエンジニアがさくらVPS で Ubuntu + Nginx + Node.js + Express + webpackの環境を整えるまでの道のり - その5)

春ですね。^ ^

なにか新しいことをはじめたくなりますね。^ ^

前回はwebpackをつかってJavaScriptでimportをつかえるようにしました。
今回は、webpackでPostCSSをつかえるようにします。

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com




ゴールまでの道のり

  1. VPSサーバ借りる
  2. Ubuntu入れる
  3. Nginx入れる
  4. nodebrew、Node.js入れる
  5. nmp入れる
  6. git入れる
  7. Express入れる
  8. webpack入れる
  9. ES2015をES5に変換できるようにする ← 前回はここまで
  10. PostCSSをつかえるようにする ← 今回はここ!
  11. サイトつくる



今回のリポジトリ

github.com



さてさて、もともとはサーバにRubyを入れずにCompassをつかうことを目的としていたはずなんですが、
webpackを導入したら、ついついJavaScriptの環境も整えたくなってしまい脱線してしまいました。
今回はCSSの環境を整えます。
もともとはCompassをつかうためにwebpackを入れたのですが、紆余曲折あってPostCSSをつかってみようと思います。



PostCSSとは

CSSを直接書くことがほとんどなくなってきた昨今に現れたCSSのフレームワークだそうです。
説明を読むに、みんな大好きAutoprefixer や、
まだサポートが十分でないCSS構文がつかえるようになったり(JavaScriptにおけるBabelみたいな感じ)するです。

postcss.org
github.com

もともとはCompassを使おうと僕ですが、
Autoprefixerは欲しいですし、
Compassでつかいたい機能といえば、

  1. ネスト
  2. 変数
  3. for文
  4. 継承

ぐらいなもんなので、PostCSSのプラグインですべてカバーできそうなので、
思い切ってCompassではなくPostCSSを使ってみることにしました。
(if文、mixinは迷いましたが過去5プロジェクトぐらい見返しても使ってなかったのでカット、逆にresetは毎回使ってましたが何処かから持ってくれば良いのでカット)

誰かの名言で、「顧客の欲しいのはドリルではない、壁に空いた穴だ」みたいなのがあったような無かったような気がしますが、
僕が欲しかったのも、「Compass」ではなく「ネスト」、「変数」、「for文」、「継承」だったわけなのでです。まったくもって語呂が悪いですが。



webpackのセットアップ

では今回もまっさらな状態で試してみます。
途中までは前回と全くもって一緒の工程です。

適当なディレクトリをつくって移動
mkdir webpack-sample-css
cd webpack-sample-css
ソース用のディレクトリと書き出し用のディレクトリをつくる
mkdir _src
mkdir _src/css
mkdir public
mkdir public/css
package.jsonつくる
npm init -y
webpackをインストール
npm install --save webpack
babelをインストール
npm install --save babel-core
npm install --save babel-preset-es2015
.babelrcにbabelのバージョンを記載
vim .babelrc

.babelrc

{
  "presets": ["es2015"]
}
コンフィグファイルをつくる
vim webpack.config.babel.js

webpack.config.babel.js

import ExtractTextPlugin from "extract-text-webpack-plugin"; // CSSファイルを書き出すのに必要

module.exports = [{
  entry: {
    index: __dirname + "/_src/css/index.css"
  },
  output: {
    path: __dirname + "/public/css/",
    filename: "[name].bundle.css"
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
        fallback: "style-loader",
        use: "css-loader?minimize!postcss-loader"
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin("[name].bundle.css")
  ]
}];
CSSのコンフィグファイルをつくる
vim postcss.config.js

postcss.config.js

module.exports = {
  plugins: [
    require("postcss-simple-vars")({
      silent: true
    }),
    require("postcss-nested"),
    require("postcss-for"),
    require("postcss-extend"),
    require("autoprefixer")({
      browsers: ["last 5 versions"]
    })
  ]
};
必要なプラグインをインストール
npm install --save autoprefixer css-loader extract-text-webpack-plugin postcss-extend postcss-for postcss-loader postcss-nested postcss-simple-vars style-loader
_src/css/index.cssを用意
vim _src/css/index.css

_src/css/index.css

@charset "UTF-8";

$teal: #004D40;

%bg-teal {
  background: $teal;
}

body {
  @extend %bg-teal;

  display: flex;

  @for $i from 0 to 10 {
    [data-index=$i] {
      &:after {
        content: "$i";
      }
    }
  }
}

無意味に変数、継承、プレフィックスが付きそうなプロパティ(flex)、ネスト、for文をもりこんでみました。

実行!
npm start

public/css/index.bundle.css

body{background:#004d40;display:-webkit-flex;display:-ms-flexbox;display:flex;content:"0";content:"1";content:"2";content:"3";content:"4";content:"5";content:"6";content:"7";content:"8";content:"9";content:"10"}

よさそう。



最終的なファイル構成

webpack-sample-css/
 └ _src/
  └ css/
   └ index.css
 └ nodemodules/
   └ ...
   └ ...
 └ package.json
 └ postcss.config.js
 └ public/
   └ css/
    └ index.bundle.css
 └ webpack.config.babel.js



リポジトリ

github.com



こんな感じですね。
他にもプラグインがめっちゃあるので、いろいろやりたい人はどんどん入れてみると良いと思います。

postcss.parts

ようやく、Ruby無しで「ネスト」、「変数」、「for文」、「継承」がつかえるようになったので、
次回はExpressのディレクトリ構成をいい感じに変更してサーバにアップしたいと思います。

webpackを導入してフロントのJavaScriptでimportをつかえるようにする。 😎 (フロントエンドエンジニアがさくらVPS で Ubuntu + Nginx + Node.js + Express + webpackの環境を整えるまでの道のり - その4)

春ですね。^ ^

なにか新しいことをはじめたくなりますね。^ ^

前回はExpressの導入を行いました。
今回は、webpackを導入します。

kimizuka.hatenablog.com

kimizuka.hatenablog.com

kimizuka.hatenablog.com



ゴールまでの道のり

  1. VPSサーバ借りる
  2. Ubuntu入れる
  3. Nginx入れる
  4. nodebrew、Node.js入れる
  5. nmp入れる
  6. git入れる
  7. Express入れる ← 前回はここまで
  8. webpack入れる ← 今回はここ!
  9. サイトつくる



今回のリポジトリ

手っ取り早くwebpackを試してみたい方は、リポジトリをcloneして、

npm install
npm start

を試してみてください。

github.com




webpackとは

開発時につかったモジュールを束ねてくれる、モジュールバンドラーです。

公式サイトのディスクリプションにもこう書いてあります。

webpack is a module bundler. It packs CommonJs/AMD modules i. e. for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand.

webpackはモジュールバンドラーだぜ。それはCommonJs / AMDモジュールをパックすることができるんだ。例えばブラウザのためにね。だから、コードベースを複数のバンドルに分割することができる。そう。バンドルは必要に応じてロードできるしね。

なるほど。いけてるモジュールバンドラーってことですね。 ^ ^

webpack.js.org




つまりどういうこと?

サーバサイドのJavaScript、node.jsにはrequireというとても便利な機能があります。(余談ですがTitaniumにもありました。require。)
これによって、容易にモジュールを読み込むことができたわけです。

しかし、フロントエンドのJavaScriptにはこれまで同様の仕組みがなく、
scriptタグで読み込んでいたわけです。ライブラリを。

例えば、jQueryを読み込む場合は、

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

という感じに。

しかし、もしもフロントエンドでrequireがつかえたならば、

const $ = require("jquery");

という感じで読み込めるわけです。^ ^

ここまで読んで、「フロントエンドでもrequireがつかえたらいいなぁ」と思ったあなたに、Good News と Bad News があります。

GoodNewsは、ES2015でimportとexportによるモジュール構文が策定されたこと。
これによって、フロントエンドでもrequireと同じような機能がつかえるようになりました。
先程のjQueryの例だと、

import $ from "jquery";

という感じで読み込めるようになったんですね。^ ^

詳しくは、こちら を見てみましょう。

developer.mozilla.org

Bad newsとしては、import文をサポートしているブラウザが、Edge、Mobile Safariぐらいしかないこと。
現状ではまだまだつかえる段階に無いわけです。

しかし、そんな現状でもフロントエンドでimportをつかう方法があるのです。
それは、依存関係を精査して1つのファイルにconcatしてしまうこと。
人間業ではありませんが、コンピュータならできるはず。
そんな感じのツールで有名なのが、Browserify。

http://browserify.org/

詳しくは説明しませんが、requireをつかったJavaScriptファイルをいい感じに1つに結合してくれます。

そして、でました、webpack。

webpack.js.org

webpackはrequireをつかったJavaScriptファイルどころか、CSSファイルや画像もJavaScriptとして読み込めるようにしてしまうツールなのです。
しかし、僕はCSSはCSSファイルとして管理したいし、画像は画像で管理したいタイプなので、
今回はJavaScriptの結合ツールとして活用することにします。



webpackのセットアップ

前置きがとても長くなりました。では具体的にはどうすれば良いのか。すすめていきましょう。
まずは前回までに構築したExpressの環境を忘れて、まっさらな状態で試してみます。
node.js、npmが入っていることを前提とします。

適当なディレクトリをつくって移動
mkdir webpack-sample-js
cd webpack-sample-js
ソース用のディレクトリと書き出し用のディレクトリをつくる
mkdir _src
mkdir _src/js
nkdir public
mkdir public/js
package.jsonつくる
npm init -y
webpackをインストール
npm install --save webpack
babelをインストール
npm install --save babel-core
npm install --save babel-preset-es2015
.babelrcにbabelのバージョンを記載
vim .babelrc

.babelrc

{
  "presets": ["es2015"]
}
babel-loaderをインストール
npm install --save babel-loader
コンフィグファイルをつくる
vim webpack.config.babel.js

webpack.config.js でも良いんですが、 webpack.config.babel.js という名前にしておくと、ES2016で書くことができます。

webpack.config.babel.js

module.exports = [{ // 後々のことを考えて配列にしておく
  entry: {
    index : __dirname + "/_src/js/index.js"
  },
  output: {
    path: __dirname + "/public/js/",
    filename: "[name].bundle.js"
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: [
        /node_modules/
      ],
      use: [{
        loader: "babel-loader",
        options: {
          presets: [
            "es2015"
          ]
        }
      }],
    }]
  }
}];
_src/js/index.jsを用意
vim _src/js/index.js

_src/js/index.js

(() => {
  alert("HELLO.");
})();

とりあえずアローファンクションをつかってみます。


package.jsonにタスクを追加

npm start で webpack --watch を実行するようにします。
_src/js/のファイルを更新する度に、public/js/以下に書き出されます。

vim package.json

package.json

{
  "name": "webpack-sample-js",
  "version": "1.0.0",
  "main": "app.js",
  "license": "MIT",
  "dependencies": {
    "babel-core": "^6.24.1",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.1",
    "webpack": "^2.4.1"
  },
  "scripts": {
    "start": "webpack --watch"
  }
}
実行!
npm start


public/js/index.bundle.js

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


(function () {
  alert("HELLO.");
})();

/***/ })
/******/ ]);

書き出されました。トランスコンパイルされていますね。
せっかくなので、import / exportをつかってみましょう。

vim _src/js/index.js

_src/js/index.js

import hello from "./hello.js";

hello.say();
vim _src/js/hello.js

_src/js/hello.js

export default class Hello {
  static say() {
    alert("HELLO.");
  }
}

public/js/index.bundle.jsを確認すると、

public/js/index.bundle.js

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _hello = __webpack_require__(1);

var _hello2 = _interopRequireDefault(_hello);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

console.log(_hello2.default);

_hello2.default.say();

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Hello = function () {
  function Hello() {
    _classCallCheck(this, Hello);
  }

  _createClass(Hello, null, [{
    key: "say",
    value: function say() {
      alert("HELLO.");
    }
  }]);

  return Hello;
}();

exports.default = Hello;

/***/ })
/******/ ]);

ばっちりです。と言いたいところですが、もはや肉眼ではわからないので確認用ページをつくって確認します。

vim public/index.html

public/index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>HELLO</title>
</head>
<body>
  <script src="./js/index.bundle.js"></script>
</body>
</html>

で、ブラウザにpublic/index.htmlを開いてみると、

f:id:kimizuka:20170417120639p:plain

しっかりアラートが表示されたので良しとしましょう。

で、どうせ肉眼で確認できなくなるのであればMinifyしたいと思うところ。
webpack.config.babel.jsを編集して、webpack.optimize.UglifyJsPluginを追加します。

vim webpack.config.babel.js


webpack.config.babel.js

import webpack from "webpack"; // 追加

module.exports = [{
  entry: {
    index : __dirname + "/_src/js/index.js"
  },
  output: {
    path: __dirname + "/public/js/",
    filename: "[name].bundle.js"
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: [
        /node_modules/
      ],
      use: [{
        loader: "babel-loader",
        options: {
          presets: [
            "es2015"
          ]
        }
      }],
    }]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin() // 追加
  ]
}];


で、npm startしなおすと、

public/js/index.bundle.js

!function(e){function n(r){if(t[r])return t[r].exports;var u=t[r]={i:r,l:!1,exports:{}};return e[r].call(u.exports,u,u.exports,n),u.l=!0,u.exports}var t={};n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=1)}([function(e,n,t){"use strict";function r(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var u=function(){function e(e,n){for(var t=0;t<n.length;t++){var r=n[t];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(n,t,r){return t&&e(n.prototype,t),r&&e(n,r),n}}(),o=function(){function e(){r(this,e)}return u(e,null,[{key:"say",value:function(){alert("HELLO.")}}]),e}();n.default=o},function(e,n,t){"use strict";var r=t(0),u=function(e){return e&&e.__esModule?e:{default:e}}(r);console.log(u.default),u.default.say()}]);

てってれー。無事にMinifyされました。
すごく長くなったんで、SCSSをCSSにコンパイルするところは次回にしましょう。



最終的なファイル構成

webpack-sample-js/
 └ _src/
  └ js/
   └ hello.js
   └ index.js
 └ nodemodules/
   └ ...
   └ ...
 └ package.json
 └ public/
   └ index.html
   └ js/
    └ index.bundle.js
 └ webpack.config.babel.js



リポジトリ

github.com



おまけ

webpackのことをしらべると、webpack1の情報とwebpack2の情報が混ざって出てきてしまうのですが、
webpack.babel.js もとい webpack.config.babel.js で、moduleを指定するところをみると1なのか2なのかがまるわかりです。

  module: {
    loaders: [{...

と、loadersで書いてあるのが1、

  module: {
    rules: [{...

と、rulesで書いてあるのが2です。
僕は2で書くことを心がけました。

そしてこのサイトが大変参考になりました。英語なので雰囲気しかわかりませんでしたが。
blog.madewithenvy.com