First, please note that I'm new to webpack and this is my first project with it.
I'm trying to include a simple webfont into my webpack application, but have difficulties to see it on my page.
My architecture looks like this :
|-- app
| |-- images
| | `-- icons
| |-- index.html
| |-- index.js
| |-- scripts
| `-- styles
| |-- fonts
| | |-- HEINEKEN Core.eot
| | |-- HEINEKEN Core.otf
| | |-- HEINEKEN Core.svg
| | |-- HEINEKEN Core.ttf
| | |-- HEINEKEN Core.woff
| |-- index.styl
| |-- _fonts.styl
|-- package.json
|-- README.md
`-- webpack.config.js
I use the stylus-loader
, and both style-loader
and css-loader
for my CSS :
{
test: /\.styl$/,
exclude: /node_modules/,
loader: [
'style-loader',
'css-loader' + (!production ? '?sourceMap' : ''),
'postcss-loader',
'stylus-loader'
].join('!')
}
and here's what I tried to include the "HEINEKEN" custom fonts (with the classic file-loader
) :
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
exclude: /node_modules/,
loader: 'file-loader?name=[path][name].[ext]&context=app/'
}
When bundling, everything seems to look good. Font files are properly consumed and are part of the final bundle, but my fonts doesn't apply in the HTML, and I can't see why.
The webpack entry file is index.js
:
import './index.html';
import './styles/index.styl';
Here's the ./styles/index.styl
:
@import '_fonts';
html
font-family 'Heineken Core', serif
... and the ./styles/_fonts.styl
:
@font-face {
font-family: 'Heineken Core';
src: url('./fonts/HEINEKEN Core.eot');
src: url('./fonts/HEINEKEN Core.eot?#iefix') format('embedded-opentype'),
url('./fonts/HEINEKEN Core.woff') format('woff'),
url('./fonts/HEINEKEN Core.ttf') format('truetype'),
url('./fonts/HEINEKEN Core.svg#HEINEKENCore') format('svg');
font-weight: normal;
font-style: normal;
}
I've checked the fonts path, it's correct. I guess the problem is somewhere else, but can't manage to find what's going on.
Any help ?
Note : I'm using webpack-dev-server
.. dunno if it can cause an issue.
By advance, thank you ! :)
[EDIT] Here's my full config :
const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const production = process.argv.indexOf("--production") > -1;
// Full Webpack Guide :
// https://medium.com/@dabit3/beginner-s-guide-to-webpack-b1f1a3638460#.olmn099wa
// and :
// https://julienrenaux.fr/2015/03/30/introduction-to-webpack-with-practical-examples/
var config = {
entry: {
vendor: ['jquery', 'jqvmap', 'gsap'],
app: './app/index.js'
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: !production ? 'http://localhost:8080/' : undefined,
filename: 'bundle.js'
},
watch: !production,
debug: !production,
module: {
preLoaders: [
{
test: /\.(js|es6)$/,
exclude: /node_modules/,
loader: 'jshint-loader'
}
],
loaders: [
{
test: /\.(js|es6)$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: { presets:[/*'react',*/'es2015'] } // Loader's options
},
{
test: /\.styl$/,
exclude: /node_modules/,
loader: [
'style-loader',
'css-loader' + (!production ? '?sourceMap' : ''), // https://github.com/webpack/style-loader#recommended-configuration
'postcss-loader',
'stylus-loader'
// 'file-loader?name=[path][name].[ext]&context=app/'
].join('!')
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
exclude: /node_modules/,
loader: 'file-loader?name=[path][name].[ext]&context=app/'
},
{
test: /\.jpg$/,
loader: 'file-loader?name=[path][name].[ext]&context=app/'
},
{
test: /\.(png|gif)$/,
loader: 'file-loader?name=[path][name].[ext]&context=app/' // 'url-loader?name=[path][name].[ext]&limit=150000' // filesize of < 150ko will be included as data URL
},
{
test: /\.html$/,
loader: [
'file-loader?name=[path][name].[ext]&context=app',
'extract-loader',
'html-loader'
].join('!')
},
// https://65535th.com/jquery-plugins-and-webpack/
// https://github.com/webpack/expose-loader
{
test: require.resolve("jquery"),
loader: [
'expose-loader?$',
'expose-loader?jQuery'
].join('!')
}
]
},
resolve: {
extensions: ['', '.js', '.es6'],
//http://stackoverflow.com/a/28989476/1187888
// alias: {
// jQuery: './node_modules/jquery/dist/jquery.js'
// }
},
plugins: [
// http://stackoverflow.com/a/28989476/1187888
/*new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),*/
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js"/*, Infinity*/)
],
// http://stackoverflow.com/a/33384364/1187888
devServer: {
contentBase: "./app",
hot: true
},
// https://github.com/postcss/autoprefixer#webpack
postcss: [ autoprefixer({ browsers: ['last 5 versions'] }) ],
jshint: { 'esversion' : 6 }
};
// Empêche UglifyJS de gueuler sur le bundle de prod
// ---
if (production) {
// http://stackoverflow.com/a/34071566/1187888
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
exclude: /node_modules/
})
);
}
module.exports = config;
The issue comes from the way that CSS handles relative paths:
Relative URLs are resolved to full URLs using a base URL. RFC 3986, section 3, defines the normative algorithm for this process. For CSS style sheets, the base URL is that of the style sheet itself, not that of the styled source document.
In our case style-loader adds styles to blob objects and injects it to DOM via <link>
tag like this:
<link rel="stylesheet" href="blob:http://localhost:8080/4f0dcf58-1e22-46b5-bc74-60c97c1ad923">
Relative URLs in our CSS are resolving using these blobs as a base, not host from which index.html was loaded. And expectedly nothing was found at these locations.
The solution of this issue is to use absolute URLs in CSS references, what can be easily done with the output.publicPath
option in the webpack config:
module.exports = {
output: {
publicPath: (!production ? "http://localhost:8080/" : "http://your-production-host.com/")
},
// rest of your config options
}