From 5ba6f355f899acfcda00d02aeb355ce32484d107 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Tue, 5 Mar 2024 16:22:42 +0100 Subject: [PATCH] Better cache configuration --- phpstan-baseline.neon | 133 ++++++++- src/Dto/AssetCache.php | 17 ++ src/Dto/FontCache.php | 24 ++ src/Dto/ImageCache.php | 24 ++ src/Dto/OfflineFallback.php | 21 ++ src/Dto/PageCache.php | 26 ++ src/Dto/Workbox.php | 77 +---- .../config/definition/service_worker.php | 268 +++++++++++++++++- .../config/definition/utils/shortcuts.php | 35 ++- src/Service/ServiceWorkerCompiler.php | 85 +++--- 10 files changed, 568 insertions(+), 142 deletions(-) create mode 100644 src/Dto/AssetCache.php create mode 100644 src/Dto/FontCache.php create mode 100644 src/Dto/ImageCache.php create mode 100644 src/Dto/OfflineFallback.php create mode 100644 src/Dto/PageCache.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ee71307..095f8dc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -291,8 +291,8 @@ parameters: path: src/Dto/Widget.php - - message: "#^Attribute class JetBrains\\\\PhpStorm\\\\Deprecated does not exist\\.$#" - count: 4 + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$assetCache\\. Give it default value or assign it in the constructor\\.$#" + count: 1 path: src/Dto/Workbox.php - @@ -306,37 +306,37 @@ parameters: path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$googleFontCache\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$fontCache\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$offlineFallbackPlaceholder\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$googleFontCache\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$standardRulesPlaceholder\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$imageCache\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$useCDN\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$offlineFallback\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$version\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$pageCache\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$widgetsPlaceholder\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$useCDN\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$workboxImportPlaceholder\\. Give it default value or assign it in the constructor\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\Workbox has an uninitialized property \\$version\\. Give it default value or assign it in the constructor\\.$#" count: 1 path: src/Dto/Workbox.php @@ -475,11 +475,126 @@ parameters: count: 1 path: src/Resources/config/definition/path_type_reference.php + - + message: "#^Anonymous function should return array but returns mixed\\.$#" + count: 10 + path: src/Resources/config/definition/service_worker.php + - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" count: 1 path: src/Resources/config/definition/service_worker.php + - + message: "#^Cannot access offset 'asset_cache' on mixed\\.$#" + count: 2 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'asset_cache_name' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'font_cache' on mixed\\.$#" + count: 2 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'font_cache_name' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'font_fallback' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'font_regex' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'image_cache' on mixed\\.$#" + count: 2 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'image_cache_name' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'image_fallback' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'image_regex' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'max_font_age' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'max_font_cache…' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'max_image_age' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'max_image_cache…' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'network_timeout…' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'offline_fallback' on mixed\\.$#" + count: 2 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'page_cache' on mixed\\.$#" + count: 2 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'page_cache_name' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'page_fallback' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'static_regex' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Cannot access offset 'warm_cache_urls' on mixed\\.$#" + count: 1 + path: src/Resources/config/definition/service_worker.php + + - + message: "#^Strict comparison using \\!\\=\\= between mixed and null will always evaluate to true\\.$#" + count: 4 + path: src/Resources/config/definition/service_worker.php + - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:end\\(\\)\\.$#" count: 1 diff --git a/src/Dto/AssetCache.php b/src/Dto/AssetCache.php new file mode 100644 index 0000000..bb1b9e7 --- /dev/null +++ b/src/Dto/AssetCache.php @@ -0,0 +1,17 @@ + + */ + public array $urls = []; +} diff --git a/src/Dto/Workbox.php b/src/Dto/Workbox.php index 36ed005..443e199 100644 --- a/src/Dto/Workbox.php +++ b/src/Dto/Workbox.php @@ -4,7 +4,6 @@ namespace SpomkyLabs\PwaBundle\Dto; -use JetBrains\PhpStorm\Deprecated; use Symfony\Component\Serializer\Attribute\SerializedName; final class Workbox @@ -22,76 +21,24 @@ final class Workbox #[SerializedName('cache_manifest')] public bool $cacheManifest; - #[SerializedName('google_fonts')] - public GoogleFontCache $googleFontCache; - - #[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')] - public null|Url $pageFallback = null; - - #[SerializedName('image_fallback')] - public null|Asset $imageFallback = null; - - #[SerializedName('font_fallback')] - public null|Asset $fontFallback = null; - - /** - * @var array - */ - #[SerializedName('warm_cache_urls')] - public array $warmCacheUrls = []; + #[SerializedName('image_cache')] + public ImageCache $imageCache; - #[SerializedName('network_timeout_seconds')] - public int $networkTimeoutSeconds = 3; + #[SerializedName('font_cache')] + public FontCache $fontCache; - #[SerializedName('max_font_age')] - public int $maxFontAge = 60 * 60 * 24 * 365; + #[SerializedName('page_cache')] + public PageCache $pageCache; - #[SerializedName('max_font_cache_entries')] - public int $maxFontCacheEntries = 60; + #[SerializedName('asset_cache')] + public AssetCache $assetCache; - #[SerializedName('max_image_age')] - public int $maxImageAge = 60 * 60 * 24 * 365; - - #[SerializedName('max_image_cache_entries')] - public int $maxImageCacheEntries = 60; - - #[SerializedName('image_regex')] - public string $imageRegex = '/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/'; - - #[SerializedName('static_regex')] - public string $staticRegex = '/\.(css|m?jsx?|json|xml|txt|map|webmanifest)$/'; + #[SerializedName('google_fonts')] + public GoogleFontCache $googleFontCache; - #[SerializedName('font_regex')] - public string $fontRegex = '/\.(ttf|eot|otf|woff2)$/'; + #[SerializedName('offline_fallback')] + public OfflineFallback $offlineFallback; #[SerializedName('clear_cache')] public bool $clearCache = true; - - #[SerializedName('image_cache_name')] - public string $imageCacheName = 'images'; - - #[SerializedName('font_cache_name')] - public string $fontCacheName = 'fonts'; - - #[SerializedName('page_cache_name')] - public string $pageCacheName = 'pages'; - - #[SerializedName('asset_cache_name')] - public string $assetCacheName = 'assets'; } diff --git a/src/Resources/config/definition/service_worker.php b/src/Resources/config/definition/service_worker.php index f33f681..1b1e614 100644 --- a/src/Resources/config/definition/service_worker.php +++ b/src/Resources/config/definition/service_worker.php @@ -35,6 +35,87 @@ ->arrayNode('workbox') ->info('The configuration of the workbox.') ->canBeDisabled() + ->beforeNormalization() + ->ifTrue(static fn (mixed $v): bool => true) + ->then(static function (mixed $v): array { + if (isset($v['asset_cache'])) { + return $v; + } + $v['asset_cache'] = array_filter([ + 'enabled' => true, + 'cache_name' => $v['asset_cache_name'] ?? 'assets', + 'regex' => $v['static_regex'] ?? '/\.(css|js|json|xml|txt|map|ico|png|jpe?g|gif|svg|webp|bmp)$/', + ], static fn (mixed $v): bool => $v !== null); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(static fn (mixed $v): bool => true) + ->then(static function (mixed $v): array { + if (isset($v['image_cache'])) { + return $v; + } + $v['image_cache'] = array_filter([ + 'enabled' => true, + 'cache_name' => $v['image_cache_name'] ?? 'images', + 'regex' => $v['image_regex'] ?? '/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/', + 'max_entries' => $v['max_image_cache_entries'] ?? 60, + 'max_age' => $v['max_image_age'] ?? 60 * 60 * 24 * 365, + ], static fn (mixed $v): bool => $v !== null); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(static fn (mixed $v): bool => true) + ->then(static function (mixed $v): array { + if (isset($v['font_cache'])) { + return $v; + } + $v['font_cache'] = array_filter([ + 'enabled' => true, + 'cache_name' => $v['font_cache_name'] ?? 'fonts', + 'regex' => $v['font_regex'] ?? '/\.(ttf|eot|otf|woff2)$/', + 'max_entries' => $v['max_font_cache_entries'] ?? 60, + 'max_age' => $v['max_font_age'] ?? 60 * 60 * 24 * 365, + ], static fn (mixed $v): bool => $v !== null); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(static fn (mixed $v): bool => true) + ->then(static function (mixed $v): array { + if (isset($v['page_cache'])) { + return $v; + } + $v['page_cache'] = array_filter([ + 'enabled' => true, + 'cache_name' => $v['page_cache_name'] ?? 'pages', + 'network_timeout' => $v['network_timeout_seconds'] ?? 3, + 'urls' => $v['warm_cache_urls'] ?? [], + ], static fn (mixed $v): bool => $v !== null); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(static fn (mixed $v): bool => true) + ->then(static function (mixed $v): array { + if (isset($v['offline_fallback'])) { + return $v; + } + $v['offline_fallback'] = array_filter([ + 'enabled' => true, + 'page' => $v['page_fallback'] ?? null, + 'image' => $v['image_fallback'] ?? null, + 'font' => $v['font_fallback'] ?? null, + ], static fn (mixed $v): bool => $v !== null); + + return $v; + }) + ->end() ->children() ->booleanNode('use_cdn') ->defaultFalse() @@ -61,10 +142,6 @@ ->defaultTrue() ->info('Whether to cache the manifest file.') ->end() - ->booleanNode('cache_google_fonts') - ->defaultTrue() - ->info('Whether to cache the Google fonts.') - ->end() ->scalarNode('version') ->defaultValue('7.0.0') ->info('The version of workbox. When using local files, the version shall be "7.0.0."') @@ -119,21 +196,155 @@ ->defaultTrue() ->info('Whether to clear the cache during the service worker activation.') ->end() + ->arrayNode('offline_fallback') + ->canBeDisabled() + ->children() + ->append(getUrlNode('page', 'The URL of the offline page fallback.')) + ->append(getUrlNode('image', 'The URL of the offline image fallback.')) + ->append(getUrlNode('font', 'The URL of the offline font fallback.')) + ->end() + ->end() + ->arrayNode('image_cache') + ->canBeDisabled() + ->children() + ->scalarNode('cache_name') + ->defaultValue('images') + ->info('The name of the image cache.') + ->end() + ->scalarNode('regex') + ->defaultValue('/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/') + ->info('The regex to match the images.') + ->example('/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/') + ->end() + ->integerNode('max_entries') + ->defaultValue(60) + ->info('The maximum number of entries in the image cache.') + ->example([50, 100, 200]) + ->end() + ->integerNode('max_age') + ->defaultValue(60 * 60 * 24 * 365) + ->info('The maximum number of seconds before the image cache is invalidated.') + ->example([60 * 60 * 24 * 365, 60 * 60 * 24 * 30, 60 * 60 * 24 * 7]) + ->end() + ->end() + ->end() + ->arrayNode('asset_cache') + ->canBeDisabled() + ->children() + ->scalarNode('cache_name') + ->defaultValue('assets') + ->info('The name of the asset cache.') + ->end() + ->scalarNode('regex') + ->defaultValue('/\.(css|js|json|xml|txt|map|ico|png|jpe?g|gif|svg|webp|bmp)$/') + ->info('The regex to match the assets.') + ->example('/\.(css|js|json|xml|txt|map|ico|png|jpe?g|gif|svg|webp|bmp)$/') + ->end() + ->end() + ->end() + ->arrayNode('font_cache') + ->canBeDisabled() + ->children() + ->scalarNode('cache_name') + ->defaultValue('fonts') + ->info('The name of the font cache.') + ->end() + ->scalarNode('regex') + ->defaultValue('/\.(ttf|eot|otf|woff2)$/') + ->info('The regex to match the fonts.') + ->example('/\.(ttf|eot|otf|woff2)$/') + ->end() + ->integerNode('max_entries') + ->defaultValue(60) + ->info('The maximum number of entries in the image cache.') + ->example([50, 100, 200]) + ->end() + ->integerNode('max_age') + ->defaultValue(60 * 60 * 24 * 365) + ->info('The maximum number of seconds before the font cache is invalidated.') + ->example([60 * 60 * 24 * 365, 60 * 60 * 24 * 30, 60 * 60 * 24 * 7]) + ->end() + ->end() + ->end() + ->arrayNode('page_cache') + ->canBeDisabled() + ->children() + ->scalarNode('cache_name') + ->defaultValue('pages') + ->info('The name of the page cache.') + ->end() + ->integerNode('network_timeout') + ->defaultValue(3) + ->info( + 'The network timeout in seconds before cache is called (for warm cache URLs only).' + ) + ->example([1, 2, 5]) + ->end() + ->arrayNode('urls') + ->treatNullLike([]) + ->treatFalseLike([]) + ->treatTrueLike([]) + ->info('The URLs to warm the cache. The URLs shall be served by the application.') + ->arrayPrototype() + ->beforeNormalization() + ->ifString() + ->then(static fn (string $v): array => [ + 'path' => $v, + ]) + ->end() + ->children() + ->scalarNode('path') + ->isRequired() + ->info('The URL of the shortcut.') + ->example('app_homepage') + ->end() + ->arrayNode('params') + ->treatFalseLike([]) + ->treatTrueLike([]) + ->treatNullLike([]) + ->prototype('variable')->end() + ->info('The parameters of the action.') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() ->scalarNode('image_cache_name') ->defaultValue('images') ->info('The name of the image cache.') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.image_cache.cache_name" instead.' + ) ->end() ->scalarNode('font_cache_name') ->defaultValue('fonts') ->info('The name of the font cache.') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.font_cache.cache_name" instead.' + ) ->end() ->scalarNode('page_cache_name') ->defaultValue('pages') ->info('The name of the page cache.') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.page_cache.cache_name" instead.' + ) ->end() ->scalarNode('asset_cache_name') ->defaultValue('assets') ->info('The name of the asset cache.') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.asset_cache.cache_name" instead.' + ) ->end() ->append(getUrlNode('page_fallback', 'The URL of the offline page fallback.')) ->append(getUrlNode('image_fallback', 'The URL of the offline image fallback.')) @@ -142,47 +353,92 @@ ->defaultValue('/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/') ->info('The regex to match the images.') ->example('/\.(ico|png|jpe?g|gif|svg|webp|bmp)$/') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.image_cache.regex" instead.' + ) ->end() ->scalarNode('static_regex') - ->defaultValue('/\.(css|js|json|xml|txt|map|webmanifest)$/') + ->defaultValue('/\.(css|js|json|xml|txt|map)$/') ->info('The regex to match the static files.') - ->example('/\.(css|js|json|xml|txt|woff2|ttf|eot|otf|map|webmanifest)$/') + ->example('/\.(css|js|json|xml|txt|map)$/') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.asset_cache.regex" instead.' + ) ->end() ->scalarNode('font_regex') ->defaultValue('/\.(ttf|eot|otf|woff2)$/') ->info('The regex to match the static files.') ->example('/\.(ttf|eot|otf|woff2)$/') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.font_cache.regex" instead.' + ) ->end() ->integerNode('max_image_cache_entries') ->defaultValue(60) ->info('The maximum number of entries in the image cache.') ->example([50, 100, 200]) + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.image_cache.max_entries" instead.' + ) ->end() ->integerNode('max_image_age') ->defaultValue(60 * 60 * 24 * 365) ->info('The maximum number of seconds before the image cache is invalidated.') ->example([60 * 60 * 24 * 365, 60 * 60 * 24 * 30, 60 * 60 * 24 * 7]) + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.image_cache.max_age" instead.' + ) ->end() ->integerNode('max_font_cache_entries') ->defaultValue(30) ->info('The maximum number of entries in the font cache.') ->example([30, 50, 100]) + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.font_cache.max_entries" instead.' + ) ->end() ->integerNode('max_font_age') ->defaultValue(60 * 60 * 24 * 365) ->info('The maximum number of seconds before the font cache is invalidated.') ->example([60 * 60 * 24 * 365, 60 * 60 * 24 * 30, 60 * 60 * 24 * 7]) + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.font_cache.max_age" instead.' + ) ->end() ->integerNode('network_timeout_seconds') ->defaultValue(3) ->info('The network timeout in seconds before cache is called (for warm cache URLs only).') ->example([1, 2, 5]) + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.page_cache.network_timeout" instead.' + ) ->end() ->arrayNode('warm_cache_urls') ->treatNullLike([]) ->treatFalseLike([]) ->treatTrueLike([]) ->info('The URLs to warm the cache. The URLs shall be served by the application.') + ->setDeprecated( + 'spomky-labs/phpwa', + '1.1.0', + 'The "%node%" option is deprecated and will be removed in 2.0.0. Please use "pwa.serviceworker.workbox.page_cache.urls" instead.' + ) ->arrayPrototype() ->beforeNormalization() ->ifString() diff --git a/src/Resources/config/definition/utils/shortcuts.php b/src/Resources/config/definition/utils/shortcuts.php index 96b2dc7..a7190f9 100644 --- a/src/Resources/config/definition/utils/shortcuts.php +++ b/src/Resources/config/definition/utils/shortcuts.php @@ -16,24 +16,23 @@ function setupShortcuts(): ArrayNodeDefinition ->treatNullLike([]) ->info('The shortcuts of the application.') ->arrayPrototype() - ->children() - ->scalarNode('name') - ->isRequired() - ->info('The name of the shortcut.') - ->example('Awesome shortcut') - ->end() - ->scalarNode('short_name') - ->info('The short name of the shortcut.') - ->example('shortcut') - ->end() - ->scalarNode('description') - ->info('The description of the shortcut.') - ->example('This is an awesome shortcut') - ->end() - ->append(getUrlNode('url', 'The URL of the shortcut.')) - ->append(getIconsNode('The icons of the shortcut.')) - ->end() - ->end() + ->children() + ->scalarNode('name') + ->isRequired() + ->info('The name of the shortcut.') + ->example('Awesome shortcut') + ->end() + ->scalarNode('short_name') + ->info('The short name of the shortcut.') + ->example('shortcut') + ->end() + ->scalarNode('description') + ->info('The description of the shortcut.') + ->example('This is an awesome shortcut') + ->end() + ->append(getUrlNode('url', 'The URL of the shortcut.')) + ->append(getIconsNode('The icons of the shortcut.')) + ->end() ->end(); return $node; diff --git a/src/Service/ServiceWorkerCompiler.php b/src/Service/ServiceWorkerCompiler.php index 0f86339..070446b 100644 --- a/src/Service/ServiceWorkerCompiler.php +++ b/src/Service/ServiceWorkerCompiler.php @@ -37,11 +37,17 @@ public function __construct( private Manifest $manifest, private ServiceWorker $serviceWorker, private AssetMapperInterface $assetMapper, + #[Autowire('%kernel.debug%')] + bool $debug, ) { $this->manifestPublicUrl = '/' . trim($manifestPublicUrl, '/'); - $this->jsonOptions = [ - JsonEncode::OPTIONS => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + $options = [ + 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 compile(): ?string @@ -141,12 +147,12 @@ private function processClearCache(Workbox $workbox, string $body): string private function processAssetCacheRules(Workbox $workbox, string $body): string { + if ($workbox->assetCache->enabled === false) { + return $body; + } $assets = []; foreach ($this->assetMapper->allAssets() as $asset) { - if (preg_match($workbox->imageRegex, $asset->sourcePath) === 1 || preg_match( - $workbox->staticRegex, - $asset->sourcePath - ) === 1) { + if (preg_match($workbox->assetCache->regex, $asset->sourcePath) === 1) { $assets[] = $asset->publicPath; } } @@ -154,9 +160,8 @@ private function processAssetCacheRules(Workbox $workbox, string $body): string $assetUrlsLength = count($assets) * 2; $declaration = <<assetCacheName}', + cacheName: '{$workbox->assetCache->cacheName}', plugins: [ new workbox.cacheableResponse.CacheableResponsePlugin({statuses: [0, 200]}), new workbox.expiration.ExpirationPlugin({ @@ -187,25 +192,27 @@ private function processAssetCacheRules(Workbox $workbox, string $body): string private function processFontCacheRules(Workbox $workbox, string $body): string { + if ($workbox->fontCache->enabled === false) { + return $body; + } $fonts = []; foreach ($this->assetMapper->allAssets() as $asset) { - if (preg_match($workbox->fontRegex, $asset->sourcePath) === 1) { + if (preg_match($workbox->fontCache->regex, $asset->sourcePath) === 1) { $fonts[] = $asset->publicPath; } } $fontUrls = $this->serializer->serialize($fonts, 'json', $this->jsonOptions); $declaration = <<fontCacheName}', + cacheName: '{$workbox->fontCache->cacheName}', plugins: [ new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200], }), new workbox.expiration.ExpirationPlugin({ - maxAgeSeconds: {$workbox->maxFontAge}, - maxEntries: {$workbox->maxFontCacheEntries}, + maxAgeSeconds: {$workbox->fontCache->maxAge}, + maxEntries: {$workbox->fontCache->maxEntries}, }), ], }); @@ -231,13 +238,15 @@ private function processFontCacheRules(Workbox $workbox, string $body): string private function processPageImageCacheRule(Workbox $workbox, string $body): string { - $routes = $this->serializer->serialize($workbox->warmCacheUrls, 'json', $this->jsonOptions); + if ($workbox->pageCache->enabled === false) { + return $body; + } + $routes = $this->serializer->serialize($workbox->pageCache->urls, 'json', $this->jsonOptions); $declaration = <<pageCacheName}', - networkTimeoutSeconds: {$workbox->networkTimeoutSeconds}, + cacheName: '{$workbox->pageCache->cacheName}', + networkTimeoutSeconds: {$workbox->pageCache->networkTimeout}, warmCache: {$routes} }); PAGE_CACHE_RULE_STRATEGY; @@ -247,17 +256,19 @@ private function processPageImageCacheRule(Workbox $workbox, string $body): stri private function processImageCacheRule(Workbox $workbox, string $body): string { + if ($workbox->imageCache->enabled === false) { + return $body; + } $declaration = << (request.destination === 'image' && !url.pathname.startsWith('{$this->assetPublicPrefix}')), new workbox.strategies.CacheFirst({ - cacheName: '{$workbox->imageCacheName}', + cacheName: '{$workbox->imageCache->cacheName}', plugins: [ new workbox.cacheableResponse.CacheableResponsePlugin({statuses: [0, 200]}), new workbox.expiration.ExpirationPlugin({ - maxEntries: {$workbox->maxImageCacheEntries}, - maxAgeSeconds: {$workbox->maxImageAge}, + maxEntries: {$workbox->imageCache->maxEntries}, + maxAgeSeconds: {$workbox->imageCache->maxAge}, }), ], }) @@ -274,7 +285,6 @@ private function processCacheRootFilesRule(Workbox $workbox, string $body): stri } $declaration = << '{$this->manifestPublicUrl}' === url.pathname, new workbox.strategies.StaleWhileRevalidate({ @@ -300,7 +310,6 @@ private function processCacheGoogleFontsRule(Workbox $workbox, string $body): st $options = count($options) === 0 ? '' : $this->serializer->serialize($options, 'json', $this->jsonOptions); $declaration = <<pageFallback === null && $workbox->imageFallback === null && $workbox->fontFallback === null) { + if ($workbox->offlineFallback->enabled === false) { return $body; } - $pageFallback = $workbox->pageFallback === null ? 'null' : $this->serializer->serialize( - $workbox->pageFallback, - 'json', - $this->jsonOptions - ); - $imageFallback = $workbox->imageFallback === null ? 'null' : $this->serializer->serialize( - $workbox->imageFallback, - 'json', - $this->jsonOptions - ); - $fontFallback = $workbox->fontFallback === null ? 'null' : $this->serializer->serialize( - $workbox->fontFallback, - 'json', - $this->jsonOptions - ); + $options = [ + 'pageFallback' => $workbox->offlineFallback->pageFallback, + 'imageFallback' => $workbox->offlineFallback->imageFallback, + 'fontFallback' => $workbox->offlineFallback->fontFallback, + ]; + $options = array_filter($options, static fn (mixed $v): bool => $v !== null); + $options = count($options) === 0 ? '' : $this->serializer->serialize($options, 'json', $this->jsonOptions); $declaration = <<