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 [email protected] and more specifically @material/[email protected] importing from its dependency @material/[email protected]. 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_modulesSo I'm not really sure if this is about configuration help, documentation example, bug report, or a feature request...
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.
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?
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.
@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.
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?
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)),
},
},
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:
https://github.com/sass/node-sass/issues/1596
https://github.com/webpack-contrib/sass-loader/issues/466
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:
loadPath: 'sass' : {
dev: {
options: {
loadPath: ['./node_modules']
}
}
}
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.
@orlin
What is glob.sync? Throws error
ReferenceError: glob is not defined
@wzup glob is a module. You need to import it:
var glob = require('glob');
@Elemecca
Thanks.
The first part appeared to be enough.
['node_modules', 'node_modules/@material/*'] .map((d) => path.join(__dirname, d))
@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 || {},
});
How do I implement this with parcel? :(
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.
My temporary solution:
Add to your package.json:
{
...
+ "postinstall": "grep -rl node_modules/@material/ -e '@import \"@material' | xargs sed -i \"\" 's/@material/~@material/g'"
}
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.
Not sure why this was closed, as I am still unable to use MDC web with Angular CLI.
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";
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.
@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?
Most helpful comment
Would it be possible to use
~(tilde) by default for@importstatements in thematerial-components/material-components-webproject? So it will automatically work withsass-loader,node-sass-magic-importer,node-sass-package-importer, etc.