Webpacking our First Assets

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

So, Webpack only needs to know three things. The first - setOutputPath() - tells it where to put the final, built files and the second - setPublicPath() - tells it the public path to this directory:

... lines 1 - 2
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
... lines 8 - 65
;
... lines 67 - 68

The Entry File

The third important piece, and where everything truly starts, is addEntry():

... lines 1 - 2
Encore
... lines 4 - 10
/*
* ENTRY CONFIG
*
* Add 1 entry for each "page" of your app
* (including one that's included on every page - e.g. "app")
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if you JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')
... lines 23 - 65
;
... lines 67 - 68

Here's the idea: we point Webpack at just one JavaScript file - assets/js/app.js. Then, it parses through all the import statements it finds, puts all the code together, and outputs one file in public/build called app.js. The first argument - app - is the entry's name, which can be anything, but it controls the final filename: app becomes public/build/app.js.

And the recipe gave us a few files to start. Open up assets/js/app.js:

15 lines assets/js/app.js
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you require will output into a single css file (app.css in this case)
require('../css/app.css');
// Need jQuery? Install it with "yarn add jquery", then uncomment to require it.
// const $ = require('jquery');
console.log('Hello Webpack Encore! Edit me in assets/js/app.js');

This is the file that Webpack will start reading. There's not much here yet - a console.log() and... woh! There is one cool thing: a require() call to a CSS file! We'll talk more about this later, but in the same way that you can import other JavaScript files, you can import CSS too! And, by the way, this require() function and the import statement we saw earlier on Webpack's docs, do basically the same thing. More on that soon.

To make the CSS a bit more obvious, open app.css and change the background to lightblue and add an !important so it will override my normal background:

body {
background-color: lightblue !important;
}

disableSingleRuntimeChunk()

Before we execute Encore, back in webpack.config.js, we need to make one other small tweak. Find the enableSingleRuntimeChunk() line, comment it out, and put disableSingleRuntimeChunk() instead:

... lines 1 - 2
Encore
... lines 4 - 26
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
//.enableSingleRuntimeChunk()
.disableSingleRuntimeChunk()
... lines 31 - 66
;
... lines 68 - 69

Don't worry about this yet - we'll see exactly what it does later.

Running Encore

Ok! We've told Webpack where to put the built files and which one file to start parsing. Let's do this! Find your terminal and run the Encore executable with:

./node_modules/.bin/encore dev

Tip

For Windows, your command may need to be node_modules\bin\encore.cmd dev

Because we want a development build. And... hey! A nice little notification that it worked!

And... interesting - it built two files: app.js and app.css. You can see them inside the public/build directory. The app.js file... well... basically just contains the code from the assets/js/app.js file because... that file didn't import any other JavaScript files. We'll change that soon. But our app entry file did require a CSS file:

15 lines assets/js/app.js
... lines 1 - 7
// any CSS you require will output into a single css file (app.css in this case)
require('../css/app.css');
... lines 10 - 15

And yea, Webpack understands this!

Here's the full flow. First, Webpack looks at assets/js/app.js. It then looks for all the import and require() statements. Each time we import a JavaScript file, it puts those contents into the final, built app.js file. And each time we import a CSS file, it puts those contents into the final, built app.css file.

Oh, and the final filename - app.css? It's app.css because our entry is called app. If we changed this to appfoo.css, renamed the file, then ran Encore again, it would still build app.js and app.css files thanks to the first argument to addEntry():

... lines 1 - 2
Encore
... lines 4 - 19
.addEntry('app', './assets/js/app.js')
... lines 21 - 66
;
... lines 68 - 69

What this means is... we now have one JavaScript file that contains all the code we need and one CSS file that contains all the CSS! All we need to do is add them to our page!

Open up templates/base.html.twig. Let's keep the existing stylesheets for now and add a new one: <link rel="stylesheet" href=""> the asset() function and the public path: build/app.css:

<!doctype html>
<html lang="en">
<head>
... lines 5 - 8
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('build/app.css') }}">
... lines 11 - 14
{% endblock %}
</head>
... lines 17 - 107
</html>

At the bottom, add the script tag with src="{{ asset('build/app.js') }}". Oh, make that app.js:

<!doctype html>
<html lang="en">
... lines 3 - 17
<body>
... lines 19 - 90
{% block javascripts %}
<script src="{{ asset('build/app.js') }}"></script>
... lines 93 - 105
{% endblock %}
</body>
</html>

If you're not familiar with the asset() function, it's not doing anything important for us. Because the build/ directory is our document root, we're literally pointing to the public path.

Let's try it! Move over, refresh and... hello, weird blue background. And in the console... yes! There's the log!

We've only started to scratch the surface of the possibilities of Webpack. So if you're still wondering: "why is going through this build process so useful?". Stay tuned. Because next, we're going to talk about the require() and import statements and start organizing our code.

Leave a comment!

  • 2020-06-05 Victor Bocharsky

    Hey Radhika,

    I suppose you just have a typo and run command:

    $ ./node_modules/.bin/encore dev

    where node_modules/ instead of node_module/

    Regarding your errors, what version of NodeJS do you have installed? Run "node --version" to know it. I suppose your Node version is too old, or maybe too new for the current dependencies. In this course we're using Node v8, it's minimum required Node version to get it working.

    Cheers!

  • 2020-06-04 Radhika Choudhary

    Hi,

    I get the following syntax error while running webpack encore with following command:

    $ ./node_module/.bin/encore dev

    Error description:


    /home/radhika/Docker/symfony5/node_modules/pkg-up/index.js:4
    module.exports = async ({cwd} = {}) => findUp('package.json', {cwd});
    ^

    SyntaxError: Unexpected token (
    at createScript (vm.js:56:10)
    at Object.runInThisContext (vm.js:97:10)
    at Module._compile (module.js:549:28)
    at Object.Module._extensions..js (module.js:586:10)
    at Module.load (module.js:494:32)
    at tryModuleLoad (module.js:453:12)
    at Function.Module._load (module.js:445:3)
    at Module.require (module.js:504:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/radhika/Docker/symfony5/node_modules/@symfony/webpack-encore/lib/config/parse-runtime.js:13:15)

    Please Help.
    Thanks in advance

  • 2020-06-01 Anders Björkland

    Thanks Vladimir!
    Yeah, I actually did see that one but I didn't really copy what it said. My confused mind thought that the point was to use encore.cmd and I completely missed the backslashes. It took me removing and installing the code on different machines before I realized my mistake. Ick!

  • 2020-06-01 Vladimir Sadicov

    Hey Anders Björkland

    Great catch =) BTW there is a TIP on the page near the original command with advice for Windows users ;)
    node_modules\bin\encore.cmd dev

    Use the force of the tips!

    Cheers!

  • 2020-06-01 Anders Björkland

    Hi fellow coders!

    I had an issue that I just resolved, so I share hear if anyone else has the same problem:
    Short version: I got a BrowserlistError with the message:

    Unknown browser query `basedir=$(dirname "$(echo "$0" | sed -e 's`. Maybe you are using old Browserslist or made typo in query.

    I got the error when I ran encore dev from within /node_modules/.bin/. So that is appearantly not something you should do, but why did I try to do that? Well, I was following along with Ryan's coding and tried this at first: ./node_modules/.bin/encore dev, but on Windows 10 I got:

    '.' is not recognized as an internal or external command, operable program or batch file.


    This led me to think that I had to go into the folder and run encore from there. I thought wrong!

    The solution (on Windows 10):
    .\node_modules\.bin\encore dev

    See? I had to use backslashes instead of forward slashes. Silly me 😬

  • 2020-04-28 Vladimir Sadicov

    Hey Deniz Cetin

    Yo! Great work! Keep going and ask questions if you got difficulties!

    Cheers!

  • 2020-04-28 Deniz Cetin

    I IMMEDIATELY take everything back- I thought I need an integrity value, but it turns out, that I just forgot to close my script-tag at the end.... fixed!

  • 2020-04-28 Deniz Cetin

    Hi there!
    I get the following error in my console after running the encore command and trying it out:
    Failed to find a valid digest in the 'integrity' attribute for resource 'https://mydomain.com/build/app.js' with computed SHA-256 integrity '/fNLzADFdE1OKuBznqu1mB6ZbO+w9cnGd99loR18+tk='. The resource has been blocked.
    I also do not see the console output, that should be there. What's the reason for this?

  • 2020-03-16 Anton Bagdatyev

    Nevermind! I was able to solve, there was a problem in my folder name, everything works now!!! Thanks!

  • 2020-03-16 Anton Bagdatyev

    Hello again, if I run `./node_modules/.bin/encore dev`, I cannot create the Webpack bundle and get the following error:

    $ ./node_modules/.bin/encore dev
    Running webpack ...

    Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
    - configuration.context: The provided value "/Users/antony/Self-development/Web development/Module bundlers/Webpack/Webpack Encore - Frontend like a Pro!/code-webpack-encore/start" contains exclamation mark (!
    ) which is not allowed because it's reserved for loader syntax.
    -> The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory.
    - configuration.output.path: The provided value "/Users/antony/Self-development/Web development/Module bundlers/Webpack/Webpack Encore - Frontend like a Pro!/code-webpack-encore/start/public/build" contains ex
    clamation mark (!) which is not allowed because it's reserved for loader syntax.
    -> The output directory as **absolute path** (required).

    I use Webpack 4:

    $ npm list webpack
    /Users/my-user/code-webpack-encore/start
    └─┬ @symfony/webpack-encore@0.28.3
    └── webpack@4.42.0
  • 2019-12-30 Victor Bocharsky

    Hey Alexey!

    Hm, Webpack Encore uses this https://github.com/Turbo87/... behind the scene, it from their readme it should work on Windows. Please, make sure you've installed and enabled it, though this issue looks related to your problem: https://github.com/Turbo87/... . Or was it opened by you? :) Anyway, nothing much I could say here, probably someone from maintainers or users might give some ideas on that issue.

    Cheers!

  • 2019-12-29 Алексей Костин

    Hi, I am using Windows 10 x64 1903. I do not see any notifications when build is completed. Are there any solutions for Windows users?

  • 2019-05-16 Diego Aguiar

    Hmm, that's weird, I don't get it why you are getting problems with Browserslist if you didn't configure it. What's your code in `app.js`?

  • 2019-05-16 Xav

    Of course, thank you Diego.
    webpack.config.js is still the default one (I haven't touched it yet)


    var Encore = require('@symfony/webpack-encore');


    Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath('/build')
    // only needed for CDN's or sub-directory deploy
    //.setManifestKeyPrefix('build/')
    /*
    * ENTRY CONFIG
    *
    * Add 1 entry for each "page" of your app
    * (including one that's included on every page - e.g. "app")
    *
    * Each entry will result in one JavaScript file (e.g. app.js)
    * and one CSS file (e.g. app.css) if you JavaScript imports CSS.
    */
    .addEntry('app', './assets/js/app.js')
    //.addEntry('page1', './assets/js/page1.js')
    //.addEntry('page2', './assets/js/page2.js')


    // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
    .splitEntryChunks()


    // will require an extra script tag for runtime.js
    // but, you probably want this, unless you're building a single-page app
    .enableSingleRuntimeChunk()


    /*
    * FEATURE CONFIG
    *
    * Enable & configure other features below. For a full
    * list of features, see:
    * https://symfony.com/doc/current/frontend.html#adding-more-features
    */
    .cleanupOutputBeforeBuild()
    .enableBuildNotifications()
    .enableSourceMaps(!Encore.isProduction())
    // enables hashed filenames (e.g. app.abc123.css)
    .enableVersioning(Encore.isProduction())


    // enables @babel/preset-env polyfills
    .configureBabel(() => {}, {
    useBuiltIns: 'usage',
    corejs: 3
    })


    // enables Sass/SCSS support
    //.enableSassLoader()


    // uncomment if you use TypeScript
    //.enableTypeScriptLoader()


    // uncomment to get integrity="..." attributes on your script & link tags
    // requires WebpackEncoreBundle 1.4 or higher
    //.enableIntegrityHashes()


    // uncomment if you're having problems with a jQuery plugin
    //.autoProvidejQuery()


    // uncomment if you use API Platform Admin (composer req api-admin)
    //.enableReactPreset()
    //.addEntry('admin', './assets/js/admin.js')
    ;


    module.exports = Encore.getWebpackConfig();

    And my package.json :

    {
    "devDependencies": {
    "@symfony/webpack-encore": "^0.27.0",
    "core-js": "^3.0.0",
    "webpack-notifier": "^1.6.0"
    },
    "license": "UNLICENSED",
    "private": true,
    "scripts": {
    "dev-server": "encore dev-server",
    "dev": "encore dev",
    "watch": "encore dev --watch",
    "build": "encore production --progress"
    }
    }

    Basically, it's still the default configuration. I just started a new S4 project to test webpack !

  • 2019-05-15 Diego Aguiar

    Hey Xav looks like your babel config is wrong. Can you show me your webpack.config.js & package.json file? Thanks!

  • 2019-05-15 Xav

    Hi !

    I'm very sorry, but I wanted to try that webpack encore, so i created a brand new S4 project on local. I just installed what I really needed (annotations, twig, toolbar and debug) then I installed Encore. Nothing else I swear ! So I watched first the entire course, then I did exactly what was written in the first chapter, typed "node_modules\.bin\encore.cmd dev" (by the way it misses the dot for the windows line) - I was pretty excited at that time - aaaaaand...Error : Module Build Failed (from ;/node_modules/babel-loader/lib/index.js): BrowserslistError: [BABEL] C:\wamp64\www\la_marine\assets\js\app.js: Unknown browser query `basedir=$(dirname "$(echo "$0" | sed -e 's`. Maybe you are using old Browserslist or made typo in query...
    I have no idea what I did wrong. What did I miss ?
    Thank you ! (as usual, awesome course)

  • 2019-05-15 Victor Bocharsky

    Ah, ok, that's great! Glad you fixed it :)

    Cheers!

  • 2019-05-15 Victor Bocharsky

    Hey Jennifer,

    OK, what version of Symfony you're on? And what exactly version of WebpackEncoreBundle is installed? You can check it with Composer:
    $ composer info symfony/webpack-encore-bundle

    Actually, "Symfony\WebpackEncoreBundle\WebpackEncoreBundle" is the correct namespace. Did you install symfony/webpack-encore-bundle globally, i.e. is it in "require" or in "require-dev" section of your composer.json? Hm, let's try to remove and install it again:
    $ composer remove symfony/webpack-encore-bundle
    $ composer require symfony/webpack-encore-bundle

    Do you still see the same error?

    Cheers!

  • 2019-05-14 Jennifer

    I fixed it. I'm running my application in Docker and I'm new to Docker so I didn't realize I needed to run a build and then restart. I really appreciate your help with this issue.

  • 2019-05-14 Jennifer

    Hi Victor,

    I'm trying to add it to an existing project at work. I'm getting that error on any template I try to load. Here's the full message I'm seeing:


    ClassNotFoundException
    Attempted to load class "WebpackEncoreBundle" from namespace "Symfony\WebpackEncoreBundle".
    Did you forget a "use" statement for another namespace?

    in Kernel.php line 33
    at Kernel->registerBundles()
    in Kernel.php line 424
    at Kernel->initializeBundles()
    in Kernel.php line 130
    at Kernel->boot()
    in Kernel.php line 193
    at Kernel->handle(object(Request))
    in index.php line 37

  • 2019-05-14 Victor Bocharsky

    Hey Jennifer,

    Hm, looks good! Then, what page are you trying to access? Did you download course code or did you create an empty Symfony 4.2 project from scratch?

    Cheers!

  • 2019-05-13 Jennifer

    Hi Victor Bocharsky ,

    Thanks for your help with this! I checked and that line exists in config/bundles.php. I was able to clear the cache successfully but I'm still seeing the same error message. I'm using version 4.2 of Symfony. Here's what I see when I run "composer require encore":

    Using version ^1.5 for symfony/webpack-encore-bundle
    ./composer.json has been updated
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Restricting packages listed in "symfony/symfony" to "4.2.*"
    Nothing to install or update
    Generating autoload files
    ocramius/package-versions: Generating version class...
    ocramius/package-versions: ...done generating version class
    Executing script cache:clear [OK]
    Executing script assets:install public [OK]
    Executing script security-checker security:check [OK]

  • 2019-05-13 Victor Bocharsky

    Hey Jennifer,

    Please, make sure you have the exact line in your config/bundles.php file:

    return [
    // ...
    Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
    ];

    Also, make sure your cache is cleared by running in the terminal:


    $ bin/console cache:clear

    Btw, what Symfony version do you use? Is "composer require encore" command executed without any errors for you?

    Cheers!

  • 2019-05-12 Jennifer

    Hi weaverryan,

    I'm able to follow along and install everything up to this point but when I load my app I get the following Symfony error:

    "ClassNotFoundException
    Attempted to load class "WebpackEncoreBundle" from namespace "Symfony\WebpackEncoreBundle".
    Did you forget a "use" statement for another namespace?"

    I'm new to Symfony, and after searching multiple times for a solution to this error I can't seem to find one. Do you have any idea what could be causing this?

    Thanks!

  • 2019-05-07 weaverryan

    Hey Marcel dos Santos!

    Hmm, nice questions! Let's look at each of them:

    > Why you decided to choose `build` directory as the output for the built files? It seems a bit weird and not common as `public/css` and `public/js` or something like that.

    The main reason is that Webpack likes to have a single "build" directory that *everything* goes inside of. In order to have directories like public/css and public/js, we would have needed to set public as the build directory. That's not a great choice because you want to have your build directory a bit isolated, for a few reasons. First, it's nice to be able to just "clean out" the entire build directory (without accidentally deleting other files) - there's an option in Encore to do that. Second, for deploying, you only need to upload the "built" files from Encore. So having them in one, isolated directory (with no other files), makes that easier. So, it's mostly because of Webpack's requirement that there be one build directory... and we want it to only contain Webpack stuff.

    > Other thing that seems odd to me is require CSS dependencies inside my JavaScript code.

    Yea, this one *can* feel weird. The important thing to realize is that Webpack was created by people who primarily build single-page applications. And if your JavaScript is what is building your HTML... then it really makes *perfect* sense for each JavaScript file to require the CSS that it needs to style itself. If you're not building an SPA, it can sometimes feel weird.

    > 'm used to have two different entries: one for my CSS file (compiled from a Sass source code) and another for JS file (compiled from my JavaScript source code). In fact, I'm used to split my JavaScript output in two different files: one for vendor (with jQuery, Parsley and other libraries) that almost don't change and can be cached for a long time and another for app (with my own code) that changes all the time.

    Ok, so let's talk about this... because it sounds like you're already doing what Webpack wants you to do ;). Creating 1 CSS entry and 1 JS entry is really identical to creating 1 entry and then requiring your main SASS file from the top of that 1 .js file. The result in both cases is exactly the same: 1 final built CSS file and 1 final JS file. So, that's just my way of saying that if you do it your way... you're really already doing it the Webpack way.

    Second, you're correct to split your vendor libraries from your app libraries. However... with the new splitEntryChunks() feature, Webpack does that automatically. Check out that chapter if you haven't already - it's craziness.

    Let me know if you still have some doubts :).

    Cheers!

  • 2019-05-06 weaverryan

    Nice find Bertin van den Ham! A very annoying issue :)

  • 2019-05-05 Bertin van den Ham

    Hi @weaverryan,

    In your video i see autocompletion on file webpack.config.js. I'm using Phpstorm as well, but there is no autocompletion, does i needs to configure this?

    In PHPstorm i see the following message:
    Can't analyse webpack.config.js: coding assistance will ignore module resolution rules in this file.
    Possible reasons: this file is not a valid webpack configuration file or its format is not currently supported by the IDE. Error details: Cannot find module '@symfony/webpack-encore'

    #UPDATE
    Fixed it, i was trying to run node and yarn through vagrant but when i run it localy all files are generated correctly.

  • 2019-05-03 Marcel dos Santos

    Hi @weaverryan!

    Why you decided to choose `build` directory as the output for the built files? It seems a bit weird and not common as `public/css` and `public/js` or something like that.

    Other thing that seems odd to me is require CSS dependencies inside my JavaScript code. I'm used to have two different entries: one for my CSS file (compiled from a Sass source code) and another for JS file (compiled from my JavaScript source code). In fact, I'm used to split my JavaScript output in two different files: one for vendor (with jQuery, Parsley and other libraries) that almost don't change and can be cached for a long time and another for app (with my own code) that changes all the time.

    []s