I'm trying to implement Webpack4 for the angularjs project. I created some entrypoints and as for static imports they works as it's expected. But in the project we have some landing pages and their folders with the same names ('1300', '1520') etc that includes landing page css and html files. So if we use url like '...?theme=1520' the html and css from the '1520' folder are used(along with general styles and html). Now I have 3 webpack bundles for custom js, vendor js, general css.
So I try to import the landing page css and html dynamically depending on the url parameter. Of course, it doesn't work even after using all the plugins and solutions that I could find everywhere. The main goal now is to insert landing page css styles dynamically into the css bundle(in css.js). Thanks!
webpack.config.js:
const webpack = require("webpack");
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const cssnano = require('cssnano');
const TerserPlugin = require('terser-webpack-plugin');
const ImageminWebpWebpackPlugin = require("imagemin-webp-webpack-plugin");
const { nodeName } = require("jquery");
module.exports = {
target: 'node',
//mode: 'production',
context: path.resolve(__dirname, 'WebContent'),
entry: {
custom: './js/app.js',
styles: './js/css.js',
vendor: './js/vendor.js'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].bundle.js'
//,publicPath: 'build'
},
plugins: [new MiniCssExtractPlugin(),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.bundle\.css$/g,
cssProcessor: cssnano,
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: false
}),
new ImageminWebpWebpackPlugin(),
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
plugins: ['#babel/plugin-syntax-dynamic-import']
}
}
},
{
test: /\.html$/i,
loader: 'html-loader',
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: './WebContent',
esModule: true
}
}, {
loader: 'css-loader',
options: {
import: true,
//modules: true
}
}]
},
{
test: /\.(gif|png|jpe?g|svg)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/',
publicPath: path.resolve(__dirname, './build/images/'),
//emitFile: false
}
},
'url-loader?limit=100000']
}
]
},
optimization: {
splitChunks: {
// chunks: 'all',
cacheGroups: {
styles: {
name: 'styles.bundle',
test: /\.css$/,
chunks: 'all',
enforce: true,
minChunks: 1
}
}
}
,
minimize: true,
minimizer: [new TerserPlugin({
extractComments: false,
exclude: /\/node_modules/,
terserOptions: {
keep_fnames: true,
mangle: false
},
})]
}
};
css.js:
var BORDER_SCREEN_WIDTH_VALUE = 768;
var device = 'mobile';
var path = '';
var lpNumber = new RegExp('[\?&]theme=([^&#]*)').exec(window.location.search)[1];
if (window.innerWidth > BORDER_SCREEN_WIDTH_VALUE) {
device = 'desktop';
}
path = '/' + lpNumber + '/' + device + '/';
console.log(path);
import(/* webpackIgnore: true */'../WebContent/LP' + path + 'style.css').then((module) => {
module();
});
require('../css/amazing-theme.min.css');
require('../css/general.min.css');
require('../css/leadgen-theme.min.css');
require('../css/partials.min.css');
require('../css/style-all.min.css');
//require('../LP/1138/desktop/style.css');
Related
I'm creating a PWA react application in which I configure the manifest.json file and serviceWroker.js file.
In the development mode, it works fine but when I create production build using webpack, manifest.json file does not load in the Build folder.
Does anyone know what's the issue?
Here is my webpack.config.js file
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WebpackRTLPlugin = require('webpack-rtl-plugin');
const WebpackMessages = require('webpack-messages');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const del = require('del');
const webpack = require('webpack');
// theme name
const themeName = 'metronic';
// global variables
const rootPath = path.resolve(__dirname);
const distPath = rootPath + '/src';
const mainConfig = function() {
const isEnvDevelopment = false;
const isEnvProduction = true;
return {
mode: 'production',
stats: 'errors-only',
performance: {
hints: false
},
externals: [ 'jspdf', 'jspdf-autotable' ],
entry: './src/index.js',
output: {
// main output path in assets folder
path: path.resolve(__dirname, 'dist'),
// output path based on the entries' filename
// filename: '[name].js',
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
// We use "/" in development.
publicPath: '/artbot/app/'
},
resolve: { extensions: [ '.scss', '.css','.mjs','.jsx', '.js', '.json' ] },
optimization: {
minimize: true,
namedChunks: true,
nodeEnv: 'production',
removeAvailableModules: true,
removeEmptyChunks: true,
mergeDuplicateChunks: true,
minimizer: [ new OptimizeCssAssetsPlugin(), new TerserPlugin({ cache: true }) ],
splitChunks: {
chunks: "all"
}
},
plugins: [
// webpack log message
new WebpackMessages({
name: themeName,
logger: (str) => console.log(`>> ${str}`)
}),
new BundleAnalyzerPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new HtmlWebpackPlugin({
title: 'Custom Template',
template: 'public/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
// create css file
new MiniCssExtractPlugin({
filename: 'static/css/[name].css'
}),
new WebpackRTLPlugin({
filename: 'static/css/[name].rtl.css',
minify: true
}),
{
apply: (compiler) => {
// hook name
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
(async () => {
await del.sync(distPath + '/sass/*.js', { force: true });
})();
});
}
}
],
module: {
rules: [
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
// exclude: /#babel(?:\/|\\{1,2})runtime/,
exclude: /node_modules/,
loader: require.resolve('babel-loader'),
options: {
babelrc: true,
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '#svgr/webpack?-svgo,+titleProp,+ref![path]'
}
}
}
]
]
}
},
{
test: /\.css$/,
loader: 'css-loader'
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'resolve-url-loader',
options: {
removeCR: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|jpg|svg)$/,
loader: 'file-loader',
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
esModule: false
}
//loader: 'url-loader?limit=8192'
},
{
test: /\.(woff2|eot|ttf|woff)$/,
loader: 'url-loader'
},
{
test: /\.json$/,
loader: 'file-loader',
type: 'javascript/auto',
exclude: /node_modules/,
options: {
name: 'static/json/[name].[hash:8].[ext]'
}
},
{
test: /\.html$/i,
loader: 'html-loader'
}
]
}
};
};
module.exports = function() {
return [ mainConfig() ];
};
After migrating my CSS files to SCSS, I can see FOUC for my layout elements at the first load (After each reloads of page).
I guess it has something to do with my webpack config so I tried to fix the problem by using mini-css-extract-plugin, but I can still see the problem.
Here is the content of my webpack.config.js file:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require('path');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,{
loader: "css-loader"
}, {
loader: "sass-loader",
}
]
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
},
{
test: /(\.(?:le|c)ss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: false,
},
},
{
loader: 'less-loader',
options: {
sourceMap: false,
javascriptEnabled: true,
},
}
]
},
{
test: /\.(png|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
},
]
}
]
},
output: {
publicPath: "/"
},
devServer: {
historyApiFallback: true,
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin(),
new MomentLocalesPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.AggressiveMergingPlugin()
],
resolve: {
alias: {
"#ant-design/icons/lib/dist$": path.resolve(__dirname, "./src/icons.js")
}
}
};
The problem wasn't because of Webpack. It was because of me being stupid :)
I imported the CSS file inside of a child component, so when I reload the page, it first shows the layout without any styles and then loads the child component and gives the layout a proper style. So basically, the only thing I did to fix the problem was to import the style in the layout component.
import '../style.scsc'
Im trying to use url-loader to convert file URLs into inline data URLs for my CSS backgrounds. When I run the build command, it generates the CSS & JS files without any error but the background image URLs remain unchanged in the CSS output.
Here's my webpack config:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const ManifestPlugin = require('webpack-manifest-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = (env, argv) => ({
entry: {
app: ['./app/Resources/js/app.js', './app/Resources/scss/app.scss']
},
output: {
path: path.join(__dirname, "/web/build"),
filename: argv.mode === 'production' ? '[name].[hash].js' : '[name].js'
},
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
})
]
},
devServer: {
contentBase: './web',
port: 9000,
disableHostCheck: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
},
resolve: {
extensions: ['.js']
}
},
{
test: /\.scss$/,
resolve: {
extensions: ['.scss']
},
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader"
},
{
loader: "sass-loader",
options: {
sassOptions: {
outputStyle: 'expanded',
includePaths: ['./node_modules/foundation-sites/scss']
}
}
}
]
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
},
}
],
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: argv.mode === 'production' ? '[name].[contenthash].css' : '[name].css'
}),
new ManifestPlugin(),
new CleanWebpackPlugin()
],
});
My CSS is just a standard SCSS file:
.icon { background:url('path/to/icon.svg'); }
In the final output, I'd expect path/to/icon.svg to be replaced by a data URL. What am I doing wrong?
I have posted my Webpack configs below for a production environment. I am attempting to use background-image: url(../img/chevron-thin-right.svg); in one of my SCSS files but it is being resolved to background-image: url([object Module]) and therefore not working. I am trying to port a purely SCSS and HTML project into a react app being bundled by Webpack so this above approach used to work. Any fixes would be greatly appreciated.
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: './src/client/index.js',
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
template: './public/index.html',
favicon: './public/favicon.ico',
hash: true,
filename: 'index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(jpg|png|woff|woff2|eot|ttf)$/,
use: {
loader: 'file-loader?limit=100000&name=images/[name].[ext]'
}
},
{
test: /\.svg$/,
use: [
"babel-loader",
{
loader: "react-svg-loader",
options: {
svgo: {
plugins: [
{
removeTitle: true,
removeComments: true
}
],
floatPrecision: 2
}
}
}
]
}
]
}
};
webpack.prod.js
const merge = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const common = require('./webpack.common.js');
const path = require('path');
const autoprefixer = require('autoprefixer');
module.exports = {};
module.exports = merge(common, {
mode: 'production',
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [
new CleanWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: '[name].css'
})
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
root: path.resolve(__dirname),
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
'browsers': ['last 2 versions', 'ie >= 10'],
}),
],
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
outputStyle: 'compressed',
sourceMap: true,
includePaths: [
'./src/client/style/scss',
],
},
}
]
}
]
}
});
In case anybody else is looking for an answer I was able to solve it. It was due to not loading the associated SVG through the url-loader. Another problem arose when adding svg back in because my inline SVGs I wanted to use as React components were running into errors as they were now going through the url-loader instead of my react-svg-loader.
Below is the working webpack.common.js and webpack.prod.js stayed the same. The solution was to differentiate my inline and external SVGs by turning all inline extensions from .svg to .inline.svg and adjusting webpack config accordingly
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: './src/client/index.js',
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
template: './public/index.html',
favicon: './public/favicon.ico',
hash: true,
filename: 'index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
exclude: /\.inline.svg$/,
use: {
loader: 'url-loader?limit=1000&name=images/[name].[ext]'
}
},
{
test: /\.inline.svg$/,
use: [
"babel-loader",
{
loader: "react-svg-loader",
options: {
svgo: {
plugins: [
{
removeTitle: true,
removeComments: true
}
],
floatPrecision: 2
}
}
}
]
}
]
}
};
I can't figure out how to access JSON data in my pug templates.
Here is my pug layout
title #{htmlWebpackPlugin.pages[page].title}
Pug page which is initializing page variable
block vars
- var page = "catalog"
Webpack part
new HtmlWebpackPlugin({
filename: 'catalog.html',
chunks: ['main'],
template: PATHS.source + '/views/pages/catalog.pug',
inject: true,
data: {
pages: require('./dev/util/options.json')
}
})
JSON
"pages": {
"catalog": {
"title": "Catalog",
"description": "",
"keywords": ""
}
}
Each page is a separate pug and json.
First, I declare the entry, in my case it is a separate js file entry.js
module.exports.html = {
'index': 'index',
'about': 'o-mnie',
'contact': 'kontakt'
};
Webpack part
Include entry:
const entry = require('./entry.js');
Next add entry to HtmlWebpackPlugin:
const entryHtmlPlugins = Object.keys(entry.html).map(entryName => {
return new HtmlWebpackPlugin({
filename: `${entry.html[entryName]}.html`,
template: `./source/templates/containers/${entryName}/${entryName}.pug`,
chunks: [entryName],
file: require(`../source/templates/containers/${entryName}/${entryName}.json`)
})
});
See the full code how to use json data from pug and webpack -> github
const webpack = require("webpack");
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin');
const entry = require('./entry.js');
const entryHtmlPlugins = Object.keys(entry.html).map(entryName => {
return new HtmlWebpackPlugin({
filename: `${entry.html[entryName]}.html`,
template: `./source/templates/containers/${entryName}/${entryName}.pug`,
path: path.join(__dirname, "../dist/"),
chunks: [entryName],
// inject: true,
// cache: true,
file: require(`../source/templates/containers/${entryName}/${entryName}.json`),
mode: 'development'
})
});
const output = {
path: path.resolve(__dirname, "source"),
filename: "[name].[hash].js",
publicPath: "/"
}
const config = {
devtool: "eval-source-map",
mode: "development",
entry: entry.site,
output: output,
module: {
rules: [
{
// JS
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
}
},
{
// CSS | SCSS
test: /\.(css|scss)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')({
'browsers': ['> 1%', 'last 2 versions']
})],
}
},
{
loader: 'sass-loader'
},
{
loader: 'sass-resources-loader', // style-resources-loader then we can use sass, less, stylus
options: {
resources: [
path.resolve(__dirname, '../source/scss/main.scss')
]
},
}
]
})
},
{
// IMAGES
test: /\.(jpe?g|png|gif|svg)$/i,
loader: "file-loader"
},
{
// PUG
test: /\.pug$/,
loader: 'pug-loader',
options: {
pretty: true,
self: true
}
}
],
},
plugins: [
new SimpleProgressWebpackPlugin({
format: 'compact'
}),
new ExtractTextPlugin({
filename: '[name].[hash].css',
// disable: false,
allChunks: true
}),
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(false)
}),
].concat(entryHtmlPlugins)
};
module.exports = config;