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