Webpackerのディレクトリ構成をRailsから切り離す形に変える方法

  • 6
    いいね
  • 2
    コメント

執筆時点でWebpackerはRailsにてプロジェクトを開始すると、

  • app/javascript/packs/

以下のファイルを見にいきます。そして、このディレクトリ以下の認識できる全てのファイルをWebpackのentryとして吐き出します。

本記事ではこのディレクトリ構成を変え、さらにentryとなるファイルを1つにし、複数のファイルを吐き出さないように書き換えてみようと思います。

バージョン情報

  • Webpacker 1.2
  • Rails 5.1.0

新しいディレクトリ構成

新しい構成ではfrontend以下にファイルを配置してみます。

frontend
├── entry
├── javascripts
└── stylesheets

Webpackのentryとするのはfrontend/entry/application.jsです。少し違和感のある構成ですが、現状のWebpackerが吐き出すファイルをあまり書き換えないようにするとこうすべきかと思います。

Webpackerの設定ファイルなどを書き換える

次に、Webpackerが生成した設定ファイルを書き換えていきます。

1. paths.ymlを修正する

config/webpack/paths.ymlを書き換えます。

paths.yml
default: &default
  config: config/webpack
- entry: packs
+ entry: entry
  output: public
  manifest: manifest.json
  node_modules: node_modules
- source: app/javascript
+ source: frontend
  extensions:
    - .coffee
    - .js
    - .jsx
    - .ts
    - .vue
    - .sass
    - .scss
    - .css
    - .png
    - .svg
    - .gif
    - .jpeg
    - .jpg

development:
  <<: *default

test:
  <<: *default
  manifest: manifest-test.json

production:
  <<: *default

2. shared.jsを書き換える

Webpackerの生成する設定ファイルは、config/webpack/shared.jsを共通ファイルとしてdevelopment環境やproduction環境によって設定を変えています。

今回はshared.jsを書き換えることによって、entryを単一のファイルだけにしてみます。

shared.js
// Note: You must restart bin/webpack-dev-server for changes to take effect

/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

const webpack = require('webpack')
const { basename, dirname, join, relative, resolve } = require('path')
const { sync } = require('glob')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
const extname = require('path-complete-extname')
const { env, paths, publicPath, loadersDir } = require('./configuration.js')
const extensionGlob = `**/*{${paths.extensions.join(',')}}*`
-const packPaths = sync(join(paths.source, paths.entry, extensionGlob))

 module.exports = {
+  entry: {
+    application: resolve(`${paths.source}/${paths.entry}/application.js`)
+  },

-  entry: packPaths.reduce(
-    (map, entry) => {
-      const localMap = map
-      const namespace = relative(join(paths.source, paths.entry), dirname(entry))
-      localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry)
-      return localMap
-    }, {}
-  ),
-
  output: {
    filename: '[name].js',
    path: resolve(paths.output, paths.entry),
    publicPath
  },

  module: {
    rules: sync(join(loadersDir, '*.js')).map(loader => require(loader))
  },

  plugins: [
    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
    new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
    new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true })
  ],

  resolve: {
    extensions: paths.extensions,
    modules: [
      resolve(paths.source),
      resolve(paths.node_modules)
    ]
  },

  resolveLoader: {
    modules: [paths.node_modules]
  }
}

これでfrontend/entry/application.jsのみがentryとして読み込まれるようになりました。

3. .gitignoreを修正する

最後に、entryが変わったので.gitignoreを修正します。

.gitignore
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

/node_modules
/yarn-error.log

.byebug_history
+/public/entry
-/public/packs
/node_modules

vendor/bundle

動作確認

それでは動作確認をしてみましょう。以下のファイルを書き換えて表示を確認してみます。

  • frontend/javascripts/application.js
  • frontend/stylesheets/application.scss
application.js
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import 'stylesheets/application'
import 'javascripts/hello_react.jsx'

console.log('Hello World from Webpacker')
application.scss
body {
  background: red;
}

WebpackerではmoduleのResolvingの設定が書かれているので、相対パスなど気にしなくて良いです。

shared.js
  resolve: {
    extensions: paths.extensions,
    modules: [
      resolve(paths.source),
      resolve(paths.node_modules)
    ]
  },

bin/webpack-dev-serverを実行すると、manifestファイルがpuclic/entry以下に生成されていることが確認できるはずです。

manifest.js
{
  "application.css": "http://localhost:8080/entry/application.css",
  "application.css.map": "http://localhost:8080/entry/application.css.map",
  "application.js": "http://localhost:8080/entry/application.js",
  "application.js.map": "http://localhost:8080/entry/application.js.map"
}

あとはjavascript_pack_tagでjsファイル、stylesheet_pack_tagでcssファイルを読み込むだけです。

<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>

適当なページを開くと、多分赤くなっていると思います。

さいごに

Webpackerが出た当初と比べると、CSSの読み込みなどがいい感じになっているので、ちょっと書き換えるとより快適になる気がしました。

といっても、もちろん単一ファイルを読み込む設定はこれがベストではないと思うので、もっといい方法があれば教えてください:smile: