*/ protected array $register_styles = []; /** * An array of registered scripts. * * @var array */ protected array $register_scripts = []; /** * Get path to file and directory. * * @since 1.8.0 * * @param string $path Path. */ public function get_base_path( string $path ): string { return WEBSTORIES_PLUGIN_DIR_PATH . $path; } /** * Get url of file and directory. * * @since 1.8.0 * * @param string $path Path. */ public function get_base_url( string $path ): string { return WEBSTORIES_PLUGIN_DIR_URL . $path; } /** * Get asset metadata. * * @since 1.8.0 * * @param string $handle Script handle. * @return array Array containing combined contents of "<$handle>.asset.php" and "<$handle>.chunks.php". * * @phpstan-return AssetMetadata */ public function get_asset_metadata( string $handle ): array { $base_path = $this->get_base_path( 'assets/js/' ); // *.asset.php is generated by DependencyExtractionWebpackPlugin. // *.chunks.php is generated by HtmlWebpackPlugin with a custom template. $asset_file = $base_path . $handle . '.asset.php'; $chunks_file = $base_path . $handle . '.chunks.php'; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable $asset = is_readable( $asset_file ) ? require $asset_file : []; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable $chunks = is_readable( $chunks_file ) ? require $chunks_file : []; // A hash calculated based on the file content of the entry point bundle at <$handle>.js. $asset['version'] ??= WEBSTORIES_VERSION; $asset['dependencies'] ??= []; $asset['js'] = $chunks['js'] ?? []; $asset['css'] = $chunks['css'] ?? []; $asset['chunks'] = $chunks['chunks'] ?? []; return $asset; } /** * Register script using handle. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $script_handle Handle of script. * @param string[] $script_dependencies Array of extra dependencies. * @param bool $with_i18n Optional. Whether to setup i18n for this asset. Default true. */ public function register_script_asset( string $script_handle, array $script_dependencies = [], bool $with_i18n = true ): void { if ( isset( $this->register_scripts[ $script_handle ] ) ) { return; } $base_script_path = $this->get_base_url( 'assets/js/' ); $in_footer = true; $asset = $this->get_asset_metadata( $script_handle ); $entry_version = $asset['version']; // Register any chunks of $script_handle first. // `$asset['js']` are preloaded chunks, `$asset['chunks']` dynamically imported ones. foreach ( $asset['js'] as $chunk ) { $this->register_script( $chunk, $base_script_path . $chunk . '.js', [], $entry_version, $in_footer, $with_i18n ); } // Dynamically imported chunks MUST NOT be added as dependencies here. $dependencies = [ ...$asset['dependencies'], ...$script_dependencies, ...$asset['js'] ]; $this->register_script( $script_handle, $base_script_path . $script_handle . '.js', $dependencies, $entry_version, $in_footer, $with_i18n ); // "Save" all the script's chunks so we can later manually fetch them and their translations if needed. wp_script_add_data( $script_handle, 'chunks', $asset['chunks'] ); // Register every dynamically imported chunk as a script, just so // that we can print their translations whenever the main script is enqueued. // The actual enqueueing of these chunks is done by the main script via dynamic imports. foreach ( $asset['chunks'] as $dynamic_chunk ) { $this->register_script( $dynamic_chunk, $base_script_path . $dynamic_chunk . '.js', [], $entry_version, // Not actually used / relevant, since enqueueing is done by webpack. $in_footer, // Ditto. $with_i18n ); if ( $with_i18n ) { wp_add_inline_script( $script_handle, (string) wp_scripts()->print_translations( $dynamic_chunk, false ) ); } } } /** * Enqueue script using handle. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $script_handle Handle of script. * @param string[] $script_dependencies Array of extra dependencies. * @param bool $with_i18n Optional. Whether to setup i18n for this asset. Default true. */ public function enqueue_script_asset( string $script_handle, array $script_dependencies = [], bool $with_i18n = true ): void { $this->register_script_asset( $script_handle, $script_dependencies, $with_i18n ); $this->enqueue_script( $script_handle ); } /** * Register style using handle. * * @since 1.8.0 * * @param string $style_handle Handle of style. * @param string[] $style_dependencies Array of extra dependencies. */ public function register_style_asset( string $style_handle, array $style_dependencies = [] ): void { if ( isset( $this->register_styles[ $style_handle ] ) ) { return; } $base_style_url = $this->get_base_url( 'assets/css/' ); $base_style_path = $this->get_base_path( 'assets/css/' ); $ext = is_rtl() ? '-rtl.css' : '.css'; // Register any chunks of $style_handle first. $asset = $this->get_asset_metadata( $style_handle ); // Webpack appends "-[contenthash]" to filenames of chunks, so omit the `?ver=` query param. $chunk_version = null; foreach ( $asset['css'] as $style_chunk ) { $this->register_style( $style_chunk, $base_style_url . $style_chunk . '.css', [], $chunk_version ); wp_style_add_data( $style_chunk, 'path', $base_style_path . $style_chunk . $ext ); } $style_dependencies = [ ...$style_dependencies, ...$asset['css'] ]; $entry_version = $asset['version']; $this->register_style( $style_handle, $base_style_url . $style_handle . '.css', $style_dependencies, $entry_version ); wp_style_add_data( $style_handle, 'rtl', 'replace' ); wp_style_add_data( $style_handle, 'path', $base_style_path . $style_handle . $ext ); } /** * Enqueue style using handle. * * @since 1.8.0 * * @param string $style_handle Handle of style. * @param string[] $style_dependencies Array of extra dependencies. */ public function enqueue_style_asset( string $style_handle, array $style_dependencies = [] ): void { $this->register_style_asset( $style_handle, $style_dependencies ); $this->enqueue_style( $style_handle ); } /** * Register a CSS stylesheet. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $style_handle Name of the stylesheet. Should be unique. * @param string|false $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. * If source is set to false, stylesheet is an alias of other stylesheets it depends on. * @param string[] $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL * as a query string for cache busting purposes. If version is set to false, a version * number is automatically added equal to current installed WordPress version. * If set to null, no version is added. * @param string $media Optional. The media for which this stylesheet has been defined. * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like * '(orientation: portrait)' and '(max-width: 640px)'. * @return bool Whether the style has been registered. True on success, false on failure. */ public function register_style( string $style_handle, $src, array $deps = [], $ver = false, string $media = 'all' ): bool { if ( ! isset( $this->register_styles[ $style_handle ] ) ) { $this->register_styles[ $style_handle ] = wp_register_style( $style_handle, $src, $deps, $ver, $media ); } return $this->register_styles[ $style_handle ]; } /** * Register a new script. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $script_handle Name of the script. Should be unique. * @param string|false $src Full URL of the script, or path of the script relative to the WordPress root directory. * If source is set to false, script is an alias of other scripts it depends on. * @param string[] $deps Optional. An array of registered script handles this script depends on. Default empty array. * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL * as a query string for cache busting purposes. If version is set to false, a version * number is automatically added equal to current installed WordPress version. * If set to null, no version is added. * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . * Default 'false'. * @param bool $with_i18n Optional. Whether to setup i18n for this asset. Default true. * @return bool Whether the script has been registered. True on success, false on failure. */ public function register_script( string $script_handle, $src, array $deps = [], $ver = false, bool $in_footer = false, bool $with_i18n = true ): bool { if ( ! isset( $this->register_scripts[ $script_handle ] ) ) { $this->register_scripts[ $script_handle ] = wp_register_script( $script_handle, $src, $deps, $ver, $in_footer ); if ( $src && $with_i18n ) { wp_set_script_translations( $script_handle, 'web-stories' ); } } return $this->register_scripts[ $script_handle ]; } /** * Enqueue a style. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $style_handle Name of the stylesheet. Should be unique. * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. * Default empty. * @param string[] $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array. * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL * as a query string for cache busting purposes. If version is set to false, a version * number is automatically added equal to current installed WordPress version. * If set to null, no version is added. * @param string $media Optional. The media for which this stylesheet has been defined. * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like * '(orientation: portrait)' and '(max-width: 640px)'. */ public function enqueue_style( string $style_handle, string $src = '', array $deps = [], $ver = false, string $media = 'all' ): void { $this->register_style( $style_handle, $src, $deps, $ver, $media ); wp_enqueue_style( $style_handle, $src, $deps, $ver, $media ); } /** * Enqueue a script. * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) * * @since 1.8.0 * * @param string $script_handle Name of the script. Should be unique. * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory. * Default empty. * @param string[] $deps Optional. An array of registered script handles this script depends on. Default empty array. * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL * as a query string for cache busting purposes. If version is set to false, a version * number is automatically added equal to current installed WordPress version. * If set to null, no version is added. * @param bool $in_footer Optional. Whether to enqueue the script before instead of in the . * Default 'false'. * @param bool $with_i18n Optional. Whether to setup i18n for this asset. Default true. */ public function enqueue_script( string $script_handle, string $src = '', array $deps = [], $ver = false, bool $in_footer = false, bool $with_i18n = false ): void { $this->register_script( $script_handle, $src, $deps, $ver, $in_footer, $with_i18n ); wp_enqueue_script( $script_handle, $src, $deps, $ver, $in_footer ); } /** * Remove admin styles. * * @since 1.8.0 * * @param string[] $styles Array of styles to be removed. */ public function remove_admin_style( array $styles ): void { wp_styles()->registered['wp-admin']->deps = array_diff( wp_styles()->registered['wp-admin']->deps, $styles ); } /** * Returns the translations for a script and all of its chunks. * * @since 1.14.0 * * @param string $script_handle Name of the script. Should be unique. * @return array Script translations. */ public function get_translations( string $script_handle ): array { /** * List of script chunks. * * @var false|string[] */ $chunks = wp_scripts()->get_data( $script_handle, 'chunks' ); if ( ! \is_array( $chunks ) ) { return []; } $translations = [ (string) load_script_textdomain( $script_handle, 'web-stories' ), ]; /** * Dynamic chunk name. * * @var string $dynamic_chunk */ foreach ( $chunks as $dynamic_chunk ) { $translations[] = (string) load_script_textdomain( $dynamic_chunk, 'web-stories' ); } return array_values( array_map( static fn( $translations ) => json_decode( $translations, true ), array_filter( $translations ) ) ); } }