From 0af2d0dd2db8844e8cb090fd903b4f3d8b3a58e0 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Tue, 5 Mar 2024 12:05:15 +0100 Subject: [PATCH] Remove placeholders --- ecs.php | 11 +- phpstan-baseline.neon | 12 +- rector.php | 6 +- src/DependencyInjection/Configuration.php | 20 +++ src/Dto/Workbox.php | 5 + src/Resources/sw-skeleton.js | 11 +- src/Service/ServiceWorkerCompiler.php | 141 ++++++++++++---------- 7 files changed, 124 insertions(+), 82 deletions(-) diff --git a/ecs.php b/ecs.php index b3b5192..917d102 100644 --- a/ecs.php +++ b/ecs.php @@ -28,7 +28,7 @@ use PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer; use PhpCsFixer\Fixer\Whitespace\CompactNullableTypehintFixer; use PhpCsFixer\Fixer\Whitespace\MethodChainingIndentationFixer; -use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer; +use Symplify\CodingStandard\Fixer\Spacing\MethodChainingNewlineFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; @@ -92,13 +92,10 @@ $config->skip([ PhpUnitTestClassRequiresCoversFixer::class, - MethodChainingIndentationFixer::class => [__DIR__.'/src/DependencyInjection/Configuration.php'], - \Symplify\CodingStandard\Fixer\Spacing\MethodChainingNewlineFixer::class => [__DIR__.'/src/DependencyInjection/Configuration.php'], + MethodChainingIndentationFixer::class => [__DIR__ . '/src/DependencyInjection/Configuration.php'], + MethodChainingNewlineFixer::class => [__DIR__ . '/src/DependencyInjection/Configuration.php'], ]); $config->parallel(); - $config->paths([ - __DIR__.'/src', - __DIR__.'/tests', - ]); + $config->paths([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php', __DIR__ . '/rector.php']); }; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a26fa3e..183af00 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -320,6 +320,11 @@ parameters: count: 1 path: src/Dto/Widget.php + - + message: "#^Attribute class JetBrains\\\\PhpStorm\\\\Deprecated does not exist\\.$#" + count: 4 + path: src/Dto/Workbox.php + - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$enabled\\. Give it default value or assign it in the constructor\\.$#" count: 1 @@ -468,4 +473,9 @@ parameters: - message: "#^Method SpomkyLabs\\\\PwaBundle\\\\Normalizer\\\\UrlNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 - path: src/Normalizer/UrlNormalizer.php \ No newline at end of file + path: src/Normalizer/UrlNormalizer.php + + - + message: "#^Property SpomkyLabs\\\\PwaBundle\\\\Service\\\\ServiceWorkerCompiler\\:\\:\\$jsonOptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Service/ServiceWorkerCompiler.php \ No newline at end of file diff --git a/rector.php b/rector.php index 9cc1e1b..fe8046d 100644 --- a/rector.php +++ b/rector.php @@ -25,10 +25,8 @@ PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES, ]); $config->phpVersion(PhpVersion::PHP_82); - $config->paths([__DIR__ . '/src', __DIR__ . '/tests']); - $config->skip([ - __DIR__ . '/tests/Controller/DummyController.php', - ]); + $config->paths([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php', __DIR__ . '/rector.php']); + $config->skip([__DIR__ . '/tests/Controller/DummyController.php']); $config->parallel(); $config->importNames(); $config->importShortClasses(); diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 2201ad6..a4a867f 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -126,6 +126,11 @@ private function setupServiceWorker(ArrayNodeDefinition $node): void ->info('The public path to the local workbox. Only used if use_cdn is false.') ->end() ->scalarNode('workbox_import_placeholder') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. No replacement.' + ) ->defaultValue('//WORKBOX_IMPORT_PLACEHOLDER') ->info( 'The placeholder for the workbox import. Will be replaced by the workbox import.' @@ -133,6 +138,11 @@ private function setupServiceWorker(ArrayNodeDefinition $node): void ->example('//WORKBOX_IMPORT_PLACEHOLDER') ->end() ->scalarNode('standard_rules_placeholder') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. No replacement.' + ) ->defaultValue('//STANDARD_RULES_PLACEHOLDER') ->info( 'The placeholder for the standard rules. Will be replaced by caching strategies.' @@ -140,11 +150,21 @@ private function setupServiceWorker(ArrayNodeDefinition $node): void ->example('//STANDARD_RULES_PLACEHOLDER') ->end() ->scalarNode('offline_fallback_placeholder') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. No replacement.' + ) ->defaultValue('//OFFLINE_FALLBACK_PLACEHOLDER') ->info('The placeholder for the offline fallback. Will be replaced by the URL.') ->example('//OFFLINE_FALLBACK_PLACEHOLDER') ->end() ->scalarNode('widgets_placeholder') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. No replacement.' + ) ->defaultValue('//WIDGETS_PLACEHOLDER') ->info( 'The placeholder for the widgets. Will be replaced by the widgets management events.' diff --git a/src/Dto/Workbox.php b/src/Dto/Workbox.php index 62313a7..c034d78 100644 --- a/src/Dto/Workbox.php +++ b/src/Dto/Workbox.php @@ -4,6 +4,7 @@ namespace SpomkyLabs\PwaBundle\Dto; +use JetBrains\PhpStorm\Deprecated; use Symfony\Component\Serializer\Attribute\SerializedName; final class Workbox @@ -19,15 +20,19 @@ final class Workbox public string $workboxPublicUrl; #[SerializedName('workbox_import_placeholder')] + #[Deprecated('No longer used.')] public string $workboxImportPlaceholder; #[SerializedName('standard_rules_placeholder')] + #[Deprecated('No longer used.')] public string $standardRulesPlaceholder; #[SerializedName('offline_fallback_placeholder')] + #[Deprecated('No longer used.')] public string $offlineFallbackPlaceholder; #[SerializedName('widgets_placeholder')] + #[Deprecated('No longer used.')] public string $widgetsPlaceholder; #[SerializedName('page_fallback')] diff --git a/src/Resources/sw-skeleton.js b/src/Resources/sw-skeleton.js index 35ece5a..7eec157 100644 --- a/src/Resources/sw-skeleton.js +++ b/src/Resources/sw-skeleton.js @@ -1,5 +1,6 @@ -// *** Workbox Bundle rules *** -//WORKBOX_IMPORT_PLACEHOLDER -//STANDARD_RULES_PLACEHOLDER -//OFFLINE_FALLBACK_PLACEHOLDER -//WIDGETS_PLACEHOLDER +// *** Service Worker *** // +/* + This is the service worker file. It will be populated with the rules you define in the + configuration file. + You can define here custom rules depending on your application needs. + */ diff --git a/src/Service/ServiceWorkerCompiler.php b/src/Service/ServiceWorkerCompiler.php index 075bac0..08c8d9d 100644 --- a/src/Service/ServiceWorkerCompiler.php +++ b/src/Service/ServiceWorkerCompiler.php @@ -18,9 +18,12 @@ use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; +use const PHP_EOL; final readonly class ServiceWorkerCompiler { + private array $jsonOptions; + public function __construct( private SerializerInterface $serializer, #[Autowire('%spomky_labs_pwa.asset_public_prefix%')] @@ -31,6 +34,9 @@ public function __construct( private ServiceWorker $serviceWorker, private AssetMapperInterface $assetMapper, ) { + $this->jsonOptions = [ + JsonEncode::OPTIONS => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + ]; } public function compile(): ?string @@ -52,6 +58,7 @@ public function compile(): ?string if ($workbox->enabled === true) { $body = $this->processWorkbox($workbox, $body); } + $body = $this->processWidgets($body); return $this->processSkipWaiting($body); } @@ -71,24 +78,23 @@ private function processSkipWaiting(string $body): string }); SKIP_WAITING; - return $body . trim($declaration); + return $body . PHP_EOL . PHP_EOL . trim($declaration); } private function processWorkbox(Workbox $workbox, string $body): string { $body = $this->processWorkboxImport($workbox, $body); $body = $this->processClearCache($workbox, $body); - $body = $this->processStandardRules($workbox, $body); - $body = $this->processWidgets($workbox, $body); + $body = $this->processAssetCacheRules($workbox, $body); + $body = $this->processFontCacheRules($workbox, $body); + $body = $this->processPageImageCacheRule($workbox, $body); + $body = $this->processImageCacheRule($workbox, $body); return $this->processOfflineFallback($workbox, $body); } private function processWorkboxImport(Workbox $workbox, string $body): string { - if (! str_contains($body, $workbox->workboxImportPlaceholder)) { - return $body; - } if ($workbox->useCDN === true) { $declaration = <<version}/workbox-sw.js'); @@ -101,7 +107,7 @@ private function processWorkboxImport(Workbox $workbox, string $body): string IMPORT_CDN_STRATEGY; } - return str_replace($workbox->workboxImportPlaceholder, trim($declaration), $body); + return trim($declaration) . PHP_EOL . PHP_EOL . $body; } private function processClearCache(Workbox $workbox, string $body): string @@ -123,60 +129,25 @@ private function processClearCache(Workbox $workbox, string $body): string }); CLEAR_CACHE; - return $body . trim($declaration); + return $body . PHP_EOL . PHP_EOL . trim($declaration); } - private function processStandardRules(Workbox $workbox, string $body): string + private function processAssetCacheRules(Workbox $workbox, string $body): string { - if (! str_contains($body, $workbox->standardRulesPlaceholder)) { - return $body; - } - $assets = []; - $fonts = []; foreach ($this->assetMapper->allAssets() as $asset) { if (preg_match($workbox->imageRegex, $asset->sourcePath) === 1 || preg_match( $workbox->staticRegex, $asset->sourcePath ) === 1) { $assets[] = $asset->publicPath; - } elseif (preg_match($workbox->fontRegex, $asset->sourcePath) === 1) { - $fonts[] = $asset->publicPath; } } - $jsonOptions = [ - JsonEncode::OPTIONS => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, - ]; - $assetUrls = $this->serializer->serialize($assets, 'json', $jsonOptions); - $fontUrls = $this->serializer->serialize($fonts, 'json', $jsonOptions); + $assetUrls = $this->serializer->serialize($assets, 'json', $this->jsonOptions); $assetUrlsLength = count($assets) * 2; - $routes = $this->serializer->serialize($workbox->warmCacheUrls, 'json', $jsonOptions); - - $declaration = <<pageCacheName}', - networkTimeoutSeconds: {$workbox->networkTimeoutSeconds}, - warmCache: {$routes} -}); - -//Images cache -workbox.routing.registerRoute( - ({request, url}) => (request.destination === 'image' && !url.pathname.startsWith('{$this->assetPublicPrefix}')), - new workbox.strategies.CacheFirst({ - cacheName: '{$workbox->imageCacheName}', - plugins: [ - new workbox.cacheableResponse.CacheableResponsePlugin({statuses: [0, 200]}), - new workbox.expiration.ExpirationPlugin({ - maxEntries: {$workbox->maxImageCacheEntries}, - maxAgeSeconds: {$workbox->maxImageAge}, - }), - ], - }) -); + $declaration = <<assetCacheName}', plugins: [ @@ -187,7 +158,6 @@ private function processStandardRules(Workbox $workbox, string $body): string }), ], }); -// - Strategy: only the Asset Mapper public route workbox.routing.registerRoute( ({url}) => url.pathname.startsWith('{$this->assetPublicPrefix}'), assetCacheStrategy @@ -203,8 +173,23 @@ private function processStandardRules(Workbox $workbox, string $body): string event.waitUntil(Promise.all(done)); }); +ASSET_CACHE_RULE_STRATEGY; + return $body . PHP_EOL . PHP_EOL . trim($declaration); + } + + private function processFontCacheRules(Workbox $workbox, string $body): string + { + $fonts = []; + foreach ($this->assetMapper->allAssets() as $asset) { + if (preg_match($workbox->fontRegex, $asset->sourcePath) === 1) { + $fonts[] = $asset->publicPath; + } + } + $fontUrls = $this->serializer->serialize($fonts, 'json', $this->jsonOptions); + $declaration = <<fontCacheName}', plugins: [ @@ -232,39 +217,68 @@ private function processStandardRules(Workbox $workbox, string $body): string event.waitUntil(Promise.all(done)); }); +FONT_CACHE_RULE_STRATEGY; + return $body . PHP_EOL . PHP_EOL . trim($declaration); + } -STANDARD_RULE_STRATEGY; + private function processPageImageCacheRule(Workbox $workbox, string $body): string + { + $routes = $this->serializer->serialize($workbox->warmCacheUrls, 'json', $this->jsonOptions); - return str_replace($workbox->standardRulesPlaceholder, trim($declaration), $body); + $declaration = <<pageCacheName}', + networkTimeoutSeconds: {$workbox->networkTimeoutSeconds}, + warmCache: {$routes} +}); +PAGE_CACHE_RULE_STRATEGY; + + return $body . PHP_EOL . PHP_EOL . trim($declaration); + } + + private function processImageCacheRule(Workbox $workbox, string $body): string + { + $declaration = << (request.destination === 'image' && !url.pathname.startsWith('{$this->assetPublicPrefix}')), + new workbox.strategies.CacheFirst({ + cacheName: '{$workbox->imageCacheName}', + plugins: [ + new workbox.cacheableResponse.CacheableResponsePlugin({statuses: [0, 200]}), + new workbox.expiration.ExpirationPlugin({ + maxEntries: {$workbox->maxImageCacheEntries}, + maxAgeSeconds: {$workbox->maxImageAge}, + }), + ], + }) +); +IMAGE_CACHE_RULE_STRATEGY; + + return $body . PHP_EOL . PHP_EOL . trim($declaration); } private function processOfflineFallback(Workbox $workbox, string $body): string { - if (! str_contains($body, $workbox->offlineFallbackPlaceholder)) { - return $body; - } if ($workbox->pageFallback === null && $workbox->imageFallback === null && $workbox->fontFallback === null) { return $body; } - - $jsonOptions = [ - JsonEncode::OPTIONS => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, - ]; $pageFallback = $workbox->pageFallback === null ? 'null' : $this->serializer->serialize( $workbox->pageFallback, 'json', - $jsonOptions + $this->jsonOptions ); $imageFallback = $workbox->imageFallback === null ? 'null' : $this->serializer->serialize( $workbox->imageFallback, 'json', - $jsonOptions + $this->jsonOptions ); $fontFallback = $workbox->fontFallback === null ? 'null' : $this->serializer->serialize( $workbox->fontFallback, 'json', - $jsonOptions + $this->jsonOptions ); $declaration = <<offlineFallbackPlaceholder, trim($declaration), $body); + return $body . PHP_EOL . PHP_EOL . trim($declaration); } - private function processWidgets(Workbox $workbox, string $body): string + private function processWidgets(string $body): string { - if (! str_contains($body, $workbox->widgetsPlaceholder)) { - return $body; - } $tags = []; foreach ($this->manifest->widgets as $widget) { if ($widget->tag !== null) { @@ -357,6 +368,6 @@ private function processWidgets(Workbox $workbox, string $body): string } OFFLINE_FALLBACK_STRATEGY; - return str_replace($workbox->widgetsPlaceholder, trim($declaration), $body); + return $body . PHP_EOL . PHP_EOL . trim($declaration); } }