Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

copy-webpack-plugin

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Our source files all live in web/assets. We code there, and Webpack moves everything we need to web/build. At runtime, when the user actually loads the site, nothing is read from web/assets. Nope! The user downloads all files from web/build.

And that means... well... web/assets does not need to be publicly accessible anymore! We can - and should - hide our source files.

Yes, I mean we can, literally:

mv web/assets assets

The assets/ directory now lives at the root of our project.

And really... that doesn't break much. All of our files use relative paths internally to refer to each other. The only thing we need to update is webpack.config.js: to remove the web/ from the 3 entries:

... lines 1 - 3
module.exports = {
entry: {
rep_log: './assets/js/rep_log.js',
login: './assets/js/login.js',
layout: './assets/js/layout.js',
},
... lines 10 - 63
};

Yep, that's it.

Find your webpack terminal tab and restart!

./node_modules/.bin/webpack --watch

Everything still builds... and the page still loads fine.

Static, Non-Webpacked Image Files

Except... we lost something small. Before this change, the leaderboard had a little dumbbell image. Inspect that element. Yea, inside the h2, there is a good, old-fashioned img tag. And it references /assets/images/dumbbell.png. I was wrong! I said nothing was referencing the public web/assets directory. But that's not true!

In app/Resources/views/lift/index.html.twig, yep, there is the img tag:

... lines 1 - 2
{% block body %}
<div class="row">
... lines 5 - 34
<div class="col-md-5">
<div class="leaderboard">
<h2 class="text-center">
<img class="dumbbell" src="{{ asset('assets/images/dumbbell.png') }}" />
Leaderboard
</h2>
... lines 41 - 42
</div>
</div>
</div>
{% endblock %}
... lines 47 - 53

The copy-webpack-plugin

How can we fix this? It would be silly to keep all of our source files in a public directory just so we can reference a few, static images.

The answer? With a plugin called copy-webpack-plugin. Google for it.

This plugin is a little tool that allows you to copy files from one location to another when Webpack runs. For us, it means we could copy files to the build/ directory that aren't processed through Webpack.

Let's get it installed! Copy the name of the package. Then, in your favorite open terminal tab, run:

yarn add copy-webpack-plugin@4 --dev

Back in the documentation, copy the require line. In our webpack.config.js, paste that, but use the trendier const keyword:

... lines 1 - 2
const CopyWebpackPlugin = require('copy-webpack-plugin');
... lines 4 - 71

I'll also copy the new CopyWebpackPlugin() line. This goes under the plugins key:

... lines 1 - 2
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
... lines 6 - 58
plugins: [
... lines 60 - 64
new CopyWebpackPlugin([
... lines 66 - 67
]),
]
};

So here's the deal: in the images/ directory, we have a mixture of images. One - dumbbell-mini.png - is processed through Webpack and copied to the build/ directory already. But the other, dumbbell.png is not.

To organize this, create a new directory in assets/ called static/. Move the dumbbell file there. This directory will hold all assets that are not processed by Webpack.

Now, for CopyWebpackPlugin, we will copy from ./assets/static to static/. I'll add a comment about what this means: copies to {output}/static:

... lines 1 - 2
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
... lines 6 - 58
plugins: [
... lines 60 - 64
new CopyWebpackPlugin([
// copies to {output}/static
{ from: './assets/static', to: 'static' }
]),
]
};

Give it a try! Hit Ctrl+C to stop Webpack, then restart it!

yarn add copy-webpack-plugin --dev

And instantly, we have a web/build/static directory with dumbbell.png inside!

This is nothing crazy, but it works well! In our template - index.html.twig - change the src to build/static/dumbbell.png:

... lines 1 - 2
{% block body %}
<div class="row">
... lines 5 - 34
<div class="col-md-5">
<div class="leaderboard">
<h2 class="text-center">
<img class="dumbbell" src="{{ asset('build/static/dumbbell.png') }}" />
Leaderboard
</h2>
... lines 41 - 42
</div>
</div>
</div>
{% endblock %}
... lines 47 - 53

Also open login.html.twig: we have another img tag there too:

... lines 1 - 10
{% block fos_user_content %}
<div class="container">
<div class="wrapper">
<form action="{{ path("fos_user_security_check") }}" method="post" class="form-signin">
<h3><img class="dumbbell" src="{{ asset('build/static/dumbbell.png') }}">Login! Start Lifting!</h3>
... lines 16 - 61
</form>
</div>
</div>
{% endblock fos_user_content %}

When we refresh, no nasty surprises this time! The image loads.

Next! Let's add some fanciness to our CSS... by using Sass...

Leave a comment!

2
Login or Register to join the conversation
Jon Avatar
Jon Avatar Jon | posted 4 years ago | edited

When adding copy-webpack-plugin this tutorial needs version 4 since version 5 is now out. Should be
yarn add copy-webpack-plugin@4 --dev

Reply

Thanks Jon!

We're going to add a note about this!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// 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
    }
}

What JavaScript libraries does this tutorial use?

// 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
    }
}
userVoice