Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot @import scss #351

Closed
orlin opened this issue Feb 23, 2017 · 20 comments
Closed

Cannot @import scss #351

orlin opened this issue Feb 23, 2017 · 20 comments

Comments

@orlin
Copy link

orlin commented Feb 23, 2017

I really want to use your library, especially with Sass, however I can’t @include the .scss styles with my current configuration. That is using Webpack 2 and sass-loader. Here is an example project. Here is a comment describing the issue - basically I can’t import the .scss (of button in this case) neither with relative paths nor with the ~ alias to node_modules that node-sass / sass-loader supports. The problem in this case is @import "@material/animation/variables”;. I expect there would be many other similar imports in the library… Do you have / know of a working Webpack 2 example?

I thought Eyeglass would help, but I can’t set that up with Webpack 2. Here is an eyeglass issue and my comment on it that has a broken version of my config trying to tell sass-loader to use eyeglass for importing.

So I'm trying this with material-components-web@0.4.0 and more specifically @material/button@0.2.1 importing from its dependency @material/animation@0.1.3. It's also worth noting that I'm using yarn which keeps a flat directory structure of my dependencies. I wonder if you assume a regular npm install and thus hierarchical dependencies?

Here is the example error:

 error  in ./pages/index.js.scss

Module build failed:
@import "@material/animation/variables";
^
      File to import not found or unreadable: @material/animation/variables.
Parent style sheet: /Users/om/Dev/mel/drmelgill.com/node_modules/@material/button/mdc-button.scss
      in /Users/om/Dev/mel/drmelgill.com/node_modules/@material/button/mdc-button.scss (line 17, column 1)

 @ ./pages?entry 43:15-41
 @ multi ./~/next/dist/client/webpack-hot-middleware-client ./pages?entry

Either of these two would cause it, and mdc-button.scss is found in both cases:

  • @import '../node_modules/@material/button/mdc-button.scss’; // valid relative path
  • @import '~@material/button/mdc-button.scss’; // ~ = node_modules

So I'm not really sure if this is about configuration help, documentation example, bug report, or a feature request...

@Garbee
Copy link
Contributor

Garbee commented Feb 23, 2017

You need to make sure you include the path to the material modules in your sass-loader config.

Give that a shot and let us know if it starts working. If not, then providing a copy of your webpack config would probably help tracking things down.

@orlin
Copy link
Author

orlin commented Feb 23, 2017

Thank you, @Garbee! Yes, setting the includePaths did it. Well, at least I'm able to include the styles, which is all I need to start working. I feel kind of silly for either not spotting the "NOTE: The components' Sass files expect that the node_modules directory containing the @material scope folder is present on the Sass include path." or for not realizing what that means. I'll leave this issue open in case anyone wants a reminder that the #readme could be more specific. The example though may differ between straight npm vs yarn and also depending on whether people install material-components-web or its individual modules in the case on npm, I think...

For the record, here is what sass-loader needed in my case:

          { loader: 'sass-loader',
            options: {
              includePaths: glob.sync('node_modules').map((d) => path.join(__dirname, d))
            }
          }

I guess I could be more specific, adding just the @material modules, plus a fixed list of other sass modules. Alternatively, I'm actually tempted to add node_modules/@material/*/ to the paths as well. This way instead of @import '@material/button/mdc-button'; I could just @import 'mdc-button'; - wouldn't that be nice? I wonder if having many include paths affects performance - my guess would be that it doesn't. Any advice about that?

@orlin
Copy link
Author

orlin commented Feb 24, 2017

Here is an improved config:

          { loader: 'sass-loader',
            options: {
              includePaths: ['node_modules', 'node_modules/@material/*']
                .map((d) => path.join(__dirname, d))
                .map((g) => glob.sync(g))
                .reduce((a, c) => a.concat(c), [])
            }
          }

With it one can just @import 'mdc-anything', using only the (.scss) filename.

@traviskaufman
Copy link
Contributor

@orlin closing this issue as it looks like we've addressed the root problem.

While the approach you outlined makes sense, the reason we don't take it is because it won't work when we publish the packages as individual modules unless consumers also follow this paradigm, which is not something we want to enforce onto them.

@Kikobeats
Copy link
Contributor

I did this and load the file but throw an syntax error:

./~/css-loader!./~/sass-loader/lib/loader.js?{"includePaths":["/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/node_modules","/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/~/@material/animation","/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/~/@material/card","/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/~/@material/elevation","/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/~/@material/theme","/Users/josefranciscoverdugambin/Projects/windtoday/windtoday-marketplace/~/@material/typography"]}!./~/postcss-loader!./~/@material/card/mdc-card.scss
Module build failed: Unknown word (94:12)

  92 | 
  93 |     @each $mult, $name in (1.5: "1dot5", 2: "2", 3: "3") {
> 94 |       &--#{$name}x {
     |            ^
  95 |         width: auto;
  96 |         height: $mult * 80px;

 @ ./~/@material/card/mdc-card.scss 4:14-140 13:2-17:4 14:20-146
 @ ./src/app/Card/index.js
 @ ./src/app/Hit/index.js
 @ ./src/app/Hits/index.js
 @ ./src/app/Results/index.js
 @ ./src/app/Main/index.js
 @ ./src/app/App/index.js
 @ ./src/app/index.js
 @ multi react-hot-loader/patch webpack-dev-server/client?http://0.0.0.0:3000 webpack/hot/only-dev-server ./src/app/index.js

any idea?

@Elemecca
Copy link
Contributor

Elemecca commented May 3, 2017

This sass-loader configuration will work even if your dependency on the components is transitive. It adds all node_modules directories that are descendants of the directory containing the Webpack configuration and have an @material subdirectory to the Sass include path.

const path = require('path');
const glob = require('glob');

{ loader: 'sass-loader',
  options: {
    sourceMap: true,

    // mdc-web doesn't use sass-loader's normal syntax for imports
    // across modules, so we add all module directories containing
    // mdc-web components to the Sass include path
    // https://github.com/material-components/material-components-web/issues/351
    includePaths: glob.sync(
      path.join(__dirname, '**/node_modules/@material')
    ).map((dir) => path.dirname(dir)),
  },
},

@mischkl
Copy link

mischkl commented Jul 20, 2017

As mentioned in #981 this unfortunately is no help to @angular/cli users, who can't configure the includePaths dynamically.

(It's kind of impressive to see firsthand how uncoordinated the various development teams at Google are with each other.)

The crux of the problem seems to be twofold: first of all, node-sass itself doesn't support scoped NPM packages. Second of all, sass-loader for webpack doesn't support package imports that don't start with tilde (~) - and users of @angular/cli can't use @Elemecca's workaround, either. Only eyeglass appears to support material-components as-is, but I would guess the user base of eyeglass is significantly smaller than that of node-sass and @angular/cli combined.

IMHO both of these problems (node-sass not processing scoped packages as well as sass-loader in @angular-cli not processing packages without a ~) need to be fixed in order for the current state of material-components to be considered acceptable for general consumption. Thus, it would be great if the material-components maintainers could get involved.

Here are relevant existing issues:
sass/node-sass#1596
webpack-contrib/sass-loader#466

@evil-shrike
Copy link

For those who uses Grunt here're some tips.
The idea is the same - specify additional folders to lookup for the compiler.

There're a couple of grunt plugins for SASS: grunt-contrib-sass and grunt-sass. The former uses Ruby-based SASS compiler the latter C-base LibSass (node-sass is a wrapper about libsass).
Both plugins have options for overriding lookup folders:

  • grunt-contrib-sass - loadPath:
    'sass' : {
        dev: {
           options: {
             loadPath: ['./node_modules']
           }
        }
    }
  • grunt-sass - includePaths:
    'sass' : {
        dev: {
           options: {
                includePaths: ['./node_modules']
           }
        }
    }

BUT Ruby-based sass failed to compile material's SCSS-es in my case. So I choose libsas (grunt-sass) which works fine.

@wzup
Copy link

wzup commented Jul 31, 2017

@orlin

What is glob.sync? Throws error

ReferenceError: glob is not defined

@Elemecca
Copy link
Contributor

@wzup glob is a module. You need to import it:
var glob = require('glob');

@wzup
Copy link

wzup commented Jul 31, 2017

@Elemecca

Thanks.
The first part appeared to be enough.

['node_modules', 'node_modules/@material/*'] .map((d) => path.join(__dirname, d))

@eromoe
Copy link

eromoe commented Aug 10, 2017

@orlin

How to use your config in webpack ?

My config:

const path = require('path');
const webpack = require('webpack');

module.exports = (options) => ({
  entry: options.entry,
  output: Object.assign({ // Compile into js/build.js
    path: path.resolve(process.cwd(), 'build'),
    publicPath: '/',
  }, options.output), // Merge with env dependent settings
  module: {
    loaders: [{
      test: /\.js$/, // Transform all .js files required somewhere with Babel
      loader: 'babel-loader',
      exclude: [/node_modules/, /styles/],
      query: options.babelQuery,
    }, {
      // Do not transform vendor's CSS with CSS-modules
      // The point is that they remain in global scope.
      // Since we require these CSS files in our JS or CSS files,
      // they will be a part of our compilation either way.
      // So, no need for ExtractTextPlugin here.
      test: /\.css$/,
      loaders: ['style-loader', 'css-loader'],
    }, {
      test: /\.scss$/,
      loader: 'style-loader!css-loader!sass-loader!resolve-url-loader!sass-loader?sourceMap',
    }, {
      test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      loader: 'url-loader?limit=10000&mimetype=application/font-woff',
    }, {
      test: /\.(eot|svg|ttf|woff|woff2)$/,
      loader: 'file-loader',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        'file-loader',
        {
          loader: 'image-webpack-loader',
          query: {
            progressive: true,
            optimizationLevel: 7,
            interlaced: false,
            pngquant: {
              quality: '65-90',
              speed: 4,
            },
          },
        },
      ],
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.json$/,
      loader: 'json-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader',
      query: {
        limit: 10000,
      },
    }],
  },
  plugins: options.plugins.concat([
    new webpack.ProvidePlugin({
      // make fetch available
      fetch: 'exports-loader?self.fetch!whatwg-fetch',
    }),

    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
    // inside your code for any environment checks; UglifyJS will automatically
    // drop any unreachable code.
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV),
        API_URL: JSON.stringify(process.env.API_URL),
      },
    }),
    new webpack.NamedModulesPlugin(),
  ]),
  resolve: {
    modules: ['app', 'node_modules'],
    extensions: [
      '.js',
      '.jsx',
      '.react.js',
    ],
    mainFields: [
      'browser',
      'jsnext:main',
      'main',
    ],
  },
  devtool: options.devtool,
  target: 'web', // Make web variables accessible to webpack, e.g. window
  performance: options.performance || {},
});

@thiagovilla
Copy link

How do I implement this with parcel? :(

@thasmo
Copy link

thasmo commented Oct 25, 2018

Why aren't SCSS files just referenced by relative path? That would just solve a lot of problems and moves the responsibility of how to import the SCSS files back to where it should be imo; to the project itself and not to the projects using it. My 2 cents.

@iddan
Copy link

iddan commented Oct 28, 2018

My temporary solution:
Add to your package.json:

{
    ...
+    "postinstall": "grep -rl node_modules/@material/ -e '@import \"@material' | xargs sed -i \"\" 's/@material/~@material/g'"
}

@Garbee
Copy link
Contributor

Garbee commented Oct 28, 2018

Why aren't SCSS files just referenced by relative path?

Because with Lerna to do a monorepo setup and split into subpackages, once the packages are split the relative paths would break. Hence, importing by the package name and not path structure.

@FilippoVigani
Copy link

Not sure why this was closed, as I am still unable to use MDC web with Angular CLI.

@thasmo
Copy link

thasmo commented Jan 26, 2019

Would it be possible to use ~ (tilde) by default for @import statements in the material-components/material-components-web project? So it will automatically work with sass-loader, node-sass-magic-importer, node-sass-package-importer, etc.

@import "~@material/textfield/mdc-text-field";

@floyd-may
Copy link

I agree with @VeegaP, getting sass compilation to work with webpack (and others, apparently) in a consistent way is still very much not obvious or turn-key. @importing MDC scss files is not a closed issue.

@elmarsan
Copy link

@thasmo your solution was well for me, but it's apper that sass is not be able to charge the animations.

That is my current code:

@import "~@material/checkbox/mdc-checkbox";
@import "~@material/animation/functions";

Is any of you having that the samme issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests