Minify and add hash to css files webpack - css

I am using webpack for my Angular 2 project.
Inside the src folder I have a global css folder and component and other folders.
My webpack.config.js is outside the src folder.
I am using CopyWebpackPlugin to copy the css folder to the dist folder :
new CopyWebpackPlugin([
{ from: 'src/css', to: 'css'}
]),
I am using the following loader also for css :
exports.css = {
test: /\.css$/,
loader: 'to-string!css?-minimize!postcss',
};
But the deal is that I want to add a hash to each css file name and also then change the css file name in the index.html since these are global files included in the index.html. What is the best way to achieve this?
EDIT : While changing the code i realised that the loader property only applies to the css inside the components folder and not to the outside folder. why is this?

Use https://github.com/webpack/extract-text-webpack-plugin.
example in webpack.config.js
config.plugins.push(
new ExtractTextPlugin({filename: 'css/[name].[hash].css'})
);
...
config.module = {
rules: [
{
test: /\.css$/,
exclude: root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css', 'postcss']})
}
]
}

Do not use CopyWebpackPlugin for application sources. This will bypass Webpack's loaders and lock you out of all of Webpack's features.
Simply use ES6 imports, require, require.ensure or System.import to require your stylesheets. Alterantively, as MichaƂ suggested, use ExtractTextPlugin in production when applicable.

Related

Importing css file to specific component react app

I am trying to import css to my specific component of react app.
webpack config:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader',
}),
}
but css is not applied.
I also included the main css inside index.html. Is it the reason why I cannot apply another css file?
<link rel="stylesheet" href="../style/style.css">
Can you suggest me what's missing?
It depends on the webpack version you're using. For example, if you're using Webpack 4, then your development config would be:
{
test: /\.s?css$/, // test for scss or css files
use: [
'style-loader', // try to use style-loader or...
{
loader: 'css-loader', // try to use css-loader
options: {
sourceMap: true, // allow source maps (allows css debugging)
modules: true, // allow css module imports
camelCase: true, // allow camel case imports
localIdentName: '[local]___[hash:base64:5]', // set imported classNames with a original className and a hashed string in the DOM, for example: "exampleClassName__2fMQK"
},
},
],
}
example.css (must use camel case instead of snake case)
.exampleClassName {
text-align: center;
}
example.js
import React from 'react';
import { exampleClassName } from './example.css';
export default () => (
<h1 className={exampleClassName}>I am centered!</h1>
)
For production, you'll want to use OptimizeCSSAssetsPlugin and MiniCssExtractPlugin :
minimizer: [
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
map: {
inline: false,
annotation: true
}
}
}),
],
{
plugins: [
new MiniCssExtractPlugin({
filename: `css/[name].[contenthash:8].css`,
chunkFilename: `[id].[contenthash:8].css`,
}),
]
}
When you run webpack to build your application for production, it'll compile the css and (when the webpack config is set up properly) will generate an index.html that automatically adds a link to the compiled stylesheet.
Webpack is a steep learning curve and there's a lot of missing options from the above examples, so if you're just trying to get it up and running, then I have a Webpack-React-Boilerplate that has (s)css modules imports and a lot more already configured for you. I've included notes within the webpack config files to help assist as to what each option is doing.
Otherwise, if you're trying to learn older versions of webpack, then you can eject the create-react-app and reverse engineer/look at their extensive webpack notes.

webpack: understanding source maps when working with CSS

Introduction
I have already setup bundling for my Javascript files with webpack in my project. Now I am in the process of adding CSS files to the webpack configuration. So far, I have been including the CSS files manually in the HTML header by adding <link> elements for every CSS file I depend on (e.g. bootstrap, my own css, etc.). Obviously this is not very elegant and using webpack would be much better, so I would like to replace the link elements and bundle them via webpack.
This should be easy, everything is pretty much documented in the webpack documentation. After reading the documentation and experimenting a bit with webpack I have arrived at the configuration below which already works.
Problem
The problem with my current setup is that I would like to have proper source map support and that does not seem to work. By proper, I mean that I expect that when I run a development build with webpack and I inspect some element in Chrome DevTools, that I will see from which file and which line in the file a certain CSS class originated and that I can click on the CSS rules and the browser jumps to that file.
I do not want to have inline styles in the head element, because then the browser will show something like .foobar { <style>..</style>, rather then .foobar { app.css:154.
With my current setup I have all CSS files combined (but not minified) into one app.css file. This means that if I inspect a bootstrap class such as .btn then it appears as .btn { app.css:3003. However, what I want to achieve is that the browser shows it as .btn { bootstrap.css:3003.
So now I am trying to understand how webpack and the different plugins such as css-loader and min-css-extract-plugin apply CSS source maps, and how I can configure them to achieve a proper debugging experience.
I am not sure how relevant this is, but when I navigate in DevTools under Sources to webpack://./bootstrap/dist/css/bootstrap.css I see that it only contains a single line:
// extracted by mini-css-extract-plugin.
Webpack Setup
index.js:
window.jQuery = require('jquery/dist/jquery');
require('bootstrap/dist/css/bootstrap.css');
require('bootstrap/dist/js/bootstrap');
/* other dependencies */
webpack.config.js:
const devMode = process.env.NODE_ENV !== 'production';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{ /* Javascript rules excluded */ },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
plugins: [
new UglifyJSPlugin (),
new HtmlWebpackPlugin({
template: 'app/index.tpl.html'
}),
new MiniCssExtractPlugin({ filename: devMode ?
'[name].css' :
'[name].[hash].css'
})
],
Conclusion
It seems I just passed the rubber duck test. While I was writing this I arrived at a solution. I will still publish the question, maybe it can help others.
The problem was that I was also using the mini-css-extract-plugin for development and not just for production. I thought that I needed to do that, because when at first I was using the style-loaded I would get styles included in the header and the browser would show me all styles as .foobar { <style>..</style>.
However, the actual problem seemed to be, that I was not using devtools. So the solution was to add devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map', to the webpack configuration to conditionally use the style-loader plugin during development builds and mini-css-extract-plugin during production builds.
webpack.config.js
{
test: /\.css$/,
use: [
{
- loader: MiniCssExtractPlugin.loader,
+ loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
/* ... */
+ devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map',

ExtractTextWebpackPlugin always outputs file, even if there are no changes

I have a project that bundles the client-side files using Webpack. We are bundling the CSS using the ExtractTextWebpackPlugin. The problem is when I edit a javascript file the CSS bundle always gets re-built despite there being absolutely no changes to the CSS state.
How can I bundle CSS but only when there are changes to the CSS files?
Extracted from my webpack config:
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: 'css-loader'
})
},
...
plugins: [
new ExtractTextPlugin(isDebug ? '[name].css' : '[name].[chunkhash].css')
],
whenever ExtractTextPlugin sees a import statement with css extension it will automatically extract the text contant of that css file whethere it changes or not
if you are using it to debug then use style-loader and HMR(Hot Module Replacement) for better experence something like this
isDebug
? {
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
: {
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: "css-loader"
})
}
if you want to use current configration and don't want ExtractTextPlugin to build css file and you are importing them in your javascript file using import then somehow you have to remove import statement for css files when there is no change in that css file
and if you are adding css file in your webpack config entry section then it would be easy because webpack allow custom command argument and you can do this by exporting a function in your webpack config file insted of object
//webpack.config.js
module.exports = function(env) {
return {
entry: {
main: env.includeCss
?
["./src/index.js", "./src/main.css"] //build with css
: ["./src/index.js"] //build without css
},
.
.
.
.
}
}
you can pass env.includeCss by command argument like this
webpack --config ./webpack.config.prod.js --env.includeCss
//notice --env.includeCss witch will set env.includeCss as true
run with --env.includeCss normally and without --env.includeCss when you dont want to compile css file
Not directly related to your question but I noticed you use [chunkhash] for extracted CSS. This will make your css name change even if you haven't change content in you CSS files.
Use [contenthash] instead.
https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/#setting-up-hashing
If you are using ExtractTextPlugin, you should use [contenthash]. This way the generated assets get invalidated only if their content changes.

Webpack ExtractTextPlugin without babel

I'm trying to get webpack to bundle all my stylesheets but I'm at a loss.
I don't want to use babel, so I try to do this in my main.js file:
require('./styles/core.css');
When I run webpack i get the following error:
You may need an appropriate loader to handle this file type.
My loader is:
loaders: [
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract('css!scss'),
include: __dirname + '/app/styles'
}
]
Any ideas?
You have loader only for .scss and you are trying to use it on .css file. Change test in loader to:
test: /\.(css|scss)$/,

extract-text-webpack-plugin to output css from scss in new folder in output

I can't seem to make extract-text-webpack-plugin generate the css from scss in a special folder in output. I know that there is a publicPath option but it doesn't seem to do anything. The css is still generated along with the other css's in my output folder.
can anyone help?
in my config:
test: /(add-to-home\.scss)$/,
loader: ExtractTextPlugin.extract("style","css!postcss!sass", {publicPath: "output/a/b"})
thanks
To output the your style.css file in "output/a/b"
You need to use the ExtractTextPlugin
{
test: /(\.scss|\.css)$/,
loader: ExtractTextPlugin.extract('style', 'css?postcss!sass'),
}
In the plugin section use :
plugins: [
new ExtractTextPlugin('output/a/b/style.css', {allChunks: true})
],
This will output the extracted css in the output/a/b/ folder.

Resources