Css-loader: Composing from a package

Created on 28 Oct 2020  路  11Comments  路  Source: webpack-contrib/css-loader

  • Operating System: OSX 10.15.7
  • Node Version: 8.17.0
  • NPM Version: 6.13.4
  • webpack Version: 4.41.2
  • css-loader Version: 2.1.1

Expected Behavior

Composing from a node_modules package should reference the file correctly.

Actual Behavior

The composed file is actually processed but PostCSS still throws a ModuleNotFoundError error

Code

Something simple:

{
  "dependencies": {
    "some-package": "^1"
  }
}
.c-class {
  composes: another-class from 'some-package/src/file.css';
}

How Do We Reproduce?

Use this sandbox: https://codesandbox.io/s/ecstatic-star-vjyuo?file=/src/index.js

The sandbox throws a bad error.

When I run the similar code in my local environment, I get the following error:

ModuleNotFoundError: Module not found: Error: Can't resolve './@mavenlink/design-system/src/components/button/button.css' in '/Users/juanca/workspace/sandbox/src/index'

Something to note:

The file that Webpack cannot resolve has an additional ./ in the reference.

I tried to debug this and stepped through the css-loader and a bunch of the PostCSS plugins it queues up. I couldn't really find _where_ the resulting ./ was added. I did find that https://github.com/css-modules/postcss-modules-extract-imports was parsing the references files though.

This is 馃樀 . Hopefully this is a correct channel to report this behavior.

I did notice that adding a ~ seems to fix it (locally) but that is _not_ documented.

All 11 comments

URL works like require, if you get Module not found: Error: Can't resolve means we can't resolve it.

design-system package should have some-package in deps, otherwise it will not works, like you work with js packages you need to put packages in dependencies, otherwise it will be error

Does the module resolution use Webpack's enhanced resolve?

In the attached codesandbox example, the host package.json is declaring @mavenlink/design-system as a dependency. The references file does exist as well: https://github.com/mavenlink/design-system/blob/master/src/components/button/button.css

I think there is something funky happening in css-loader@^2 and css-loader@^3 that keeps prefixing each composes: ... from <lib> with a leading ./.

I can try debugging more but I would need help determining which libs to step through and which modules in those libs to keep an eye on.

Does the module resolution use Webpack's enhanced resolve?

Yes

I think there is something funky happening in css-loader@^2 and css-loader@^3 that keeps prefixing each composes: ... from with a leading ./.

We don't add ./ because it is invalid

I can try debugging more but I would need help determining which libs to step through and which modules in those libs to keep an eye on.

I will look at reproducible repo tomorrow

Okay, I will continue adding more details to this.

I think it might be worthwhile to note that I am upgrading a massive codebase from css-loader@^1.0.1 to either css-loader@^2 or css-loader@^3.

More findings (new day, more coffee):

  1. For composes: ... from <dependency>, adding a ~ seems to fix the errors. e.g.
    Originally: composes: button from "@mavenlink/design-system/src/components/button/button.css"
    Now: composes: button from "~@mavenlink/design-system/src/components/button/button.css"
  2. Looks like other compose statements are throwing but for files referenced as absolute paths from the project's root.
    Originally: composes: link from 'app/assets/stylesheets/components/link/link.css'
    Now: composes: link from '~app/assets/stylesheets/components/link/link.css'
  3. Looks like a url statement is throwing a similar error.
    Originally:
    .illustration { background-image: url('frontend/features/access-groups/assets/access-groups-illustration.svg'); }

I think this means my Webpack config might be missing something to indicate the project root and node_modules as valid directories for resolving. 馃

Ideally you should avoid using ~ because it will be deprecated, anyway I will look at your repo tomorrow, if you have more problems please add them to reproducible test repo + small notes in readme and I will help

What is version do you want to use?

I am stuck on Node ^8. So I'm planning on upgrading to css-loader@^2 then css-loader@^3. I have to coordinate with my team mates on upgrading Node and that's for another day (and then I will upgrade to latest css-loader).

I just remembered something that is possibly critical. We used to configure css-loader with:

        root: `${railsRoot}/public`,

I removed the option after upgrading because it seems to no longer be a valid option. Was it replaced with something in particular?

I am stuck on Node ^8. So I'm planning on upgrading to css-loader@^2 then css-loader@^3.

No need to do it, update css-loader/style-loadr/mini-css-extract-plugin to latest versions, some versions have bugs and I will not fix them, because they are deprecated

root:${railsRoot}/public, was removed, as I said above url/import works like require/import

Okay, after getting to work with ~, it still bothered me that Webpack was just not doing the right thing. I found that in Webpack 5, there is a module.resolve.roots option which allows resolving absolute file paths from a variety of directories. That is basically the behavior I want.

But I am on Wepback 4. The way to do a similar thing is by defining a custom resolve plugin.

Here is my plugin that gets it working without a ~.

const railsRoot = path.join(__dirname, '.');

function Webpack5ResolveRootsShim (source, target) {
  this.source = source || 'resolve';
  this.target = target || 'resolve';
}

Webpack5ResolveRootsShim.prototype.apply = function(resolver) {
  var target = resolver.ensureHook(this.target);
  resolver.getHook(this.source).tapAsync('Webpack5ResolveRootsShim', function(request, resolveContext, callback) {
      const appFile = path.join(railsRoot, request.request);
      const depFile = path.join(railsRoot, 'node_modules', request.request);

      if (fs.existsSync(appFile) && !fs.lstatSync(appFile).isDirectory()) {
        var obj = Object.assign({}, request, {
          request: appFile,
        });
        return resolver.doResolve(target, obj, null, resolveContext, callback);
      } else if (fs.existsSync(depFile) && !fs.lstatSync(depFile).isDirectory()) {
        var obj = Object.assign({}, request, {
          request: depFile,
        });
        return resolver.doResolve(target, obj, null, resolveContext, callback)
      }
      callback();
  });
}

module.exports = const config = {
  context: railsRoot,

  resolve: {
    ...,
    plugins: [
      new Webpack5ResolveRootsShim(),
    ],
  },
};

Ultimately, I think it was a Webpack error. Though, I am not sure how it worked before css-loader@^2. I do have the roots defined in module.resolve.modules -- maybe it was using those?

Anyways, I am going to use the shim while I get the whole codebase set up for NodeJS > 8 and Webpack 5.

I am going to close this issue? Feel free to open it again if it's a fixable thing in the css-loader@2 or 3 and if it's even a thing you want to maintain.

I think we should not touch old css-loader versions, feel free to open new issues if you faced with problems again

Was this page helpful?
0 / 5 - 0 ratings