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

getalog

console.log geta6

webpack(v1)とbabelでES6コードをさくっと書く

最低限のコストで最近よく聞くいい感じのjsを書きたい時の構成をずらーっと書いてみる

準備するもの

  • node/npm (最近はrbenvクローンのnodenvがいい感じ、操作は同じ)
  • webpack
  • babel

.babelrc

.babelrcを設置しとくとbabelのデフォルト設定がこいつの中身で書き換わる

  • Reactを使わないなら、presetのreactはいらない
  • export defaultされたパッケージをimportした時に.defaultで引くのを許せるなら、add-module-exportsはいらない(後述)

Reactいる

{
  "presets": [
    "es2015",
    "stage-0",
    "react"
  ],
  "plugins": [
    "add-module-exports"
  ]
}

いらない

{
  "presets": [
    "es2015",
    "stage-0"
  ],
  "plugins": [
    "add-module-exports"
  ]
}

webpack.config.babel.js

webpack.config.jsという名前でファイルを設置しとくと、webpackコマンドが勝手に中身を読みに行ってくれる

さらにwebpack.config.babel.jsという名前にしておくと、babelなコードとして読んでくれる(webpackはinterpretをロードしているため。このbabelにも.babelrcの内容は適用される)

  • babel-polyfillを食わせておくと、Array.prototype.includesみたいなprototypeメソッドが拡張される
    • babel-runtimeはトップオブジェクトだけ
  • DefinePluginを使っておくとUglifyのCodeElimination(後述)でいい感じにデバッグコードを除去できる
  • ...オペレータを使うと、条件で中身が切り替わるオブジェクトがいい感じに作れる
  • .js以外にもビルドしたいassetがあるならmodule.loadersに追加する(後述)
import 'babel-polyfill';
import path from 'path';
import webpack from 'webpack';

const DEBUG = !process.argv.includes('--release');
const VERBOSE = process.argv.includes('--verbose');

export default {
  cache: DEBUG,

  debug: DEBUG,

  stats: {
    colors: true,
    reasons: DEBUG,
    hash: VERBOSE,
    version: VERBOSE,
    timings: true,
    chunks: VERBOSE,
    chunkModules: VERBOSE,
    cached: VERBOSE,
    cachedAssets: VERBOSE,
  },

  entry: './src/app.js',

  output: {
    publicPath: '/',
    sourcePrefix: '  ',
    path: path.join(__dirname, 'public'),
    filename: 'app.js',
  },

  target: 'web',

  devtool: DEBUG ? 'cheap-module-eval-source-map' : false,

  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({ 'process.env.NODE_ENV': `"${process.env.NODE_ENV || (DEBUG ? 'development' : 'production')}"` }),
    ...(DEBUG ? [] : [
      new webpack.optimize.DedupePlugin(),
      new webpack.optimize.UglifyJsPlugin({ compress: { screw_ie8: true, warnings: VERBOSE } }),
      new webpack.optimize.AggressiveMergingPlugin(),
    ]),
  ],

  resolve: {
    extensions: ['', '.js', '.jsx'],
  },

  module: {
    loaders: [
      { test: /\.jsx?$/, include: [path.resolve(__dirname, 'src')], loader: 'babel' },
    ],
  },
};

Reactがいらない場合、resolvemodule.loadersを下記のように削る

{
  resolve: {
    extensions: ['', '.js'],
  },

  module: {
    loaders: [
      { test: /\.js$/, include: [path.resolve(__dirname, 'src')], loader: 'babel' },
    ],
  },
}

これでwebpackとbabelの設定はおしまい

もろもろインストールする

Reactがいらない場合、最後の一行はいらない

npm init
npm i --save babel-polyfill
npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 babel-plugin-add-module-exports
npm i --save-dev babel-preset-react

ファイルを設置してビルドする

src/app.jsを設置する

わかりやすい例としてasync/awaitを使ったsleepコードでも書いてみる

import 'babel-polyfill';

const sleep = (msec) => new Promise((resolve) => {
  setTimeout(resolve, msec);
});

(async () => {
  console.log('start');
  await sleep(2000);
  console.log('end');
})();

ビルドする

$(npm bin)/webpack

publicフォルダの中にappとかができてると思うので、nodeする

node public/app.js

以上となります

追記

webpackは基本的にすべての依存パッケージを同一ファイルにまとめてくれるので、このpublic/app.jsはどこへ持っていっても動く

webpackには画像などの重いファイルをoutput.publicPathを元にパス化してファイル分割する機能があるため、assetを一緒にbundleした場合はその限りでない

jsだけ書いてたら気にしなくていいです

追記

で、実際ここからどう開発するの?という話

たとえばdom-loaded待ってfastclickでも入れたいとする

npm i --save domready fastclick

で、app.jsでimportする

import 'babel-polyfill';
import domready from 'domready';
import fastclick from 'fastclick';

const sleep = (msec) => new Promise((resolve) => {
  setTimeout(resolve, msec);
});

(async () => {
  console.log('start');
  await sleep(2000);
  console.log('end');
})();

domready(() => {
  fastclick.attach(document.body);
});

サーバー側でロードされることを期待しないのであれば、client側のライブラリもガンガン呼んでいい

余談: export default

add-module-exportsがある

// a.js
export default 'hoge';

// b.js
import a from './a.js';
console.log(a); // 'hoge'

ない

// a.js
export default 'hoge';

// b.js
import a from './a.js';
console.log(a); // { default: 'hoge' }

余談: CodeElimination

const debugInfo = 'hoge';
if (process.env.NODE_ENV === 'development') {
  console.log(debugInfo);
}

こいつをwebpackに通すと、DefinePluginによってprocess.env.NODE_ENVが置換される

const debugInfo = 'hoge';
if ('production' === 'development') {
  console.log(debugInfo);
}

'production' === 'development'は常にfalseなので、Uglifyによってこのconditionはまるごと削除される

const debugInfo = 'hoge';

余談: 例えばStylusをビルドする場合

こんな感じにする

{
  module: {
    loaders: {
      { test: /\.styl$/, loaders: ['style?useable', `css?${DEBUG ? 'sourceMap' : 'minimize'}`, 'stylus'] },
    },
  },
};