LoginSignup
15
10

More than 3 years have passed since last update.

webpackerからapp/assets内のリソースを共用する

Last updated at Posted at 2018-01-05

こんにちは、はなくらです :raised_hands:
今回は Webpackerのsass-loaderからRailsの app/assets 以下のリソースを利用する という:cross::cross:を生成する話をします:smiling_imp:

Q. なんでそんな不毛な事してるの?🙃

A.app/assets/stylesheets に定義してある変数とコンポーネントをWebpacker上から使いたかったんだ:innocent:

image-url()などのないsassを読むだけなら相対パスでimportを頑張ればよいのですが、app/assets以下のsassでは
image-urlfont-url という少し厄介なsass関数がいます:rolling_eyes:
これらの関数が使われているsassファイルをSprockets / Webpackerのどちらからimportされても動くようにするというのが今回のぽはなしです:sleeping:

✋ 手順

  1. node-sass上でimage-url/font-url関数を使えるようにする:innocent:
  2. Sprocketsによるコンパイル時と同じパスでリソースを解決できるようにする :sunglasses:
  3. file-loaderでリソースを読み込めるようにする :file_folder:

🔨 解説

1. node-sass上でimage-url/font-url関数を使えるようにする

node-sass上で読み込まれたsassにはimage-urlfont-urlなどの関数がありませんが、node-sassの機能で、JSの関数をsassの世界へ公開する事ができます。
今回はnode-sass用のimage-url / font-urlなどの関数を予め定義してあり、パス解決を自前で行うことが出来るnode-sass-asset-functionsというモジュールを利用します。

config/webpack/loaders/sass.js
const assetFunctions = require('node-sass-asset-functions')
const assetFuncs = assetFunctions();

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: [
      // ...
      { loader: 'sass-loader', options: {
          // 生成した関数をnode-sassへパスする
          functions: assetFuncs,
      } }
      // ...
    ]
}

まずこれでimage-urlなどの関数が使えるようになります。

2. Sprocketsによるコンパイル時と同じパスでリソースを解決できるようにする

node-sass-asset-functionsは第1引数にオプションを渡すことができ、このオプションのasset_cache_busterプロパティにパス解決を行う関数を渡すことで、url(path)に埋め込まれるpathを自由に書き換えることが出来ます。
この関数内でリソースをファイルシステム上の絶対パスへ変換します。
(相対パスでもいいかもしれないんだけど、どこが基点パスになるのか考えるのが面倒)

const assetFuncs = assetFunctions({
  asset_cache_buster(filePath, realPath, done) {
    // sass上で `image-url('path/to/image.png')` されると
    // filePathに `images/path/to/image.png` のようなパスが入ってくるので、ファイルシステム上の絶対パスへ解決する
    const { pathname } = url.parse(filePath);
    done({ path: path.join(assetsPath, pathname) });
  },
});

さらに、app/assets/stylesheets内のスタイルシートをimport出来るようにnode-sassへincludePathsオプションを渡します。

const stylesheetsPath = join(process.cwd(), 'app/assets/stylesheets')

// ...

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: [
      // ...
      { loader: 'sass-loader', options: {
          // `app/assets/stylesheets` へのパスを渡す
          includePaths: [stylesheetsPath],
          functions: assetFuncs,
      } }
      // ...
    ]
}

これでリソースとスタイルシートのパスがSprockets上でのコンパイルと変わらずに解決されるようになります。

3. file-loaderでリソースを読み込めるようにする

最後にfile-loaderを設定することでwebpack上からapp/assets以下のリソース郡を読み込めるようになります。
image-urlなどをファイルシステムの絶対パスに変換されたものが、file-loaderで読み込まれ、webpackでバンドルされます。

config/webpack/loaders/file.js
const path = require('path');

module.exports = {
  test: /\.(jpg|jpeg|png|gif|svg|eot|ttf|woff|woff2)$/i,
  use: [{
    loader: 'file-loader',
    options: {
      // webpacker.yml のentryと同じにする。 publicPathは/で囲わないと、urlの結合がおかしくなるので注意
      // ( `url('packsapp/assets/...')` みたいになるし、相対パスだと実行時にちゃんと解決されるかわからん)
      publicPath: '/packs/',
      name: process.env.NODE_ENV === 'production' ? '[path][name]-[hash].[ext]' : '[path][name].[ext]',
    },
  }],
};

最終型✨

あとはお好みでExtractTextPluginとかをどうぞ!:golfer:

config/webpack/loaders/sass.js
const assetFunctions = require('node-sass-asset-functions')
const path = require('path')
const url = require('url')

const assetsPath = path.join(process.cwd(), 'app/assets')
const stylesheetsPath = path.join(assetsPath, 'stylesheets')

const assetFuncs = assetFunctions({
  asset_cache_buster: (filePath, realPath, done) => {
    const { pathname } = url.parse(filePath);
    done({ path: path.join(assetsPath, pathname) });
  }
});

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: [
    {
      loader: 'css-loader',
      options: { modules: true }
    },
    { loader: 'resolve-url-loader' },
    {
      loader: 'sass-loader',
      options: {
        includePaths: [stylesheetsPath],
        functions: assetFuncs,
      }
    }
  ]
}

古い資産を限界まで使い倒したい限界オタクにぽすすめです:relaxed:

15
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
10