Trying to use css-loader with webpack to minimize our css - reactjs

I've tried to go through a dozen or so different searches and read the docs on css-loader but I believe they aren't up to date.
I'm also trying to learn our react code and conventions coming from front end javascript / jquery so this is new for me.
What I want to do is take our css and minimize it using css-loader but I don't know how to do this from what I've read.
A piece of our current code looks like this - prod.config.js :
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import PurifyCSSPlugin from 'purifycss-webpack-plugin';
import baseConfig from './base.config';
const PUBLIC_PATH = '//d1yepz0pwej23y.cloudfront.net/assets/' + process.env.TRAVIS_BUILD_NUMBER + '/';
export default {
...baseConfig,
output: {...baseConfig.output, publicPath: PUBLIC_PATH },
module: {
loaders: [
...baseConfig.module.loaders, {
test: /\.(woff|woff2|eot|ttf|otf|svg)(\?v=[0-9].[0-9].[0-9])?$/,
loader: 'file?name=[sha512:hash:base64:7].[ext]',
exclude: /node_modules\/(?!font-awesome)/
}, {
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file?name=[sha512:hash:base64:7].[ext]!image?optimizationLevel=7&progressive&interlaced',
exclude: /node_modules\/(?!font-awesome)/
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss'),
exclude: /node_modules/
}
]
},
plugins: [
// extract css
new ExtractTextPlugin('[name]-[chunkhash].css'),
// set env
new webpack.DefinePlugin({
'process.env': {
BROWSER: JSON.stringify(true),
NODE_ENV: JSON.stringify('production')
}
}), ...
...baseConfig.plugins
]
};
Is this enough to give me a suggestion on how to make this work? Or suggest where i can get some more info?
The docs say that I should require the css like this:
require("css-loader?minimize!./file.css")
but I'm not sure how to implement.
Thanks!
Update:
So after trying out what #Brandon mentioned, I actually saw code in our entry already that require's the css file.
if (process.env.BROWSER) {
require('styles/app.css');
}
I updated that to:
require('css-loader?minimize!styles/app.css');
but ended up with this error:
ERROR in ./~/css-loader?minimize!./app/styles/app.css
Module build failed: CssSyntaxError: /css-loader!/Users/homeImac/workspace/node_modules/style-loader/index.js!/Users/homeImac/workspace/node_modules/css-loader/index.js?sourceMap!/Users/homeImac/workspace/node_modules/postcss-loader/index.js!/Users/homeImac/workspace/app/styles/app.css:5:1: Unknown word
but that word is #import, does this make sense? If you guys can enlighten me on why this error is appearing, I'd appreciate it.
Thanks again!

So after trying to find solutions that solve the problem with loaders, I decided to look for another solution. A plugin called Purify CSS Plugin ended up being the answer.
new PurifyCSSPlugin({
purifyOptions: { info: true, minify: true }
})
Using the loader brought on a 'unknown word' error. Everything I tried either shifted the unknown word or did nothing to change the situation. All the folks online seemed to not be able to overcome this issue either.
Just proves the fact that there is usually more than one way to fix a problem.

put that require statement in one of your app's JavaScript source files. If it is a "global" css file, then your app's main entry JS file is a good candidate.
e.g. in app.js:
require("css-loader?minimize!./file.css"); // tell webpack about your css dependency so it can load and minimize it

Related

Webpack 5 DependOn only works when the entry depends on only one dependency

To give you some context, I'm working on a .NET core app, and in one of the cshtml I'm injecting a react mini app like this:
<div id="workshop" data-id=#Model></div>
<script src="~/bundles/js/workshop-bundle.js"></script>
It was working well, but when I compile with webpack, the workshop bundle was too big (800kb) and I was receiving a warning. The workshop-bundle was including some dependencies, like axios, highcharts-react-official and highcharts/highmaps. So I tried to split the bundles.
On the webpack.config.js I'm trying to use DependOn in one of the entries. In this case, the workshop depends on axios, highcharts-react-official and highcharts/highmaps, so based on the webpack doc I tried this:
webpack.config.js
entry: {
workshop: {
import: "./wwwroot/component/WorkshopApp.tsx",
dependOn: ["axios", "highmaps", "highchartreact"],
},
highchartreact: "highcharts-react-official",
highmaps: "highcharts/highmaps",
axios: "axios",
},
.cshtml:
<div id="workshop" data-id=#Model></div>
<script src="~/bundles/js/axios-bundle.js"></script>
<script src="~/bundles/js/highchartreact-bundle.js"></script>
<script src="~/bundles/js/highmaps-bundle.js"></script>
<script src="~/bundles/js/workshop-bundle.js"></script>
It generates the 4 bundles, but the app is not displayed and I'm not getting any error.
However, if I put the dependencies in one entry, it works well:
webpack.config.js
entry: {
workshop: {
import: "./wwwroot/component/WorkshopApp.tsx",
dependOn: ["workshopVendor"],
},
workshopVendor: [
"axios",
"highcharts-react-official",
"highcharts/highmaps",
],
},
.cshtml:
<div id="workshop" data-id=#Model></div>
<script src="~/bundles/js/workshopVendor-bundle.js"></script>
<script src="~/bundles/js/workshop-bundle.js"></script>
This is not a solution, because I want the dependencies in separate bundles, any idea?
Thanks in advance!
Defining vendor bundles as separate entry points is no longer recommended. I think you're running into some dependency graph errors. Webpack should be able to fix this automatically using optimization.splitChunks. Something like this should work:
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
axios: {
test: /[\\/]node_modules[\\/]axios[\\/]/,
name: 'axios',
chunks: 'all',
},
'highcharts-react-official': {
test: /[\\/]node_modules[\\/]highcharts-react-official[\\/]/,
name: 'highcharts-react-official',
chunks: 'all',
},
'highcharts/highmaps': {
test: /[\\/]node_modules[\\/]highcharts[\\/]highmaps[\\/]/,
name: 'highcharts/highmaps',
chunks: 'all',
},
},
},
},
};
Per the docs:
Tip
In webpack version < 4 it was common to add vendors as a separate
entry point to compile it as a separate file (in combination with the
CommonsChunkPlugin).
This is discouraged in webpack 4. Instead, the
optimization.splitChunks option takes care of separating vendors and
app modules and creating a separate file. Do not create an entry for
vendors or other stuff that is not the starting point of execution.

How to move css-in-js (Styled Components) to an external css files during build using webpack - ReactJS

I am trying to figure out where the CSS files are residing when I build the react project. I am using webpack and I am able to make a single CSS file for all the styles used throughout the project if I use normal CSS. When I use CSS in js using styled component, I am not getting an external CSS file.
webpack.config.js
var path = require('path');
var hwp = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: path.join(__dirname, '/src/index.js'),
output: {
filename: 'index.js',
path: path.join(__dirname, './dist'),
publicPath : '/'
},
module:{
rules:[{
exclude: /node_modules/,
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /\.css$/i,
use: [
{
loader : MiniCssExtractPlugin.loader,
options : {
publicPath: '/public/path/to/',
},
},
'css-loader'
]
}
]
},
devServer: {
historyApiFallback: true,
},
plugins:[
new hwp({template:path.join(__dirname, '/src/index.html')}),
new MiniCssExtractPlugin({
filename : '[name].css',
chunkFilename : '[id].css',
})
]
}
Contact.js
import React from 'react'
import styled from "styled-components"
const Container = styled.div`
background-color : red;
`
function contact() {
return (
<Container>
<h1>
Welcome to Contacts page
</h1>
</Container>
)
}
export default contact
This isn't supported by styled-components at the moment. From a project member -
We don't support static CSS extraction. You can try out emotion which does.
You might not need static CSS extraction actually, because of several reasons:
There's SSR which sends only critical CSS, instead of all static CSS, for the entire page. You don't even need to do SSR, but can use snapshotting (react-snapshot) or generate a static page (gatsby, et al), which basically saves a SSR result to a html.
Static extraction doesn't generate dynamic CSS, which means your page will either appear broken until the JS executes, or you'll need to defer until the JS is loaded
Caching doesn't actually buy you an advantage, because the JS bundles will likely always change in tandem with the extracted CSS
In v3 we will add preprocessing, which will actually a bigger advantage. As part of that we might support an option for static extraction, since the core library that will bring preprocessing will probably also be integrated into emotion, which does support extraction. So it might become an option. Or not 😉
Source
Emotion has also removed static css extraction
Duplicate of How can I opt for React Styled-Components to generate a physical CSS file?

Webpack pdf file loader

This is the error:
"Build failed!
× ERROR ./media/fonts/handFont3.otf 1:4.
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
# ./index.js 3:0-37
# ../node_modules/preact-cli/lib/lib/entry.js
# multi ../node_modules/preact-cli/lib/lib/entry webpack-dev-server/client webpack/hot/dev-server"
I'm trying to import pdf into preact component to make it build so I can get the link to the page with pdf file. Here is how:
import pdfFile from '../../media/images/pdfFile.pdf'
<a href={pdfFile} target="_blank"...
It didn't work, so I Googled this two solutions to add in wepback.config.js:
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|pdf)$/,
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
]
},
};
and
{
test: /\.(pdf)$/,
use: ['url-loader']
},
I do manually make webpack.config work by this string:
--config webpack.config.js
And file is working, but result is the same. I'm still getting the error. And the same with otf fonts.
Thank you.
I figured out the solution. You can't use webpack config with preact config together.
So I removed webpack config and changed preact one to this:
export default (config, env, helpers, options) => {
const rule = {
test: /\.(otf|pdf)$/,
loader: 'file-loader'
}
config.module.rules.push(rule);
}

Adding uncompiled typescript library to Craco config

I have a "common library" written in Typescript and I want to use it in a couple more projects. I don't want to create a npm package from it, I just want to import it as "uncompiled package" but I'm having trouble setting this up.
I tried adding ts-loader and excluding the module like this:
webpackConfig.module.rules.push({
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules\/((?!common-library).)*$/,
});
However, this results in Error: TypeScript emitted no output for /path/to/index,tsx.
I also tried using craco-babel-loader https://github.com/rjerue/craco-babel-loader, however when adding it to the list of plugins:
{
plugin: rewireBabelLoader,
options: {
includes: [resolveApp('node_modules/common-library')],
excludes: [/node_modules/],
}
},
I'm getting the following:
C:\path\to\app\node_modules\#craco\craco\lib\loaders.js:34
rules.some((rule, index) => {
^
TypeError: rules.some is not a function

hot-module-loader apply cold to node_module package

I'm trying to figure out how I can apply cold() to just a single node package.
according to the docs I need to use setConfig and also apply another babel-loader to include just node_modules. However I can't for the life of me figure out where or how to implement this.
import { setConfig, cold } from 'react-hot-loader'
setConfig({
onComponentRegister: (type, name, file) =>
file.indexOf('node_modules') > 0 && cold(type),
})
I'm using Kendo UI React which does not support HMR. So I need to wrap the PanelBarItem so that react-hot-loader doesnt wrap the component. What I would like to do is apply this rule all my kendo packages so I don't have to explicitly call cold on each component when I use it.
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { Button } from '#progress/kendo-react-buttons';
import { PanelBar, PanelBarItem } from '#progress/kendo-react-layout';
import { changeLocale } from '../LanguageProvider/actions';
import { cold } from 'react-hot-loader';
class Home extends React.Component {
render() {
cold(PanelBarItem);
return (
<div>
<PanelBar title="Test">
<PanelBarItem title={'Sales Forecasts'}>
<PanelBarItem title={'Q1 sdfds'} />
<PanelBarItem title={'Q2 Forecast'} />
<PanelBarItem title={'Q3 Forecast'} />
<PanelBarItem title={'Q4 Forecast'} />
</PanelBarItem>
</PanelBar>
</div>
);
}
}
When you configure your webpack, you probably have something like this:
{
module: {
rules: [{
// testing for js/jsx files, and using babel-loader load them int webpack
test: /\.jsx?$/,
// Maybe looking only on your source folder
// (and looking aside from the node_modules as a consequence)
include: [
path.resolve(__dirname, '../src'),
],
use: [{
loader: 'babel-loader', // Ask babel to eat up those files and prep them for webpack
options: {
presets: [
// What ever babel presets you might use
],
plugins: [
// Some babel plugins you might use, like transform-runtime, or lodash
// This plugin is RHL's dude that goes over your code and marks
// things for patching later by the client
'react-hot-loader/babel',
]
},
}, {
// some other loaders for styles, images etc.
},]
}
// More webpack stuff
}
Usually, you would not apply the "js/jsx" loader to node_modules (as shown above) and this is why the babel plugin which RHL uses to patch things up never gets a chance to process the Kendo code.
What the documentation asks you to do it to add another loader that looks for js/jsx files but only applies the RHL babel plugin. like this:
{
module: {
rules: [{
// Your usual loaders including the usual babel-loader for your code
}, {
test: /\.jsx?$/,
include: [
// Focus on the folder of the module you want to "cold" later
// or go for all node_modules if you need to
path.resolve(__dirname, '../node_modules/<YOUR MODULE>'),
],
use: [{
loader: 'babel-loader',
options: {
plugins: [
// The only plugin
'react-hot-loader/babel',
]
},
}]
}
}
Then, when you configure RHL on the client (first thing you do) by calling setConfig you will also get the files from the now marked modules to apply cold on.
On your very very first file, just like they show in the documentation, require the rhlconfig.js file. The onComponentRegister should now also see files from node_modules.

Resources