Skip to content

Commit

Permalink
cache implementation for manifest.json
Browse files Browse the repository at this point in the history
  • Loading branch information
lhapaipai committed Nov 2, 2023
1 parent de63d45 commit bdb710e
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 57 deletions.
5 changes: 5 additions & 0 deletions MiGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!--
```diff
- Pentatrion\ViteBundle\Asset\ViteAssetVersionStrategy
+ Pentatrion\ViteBundle\Asset\VersionStrategy\ViteAssetVersionStrategy
``` -->
12 changes: 6 additions & 6 deletions src/Asset/EntrypointsLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class EntrypointsLookup

public function __construct(
string $basePath,
string $configName = '_default',
?string $configName, // for cache to retrieve content : configName is cache key
bool $throwOnMissingEntry = false,
CacheItemPoolInterface $cache = null
) {
Expand Down Expand Up @@ -44,10 +44,10 @@ public function hasFile(): bool
private function getFileContent(): array
{
if ($this->cache) {
$cached = $this->cache->getItem($this->configName);
$entrypointsCacheItem = $this->cache->getItem("{$this->configName}.entrypoints");

if ($cached->isHit()) {
$this->fileInfos['content'] = $cached->get();
if ($entrypointsCacheItem->isHit()) {
$this->fileInfos['content'] = $entrypointsCacheItem->get();
}
}

Expand All @@ -63,8 +63,8 @@ private function getFileContent(): array
throw new \Exception($this->fileInfos['entrypointsPath'].' : entryPoints, base or viteServer not exists');
}

if (isset($cached)) {
$this->cache->save($cached->set($content));
if (isset($entrypointsCacheItem)) {
$this->cache->save($entrypointsCacheItem->set($content));
}

$this->fileInfos['content'] = $content;
Expand Down
85 changes: 57 additions & 28 deletions src/Asset/ViteAssetVersionStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Pentatrion\ViteBundle\Asset;

use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Asset\Exception\AssetNotFoundException;
use Symfony\Component\Asset\Exception\RuntimeException;
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
Expand All @@ -11,44 +12,46 @@ class ViteAssetVersionStrategy implements VersionStrategyInterface
{
private string $publicPath;
private array $configs;
private string $configName;
private $useAbsoluteUrl;
private $router;
private ?CacheItemPoolInterface $cache;
private ?RouterInterface $router;
private bool $strictMode;

private string $manifestPath;
private string $entrypointsPath;
private ?string $viteMode = null;
private string $basePath;
private $manifestData;
private $entrypointsData;
private ?array $config = null;
private bool $strictMode;
private ?string $mode = null;

public function __construct(
string $publicPath,
array $configs,
string $defaultConfigName,
bool $useAbsoluteUrl,
CacheItemPoolInterface $cache = null,
RouterInterface $router = null,
bool $strictMode = true
) {
$this->publicPath = $publicPath;
$this->configs = $configs;
$this->strictMode = $strictMode;
$this->configName = $defaultConfigName;
$this->useAbsoluteUrl = $useAbsoluteUrl;
$this->cache = $cache;
$this->router = $router;
$this->strictMode = $strictMode;

$this->setConfig($defaultConfigName);
$this->setConfig($this->configName);

if (($scheme = parse_url($this->manifestPath, \PHP_URL_SCHEME)) && 0 === strpos($scheme, 'http')) {
if (($scheme = parse_url($this->basePath.'manifest.json', \PHP_URL_SCHEME)) && 0 === strpos($scheme, 'http')) {
throw new \Exception('You can\'t use a remote manifest with ViteAssetVersionStrategy');
}
}

public function setConfig(string $configName): void
{
$this->mode = null;
$this->config = $this->configs[$configName];
$this->manifestPath = $this->publicPath.$this->config['base'].'manifest.json';
$this->entrypointsPath = $this->publicPath.$this->config['base'].'entrypoints.json';
$this->viteMode = null;
$this->configName = $configName;
$this->basePath = $this->publicPath.$this->configs[$configName]['base'];
}

/**
Expand Down Expand Up @@ -78,39 +81,65 @@ private function completeURL(string $path)

private function getassetsPath(string $path): ?string
{
if (null === $this->mode) {
if (!is_file($this->entrypointsPath)) {
throw new RuntimeException(sprintf('assets entrypoints file "%s" does not exist. Did you forget configure your `build_dir` in pentatrion_vite.yml?', $this->entrypointsPath));
if (null === $this->viteMode) {
$manifestPath = $this->basePath.'manifest.json';
$entrypointsPath = $this->basePath.'entrypoints.json';

$this->entrypointsData = null;
$this->manifestData = null;

$this->viteMode = is_file($manifestPath) ? 'build' : 'dev';

if (!is_file($entrypointsPath)) {
throw new RuntimeException(sprintf('assets entrypoints file "%s" does not exist. Did you forget configure your `build_dir` in pentatrion_vite.yml?', $entrypointsPath));
}

if (is_file($this->manifestPath)) {
// when vite server is running manifest file doesn't exists
$this->mode = 'build';
if ('build' === $this->viteMode && $this->cache) {
$entrypointsCacheItem = $this->cache->getItem("{$this->configName}.entrypoints");
$manifestCacheItem = $this->cache->getItem("{$this->configName}.manifest");

if ($entrypointsCacheItem->isHit()) {
$this->entrypointsData = $entrypointsCacheItem->get();
}
if ($manifestCacheItem->isHit()) {
$this->manifestData = $manifestCacheItem->get();
}
}

if ('build' === $this->viteMode && is_null($this->manifestData)) {
try {
$this->manifestData = json_decode(file_get_contents($this->manifestPath), true, 512, \JSON_THROW_ON_ERROR);
$this->manifestData = json_decode(file_get_contents($manifestPath), true, 512, \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new RuntimeException(sprintf('Error parsing JSON from entrypoints file "%s": ', $this->manifestPath).$e->getMessage(), 0, $e);
throw new RuntimeException(sprintf('Error parsing JSON from entrypoints file "%s": ', $manifestPath).$e->getMessage(), 0, $e);
}
} else {
$this->mode = 'dev';
}

if (is_null($this->entrypointsData)) {
try {
$this->entrypointsData = json_decode(file_get_contents($this->entrypointsPath), true, 512, \JSON_THROW_ON_ERROR);
$this->entrypointsData = json_decode(file_get_contents($entrypointsPath), true, 512, \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new RuntimeException(sprintf('Error parsing JSON from entrypoints file "%s": ', $this->manifestPath).$e->getMessage(), 0, $e);
throw new RuntimeException(sprintf('Error parsing JSON from entrypoints file "%s": ', $manifestPath).$e->getMessage(), 0, $e);
}
}

if (isset($entrypointsCacheItem)) {
$this->cache->save($entrypointsCacheItem->set($this->entrypointsData));
}
if (isset($manifestCacheItem)) {
$this->cache->save($manifestCacheItem->set($this->manifestData));
}
}

if ('build' === $this->mode) {
if ('build' === $this->viteMode) {
if (isset($this->manifestData[$path])) {
return $this->completeURL($this->config['base'].$this->manifestData[$path]['file']);
return $this->completeURL($this->basePath.$this->manifestData[$path]['file']);
}
} else {
return $this->entrypointsData['viteServer'].$this->entrypointsData['base'].$path;
}

if ($this->strictMode) {
$message = sprintf('assets "%s" not found in manifest file "%s".', $path, $this->manifestPath);
$message = sprintf('assets "%s" not found in manifest file "%s".', $path, $manifestPath);

Check failure on line 142 in src/Asset/ViteAssetVersionStrategy.php

View workflow job for this annotation

GitHub Actions / CI (8.1)

Variable $manifestPath might not be defined.

Check failure on line 142 in src/Asset/ViteAssetVersionStrategy.php

View workflow job for this annotation

GitHub Actions / CI (8.1)

Variable $manifestPath might not be defined.
$alternatives = $this->findAlternatives($path, $this->manifestData);
if (\count($alternatives) > 0) {
$message .= sprintf(' Did you mean one of these? "%s".', implode('", "', $alternatives));
Expand Down
33 changes: 24 additions & 9 deletions src/CacheWarmer/EntrypointsCacheWarmer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,50 @@

use Exception;
use Pentatrion\ViteBundle\Asset\EntrypointsLookup;
use Pentatrion\ViteBundle\Exception\EntrypointsFileNotFoundException;
use Pentatrion\ViteBundle\Asset\ViteAssetVersionStrategy;
use Symfony\Bundle\FrameworkBundle\CacheWarmer\AbstractPhpFileCacheWarmer;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

class EntrypointsCacheWarmer extends AbstractPhpFileCacheWarmer
{
private $basePaths;
private string $publicPath;
private array $configs;

public function __construct(array $basePaths, string $phpCacheFile)
public function __construct(
string $publicPath,
array $configs,
string $phpCacheFile)
{
$this->basePaths = $basePaths;
$this->publicPath = $publicPath;
$this->configs = $configs;
parent::__construct($phpCacheFile);
}

protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter): bool
{
foreach ($this->basePaths as $basePath) {
$entrypointsPath = $basePath.'entrypoints.json';
foreach ($this->configs as $configName => $config) {
$entrypointsPath = $this->publicPath.$this->configs[$configName]['base'].'entrypoints.json';

if (!file_exists($entrypointsPath)) {
continue;
}

$entrypointsLookup = new EntrypointsLookup($entrypointsPath);
$viteAssetVersionStrategy = new ViteAssetVersionStrategy(
$this->publicPath,
$this->configs,
$configName,
false,
$arrayAdapter,
null,
false
);
// $entrypointsLookup = new EntrypointsLookup($basePath, $configName, false, $arrayAdapter);
try {
// any method that will call getFileContent and generate
// the file in cache.
$entrypointsLookup->getBase();
} catch (EntrypointsFileNotFoundException $e) {
// $entrypointsLookup->getBase();
$viteAssetVersionStrategy->applyVersion('/some-dummy-path');
} catch (\Exception $e) {
// ignore exception
}
}
Expand Down
10 changes: 4 additions & 6 deletions src/DependencyInjection/PentatrionViteExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ public function load(array $bundleConfigs, ContainerBuilder $container): void
$lookupFactories = [];
$tagRendererFactories = [];
$configs = [];
$cacheKeys = [];

foreach ($bundleConfig['configs'] as $configName => $config) {
if (!preg_match('/^[a-zA-Z_]+$/', $configName)) {
throw new \Exception('Invalid config name, you should use only a-z A-Z and _ characters.');
}

$configs[$configName] = $configPrepared = self::prepareConfig($config);
$lookupFactories[$configName] = $this->entrypointsLookupFactory(
$container,
Expand All @@ -58,7 +61,6 @@ public function load(array $bundleConfigs, ContainerBuilder $container): void
$bundleConfig['cache']
);
$tagRendererFactories[$configName] = $this->tagRendererFactory($container, $configName, $configPrepared);
$cacheKeys[] = $this->resolveBasePath($container, $configPrepared);
}
} else {
$defaultConfigName = '_default';
Expand All @@ -75,7 +77,6 @@ public function load(array $bundleConfigs, ContainerBuilder $container): void
$tagRendererFactories = [
'_default' => $this->tagRendererFactory($container, $defaultConfigName, $configPrepared),
];
$cacheKeys = [$this->resolveBasePath($container, $configPrepared)];
}

if ('link-header' === $bundleConfig['preload']) {
Expand All @@ -96,9 +97,6 @@ public function load(array $bundleConfigs, ContainerBuilder $container): void
$container->getDefinition('pentatrion_vite.tag_renderer_collection')
->addArgument(ServiceLocatorTagPass::register($container, $tagRendererFactories))
->addArgument($defaultConfigName);

$container->getDefinition('pentatrion_vite.cache_warmer')
->replaceArgument(0, $cacheKeys);
}

private function entrypointsLookupFactory(
Expand Down
20 changes: 12 additions & 8 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,33 @@ services:
- "%pentatrion_vite.configs%"
- "%pentatrion_vite.default_config%"
- "%pentatrion_vite.absolute_url%"
- "@?pentatrion_vite.cache"
- "@?router"
- true


pentatrion_vite.preload_assets_event_listener:
class: Pentatrion\ViteBundle\EventListener\PreloadAssetsEventListener
tags: ["kernel.event_subscriber"]
arguments:
- "@pentatrion_vite.entrypoint_renderer"

pentatrion_vite.cache_warmer:
class: Pentatrion\ViteBundle\CacheWarmer\EntrypointsCacheWarmer
tags: ["kernel.cache_warmer"]
arguments:
- []
- '%kernel.cache_dir%/pentatrion_vite.cache.php'

pentatrion_vite.cache:
class: Symfony\Component\Cache\Adapter\PhpArrayAdapter
factory: [Symfony\Component\Cache\Adapter\PhpArrayAdapter, create]
arguments:
- '%kernel.cache_dir%/pentatrion_vite.cache.php'
- '@cache.pentatrion_vite'
- '@cache.pentatrion_vite_fallback'

pentatrion_vite.cache_warmer:
class: Pentatrion\ViteBundle\CacheWarmer\EntrypointsCacheWarmer
tags: ["kernel.cache_warmer"]
arguments:
- "%kernel.project_dir%%pentatrion_vite.public_directory%"
- "%pentatrion_vite.configs%"
- '%kernel.cache_dir%/pentatrion_vite.cache.php'

cache.pentatrion_vite:
cache.pentatrion_vite_fallback:
tags: ["cache.pool"]
parent: cache.system
1 change: 1 addition & 0 deletions tests/Asset/EntrypointRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ private function getEntrypointsLookup($prefix)
{
return new EntrypointsLookup(
__DIR__.'/../fixtures/entrypoints/'.$prefix.'/',
'_default',
true
);
}
Expand Down
1 change: 1 addition & 0 deletions tests/Asset/EntrypointsLookupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ private function getEntrypointsLookup($prefix)
{
return new EntrypointsLookup(
__DIR__.'/../fixtures/entrypoints/'.$prefix.'/',
'_default',
true
);
}
Expand Down

0 comments on commit bdb710e

Please sign in to comment.