Skip to content

Created cached entrypoint look up class #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions src/Asset/EntrypointLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace Symfony\WebpackEncoreBundle\Asset;

use Psr\Cache\CacheItemPoolInterface;
use Symfony\WebpackEncoreBundle\Exception\EntrypointNotFoundException;

/**
Expand All @@ -26,9 +27,13 @@ class EntrypointLookup implements EntrypointLookupInterface

private $returnedFiles = [];

public function __construct(string $entrypointJsonPath)
private $cache;

public function __construct(string $entrypointJsonPath, CacheItemPoolInterface $cache = null, string $cacheKey = null)
{
$this->entrypointJsonPath = $entrypointJsonPath;
$this->cache = $cache;
$this->cacheKey = $cacheKey;
}

public function getJavaScriptFiles(string $entryName): array
Expand Down Expand Up @@ -84,20 +89,34 @@ private function validateEntryName(string $entryName)

private function getEntriesData(): array
{
if (null === $this->entriesData) {
if (!file_exists($this->entrypointJsonPath)) {
throw new \InvalidArgumentException(sprintf('Could not find the entrypoints file from Webpack: the file "%s" does not exist.', $this->entrypointJsonPath));
}
if (null !== $this->entriesData) {
return $this->entriesData;
}

$this->entriesData = json_decode(file_get_contents($this->entrypointJsonPath), true);
if ($this->cache) {
$cached = $this->cache->getItem($this->cacheKey);

if (null === $this->entriesData) {
throw new \InvalidArgumentException(sprintf('There was a problem JSON decoding the "%s" file', $this->entrypointJsonPath));
if ($cached->isHit()) {
return $this->entriesData = $cached->get();
}
}

if (!isset($this->entriesData['entrypoints'])) {
throw new \InvalidArgumentException(sprintf('Could not find an "entrypoints" key in the "%s" file', $this->entrypointJsonPath));
}
if (!file_exists($this->entrypointJsonPath)) {
throw new \InvalidArgumentException(sprintf('Could not find the entrypoints file from Webpack: the file "%s" does not exist.', $this->entrypointJsonPath));
}

$this->entriesData = json_decode(file_get_contents($this->entrypointJsonPath), true);

if (null === $this->entriesData) {
throw new \InvalidArgumentException(sprintf('There was a problem JSON decoding the "%s" file', $this->entrypointJsonPath));
}

if (!isset($this->entriesData['entrypoints'])) {
throw new \InvalidArgumentException(sprintf('Could not find an "entrypoints" key in the "%s" file', $this->entrypointJsonPath));
}

if ($this->cache) {
$this->cache->save($cached->set($this->entriesData));
}

return $this->entriesData;
Expand Down
2 changes: 1 addition & 1 deletion src/Asset/TagRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function __construct(
@trigger_error(sprintf('The "$entrypointLookupCollection" argument in method "%s()" must be an instance of EntrypointLookupCollection.', __METHOD__), E_USER_DEPRECATED);

$this->entrypointLookupCollection = new EntrypointLookupCollection(
new ServiceLocator(['_default' => function() use ($entrypointLookupCollection) {
new ServiceLocator(['_default' => function () use ($entrypointLookupCollection) {
return $entrypointLookupCollection;
}])
);
Expand Down
47 changes: 47 additions & 0 deletions src/CacheWarmer/EntrypointCacheWarmer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/*
* This file is part of the Symfony WebpackEncoreBundle package.
* (c) Fabien Potencier <[email protected]>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\WebpackEncoreBundle\CacheWarmer;

use Psr\Cache\CacheItemPoolInterface;
use Symfony\Bundle\FrameworkBundle\CacheWarmer\AbstractPhpFileCacheWarmer;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup;

class EntrypointCacheWarmer extends AbstractPhpFileCacheWarmer
{
private $cacheKeys;

public function __construct(array $cacheKeys, string $phpArrayFile, CacheItemPoolInterface $fallbackPool)
{
$this->cacheKeys = $cacheKeys;
parent::__construct($phpArrayFile, $fallbackPool);
}

/**
* {@inheritdoc}
*/
protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
{
foreach ($this->cacheKeys as $cacheKey => $path) {
// If the file does not exist then just skip past this entry point.
if (!file_exists($path)) {
continue;
}

$entryPointLookup = new EntrypointLookup($path, $arrayAdapter, $cacheKey);

try {
$entryPointLookup->getJavaScriptFiles('dummy');
} catch (EntrypointNotFoundException $e) {
// ignore exception
}
}
}
}
4 changes: 4 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public function getConfigTreeBuilder()
->isRequired()
->info('The path where Encore is building the assets - i.e. Encore.setOutputPath()')
->end()
->booleanNode('cache')
->info('Enable caching of the entry point file(s)')
->defaultFalse()
->end()
->arrayNode('builds')
->useAttributeAsKey('name')
->scalarPrototype()
Expand Down
29 changes: 18 additions & 11 deletions src/DependencyInjection/WebpackEncoreExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
namespace Symfony\WebpackEncoreBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup;

final class WebpackEncoreExtension extends Extension
Expand All @@ -29,22 +29,29 @@ public function load(array $configs, ContainerBuilder $container)
$config = $this->processConfiguration($configuration, $configs);

$factories = [
'_default' => new Reference($this->entrypointFactory($container, '_default', $config['output_path']))
'_default' => $this->entrypointFactory($container, '_default', $config['output_path'], $config['cache']),
];
$cacheKeys = [
'_default' => $config['output_path'].'/entrypoints.json',
];
foreach ($config['builds'] as $name => $path) {
$factories[$name] = new Reference($this->entrypointFactory($container, $name, $path));
};
$factories[$name] = $this->entrypointFactory($container, $name, $path, $config['cache']);
$cacheKeys[rawurlencode($name)] = $path;
}

$container->getDefinition('webpack_encore.entrypoint_lookup.cache_warmer')
->replaceArgument(0, $cacheKeys);

$container->getDefinition('webpack_encore.entrypoint_lookup')
->replaceArgument(0, $factories['_default']);
$container->getDefinition('webpack_encore.entrypoint_lookup_collection')
->replaceArgument(0, ServiceLocatorTagPass::register($container, $factories));
}

private function entrypointFactory(ContainerBuilder $container, string $name, string $path): string
private function entrypointFactory(ContainerBuilder $container, string $name, string $path, bool $cacheEnabled): Reference
{
$id = sprintf('webpack_encore.entrypoint_lookup[%s]', $name);
$container->setDefinition($id, new Definition(EntrypointLookup::class, [$path.'/entrypoints.json']));
return $id;
$arguments = [$path.'/entrypoints.json', $cacheEnabled ? new Reference('webpack_encore.cache') : null, $name];
$container->setDefinition($id, new Definition(EntrypointLookup::class, $arguments));

return new Reference($id);
}
}
23 changes: 18 additions & 5 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@
<services>
<defaults public="false" />

<service id="webpack_encore.entrypoint_lookup" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookup">
<argument /> <!-- entrypoints.json path -->
</service>
<service id="webpack_encore.entrypoint_lookup_collection" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookupCollection">
<argument /> <!-- build list of entrypoints path -->
<argument /> <!-- build list of entrypoints locator -->
</service>

<service id="webpack_encore.tag_renderer" class="Symfony\WebpackEncoreBundle\Asset\TagRenderer">
Expand All @@ -25,12 +22,28 @@
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
<tag name="container.service_locator" />
<argument type="collection">
<argument key="webpack_encore.entrypoint_lookup" type="service" id="webpack_encore.entrypoint_lookup" />
<argument key="webpack_encore.entrypoint_lookup_collection" type="service" id="webpack_encore.entrypoint_lookup_collection" />
<argument key="webpack_encore.tag_renderer" type="service" id="webpack_encore.tag_renderer" />
</argument>
</service>
</argument>
</service>

<service id="webpack_encore.entrypoint_lookup.cache_warmer" class="Symfony\WebpackEncoreBundle\CacheWarmer\EntrypointCacheWarmer">
<tag name="kernel.cache_warmer" />
<argument /> <!-- build list of entrypoint paths -->
<argument>%kernel.cache_dir%/webpack_encore.cache.php</argument>
<argument type="service" id="cache.webpack_encore" />
</service>

<service id="webpack_encore.cache" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
<argument>%kernel.cache_dir%/webpack_encore.cache.php</argument>
<argument type="service" id="cache.webpack_encore" />
</service>

<service id="cache.webpack_encore" parent="cache.system" public="false">
<tag name="cache.pool" />
</service>
</services>
</container>
38 changes: 38 additions & 0 deletions tests/Asset/EntrypointLookupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Symfony\WebpackEncoreBundle\Tests\Asset;

use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup;
use PHPUnit\Framework\TestCase;
use Symfony\WebpackEncoreBundle\Exception\EntrypointNotFoundException;
Expand Down Expand Up @@ -144,4 +146,40 @@ public function testExceptionOnEntryWithExtension()
{
$this->entrypointLookup->getJavaScriptFiles('my_entry.js');
}

public function testCachingEntryPointLookupCacheMissed()
{
$filename = tempnam(sys_get_temp_dir(), 'WebpackEncoreBundle');
file_put_contents($filename, self::$testJson);

$cache = new ArrayAdapter();
$entrypointLookup = new EntrypointLookup($filename, $cache, 'cacheKey');

$this->assertEquals(
['file1.js', 'file2.js'],
$entrypointLookup->getJavaScriptFiles('my_entry')
);
// Test it saved the result to cache
$cached = $cache->getItem('cacheKey');
$this->assertTrue($cached->isHit());
$this->assertEquals(json_decode(self::$testJson, true), $cached->get());
}

public function testCachingEntryPointLookupCacheHit()
{
$filename = tempnam(sys_get_temp_dir(), 'WebpackEncoreBundle');
file_put_contents($filename, self::$testJson);

$cache = new ArrayAdapter();
$entrypointLookup = new EntrypointLookup($filename, $cache, 'cacheKey');

$cached = $cache->getItem('cacheKey');
$cached->set(json_decode(self::$testJson, true));
$cache->save($cached);

$this->assertEquals(
['file1.js', 'file2.js'],
$entrypointLookup->getJavaScriptFiles('my_entry')
);
}
}
1 change: 1 addition & 0 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public function registerContainerConfiguration(LoaderInterface $loader)

$container->loadFromExtension('webpack_encore', [
'output_path' => __DIR__.'/fixtures/build',
'cache' => false,
'builds' => [
'different_build' => __DIR__.'/fixtures/different_build'
]
Expand Down