MiniCssExtractPlugin is garbling and obfuscating my class names - css

I am using MiniCssExtractPlugin in my typescript and webpack project.
My webpack config for the MiniCssExtractPlugin looks like
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './src/index.tsx',
mode: "development",
output: {
path: path.resolve(__dirname, "build"),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader"
},
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: true,
importLoader: 2
}
},
"sass-loader"
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "foo.css",
chunkFilename: "[id].css"
})],
devtool: "source-map",
resolve: {
extensions: [".js", ".ts", ".tsx"]
}
}
Now the scss file in my project has this fragment
h1 {
border-bottom: 3px solid #880055;
display: inline;
}
.container {
font-size: 1.3rem;
}
.is-completed {
text-decoration: line-through;
color: #00ff00;
}
when my application is run using npm start I can see that the heading H1 has a underline of the color 880055. So this means that my scss file was read correctly.
If I go into chrome developer tools and go into network tab and look for CSS. I can see a foo.css being downloaded. If I look into the content of foo.css
It doesn't have my "is-completed" class. instead I see something like
h1 {
border-bottom: 3px solid #880055;
display: inline; }
.pxcHIyOVHeytUeG27u4TO {
font-size: 1.3rem; }
._1Z5_KVJNKd1X2P3HKM63j {
text-decoration: line-through;
color: #00ff00; }
So element classes like h1 are good, but everything else is garbled. What's going on?

When you set modules: true in your CSS config you are telling the css-loader to use CSS-Modules to scope your class names to a particular file.

You can use the localIndentName query paramater in the css-loader options to specify what you want your generated class (identifier) to look like in development and/or in prod. See example below for what solved this for me.
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]-[local]--[hash:base64:5]',
},
},
},
],
},
};
If I were to use the configuration in the example above and the name of the component that I was rendering was called HelloWorld and a class used in that component was .container, if I were to run my app (dev or prod) and inspect the element in the devtools the class on my HelloWorld component appear as follows:
<div class="HelloWorld-container--16ABh"> Hello World </div>
You can play around with what you set as your localIdentName and how many characters of the hash you show.
See the documentation for the localIdentName query param here: https://github.com/webpack-contrib/css-loader#localidentname

Related

Unable to deminify Webpack CSS output

For some reason, Webpack and associated plugins and modules refuse to output my Sass/CSS in non-minified way. I've scavanged the web for a possible solution or a simple config parameter but I can't really find anything on the matter. I deem it strange because it appears to me to be a pretty straightforward setup.
main.scss (input)
.example {
display: grid;
transition: all .5s;
user-select: none;
background: linear-gradient(to bottom, white, black);
}
styles.css (output)
.example{display:grid;transition:all .5s;user-select:none;background:linear-gradient(to bottom, white, black)}
Webpack config (simplified)
module.exports = {
mode: 'development',
optimization: {
minimize: false,
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'styles.css',
}),
],
};
postcss.config
module.exports = {
plugins: [
],
};
I solved it in the end. I had simply tried the same thing without "sassOptions" earlier.
Reconfigure your Webpack configuration as such:
{
loader: 'sass-loader',
options: {
sassOptions: {
outputStyle: 'expanded',
},
},
},

Why are imported SASS styles being purged by PurgeCSS when using camelCase?

I am trying to combine CSS-modules and PurgeCSS to get module scoping and a light CSS bundle.
I have been able to accomplish this using syntax like styles["my-class"] but would prefer styles.myClass (which works, but gets stripped out for imported SASS within SASS files).
SASS from a local file works as expected, but SASS #imported from node_modules or another file doesn't work with camelCase references.
In the below code, I will get an output like:
.Component--container--AfniF {
margin: 1em;
}
but all the imported SASS that I reference in the component as styles.myClass is purged.
But, when referenced like styles["my-class"] everything works as expected and I get output like:
.Component--my-class--6Mlw4 {
color: purple;
}
.Component--container--AfniF {
margin: 1em;
}
Any ideas?
// otherstyles.scss
.my-class {
color: purple;
}
// Component.scss
#import "otherstyles";
.container {
margin: 1em;
}
///Component.js
import React, { Fragment, useState } from "react";
import classnames from "classnames";
import styles from "./Component.scss";
export default function ({ data }) {
return <div className={classnames([styles.myClass, styles.container])}>
//webpack.config.js
...
{
test: /.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: "css-loader",
options: {
modules: true,
camelCase: true,
importLoaders: 1,
sourceMap: true,
localIdentName: "[name]--[local]--[hash:base64:5]",
},
},
{
loader: "postcss-loader",
},
{
loader: "#americanexpress/purgecss-loader",
options: {
paths: [path.join(__dirname, "src/**/*.{js,jsx}")],
},
},
{
loader: "sass-loader",
options: {
includePaths,
outputStyle: "expanded",
sourceMap: true,
},
},
],
},

Svelte: Is there a way to make global css variables in scope of svelte components?

I have set my global.css file which I import in index.js
--root {
--main-color: red;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
index.js
import "./global.css";
import App from "./App.svelte";
const app = new App({
target: document.body
});
My webpack setup
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: {
loader: "svelte-loader",
options: {
emitCss: true,
hotReload: true
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: { loader: "style-loader", options: { sourceMap: true } },
use: [
{ loader: "css-loader", options: { sourceMap: true } },
{
loader: "postcss-loader",
options: {
sourceMap: true,
ident: "postcss",
plugins: loader => [
require("postcss-import")({}),
require("postcss-preset-env")(),
require("cssnano")()
]
}
}
]
})
}
]
},
plugins: [new HtmlWebpackPlugin(), new ExtractTextPlugin("styles.css")]
};
Works perfect for setting up global css for the entire app. But I am trying to use the --main-color in my svelte components. Is there a way to inject them down to all the components' css ?
Since I import global.css first, it should work as it emits a file with --root{} first then rest of the component styles.
You can place global styles under /routes/index.svelte file, like the example below:
<style>
:global(:root){
--header-color: purple
}
</style>
And simply use it anywhere like normally how you use CSS variables like so:
h1 {
color: var(--header-color);
}
I was busy with this, trying different webpack settings etc., seeing that the output css should work, I just could not find why it did not work. I wrote the post before trying for one last time, which wasted another hour. I finally found the error.
Instead of using :root{} I have mistyped it --root{}. I have posted it anyways, in case someone is stuck with the same mistake.

React not reflecting style changes

I'm trying to set up a very basic css configuration for my react project. I'm using webpack and style loaders, like so:
// webpack.config.js
const {resolve} = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const port = process.env.PORT || 3000;
module.exports = {
entry: "./src/js/index.js",
output: {
filename: "bundle.[hash].js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}, {
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, {
loader: "css-loader",
options: {
modules: true,
camelCase: true,
sourceMap: true
}
}, {
loader: "sass-loader",
options: {
sourceMap: true,
precision: 8,
data: "$ENV: " + "PRODUCTION" + ";"
}
}
]
}
]
},
devServer: {
host: 'localhost',
port: port
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
template: resolve("public", "index.html"),
favicon: resolve("public", "favicon.ico")
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
]
};
My problem is that changes in my css file aren't reflected in the html that my react components return.
So if I have a component like:
import React from 'react'
require('../../styles/style.scss')
const App = () => (<div className="root">
<div id='banner1' className='banner'>
<h1>foo</h1>
<h2>bar</h2>
</div>
</div>)
export default App
... and an scss file like:
#banner1 {
height: 100vh;
background: blue;
width: 100%;
}
h1 {
font-size: 30px;
color: white;
}
... my styles will show up initially, but any changes while the server is still running won't be reflected if I refresh the page. It will only reflect the changes in my stylesheet when I restart the server.
My suspicion is that the mini-css-extract-plugin package is minifying the css and packing it into a bundle that react doesn't see in the development environment whenever it's changed, and it doesn't get rebundled.
If I'm right, my conflict is that this is the ubiquitous way I've read in tutorials to set up your webpack configuration, and there is literally zero mention of this side effect being present in a dev environment. Is there an alternate configuration I should be specifying for a dev environment? Is there something I'm missing?
Try using classname={styles.banner} in case of scss.

CSS Modules composes not working

I'm trying to setup css modules with postcss + cssnext. It all seems to be working fine, except that the composes keyword is simply not working. The rule vanishes when the bundle is compiled.
Here's my webpack config file:
'use strict'
const path = require('path')
const webpack = require('webpack')
const HtmlPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
path.join(__dirname, 'src', 'index')
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]-[hash].js',
publicPath: ''
},
plugins: [
// new DashboardPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new HtmlPlugin({
title: 'Github App',
template: path.join(__dirname, 'src', 'html', 'template-dev.html')
})
],
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
include: /src/,
use: 'babel-loader'
}, {
test: /\.css$/,
exclude: /node_modules/,
include: /src/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]--[hash:base64:5]'
}
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss'
}
}]
}]
},
resolve: {
alias: {
Src: path.join(__dirname, 'src'),
Components: path.join(__dirname, 'src', 'components')
}
}
}
I'm using style-loader for this dev environment so I can use hot reloading. The css file is being imported like this: import './app.css'
app.css:
:global{
.app {
float: left;
padding: 10px;
width: 100%;
}
}
.className {
color: green;
background: red;
}
.otherClassName{
composes: className;
color: yellow;
}
this results in:
My postcss.config.js file:
module.exports = {
plugins: {
'postcss-import': {},
'postcss-cssnext': {
browsers: ['last 2 versions', '> 5%']
},
'postcss-nested': {}
}
}
Am I missing something to get composes to work?
Looks like this is fine:
The implementation of webpack's css_loader is to add both classes when exporting the styles (see https://github.com/webpack-contrib/css-loader#composing)
This also makes more sense, since it will ultimately render out less CSS code.
Try importing the styles and apply them to an HTML node and you will see it should receive both classes.
In your example it would have done something like:
exports.locals = {
className: 'className-2yqlI',
otherClassName: 'className-2yqlI otherClassName-1qAvb'
}
So when you do:
import styles from '../app.css'
// ...
<div className={styles.otherClassName} />
The div gets both classes.

Resources