Loading SVG with Webpack5 - html-webpack-plugin

I am trying to migrate our package from Webpack 4 to Webpack 5 but have issue with svg loading.
Previously, we use HtmlWebpackInlineSourcePlugin and raw-loader in our webpack config.
plugins: [
new HtmlWebpackPlugin({
inlineSource: '.(js|css)$',
template: <>,
filename: <>,
inject: 'head',
}),
new HtmlWebpackInlineSourcePlugin(),
]
...
module: {
rules: [
{
test: /\.(svg)$/,
loader: 'raw-loader',
},
Here is the html file:
<button id="button" type="button">
${require('../../node_modules/open-iconic<link to svg>').default}
</button>
Expected output:
<button id="button" type="button">
<svg ...>
<path/>
</svg>
</button>
Both of HtmlWebpackInlineSourcePlugin and raw-loader are deprecated in Webpack 5. For HtmlWebpackInlineSourcePlugin, I replaced with InlineChunkHtmlPlugin from react-dev-utils and for raw-loader I tried to follow the asset module guide but it did not work (the import statement is not updated).
plugins: [
new HtmlWebpackPlugin({
inlineSource: '.(js|css)$',
template: <>,
filename: <>,
inject: 'head',
}),
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [new RegExp(`<output name>`)]),,
]
...
module: {
rules: [
{
test: /\.(svg)$/,
type: 'asset/source'
},
Does anyone know if I miss anything?

Related

i am not able to render images with pug view template

doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(http-equiv="X-UA-Compatible", content="IE=edge")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title Document
body
div
img(src='./tree.jpg' alt='login' style='width:100px; height:100px;')
#form
form(action="/" method="post")
label(for="name") name
input(type="text" name="name" id="name" placeholder="okay")
input(type="submit" value="dingoo")
Install the pug-loader:
npm install #webdiscus/pug-loader --save-dev
In webpack config module add following rules:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.pug$/,
loader: '#webdiscus/pug-loader'
},
{
test: /\.(png|jpg|jpeg|svg|ico)/,
type: 'asset/resource',
generator: {
filename: 'assets/images/[name].[hash:8][ext]',
},
},
]
},
}
In pug use require() for image:
img(src=require('./path/to/tree.jpg)')
Generated HTML:
<img src="/assets/images/tree.23fe5de2.jpg">
This works fine with the #webdiscus/pug-loader.

Does the url-loader plugin inline URLs in <img> tags, how?

Udate:
Although I haven't been able to find out whether this is a feature or a bug. It seems url-loader can't process the assets used in the Svelte component unless those are loaded via require. So I would like to know what's the more common or recommended way to load/preprocess, using Webpack 4, a Svelte component source so that all image assets used in src/url in HTML tags and CSS styles get inlined, i.e. converted to use the embedded base64 data in the HTML or CSS output files.
Original Question
I want to have a tag <img src="./assets/logo.png"> in a svelte component to be converted to <img src="data:image/png;base64,iV...CC"> in the output, but if I add url-loader to my webpack.config.js file like so:
module: {
rules: [
//...
{
test: /logo.png/,
loader: (process.stdout.write("!!!\n"), 'url-loader')
}
The URL in src does still appear as "./assets/logo.png" in the output even is the console shows "!!!" during the webpack build process with no errors, why is url-loader not making the conversion? The file logo.png is about 3KB in size, but I'm not sure is that's the problem since it's small in size.
The image is being used here like so:
<div id="app">
{#if path === '/'}
<span on:click={navigateToSettings} id="menu"><div class="hamburger" /></span>
{/if}
{#if path === '/settings' && isStored()}
<span on:click={cancel} id="menu"><div class="close" /></span>
{/if}
<img class='logo' src='./assets/img/logo.png' alt='Spark'>
{#if connected}
<span id="status" class="green">•</span>
{:else}
<span id="status" class="red">•</span>
{/if}
<div id="content">
<RouterView />
</div>
</div>
And I'm adding the url-loader rule here before the rule for audio files:
module: {
rules: [
//...
{
test: /logo.png/,
loader: (process.stdout.write("!!!\n"), 'url-loader')
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader'
},
If you use webpack and url-loader with any of modern frontend frameworks it is a common practice to use images via require.
Example from react world:
import iconMore from “../path_to_image/name_of_image.jpg”;
const Icon = (props) => {
return <img src={iconMore} />
}
for more examples please see this question.
Also I found svelte-assets-preprocessor - you can use it in pair with url-loader without direct require, but under the hood it is the same technique:
module: {
rules: [
...
{
test: /\.(png|svg|jpg|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'images',
}
},
{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: {
loader: 'svelte-loader',
options: {
preprocess: require('svelte-assets-preprocessor')({ /* options */ exclude: [ (attr) => !/\.(png|svg|jpg|gif)$/.test(attr)} ])
},
},
},
...
]
}
Input
<img src="./example.png">
Output
<script>
import ___ASSET___1 from './example.png';
</script>
<img src="{___ASSET___1}">

URIError: Failed to decode param '/%PUBLIC_URL%/favicon.ico'

I am new to webpack and I got the babel loader and css loader to work and project compiles successfully but when I try to access via browser I get the below error. It looks as if PUBLIC_URL is not recognized. I believe I don't know how to configure this.
I appreciate your valuable comments.
Thanks
ℹ 「wdm」: Compiled successfully. URIError: Failed to decode param
'/%PUBLIC_URL%/favicon.ico' at decodeURIComponent (<anonymous>) at
decode_param (/home/mike/finance-
grapher/node_modules/express/lib/router/layer.js:172:12) at Layer.match
(/home/mike/finance-
grapher/node_modules/express/lib/router/layer.js:123:27) at matchLayer
(/home/mike/finance-
grapher/node_modules/express/lib/router/index.js:574:18) at next
(/home/mike/finance-
grapher/node_modules/express/lib/router/index.js:220:15) at expressInit
(/home/mike/finance-
grapher/node_modules/express/lib/middleware/init.js:40:5) at Layer.handle
[as handle_request] (/home/mike/finance-
grapher/node_modules/express/lib/router/layer.js:95:5) at trim_prefix
(/home/mike/finance-
grapher/node_modules/express/lib/router/index.js:317:13) at
/home/mike/finance-grapher/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/mike/finance-
grapher/node_modules/express/lib/router/index.js:335:12)
Webpack.config.js
.babelrc
package.json
project folder structure
Quick fix
What if you were to replace %PUBLIC_URL% with the actual path. I think that Babel is having issues transpiling the %. Try replacing %PUBLIC_URL%/favicon.ico with /public/favicon.ico and the issue is resolved.
Better fix
Add a new rule to your webpack.config.js.
//...
{
test: /\.(png|svg|jpg|jpeg|gif|ico)$/,
exclude: /node_modules/,
use: ['file-loader?name=[name].[ext]'] // ?name=[name].[ext] is only necessary to preserve the original file name
}
//...
Then have the .ico resource copied to the dist directory by adding an import in your App.js. import '../public/favicon.ico';
In your index.html; <link rel="shortcut icon" href="favicon.ico"> to make use of your icon. No longer need to provide a path since it will be copied to the dist directory
OR:
In addition to the rule added to the webpack.config.js mentioned above, adding plugins to the webpack config may be a better way to go depending on your setup.
For me this looks like adding the npm package html-webpack-plugin to the project. Then requiring it in the webpack config; const HtmlWebpackPlugin = require('html-webpack-plugin');. Then adding plugins to the module.exports.
//...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: './index.html',
favicon: './public/favicon.ico'
})
]
//...
Going this route and doing the work in the webpack config means the line added to the App.js to import the favicon.ico will no longer be necessary.
EDIT: As mentioned by #Tolumide
Don't forget to configure the webpack.config appropriately per environment.
I had the same issue and fixed it with the following:
Inside webpack.config.js in the plugins array, add HtmlWebpackPlugin and InterpolateHtmlPlugin
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw)
This is the documentation of InterpolateHtmlPlugin
Makes some environment variables available in index.html.
The public URL is available as %PUBLIC_URL% in index.html, e.g.:
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
In production, it will be an empty string unless you specify "homepage"
in `package.json`, in which case it will be the pathname of that URL.
In development, this will be an empty string.
Problem fixed
step 1)
remove %PUBLIC_URL% with the actual path. %PUBLIC_URL%/favicon.ico with favicon.ico
Before <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
After <link rel="icon" href="favicon.ico" />
step 2) add this rule to the webpack.config.js
plugins: [new HtmlWebpackPlugin({ template: path.resolve(__dirname, "public", "index.html"),
favicon: "./public/favicon.ico",
filename: "index.html",
manifest: "./public/manifest.json",
})]
step 3) add svg support in webpack(important)
install svg-url-loader package
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10000,
},
},
],
}
Solution
npm install interpolate-html-plugin --save-dev
Add to list of plugins in webpack config
new InterpolateHtmlPlugin({PUBLIC_URL: 'static })
<link href="<%= htmlWebpackPlugin.options.favicon %>" rel="shortcut icon">
and
new HtmlWebpackPlugin({
favicon: "image/favicon.ico",
})
and
{
test: /\.(jpe?g|gif|png|ico)$/,
use: ['file-loader?name=[name].[ext]']
},
I was getting this error from create-react-app when I was serving the page from express server. It was because I was serving static pages from public folder instead of build folder.
Not working:
app.use('/', express.static(path.join(__dirname, '../public')));
Working
app.use('/', express.static(path.join(__dirname, '../build')));
Installing HtmlWebpackPlugin then import the plugin into webpack.config.js and add this into plugins section worked for me.
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: './index.html',
favicon: './public/favicon.png'
}),
]

"You may need an appropriate loader for this file type", webpack can't parse angular2 file

I'm trying to get a very simple Angular2 app working, with Webpack as a module bundler. I'm following this code, and I copied all the configuration files as they are, only changing file paths. However, when I run npm-start, I get the following error, which I think is a Webpack error:
ERROR in ./hello.js
Module parse failed: /home/marieficid/Documentos/cloud/cloud/hello.js Line 1: Unexpected token
You may need an appropriate loader to handle this file type.
| import {bootstrap} from "angular2/platform/browser";
| import {Component} from "angular2/core";
|
# ./app.ts 2:0-21
As a result, the Angular2 code in my app isn't loaded.
This is my app.ts:
import "./hello.js";
This is hello.js, where the error seems to be (which I take to mean that webpack parsed app.ts just fine):
import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
#Component({
selector: 'app',
template: '<div>Hello world</div>'
})
class App{}
bootstrap(App);
And this iswebpack.config.js:
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
entry: {
'app': './app.ts',
'vendor': './vendor.ts'
},
output: {
path: "./dist",
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
new HtmlWebpackPlugin({
inject: false,
template: './index.html'
})
],
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
],
noParse: [ path.join(__dirname, 'node_modules', 'angular2', 'bundles') ]
},
devServer: {
historyApiFallback: true
}
};
All these files and node_modules are in the same directory.
I have found similar questions online but nothing worked for me. I also didn't install babel because the sample code I'm using as base doesn't use it, but if it's necessary I'm will.
As suggested by #napstablook
Since in your webpack.config.js file you have
resolve: {
extensions: ['', '.ts', '.js']
},
Webpack will try to handle those .js files but it needs a specific loader to do so which is, if I'm not wrong, script-loader.
In your case the solution is as simple as deleting the .js files, or changing their extension to be .ts.
For me this issue occurred when I ran ng test,
please check below points,
Console will list out the files that is causing the error.
Check the html file is correctly mapped from the typescript.
styleUrls file should point to the CSS file not html, this is the mistake I
did.
this error also comes up for me in angular forms when i had patch value set then an extra = sign
ncont.controls[position].patchValue({[cardname]:file}) = file
which is a dumb part on me and angular for not telling me

RequireJS + Optimizer Include modules defined on the main file

I'm using Optimizer for the first time and I am running in some issues or questions.
I'm trying to optimize a main file and it puts, like I've expected, the jQuery, Backbone and Require modules ( and uses then across the whole navigation). But let's say I have a jQuery Plugin that I use on several views. I've tried to add it in the main file using the "include" option on the build.js file. It adds it ( e.g jQuery Slides ) but as I have a view with define("jquery-slides") ( again, an example ) the browser loads the file of the plugin again. Even if it is on the main built file.
Is this suppose to happen? Can I fix this?
Thanks.
Here is some code. Hope it helps =)
build.js
{
baseUrl: "javascripts/",
appDir: "..",
dir: "dist",
name: "main-site",
include: ['libs/requirejs/require', jquery-slides'],
insertRequire: ['main-site'],
paths: {
"main-site": 'main-site',
'jquery': 'libs/jquery/jquery',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
}
}
main-site.js
require.config({
baseUrl: "/javascripts/",
paths: {
'jquery': 'libs/jquery/jquery',
'underscore': 'libs/underscore/underscore',
'bootstrap': 'libs/bootstrap/bootstrap.min',
'datepicker': 'libs/bootstrap/plugins/bootstrap-datepicker',
'backbone': 'libs/backbone/backbone.max',
'backbone-paginator': 'libs/backbone/plugins/backbone.paginator',
'backbone-validation': 'libs/backbone/plugins/backbone.validation',
'text': 'libs/requirejs/text',
'templates': '/templates/site',
'views': 'views/site',
'jquery-cookie': 'libs/jquery/plugins/jquery.cookie',
'jquery-raty': 'libs/jquery/plugins/jquery.raty.min',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
},
shim: {
'backbone-paginator': ['backbone'],
'bootstrap': ['jquery'],
'datepicker': ['bootstrap'],
'jquery-cookies': ['jquery'],
'jquery-raty': ['jquery'],
'jquery-slides': ['jquery'],
'backbone-validation': ['backbone']
}
});
require([
'app-site'
], function(App) {
$(function(){
App.initialize();
});
});
Instead of using include I recommend you to declare the modules you want to build. In this way requirejs will package the module and all its dependencies in the optimized bundle.
{
baseUrl: "javascripts/",
appDir: "..",
dir: "dist",
paths: {
"main-site": 'main-site',
'jquery': 'libs/jquery/jquery',
'jquery-slides': 'libs/jquery/plugins/slides.min.jquery'
},
modules : [
{
name : 'main-site',
}
]
}
Further considerations:
If you have jquery-slides included as a dependency in any of your modules define(['jquery-slides'], function() {... } you don't need to use the include directive since all the dependencies of that module will be included in the optimized file
See the documentation of the modules property in this link
https://github.com/jrburke/r.js/blob/master/build/example.build.js#L330
Use the property mainConfigFile to avoid duplications https://github.com/jrburke/r.js/blob/master/build/example.build.js#L35
Good luck and I hope this helps you

Resources