How to exclude certain .CSS file from output in Webpack:? - javascript

foo.js
foo.css
bar.css
foo.js includes foo.css.
And foo.css includes bar.css.
As result, I have bundle with bar.css. I want to exclude it. I am also using MiniCssExtractPlugin since I nee to separate css and js files.
What I already tried:
IgnorePlugin. It removes bar, but then MiniCssExtractPlugin fails because of it.
null-loader for bar.css ( I tried both test with regexp and include/exclude). It simply doesn't work (it could be that I miss something.
Two oneOf rules for /\.css$/: for with null-loader and another with everything else.
Nothing works.
What is the correct way to achieve it?
My webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
module.exports = {
plugins: [
new MiniCssExtractPlugin({ }),
new webpack.IgnorePlugin(/bar\.css$/)
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
],
},
],
},
};
And the output
ERROR in ./src/foo.css
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: Cannot find module '-!../node_modules/css-loader/dist/cjs.js!./bar.css'
It seems that MiniCssExtractPlugin uses css-loader explicitly, so I can't skip module loading with configuration.

Solution is to use NormalModuleReplacementPlugin to replace bad file with empty one
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const pluginToIgnore = /bad_css\.css$/;
module.exports = {
plugins: [
new MiniCssExtractPlugin({}),
new webpack.NormalModuleReplacementPlugin(
pluginToIgnore,
'!./empty.css'
)
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
],
},
};

Related

Webpack erroring on mini-css-extract-plugin loader

When I try to use the loader for mini-css-extract-plugin webpack returns the following error:
/node_modules/mini-css-extract-plugin/dist/loader.js:122
for (const asset of compilation.getAssets()) {
^
TypeError: compilation.getAssets(...) is not a function or its return value is not iterable
I am requiring the plugin and invoking the loader in my prod config file:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new MiniCssExtractPlugin({filename: "[name].[contentHash].css"}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
],
},
],
},
});
I have it included in my devDependencies:
"mini-css-extract-plugin": "^1.3.6",
But still I receive the error. I haven't found anything in the [documentation][1] to indicate what could be happening. Is there something I'm overlooking with this code?
Why would methods from loader.js be getting flagged as 'not a function?'
[1]: https://www.npmjs.com/package/mini-css-extract-plugin
I understand getAssets is only available since webpack 4.40.0 https://webpack.js.org/api/compilation-object/#getassets
You could try:
Update webpack version to at least 4.40.0
Downgrade min-css-extract-plugin to 1.3.0
I tried the second one, and worked for me, but upgrading webpack should work too.

Prerender React app as static HTML

I have an interactive landing page (single page - no react router). This landing page will need to be uploaded to a CMS where you just paste in the HTML - meaning I do not need <html> <head> <body> tags and the JavaScript file is uploaded separately. Currently I can just paste <div id="react-root"></div> into the CMS and upload my bundled JavaScript file and the page works.
However, the feedback is that this page now needs be be crawlable on all search engines. This means I need to basically render the initial HTML generated by react to paste into the CMS and have the page still all work as expected.
I am trying to make my build command output the rendered HTML to an index.html file where I can copy and paste the code into the CMS. (I have got my bundle.js file working fine).
webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
]
},
plugins: [
new CleanWebpackPlugin(['dist'])
],
};
webpack.prod.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = merge(common, {
plugins: [
new UglifyJSPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new HtmlWebpackPlugin({
inject: false,
template: require('./src/template.js'),
})
]
});
template.js
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const LandingPage = require('./App.js');
const html = ReactDOMServer.renderToString(LandingPage);
module.exports = function () {
return ReactDOM.hydrate(html, document.getElementById('lp-root'));
};
So I have used react-dom-server to renderToString. Is this the best way to do this? If so, this way isn't working as HtmlWebpackPlugin cannot process the ES6 syntax used in the landing pages (import and exports) - anyway around this?
The approach seems correct.
However, you might need babel transpiler to convert your code from ES6 syntax.
Try adding a new rule in the webpack config for the js/jsx files.
Something like this:
{
test: /js$/,
exclude: /(node_modules)/,
loader: "babel-loader",
query: { presets: ["react", "es2016", "es2015"], plugins: ["transform-class-properties"] },
},
It will transpile your code properly before using.
Hope it helps.
Please revert for any doubts.

How to import jquery in webpack

I have a problem with jquery when i using it on webpack.
My code:
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
entry: {
vendor: [
'./src/main/webapp/js/vendor/jquery-3.3.1.min.js',
// './src/main/webapp/js/vendor/fs.js',
'./src/main/webapp/js/vendor/google-adsense.js',
'./src/main/webapp/js/vendor/jquery.menu-aim.min.js',
'./src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
],
app: './src/main/assets/js/desktop/app.js',
mobile: './src/main/assets/js/mobile/app.js',
touch: './src/main/assets/js/touch/app.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: require.resolve('jquery'),
loader: 'expose-loader?jQuery!expose-loader?$'
}
],
},
plugins: [
// new CleanWebpackPlugin(['src/main/webapp/assets']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
new UglifyJsPlugin({
test: /\.js$/,
sourceMap: process.env.NODE_ENV === "development"
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, './src/main/webapp/js')
}
};
When above code compiles , console throws this error
vendor.js:1 Uncaught ReferenceError: webpackJsonp is not defined
at vendor.js:1
And when i try to use this
externals: {
jquery: 'jQuery'
}
It throws
vendor.js:1 Uncaught ReferenceError: jQuery is not defined
at Object.231 (vendor.js:1)
at o (common.js:1)
at Object.228 (vendor.js:1)
at o (common.js:1)
at window.webpackJsonp (common.js:1)
at vendor.js:1
And i using jquery in my core js file import $ from 'jquery'.
What did i do wrong ? any help ? Thank you.
So there are few themes in your webpack.config.js, some of which conflict. Just going to list them so I can better understand what I think you are trying to achieve.
Theme 1
You have an entry called vendor that is clearly referencing a minified jQuery library you have presumably downloaded and placed in the directory specified.
Theme 2
You also have an expose-loader that is essential exposing the jquery library from its node_modules probably listed in the dependencies of your package.json.
This makes the jquery in the node_modules available as $ and jQuery in the global scope of the page where your bundle is included.
Theme 3
You also have included the ProvidePlugin with configuration for jQuery.
The ProvidePlugin is supposed to inject dependencies into the scope of your module code, meaning you do not need to have import $ from 'jquery' instead $ and jQuery will already be available in all of your modules.
Conclusion
From what I have gathered I think you're trying to bundle jQuery from the static file at ./src/main/webapp/js/vendor/jquery-3.3.1.min.js in a vendor bundle.
You are then trying to expose the libraries in the vendor bundle to the global scope (jQuery).
Then also have your application code able to import jQuery from what is made available by the vendor bundle in the global scope.
Answer
So if that is what you are doing you need to do the following things.
Firstly, check in your package.json files dependencies for jquery. If its there you want to remove it, there's no need for it if you're going to use your jquery-3.3.1.min.js file instead to provide jQuery to your application.
Secondly, change your test of the expose-loader to trigger when it sees your jquery-3.3.1.min.js file in your entries, not resolve it from the jquery dependency from your node_modules.
This regex pattern should do the trick.
{
test: /jquery.+\.js$/,
use: [{
loader: 'expose-loader',
options: 'jQuery'
},{
loader: 'expose-loader',
options: '$'
}]
}
Thirdly, remove the ProvidePlugin if you're going to import the library explicitly with import $ from 'jquery' you do not need it.
Lastly, you need to tell webpack when it sees an import for jquery it can resolve this from window.jQuery in the global scope. You can do this with the externals configuration you already referenced.
externals: {
jquery: 'jQuery'
}
With all these changes you should end up with a webpack.config.js file that looks like this.
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
entry: {
vendor: [
'./src/main/webapp/js/vendor/jquery-3.3.1.min.js',
// './src/main/webapp/js/vendor/fs.js',
'./src/main/webapp/js/vendor/google-adsense.js',
'./src/main/webapp/js/vendor/jquery.menu-aim.min.js',
'./src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
],
app: './src/main/assets/js/desktop/app.js',
mobile: './src/main/assets/js/mobile/app.js',
touch: './src/main/assets/js/touch/app.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /jquery.+\.js$/,
use: [{
loader: 'expose-loader',
options: 'jQuery'
},{
loader: 'expose-loader',
options: '$'
}]
}
],
},
plugins: [
// new CleanWebpackPlugin(['src/main/webapp/assets']),
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
new UglifyJsPlugin({
test: /\.js$/,
sourceMap: process.env.NODE_ENV === "development"
})
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, './src/main/webapp/js')
},
externals: {
jquery: 'jQuery'
}
};
I hope that does not just give you the answer but enough explanation as to where you were going wrong.

Concat and minify CSS files with Webpack without requiring them from JS

what I'd like to achieve is the simplest way to concat and minify CSS with Webpack. I (successfully) tried ExtractTextPlugin and OptimizeCssAssetsPlugin, as follows
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
app: "./assets/js/main.js"
},
output: {
path: 'dist',
filename: "main.bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: "style.min.css",
allChunks: true
}),
new OptimizeCssAssetsPlugin()
]
}
But this only works requiring CSS files from within JS file, like that
require("../../css/normalize.css");
require("../../css/modal.css");
I would like JS files had nothing to do with CSS files.
My question is: how can I avoid requiring CSS file from JS entry point, and thus how can I specify in webpack.config.js what CSS file to concat and minify? So I want to write in webpack.config.js something like "i want to concat and minify all CSS files contained in /assets/css/ folder".
Thank you

Webpack error when importing CSS vendor library into index.js

I am moving existing javascript vendor libraries into a webpack setup, where possible using npm to install an npm module and deleting the old script tag on the individual page as bundle.js and bundle.css include it
In index.js, I have the following
import 'materialize-css/dist/css/materialize.min.css';
However, when webpack is run, it errors with the following
Module parse failed: .......Unexpected character ''
You may need an appropriate loader to handle this file type.
So, for some reason, webpack is looking at the entire materialize folder, rather than just the single minified css file, and is then error when it comes across materialize/fonts directory.
I am unclear why this is happening, and what to do to stop it. Any advice would be greatly appreciated.
My webpack config is below
const webpack = require('webpack');
const path = require('path');
var precss = require('precss');
var autoprefixer = require('autoprefixer');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var postcssImport = require('postcss-import');
module.exports = {
context: __dirname + '/frontend',
devtool: 'source-map',
entry: "./index.js",
output: {
filename: 'bundle.js',
path: path.join(__dirname, './static')
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
query: {
presets: ['es2015']
}
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', '!css-loader?sourceMap&importLoaders=1!postcss-loader')
}
]
},
plugins: [
new ExtractTextPlugin("si-styles.css")
],
postcss: function(webpack) {
return [
postcssImport({ addDependencyTo: webpack }), // Must be first item in list
precss,
autoprefixer({ browsers: ['last 2 versions'] })
];
},
}

Categories

Resources