Open andyexeter opened 5 years ago
Hey @andyexeter,
Why does the "make sure to not return the same file multiple times" code need to be there?
That's because entrypoints can share some chunks.
For instance if you have app.css
, admin.css
and app~admin.css
(common chunk) you may also want to call {{ encore_entry_link_tags('app') }}
and {{ encore_entry_link_tags('admin') }}
on the same page.
If that check didn't exist you'd then end-up with the link tag for app~admin.css
being added multiple times to your page (the same thing applies to encore_entry_script_tags
).
Can it be removed, or can the issue it solves be solved in a different way?
It's a tricky topic... that behavior also causes issues in some cases such as error pages generation since that can happen after you already started processing your main template (see https://github.com/symfony/webpack-encore-bundle/pull/21)
What we “kinda” want to do is making this “memory” this has almost tied to each Twig “rendering cycle”. Like, each time you render Twig, you get a fresh “memory/cache” that’s shared by all rendered sub-templates. But I’m not sure if knowing such a thing is even possible.
I just ran into this issue again, with error pages this time. It happened because a RuntimeError exception was thrown within a template after the stylesheets were rendered.
FWIW I have implemented a fix for this particular scenario by adding a kernel.exception
event listener and resetting within that:
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupCollectionInterface;
class ExceptionListener implements EventSubscriberInterface
{
private $entrypointLookupCollection;
public function __construct(EntrypointLookupCollectionInterface $entrypointLookupCollection)
{
$this->entrypointLookupCollection = $entrypointLookupCollection;
}
public function onKernelException(ExceptionEvent $event)
{
if ($event->getException() instanceof \Twig\Error\RuntimeError) {
$this->entrypointLookupCollection->getEntrypointLookup()->reset();
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::EXCEPTION => 'onKernelException',
];
}
}
This same problem occurs when you send more than one HTML e-mail. I use Encore for the CSS in my emails. When sending multiple emails, only the first email will have a stylesheet. All subsequent emails will not have a stylesheet.
This problem is compounded when using the messenger component to handle things in a background consumer. The first email that the consumer sends will be fine, but any subsequent emails will not be.
There should be a straightforward way to reset the cache. Does Twig some sort of events that we can hook into? Something that we can use to clear the cache for each rendering cycle?
I personally think this would be a very interesting improvement.
In my case I am using asynchronous loading of stylesheets to avoid rendering blocking caused by stylesheets to improve SEO, for that I have to use this trick (see code below). The problem with this is that if the user has Javascript disabled in his browser, the stylesheets never get loaded because onload
never gets executed, this causes all the styles to be lost.
To avoid this situation I am trying to use a second time encore_entry_link_tags()
inside <noscript>
and this is when I have encountered the same problem as @andyexeter, as the second time we use encore_entry_link_tags()
it returns nothing.
{% block head_stylesheets %}
{{ critical_path_css('critical', 'critical_frontend_homepage') }}
{{ encore_entry_link_tags('frontend_homepage', null, 'frontend', attributes={
media: "none",
onload: "this.media='all'"
}) }}
<noscript>
{{ encore_entry_link_tags('frontend_homepage', null, 'frontend') }}
</noscript>
{% endblock %}
Is there a possibility to implement an enhancement to be able to call the same stylesheets twice?
here a dirty workaround 1- store the tags in a global variable (for example $_POST) the first time 2- use the global variable in place of the encore_entry_link_tags() or encore_entry_script_tags() method
To do that, make a twig extension (for exemple in src/Twig/GeneralExtension.php
)
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class GeneralExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('setPostVar', [$this, 'setPostVar']),
new TwigFunction('getPostVar', [$this, 'getPostVar']),
];
}
public function setPostVar(string $key, mixed $value)
{
$_POST[$key]=$value;
return true;
}
public function getPostVar(string $key)
{
if(!isset($_POST[$key]))return null;
return $_POST[$key];
}
}
in your template use it like this :
{% block styles %}
{% if getPostVar('encoreCss') is null %}
{% do setPostVar('encoreCss',encore.encore_absolute_link_tags('pdf')) %}
{% endif %}
{{ getPostVar('encoreCss')|raw }}
{% endblock %}
When calling
encore_entry_link_tags()
in a Twig template, the underlying PHP code has some safe-guarding in place to ensure the same link tag isn't outputted twice:https://github.com/symfony/webpack-encore-bundle/blob/5e1cab3d223f65933d59a5a95ea01a6ed2833db4/src/Asset/EntrypointLookup.php#L84-L89
I'm not entirely sure of the rationale behind this, but it causes an issue when
encore_entry_link_tags()
is called twice in the same request.This issue came to light when investigating why an order confirmation page of a website wasn't outputting a stylesheet's
link
tag. After some debugging I figured out it was because the controller for this page sends a confirmation email with an attached PDF which is rendered via HTML/Twig.The Twig template for the PDF called
encore_entry_link_tags()
, so when the confirmation page was loaded andencore_entry_link_tags()
was called a second time, nothing was returned.I have worked around this by calling the
reset()
method of theEntrypointLookup
object right after the confirmation email is sent but it feels like a bit of a hack.Why does the "make sure to not return the same file multiple times" code need to be there? Can it be removed, or can the issue it solves be solved in a different way?
Relevant Slack discussion: https://symfony-devs.slack.com/archives/C5VHNHY11/p1562584991057800