diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index 72dba3f..949c210 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -78,28 +78,28 @@ class Asset { protected array $groups = []; /** - * Should the asset be loaded in the footer? + * Whether the asset should be loaded in the footer. * * @var bool */ protected bool $in_footer = true; /** - * Should the asset be marked as async? + * Whether the asset should be marked as async. * * @var bool */ protected bool $is_async = false; /** - * Should the asset be marked as deferred? + * Whether the asset should be marked as deferred. * * @var bool */ protected bool $is_deferred = false; /** - * Is the asset enqueued? + * Whether the asset has been enqueued. * * @var bool */ @@ -113,21 +113,21 @@ class Asset { protected bool $is_module = false; /** - * Is the asset printed? + * Whether the asset has been printed. * * @var bool */ protected bool $is_printed = false; /** - * Is the asset registered? + * Whether the asset has been registered. * * @var bool */ protected bool $is_registered = false; /** - * Is the asset a vendor asset? + * Whether this is a vendor asset. * * @var bool */ @@ -344,6 +344,8 @@ public function add_to_group_path( string $group_path_name ) { * @param string $file The asset file path. * @param string|null $version The asset version. * @param string|null $root_path The path to the root of the plugin. + * + * @return self */ public static function add( string $slug, string $file, string $version = null, $root_path = null ) { return Assets::init()->add( new self( $slug, $file, $version, $root_path ) ); @@ -534,7 +536,7 @@ protected function build_asset_url(): string { $url = $plugin_base_url . $resource_path . $resource; /** - * Filters the asset URL + * Filters the asset URL. * * @param string $url Asset URL. * @param string $slug Asset slug. @@ -681,11 +683,11 @@ public function clone_to( string $clone_type, ...$dependencies ) { $source_type = $this->get_type(); if ( $clone_type === $source_type ) { - throw new \InvalidArgumentException( 'The clone type must be different from the source type.' ); + throw new InvalidArgumentException( 'The clone type must be different from the source type.' ); } if ( ! in_array( $clone_type, [ 'css', 'js' ], true ) ) { - throw new \InvalidArgumentException( 'The clone type must be either "css" or "js".' ); + throw new InvalidArgumentException( 'The clone type must be either "css" or "js".' ); } $slug = $this->slug; diff --git a/src/Assets/Assets.php b/src/Assets/Assets.php index f389b04..dc7d51e 100755 --- a/src/Assets/Assets.php +++ b/src/Assets/Assets.php @@ -109,22 +109,21 @@ public static function asset( string $slug, string $file, string $version = null * * @param Asset $asset Register an asset. * - * @return Asset|false The registered object or false on error. + * @return Asset|VendorAsset The registered object. * @since 1.0.0 * */ public function add( Asset $asset ) { - // Prevent weird stuff here. + // Check if the slug is registered, and if so return the previously-registered Asset. $slug = $asset->get_slug(); if ( $this->exists( $slug ) ) { return $this->get( $slug ); } - // Set the Asset on the array of notices. + // Add the asset to the array of assets. $this->assets[ $slug ] = $asset; - // Return the Slug because it might be modified. return $asset; } @@ -193,7 +192,7 @@ public function filter_print_before_after_script( $tag, $handle ): string { * @param string|array $slug Slug of the Asset. * @param boolean $sort If we should do any sorting before returning. * - * @return array|Asset Array of asset objects, single asset object, or null if looking for a single asset but + * @return array|Asset|VendorAsset Array of asset objects, single asset object, or null if looking for a single asset but * it was not in the array of objects. * @since 1.0.0 * diff --git a/src/Assets/VendorAsset.php b/src/Assets/VendorAsset.php new file mode 100644 index 0000000..0df9b49 --- /dev/null +++ b/src/Assets/VendorAsset.php @@ -0,0 +1,276 @@ +url = $filtered; + $this->slug = sanitize_key( $slug ); + $this->type = strtolower( $type ); + } + + /** + * Registers a vendor asset. + * + * @param string $slug The asset slug. + * @param string $url The asset file path. + * @param ?string $type The asset type. + * @param ?string $version The asset version. + * + * @return self + */ + public static function add( string $slug, string $url, ?string $type = null, $version = null ) { + $instance = new self( $slug, $url, $type ?? 'js' ); + + if ( null !== $version ) { + $instance->set_version( (string) $version ); + } + + $registered = Assets::init()->add( $instance ); + if ( ! $registered instanceof self ) { + throw new RuntimeException( 'The asset was already registered as a different type.' ); + } + + return $registered; + } + + /** + * Set the asset version. + * + * @param string $version The asset version. + * + * @return static + */ + public function set_version( string $version ): self { + $this->version = $version; + return $this; + } + + /** + * Get the asset version. + * + * @since 1.0.0 + * + * @return string The asset version. + */ + public function get_version(): string { + return $this->version ?? ''; + } + + /** + * Get the asset url. + * + * If the version has been provided, then it will be used to format the URL. + * + * @since 1.0.0 + * + * @param bool $_unused (Unused) Use the minified version of the asset if available. + * + * @return string + * @throws LogicException If the URL has a placeholder but no version is provided. + */ + public function get_url( bool $_unused = true ): string { + $has_version = null !== $this->version; + if ( ! $has_version && $this->url_has_placeholder( $this->url ) ) { + throw new LogicException( 'A URL with a placeholder must have a version provided.' ); + } + + $url = $has_version + ? $this->get_formatted_url() + : $this->url; + + $hook_prefix = Config::get_hook_prefix(); + + /** + * Filters the asset URL. + * + * @param string $url Asset URL. + * @param string $slug Asset slug. + * @param Asset $asset The Asset object. + */ + return (string) apply_filters( "stellarwp/assets/{$hook_prefix}/resource_url", $url, $this->slug, $this ); + } + + /** + * Get the minified version of the URL. + * + * @return string + */ + public function get_min_url(): string { + return $this->get_url(); + } + + /** + * Get the formatted version of the URL. + * + * This will replace the version placeholder in the URL with the actual version. + * If there is no placeholder, it will append the version as a query string. + * + * @return string + */ + protected function get_formatted_url() { + return $this->url_has_placeholder( $this->url ) + ? sprintf( $this->url, $this->version ) + : add_query_arg( 'ver', $this->version, $this->url ); + } + + /** + * Determine if the URL has a placeholder for the version. + * + * @param string $url The URL to check. + * + * @return bool True if the URL has a placeholder, false otherwise. + */ + protected function url_has_placeholder( string $url ): bool { + return false !== strpos( $url, '%s' ); + } + + // --------------------------------------------- + // NO-OP or UNUSED METHODS + // --------------------------------------------- + + /** + * Get the asset asset file path. + * + * @return string + */ + public function get_asset_file_path(): string { + return ''; + } + + /** + * Get the asset file. + * + * @return string + */ + public function get_file(): string { + return ''; + } + + /** + * Get the asset min path. + * + * @return string + */ + public function get_min_path(): string { + return ''; + } + + /** + * Get the asset min path. + * + * @return string + */ + public function get_path(): string { + return ''; + } + + /** + * Gets the root path for the resource. + * + * @return ?string + */ + public function get_root_path(): ?string { + return ''; + } + + /** + * Get the asset translation path. + * + * @return string + */ + public function get_translation_path(): string { + return ''; + } + + /** + * Get the asset's full path - considering if minified exists. + * + * @param bool $_unused + * + * @return string + */ + public function get_full_resource_path( bool $_unused = true ): string { + return $this->get_url( $_unused ); + } + + /** + * Set the asset file path for the asset. + * + * @param string $path The partial path to the asset. + * + * @return static + */ + public function set_asset_file( string $path ) { + return $this; + } + + /** + * Set the directory where asset should be retrieved. + * + * @param ?string $path The path to the minified file. + * @param ?bool $prefix Whether to prefix files automatically by type (e.g. js/ for JS). Defaults to true. + * + * @return static + */ + public function set_path( ?string $path = null, $prefix = null ) { + return $this; + } + + /** + * Set the directory where min files should be retrieved. + * + * @param ?string $path The path to the minified file. + * + * @return static + */ + public function set_min_path( ?string $path = null ) { + return $this; + } + + /** + * Set whether or not to use an .asset.php file. + * + * @param boolean $_unused Whether to use an .asset.php file. + * + * @return static + */ + public function use_asset_file( bool $_unused = true ): self { + return $this; + } +} diff --git a/tests/wpunit/VendorAssetTest.php b/tests/wpunit/VendorAssetTest.php new file mode 100644 index 0000000..c49e736 --- /dev/null +++ b/tests/wpunit/VendorAssetTest.php @@ -0,0 +1,67 @@ +assertEquals( 'test-script', $asset->get_slug() ); + $this->assertEquals( 'https://example.com/fake.js', $asset->get_url() ); + $this->assertEquals( 'js', $asset->get_type() ); + } + + public function test_invalid_url_throws_exception(): void { + $this->expectException( InvalidArgumentException::class ); + + new VendorAsset( 'test-script', 'invalid-url' ); + } + + public function test_can_set_version(): void { + $asset = new VendorAsset( 'test-script', 'https://example.com/fake.js' ); + $asset->set_version( '1.0.0' ); + + $this->assertEquals( '1.0.0', $asset->get_version() ); + $this->assertEquals( 'https://example.com/fake.js?ver=1.0.0', $asset->get_url() ); + } + + public function test_can_set_version_with_url_placeholder(): void { + $asset = new VendorAsset( 'test-script', 'https://example.com/path/to/version/%s/fake.js' ); + $asset->set_version( '1.2.3' ); + $this->assertEquals( '1.2.3', $asset->get_version() ); + $this->assertEquals( 'https://example.com/path/to/version/1.2.3/fake.js', $asset->get_url() ); + } + + public function test_placeholder_without_version_throws_error(): void { + $this->expectException( LogicException::class ); + $this->expectExceptionMessage( 'A URL with a placeholder must have a version provided.' ); + + $asset = new VendorAsset( 'test-script', 'https://example.com/path/to/version/%s/fake.js' ); + $asset->get_url(); + } + + public function test_version_with_query_string_has_ver_appended_correctly(): void { + $asset = new VendorAsset( 'test-script', 'https://example.com/path/to/version?query=string' ); + $asset->set_version( '1.2.3' ); + + $this->assertEquals( '1.2.3', $asset->get_version() ); + $this->assertEquals( 'https://example.com/path/to/version?query=string&ver=1.2.3', $asset->get_url() ); + } +}