How to load images through webpack when using HTMLWebpackPlugin? - javascript

I am using HTMLWebpackPlugin and in my template I have an img tag:
<img src="./images/logo/png">
If you notice, here I am using a relative path thinking webpack will trigger the file loader that's configured in my webpack.config.js file but after compilation I get the exact same src attribute in my html:
<img src="./images/logo/png">
How can I trigger webpack to dynamically replace these relative paths with, well whatever I've configured in my webpack configuration?

I'm not a webpack expert, but i got it to work by doing this:
<img src="<%=require('./src/assets/logo.png')%>">
Plugin config
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
}),
According to the docs: https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md
By default (if you don't specify any loader in any way) a fallback
lodash loader kicks in.
The <%= %> signifies a lodash template
Under the hood it is using a webpack child compilation which inherits
all loaders from your main configuration.
Calling require on your img path will then call the file loader.
You may run into some path issues, but it should work.

Using html-loader with HTML webpack plugin worked for me.
module: {
rules: [
{
test: /\.(html)$/,
use: ['html-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]

You should use the CopyWebpackPlugin.
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins:[
....
new CopyWebpackPlugin({'patterns': [
{from:'./src/assets/images', to:'images'}
]}),
....
]
This is copy the src/assets/images to your `distfolder/images'.

You could use url-loader in your webpack config to add images below a certain limit encoded as base64 uri's in your final bundle and images above the limit as regular image tags (relative to the publicPath)
module.rules.push({
test: /\.(png|jp(e*)g|gif)$/,
exclude: /node_modules/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
publicPath: "/"
}
}]
})

I ran into this issue while following the Getting Started guide that Webpack provides. I was using the template code from the guide and bundling images. But then when migrating an existing vanilla html/js/css project to use Webpack, I discovered that in order to use the template HTML loading like I wanted -- with paths to image resource contained in the template -- I had to remove the asset loader usage from my webpack.config.js for the html-loader to properly resolve the new hashed paths it was creating in dist
To use Webpack doc syntax, remove the lines prefixed with "-" and add the lines prefixed with "+"
module: {
rules: [
{
- test: /\.(png|svg|jpg|jpeg|gif)$/i,
- type: 'asset/resource',
+ test: /\.(html)$/,
+ use: ['html-loader'],
}
]
}

Related

Public file is missing from build/public in Webpack [duplicate]

I'm trying to move from Gulp to Webpack. In Gulp I have task which copies all files and folders from /static/ folder to /build/ folder. How to do the same with Webpack? Do I need some plugin?
Requiring assets using the file-loader module is the way webpack is intended to be used (source). However, if you need greater flexibility or want a cleaner interface, you can also copy static files directly using my copy-webpack-plugin (npm, Github). For your static to build example:
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
context: path.join(__dirname, 'your-app'),
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: 'static' }
]
})
]
};
Compatibility note: If you're using an old version of webpack like webpack#4.x.x, use copy-webpack-plugin#6.x.x. Otherwise use latest.
You don't need to copy things around, webpack works different than gulp. Webpack is a module bundler and everything you reference in your files will be included. You just need to specify a loader for that.
So if you write:
var myImage = require("./static/myImage.jpg");
Webpack will first try to parse the referenced file as JavaScript (because that's the default). Of course, that will fail. That's why you need to specify a loader for that file type. The file- or url-loader for instance take the referenced file, put it into webpack's output folder (which should be build in your case) and return the hashed url for that file.
var myImage = require("./static/myImage.jpg");
console.log(myImage); // '/build/12as7f9asfasgasg.jpg'
Usually loaders are applied via the webpack config:
// webpack.config.js
module.exports = {
...
module: {
loaders: [
{ test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, loader: "file" }
]
}
};
Of course you need to install the file-loader first to make this work.
If you want to copy your static files you can use the file-loader in this way :
for html files :
in webpack.config.js :
module.exports = {
...
module: {
loaders: [
{ test: /\.(html)$/,
loader: "file?name=[path][name].[ext]&context=./app/static"
}
]
}
};
in your js file :
require.context("./static/", true, /^\.\/.*\.html/);
./static/ is relative to where your js file is.
You can do the same with images or whatever.
The context is a powerful method to explore !!
One advantage that the aforementioned copy-webpack-plugin brings that hasn't been explained before is that all the other methods mentioned here still bundle the resources into your bundle files (and require you to "require" or "import" them somewhere). If I just want to move some images around or some template partials, I don't want to clutter up my javascript bundle file with useless references to them, I just want the files emitted in the right place. I haven't found any other way to do this in webpack. Admittedly it's not what webpack originally was designed for, but it's definitely a current use case.
(#BreakDS I hope this answers your question - it's only a benefit if you want it)
Webpack 5 adds Asset Modules which are essentially replacements for common file loaders. I've copied a relevant portion of the documentation below:
asset/resource emits a separate file and exports the URL. Previously achievable by using file-loader.
asset/inline exports a data URI of the asset. Previously achievable by using url-loader.
asset/source exports the source code of the asset. Previously achievable by using raw-loader.
asset automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.
To add one in you can make your config look like so:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
type: "asset/resource"
}
]
}
};
To control how the files get output, you can use templated paths.
In the config you can set the global template here:
// webpack.config.js
module.exports = {
...
output: {
...
assetModuleFilename: '[path][name].[hash][ext][query]'
}
}
To override for a specific set of assets, you can do this:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/,
type: "asset/resource"
generator: {
filename: '[path][name].[hash][ext][query]'
}
}
]
}
};
The provided templating will result in filenames that look like build/images/img.151cfcfa1bd74779aadb.png. The hash can be useful for cache busting etc. You should modify to your needs.
Above suggestions are good. But to try to answer your question directly I'd suggest using cpy-cli in a script defined in your package.json.
This example expects node to somewhere on your path. Install cpy-cli as a development dependency:
npm install --save-dev cpy-cli
Then create a couple of nodejs files. One to do the copy and the other to display a checkmark and message.
copy.js
#!/usr/bin/env node
var shelljs = require('shelljs');
var addCheckMark = require('./helpers/checkmark');
var path = require('path');
var cpy = path.join(__dirname, '../node_modules/cpy-cli/cli.js');
shelljs.exec(cpy + ' /static/* /build/', addCheckMark.bind(null, callback));
function callback() {
process.stdout.write(' Copied /static/* to the /build/ directory\n\n');
}
checkmark.js
var chalk = require('chalk');
/**
* Adds mark check symbol
*/
function addCheckMark(callback) {
process.stdout.write(chalk.green(' ✓'));
callback();
}
module.exports = addCheckMark;
Add the script in package.json. Assuming scripts are in <project-root>/scripts/
...
"scripts": {
"copy": "node scripts/copy.js",
...
To run the sript:
npm run copy
The way I load static images and fonts:
module: {
rules: [
....
{
test: /\.(jpe?g|png|gif|svg)$/i,
/* Exclude fonts while working with images, e.g. .svg can be both image or font. */
exclude: path.resolve(__dirname, '../src/assets/fonts'),
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}]
},
{
test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
/* Exclude images while working with fonts, e.g. .svg can be both image or font. */
exclude: path.resolve(__dirname, '../src/assets/images'),
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
},
}
]
}
Don't forget to install file-loader to have that working.
You can write bash in your package.json:
# package.json
{
"name": ...,
"version": ...,
"scripts": {
"build": "NODE_ENV=production npm run webpack && cp -v <this> <that> && echo ok",
...
}
}
Most likely you should use CopyWebpackPlugin which was mentioned in kevlened answer. Alternativly for some kind of files like .html or .json you can also use raw-loader or json-loader. Install it via npm install -D raw-loader and then what you only need to do is to add another loader to our webpack.config.js file.
Like:
{
test: /\.html/,
loader: 'raw'
}
Note: Restart the webpack-dev-server for any config changes to take effect.
And now you can require html files using relative paths, this makes it much easier to move folders around.
template: require('./nav.html')
I was stuck here too. copy-webpack-plugin worked for me.
However, 'copy-webpack-plugin' was not necessary in my case (i learned later).
webpack ignores root paths
example
<img src="/images/logo.png'>
Hence, to make this work without using 'copy-webpack-plugin'
use '~' in paths
<img src="~images/logo.png'>
'~' tells webpack to consider 'images' as a module
note:
you might have to add the parent directory of images directory in
resolve: {
modules: [
'parent-directory of images',
'node_modules'
]
}
Visit https://vuejs-templates.github.io/webpack/static.html
The webpack config file (in webpack 2) allows you to export a promise chain, so long as the last step returns a webpack config object. See promise configuration docs. From there:
webpack now supports returning a Promise from the configuration file. This allows to do async processing in you configuration file.
You could create a simple recursive copy function that copies your file, and only after that triggers webpack. E.g.:
module.exports = function(){
return copyTheFiles( inpath, outpath).then( result => {
return { entry: "..." } // Etc etc
} )
}
lets say all your static assets are in a folder "static" at the root level and you want copy them to the build folder maintaining the structure of subfolder, then
in your entry file) just put
//index.js or index.jsx
require.context("!!file?name=[path][name].[ext]&context=./static!../static/", true, /^\.\/.*\.*/);
In my case I used webpack for a wordpress plugin to compress js files, where the plugin files are already compressed and need to skip from the process.
optimization: {
minimize: false,
},
externals: {
"jquery": "jQuery",
},
entry: glob.sync('./js/plugin/**.js').reduce(function (obj, el) {
obj[path.parse(el).name] = el;
return obj
}, {}),
output: {
path: path.resolve(__dirname, './js/dist/plugin'),
filename: "[name].js",
clean: true,
},
That used to copy the js file as it is to the build folder. Using any other methods like file-loader and copy-webpack create issues with that.
Hope it will help someone.

Purpose of Webpack-Manifest-Plugin in Webpack

In official doc says when applying Code Splitting and generating chunk files, if chunk code changes, then the filename of it will change. However index.html which uses the chunk code files can't change the filename in its <script> tag, so in this case the manifest.json which is generated by webpack-manifest-plugin will help mapping [name].js to [name].[hash].js.
But opposing to what the doc says, it seems that every time I run webpack to build my codes, new codes are generated with new hash value in its file(in case something in code changed), and HTML-Webpack-Plugin will autometically inject <script> tag with new name of code's file. This seems that there is no need to use webpack-manifest-plugin, I don't even see where the manifest.json is used.
In case if you are looking for my webpack.config.js:
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
module.exports = {
entry: ["core-js/stable", "regenerator-runtime/runtime", "./src/index.jsx"],
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(`${__dirname}/build`)
},
mode: "none",
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: "/node_modules",
use: ['babel-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
template: './public/index.html'
}),
new ManifestPlugin({
fileName: 'manifest.json'
})
// need this plugin for SSR?
]
}
What is the detailed usage of webpack-manifest-plugin and manifest.json?
HtmlWebpackPlugin "knows" that your asset bundle.js maps to bundle.some-hash.js because it uses the Webpack's Manifest. This manifest is not emitted though. It's just data that Webpack uses to keep track of how all the modules map to the output bundles.
WebpackManifestPlugin uses Webpack's manifest data data to emit a JSON file (that you can call manifest.json or whatever you want).
Since you are using HtmlWebpackPlugin with the inject: true option (it's the default one), HtmlWebpackPlugin injects your bundle bundle.some-hash.js into your template. So you probably have no need to use WebpackManifestPlugin for your application.
If you did not use HtmlWebpackPlugin, or if you used it with inject: false, then you would need to find a way to "manually" inject the assets generated by Webpack.
So, the manifest.json is not for Webpack. It's for you.
Let's say for example that you have a Python Flask web application and your web pages are built with Jinja templates. You could use Webpack to generate all of your static assets, and use the manifest.json to resolve the asset generated by Webpack. This Flask extension does just that. This means that in your jinja template you can write this:
<img src="{{ asset_for('images/hamburger.svg') }}" alt="Hamburger">
and get this:
<img src="images/hamburger.d2cb0dda3e8313b990e8dcf5e25d2d0f.svg" alt="Hamburger">
Another use case would be if you want fine control where the bundles are injected into your templates. For that, have a look at this example in the html-webpack-plugin repo.

Using CSS in Webpack

I've inherited a web app that uses webpack. In my app, I have a directory called "pub", which looks like this:
./pub
/styles
app.css
/images
brand.png
I have been trying unsuccessfully all morning to use these via webpack. In my webpack.config.js file, I have the following:
const path = require('path');
const projectRoot = path.resolve(__dirname, '../');
module.exports = {
entry: {
app: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
};
Then, in my index.js file, I have the following:
import logoImage from './public/images/brand.png';
require("css!./public/css/app.css");
When I run webpack, I receive an error that says:
BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.
You need to specify 'css-loader' instead of 'css',
see https://webpack.js.org/guides/migrating/#automatic-loader-module-name-extension-removed
I don't really understand this error. When I look at it, and then I look at my webpack.config.js file, it looks to me like I'm using css-loader. Beyond that though, how do I use a style in my webpage once the require statement is working. I'm just trying to use webpack with a web app and want to import my brand and CSS and I can't figure it out.
You don't need the css! in your require statement
require("css!./public/css/app.css");
You can just use
require("./public/css/app.css");
Because you are testing files with:
{
test: /\.css$/, // <-- here
loader: "style-loader!css-loader"
},
Or without the rule in your webpack config
// No test in rules matched but you tell webpack
// explicitly to use the css loader
require("style-loader!css-loader!./public/css/app.css");
Your hierarchy is pub/styles/app.css but the location you use in your require is public/css/app.css. It looks like you're trying to call your css from the wrong location.
If this doesn't solve your issue, check out this link https://webpack.github.io/docs/stylesheets.html
The first step on that page is to install css-loader and configure it, this might be a good place to start.

vueJS + webpack: importing fonts, CSS, and node_modules

I'm starting with Vue.js and Webpack and I'm having some doubts about how to correctly import and reference my fonts, CSS, and node_modules correctly.
I started my application using vue-cli, and here's the resultant structure:
build
config
node_modules
src
--assets
--components
--router
static
And here's my webpack.base.conf file:
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'#': resolve('src')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
First of all, where is the correct place to put my custom CSS and images? I'm currently putting them inside assets/css and assets/img, respectively (I created these folders). Is it correct?
I also have some CSS and fonts from external libraries (Bootstrap and Font Awesome, for example) that I have installed via NPM. They're located at node_modules.
If I'm not wrong, Webpack transforms, and copies these files to another location. How can I reference them on my Vue files and CSS files?
Using import './assets/css/style.css'import '../node_modules/path/to/bootstrap.min.css' works (at least in production), but should I be using another path?
Inside my custom CSS files, I reference some fonts from an external library using:
src: url('/node_modules/roboto-fontface/fonts/Roboto/Roboto-LightItalic.eot')
The code compiles, but when I open the page in the browser, I receive a 404 error when searching for these fonts. How should I be referencing these fonts in my custom CSS?
First of all, where is the correct place to put my custom css and images? I'm currently putting them inside assets/css and assets/img, respectively (I created these folders). Is it correct?
This is kind of a subjective question, but the short answer is yes.
The cli tool already created this for you, defined some stuff in the Webpack config files, so why not use it?
Using import './assets/css/style.css'import '../node_modules/path/to/bootstrap.min.css' works (at least in production), but should I be using another path?
Webpack embeds the css into the it's JS file, so if you don't import it Webpack will not know about it.
Here is an example with loading images dynamically
<ul id="albums">
<li v-for="album in albums">
<img :src="LoadImage(album.data.imagefile)" />
</li>
</ul>
if you'll just hand the src binding the artwork file it will fail to load it, so we hand the image file name to a method that goes like this
LoadImage(filename) {
const image = require('#/assets/img/' + filename)
return image
}
now inside the method we load the image from the assets folder ( using the # notation that was configured in the webpack.base.conf file under resolve.alias )
So yes, using the import/require functions are the way to go for Webpack to get to know your files.
Inside my custom css files, I reference some fonts from an external library using:
src: url('/node_modules/roboto-fontface/fonts/Roboto/Roboto-LightItalic.eot')
The code compiles, but when I open the page in the browser, I recieve a 404 Error when searching for these fonts. How should I be referencing these fonts on my custom css?
It's best that you'll copy everything you want in your dist folder in your src folder. I'm not really sure, never tried it, but looking at the webpack.prod.conf file it looks like it will only copy files from the src/assets folder.
Regarding the font not loading, this is a bit different since the url-loader will handle the files, so you have to think of it from the browser point of view, and reference it like a url path.
here is an something i have in one of my components
#font-face {
font-family: BebasNeue;
src: url('./assets/fonts/BebasNeue.otf');
}
See how i didn't used the # notation to reference it from the src folder? no need for urls.
I'm guessing you already answered this question and if not, Hope that helps!
You can ask questions here http://chat.vuejs.org/ and get answers from the community and the core team.
Quck solution
what i did comment #font-face {} section and build and added custom css to style file

Compiling SCSS into CSS and transpiling ES6 to ES5 simultaneously

So I have a SCSS file called style.scss. And I have an ES6 file called script.js. Both these files are in a ./src/ directory.
I want to use Webpack to take the style.scss, convert it to CSS and stick it into a folder called ./dist/ with the filename style.css. I can actually make that happen.
I also want Webpack to take the script.js, transpile to ES5, and put that in to the same ./dist/ folder with a file called script.js. I can also make that happen.
What I cannot do, is make both happen simultaneously. Is this something that Webpack can even do? In this instance I don't want all my styles putting into my JS, I specifically want them to be kept in separate files like a standard website (as that's what this is, really).
The closest I have got is this:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
script: './src/script.js',
style: './src/style.scss'
},
output: {
path: './dist',
filename: '[name].js'
},
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin('style.css')
] }
But this gives me a script.js file, a style.css file and ALSO a style.js file which is a mess.
Any suggestions?
You don't want to create a separate entry point for the CSS. You can either import the style.scss in your script.js or you can add it to the same entry. An entry can also be an array and webpack will include them in the same bundle, well the CSS is extracted anyway so there won't really be any additional JavaScript. Your entry would be as follows:
entry: {
script: ['./src/script.js', './src/style.scss']
},

Categories

Resources