Webpack "OTS parsing error" loading fonts - css

My webpack config specifies that fonts should be loaded using url-loader, and when I try to view the page using Chrome I get the following error:
OTS parsing error: invalid version tag
Failed to decode downloaded font: [My local URL]
The relevant parts of my config look like this:
{
module: {
loaders: [
// ...
{
test: /\.scss$/,
loaders: ['style', 'css?sourceMap', 'autoprefixer', 'sass?sourceMap'],
},
{
test: /images\/.*\.(png|jpg|svg|gif)$/,
loader: 'url-loader?limit=10000&name="[name]-[hash].[ext]"',
},
{
test: /fonts\/.*\.(woff|woff2|eot|ttf|svg)$/,
loader: 'file-loader?name="[name]-[hash].[ext]"',
}
],
},
}
It doesn't happen in Safari, and I haven't tried Firefox.
In development I'm serving files through webpack-dev-server, in production they're written to disk and copied to S3; in both cases I get the same behaviour in Chrome.
This also happens to larger images (greater than the 10kB limit in the image loader config).

TL;DR Use absolute paths to your assets (including your complete hostname) by setting your output.publicPath to e.g. "http://example.com/assets/".
The problem
The problem is the way that URLs are resolved by Chrome when they're parsed from a dynamically loaded CSS blob.
When you load the page, the browser loads your Webpack bundle entry JavaScript file, which (when you're using the style-loader) also contains a Base64 encoded copy of your CSS, which gets loaded into the page.
This is what it looks like in Chrome DevTools
That's fine for all the images or fonts which are encoded into the CSS as data URIs (i.e. the content of the file is embedded in the CSS), but for assets referenced by URL, the browser has to find and fetch the file.
Now by default the file-loader (which url-loader delegates to for large files) will use relative URLs to reference assets - and that's the problem!
These are the URLs generated by file-loader by default - relative URLs
When you use relative URLs, Chrome will resolve them relative to the containing CSS file. Ordinarily that's fine, but in this case the containing file is at blob://... and any relative URLs are referenced the same way. The end result is that Chrome attempts to load them from the parent HTML file, and ends up trying to parse the HTML file as the content of the font, which obviously won't work.
The Solution
Force the file-loader to use absolute paths including the protocol ("http" or "https").
Change your webpack config to include something equivalent to:
{
output: {
publicPath: "http://localhost:8080/", // Development Server
// publicPath: "http://example.com/", // Production Server
}
}
Now the URLs that it generates will look like this:
Absolute URLs!
These URLs will be correctly parsed by Chrome and every other browser.
Using extract-text-webpack-plugin
It's worth noting that if you're extracting your CSS to a separate file, you won't have this problem because your CSS will be in a proper file and URLs will be correctly resolved.

For me the problem was my regex expression. The below did the trick to get bootstrap working:
{
test: /\.(woff|ttf|eot|svg)(\?v=[a-z0-9]\.[a-z0-9]\.[a-z0-9])?$/,
loader: 'url-loader?limit=100000'
},

As asnwered here by #mcortesi if you remove the sourceMaps from the css loader query the css will be built without use of blob and the data urls will be parsed fine

As with #user3006381 above, my issue was not just relative URLs but that webpack was placing the files as if they were javascript files. Their contents were all basically:
module.exports = __webpack_public_path__ + "7410dd7fd1616d9a61625679285ff5d4.eot";
in the fonts directory instead of the real fonts and the font files were in the output folder under hash codes. To fix this, I had to change the test on my url-loader (in my case my image processor) to not load the fonts folder. I still had to set output.publicPath in webpack.config.js as #will-madden notes in his excellent answer.

I experienced the same problem, but for different reasons.
After Will Madden's solution didn't help, I tried every alternative fix I could find via the Intertubes - also to no avail. Exploring further, I just happened to open up one of the font files at issue. The original content of the file had somehow been overwritten by Webpack to include some kind of configuration info, likely from previous tinkering with the file-loader. I replaced the corrupted files with the originals, and voilĂ , the errors disappeared (for both Chrome and Firefox).

I know this doesn't answer OPs exact question but I came here with the same symptom but a different cause:
I had the .scss files of Slick Slider included like this:
#import "../../../node_modules/slick-carousel/slick/slick.scss";
On closer inspection it turned out that the it was trying to load the font from an invalid location (<host>/assets/css/fonts/slick.woff), the way it was referenced from the stylesheet.
I ended up simply copying the /font/ to my assets/css/ and the issue was resolved for me.

Since you use url-loader:
The url-loader works like the file-loader, but can return a DataURL if the file is smaller than a byte limit.
So another solution to this problem would be making the limit higher enough that the font files are included as DataURL, for example to 100000 which are more or less 100Kb:
{
module: {
loaders: [
// ...
{
test: /\.scss$/,
loaders: ['style', 'css?sourceMap', 'autoprefixer', 'sass?sourceMap'],
},
{
test: /images\/.*\.(png|jpg|svg|gif)$/,
loader: 'url-loader?limit=10000&name="[name]-[hash].[ext]"',
},
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: 'url-loader?limit=100000&mimetype=application/font-woff',
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: 'url-loader?limit=100000&mimetype=application/font-woff',
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: 'url-loader?limit=100000&mimetype=application/octet-stream',
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader',
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: 'url-loader?limit=100000&mimetype=image/svg+xml',
},
],
},
}
Allways taking into account on what the limit number represents:
Byte limit to inline files as Data URL
This way you don't need to specify the whole URL of the assets. Which can be difficult when you want Webpack to not only respond from localhost.
Just one last consideration, this configuration is NOT RECOMMENDED for production. This is just for development easiness.

The best and easiest method is to base64 encode the font file. And use it in font-face.
For encoding, go to the folder having the font-file and use the command in terminal:
base64 Roboto.ttf > basecodedtext.txt
You will get an output file named basecodedtext.txt. Open that file. Remove any white spaces in that.
Copy that code and add the following line to the CSS file:
#font-face {
font-family: "font-name";
src: url(data:application/x-font-woff;charset=utf-8;base64,<<paste your code here>>) format('woff');
}
Then you can use the font-family: "font-name" in your CSS.

I just had the same issue with Font Awesome. Turned out this was
caused by a problem with FTP. The file was uploaded as text (ASCII)
instead of binary, which corrupted the file. I simply changed my FTP
software to binary, re-uploaded the font files, and then it all
worked.
https://css-tricks.com/forums/topic/custom-fonts-returns-failed-to-decode-downloaded-font/
this helped me in the end
I had the same issue with FTP transferring files as text

If you're using Angular you need to check to make sure your
<base href="/">
tag comes before your style sheet bundle. I switched my code from this:
<script src="~/bundles/style.bundle.js"></script>
<base href="~/" />
to this:
<base href="~/" />
<script src="~/bundles/style.bundle.js"></script>
and the problem was fixed.
Thanks to this post for opening my eyes.

As of 2018,
use MiniCssExtractPlugin
for Webpack(> 4.0) will solve this problem.
https://github.com/webpack-contrib/mini-css-extract-plugin
Using extract-text-webpack-plugin in the accepted answer is NOT recommended for Webpack 4.0+.

The limit was the clue for my code, but I had to specify it like this:
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],

In my case adding following lines to lambda.js {my deployed is on AWS Lambda} fixed the issue.
'font/opentype',
'font/sfnt',
'font/ttf',
'font/woff',
'font/woff2'

Had same problem like #adam-mccormick and user3006381. I am using webpack5 and hot module.
My .eot or .woff files contained:
export default __webpack_public_path__ + "./fonts/montserrat-v24-latin-ext-regular.woff2";
and my webpack.config.js looked like:
modules.exports = {
//...
module: {
rules: [
//...
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: [{
loader: "file-loader",
options: {
name: './fonts/[name].[ext]',
}
}]
}
]
}
}
and this was my problem solution:
modules.exports = {
//...
module: {
rules: [
//...
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: './fonts/[name].[ext]',
}
}
]
}
}

Related

My css file is too big because url-loader is inserting a .jpg code into the .css file - How can I config webpack so it points towards the img?

I'm finishing an online course about some web dev tools.
In the course, somehow, the instructor doesn't run into a problem with webpack while using a .jpg url in the .css. After searching on StackOverflow and Google I've managed to find the answer in using the url-loader webpack plugin.
This worked fine while I was running a local dev environment.
However, when I built the website for deployment, the .css file was sized in 200kb. After running the file through a beautifier, I've found that it was because of the .jpg code being injected into the .css. After deleting that portion, the file size dropped down to less than 16kb.
How can I configure my webpack so when it builds, it replaces the inserted .jpg to a url pointing towards the actual image file?
Here are:
The repo: https://github.com/sethalberdier/fictional-travel-site
The folder containing the huge css file (and also the one that is live): https://github.com/sethalberdier/fictional-travel-site/tree/master/docs
The code bits that matter, imo:
css:
#mixin atLarge {
background: url('/app/assets/images/testimonials-bg.jpg') top center;
}
inside webpack.config I have this rule for the exports module:
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
},
As per url-loader docs, it seems like you need to specify a limit in your webpack config file. For instance:
{
loader: 'url-loader',
options: {
limit: 8192
}
}
If you want url-loader to never transform images into base64 strings, specify {limit: false} or use file-loader instead.

CSS/SCSS Code module not found in VSCode using relative path

I'm writing a SharePoint Framework web-part in VS Code.
Unfortunately my scss styles file cannot be found, even though it's in the same path - please see the image below:
I also checked the styles file Events.module.scss in case an error in their was preventing it being imported, but there are no issues with that file...
I have tried restarting VS Code...tried moving the files around, tried renaming the scss file...
So what is missing here, and how can I resolve it?
I found the solution at How to use CSS Modules with TypeScript and webpack
Either we can bypass the import by using const s = require('./Button.css');
or, install Typings for CSS Modules like so:
npm install --save-dev typings-for-css-modules-loader
then add the new rule to webpack.config:
module: {
rules: [
{
test: /\.css$/,
include: path.join(__dirname, 'src/components'),
use: [
'style-loader',
{
loader: 'typings-for-css-modules-loader',
options: {
modules: true,
namedExport: true
}
}
]
}
]
}
it's a bit of a faff or kerfuffle but it is possible

with webpack, why should one import CSS files from JS source code and not build CSS in isolation like the tradition did?

webpack doc for loading css:
https://webpack.js.org/guides/asset-management/#loading-css1
The webpack doc asks to import the CSS files from the Javascript code and to then run extract-text-webpack-plugin to extract the CSS.
--> with webpack, why should one import CSS files from JS source code and not build CSS in isolation like the tradition did?
By not importing CSS from Javascript, I mean that the webpack config for CSS has no ".js" extension, and it parses the CSS/SCSS files directly.
There are benefits of not importing CSS from javascript:
(objective fact). If one wants to build only CSS, it will be faster if the bundler does not need to parse the Javascript source code. Besides, one can run the bundlers for CSS and Javscript in parallel by using parallel-webpack.
(subjective, based on tradition, but most important in my opinion). SASS building in isolation has been the tradition for years. Therefore, we can achieve better HTML semantics and maintainability. Importing CSS from JS is virtual and may lead to neglect the generated separate CSS bundle.
(objective fact) One can split more the config files for CSS and Javascript, for more clarity.
I'm more a traditionalist than most as i've been doing this for over 15 years, but I would say the new way to separate concerns is better than the traditional way.
In the old thinking we use to separate layout from styling from functionality (html from css from js). This is more 'vertical' and made knowing where files are easy, but made finding specific code to do with 'features' hard. I.e. a button might be made up of a small button section within /src/shop-front.html, a few lines of code within /src/css/shop-front.css and then the enhanced functionality would live somewhere in /src/js/shop-front.js
The new way of thinking is to separate concerns by components. So now you would have your shop-front, but this would be made from /src/components/button/ and all files will live in the same place. Including the js file which includes the css as mentioned.
The benefit being, if you decide to swap button for fancy-button all you do is change the shop-front import button from 'button' to import button from 'fancy-button'. all old code will be removed automatically. no need to try to digest and change code in multiple places.
To address your concerns:
true, node-sass is fast, but I would say the difference between that and going via webpack is negligible and the improvement in dev experience (as mentioned above) is worth any extra ms
This point does apply as the way the css/sass is built does not change just because we require it from a js file. I guess you mean if you were to use css modules, but this is a choice, you don't have to. I use sass and apply class names as i would normally.
In webpack config, you could split the js config from the css config if you wanted to. I wouldn't argue that this would make things any clearer though. the config is so small, it's not worth worrying about.
My folder structure is nice and easy to reason with:
This is a basic config for simple scss imports
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { SRC, DIST } = require('./src/config/paths');
module.exports = {
devtool: 'source-map',
cache: true,
context: SRC,
entry: {
app: [`${SRC}/client-entry.js`]
},
output: {
path: DIST,
filename: '[name]-[chunkhash].js',
publicPath: '/'
},
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
],
resolve: {
modules: ['node_modules', SRC],
extensions: ['.js', '.jsx', '.scss']
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [/src/],
loader: 'babel-loader',
options: {
cacheDirectory: true
}
},
{
test: /\.s?css$/,
include: [/src/],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader', 'sass-loader']
})
}
]
}
};

Webpack Import Including Font Face with Relative Path

In my Webpack configuration I'm defining one resource root for common files like this:
{
loader: 'sass-loader',
options: {
includePaths: [
'node_modules',
'src/components/_common'
]
}
}
Now I'm having e.g. a file _fonts.scss in the resource root and can import it using #import "fonts";. This is working like a charm.
However, if this file contains a #font-face directive that is written relative to src/components/_common (path like in the Webpack configuration above) the file loading of that font url won't work. Webpack isn't able to resolve this URL as it assumes that it's written based on the path of the actual file which imports _fonts.scss, which is not src/components/_common.
So, my question is: Would it be possible that I write the path absolute from the beginning of the project, so that Webpack can always resolve it as it's no longer relative? I've tried it with no luck. Also I've tried specifying resolve.modules and resolve.alias with no luck too.
To solve this issue, it's only necessary to use the resolve-url-loader:
https://github.com/bholloway/resolve-url-loader
Keep in mind that it's currently necessary to have sourceMap enabled on any previous loader. It's just that easy.

Webpack and Sass correctly processes background: url() image but then it's not found when used with webpack-dev-server

I am using Webpack 2 and webpack-dev-server together with Sass loader, actual configuration:
{
test: /\.scss/,
loaders: [
"style",
{ loader: "css", query: { modules: false, sourceMap: true } },
{ loader: "resolve-url" },
{ loader: "sass", query: { sourceMap: true } }
]
}
This works quite well and the image referenced in background: url() is processed by webpack and also replaced in the style for something like background-somehash.jpg, this file can be accessed by typing http://localhost:8080/background-somehash.jpg. It also works when I provide whole url (including localhost) in a style background definition using developer tools...
The only thing that don't work is the original css produced by webpack which looks like background: url(background-somehash.jpg). I also tried various urls like ./, ../, ../../ or ./images/ to try out if root was set somehow differently. What I don't get is that the file is readily available at the root...
EDIT:
When used together with extract-text-webpack-plugin which extracts styles into separate real styles.css file it works just fine. The question is why it doesn't work when final css is being served from javascript bundle ?
CLARIFICATION:
Everything is referenced correctly, the image is available, it all works when I extract css into separate file using extract-text-webpack-plugin it just doesn't work when the completely same css is served from bundle.js which is then referenced in the index.html like <link href="blob:..." rel="stylesheet">
Things you should check:
Is the referenced image recognized by webpack?
Just delete the image and check if the webpack build fails. If that's the case, it is not an issue with your loader configuration.
Check the requested URL with your browser developer tools
If the request is terminated with a 404:
Check if output.publicPath (webpack) / contentBase (webpack-dev-server) point to the same location. This is from the browser's perspective (=no absolute file paths)
If you're using a <base>-tag, you need to ensure that it does replace the base URL correctly.

Resources