Import Library Stylesheets With Babel-Plugin-React-Css-Modules - reactjs

I am trying to include another library's css for their component in my own application. For reference, I am trying to use this data table library: https://github.com/filipdanic/spicy-datatable.
In the docs, it states Out of the box, spicy-datatable is bare-bones. Include this CSS starter file in your project to get the look from the demo. Edit it to suit your needs.
I tried to import the style sheet at the top of the component that I am building like this: import * as spicy from 'spicy-datatable/src/sample-styles.css'; in my own component file. It was not styled. I tried putting the raw code into my index.scss file in my assets/styles folder - did not work. I tried putting it in my own styles file ./component.scss - did not work.
I have them currently set up like:
import * as styles from './component.scss';
import * as spicy from 'spicy-datatable/src/sample-styles.css';
and am getting an error:
Module parse failed: Unexpected token (4:0)
You may need an appropriate loader to handle this file type.
webpack.config.js
const dirNode = 'node_modules';
const dirApp = path.join(__dirname, 'client');
const dirAssets = path.join(__dirname, 'assets');
/**
* Webpack Configuration
*/
module.exports = {
entry: {
vendor: ['lodash'],
bundle: path.join(dirApp, 'index')
},
resolve: {
modules: [dirNode, dirApp, dirAssets]
},
plugins: [],
module: {
rules: [
// BABEL
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules)/,
options: {
compact: true
}
},
// CSS / SASS
{
test: /\.(scss)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[path]___[name]__[local]___[hash:base64:5]'
}
},
'sass-loader'
]
},
// IMAGES
{
test: /\.(jpe?g|png|gif)$/,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
};
.babelrc
"plugins": [
[
"react-css-modules",
{
"filetypes": {
".scss": {
"syntax": "postcss-scss"
}
},
"webpackHotModuleReloading": true
}
]
I'm not sure if I need to add something to specifically handle .css files, this is my first time working with CSS Modules. I thought react-css-modules did that so I'm not quite sure why the CSS file isn't loading correctly.
Edit:
Edited my webpack around to include CSS:
{
test: /\.(css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path]___[name]__[local]___[hash:base64:5]'
}
}
]
},
Error is gone, but styles still do not appear.

Could you try changing below:
import * as spicy from 'spicy-datatable/src/sample-styles.css';
to
import from 'spicy-datatable/src/sample-styles.css';
If you are using CSS-Modules, try below:
import spicy from 'spicy-datatable/src/sample-styles.css';
and then use the style on JSX element like below:
<h1 className={classes.<className in CSS here>}>
I setup a codesandbox with the spicy-datatable library and imported the styles and looks like it applied. The styles are in "Hello.css" file and it is imported in "index.js".
https://codesandbox.io/s/4j31xj3689

If library doesn't use css-modules (uses className attribute instead of styleName) we need to disable modules for imported css, so the class names will remain unchanged. This can be done in 2 ways:
Modify your Webpack config
module: {
rules: [
{
test: /\.(css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false
}
}
]
},
...
]
}
Import library css directly into your scss stylesheet (thanks to this answer pointing out how to perform proper .css import). Make sure to exclude .css file extension from import line. :global directive will prevent css-modules to modify class names for all styles within this directive.
:global {
#import "~library-module-name/.../CssFileWithoutExtension";
}

Related

How to add classnames from less with webpack and react?

I have a webpack configuration that uses less-loader, css-loader, and style-loader. When I import less file into my component file, the css is visible in chrome's devTools, but the classname is not.
I have google'd for a couple of hours and can't seem to find anything that can explain this. I know I am suppose to use this.props.className, but I am not sure how the className gets propagated. I have also tried using static strings for the className.
Here is my webpack config:
module: {
rules: [
...
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
]
},
Here is my component:
import React from 'react';
import {AppBar} from '#material-ui/core';
import '../styles/layout.less';
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<AppBar className='header'></AppBar>
);
}
}
export default Home;
I expect the className to be header, but it doesn't show any class names i provide. It only happens with Material-UI components.
Sorry for late answer, recently had similar problem with 'antd' library and theme modification using less files. I had to split my webpack configuration into two parties (for node_modules and for resources) as follow:
{
test: /\.less$/,
include: /node_modules/,
use: [
{
loader: 'style-loader' // creates style nodes from JS strings
},
{
loader: 'css-loader', // translates CSS into CommonJs
},
{
loader: "less-loader", // compiles Less to CSS
options: {
javascriptEnabled: true
}
}
],
},
{
test: /\.less$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader' // creates style nodes from JS strings
},
{
loader: 'css-loader', // translates CSS into CommonJs
options: {
modules: true
}
},
{
loader: "less-loader", // compiles Less to CSS
options: {
javascriptEnabled: true
}
}
],
}
Hope that helps.

Make named import of Sass file available as a string

I'm building a web component in React and need to pass my compiled component styles, which are written in Sass, into the the component's <style> tag as a string.
Component code:
<MyWebComponent> is already successfully set up to make the component's shadow using ReactShadow
It's currently working if I use a compiled CSS file using this import statement:
import componentCSS from '!!css-loader!./my-component/index.css';
I need:
to use an SCSS file in that import
to do away with the inline !!css-loader! loader config syntax in the component code
import componentCSS from './my-component/index.scss';
...
render() {
return (
<MyWebComponent shadowStyles={[componentCSS]}> // componentCSS needs to be a string
<h1>Hello Web Component</h1>
</MyWebComponent>
);
}
If I console.log(componentCSS), I get an empty object {}.
the style bits of Webpack config:
...
{
test: /\.scss$/,
include: [
path.resolve(__dirname, './web-components'),
],
use: [
'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1 }
},
{
loader: 'postcss-loader',
options: { sourceMap: true }
},
{
loader: 'resolve-url-loader',
options: { sourceMap: true }
},
{
loader: 'sass-loader',
options: { sourceMap: true, outputStyle: 'compressed' }
},
{
loader: '#epegzz/sass-vars-loader',
options: {
syntax: 'scss',
files: [path.resolve(__dirname, './scss/index.scss')]
}
}
]
},
{
test: /\.css$/,
include: path.resolve(__dirname, './web-components'),
use: [
'raw-loader'
]
},
...
I've explored making a custom loader to no success, like trying to replace the import string in the component code with the working inline loader syntax:
// my-loader.js / Custom webpack loader
module.exports = function(content) {
content = content.replace(
"import componentCSS from './my-component/index.scss'",
`import componentCSS from '#epegzz/sass-vars-loader?syntax=scss&files[]=${path.resolve(__dirname, './scss/index.scss')}!sass-loader!css-loader!./my-component/index.scss'`
)
return content;
};
Before I go too far down the custom loader rabbit hole, is there a config option or syntax change that is possible to make it work?

import css from node modules react-boilerplate

I am trying to import a css file from node_modules.
My webpack config:
{
// Preprocess 3rd party .css files located in node_modules
test: /\.css$/,
include: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
}
Furthermore, I woud like to import it in custom header component, like this:
import 'react-s-alert/dist/s-alert-default.css';
But it doesn't seem to work. Does anybody know what the problem is here?

Unable to skip 3rd party library CSS from CSS-Module transformation

I am trying CSS Modules for the first time with React and Webpack and I came across at least three ways to achieve it:
css-loader
react-css-modules
babel-plugin-react-css-modules
I went with babel-plugin-react-css-modules in order to balance code simplicity and performance and everything seems to be working fine except for one thing: my 3rd party libraries (Bootstrap and Font Awesome) are also included in CSS Modules transformation.
<NavLink to="/about" styleName="navigation-button"></NavLink>
The above assigns a properly transformed className to the NavLink. However, a span inside needs to refer to global styles in order to render an icon.
<span className="fa fa-info" />
The above span is not assigned a transformed className which is expected, but my bundled stylesheet does not have these CSS classes as they are being transformed into something else, to simulate local scope.
Below is the content in my .babelrc file to activate babel-plugin-react-css-modules:
{
"presets": ["env", "react"],
"plugins": [
["react-css-modules", {
"generateScopedName": "[name]__[local]___[hash:base64:5]",
"filetypes": {
".less": {
"syntax": "postcss-less"
}
}
}]
]
}
In my Webpack configuration, below is the section to configure css-loader for transforms:
{
test: /\.(less|css)$/,
exclude: /node_modules/,
use: extractCSS.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
minimize: true,
modules: true,
sourceMap: true,
localIdentName: '[name]__[local]___[hash:base64:5]'
}
},
{
loader: 'less-loader'
}
]
})
}
As far as I have read, the above rule should exclude the library stylesheets and I also tried adding another rule specifically for the excluded stylesheets, however that did not seem to work, as I guess as those stylesheets were still transformed with the original rule.
In order to import CSS from the two libraries, I have the below two lines in my parent stylesheet that declares some global styles:
#import '../../../node_modules/bootstrap/dist/css/bootstrap.min.css';
#import '../../../node_modules/font-awesome/css/font-awesome.min.css';
I find these two approaches below might be helpful:
https://stackoverflow.com/a/52294675
https://github.com/css-modules/css-modules/pull/65#issuecomment-412050034
In short, there seems to be no options to ignore/exclude certain paths from being modularized by the css-modules webpack plugin so far. Ideally it should be supported by the plugin, but here're some approaches you can try out:
use two webpack rules to utilise the webpack rule exclusion/inclusion:
module.exports = {
rules: [
{
test: /\.css$/,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path]__[local]___[hash:base64:5]',
},
},
],
},
{
test: /\.css$/,
include: /node_modules/,
use: ['style-loader', 'css-loader']
}
]
}
...or, inject into webpack's getLocalIdent from the second answer above to manually exclude certain paths.
const getLocalIdent = require('css-loader/lib/getLocalIdent');
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]',
getLocalIdent: (loaderContext, localIdentName, localName, options) => {
return loaderContext.resourcePath.includes('semantic-ui-css') ?
localName :
getLocalIdent(loaderContext, localIdentName, localName, options);
}
}
}
For me using :global worked :
.my-component {
:global {
.external-ui-component {
padding: 16px;
// Some other styling adjustments here
}
...
}
}
Ps: for doing it with webpack config, please see another answer.
source
Updated solution from playing771
{
loader: 'css-loader',
options: {
modules: {
auto: (resourcePath) => !resourcePath.includes('node_modules'),
localIdentName: '[name]__[local]__[hash:base64:5]',
},
},
},

How can I set a CSS name not to be a hash in a Webpack configuration file?

I just wondering why my CSS name became hash after I build and run my React + Webpack application. Is there advance configuration that I may have missed to set the CSS name as normal?
This is my Webpack configuration:
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: './app/app.jsx',
output: {
path: __dirname,
filename: './public/bundle.js'
},
resolve: {
alias: {
applicationStyles: path.resolve(__dirname,'app/styles/app.css'),
Clock: path.resolve(__dirname,'app/components/Clock.jsx'),
Countdown: path.resolve(__dirname,'app/components/Countdown.jsx'),
CountdownForm: path.resolve(__dirname,'app/components/CountdownForm.jsx')
},
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
},
devtool: 'cheap-module-eval-source-map'
};
This is the CSS name that becomes hash:
To be more clear, I add the source code of how I import and use the CSS on React:
import React from 'react';
import ReactDOM from 'react-dom';
import Countdown from 'Countdown';
/* Import the CSS file */
import Styles from 'applicationStyles';
ReactDOM.render(
/* Use CSS */
<div className={Styles.box}>
<Countdown/>
</div>,
document.getElementById('app')
);
This is what Webpack does by default to avoid identical CSS classes (from different CSS modules) to collide.
Here are three things you can do:
1: At the app level, you can add the following configuration to your Webpack to disable CSS modules. It is not recommended as it could lead to collisions and hard-to-find bugs.
options: {
modules: false
}
2: At the file level, you can import it like this to prevent Webpack from obfuscating the class names. This is useful when importing third-party configuration libraries CSS files.
import '!style!css!golden-layout-css-base';
3: At the CSS class level, you can use :global(.your-class-name) to avoid obfuscating a specific class
:global(.container) {
padding: 10px;
}
In your Webpack configuration, the CSS loader needs a configuration for prefixed names. Basically localIdentName:'[local]' sets the pre-fixer as the local class name only.
For detailed info, you can look at the documentation for CSS Loader
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
localIdentName:'[local]'
}
}
]
}
]
}
The class name can be combined with an auto-generated hash using the localIdentName option of CSS Modules by setting it to [local]_[hase:base64:5].
[local] here refers to the class name.
[hash:base64:5] means generate a Base64 hash string of length 5.
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[local]_[hash:base64:5]'
}
}
]
}
By setting the css-loader modules options to an object, you're essentially setting modules to true, but with specific options.
Setting the localIdentName to [local] completely defeats the purpose of using CSS Modules.

Resources