Webpack loads wrong images to dist directory - javascript

My webpack.config.js file
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
entry: {
index: './src/js/index.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: { minimize: true },
},
],
},
{
test: /\.(jpg|png)$/,
use: {
loader: 'url-loader',
},
},
{
test: /\.(css|scss)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
],
},
output: {
path: path.resolve(__dirname, './dist'),
filename: './js/[name].bundle.js',
},
plugins: [
new HtmlWebPackPlugin({
template: path.resolve(__dirname, './src/index.html'),
filename: 'index.html',
chunks: ['index'],
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: './css/[name].css',
}),
new CopyWebpackPlugin({
patterns: [{ from: './src/assets', to: './assets' }],
}),
],
devtool: 'source-map',
mode: 'development',
};
my files structure where the problem occurs:
what is strange, when I run npm run build in the terminal, all directories are loaded correctly I mean: background-image works, and slider directory with images is loaded, but there is also loaded some additional files with random numerical names
and those three additional .png files are loaded to my index.html as img1.png img2.png and img3.png files which not work on my website, they just do not want to load

This is caused by Webpack 5's new Asset Modules. You can either remove url-loader and specify the asset type like so:
{
test: /\.(jpg|png)$/,
type: 'asset/inline',
},
Or you can disable asset modules by adding type: 'javascript/auto' to your rule:
{
test: /\.(jpg|png)$/,
use: {
loader: 'url-loader',
},
type: 'javascript/auto'
}
See the documentation to learn more, and to see how to do this for file-loader and raw-loader as well.

I think your image files are probably double-processed, because you once just copy your assets folder to your dist output folder, and then you somewhere probably use the images with url("./someImage") or similar in your css files, which get processed by your css loader or css Mini Exctract plugin, which creates that cryptic image asset output, that then gets used in your html files, look at here: webpack.js.org/guides/asset-management/#loading-images.. I dont know why you need to copy your assets folder but that looks redundant to me...
EDIT: I'm not an expert at this, but after investigating some hours into this weird error, I realized that the paths were set correctly to the image files, but the image output that file-loader or other loaders generate with the image (like 0f9b8be78d378ad2ef78.jpg) seems to be something different, if I open them in vscode editor in standard text/binary editor, I get this line: export default __webpack_public_path__ + "someimage.jpg";, with url-loader its the binary64 converted string with data-url(..).. so it seems its not for the purpose to use it in static html files..
I then used the recommended way to do it like this in webpack.config.js:
{ test: /\.(jpg|png)$/,
type: 'asset/resource', }
after that my image was shown correctly, I have no idea what file-loader etc. is doing..
here is my final webpack.config.js:
const path = require('path')
const HtmlWebPackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: {
index: './src/js/index.js',
},
devServer: {
port: 5757,
contentBase: path.resolve(__dirname, 'src'),
publicPath: '/src',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: { minimize: true },
},
],
},
{
test: /\.(jpg|png)$/,
type: 'asset/resource',
},
{
test: /\.(css|scss)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
],
},
output: {
path: path.resolve(__dirname, './build'),
//publicPath: './assets/images',
},
plugins: [
new HtmlWebPackPlugin({
template: path.resolve(__dirname, './src/index.html'),
filename: 'index.html',
inject: 'body',
}),
new MiniCssExtractPlugin({
filename: './css/[name].css',
}),
],
devtool: 'source-map',
mode: 'development',
}

I have just solved my every problem because after using the answer from #ChillaBee, images were working correctly expect my background photo which I used in css file as url. Use the answer above but change
{
test: /\.(jpg|png)$/,
type: 'asset/resource',
},
to
{
test: /\.(jpg|png)$/,
type: 'asset/inline',
},
now images from html and css are loaded correctly

Related

CSS and other assets from the public folder not getting included in the build

In my public folder I have index.html, the css file and the fonts.
In my source folder I have the index.js file.
This is how the folder structure looks
I have setup the webpack.config.js file like this
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.join(__dirname, 'src', 'index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '',
filename: 'bundle.js',
},
mode: 'development',
module: {
rules: [
{
test: /\.?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: ['#babel/transform-runtime'],
},
},
},
{
test: /\.css$/i,
exclude: /node_modules/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
exclude: /node_modules/,
use: 'file-loader',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
exclude: /node_modules/,
use: 'url-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
publicPath: './',
}),
],
};
Everything is working fine in dev, but when I create the build the files inside the public folder except the index.html are not included.
The dist folder after the build
You can use the copyWebpackPlugin to move the files from /public to the new build destination (/dist).
Example:
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{ from: "./public/*", to: "./" },
],
}),
],
};
However, how are you using this CSS and Font files? Webpack should also help to pack your CSS correctly along with the rest of your code. I recommend you to follow the oficial documentation: Webpack.

file-loader creating extra file in root of dist folder

I am using file loader 6.2 to copy static image files into an images folder in my dist destination and it all works fine using the following config (ie the svg is copied from my src folder to the dist/images):
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images',
publicPath: 'images',
},
However, as well as the static file being copied over, it also creates another file in the root of the dist folder that references the static - eg, when I copy a file called loading.svg, it creates that file as well as a file with a random hash (eg 817e56ed349aea52edfa.svg)
and inside this file, it just references the other file:
export default "images/loading.svg";
If I then check my compiled sass, I can see it references the hash file instead of the asset that was copied into the images folder.
background: url(../817e56ed349aea52edfa.svg) center center no-repeat;
How do I stop the extra file being created so that it only creates and references the image in the images folder directly?
On a side note, I tried swapping file loader for copy webpack plugin, and that also copied the files into the images folder, but on that occasion, instead of referencing that file, it just also created a hash version of the svg (but with the actual svg contents rather than a webpack export) in the root directory and referenced that instead - so it may be something else causing this issue? I include full webpack config below in case anyone can spot anything obvious or something else is causing the issue
Full webpack config
const paths = require("./webpack.paths");
const { VueLoaderPlugin } = require('vue-loader');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require("terser-webpack-plugin");
const isProduction = (process.env.NODE_ENV === 'production');
if (isProduction) {
console.log("Bundling in PRODUCTION mode")
} else {
console.log("Bundling in DEVELOPMENT mode")
}
module.exports = {
entry: {
styles: paths.styles,
home: './vue/templates/home/main.js',
},
mode: isProduction ? 'production' : 'development',
output: {
path: paths.dist,
filename: 'js/[name].js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.(css|scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
// Use the css-loader to parse and minify CSS imports.
loader: 'css-loader',
options: { sourceMap: true }
},
{
// Use the postcss-loader to add vendor prefixes via autoprefixer.
loader: 'postcss-loader',
options: {
postcssOptions: {
config: paths.postcssConfig,
},
sourceMap: true
}
},
{
// Use the sass-loader to parse and minify CSS imports.
loader: 'sass-loader',
options: { sourceMap: true }
},
],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images',
publicPath: 'images',
},
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', { targets: "defaults" }],
]
}
},
},
]
},
devServer: {
hot: true,
noInfo: true,
overlay: true,
contentBase: [paths.root],
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
}),
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [
`${paths.dist}/css/*`,
`${paths.dist}/images/*`,
`${paths.dist}/js/*`,
],
}),
new ESLintPlugin({
extensions: ['vue', 'js'],
})
],
optimization: {
minimize: isProduction,
minimizer: [
new TerserPlugin(),
],
runtimeChunk: 'single',
splitChunks: {
minSize: 0,
cacheGroups: {
vendor: {
name: 'vendor',
chunks: 'all',
test: /[\\/]node_modules[\\/]/,
priority: 10,
enforce: true
}
}
}
}
}
if (isProduction) {
module.exports.plugins = (module.exports.plugins || []).concat([
new CompressionWebpackPlugin(),
])
}
In the end it looks as if it was the css loader that was causing the issue - so I configured it to ignore urls and then changed from the file-loader plugin to the copy-webpack-plugin to copy the images:
Ignore urls with css loader
MiniCssExtractPlugin.loader,
{
// Use the css-loader to parse and minify CSS imports.
loader: 'css-loader',
options: {
sourceMap: true,
url: false,
}
},
Copy webpack plugin to copy images
const CopyPlugin = require("copy-webpack-plugin");
plugins: [
new CopyPlugin({
patterns: [
{
from: 'vue/images', // src location
to: 'images', // destination location in dist folder
},
],
options: {
concurrency: 100,
},
}),
]
Then in my css I could just reference the image as relative from the dist folders css to the dist folder images folders:
background-image: url(../images/loading.svg);
Seems a bit odd to me that I had to do it this way in order not to get the extra svg file so if anyone comes up with a better solution or knows how not to create the extra file, please feel free to answer

How to set image links for a multipage webpack configuration when the landing page is not in a subdirectory?

I am trying my hand at a multipage webpack configuration and have a question about how to load images differently for a landing page compared to the other pages on the site. The landing page builds to the root directory, while the other pages build to their respective subfolders.
Webpack appends the correct relative path ../images/ for the other pages, however the landing page needs to remain as images/, as it is located on the root directory along with the images folder.
How can I configure webpack such that <img src="images/00.jpg"> stays the same for the landing page, but is updated to <img src="../images/00.jpg"> for all other pages?
Here is the source folder:
/ src /
-home.html
-about.html
js/
-home.js
-about.js
images/
-00.jpg
-01.jpg
scss/
-style.scss
Here is the build folder webpack generates:
/ public_html /
-index.html // relative links in this file become broken :(
-bundle.js
about/
-index.html
-bundle.js
images/
-00.jpg
-01.jpg
css/
-style.css
Finally, here is the webpack configuration. Please excuse the wall of code, I decided to include the entire configuration in case there is a better way to set this up.
// webpack.config.js
const webpack = require('webpack');
const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
home: './src/js/home.js',
about: './src/js/about.js',
},
output: {
filename: (pathData) => {
return pathData.chunk.name === 'home' ? 'bundle.js' : '[name]/bundle.js';
},
path: path.resolve(__dirname, 'public_html')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: { minimize: true }
}
]
},{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},{
test: /\.sass$|\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
},{
test: /\.(jpg|png|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath:'images/',
publicPath:'../images/' // how to exclude home.html ?
}
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
hash: true,
filename: 'index.html', // landing page remains in root directory
template: 'src/index.html',
chunks: ['home']
}),
new HtmlWebPackPlugin({
hash: true,
filename: 'about/index.html', // all other pages move to subdirectories
template: 'src/about.html',
chunks: ['about']
}),
new MiniCssExtractPlugin({
filename: "css/style.css"
}),
new CleanWebpackPlugin()
]
};
Thank you! And also, let me know how you like this configuration file!
I have it working. horray!
I never ended up using publicPath. Maybe there would have been a way to change it, but this ended up being a red herring. Instead I restructured my src directory to follow the pattern I was looking for, and removed html-loader so the paths wouldn't get changed during the build process.
Here is my new source directory:
/ src /
-home.html
templates/
-about.html
js/
-home.js
-about.js
images/
-00.jpg
-01.jpg
scss/
-style.scss
You can see home.html is purposely on the main directory rather than in /templates. Image sources are referenced as images/ in home.html, and ../images/ elsewhere.
Now instead of html-loader, I used copy-webpack-plugin to require / copy the images from the source directory to the build folder, as html-loader was changing the paths during the build process. It took me a few tries to figure out that html-loader was the culprit.
Here is my final working webpack config for the record.
// webpack.config.js
const webpack = require('webpack');
const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: {
home: './js/home.js',
about: './js/about.js',
},
output: {
filename: (pathData) => {
return pathData.chunk.name === 'home' ? 'bundle.js' : '[name]/bundle.js';
},
path: path.resolve(__dirname, 'public_html')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},{
test: /\.sass$|\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
},{
test: /\.(jpg|png|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
]
},
plugins: [
new CopyPlugin({
patterns: [
{from:'./image',to:'image'}
],
}),
new HtmlWebPackPlugin({
hash: true,
filename: 'index.html', // landing page remains in root directory
template: 'index.html',
chunks: ['home']
}),
new HtmlWebPackPlugin({
hash: true,
filename: 'about/index.html', // all other pages move to subdirectories
template: 'templates/about.html',
chunks: ['about']
}),
new MiniCssExtractPlugin({
filename: "style.css"
}),
new CleanWebpackPlugin()
]
};
I solve the problem on this Way.
Import images on views
Import YourImage from '../../public/assets/img/your-image.png';
Call where you need with implement them
src={YourImage}
Or in your classNames
background-image: `url(${YourImage})`
Config the path to your project repository from GitHub in the rules section of webpack.config.js file.
Repository: "https://maikpwwq.github.io/your-repository/"
rules: [ { type: "asset", test: /\.(png|svg|jpg|gif|jpeg)$/i, use: [{ options: { publicPath: '/your-repository/'}, loader: 'file-loader' ]} } ]

Webpack file-loader and Svelte style background image not working [duplicate]

Trying to load a CSS background image, defined in SCSS, and serving via WebPack devServer.
The background image defined in SCSS is picked up by WebPack, but it doesn't show on the page.
I have tried setting publicPath option in MiniCssExtractPlugin.loader and looked at all the answers I could find related to this problem, but haven't been able to get this to work.
Update: Also tried setting publicPath option in file-loader. According to the documentation this defaults to the output publicPath, which is the same in my case.
Update: When using an absolute path in SCSS it compiles to CSS as that exact path, but that isn't going to work as local dev and staging and prod all have different paths.
Relevant part of the output when running webpack-dev-server:
Child mini-css-extract-plugin node_modules/css-loader/index.js??ref--5-1!node_modules/sass-loader/dist/cjs.js??ref--5-2!index.scss:
Asset Size Chunks Chunk Names
pages/index/404#2x.png 29.2 KiB [emitted]
index.scss:
body {
background: url("./pages/index/404#2x.png");
}
CSS output:
body {
background: url([object Module]);
}
My WebPack config:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const outputDir = 'dist/website';
const publicPath = '/web/website/';
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './index.ts',
output: {
path: path.resolve(__dirname, outputDir),
publicPath: publicPath,
filename: 'bundle.js',
},
resolve: {
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: ['ts-loader'],
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourcemap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
},
},
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
path: path.resolve(__dirname, outputDir),
filename: '[name].css',
chunkFilename: '[id].css',
}),
new CleanWebpackPlugin([outputDir]),
],
devServer: {
contentBase: path.join(__dirname, outputDir),
publicPath: publicPath,
port: 9009,
proxy: {
'/': 'http://127.0.0.1:5000/',
},
},
};
index.ts:
import './index.scss';
console.log('WebPack is working!');
I have figured out the issue: css-loader was on version 1.0.0. Probab ly because it was installed a long time ago.
Upgrading to the latest version of css-loader with npm install css-loader#latest --save-dev solved the issue.

MiniCssExtractPlugin public path not working

I am using MiniCssExtractPlugin to lazyload CSS files in my React application.
I have given publicPath option for MiniCssExtractPlugin but it is not taking this option value, it is taking output.publicPath option.
config: {
test: /\.(css)?$/,
use: [{
loader : MiniCssExtractPlugin.loader,
options : {
publicPath : '../'
}},
'css-loader'
],
}
Seems as though your not the only one confused, 52 comments on how to get this right. The issue of publicPath in html-webpack-plugin was similar and helped. However, the biggest help was from npm run ejecting out of the create-react-app and inspecting the webpack.config.js files.
TL;DR: You need to specify the save path in the plugin constructor.
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
filename: "assets/css/[name].css",
}),
You might need to re-think the module output logic.
Avoid specifying a nested path in module.output.path e.g. public/assets/js, instead set the root directory: public and set the nesting for the filename key: assets/js/[name].js.
You can then specify a publicPath in the main module.output that you would use for subdomains or CDN's etc.
The final complete config worked for me:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const publicPath = '/';
module.exports = {
entry: './_src/_js/index.js',
output: {
filename: 'assets/js/[name].js',
path: path.resolve(__dirname, 'dist'),
publicPath: publicPath,
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/images/[name].[ext]',
},
},
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader','eslint-loader']
},
],
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "assets/css/[name].css",
}),
new HtmlWebpackPlugin({
inject: true,
}),
]
};

Categories