2018/05/23 追記
entry 作る箇所だけ小さく別ライブラリにしました。
euxn23/webpacker-entry
はじめに
この内容は meguro.es#15 での発表を元に内容を整理したものです。
リポジトリ: euxn23/webpacker-pure-config
スライド: slideshare
Rails で JS つらかった問題
- 標準設定で ES6 が使えない
- JS ライブラリの gem 移植 hoge-rails の本体追従が遅い
- gem 経由でインストールされる JS / CSS ライブラリ
- Sprockets という独自のモジュールバンドラ
Webpacker の誕生
- Rails 5.1 で標準搭載
- Webpacker を Rails に合わせて使えるように作られた Ruby / JS ライブラリ
- 隠蔽されたゼロコンフィグの Webpack
- npm エコシステムベースのビルドフローが使える
- Rails 向けの View Helper
普通の JS 開発ができる!!
本当に?
- 環境変数で分岐するモジュール(Undocumented)
1const createEnvironment = () => {
2 const path = resolve(__dirname, "environments", `${nodeEnv}.js`);
3 const constructor = existsSync(path) ? require(path) : Environment;
4 return new constructor();
5};
1// Set nested object prop using path notation
2environment.config.set("resolve.extensions", [".foo", ".bar"]);
3environment.config.set("output.filename", "[name].js");
4
5// Merge custom config
6environment.config.merge(customConfig);
7
8// Delete a property
9environment.config.delete("output.chunkFilename");
Webpacker makes it easy to use the JavaScript pre-processor and bundler webpack 3.x.x+ to manage application-like JavaScript in Rails.



何がつらいか
- 独自の設定構文を覚える必要がある
- 既存の webpack.config をそのまま使えない
- Undocumented な挙動
- Webpack 本体への追従の遅さ
webpacker.js 危険なのでは?
脱 webpacker.js
config の要件
- ruby 側の webpacker のデフォルト設定で動くようにする
- JS の配置ディレクトリ(public/packs or public/packs-test)
- キャッシュ回避のためのハッシュ(Rails の作法)

- ManifestPlugin ベースの key-value の entry 設定

{ [relativePath]: absolutePath }
実装
1const readdirRecursivelySync = (baseDir, options = {}) =>
2 fs
3 .readdirSync(baseDir, options)
4 .map(
5 path =>
6 fs.statSync(`${baseDir}/${path}`).isDirectory()
7 ? readdirRecursivelySync(`${baseDir}/${path}`, options)
8 : `${baseDir}/${path}`
9 )
10
11const flatten = paths => {
12 const flattened = paths.reduce(
13 (prev, next) =>
14 Array.isArray(next) ? [...prev, ...next] : [...prev, next],
15 []
16 )
17 return flattened.filter(Array.isArray).length > 0
18 ? flatten(flattened)
19 : flattened
20}
21
22{
23 mode: 'development',
24 devtool: 'cheap-module-source-map',
25 entry: flatten(readdirRecursivelySync(baseDir))
26 .filter(p => extensions.includes(extname(p)))
27 .reduce(
28 (prev, next) => ({
29 ...prev,
30 [relative(
31 '.',
32 `${dirname(relative(baseDir, next))}/${basename(next, extname(next))}`
33 )]: next
34 }),
35 {}
36 ),
37
38...
煩雑
ライブラリ化
euxn23/webpacker-pure-config
特徴
1return {
2 mode: 'development',
3 devtool: 'cheap-module-source-map',
4 entry: mapPathArrayToEntryHash(
5 readdirRecursivelySyncFlatten(baseDir),
6 baseDir,
7 extensions
8 ),
9 output: {
10 filename: '[name]-[chunkhash].js',
11 chunkFilename: '[name]-[chunkhash].chunk.js',
12 hotUpdateChunkFilename: '[id]-[hash].hot-update.js',
13 path: resolve('./public/packs'),
14 publicPath: '/packs/'
15 },
16 module: {
17 strictExportPresence: true,
18 rules
19 },
20 plugins: [
21 new ManifestPlugin({ publicPath: '/packs/', writeToFileEmit: true })
22 ],
23...
- 暗黙的な NODE_ENV 依存無し(環境別に提供)
1const { dev } = require("webpacker-pure-config");
2module.exports = dev();
- Config Object が export されるだけなので Rest Spread で拡張可能
1module.exports = {
2 ...baseConfig,
3 plugins: [
4 ...baseConfig.plugins,
5 new webpack.DefinePlugin({
6 "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
7 }),
8 ],
9};
- Rails / webpacker.yml 依存無し(ゼロコンフィグ)
Q. Webpack に追従できるの?
A. 独自機能とかないのでまあ、がんばります(がんばりましょう)(一緒にメンテしてください)