Css-loader: Is there a proper way to import vendor styles from a CSS module?

Created on 6 Apr 2016  路  15Comments  路  Source: webpack-contrib/css-loader

Are any of the following intended to work?

@import "leaflet/dist/leaflet.css";
    // throws `Error: Cannot resolve module 'images/layers.png'`

:global { @import "leaflet/dist/leaflet.css"; }
    // throws `Error: Cannot resolve module 'images/layers.png'`

:global { @import '../../node_modules/leaflet/dist/leaflet.css'; }
    // throws `Error: Cannot resolve module 'images/layers.png'`

:global { @import '!style!css?-module!leaflet/dist/leaflet.css'; }
    // throws `CSSSyntaxError
    //   @ ./~/css-loader?module&localIdentName=[name]__[local]!./~/less-loader!./app/components/MapWidget.less 3:10-138`

Background

I'm trying to load the vendor CSS from Leaflet and running into errors because it does relative imports for image assets. What I'd like to have happen is the CSS is loaded and everything in it treated as a global.

I've been scouring the web for a few hours now and couldn't find an answer to this.

Temporary Workaround

I've disabled modules on plain .css files and added import 'leaflet/dist/leaflet.css'; to the top of the .jsx file. Is this the happy path? It's certainly the path of least resistance, but it feels odd to have this thing dangling outside of CSS.

My guess as to what's happening

My guess is the CSS loader is preempting the LESS loader and doesn't recognize the :global {} nesting.

Relevant parts of webpack.config.js:

module.exports = {
  context: path.join(__dirname, 'app'),
  entry: './index.js',
  devtool: 'source-map',

  resolve: {
    extensions: ['', '.js', '.jsx']
  },

  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },

  module: {
    preLoaders: [
      {test: /\.jsx?$/, loader: 'eslint', exclude: /node_modules/}
    ],
    loaders: [
      {test: /\.jsx?$/, loader: 'babel', exclude: /node_modules/},
      {test: /\.css$/, loader: 'style!css'},
      {test: /\.less$/, loader: 'style!css?module&localIdentName=[name]__[local]!less'},
      {test: /\.(png|jpg|gif)$/, loader: 'file'}
    ]
  },

  plugins: [
    new webpack.EnvironmentPlugin(['NODE_ENV']),
    new HtmlWebpackPlugin({
      title: `${pkg.name} v${pkg.version}`,
      hash: true,
      xhtml: true
    }),

    // Polyfill just in case -- unsure what cutoff is for "modern browsers"
    new webpack.ProvidePlugin({fetch:'isomorphic-fetch'})
  ]
};
5 (nice to have) Minor 4 (inconvenient) Feature

Most helpful comment

I _think_ one of your first 2 options is the way to go. Going off less-loader readme adding a ~ to the import statement should make it look in node modules directory. You will want to use less-loader for this though (both css/less files processed by less-loader in webpack.config)

then try (maybe)

@import "~leaflet/dist/leaflet.css";

I think imports remain unchanged, css-modules uses composes for transforming imports. May be way off though

All 15 comments

I _think_ one of your first 2 options is the way to go. Going off less-loader readme adding a ~ to the import statement should make it look in node modules directory. You will want to use less-loader for this though (both css/less files processed by less-loader in webpack.config)

then try (maybe)

@import "~leaflet/dist/leaflet.css";

I think imports remain unchanged, css-modules uses composes for transforming imports. May be way off though

@dbazile any luck so far? I'm still struggling with this. I don't really want to add less-loader since I'm only dealing with plain css in my project.

For added context, here's what I did so far:

Added to webpack.config.js

  resolve: {
    extensions: ['', '.html', '.js', '.json', '.scss', '.css'],
    alias: {
      leaflet_css: __dirname + "/node_modules/leaflet/dist/leaflet.css"
    }
  }

I also add images via url-loader

{ test: /\.(png|jpg|jpeg|gif|woff)$/, loader: 'url-loader?limit=8192' },

And then imported it together with leaflet and react-leaflet

import 'leaflet'
import 'leaflet_css'
import { Map, TileLayer, LayersControl, ScaleControl, Marker, Popup } from 'react-leaflet'

The errors I'm seeing are

ERROR in ./~/css-loader!./~/leaflet/dist/leaflet.css
Module not found: Error: Cannot resolve directory './images' in /Users/ullrich/projects/cruisejs/node_modules/leaflet/dist
 @ ./~/css-loader!./~/leaflet/dist/leaflet.css 6:9471-9491

ERROR in ./~/css-loader!./~/leaflet/dist/leaflet.css
Module not found: Error: Cannot resolve module 'url-loader' in /Users/ullrich/projects/cruisejs/node_modules/leaflet/dist
 @ ./~/css-loader!./~/leaflet/dist/leaflet.css 6:8253-8283

ERROR in ./~/css-loader!./~/leaflet/dist/leaflet.css
Module not found: Error: Cannot resolve module 'url-loader' in /Users/ullrich/projects/cruisejs/node_modules/leaflet/dist
 @ ./~/css-loader!./~/leaflet/dist/leaflet.css 6:8417-8450

The './images' directory error is caused by this line:
https://github.com/Leaflet/Leaflet/blob/v1.0.0/dist/leaflet.css#L379

For convenience, this is the commit that added this line:
https://github.com/Leaflet/Leaflet/blob/v1.0.0/dist/leaflet.css#L379

Theres an issue open under leaflet about this too:
https://github.com/Leaflet/Leaflet/issues/4849

@stigi: Haven't tried in a while. My temporary workaround has since evolved into a permanent workaround; I just require the vendor CSS in the .JSX file which sidesteps the module scoping.

Haven't tried in a while. My temporary workaround has since evolved into a permanent workaround

Same, but it would be nice if :global @import could work tho right?

@gnarf: That syntax looks like it might lead down a dark path. :D

IMO, it should work such that the idiomatic Webpack way works, i.e., @import "!style!css!~leaflet/dist/leaflet.css"; avoids modularizing the imported style sheet. It may well be the case now; I don't have to same configuration anymore so I can't easily check.

Adding css?url=false to your CSS loader's config gets rid of the issue (with the side effect of disabling any URL parsing in your CSS, which in my case isn't a problem).

Another potential solution is to use css-raw-loader only on leaflet.css to import it without running it through your CSS loader.

I have the same problem...

ERROR in ./~/css-loader?{"localIdentName":"[path]---[name]---[local]","modules":false,"importLoaders":1,"sourceMap":true,"minimize":false,"discardComments":{"removeAll":true}}!./~/postcss-loader?{"sourceMap":true}!./~/sass-loader?{"sourceMap":true}!./src/components/Map/styles.global.scss
Module not found: Error: Can't resolve './images/layers-2x.png' in '/Users/user/Desktop/Dev/project/src/components/Map'
 @ ./~/css-loader?{"localIdentName":"[path]---[name]---[local]","modules":false,"importLoaders":1,"sourceMap":true,"minimize":false,"discardComments":{"removeAll":true}}!./~/postcss-loader?{"sourceMap":true}!./~/sass-loader?{"sourceMap":true}!./src/components/Map/styles.global.scss 6:7558-7591
Can't resolve './images/layers-2x.png'

The only way to easily fix it is by disabling url only for leaflet like @sunny-g said.
It's not a big deal in this case because leaflet has API that allows for defining urls for images
https://github.com/PaulLeCam/react-leaflet/issues/255#issuecomment-261904061 but a lot of other packages don't expose such API.

Two weeks ago the same problem was reported on official css-modules repo -
https://github.com/css-modules/css-modules/issues/205

Could someone explain what is the expected behaviour here? It seems a bit strange for me that scss / css files in node_modules cannot reference any images with relative paths...

Solved: Had similar problem needed to to import bootstrap template into css from scss see below:
@import "vendors/_bootstrap.scss";
body{
padding-bottom:50px;
}
The folder vendors has to be in the same directory as the scss file for it to work

@Origslammer1 while that solution works, what happens when bootstrap updates? The real solution needs to address installed node modules.

Sorry for delay! PR welcome!

Not related to css-loader please create issue with feature request in https://github.com/css-modules/postcss-modules, thanks!

Was this page helpful?
0 / 5 - 0 ratings