webpacker: Nested dependancies not loaded

When loading a dependancy that’s external you prefix it with a tilde e.g. @import "~external-package/external-file";

However if that external package also has external dependancies you will get Module build failed errors when compiling the pack.

To fix this you have to manually go into the package and prefix all additional dependancies with the tilde so that they can be found and imported… which is obviously not a solution.

For example I might have: @import "~@material/typography/mdc-typography";

And then I’ve had to go into node_modules/@material/typography/_mixins.scss and do this:

@import "~@material/feature-targeting/functions";
@import "~@material/feature-targeting/mixins";

Apparently this was an issue with the sass-loader and scoped modules/dependancies: https://github.com/material-components/material-components-web/issues/981 and https://github.com/webpack-contrib/sass-loader/issues/466

They mention in the ticket to add includePaths to sass-loader to load the modules:

{
                    loader: "sass-loader", // compiles Sass to CSS
                    options: {
                      includePaths: [
                        join(dirname(module.filename), 'node_modules')
                      ]
                    }
                }

which in Webpacker I’m assuming is the same as adding resolved_paths: ['node_modules'] to webpacker.yml so that Webpacker knows where to look…

But this does not fix the issue without doing the manual hack job above to prefix everything…

Does Webpacker have a solution for this?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 26 (10 by maintainers)

Most upvoted comments

@jakeNiemiec it worked, my bad there, thanks!

I still find it amusing how using an npm-distributed component is an edge-case though 😄

@jakeNiemiec How is this related to tree-shaking? I think that @iamdriz correctly identified the issue of webpacker not being configured correctly to allow loading nested modules.

At any rate - your solution does not work as well:

@import "~@material/feature-targeting/_functions.scss";
@import "~@material/typography/mdc-typography";

yields

app_1           |     @import "@material/feature-targeting/functions";
app_1           |     ^
app_1           |           File to import not found or unreadable: @material/feature-targeting/functions.
app_1           |           in /app/node_modules/@material/typography/_mixins.scss (line 23, column 1)

And yes, the file is there:

╭─quintasan@tyria ~/Sauce/polikompas ‹2.6.2› ‹master*›
╰─$ ls -al node_modules/@material/feature-targeting/_functions.scss
-rw-r--r-- 1 quintasan quintasan 6477 kwi  3 14:51 node_modules/@material/feature-targeting/_functions.scss

Above I saw: "includePaths": ["/app/config/webpack/node_modules"], I don’t think that is right.

The path is relative to where your run webpack, it should be: includePaths: ["./node_modules"] or includePaths: ["node_modules"]

If none of that works, you could use a dist/pre-compiled version of the @material repo into your source. Not pretty, but I suspect that webpacker is just not up for this edge case.

Edit: Seems like most frameworks need custom directions to use this lib. A PR might be needed to deal with this in a way like: https://github.com/material-components/material-components-web-react/blob/master/README.md#step-3-using-sass.

05/07/19 edit: @aried3r Nice spread finesse.

@jakeNiemiec yeah, still doesn’t work lol

# environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
const path = require('path')

module.exports = environment

environment.loaders.get("sass").use[3] = {
   loader: "sass-loader",
   options: {
     sourceMap: true,
     includePaths: [
       path.join(path.dirname(module.filename), 'node_modules')
     ]
   }
}

environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery'
  })
)
# app/webpack/packs/stylesheets.scss
@import "~@material/feature-targeting/functions";
@import "~@material/typography/mdc-typography";
@import '~bootstrap/scss/bootstrap';

yield

app_1           | [Webpacker] Compiling…
app_1           | [Webpacker] Compilation failed:
app_1           |
app_1           | Hash: 6b7a65d4a5d05365170b
app_1           | Version: webpack 4.29.6
app_1           | Time: 1386ms
app_1           | Built at: 04/19/2019 12:39:33 PM
app_1           |                                      Asset       Size       Chunks             Chunk Names
app_1           |     js/application-b1d30b46897359a051be.js   97.9 KiB  application  [emitted]  application
app_1           | js/application-b1d30b46897359a051be.js.map   81.2 KiB  application  [emitted]  application
app_1           |     js/stylesheets-e5228ab453b47e372b4b.js   4.82 KiB  stylesheets  [emitted]  stylesheets
app_1           | js/stylesheets-e5228ab453b47e372b4b.js.map   3.53 KiB  stylesheets  [emitted]  stylesheets
app_1           |                              manifest.json  703 bytes               [emitted]
app_1           | Entrypoint application = js/application-b1d30b46897359a051be.js js/application-b1d30b46897359a051be.js.map
app_1           | Entrypoint stylesheets = js/stylesheets-e5228ab453b47e372b4b.js js/stylesheets-e5228ab453b47e372b4b.js.map
app_1           | [./app/webpack/controllers sync recursive _controller\.js$] ./app/webpack/controllers sync _controller\.js$ 216 bytes {application} [built]
app_1           | [./app/webpack/controllers/clipboard_controller.js] 2.48 KiB {application} [optional] [built]
app_1           | [./app/webpack/controllers/hello_controller.js] 2.54 KiB {application} [optional] [built]
app_1           | [./app/webpack/controllers/index.js] 396 bytes {application} [built]
app_1           | [./app/webpack/packs/application.js] 47 bytes {application} [built]
app_1           | [./app/webpack/packs/stylesheets.scss] 949 bytes {stylesheets} [built] [failed] [1 error]
app_1           |     + 32 hidden modules
app_1           |
app_1           | ERROR in ./app/webpack/packs/stylesheets.scss
app_1           | Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
app_1           | ModuleBuildError: Module build failed (from ./node_modules/sass-loader/lib/loader.js):
app_1           |
app_1           | @import "@material/feature-targeting/functions";
app_1           | ^
app_1           |       File to import not found or unreadable: @material/feature-targeting/functions.
app_1           |       in /app/node_modules/@material/typography/_mixins.scss (line 23, column 1)
app_1           |     at runLoaders (/app/node_modules/webpack/lib/NormalModule.js:301:20)
app_1           |     at /app/node_modules/loader-runner/lib/LoaderRunner.js:367:11
app_1           |     at /app/node_modules/loader-runner/lib/LoaderRunner.js:233:18
app_1           |     at context.callback (/app/node_modules/loader-runner/lib/LoaderRunner.js:111:13)
app_1           |     at Object.render [as callback] (/app/node_modules/sass-loader/lib/loader.js:52:13)
app_1           |     at Object.done [as callback] (/app/node_modules/neo-async/async.js:8077:18)
app_1           |     at options.error (/app/node_modules/node-sass/lib/index.js:294:32)
app_1           | Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--7-1!node_modules/postcss-loader/src/index.js??ref--7-2!node_modules/sass-loader/lib/loader.js??ref--7-3!app/webpack/packs/stylesheets.scss:
app_1           |     Entrypoint mini-css-extract-plugin = *
app_1           |     [./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/lib/loader.js?!./app/webpack/packs/stylesheets.scss] ./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/lib/loader.js??ref--7-3!./app/webpack/packs/stylesheets.scss 313 bytes {mini-css-extract-plugin} [built] [failed] [1 error]
app_1           |
app_1           |     ERROR in ./app/webpack/packs/stylesheets.scss (./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/lib/loader.js??ref--7-3!./app/webpack/packs/stylesheets.scss)
app_1           |     Module build failed (from ./node_modules/sass-loader/lib/loader.js):
app_1           |
app_1           |     @import "@material/feature-targeting/functions";
app_1           |     ^
app_1           |           File to import not found or unreadable: @material/feature-targeting/functions.
app_1           |           in /app/node_modules/@material/typography/_mixins.scss (line 23, column 1)
app_1           |
app_1           | Completed 500 Internal Server Error in 7082ms (ActiveRecord: 0.5ms)
app_1           |
app_1           |
app_1           |
app_1           | Webpacker::Manifest::MissingEntryError - Webpacker can't find stylesheets in /app/public/packs/manifest.json. Possible causes:
app_1           | 1. You want to set webpacker.yml value of compile to true for your environment
app_1           |    unless you are using the `webpack -w` or the webpack-dev-server.
app_1           | 2. webpack has not yet re-run to reflect updates.
app_1           | 3. You have misconfigured Webpacker's config/webpacker.yml file.
app_1           | 4. Your webpack configuration is not creating a manifest.
app_1           | Your manifest contains:
app_1           | {
app_1           |   "application.js": "/packs/js/application-b1d30b46897359a051be.js",
app_1           |   "application.js.map": "/packs/js/application-b1d30b46897359a051be.js.map",
app_1           |   "entrypoints": {
app_1           |     "application": {
app_1           |       "js": [
app_1           |         "/packs/js/application-b1d30b46897359a051be.js"
app_1           |       ],
app_1           |       "js.map": [
app_1           |         "/packs/js/application-b1d30b46897359a051be.js.map"
app_1           |       ]
app_1           |     },
app_1           |     "stylesheets": {
app_1           |       "js": [
app_1           |         "/packs/js/stylesheets-e5228ab453b47e372b4b.js"
app_1           |       ],
app_1           |       "js.map": [
app_1           |         "/packs/js/stylesheets-e5228ab453b47e372b4b.js.map"
app_1           |       ]
app_1           |     }
app_1           |   },
app_1           |   "stylesheets.js": "/packs/js/stylesheets-e5228ab453b47e372b4b.js",
app_1           |   "stylesheets.js.map": "/packs/js/stylesheets-e5228ab453b47e372b4b.js.map"
app_1           | }
app_1           | :
app_1           |   app/views/layouts/application.html.haml:10:in `_app_views_layouts_application_html_haml___4004440967550002970_47332951207380'

Hey, thanks for your work, I was facing the exact same issue.

This is a bit more involved, but doesn’t hardcode use[3]. Maybe there’s a cleaner version as well.

const sassLoaderIndex = environment.loaders
  .get("sass")
  .use.findIndex(el => el.loader === "sass-loader")

let sassLoader = environment.loaders.get("sass").use[sassLoaderIndex]

sassLoader = {
  ...sassLoader,
  options: {
    ...sassLoader.options,
    includePaths: ["./node_modules"],
  },
}

environment.loaders.get("sass").use[sassLoaderIndex] = sassLoader

I’m probably being a pain in the ass but people making those moudules are not making it any easier to use them.

I just ran into this today. Is there any solution apart from monkeypatching the packages?