Using null-loader to remove unneeded fonts in Webpack - css

I'm using FontAwesome and other font packages which include eot, ttf, svg fonts. I don't want to package them so I've got something like that in Webpack:
/*
* Load fonts from URL
*/
{
test: /\.(png|woff|woff2)(\?.*$|$)/,
loader: 'file-loader'
},
/*
* Remove outdated fonts
*/
{
test: /\.(eot|ttf|svg)(\?.*$|$)/,
loader: 'null-loader'
},
And then in main.css I got something like this:
url(fee66e712a8a08eef5805a46892932ad.woff) format("woff"),
url([object Object]) format("truetype"),
url([object Object]#fontawesomeregular) format("svg");
Which is not valid I believe and I don't see any FontAwesome icons. Is there a way to acheive it with Webpack?

At last I found a solution. Which is best described here
To ignore some fonts and still have proper CSS one need to combine ignore-loader which returns empty string + raw-loader which returns contents but not object.
{
// Ignore fonts
test: /\.(eot|ttf|svg)(\?.*$|$)/,
use: ['raw-loader', 'ignore-loader']
},

If you only want to support woff you can use the url-loader
{
// Match woff2 in addition to patterns like .woff?v=1.1.1.
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url-loader',
options: {
// Limit at 50k. Above that it emits separate files
limit: 50000,
// url-loader sets mimetype if it's passed.
// Without this it derives it from the file extension
mimetype: 'application/font-woff',
// Output below fonts directory
name: './fonts/[name].[ext]',
},
},

Related

webpack: understanding source maps when working with CSS

Introduction
I have already setup bundling for my Javascript files with webpack in my project. Now I am in the process of adding CSS files to the webpack configuration. So far, I have been including the CSS files manually in the HTML header by adding <link> elements for every CSS file I depend on (e.g. bootstrap, my own css, etc.). Obviously this is not very elegant and using webpack would be much better, so I would like to replace the link elements and bundle them via webpack.
This should be easy, everything is pretty much documented in the webpack documentation. After reading the documentation and experimenting a bit with webpack I have arrived at the configuration below which already works.
Problem
The problem with my current setup is that I would like to have proper source map support and that does not seem to work. By proper, I mean that I expect that when I run a development build with webpack and I inspect some element in Chrome DevTools, that I will see from which file and which line in the file a certain CSS class originated and that I can click on the CSS rules and the browser jumps to that file.
I do not want to have inline styles in the head element, because then the browser will show something like .foobar { <style>..</style>, rather then .foobar { app.css:154.
With my current setup I have all CSS files combined (but not minified) into one app.css file. This means that if I inspect a bootstrap class such as .btn then it appears as .btn { app.css:3003. However, what I want to achieve is that the browser shows it as .btn { bootstrap.css:3003.
So now I am trying to understand how webpack and the different plugins such as css-loader and min-css-extract-plugin apply CSS source maps, and how I can configure them to achieve a proper debugging experience.
I am not sure how relevant this is, but when I navigate in DevTools under Sources to webpack://./bootstrap/dist/css/bootstrap.css I see that it only contains a single line:
// extracted by mini-css-extract-plugin.
Webpack Setup
index.js:
window.jQuery = require('jquery/dist/jquery');
require('bootstrap/dist/css/bootstrap.css');
require('bootstrap/dist/js/bootstrap');
/* other dependencies */
webpack.config.js:
const devMode = process.env.NODE_ENV !== 'production';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{ /* Javascript rules excluded */ },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
plugins: [
new UglifyJSPlugin (),
new HtmlWebpackPlugin({
template: 'app/index.tpl.html'
}),
new MiniCssExtractPlugin({ filename: devMode ?
'[name].css' :
'[name].[hash].css'
})
],
Conclusion
It seems I just passed the rubber duck test. While I was writing this I arrived at a solution. I will still publish the question, maybe it can help others.
The problem was that I was also using the mini-css-extract-plugin for development and not just for production. I thought that I needed to do that, because when at first I was using the style-loaded I would get styles included in the header and the browser would show me all styles as .foobar { <style>..</style>.
However, the actual problem seemed to be, that I was not using devtools. So the solution was to add devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map', to the webpack configuration to conditionally use the style-loader plugin during development builds and mini-css-extract-plugin during production builds.
webpack.config.js
{
test: /\.css$/,
use: [
{
- loader: MiniCssExtractPlugin.loader,
+ loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
/* ... */
+ devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map',

Webpack Sass-loader relative path

I have an issue where I import font awesome into my main scss file using #import. The issue is font awesome _varibles.scss uses a relative path to import its fonts ../fonts/*. This is causing an error because webpack is trying to load the file relative to the entry point which is the main sass file. I have been trying to use options such as includePaths with no luck and loaders like resolve-url-loader. I have tried multiple paths but no joy. the main.scss file is located in the sass folder along with a vendor folder that holds the font awesome data.
test: /\.scss$/,
use: [{
loader: "style-loader"
},
{
loader: 'css-loader'
},
{
loader: 'resolve-url-loader'
},
{
loader: 'sass-loader',
options: {
includePaths: [
path.resolve(__dirname, "assets/css/vendor/fontawsome/scss")
]
}
}
]
The issue isn't related to the fact that you misspelled fontawesome in your path?
"assets/css/vendor/fontawsome/scss"

Font doesn't get loaded through webpack

Hello dear webpack community.
I desperately try to load fonts using the file-loader
I tried both of these rules:
{ test: /\.(woff|eot|svg|otf|ttf)$/i, loader: `file-loader`, options: {name:'fonts/[name].[ext]'}},
{ test: /\.(woff|eot|svg|otf|ttf)$/i, loader: `file-loader?name=fonts/[name].[ext]`},
however, unfortunately, the fonts don't get pulled when asked for them in the #font-face rule:
#font-face {
font-family: 'Example';
font-style: normal;
font-weight: 400;
src: url('../fonts/Example.ttf') format('truetype');
}
This is how my project structure looks like:
This is the Webpack output:
While fonts.less is loaded, I don't see any font getting loaded through the definition of src().
Thank you a lot for any hint in advance!
See the full config file here: https://gist.github.com/MartinMuzatko/2873b52fed204bd6e8aaaa94527e34a9
The problem is that you're using raw-loader to transform the output of the less-loader to JavaScript so that extract-text-webpack-plugin can use it.
{
test: /\.less$/, use: ExtractTextPlugin.extract(
[
{loader:'raw'},
{loader:'less'}
]
),
}
The less-loader simply transforms the Less syntax to regular CSS. The url() won't be touched. The css-loader on the other hand treats them as imports that webpack will resolve. Only then your rules for the fonts will take effect. All you need to do is change your .less rule to use css-loader instead of raw-loader:
{
test: /\.less$/, use: ExtractTextPlugin.extract(
[
{loader:'css'},
{loader:'less'}
]
),
}

Webpack file-loader and images in CSS

I'm using images in my stylesheet (less) fine by doing:
.foo { background: url('../images/foo.png') }
When using HMR they're base64 encoded into the stylesheet which I'm fine about. However when compiling for production I want the images not to be embedded in the stylesheet. I've tried both url-loader and file-loader.
With url-loader I couldn't get it to emit the images properly. If I set no limit then the files were emitted to output/images/ and had the right size but weren't valid images. If I set the limit to something smaller than 8k the images were emitted to output and correct.
In either case the emitted images appeared in the CSS like so (example when using url-loader with limit=1):
url(data:image/png;base64,bW9kdWxlLmV4cG9ydHMgPSBfX3dlYnBhY2tfcHVibGljX3BhdGhfXyArICJhMDdjZWVkMGRiZTNlMjk5ODY5NWQ3ZjM4MzYxZDY1Zi5wbmciOw==);
Which when you decode it is:
module.exports = __webpack_public_path__ + "a07ceed0dbe3e2998695d7f38361d65f.png";
How do I get the css to use the URL rather than trying to base64 encode the value?
This is my webpack (still on webpack 1) loaders config:
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css!postcss'),
},
{
test: /\.less$/,
loader: ExtractTextPlugin.extract('style-loader', 'css!less!postcss'),
},
{
test: /\.(png|jpg|gif)$/,
loader: 'file-loader?name=/images/[name].[ext]'
}
Update: Turns out disabling the less-loader stops the URLs from being encoded when using the url-loader (but the images are still not valid) but not when using the file-loader.
Update 2: Added a custom loader at the end of the css!less!postcss loaders and the source still has the image URL of ../images/foo.png so it appears the issue is further down the line. Also tried removing the ExtractTextPlugin but the compiled JS for the image then has the Base64 encoded value for the export like the CSS does.
It seems that having the 2 ExtractTextPlugins (for the css & less tests were causing the problems) as I don't have any css files I removed the first and it's now working as expected.

Include 3rd party css in ionic2

How to include a 3rd party css in ionic2? I guess it is probably linked to webpack config but I can't find any example anywhere, does someone know? for example, adding font-awesome css file after npm install font-awesome
For those who are interested in this, you can just add the files in the build process in the ionic.config.js like:
module.exports = {
...
sass: {
src: [
'app/theme/app.+(ios|md).scss',
'node_modules/font-awesome/scss/font-awesome.scss'
],
dest: 'www/build/css',
include: [
'node_modules/ionic-framework',
'node_modules/ionicons/dist/scss',
'node_modules/font-awesome/scss'
]
},
fonts: {
src: [
'node_modules/ionic-framework/fonts/**/*.+(ttf|woff|woff2)',
'node_modules/font-awesome/fonts/*.+(ttf|woff|woff2)'
],
dest: 'www/build/fonts'
}
...
}
This will compile font-awesome.css under www/build/css and fonts under www/build/fonts
ionic.config.js has been deprecated.
The correct answer is now:
npm install font-awesome
Then edit your gulpfile.js to add options to the sass and fonts tasks:
gulp.task('sass', function(){
return buildSass({
sassOptions: {
includePaths: [
'node_modules/ionic-angular',
'node_modules/ionicons/dist/scss',
'node_modules/font-awesome/scss'
]
}
});
});
gulp.task('fonts', function(){
return copyFonts({
src: [
'node_modules/ionic-angular/fonts/**/*.+(ttf|woff|woff2)',
'node_modules/font-awesome/fonts/**/*.+(eot|ttf|woff|woff2|svg)'
]
});
});
You can find more information on the gulp tasks here: https://github.com/driftyco/ionic-gulp-tasks.
Then you should be able to #import "font-awesome" in your app/theme/app.core.scss file and use it in your project wherever.
You can normally put css files in the index.html page and just use the css classes wherever you want. By default, your components are not completely isolated from the outside world so you should be able to use lets say bootstrap without any problems

Resources