Configure webpack to output images/fonts in a separate subfolders
WebpackWebpack Problem Overview
I managed configure webpack to output CSS and JS into respective sub-directories, i.e. public/asests/css
and public/assets/js
. However, I don't know how to do the same for images and fonts.
In other words, I want to output images in public/assets/images
folder and fonts into public/assets/fonts
folder.
Here's my webpack config file:
var path = require('path');
var ExtractCSS = require('extract-text-webpack-plugin');
module.exports = {
context: path.resolve('private/js'),
resolve: ['', '.js', '.jsx', '.es6', '.json'],
entry: {
homepage: './homepage'
},
output: {
path: path.resolve('public/assets'),
publicPath: '/public/assets/',
filename: 'js/[name].js'
},
plugins: [
new ExtractCSS('css/[name].css')
],
devServer: {
contentBase: 'public'
},
module: {
loaders: [
{
test: /\.(es6|js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.css$/,
exclude: /node_modules/,
loader: ExtractCSS.extract('style-loader', 'css-loader')
},
{
test: /\.less$/,
exclude: /node_modules/,
loader: ExtractCSS.extract('style-loader', 'css-loader!less-loader')
},
{
test: /\.(jpg|jpeg|gif|png|woff|woff2|eot|ttf|svg)$/,
exclude: /node_modules/,
loader: 'url-loader?limit=1024'
}
]
}
}
Webpack Solutions
Solution 1 - Webpack
I could figure this out by referring to [url-loader][1] & [file-loader][2] documentation on GitHub.
All, I needed to do was to add a name query-string parameter in loader to specify full path. I also learned that you can specify how files should be named in output location.
{
test: /\.(jpg|jpeg|gif|png)$/,
exclude: /node_modules/,
loader:'url-loader?limit=1024&name=images/[name].[ext]'
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
exclude: /node_modules/,
loader: 'url-loader?limit=1024&name=fonts/[name].[ext]'
}
[1]: https://github.com/webpack/url-loader "url-loader" [2]: https://github.com/webpack/file-loader "file-loader"
Solution 2 - Webpack
{
test: /\.(ttf|eot|svg|woff2?)(\?v=[a-z0-9=\.]+)?$/i,
include: folders.npm,
loader: 'file?name=fonts/[name].[ext]'
},
{
test: /\.(jpe?g|png|gif|svg|ico)$/i,
include: folders.src,
loaders: [
'file?name=images/[sha512:hash:base64:7].[ext]',
'image-webpack?progressive=true&optimizationLevel=7&interlaced=true'
]
}
This is what I use in production. I often come across situation where *.svg pictures are used and SVG fonts for IE fallback. Here I assume font are always inside node_modules. I have also seen devs doing test: /fonts\/[w+].(svg|eot ....)
.
Solution 3 - Webpack
Webpack 5 case-
Keeping it here just in case someone is looking for a Webpack 5 solution to the requirement of outputting image/font assets into separate directories into the output path.
Webpack 5 replaces raw-loader
, url-loader
and file-loader
with Assets Module and it has 4 new modules to handle assets. Read in more details here - https://webpack.js.org/guides/asset-modules/
To our problem statement, the following configuration outputs all fonts/svgs into the directory fonts
. But if certain files are less than 8KB, it throws them inline.
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset', // <-- Assets module - asset
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8kb
}
},
generator: { //If emitting file, the file path is
filename: 'fonts/[hash][ext][query]'
}
}
Similarly we could do the same for images -
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource', //<-- Assets module - asset/resource
generator: {
filename: 'images/[hash][ext][query]'
}
}
Outputs all images to images
directory irrespective of file size.
Solution 4 - Webpack
I was able to solve this with file-loader
If you're using the Webpack 4, you would use use
instead of loader
.
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader?name=fonts/[name].[ext]']
}
Solution 5 - Webpack
Configure file/url-loader output path & file name
You can read in the official file-loader documentation: there's a name
parameter in the configuration object, where a path can be specified.
This would do the job:
// ...
{
test : /\.(woff|woff2|ttf|eot)$/,
loader : 'url-loader',
options: {
limit : 70000,
name: 'fonts/[name].[ext]'
}
},
// ...
I suggest you to take a look at the available placeholders, to know what can be used in a file name.
Loaders usually have a configuration object available, even if you can pass the options like other devs suggested ( loader:'url-loader?limit=1024&name=images/[name].[ext]'
)
I'd prefer using a plain object configuration, to have something more readable, and easier to configure/debug as well
Output path
There's an outputPath
(optional) configuration param, in case one wants to override the default output path, which in any case must be configured (check the docs).
I'm pretty sure that webpack for it's loaders will use the default output dir. In my project I've got this:
// ...
output: {
path: helpers.publicPath(), // local output path: <project-root>/public
publicPath, // same path on the server: /myapp/public
filename: 'js/[name].bundle.js', // default output configuration for js
chunkFilename: 'js/[name].chunk.js'
},
// ...
Note
I'm not sure that you need to exclude the node_modules
folder: in case that libraries like font-awesome or material-icons are used, then you need node_modules to be included. Webpack will automatically leave out files that are not useful to make the project build & run.
I would only exclude folders containing old legacy scripts that are not using imports and stuff like that, and I don't want to be considered by webpack in any case.