From 07da81167a08c80af69faf4555428f565d27c01e Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Thu, 21 Mar 2024 07:03:01 +0100 Subject: [PATCH 1/5] features: CachePlugin and CacheStrategy are now interfaces --- phpstan-baseline.neon | 50 +++-- src/CachingStrategy/AssetCache.php | 36 ++-- src/CachingStrategy/BackgroundSync.php | 14 +- src/CachingStrategy/CacheStrategy.php | 15 +- src/CachingStrategy/FontCache.php | 38 ++-- src/CachingStrategy/GoogleFontCache.php | 21 +- src/CachingStrategy/ImageCache.php | 20 +- src/CachingStrategy/ManifestCache.php | 8 +- src/CachingStrategy/ResourceCaches.php | 33 +-- src/CachingStrategy/WorkboxCacheStrategy.php | 192 +++++++++++++----- src/Command/ListCacheStrategiesCommand.php | 18 +- .../RegexMatchCallbackHandler.php | 18 -- .../AppendCacheStrategies.php | 21 +- src/WorkboxPlugin/BackgroundSyncPlugin.php | 44 ++-- src/WorkboxPlugin/BroadcastUpdatePlugin.php | 33 ++- src/WorkboxPlugin/CachePlugin.php | 13 +- src/WorkboxPlugin/CacheableResponsePlugin.php | 37 +++- src/WorkboxPlugin/ExpirationPlugin.php | 29 ++- src/WorkboxPlugin/RangeRequestsPlugin.php | 11 +- 19 files changed, 393 insertions(+), 258 deletions(-) delete mode 100644 src/MatchCallbackHandler/RegexMatchCallbackHandler.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 33ad337..3bf735d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,27 +1,52 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$name of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects string, string\\|null given\\.$#" + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" count: 1 path: src/CachingStrategy/AssetCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$name of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withName\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/CachingStrategy/AssetCache.php + + - + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" count: 1 path: src/CachingStrategy/AssetCache.php - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" count: 1 + path: src/CachingStrategy/AssetCache.php + + - + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" + count: 1 path: src/CachingStrategy/FontCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" count: 1 path: src/CachingStrategy/FontCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" + count: 2 + path: src/CachingStrategy/FontCache.php + + - + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" + count: 1 + path: src/CachingStrategy/ResourceCaches.php + + - + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/CachingStrategy/ResourceCaches.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" count: 1 path: src/CachingStrategy/ResourceCaches.php @@ -703,19 +728,4 @@ parameters: - message: "#^Property SpomkyLabs\\\\PwaBundle\\\\Subscriber\\\\PwaDevServerSubscriber\\:\\:\\$jsonOptions type has no value type specified in iterable type array\\.$#" count: 1 - path: src/Subscriber/PwaDevServerSubscriber.php - - - - message: "#^Part \\$broadcastChannel \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php - - - - message: "#^Part \\$maxRetentionTime \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php - - - - message: "#^Part \\$queueName \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 2 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php \ No newline at end of file + path: src/Subscriber/PwaDevServerSubscriber.php \ No newline at end of file diff --git a/src/CachingStrategy/AssetCache.php b/src/CachingStrategy/AssetCache.php index 8cd8f5a..927ccf4 100644 --- a/src/CachingStrategy/AssetCache.php +++ b/src/CachingStrategy/AssetCache.php @@ -49,23 +49,25 @@ public function getCacheStrategies(): array $urls = json_decode($this->serializer->serialize($this->getAssets(), 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]), true); - return [ - WorkboxCacheStrategy::create( - $this->workbox->assetCache->cacheName, - CacheStrategy::STRATEGY_CACHE_FIRST, - sprintf("({url}) => url.pathname.startsWith('%s')", $this->assetPublicPrefix), - $this->workbox->enabled && $this->workbox->assetCache->enabled, - true, - null, - [ - ExpirationPlugin::create( - count($this->getAssets()) * 2, - $this->workbox->assetCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, - ), - ], - $urls - ), - ]; + + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled && $this->workbox->assetCache->enabled, + true, + CacheStrategy::STRATEGY_CACHE_FIRST, + sprintf("({url}) => url.pathname.startsWith('%s')", $this->assetPublicPrefix), + ) + ->withName($this->workbox->assetCache->cacheName) + ->withPlugin( + ExpirationPlugin::create( + count($this->getAssets()) * 2, + $this->workbox->assetCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, + ), + ); + + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + return [$strategy]; } /** diff --git a/src/CachingStrategy/BackgroundSync.php b/src/CachingStrategy/BackgroundSync.php index 9316a8e..29c9d89 100644 --- a/src/CachingStrategy/BackgroundSync.php +++ b/src/CachingStrategy/BackgroundSync.php @@ -33,21 +33,21 @@ public function getCacheStrategies(): array $strategies = []; foreach ($this->workbox->backgroundSync as $sync) { $strategies[] = WorkboxCacheStrategy::create( - 'BackgroundSync API', - CacheStrategy::STRATEGY_NETWORK_ONLY, - $this->prepareMatchCallback($sync->matchCallback), $this->workbox->enabled, true, - null, - [ + CacheStrategy::STRATEGY_NETWORK_ONLY, + $this->prepareMatchCallback($sync->matchCallback) + ) + ->withName('Background Sync') + ->withPlugin( BackgroundSyncPlugin::create( $sync->queueName, $sync->maxRetentionTime, $sync->forceSyncFallback, $sync->broadcastChannel ), - ] - ); + ) + ->withMethod($sync->method); } return $strategies; diff --git a/src/CachingStrategy/CacheStrategy.php b/src/CachingStrategy/CacheStrategy.php index bc4f459..ccfb686 100644 --- a/src/CachingStrategy/CacheStrategy.php +++ b/src/CachingStrategy/CacheStrategy.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; -abstract readonly class CacheStrategy +interface CacheStrategy { public const STRATEGY_CACHE_FIRST = 'CacheFirst'; @@ -24,12 +24,11 @@ self::STRATEGY_STALE_WHILE_REVALIDATE, ]; - public function __construct( - public string $name, - public bool $enabled, - public bool $requireWorkbox, - ) { - } + public function getName(): ?string; - abstract public function render(string $cacheObjectName, int $jsonOptions = 0): string; + public function isEnabled(): bool; + + public function needsWorkbox(): bool; + + public function render(string $cacheObjectName, bool $debug = false): string; } diff --git a/src/CachingStrategy/FontCache.php b/src/CachingStrategy/FontCache.php index 050eb41..775e6f3 100644 --- a/src/CachingStrategy/FontCache.php +++ b/src/CachingStrategy/FontCache.php @@ -46,24 +46,26 @@ public function getCacheStrategies(): array ]), true); $maxEntries = count($urls) + ($this->workbox->fontCache->maxEntries ?? 60); - return [ - WorkboxCacheStrategy::create( - $this->workbox->fontCache->cacheName ?? 'fonts', - CacheStrategy::STRATEGY_CACHE_FIRST, - "({request}) => request.destination === 'font'", - $this->workbox->enabled && $this->workbox->fontCache->enabled, - true, - null, - [ - ExpirationPlugin::create( - $maxEntries, - $this->workbox->fontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, - ), - CacheableResponsePlugin::create(), - ], - $urls - ), - ]; + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled && $this->workbox->fontCache->enabled, + true, + CacheStrategy::STRATEGY_CACHE_FIRST, + "({request}) => request.destination === 'font'" + ) + ->withName($this->workbox->fontCache->cacheName ?? 'fonts') + ->withMethod('GET') + ->withPlugin( + CacheableResponsePlugin::create(), + ExpirationPlugin::create( + $maxEntries, + $this->workbox->fontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365 + ), + ); + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + + return [$strategy]; } /** diff --git a/src/CachingStrategy/GoogleFontCache.php b/src/CachingStrategy/GoogleFontCache.php index 12db19e..94cf243 100644 --- a/src/CachingStrategy/GoogleFontCache.php +++ b/src/CachingStrategy/GoogleFontCache.php @@ -28,27 +28,26 @@ public function getCacheStrategies(): array return [ WorkboxCacheStrategy::create( - $prefix . 'google-fonts-stylesheets', + $this->workbox->enabled && $this->workbox->googleFontCache->enabled, + true, CacheStrategy::STRATEGY_STALE_WHILE_REVALIDATE, "({url}) => url.origin === 'https://fonts.googleapis.com'", - $this->workbox->enabled && $this->workbox->googleFontCache->enabled, - true - ), + ) + ->withName($prefix . 'google-fonts-stylesheets'), WorkboxCacheStrategy::create( - $prefix . 'google-fonts-webfonts', - CacheStrategy::STRATEGY_CACHE_FIRST, - "({url}) => url.origin === 'https://fonts.gstatic.com'", $this->workbox->enabled && $this->workbox->googleFontCache->enabled, true, - null, - [ + CacheStrategy::STRATEGY_CACHE_FIRST, + "({url}) => url.origin === 'https://fonts.gstatic.com'" + ) + ->withName($prefix . 'google-fonts-webfonts') + ->withPlugin( CacheableResponsePlugin::create(), ExpirationPlugin::create( $this->workbox->googleFontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, $this->workbox->googleFontCache->maxEntries ?? 30 ), - ] - ), + ), ]; } } diff --git a/src/CachingStrategy/ImageCache.php b/src/CachingStrategy/ImageCache.php index b5809e3..db46eee 100644 --- a/src/CachingStrategy/ImageCache.php +++ b/src/CachingStrategy/ImageCache.php @@ -6,8 +6,6 @@ use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; -use SpomkyLabs\PwaBundle\WorkboxPlugin\CacheableResponsePlugin; -use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -30,23 +28,15 @@ public function getCacheStrategies(): array { return [ WorkboxCacheStrategy::create( - $this->workbox->imageCache->cacheName ?? 'images', + $this->workbox->enabled && $this->workbox->imageCache->enabled, + true, CacheStrategy::STRATEGY_CACHE_FIRST, sprintf( "({request, url}) => (request.destination === 'image' && !url.pathname.startsWith('%s'))", $this->assetPublicPrefix - ), - $this->workbox->enabled && $this->workbox->imageCache->enabled, - true, - null, - [ - CacheableResponsePlugin::create(), - ExpirationPlugin::create( - $this->workbox->imageCache->maxEntries ?? 60, - $this->workbox->imageCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 7 - ), - ] - ), + ) + ) + ->withName($this->workbox->imageCache->cacheName ?? 'images'), ]; } } diff --git a/src/CachingStrategy/ManifestCache.php b/src/CachingStrategy/ManifestCache.php index 3fa985a..4840298 100644 --- a/src/CachingStrategy/ManifestCache.php +++ b/src/CachingStrategy/ManifestCache.php @@ -27,12 +27,12 @@ public function getCacheStrategies(): array { return [ WorkboxCacheStrategy::create( - 'manifest', + $this->workbox->enabled && $this->workbox->cacheManifest, + true, CacheStrategy::STRATEGY_STALE_WHILE_REVALIDATE, sprintf("({url}) => '%s' === url.pathname", $this->manifestPublicUrl), - $this->workbox->enabled && $this->workbox->cacheManifest, - true - ), + ) + ->withName('manifest'), ]; } } diff --git a/src/CachingStrategy/ResourceCaches.php b/src/CachingStrategy/ResourceCaches.php index 6ccb90a..62ed80f 100644 --- a/src/CachingStrategy/ResourceCaches.php +++ b/src/CachingStrategy/ResourceCaches.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\Serializer\Encoder\JsonEncode; use Symfony\Component\Serializer\SerializerInterface; +use function count; use const JSON_PRETTY_PRINT; use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; @@ -52,7 +53,7 @@ public function getCacheStrategies(): array $routes = $this->serializer->serialize($resourceCache->urls, 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]); - $url = json_decode($routes, true, 512, JSON_THROW_ON_ERROR); + $urls = json_decode($routes, true, 512, JSON_THROW_ON_ERROR); $cacheName = $resourceCache->cacheName ?? sprintf('page-cache-%d', $id); @@ -72,20 +73,22 @@ public function getCacheStrategies(): array $plugins[] = ExpirationPlugin::create($resourceCache->maxEntries, $resourceCache->maxAgeInSeconds()); } - $strategies[] = - WorkboxCacheStrategy::create( - $cacheName, - $resourceCache->strategy, - $this->prepareMatchCallback($resourceCache->matchCallback), - $this->workbox->enabled, - true, - null, - $plugins, - $url, - [ - 'networkTimeoutSeconds' => $resourceCache->networkTimeout, - ] - ); + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled, + true, + $resourceCache->strategy, + $this->prepareMatchCallback($resourceCache->matchCallback) + ) + ->withName($cacheName) + ->withPlugin(...$plugins) + ->withOptions([ + 'networkTimeoutSeconds' => $resourceCache->networkTimeout, + ]); + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + + $strategies[] = $strategy; } return $strategies; diff --git a/src/CachingStrategy/WorkboxCacheStrategy.php b/src/CachingStrategy/WorkboxCacheStrategy.php index 892fb31..7fb4ec7 100644 --- a/src/CachingStrategy/WorkboxCacheStrategy.php +++ b/src/CachingStrategy/WorkboxCacheStrategy.php @@ -6,62 +6,108 @@ use SpomkyLabs\PwaBundle\WorkboxPlugin\CachePlugin; use function in_array; +use const JSON_PRETTY_PRINT; +use const JSON_THROW_ON_ERROR; +use const JSON_UNESCAPED_SLASHES; +use const JSON_UNESCAPED_UNICODE; -final readonly class WorkboxCacheStrategy extends CacheStrategy +final class WorkboxCacheStrategy implements CacheStrategy { + private null|string $name = null; + + private null|string $method = null; + /** - * @param array $plugins - * @param array $preloadUrls - * @param array $options + * @var array + */ + private array $plugins = []; + + /** + * @var array */ + private array $preloadUrls = []; + + /** + * @var array + */ + private array $options = []; + public function __construct( - string $name, - public string $strategy, - public string $matchCallback, - bool $enabled, - bool $requireWorkbox, - public null|string $method = null, - public array $plugins = [], - public array $preloadUrls = [], - public array $options = [], + public readonly bool $enabled, + public readonly bool $needsWorkbox, + public readonly string $strategy, + public readonly string $matchCallback, ) { - parent::__construct($name, $enabled, $requireWorkbox); } - /** - * @param array $plugins - * @param array $preloadUrls - * @param array $options - */ public static function create( - string $name, - string $strategy, - string $matchCallback, bool $enabled, bool $requireWorkbox, - null|string $method = null, - array $plugins = [], - array $preloadUrls = [], - array $options = [], + string $strategy, + string $matchCallback, ): static { - return new static( - $name, - $strategy, - $matchCallback, - $enabled, - $requireWorkbox, - $method, - $plugins, - $preloadUrls, - $options - ); + return new static($enabled, $requireWorkbox, $strategy, $matchCallback,); + } + + public function withName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function withMethod(string $method): static + { + $this->method = $method; + + return $this; + } + + public function withPlugin(CachePlugin $plugin, CachePlugin ...$plugins): static + { + $this->plugins = array_merge([$plugin], $plugins); + return $this; + } + + public function withPreloadUrl(string $preloadUrl, string ...$preloadUrls): static + { + $this->preloadUrls = array_merge([$preloadUrl], $preloadUrls); + return $this; + } + + /** + * @param array $options + */ + public function withOptions(array $options): static + { + $this->options = $options; + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function isEnabled(): bool + { + return $this->enabled; } - public function render(string $cacheObjectName, int $jsonOptions = 0): string + public function needsWorkbox(): bool + { + return $this->needsWorkbox; + } + + public function render(string $cacheObjectName, bool $debug = false): string { if ($this->enabled === false) { return ''; } + $jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR; + if ($debug === true) { + $jsonOptions |= JSON_PRETTY_PRINT; + } $timeout = ''; if (in_array( @@ -73,22 +119,39 @@ public function render(string $cacheObjectName, int $jsonOptions = 0): string } $cacheName = ''; if ($this->strategy !== self::STRATEGY_NETWORK_ONLY) { - $cacheName = sprintf("cacheName: '%s',", $this->name ?? $cacheObjectName); + $cacheName = sprintf("cacheName: '%s',", $this->getName() ?? $cacheObjectName); } $plugins = sprintf('[%s]', implode(', ', array_map( fn (CachePlugin $plugin) => $plugin->render($jsonOptions), $this->plugins ))); + $method = $this->method !== null ? ",'{$this->method}'" : ''; + + $declaration = ''; + if ($debug) { + $declaration .= <<strategy} +// Match: {$this->matchCallback} +// Cache Name: {$this->getName()} +// Enabled: {$this->enabled} +// Needs Workbox: {$this->needsWorkbox()} +// Method: {$this->method} + + +DEBUG_STATEMENT; + } + + $declaration .= <<strategy}({ {$timeout}{$cacheName}plugins: {$plugins} }); -workbox.routing.registerRoute( - {$this->matchCallback}, - {$cacheObjectName} -); -FONT_CACHE_RULE_STRATEGY; +workbox.routing.registerRoute({$this->matchCallback},{$cacheObjectName}{$method}); + +ROUTE_REGISTRATION; if ($this->preloadUrls !== []) { $fontUrls = json_encode($this->preloadUrls, $jsonOptions); @@ -103,9 +166,46 @@ public function render(string $cacheObjectName, int $jsonOptions = 0): string ); event.waitUntil(Promise.all(done)); }); + + ASSET_CACHE_RULE_PRELOAD; } - return trim($declaration); + $declaration .= <<method; + } + + /** + * @return array + */ + public function getPlugins(): array + { + return $this->plugins; + } + + /** + * @return array + */ + public function getPreloadUrls(): array + { + return $this->preloadUrls; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; } } diff --git a/src/Command/ListCacheStrategiesCommand.php b/src/Command/ListCacheStrategiesCommand.php index f20efd2..52075b5 100644 --- a/src/Command/ListCacheStrategiesCommand.php +++ b/src/Command/ListCacheStrategiesCommand.php @@ -43,22 +43,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($strategies as $strategy) { if ($strategy instanceof WorkboxCacheStrategy) { $table->addRow([ - $strategy->name, + $strategy->getName(), $strategy->strategy, $strategy->matchCallback, - $strategy->enabled ? 'Yes' : 'No', - $strategy->requireWorkbox ? 'Yes' : 'No', - Yaml::dump(array_map(fn (CachePlugin $v): string => $v->name, $strategy->plugins)), - count($strategy->preloadUrls), - Yaml::dump($strategy->options), + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', + Yaml::dump(array_map(fn (CachePlugin $v): string => $v->getName(), $strategy->getPlugins())), + count($strategy->getPreloadUrls()), + Yaml::dump($strategy->getOptions()), ]); } else { $table->addRow([ - $strategy->name, + $strategy->getName(), '---', '---', - $strategy->enabled ? 'Yes' : 'No', - $strategy->requireWorkbox ? 'Yes' : 'No', + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', '', '', '', diff --git a/src/MatchCallbackHandler/RegexMatchCallbackHandler.php b/src/MatchCallbackHandler/RegexMatchCallbackHandler.php deleted file mode 100644 index 3dbb669..0000000 --- a/src/MatchCallbackHandler/RegexMatchCallbackHandler.php +++ /dev/null @@ -1,18 +0,0 @@ - $cacheStrategies */ @@ -24,13 +18,8 @@ public function __construct( #[TaggedIterator('spomky_labs_pwa.cache_strategy')] private iterable $cacheStrategies, #[Autowire('%kernel.debug%')] - bool $debug, + public bool $debug, ) { - $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR; - if ($debug === true) { - $options |= JSON_PRETTY_PRINT; - } - $this->jsonOptions = $options; } public function process(): string @@ -38,14 +27,14 @@ public function process(): string $body = ''; foreach ($this->cacheStrategies as $idCacheStrategy => $cacheStrategy) { foreach ($cacheStrategy->getCacheStrategies() as $idStrategy => $strategy) { - if ($strategy->enabled === false) { + if ($strategy->isEnabled() === false) { continue; } - $body .= PHP_EOL . trim($strategy->render( + $body .= PHP_EOL . $strategy->render( sprintf('cache_%d_%d', $idCacheStrategy, $idStrategy), - $this->jsonOptions - )); + $this->debug + ); } } diff --git a/src/WorkboxPlugin/BackgroundSyncPlugin.php b/src/WorkboxPlugin/BackgroundSyncPlugin.php index e8ee165..21c8008 100644 --- a/src/WorkboxPlugin/BackgroundSyncPlugin.php +++ b/src/WorkboxPlugin/BackgroundSyncPlugin.php @@ -4,16 +4,28 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BackgroundSyncPlugin extends CachePlugin +final readonly class BackgroundSyncPlugin implements CachePlugin { + private const NAME = 'BackgroundSyncPlugin'; + + public function __construct( + public string $queueName, + public bool $forceSyncFallback, + public null|string $broadcastChannel, + public int $maxRetentionTime, + ) { + } + + public function getName(): string + { + return self::NAME; + } + public function render(int $jsonOptions = 0): string { - $forceSyncFallback = $this->options['forceSyncFallback'] === true ? 'true' : 'false'; - $broadcastChannel = $this->options['broadcastChannel']; - $maxRetentionTime = $this->options['maxRetentionTime']; - $queueName = $this->options['queueName']; + $forceSyncFallback = $this->forceSyncFallback === true ? 'true' : 'false'; $broadcastChannelSection = ''; - if ($broadcastChannel !== null) { + if ($this->broadcastChannel !== null) { $broadcastChannelSection = << { try { @@ -22,8 +34,8 @@ public function render(int $jsonOptions = 0): string // Failed to replay one or more requests } finally { remainingRequests = await queue.getAll(); - const bc = new BroadcastChannel('{$broadcastChannel}'); - bc.postMessage({name: '{$queueName}', remaining: remainingRequests.length}); + const bc = new BroadcastChannel('{$this->broadcastChannel}'); + bc.postMessage({name: '{$this->queueName}', remaining: remainingRequests.length}); bc.close(); } } @@ -31,8 +43,8 @@ public function render(int $jsonOptions = 0): string } $declaration = <<queueName}',{ + "maxRetentionTime": {$this->maxRetentionTime}, "forceSyncFallback": {$forceSyncFallback}{$broadcastChannelSection} }) BACKGROUND_SYNC_RULE_STRATEGY; @@ -44,16 +56,8 @@ public static function create( string $queueName, int $maxRetentionTime, bool $forceSyncFallback, - ?string $broadcastChannel + null|string $broadcastChannel ): static { - return new self( - 'BackgroundSyncPlugin', - [ - 'queueName' => $queueName, - 'maxRetentionTime' => $maxRetentionTime, - 'forceSyncFallback' => $forceSyncFallback, - 'broadcastChannel' => $broadcastChannel, - ] - ); + return new self($queueName, $forceSyncFallback, $broadcastChannel, $maxRetentionTime); } } diff --git a/src/WorkboxPlugin/BroadcastUpdatePlugin.php b/src/WorkboxPlugin/BroadcastUpdatePlugin.php index c8e527b..a50ee43 100644 --- a/src/WorkboxPlugin/BroadcastUpdatePlugin.php +++ b/src/WorkboxPlugin/BroadcastUpdatePlugin.php @@ -4,13 +4,36 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BroadcastUpdatePlugin extends CachePlugin +final readonly class BroadcastUpdatePlugin implements CachePlugin { + private const NAME = 'BroadcastUpdatePlugin'; + + /** + * @var array + */ + private array $headersToCheck; + + /** + * @param array $headersToCheck + */ + public function __construct( + array $headersToCheck = [] + ) { + $this->headersToCheck = $headersToCheck === [] ? ['Content-Type', 'ETag', 'Last-Modified'] : $headersToCheck; + } + + public function getName(): string + { + return self::NAME; + } + public function render(int $jsonOptions = 0): string { return sprintf( 'new workbox.broadcastUpdate.BroadcastUpdatePlugin(%s)', - json_encode($this->options, $jsonOptions) + json_encode([ + 'headersToCheck' => $this->headersToCheck, + ], $jsonOptions) ); } @@ -19,10 +42,6 @@ public function render(int $jsonOptions = 0): string */ public static function create(array $headersToCheck = []): static { - $headersToCheck = $headersToCheck === [] ? ['Content-Type', 'ETag', 'Last-Modified'] : $headersToCheck; - - return new self('BroadcastUpdatePlugin', [ - 'headersToCheck' => $headersToCheck, - ]); + return new self($headersToCheck); } } diff --git a/src/WorkboxPlugin/CachePlugin.php b/src/WorkboxPlugin/CachePlugin.php index cba3e23..c778a8b 100644 --- a/src/WorkboxPlugin/CachePlugin.php +++ b/src/WorkboxPlugin/CachePlugin.php @@ -4,16 +4,9 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -abstract readonly class CachePlugin +interface CachePlugin { - /** - * @param array $options - */ - public function __construct( - public string $name, - public array $options = [] - ) { - } + public function getName(): string; - abstract public function render(int $jsonOptions = 0): string; + public function render(int $jsonOptions = 0): string; } diff --git a/src/WorkboxPlugin/CacheableResponsePlugin.php b/src/WorkboxPlugin/CacheableResponsePlugin.php index d0b5096..3f1a507 100644 --- a/src/WorkboxPlugin/CacheableResponsePlugin.php +++ b/src/WorkboxPlugin/CacheableResponsePlugin.php @@ -4,8 +4,30 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class CacheableResponsePlugin extends CachePlugin +final readonly class CacheableResponsePlugin implements CachePlugin { + private const NAME = 'CacheableResponsePlugin'; + + /** + * @var array{options?: array{statuses: array, headers?: array}} + */ + private array $options; + + /** + * @param array $statuses + * @param array $headers + */ + public function __construct(array $statuses = [0, 200], array $headers = []) + { + $options = array_filter([ + 'statuses' => $statuses, + 'headers' => $headers, + ], fn ($value) => $value !== []); + $this->options = $options === [] ? [ + 'statuses' => [0, 200], + ] : $options; + } + public function render(int $jsonOptions = 0): string { return sprintf( @@ -20,14 +42,11 @@ public function render(int $jsonOptions = 0): string */ public static function create(array $statuses = [0, 200], array $headers = []): static { - $options = array_filter([ - 'statuses' => $statuses, - 'headers' => $headers, - ], fn ($value) => $value !== []); - $options = $options === [] ? [ - 'statuses' => [0, 200], - ] : $options; + return new self($statuses, $headers); + } - return new self('CacheableResponsePlugin', $options); + public function getName(): string + { + return self::NAME; } } diff --git a/src/WorkboxPlugin/ExpirationPlugin.php b/src/WorkboxPlugin/ExpirationPlugin.php index d260098..e048d2f 100644 --- a/src/WorkboxPlugin/ExpirationPlugin.php +++ b/src/WorkboxPlugin/ExpirationPlugin.php @@ -4,18 +4,35 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class ExpirationPlugin extends CachePlugin +final readonly class ExpirationPlugin implements CachePlugin { + private const NAME = 'ExpirationPlugin'; + + /** + * @var array{maxEntries: null|int, maxAgeSeconds: null|int} + */ + private array $options; + + public function __construct(null|int $maxEntries, null|int $maxAgeSeconds) + { + $this->options = [ + 'maxEntries' => $maxEntries, + 'maxAgeSeconds' => $maxAgeSeconds, + ]; + } + public function render(int $jsonOptions = 0): string { return sprintf('new workbox.expiration.ExpirationPlugin(%s)', json_encode($this->options, $jsonOptions)); } - public static function create(null|int $maxEntries, null|string|int $maxAgeSeconds): static + public static function create(null|int $maxEntries, null|int $maxAgeSeconds): static { - return new self('ExpirationPlugin', [ - 'maxEntries' => $maxEntries, - 'maxAgeSeconds' => $maxAgeSeconds, - ]); + return new self($maxEntries, $maxAgeSeconds); + } + + public function getName(): string + { + return self::NAME; } } diff --git a/src/WorkboxPlugin/RangeRequestsPlugin.php b/src/WorkboxPlugin/RangeRequestsPlugin.php index 42a0c40..d300855 100644 --- a/src/WorkboxPlugin/RangeRequestsPlugin.php +++ b/src/WorkboxPlugin/RangeRequestsPlugin.php @@ -4,8 +4,10 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class RangeRequestsPlugin extends CachePlugin +final readonly class RangeRequestsPlugin implements CachePlugin { + private const NAME = 'RangeRequestsPlugin'; + public function render(int $jsonOptions = 0): string { return 'new workbox.rangeRequests.RangeRequestsPlugin()'; @@ -13,6 +15,11 @@ public function render(int $jsonOptions = 0): string public static function create(): static { - return new self('RangeRequestsPlugin'); + return new self(); + } + + public function getName(): string + { + return self::NAME; } } From a4fd6952333f5a8129f6a73e8ce31279e8f5e180 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Thu, 21 Mar 2024 07:08:05 +0100 Subject: [PATCH 2/5] features: CachePlugin and CacheStrategy are now interfaces --- src/Command/ListCacheStrategiesCommand.php | 56 ++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/Command/ListCacheStrategiesCommand.php b/src/Command/ListCacheStrategiesCommand.php index 52075b5..e795c5d 100644 --- a/src/Command/ListCacheStrategiesCommand.php +++ b/src/Command/ListCacheStrategiesCommand.php @@ -4,11 +4,13 @@ namespace SpomkyLabs\PwaBundle\Command; +use SpomkyLabs\PwaBundle\CachingStrategy\CacheStrategy; use SpomkyLabs\PwaBundle\CachingStrategy\HasCacheStrategies; use SpomkyLabs\PwaBundle\CachingStrategy\WorkboxCacheStrategy; use SpomkyLabs\PwaBundle\WorkboxPlugin\CachePlugin; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -39,35 +41,39 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['Name', 'Strategy', 'URL pattern', 'Enabled', 'Workbox?', 'Plugins', 'Preload URLs', 'Options'] ); foreach ($this->services as $service) { - $strategies = $service->getCacheStrategies(); - foreach ($strategies as $strategy) { - if ($strategy instanceof WorkboxCacheStrategy) { - $table->addRow([ - $strategy->getName(), - $strategy->strategy, - $strategy->matchCallback, - $strategy->isEnabled() ? 'Yes' : 'No', - $strategy->needsWorkbox() ? 'Yes' : 'No', - Yaml::dump(array_map(fn (CachePlugin $v): string => $v->getName(), $strategy->getPlugins())), - count($strategy->getPreloadUrls()), - Yaml::dump($strategy->getOptions()), - ]); - } else { - $table->addRow([ - $strategy->getName(), - '---', - '---', - $strategy->isEnabled() ? 'Yes' : 'No', - $strategy->needsWorkbox() ? 'Yes' : 'No', - '', - '', - '', - ]); - } + foreach ($service->getCacheStrategies() as $strategy) { + $this->processStrategy($strategy, $table); } } $table->render(); return self::SUCCESS; } + + private function processStrategy(CacheStrategy $strategy, Table $table): void + { + if ($strategy instanceof WorkboxCacheStrategy) { + $table->addRow([ + $strategy->getName(), + $strategy->strategy, + $strategy->matchCallback, + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', + Yaml::dump(array_map(fn (CachePlugin $v): string => $v->getName(), $strategy->getPlugins())), + count($strategy->getPreloadUrls()), + Yaml::dump($strategy->getOptions()), + ]); + } else { + $table->addRow([ + $strategy->getName(), + '---', + '---', + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', + '', + '', + '', + ]); + } + } } From 1442a4f894a39577b7c3a0d95c8cebad858fcd2d Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Thu, 21 Mar 2024 07:33:10 +0100 Subject: [PATCH 3/5] features: CachePlugin and CacheStrategy are now interfaces --- src/CachingStrategy/WorkboxCacheStrategy.php | 5 +- src/Service/ServiceWorkerCompiler.php | 8 ++- .../AppendCacheStrategies.php | 2 +- src/ServiceWorkerRule/ClearCache.php | 30 +++++++-- src/ServiceWorkerRule/OfflineFallback.php | 67 +++++++++++++------ src/ServiceWorkerRule/SkipWaiting.php | 27 +++++++- src/ServiceWorkerRule/WindowsWidgets.php | 49 ++++++++++++-- src/ServiceWorkerRule/WorkboxImport.php | 43 ++++++++++-- 8 files changed, 190 insertions(+), 41 deletions(-) diff --git a/src/CachingStrategy/WorkboxCacheStrategy.php b/src/CachingStrategy/WorkboxCacheStrategy.php index 7fb4ec7..ad984f4 100644 --- a/src/CachingStrategy/WorkboxCacheStrategy.php +++ b/src/CachingStrategy/WorkboxCacheStrategy.php @@ -145,10 +145,11 @@ public function render(string $cacheObjectName, bool $debug = false): string } $declaration .= <<strategy}({ {$timeout}{$cacheName}plugins: {$plugins} }); +// Register the route with the Workbox Router workbox.routing.registerRoute({$this->matchCallback},{$cacheObjectName}{$method}); ROUTE_REGISTRATION; @@ -156,6 +157,7 @@ public function render(string $cacheObjectName, bool $debug = false): string if ($this->preloadUrls !== []) { $fontUrls = json_encode($this->preloadUrls, $jsonOptions); $declaration .= << { const done = {$fontUrls}.map( path => @@ -175,6 +177,7 @@ public function render(string $cacheObjectName, bool $debug = false): string /**************************************************** END CACHE STRATEGY ****************************************************/ + DEBUG_STATEMENT; return $debug === true ? $declaration : trim($declaration); diff --git a/src/Service/ServiceWorkerCompiler.php b/src/Service/ServiceWorkerCompiler.php index 76b09ff..dda6476 100644 --- a/src/Service/ServiceWorkerCompiler.php +++ b/src/Service/ServiceWorkerCompiler.php @@ -24,6 +24,8 @@ public function __construct( private AssetMapperInterface $assetMapper, #[TaggedIterator('spomky_labs_pwa.service_worker_rule', defaultPriorityMethod: 'getPriority')] private iterable $serviceworkerRules, + #[Autowire('%kernel.debug%')] + public bool $debug, ) { } @@ -35,7 +37,11 @@ public function compile(): ?string $body = ''; foreach ($this->serviceworkerRules as $rule) { - $body .= $rule->process(); + $ruleBody = $rule->process($this->debug); + if ($this->debug === false) { + $ruleBody = trim($ruleBody); + } + $body .= $ruleBody; } return $body . $this->includeRootSW(); diff --git a/src/ServiceWorkerRule/AppendCacheStrategies.php b/src/ServiceWorkerRule/AppendCacheStrategies.php index 15133e3..d4c06f4 100644 --- a/src/ServiceWorkerRule/AppendCacheStrategies.php +++ b/src/ServiceWorkerRule/AppendCacheStrategies.php @@ -22,7 +22,7 @@ public function __construct( ) { } - public function process(): string + public function process(bool $debug = false): string { $body = ''; foreach ($this->cacheStrategies as $idCacheStrategy => $cacheStrategy) { diff --git a/src/ServiceWorkerRule/ClearCache.php b/src/ServiceWorkerRule/ClearCache.php index d11b0a0..4ecc653 100644 --- a/src/ServiceWorkerRule/ClearCache.php +++ b/src/ServiceWorkerRule/ClearCache.php @@ -12,12 +12,12 @@ private Workbox $workbox; public function __construct( - ServiceWorker $serviceWorker + ServiceWorker $serviceWorker, ) { $this->workbox = $serviceWorker->workbox; } - public function process(): string + public function process(bool $debug = false): string { if ($this->workbox->enabled === false) { return ''; @@ -26,7 +26,18 @@ public function process(): string return ''; } - $declaration = << - */ - private array $jsonOptions; - private Workbox $workbox; public function __construct( ServiceWorker $serviceWorker, private SerializerInterface $serializer, - #[Autowire('%kernel.debug%')] - bool $debug, ) { $this->workbox = $serviceWorker->workbox; - $options = [ - AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => true, - AbstractObjectNormalizer::SKIP_NULL_VALUES => true, - JsonEncode::OPTIONS => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, - ]; - if ($debug === true) { - $options[JsonEncode::OPTIONS] |= JSON_PRETTY_PRINT; - } - $this->jsonOptions = $options; } - public function process(): string + public function process(bool $debug = false): string { if ($this->workbox->enabled === false || ! isset($this->workbox->offlineFallback)) { return ''; } + $options = [ 'pageFallback' => $this->workbox->offlineFallback->pageFallback, 'imageFallback' => $this->workbox->offlineFallback->imageFallback, @@ -57,13 +41,54 @@ public function process(): string if (count($options) === 0) { return ''; } - $options = count($options) === 0 ? '' : $this->serializer->serialize($options, 'json', $this->jsonOptions); + $options = count($options) === 0 ? '' : $this->serializer->serialize( + $options, + 'json', + $this->serializerOptions($debug) + ); + + $declaration = ''; + if ($debug === true) { + $declaration .= << + */ + private function serializerOptions(bool $debug): array + { + $jsonOptions = [ + AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => true, + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + ]; + if ($debug === true) { + $jsonOptions[JsonEncode::OPTIONS] |= JSON_PRETTY_PRINT; + } + + return $jsonOptions; } } diff --git a/src/ServiceWorkerRule/SkipWaiting.php b/src/ServiceWorkerRule/SkipWaiting.php index d32a155..1ef75f2 100644 --- a/src/ServiceWorkerRule/SkipWaiting.php +++ b/src/ServiceWorkerRule/SkipWaiting.php @@ -13,21 +13,42 @@ public function __construct( ) { } - public function process(): string + public function process(bool $debug = false): string { if ($this->serviceWorker->skipWaiting === false) { return ''; } - $declaration = <<manifest->widgets as $widget) { @@ -31,11 +33,19 @@ public function process(): string if (count($tags) === 0) { return ''; } - $data = $this->serializer->serialize($tags, 'json', [ - JsonEncode::OPTIONS => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, - ]); + $data = $this->serializer->serialize($tags, 'json', $this->serializerOptions($debug)); + if ($debug === true) { + $declaration = << { event.waitUntil(renderWidget(event.widget)); }); @@ -93,8 +103,35 @@ public function process(): string await self.widgets.updateByTag(widget.definition.tag, {template, data}); } } + OFFLINE_FALLBACK_STRATEGY; + if ($debug === true) { + $declaration .= << + */ + private function serializerOptions(bool $debug): array + { + $jsonOptions = [ + AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => true, + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + ]; + if ($debug === true) { + $jsonOptions[JsonEncode::OPTIONS] |= JSON_PRETTY_PRINT; + } - return trim($declaration); + return $jsonOptions; } } diff --git a/src/ServiceWorkerRule/WorkboxImport.php b/src/ServiceWorkerRule/WorkboxImport.php index 5ec58cf..c998e27 100644 --- a/src/ServiceWorkerRule/WorkboxImport.php +++ b/src/ServiceWorkerRule/WorkboxImport.php @@ -17,24 +17,59 @@ public function __construct( $this->workbox = $serviceWorker->workbox; } - public function process(): string + public function process(bool $debug = false): string { if ($this->workbox->enabled === false) { return ''; } + $declaration = ''; + if ($debug === true) { + $declaration .= <<workbox->useCDN === true) { - $declaration = <<workbox->version}/workbox-sw.js'); IMPORT_CDN_STRATEGY; } else { $publicUrl = '/' . trim($this->workbox->workboxPublicUrl, '/'); - $declaration = << Date: Thu, 21 Mar 2024 07:35:16 +0100 Subject: [PATCH 4/5] features: CachePlugin and CacheStrategy are now interfaces --- src/ServiceWorkerRule/ServiceWorkerRule.php | 2 +- src/ServiceWorkerRule/WindowsWidgets.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ServiceWorkerRule/ServiceWorkerRule.php b/src/ServiceWorkerRule/ServiceWorkerRule.php index 64f0a83..88c0078 100644 --- a/src/ServiceWorkerRule/ServiceWorkerRule.php +++ b/src/ServiceWorkerRule/ServiceWorkerRule.php @@ -6,5 +6,5 @@ interface ServiceWorkerRule { - public function process(): string; + public function process(bool $debug = false): string; } diff --git a/src/ServiceWorkerRule/WindowsWidgets.php b/src/ServiceWorkerRule/WindowsWidgets.php index d9a6245..bd90eb2 100644 --- a/src/ServiceWorkerRule/WindowsWidgets.php +++ b/src/ServiceWorkerRule/WindowsWidgets.php @@ -34,8 +34,9 @@ public function process(bool $debug = false): string return ''; } $data = $this->serializer->serialize($tags, 'json', $this->serializerOptions($debug)); + $declaration = ''; if ($debug === true) { - $declaration = << Date: Thu, 21 Mar 2024 07:43:56 +0100 Subject: [PATCH 5/5] features: CachePlugin and CacheStrategy are now interfaces --- src/CachingStrategy/WorkboxCacheStrategy.php | 10 ++++++---- src/ServiceWorkerRule/ClearCache.php | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/CachingStrategy/WorkboxCacheStrategy.php b/src/CachingStrategy/WorkboxCacheStrategy.php index ad984f4..b916b81 100644 --- a/src/CachingStrategy/WorkboxCacheStrategy.php +++ b/src/CachingStrategy/WorkboxCacheStrategy.php @@ -140,16 +140,17 @@ public function render(string $cacheObjectName, bool $debug = false): string // Needs Workbox: {$this->needsWorkbox()} // Method: {$this->method} +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed DEBUG_STATEMENT; } $declaration .= <<strategy}({ {$timeout}{$cacheName}plugins: {$plugins} }); -// Register the route with the Workbox Router workbox.routing.registerRoute({$this->matchCallback},{$cacheObjectName}{$method}); ROUTE_REGISTRATION; @@ -157,7 +158,6 @@ public function render(string $cacheObjectName, bool $debug = false): string if ($this->preloadUrls !== []) { $fontUrls = json_encode($this->preloadUrls, $jsonOptions); $declaration .= << { const done = {$fontUrls}.map( path => @@ -173,12 +173,14 @@ public function render(string $cacheObjectName, bool $debug = false): string ASSET_CACHE_RULE_PRELOAD; } - $declaration .= <<