Webpack: Loading images from html templates - javascript

I'm trying to set up an angular project using Webpack but I can't figure out how to reference images from within html templates and have them included in the build.
My project tree is as follows:
package.json
app/
- images/
- foo.png
- scripts/
- styles/
- templates/
I'm trying to use html-loader along with url-loader and file-loader but it's just not happening.
This is an example template: app/templates/foo.html
<img src="../images/foo.png" />
Problem #1: I would like to be able to reference images relative to app/. Right now, the paths need to be relative to the template file and this will get ugly very quickly (../../../images/foo.png).
Problem #2: Even if I specify the relative path, as I have done above, the project builds successfully but nothing really happens. The paths are left as-is and no images appear in dist/.
Here is my webpack config:
var path = require('path');
var webpack = require('webpack');
var ngminPlugin = require('ngmin-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
module.exports = function(config, env) {
var appRoot = path.join(__dirname, 'app/')
if(!env) env = 'development';
var webpackConfig = {
cache: true,
debug: true,
contentBase: appRoot,
entry: {
app: path.join(appRoot, '/scripts/app.coffee')
},
output: {
path: path.join(__dirname, 'dist/),
publicPath: '/',
libraryTarget: 'var',
filename: 'scripts/[name].[hash].js',
chunkFilename: '[name].[chunkhash].js'
},
module: {
loaders: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader?outputStyle=expanded&includePaths[]=./node_modules/foundation/scss/')
},
{
test: /\.coffee$/,
loader: 'coffee-loader'
},
{
loader: 'ngtemplate?relativeTo=' + (path.resolve(__dirname, './app')) + '/!html'
},
{
test: /\.png$/, loader: "url-loader?limit=100000&mimetype=image/png&name=[path][name].[hash].[ext]"
},
{
test: /\.jpg$/, loader: "file-loader?name=[path][name].[hash].[ext]"
},
{
test: /\.(woff|woff2)(\?(.*))?$/,
loader: 'url?prefix=factorynts/&limit=5000&mimetype=application/font-woff'
},
{
test: /\.ttf(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.eot(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.svg(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.json$/,
loader: 'json'
}
]
},
resolve: {
extensions: [
'',
'.js',
'.coffee',
'.scss',
'.css'
],
root: [appRoot],
},
singleRun: true,
plugins: [
new webpack.ContextReplacementPlugin(/.*$/, /a^/),
new webpack.ProvidePlugin({
'_': 'lodash'
}),
new ExtractTextPlugin("styles/[name].[chunkhash].css", {allChunks: true}),
new HtmlWebpackPlugin({
template: appRoot + '/app.html',
filename: 'app.html',
inject: 'body',
chunks: ['app']
})
],
devtool: 'eval'
}
if(env === 'production') {
webpackConfig.plugins = webpackConfig.plugins.concat(
new ngAnnotatePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.DefinePlugin({
'process-env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin()
);
webpackConfig.devtool = false;
webpackConfig.debug = false;
}
return webpackConfig;
}

If you are using HTML templates in Webpack 2, in addition to use the file-loader you need to change in your HTML:
<img src="../images/foo.png" />
to this
<img src=<%=require("../images/foo.png")%> />

Yes, you will have to do so for loading images from different path.
I had similar issue and I resolved this using file loader:
.
loaders: [{
// JS LOADER
test: /\.js$/,
loader: 'babel-loader?optional[]=runtime',
exclude: /node_modules/
}, {
// ASSET LOADER
test: /\.(woff|woff2|ttf|eot)$/,
loader: 'file-loader'
},
{
//IMAGE LOADER
test: /\.(jpe?g|png|gif|svg)$/i,
loader:'file-loader'
},
{
// HTML LOADER
test: /\.html$/,
loader: 'html-loader'
},
{
//SCSS LOADER
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader?indentedSyntax"]
}
]
Good Luck

For Webpack 5 you have to write this way:
module: {
rules: [
// other stuff above.....
{
test: /\.html$/,
use: [
{
loader: 'html-loader'
}
]
}
]
}

With Webpack 4, I was able to solve this problem by updating by html-loader to specify the root for my files. So given #syko's original structure; in webpack.config.js...
module: {
rules: [
// other stuff above.....
{
test: /\.html$/,
use: [
// ...The other file-loader and extract-loader go here.
{
loader: 'html-loader'
options: {
// THIS will resolve relative URLs to reference from the app/ directory
root: path.resolve(__dirname, 'app')
}
}
]
}
]
}
This tells the html-loader to interpret all absolute urls from the /app folder. So in our app/templates/foo.html, you can then use the following...
<img src="/images/foo.png" />
This then tells html-loader to see the above as path.resolve(__dirname, 'app', 'images', 'foo.png'). Then if you have extract-loader and/or file-loader, all the paths should be correct.
Managed to get this solution after hammering away at it for a while. The biggest confusion is where about in the loader stack the /images/foo.png is interpreted, in this case it starts at the html-loader plugin. Everything else after that, is more about how the image files are to be published.
Hope that helps.

You can use file-loader to extract images. Then using html-loader you can specify which tag-attribute combination should be processed by this loader via the query parameter attrs.
I could make it work with this configuration:
{
test: /\.(jpe?g|png|gif|svg|ico)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}]
},
{
test: /\.(html)$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'link:href']
}
}
}
Or in angular something like this:
attrs: [':ng-src', ':ng-href']

Related

Webpack url() path resolution with css-loader

I am developing a site and using webpack for obvious reasons. The problem I am having is with path resolution for images which are imported into my project via my SCSS files. The issue is that css-loader isn't resolving the correct path. What seems to be happening is the following:
If I allow css-loader to handle the url() imports (leaving the url option to true) it rewrites the file path relative to the output directory specified in ExtractCSSChunksPlugin(), for example:
url('../img/an-image.jpg') should be rewritten to url('http://localhost:3000/assets/img/an-image.jpg'), however, what is actually being outputted is url('http://localhost:3000/assets/css/assets/img/an-image.jpg').
If I change it to false the correct path is resolved but the file-loader isn't able to find the images and then emit them.
I know that the images are being outputted when the css-loader is handling url resolution as I can see the emitted message when the bundle is compiled -- it does not fail.
I can also get the images to display if I manually add import calls to them in the JS entry point, set in the entry: field, and then call the absolute path in SCSS. But this is not desirable as it becomes tedious with the growing project.
I have tried to use resolve-url-loader and changing multiple settings but I just can't seem to get this to work.
I have also tried using the resolve: { alias: { Images: path.resolve(__dirname, 'src/assets/img/' } } option provided by webpack and then calling url('~Images/an-image.jpg') in my SCSS but it just reproduces the same results.
So, overall, my issue is that I need to be able to use relative paths in my SCSS and then have them rewritten to the correct path by one of my loaders.
My current webpack config (outputting the files with file loader but prepending assets/css/ to the start of the url) is as follows:
"use strict";
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
const ExtractCSSChunksPlugin = require('extract-css-chunks-webpack-plugin');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
entry: [
'webpack-hot-middleware/client',
],
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
}
}
},
{
test: /\.scss$/,
use: [
{
loader: ExtractCSSChunksPlugin.loader,
options: {
hot: true,
}
},
{
loader: 'css-loader',
options: {
sourceMap: true,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
}
}
]
},
{
test: /\.html$/,
use:['html-loader']
},
{
test:/\.(svg|jpg|png|gif)$/,
use: [{
loader:'file-loader',
options: {
publicPath: 'assets/img',
outputPath: 'assets/img',
name: '[name].[ext]',
esModule: false
}
}],
},
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ExtractCSSChunksPlugin({
filename: 'assets/css/[name].css',
chunkFilename: 'assets/css/[id].css',
}),
]
});
Thank you in advance.
Ok, so it seems I have fixed the issue by resolving the publicPath set in the file loader config field: publicPath: path.resolve(__dirname, '/assets/img').
My config is now:
"use strict";
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
const path = require('path');
const ExtractCSSChunksPlugin = require('extract-css-chunks-webpack-plugin');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
entry: [
'webpack-hot-middleware/client',
],
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
}
}
},
{
test: /\.scss$/,
use: [
{
loader: ExtractCSSChunksPlugin.loader,
options: {
hot: true,
}
},
{
loader: 'css-loader',
options: {
sourceMap: true,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
}
}
]
},
{
test: /\.html$/,
use:['html-loader']
},
{
test:/\.(svg|jpg|png|gif)$/,
use: [{
loader:'file-loader',
options: {
publicPath: path.resolve(__dirname, '/assets/img'),
outputPath: 'assets/img',
name: '[name].[ext]',
esModule: false
}
}],
},
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ExtractCSSChunksPlugin({
filename: 'assets/css/[name].css',
chunkFilename: 'assets/css/[id].css',
}),
]
});
I think adding url loader in the webpack configuration would help.
{
test: /\.(jpg|png)$/,
use: {
loader: "url-loader",
options: {
limit: 25000,
},
},
},

webpack 4 gives background: url([object Module]) as bg image

I'm having issues with setting up web-pack 4 and svg-sprite-loader to render svg icons as background images. I was following these instructions from official docs for svg-sprite-loader (https://github.com/kisenka/svg-sprite-loader/tree/master/examples/extract-mode).
I have successfully managed to create sprite.svg file in my dist folder and use it as reference for my use tags inside of html. However, i was also trying to use svg icons from my src/images/icons folder for a background image like this:
background: url('../images/icons/upload_icon.svg') 10% 50% no-repeat;
when doing this, webpack compiles correctly, but creates this in dist css file:
background: url([object Module]) 10% 50% no-repeat;
Any help would be great.
here is my webpack config file:
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
module.exports = {
mode: "development",
devtool: "source-map",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
sourceMap: true
}
}
},
{
// scss configuration
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader"
},
{
loader: "postcss-loader"
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
},
{
// html configuration
test: /\.html$/,
use: {
loader: "html-loader"
}
},
{
// images configuration
test: /\.(jpg|jpeg|gif|png|woff|woff2)$/,
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]"
}
}
]
},
{
test: /\.svg$/,
use: [
{
loader: "svg-sprite-loader",
options: {
extract: true,
spriteFilename: "sprite.svg"
}
}
]
}
]
},
plugins: [
// all plugins used for compiling by webpack
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Style Guide",
template: path.resolve(__dirname, "src", "index.html")
}),
new MiniCssExtractPlugin({
filename: "app.css"
}),
new SpriteLoaderPlugin()
]
};
Adding esModule: false to the file-loader options did the trick for me.
{
test: /\.(jpg|png|gif|svg)$/,
use: {
loader: 'file-loader',
options: {
name: "[name].[ext]",
outputPath: "img",
esModule: false
}
},
You have to pass esModule: false for svg-sprite-loader options.
By the way (it is not related to esModule): With MiniCssExtractPlugin you can not to extract svg sprite. I've faced this problem one hour ago..
After a few hours, I have managed to make this thing to work, thanks to #WimmDeveloper for pointing me in right direction. Main change from prior webpack config file is that I have added esModule: false in svg-sprite-loader options and replaced MiniCssExtractPlugin with extract-text-webpack-plugin. Mind you that this solution is not ideal since this webpack plugin is deprecated.
here is my full webpack config file:
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
mode: "development",
devtool: "source-map",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
sourceMap: true
}
}
},
{
test: /\.(s*)css$/,
use: ExtractTextPlugin.extract({
use: ["css-loader", "postcss-loader", "sass-loader"]
})
},
{
// html configuration
test: /\.html$/,
use: {
loader: "html-loader"
}
},
{
test: /\.svg$/,
use: [
{
loader: "svg-sprite-loader",
options: {
esModule: false,
extract: true,
spriteFilename: "sprite.svg"
}
}
]
},
{
// files configuration
test: /\.(jpg|jpeg|gif|png|woff|woff2)$/,
use: [
{
loader: "file-loader",
options: {
name: "[path][name].[ext]"
}
}
]
}
]
},
plugins: [
// all plugins used for compiling by webpack
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Style Guide",
template: path.resolve(__dirname, "src", "index.html")
}),
new ExtractTextPlugin({ filename: "app.css" }),
new SpriteLoaderPlugin()
]
};

Webpack cannot find properly path for slick files

I started to using a webpack bundler, and I got a little issue with slick slider.
Slick fonts and gif doesn't load properly
It looks like webpack skips main folder, where my project is, and looking for files in d:/node_modules instead of d:/main_folder/node_modules
I was trying to fix this by creating slick-fix.scss and set font and loader path
but it doesn't work properly
This is the error screenshot:
slick-fix.scss
$slick-loader-path: "/node_modules/slick-carousel/slick/" !default;
$slick-font-path: "/node_modules/slick-carousel/slick/fonts/" !default;
main.scss
#import "slick-fix";
#import "~slick-carousel/slick/slick.scss";
#import "~slick-carousel/slick/slick-theme.scss";
main.js
require('./styles/main.scss');
require('jquery');
import 'slick-carousel/slick/slick';
$('.slider').slick();
const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');
const $ = require('jquery');
module.exports = {
context: path.resolve(__dirname),
entry: './src',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: { minimize: true }
}
]
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
}
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.$': 'jquery',
'window.jQuery': 'jquery'
})
]
}
You cannot access local paths from your http server. In other words, you cannot load files from your harddrive directly. They have to be served by the server.

CSS not loading instantly in webpack project

Here is the image which shows the first time of loading
After microseconds, everything is fine such as design, javascript, images, etc...
Here is my webpack.config.js:
const path = require('path');
const webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: { minimize: true }
}
]
},
{
test: /\.(scss|css)$/,
use: [
{
// Adds CSS to the DOM by injecting a `<style>` tag
loader: 'style-loader'
},
{
// Interprets `#import` and `url()` like `import/require()` and will resolve them
loader: 'css-loader'
},
{
// Loader for webpack to process CSS with PostCSS
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
},
{
// Loads a SASS/SCSS file and compiles it to CSS
loader: 'sass-loader'
},
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "./index.html"
})
]
};
I don't know what I'm missing but I know definitely missing something but not indicating. I tried but didn't get it to work.
I'd really appricitate if could help me resolve this issue.
Thanks
Apparently CSS always loads after JS. Have a look at this issue.
You could also set display: none to your body or main div and change it in the CSS. It would take the same time to load, but you wouldn't see ugly styled content during those microseconds.

webpack not loading svgs

I have the following app structure:
/webapp
/lib
/assets
ic_add_black_24px.svg
ic_clear_black_24px.svg
..
..
Here is my webpack.config.js:
var path = require('path'),
webpack = require("webpack"),
libPath = path.join(__dirname, 'lib'),
wwwPath = path.join(__dirname, 'www'),
pkg = require(path.join(__dirname,'package.json')),
HtmlWebpackPlugin = require('html-webpack-plugin');
var config = {
entry: path.join(libPath, 'index.js'),
output: {
path: path.join(wwwPath),
filename: 'bundle-[hash:6].js'
},
module: {
loaders: [{
test: /\.html$/,
loader: 'file?name=templates/[name]-[hash:6].html'
}, {
test: /\.(png|jpg|svg)$/,
loader: 'file-loader?name=assets/[name].[ext]' // inline base64 URLs for <=10kb images, direct URLs for the rest
}, {
test: /\.css$/,
loader: "style!css"
}, {
test: /\.scss$/,
loader: "style!css!autoprefixer!sass"
}, {
test: /\.js$/,
exclude: /(node_modules)/,
loader: "ng-annotate?add=true!babel"
}, {
test: [/fontawesome-webfont\.svg/, /fontawesome-webfont\.eot/, /fontawesome-webfont\.ttf/, /fontawesome-webfont\.woff/, /fontawesome-webfont\.woff2/],
loader: 'file?name=fonts/[name].[ext]'
}]
},
plugins: [
// HtmlWebpackPlugin: Simplifies creation of HTML files to serve your webpack bundles : https://www.npmjs.com/package/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
pkg: pkg,
template: path.join(libPath, 'index.html')
}),
// OccurenceOrderPlugin: Assign the module and chunk ids by occurrence count. : https://webpack.github.io/docs/list-of-plugins.html#occurenceorderplugin
new webpack.optimize.OccurenceOrderPlugin(),
// Deduplication: find duplicate dependencies & prevents duplicate inclusion : https://github.com/webpack/docs/wiki/optimization#deduplication
new webpack.optimize.DedupePlugin()
]
};
module.exports = config;
Here is how I am using the svg asset in one of my html file:
<md-card-header>
<span flex></span>
<md-button class="md-icon-button" aria-label="remove condition" style="background-color: #DCD8D8" ng-click="event.removeCondition(condition)">
<md-icon md-svg-src="/lib/assets/ic_clear_black_24px.svg"></md-icon>
</md-button>
</md-card-header>
When I do rm -rf www/* && webpack -p, it creates the bundle successfully, but without any assets loaded.. I have tried to use svg-loader, url-loader, file, but none of them works.. What am I doing wrong here?
In case it helps anyone, I ended up using CopyWebpackPlugin to load the assets manually to my required location. This is what my webpack.config looks like now:
var path = require('path'),
webpack = require("webpack"),
libPath = path.join(__dirname, 'lib'),
wwwPath = path.join(__dirname, 'www'),
pkg = require(path.join(__dirname,'package.json')),
CopyWebpackPlugin = require('copy-webpack-plugin'),
HtmlWebpackPlugin = require('html-webpack-plugin');
var config = {
entry: path.join(libPath, 'index.js'),
output: {
path: path.join(wwwPath),
filename: 'bundle-[hash:6].js'
},
module: {
loaders: [{
test: /\.html$/,
loader: 'file?name=templates/[name]-[hash:6].html'
}, {
test: /\.(png|jpg|svg)$/,
loader: 'svg-url-loader?name=assets/[name].[ext]' // inline base64 URLs for <=10kb images, direct URLs for the rest
}, {
test: /\.css$/,
loader: "style!css"
}, {
test: /\.scss$/,
loader: "style!css!autoprefixer!sass"
}, {
test: /\.js$/,
exclude: /(node_modules)/,
loader: "ng-annotate?add=true!babel"
}, {
test: [/fontawesome-webfont\.svg/, /fontawesome-webfont\.eot/, /fontawesome-webfont\.ttf/, /fontawesome-webfont\.woff/, /fontawesome-webfont\.woff2/],
loader: 'file?name=fonts/[name].[ext]'
}]
},
plugins: [
new CopyWebpackPlugin([{
from: 'lib/assets',
to: wwwPath + '/lib/assets'
}]),
// HtmlWebpackPlugin: Simplifies creation of HTML files to serve your webpack bundles : https://www.npmjs.com/package/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
pkg: pkg,
template: path.join(libPath, 'index.html')
}),
// OccurenceOrderPlugin: Assign the module and chunk ids by occurrence count. : https://webpack.github.io/docs/list-of-plugins.html#occurenceorderplugin
new webpack.optimize.OccurenceOrderPlugin(),
// Deduplication: find duplicate dependencies & prevents duplicate inclusion : https://github.com/webpack/docs/wiki/optimization#deduplication
new webpack.optimize.DedupePlugin()
]
};
module.exports = config;

Categories

Resources