Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting webpack chunks to twig files

I am creating a traditional website (not a single page application) using php/twig/webpack tools. Almost every site has its own entry scripts. There are also vendor and common js files.

How can I inject entry chunks with hash (to handle browser cache) in my twig files? Those are stored in templates folder and should stay there (not go to public folder since they are used by PHP). How can I inject script tags to twig files?

This is my twig general layout file:

{% block html %}
<!DOCTYPE html>
<html lang="pl">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <base href="/">
        <title>
            {% block title %}{% endblock %}
        </title>
        <link href="css/style.css" rel="stylesheet">
        <script src="js/vendor.js"></script>
        <script src="js/common.js"></script>
    </head>
    <body>
        {% block body %}
        {% endblock %}

        {% block scripts %}
        {% endblock %}
    </body>
</html>
{% endblock %}

And twig for example contact page:

{% extends "layout/bootstrap.twig" %}

{% block scripts %}
    <script src="js/entry-contact.js"></script>
{% endblock %}

{% block body %}
    <h1>Contact form</h1>
{% endblock %}
like image 412
piernik Avatar asked Apr 14 '17 11:04

piernik


1 Answers

Extraction

As said in the Webpack documentation, file hashes can be extracted from compilation stats.

module.exports = {
  /*...*/
  plugins: [
    function() {
      this.plugin("done", function(stats) {
        require("fs").writeFileSync(
          "stats.json", 
          JSON.stringify(stats.toJson())
        );
      });
    }
  ]
};

But it's easier to use one of the plug-ins: webpack-manifest-plugin or assets-webpack-plugin. For example, the WebpackManifestPlugin creates a simple JSON file manifest.json with bundles mapping to actual file names:

{
  "main.js": "main.155567618f4367cd1cb8.js",
  "vendor.js": "vendor.c2330c22cd2decb5da5a.js"
}

Now you need to read it and change the paths in the templates.

Injection

For example, we can create a simple Twig extension:

use Twig_Extension;
use Twig_SimpleFilter;

class WebpackAssetsExtension extends Twig_Extension
{
    private $manifest;

    public function __construct()
    {
        // ONLY FOR EXAMPLE! Code is intentionally simplified.
        // In real world you should not parse this JSON in production.
        // Do it at the container building step (in bundle extensions, 
        // compiler passes, etc) or at the cache warming.

        $jsonContents   = file_get_contents('manifest.json');
        $this->manifest = json_decode($jsonContents, true);
    }

    public function getFilters()
    {
        return [
            new Twig_SimpleFilter('webpack_asset', function (string $name): string {
                return $this->manifest[$name];
            }),
        ];
    }
}

And apply this filter in your template:

<script src="{{ 'vendor.js'|webpack_asset }}"></script>
like image 75
Timurib Avatar answered Oct 20 '22 02:10

Timurib