diff --git a/_ide_helper.php b/_ide_helper.php index 228893b3169..a838afac059 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -33,7 +33,6 @@ class Hyde extends \Hyde\Hyde {} class Site extends \Hyde\Facades\Site {} class Meta extends \Hyde\Facades\Meta {} -/** @mixin \Hyde\Framework\Services\AssetService */ class Asset extends \Hyde\Facades\Asset {} class Author extends \Hyde\Facades\Author {} class Features extends \Hyde\Facades\Features {} diff --git a/monorepo/docs/hydefront.md b/monorepo/docs/hydefront.md index 377f920e6e0..cb7733eabcd 100644 --- a/monorepo/docs/hydefront.md +++ b/monorepo/docs/hydefront.md @@ -64,6 +64,6 @@ This will create commits in both the monorepo and submodule. Now follow the foll 9. Update the monorepo to use the new version. - [ ] `npm update hydefront` - **Note:** On major/minor version bumps, **remember to update** the `package.json` in the `packages/hyde` directory to use the new version! - - **Note:** On major version bumps, **remember to update the HydeFront version in the Asset Service!** + - **Note:** On major version bumps, **remember to update the HydeFront version in the Asset Facade!** 10. Amend the monorepo commit with the updated files (package-lock.json, _media/app.css, packages\hydefront, packages\hyde\package.json) - - [ ] `git add packages/hydefront && git add packages/hyde/package.json && git add package-lock.json && git add _media/app.css && git add packages/framework/src/Framework/Services/AssetService.php && git commit --amend --no-edit` + - [ ] `git add packages/hydefront && git add packages/hyde/package.json && git add package-lock.json && git add _media/app.css && git add packages/framework/src/Facades/Asset.php && git commit --amend --no-edit` diff --git a/packages/framework/src/Facades/Asset.php b/packages/framework/src/Facades/Asset.php index f0bd3b6c651..49065d3b352 100644 --- a/packages/framework/src/Facades/Asset.php +++ b/packages/framework/src/Facades/Asset.php @@ -4,25 +4,83 @@ namespace Hyde\Facades; -use Hyde\Framework\Services\AssetService; -use Illuminate\Support\Facades\Facade; +use Hyde\Hyde; +use Illuminate\Support\Str; + +use function rtrim; +use function explode; +use function implode; +use function md5_file; +use function file_exists; +use function str_replace; +use function preg_replace; +use function str_contains; +use function file_get_contents; /** * Handles the retrieval of core asset files, either from the HydeFront CDN or from the local media folder. * - * @see \Hyde\Framework\Services\AssetService - * - * @method static string version() - * @method static string cdnLink(string $file) - * @method static string mediaLink(string $file) - * @method static bool hasMediaFile(string $file) - * @method static string injectTailwindConfig() + * This class provides static methods for interacting with versioned files, + * as well as the HydeFront CDN service and the media directories. */ -class Asset extends Facade +class Asset { - /** @psalm-return AssetService::class */ - protected static function getFacadeAccessor(): string + /** @var string The default HydeFront SemVer tag to load. This constant is set to match the styles used for the installed framework version. */ + final protected const HYDEFRONT_VERSION = 'v3.4'; + + /** @var string The default HydeFront CDN path pattern. The Blade-style placeholders are replaced with the proper values. */ + final protected const HYDEFRONT_CDN_URL = 'https://cdn.jsdelivr.net/npm/hydefront@{{ $version }}/dist/{{ $file }}'; + + public static function version(): string + { + return static::HYDEFRONT_VERSION; + } + + public static function cdnLink(string $file): string + { + return static::constructCdnPath($file); + } + + public static function mediaLink(string $file): string + { + return Hyde::mediaLink($file).static::getCacheBustKey($file); + } + + public static function hasMediaFile(string $file): bool + { + return file_exists(Hyde::mediaPath($file)); + } + + public static function injectTailwindConfig(): string + { + if (! file_exists(Hyde::path('tailwind.config.js'))) { + return ''; + } + + $config = Str::between(file_get_contents(Hyde::path('tailwind.config.js')), '{', '}'); + + // Remove the plugins array, as it is not used in the frontend. + if (str_contains($config, 'plugins: [')) { + $tokens = explode('plugins: [', $config, 2); + $tokens[1] = Str::after($tokens[1], ']'); + $config = implode('', $tokens); + } + + return preg_replace('/\s+/', ' ', "/* tailwind.config.js */ \n".rtrim($config, ",\n\r")); + } + + protected static function constructCdnPath(string $file): string + { + return str_replace( + ['{{ $version }}', '{{ $file }}'], [static::version(), $file], + static::HYDEFRONT_CDN_URL + ); + } + + protected static function getCacheBustKey(string $file): string { - return AssetService::class; + return Config::getBool('hyde.enable_cache_busting', true) + ? '?v='.md5_file(Hyde::mediaPath("$file")) + : ''; } } diff --git a/packages/framework/src/Framework/HydeServiceProvider.php b/packages/framework/src/Framework/HydeServiceProvider.php index c2896efdc9b..e3dbd7eb5ea 100644 --- a/packages/framework/src/Framework/HydeServiceProvider.php +++ b/packages/framework/src/Framework/HydeServiceProvider.php @@ -10,7 +10,6 @@ use Hyde\Pages\MarkdownPost; use Hyde\Foundation\HydeKernel; use Hyde\Pages\DocumentationPage; -use Hyde\Framework\Services\AssetService; use Hyde\Framework\Services\BuildTaskService; use Hyde\Framework\Concerns\RegistersFileLocations; use Illuminate\Support\ServiceProvider; @@ -29,7 +28,6 @@ public function register(): void { $this->kernel = HydeKernel::getInstance(); - $this->app->singleton(AssetService::class, AssetService::class); $this->app->singleton(BuildTaskService::class, BuildTaskService::class); $this->kernel->setSourceRoot(Config::getString('hyde.source_root', '')); diff --git a/packages/framework/src/Framework/Services/AssetService.php b/packages/framework/src/Framework/Services/AssetService.php deleted file mode 100644 index c306f623a09..00000000000 --- a/packages/framework/src/Framework/Services/AssetService.php +++ /dev/null @@ -1,94 +0,0 @@ -version; - } - - public function cdnLink(string $file): string - { - return $this->constructCdnPath($file); - } - - public function mediaLink(string $file): string - { - return Hyde::mediaLink($file).$this->getCacheBustKey($file); - } - - public function hasMediaFile(string $file): bool - { - return file_exists(Hyde::mediaPath($file)); - } - - public function injectTailwindConfig(): string - { - if (! file_exists(Hyde::path('tailwind.config.js'))) { - return ''; - } - - $config = Str::between(file_get_contents(Hyde::path('tailwind.config.js')), '{', '}'); - - // Remove the plugins array, as it is not used in the frontend. - if (str_contains($config, 'plugins: [')) { - $tokens = explode('plugins: [', $config, 2); - $tokens[1] = Str::after($tokens[1], ']'); - $config = implode('', $tokens); - } - - return preg_replace('/\s+/', ' ', "/* tailwind.config.js */ \n".rtrim($config, ",\n\r")); - } - - protected function constructCdnPath(string $file): string - { - return str_replace( - ['{{ $version }}', '{{ $file }}'], [$this->version(), $file], - $this->cdnUrl - ); - } - - protected function getCacheBustKey(string $file): string - { - return Config::getBool('hyde.enable_cache_busting', true) - ? '?v='.md5_file(Hyde::mediaPath("$file")) - : ''; - } -} diff --git a/packages/framework/tests/Feature/AssetServiceTest.php b/packages/framework/tests/Feature/AssetFacadeTest.php similarity index 65% rename from packages/framework/tests/Feature/AssetServiceTest.php rename to packages/framework/tests/Feature/AssetFacadeTest.php index ae8661b8118..3e934eab38b 100644 --- a/packages/framework/tests/Feature/AssetServiceTest.php +++ b/packages/framework/tests/Feature/AssetFacadeTest.php @@ -4,22 +4,20 @@ namespace Hyde\Framework\Testing\Feature; -use Hyde\Framework\Services\AssetService; +use Hyde\Facades\Asset; use Hyde\Hyde; use Hyde\Testing\TestCase; /** - * @covers \Hyde\Framework\Services\AssetService + * @covers \Hyde\Facades\Asset * - * @see \Hyde\Framework\Testing\Unit\AssetServiceUnitTest + * @see \Hyde\Framework\Testing\Unit\Facades\AssetFacadeUnitTest */ -class AssetServiceTest extends TestCase +class AssetFacadeTest extends TestCase { public function testMediaLinkReturnsMediaPathWithCacheKey() { - $service = new AssetService(); - - $this->assertIsString($path = $service->mediaLink('app.css')); + $this->assertIsString($path = Asset::mediaLink('app.css')); $this->assertSame('media/app.css?v='.md5_file(Hyde::path('_media/app.css')), $path); } @@ -27,8 +25,7 @@ public function testMediaLinkReturnsMediaPathWithoutCacheKeyIfCacheBustingIsDisa { config(['hyde.enable_cache_busting' => false]); - $service = new AssetService(); - $path = $service->mediaLink('app.css'); + $path = Asset::mediaLink('app.css'); $this->assertIsString($path); $this->assertSame('media/app.css', $path); @@ -41,8 +38,7 @@ public function testMediaLinkSupportsCustomMediaDirectories() Hyde::setMediaDirectory('_assets'); - $service = new AssetService(); - $path = $service->mediaLink('app.css'); + $path = Asset::mediaLink('app.css'); $this->assertIsString($path); $this->assertSame('assets/app.css?v='.md5_file(Hyde::path('_assets/app.css')), $path); diff --git a/packages/framework/tests/Feature/HydeServiceProviderTest.php b/packages/framework/tests/Feature/HydeServiceProviderTest.php index 6904c5b11d6..ee7d35ae140 100644 --- a/packages/framework/tests/Feature/HydeServiceProviderTest.php +++ b/packages/framework/tests/Feature/HydeServiceProviderTest.php @@ -9,7 +9,6 @@ use Illuminate\Contracts\Container\BindingResolutionException; use Hyde\Console\ConsoleServiceProvider; use Hyde\Framework\HydeServiceProvider; -use Hyde\Framework\Services\AssetService; use Hyde\Framework\Services\BuildTaskService; use Hyde\Foundation\HydeCoreExtension; use Hyde\Hyde; @@ -54,13 +53,6 @@ public function testProviderHasBootMethod() $this->assertTrue(method_exists($this->provider, 'boot')); } - public function testProviderRegistersAssetServiceAsSingleton() - { - $this->assertTrue($this->app->bound(AssetService::class)); - $this->assertInstanceOf(AssetService::class, $this->app->make(AssetService::class)); - $this->assertSame($this->app->make(AssetService::class), $this->app->make(AssetService::class)); - } - public function testProviderRegistersBuildTaskServiceAsSingleton() { $this->assertTrue($this->app->bound(BuildTaskService::class)); diff --git a/packages/framework/tests/Unit/Facades/AssetFacadeTest.php b/packages/framework/tests/Unit/Facades/AssetFacadeTest.php deleted file mode 100644 index df8344a3771..00000000000 --- a/packages/framework/tests/Unit/Facades/AssetFacadeTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertInstanceOf(AssetService::class, Asset::getFacadeRoot()); - } - - public function testFacadeReturnsSameInstanceAsBoundByTheContainer() - { - $this->assertSame(Asset::getFacadeRoot(), app(AssetService::class)); - } - - public function testAssetFacadeCanCallMethodsOnTheAssetService() - { - $service = new AssetService(); - $this->assertEquals($service->version(), Asset::version()); - } -} diff --git a/packages/framework/tests/Unit/AssetServiceUnitTest.php b/packages/framework/tests/Unit/Facades/AssetFacadeUnitTest.php similarity index 62% rename from packages/framework/tests/Unit/AssetServiceUnitTest.php rename to packages/framework/tests/Unit/Facades/AssetFacadeUnitTest.php index 02c33795d15..ddfafe2ec72 100644 --- a/packages/framework/tests/Unit/AssetServiceUnitTest.php +++ b/packages/framework/tests/Unit/Facades/AssetFacadeUnitTest.php @@ -2,18 +2,18 @@ declare(strict_types=1); -namespace Hyde\Framework\Testing\Unit; +namespace Hyde\Framework\Testing\Unit\Facades; -use Hyde\Framework\Services\AssetService; -use Hyde\Testing\UnitTestCase; use Hyde\Hyde; +use Hyde\Facades\Asset; +use Hyde\Testing\UnitTestCase; /** - * @covers \Hyde\Framework\Services\AssetService + * @covers \Hyde\Facades\Asset * - * @see \Hyde\Framework\Testing\Feature\AssetServiceTest + * @see \Hyde\Framework\Testing\Feature\AssetFacadeTest */ -class AssetServiceUnitTest extends UnitTestCase +class AssetFacadeUnitTest extends UnitTestCase { protected function setUp(): void { @@ -23,31 +23,32 @@ protected function setUp(): void public function testServiceHasVersionString() { - $this->assertIsString((new AssetService())->version()); + $this->assertIsString(Asset::version()); } public function testCdnLinkHelper() { $this->assertSame( 'https://cdn.jsdelivr.net/npm/hydefront@v3.4/dist/styles.css', - (new AssetService())->cdnLink('styles.css') + Asset::cdnLink('styles.css') ); } public function testHasMediaFileHelper() { - $this->assertFalse((new AssetService())->hasMediaFile('styles.css')); + $this->assertFalse(Asset::hasMediaFile('styles.css')); } public function testHasMediaFileHelperReturnsTrueForExistingFile() { - $this->assertTrue((new AssetService())->hasMediaFile('app.css')); + $this->assertTrue(Asset::hasMediaFile('app.css')); } public function testInjectTailwindConfigReturnsExtractedTailwindConfig() { - $service = new AssetService(); - $this->assertIsString($config = $service->injectTailwindConfig()); + $config = Asset::injectTailwindConfig(); + + $this->assertIsString($config); $this->assertStringContainsString("darkMode: 'class'", $config); $this->assertStringContainsString('theme: {', $config); $this->assertStringContainsString('extend: {', $config); @@ -58,8 +59,8 @@ public function testInjectTailwindConfigReturnsExtractedTailwindConfig() public function testInjectTailwindConfigHandlesMissingConfigFileGracefully() { rename(Hyde::path('tailwind.config.js'), Hyde::path('tailwind.config.js.bak')); - $this->assertIsString((new AssetService())->injectTailwindConfig()); - $this->assertSame('', (new AssetService())->injectTailwindConfig()); + $this->assertIsString(Asset::injectTailwindConfig()); + $this->assertSame('', Asset::injectTailwindConfig()); rename(Hyde::path('tailwind.config.js.bak'), Hyde::path('tailwind.config.js')); } }