If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeMost of the CSS comes from our base layout: base.html.twig
. On top, there's a link tag to assets/css/main.css
:
... lines 1 - 2 | |
<head> | |
... lines 4 - 10 | |
{% block stylesheets %} | |
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> | |
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous" /> | |
<link href="{{ asset('assets/css/main.css') }}" rel="stylesheet" /> | |
{% endblock %} | |
... lines 16 - 17 | |
</head> | |
... lines 19 - 107 |
But, to keep with our new system, instead of adding this link tag manually, I want to require that CSS from JavaScript. For global CSS, I'll include it from my global JavaScript: layout.js
.
In other words, remove that link tag!
... lines 1 - 2 | |
<head> | |
... lines 4 - 10 | |
{% block stylesheets %} | |
... lines 12 - 13 | |
<link href="{{ asset('assets/css/main.css') }}" rel="stylesheet" /> | |
{% endblock %} | |
... lines 16 - 17 | |
</head> | |
... lines 19 - 107 |
Then, in the source layout.js
file, add require('../css/main.css')
:
... lines 1 - 6 | |
require('../css/main.css'); | |
... lines 8 - 12 |
And... that should just work!
So... refresh! And as promised... woh! Nevermind! It does not work... it looks terrible! What happened?
Our console shows an error:
Module parse failed, dummbell-mini.png, unexpected character
Huh. Over in the terminal, the watch script has a similar message... and it looks like it happens when Webpack reads main.css
.
Hmm. Open main.css
. Ah! There's the image: it's a background image inside our CSS!
... lines 1 - 7 | |
.mini-dumbbell { | |
... lines 9 - 10 | |
background: url('../images/dumbbell-mini.png') center center no-repeat; | |
... line 12 | |
} | |
... lines 14 - 78 |
This, is very interesting. When we tell Webpack to load a CSS file, it actually parses the background images and - basically - requires them! It does the same with fonts and also finds and requires any @import
calls.
So... why is this failing? Well, just like with CSS, a .png
file is not JavaScript... so Webpack has no idea what to do with it!
What's the fix? We need a loader capable of understanding image files.
Head over to webpack.js.org, click on Guides, Asset Management and then Loading Images.
Ah, the file-loader
! It has one simple job: move any files it processes into the build/
directory. When it does that, internally, it will return the filename to that new file... which Webpack will use to re-rewrite the background-image path in our CSS to point to it. It's pretty amazing.
Install it first: copy the name of the module and then, in your open terminal, run:
yarn add file-loader@5 --dev
Back in webpack.config.js
, we need to add a third loader. Copy the css-loader
config. This time, for test
, in the docs, it basically looks for any image file. I'll paste something similar that includes even more image filenames. And for use
, pass these to file-loader
:
... lines 1 - 3 | |
module.exports = { | |
... lines 5 - 13 | |
module: { | |
rules: [ | |
... lines 16 - 32 | |
{ | |
test: /\.(png|jpg|jpeg|gif|ico|svg)$/, | |
use: [ | |
'file-loader' | |
] | |
} | |
] | |
}, | |
... lines 41 - 46 | |
}; |
Before you do anything else, head over, and restart Webpack!
./node_modules/.bin/webpack --watch
Ah, look at the output! Not only did it write layout.js
, rep_log.js
and login.js
files, it output a .png
file... with a long funny name. You can see it in the build/
directory. This is mini-dumbbell.png
. Its name is a hash of its contents - more on that later.
Let's try it! Refresh! The image should show up on this first menu item... but it's not there! And my console has a 404 for the image! What's up?!
Inspect the element. Ok, the final CSS from Webpack changed the background-image
to point to the new filename. Let's open that in a new tab.
Ah! The filename is right, but it's missing the build/
directory prefix!
Webpack is almost doing everything correctly: it moves the file into build/
and even updates the CSS to point to that filename.
Open webpack.config.js
. Yes, we did tell Webpack to put everything into web/build
. But, Webpack doesn't actually know what the public path is to files in this directory. I mean, it doesn't know that web/
is our document root, and so it doesn't know that these files are accessible via build/
then the filename. Nope, this is something we need to tell Webpack, so that it can create the correct paths.
How? Under output
, set publicPath
to /build/
:
... lines 1 - 3 | |
module.exports = { | |
... lines 5 - 9 | |
output: { | |
... lines 11 - 12 | |
publicPath: '/build/' | |
}, | |
... lines 15 - 47 | |
}; |
Find you terminal and restart Webpack:
./node_modules/.bin/webpack --watch
Everything looks the same here... but when we refresh and open the menu... there it is! Our little icon. The background-image
now point to /build/
the filename.
Guys, this is another monumental step forward! Now, as long as we correctly reference image paths in CSS, Webpack will make sure those images are available in build/
and that their paths in the final CSS are correct. We focus on our source files, and Webpack takes care of the rest.
Even better, if you make a mistake - like a typo - you'll actually see a Webpack build error. There's no way to accidentally have a broken link.
Hey Marcos,
I haven't tried the solution yet... but did you restarted your webpack encore after making those changes? Encore requires to be restarted on any changes in webpack.config.js file.
I hope this helps!
Cheers!
It's happy with yarn add file-loader@5 --dev
but not yarn add file-loader --dev
which causes ERROR in ./web/assets/images/dumbbell-mini.png<br />Module build failed: TypeError [ERR_INVALID_ARG_TYPE]: The "from" argument must be of type string. Received type undefined
Ryan,
This is really good stuff, but I've hit a snag! When I code along with you on this episode, I don't get the 404 error on mini-dumbbell. Instead I get no image and no error. When I inspect the element, I see it has this style:
background: url([object Object]) center center no repeat;
Changing the publicPath as you showed did not help.
What in the world?!!
Yo Dan_M!
Woooh! That's totally weird! But, this might be the problem: https://github.com/webpack-contrib/html-loader/issues/140
If you lock your version of file-loader 0.11.2, does that help? I've not seen this issue before, but it's sort weird, that I searched for a bug first :).
If that doesn't help, can you post your webpack.config.js
and package.json files?
Cheers!
weaverryan,
OK, this is really weird! I downgraded to 0.11.2 and it worked. Then I upgraded to 1.1.4, and...it STILL worked.
I don't know what version of file-loader I had originally, but now it just works and I can't break it. Go figure!
Oh man, super weird. Honestly, the package version resolution in npm/yarn still doesn't seem to be as strong as Composer. I *still* sometimes do an rm -rf node_modules to reset things!
Anyways, happy it's working now :)
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.3.*", // v3.3.16
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.0
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.3
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.4.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "^2.0", // v2.1.2
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.3.5
}
}
// package.json
{
"dependencies": [],
"devDependencies": {
"babel-core": "^6.25.0", // 6.25.0
"babel-loader": "^7.1.1", // 7.1.1
"babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
"babel-preset-env": "^1.6.0", // 1.6.0
"bootstrap-sass": "^3.3.7", // 3.3.7
"clean-webpack-plugin": "^0.1.16", // 0.1.16
"copy-webpack-plugin": "^4.0.1", // 4.0.1
"core-js": "^2.4.1", // 2.4.1
"css-loader": "^0.28.4", // 0.28.4
"extract-text-webpack-plugin": "^3.0.0", // 3.0.0
"file-loader": "^0.11.2", // 0.11.2
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.2.1", // 3.2.1
"lodash": "^4.17.4", // 4.17.4
"node-sass": "^4.5.3", // 4.5.3
"resolve-url-loader": "^2.1.0", // 2.1.0
"sass-loader": "^6.0.6", // 6.0.6
"style-loader": "^0.18.2", // 0.18.2
"sweetalert2": "^6.6.6", // 6.6.6
"webpack": "^3.4.1", // 3.4.1
"webpack-chunk-hash": "^0.4.0", // 0.4.0
"webpack-dev-server": "^2.6.1", // 2.6.1
"webpack-manifest-plugin": "^1.2.1" // 1.2.1
}
}
Hey,
Just ran into a problem where (using webpack 4) my url('../images/dumbbell-mini.png') was replaced with url([object Module]).
I fixed it with esModule to false in my file loader options