From ffa8f69fef81ea62f85a218d7ddc34027808f23d Mon Sep 17 00:00:00 2001 From: Ryan Leeson Date: Thu, 10 Aug 2023 13:57:51 -0400 Subject: [PATCH] WordPress Enqueue Priorities and Dependency Fixes (#10) - Expose the ability to change the script registration hook priorities - Remove the dependency array for style scripts in dev mode - Add option to place all dev styles (through JS) in the document head - Update documentation for priority and development style adjustments --- Readme.md | 67 ++++++------ src/AssetLoader.php | 10 +- src/Model/LoaderConfiguration.php | 166 ++++++++++++++++-------------- src/Registry/WordPress.php | 53 +++++++++- 4 files changed, 183 insertions(+), 113 deletions(-) diff --git a/Readme.md b/Readme.md index ac8085f..781e37a 100644 --- a/Readme.md +++ b/Readme.md @@ -16,19 +16,20 @@ PHP library for use in PHP and WordPress applications to facilitate development All assets are registered with the following sets of configuration parameters: -| Parameter | Default | -| --- | --- | -| `Asset Manifest Path` | None | -| `Handle Prefixes`| kanopi-pack- | -| `Production Domains` | None | -| `Script Path`| /assets/dist/js/ | -| `Static Path`| /assets/dist/static/ | -| `Style Path`| /assets/dist/css/ | -| `Version` | None | - - -| Class Method | Description | -| --- | --- | +| Parameter | Default | +|------------------------------|----------------------| +| `Asset Manifest Path` | None | +| `Development Styles In Head` | False | +| `Handle Prefixes` | kanopi-pack- | +| `Production Domains` | None | +| `Script Path` | /assets/dist/js/ | +| `Static Path` | /assets/dist/static/ | +| `Style Path` | /assets/dist/css/ | +| `Version` | None | + + +| Class Method | Description | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| | `__construct( ?string $_version = null, ?array $_production_domains = null, ?string $_asset_manifest_path = null, ?string $_handle_prefix = null, ?string $_script_path = null, ?string $_style_path = null, ?string $_static_path = null )` | Override any default values associated with the configuration | @@ -41,16 +42,16 @@ Webpack generates two special script types which are loaded at specific times, t 1. **Runtime** - Present to coordinate modules shared between multiple packages. For instance, if `app1.js` and `app2.js` share Vue3, a runtime file (default handle of `runtime`) is generated and must be loaded before either. 2. **Vendor** - Contains any third-party imported modules, either listed under the `package.json` key of `require`, or referenced in an entry point with `@import `. For instance, if `app1.js` and `app2.js` share Vue3, a vendor file (default handle of `vendor`) is generated and must be loaded before any Runtime and App entry points. **NOTE** - Webpack may also generate another module coordinating file, default handle of `central`, which is always present on the Dev Server. -| Class Method | Description | -| --- | --- | -| `__construct( string $_base_url, ?string $_development_url, Model\LoaderConfiguration $_configuration )` | Coordinate the asset loader, based on a set of production and development URLs, and path configurations | -| `enqueue_assets()` | Helper function generates a series of WordPress `wp_enqueue_*` calls | -| `in_development_mode(): bool` | Flag to tell whether the assets are currently loaded in Production or Development mode | -| `register_applications( string $_handle, array $_dependencies = [] )` | Register an application (contains both script and styles), via entry point handle and any preceding script handles (not styles) | -| `register_runtime_script( string $_handle, array $_dependencies = [] )` | Register the Webpack runtime script, via entry point handle and any preceding scripts | -| `register_script( string $_handle, array $_dependencies = [] )` | Register a script, via entry point handle and any preceding scripts | -| `register_style( string $_handle, array $_dependencies = [] )` | Register a style, via entry point handle and any preceding styles | -| `register_vendor_script( string $_handle, array $_dependencies = [] )` | Register a Webpack generated vendor script, via entry point handle and any preceding scripts | +| Class Method | Description | +|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +| `__construct( string $_base_url, ?string $_development_url, Model\LoaderConfiguration $_configuration )` | Coordinate the asset loader, based on a set of production and development URLs, and path configurations | +| `enqueue_assets()` | Helper function generates a series of WordPress `wp_enqueue_*` calls | +| `in_development_mode(): bool` | Flag to tell whether the assets are currently loaded in Production or Development mode | +| `register_applications( string $_handle, array $_dependencies = [] )` | Register an application (contains both script and styles), via entry point handle and any preceding script handles (not styles) | +| `register_runtime_script( string $_handle, array $_dependencies = [] )` | Register the Webpack runtime script, via entry point handle and any preceding scripts | +| `register_script( string $_handle, array $_dependencies = [] )` | Register a script, via entry point handle and any preceding scripts | +| `register_style( string $_handle, array $_dependencies = [] )` | Register a style, via entry point handle and any preceding styles; In development mode, these are ignored. | +| `register_vendor_script( string $_handle, array $_dependencies = [] )` | Register a Webpack generated vendor script, via entry point handle and any preceding scripts | ### WordPress Enqueue Registry @@ -61,11 +62,13 @@ Coordinates enqueuing assets within WordPress with the Front-end and Block Edito The main WordPress registration class is located at `Kanopi\Assets\Registry\WordPress`. -| Class Method | Description | -| --- | --- | -| `__construct( LoaderConfiguration $_configuration )` | Configures the asset manifest and versioning features | -| `register_block_editor_scripts( callback $_closure )` | Wrapper to enqueue scripts and styles for the Block Editor | -| `register_frontend_scripts( callback $_closure )` | Wrapper to enqueue scripts and styles on the site front-end | +| Class Method | Description | +|-------------------------------------------------------|----------------------------------------------------------------------------------------------| +| `__construct( LoaderConfiguration $_configuration )` | Configures the asset manifest and versioning features | +| `register_block_editor_scripts( callback $_closure )` | Wrapper to enqueue scripts and styles for the Block Editor | +| `register_frontend_scripts( callback $_closure )` | Wrapper to enqueue scripts and styles on the site front-end | +| `update_block_editor_priority` | Configure the WordPress action priority of the loaded scripts and styles in the Block Editor | +| `update_frontend_priority` | Configure the WordPress action priority of the loaded scripts and styles on the front-end | ## WordPress Configuration Note @@ -148,7 +151,7 @@ module.exports = { "cssOutputPath": "css/[name].css", "entryPoints": { "song": "./assets/src/scss/song/index.scss", - "song-app": "./assets/src/js/song/index.js" + "song-app": "./assets/src/js/song/index.js", "theme": "./assets/src/scss/theme/index.scss", "theme-app": "./assets/src/js/theme/index.js" }, @@ -216,11 +219,11 @@ module.exports = { "cssOutputPath": "css/[name].css", "entryPoints": { "block-editor": "./assets/src/js/block-editor/index.ts", - "block-theme": "./assets/src/scss/block-editor/index.scss" + "block-theme": "./assets/src/scss/block-editor/index.scss", "song-listing": "./assets/src/scss/song-listing/index.scss", - "song-listing-app": "./assets/src/js/song-listing/index.js" + "song-listing-app": "./assets/src/js/song-listing/index.js", "testimonial": "./assets/src/scss/testimonial/index.scss", - "testimonial-app": "./assets/src/js/testimonial/index.js" + "testimonial-app": "./assets/src/js/testimonial/index.js", "theme": "./assets/src/scss/theme/index.scss", "theme-app": "./assets/src/js/theme/index.js" }, diff --git a/src/AssetLoader.php b/src/AssetLoader.php index f2547c7..a35e0a0 100644 --- a/src/AssetLoader.php +++ b/src/AssetLoader.php @@ -295,6 +295,11 @@ protected function enqueue_chained_style( ? array_merge( $_dependencies, [ $_last_chain ] ) : $_dependencies; + /** + * Styles are registered as Scripts in Dev Mode + * - Dependencies are invalid there, as style dependencies are invalid for scripts + * - This should be ok, as scripts are loaded in the footer + */ if ( $this->use_production ) { wp_register_style( $handle, @@ -304,12 +309,13 @@ protected function enqueue_chained_style( wp_enqueue_style( $handle ); } else { + // Dependencies deliberately disabled in Dev Mode as scripts cannot depend on styles wp_enqueue_script( $handle, $this->build_entry_url( $_base, $this->_configuration->script_path(), $_entry, 'js' ), - $dependencies, + [], $this->_configuration->version(), - true + $this->_configuration->development_styles_in_head() ); } diff --git a/src/Model/LoaderConfiguration.php b/src/Model/LoaderConfiguration.php index ff5539f..f465548 100644 --- a/src/Model/LoaderConfiguration.php +++ b/src/Model/LoaderConfiguration.php @@ -7,11 +7,8 @@ class LoaderConfiguration { const DEFAULT_SCRIPT_PATH = '/assets/dist/js/'; - const DEFAULT_STATIC_PATH = '/assets/dist/static/'; - const DEFAULT_STYLE_PATH = '/assets/dist/css/'; - const DEFAULT_PREFIX = 'kanopi-pack-'; /** @@ -19,24 +16,31 @@ class LoaderConfiguration { */ protected string $_asset_manifest_path; + /** + * Whether to place all Development stylesheets (that are loaded as scripts) in the document head + * + * @var bool + */ + protected bool $_development_styles_in_head; + /** * @var string */ protected string $_handle_prefix; - /** - * @var array - */ - protected array $_production_domains; + /** + * @var array + */ + protected array $_production_domains; - /** + /** * Root file path to the files in production, to find the asset manifest * * @var string */ protected string $_production_file_path; - /** + /** * @var string */ protected string $_script_path; @@ -51,50 +55,53 @@ class LoaderConfiguration { */ protected string $_style_path; - /** + /** * @var string */ protected string $_version; - /** + /** * LoaderConfiguration constructor. * - * @param ?string $_version - * @param ?array $_production_domains + * @param ?string $_version + * @param ?array $_production_domains * @param ?string $_asset_manifest_path * @param ?string $_handle_prefix * @param ?string $_script_path * @param ?string $_style_path * @param ?string $_static_path * @param ?string $_production_file_path + * @param ?bool $_development_styles_in_head */ public function __construct( - ?string $_version = null, - ?array $_production_domains = null, + ?string $_version = null, + ?array $_production_domains = null, ?string $_asset_manifest_path = null, ?string $_handle_prefix = null, ?string $_script_path = null, ?string $_style_path = null, ?string $_static_path = null, - ?string $_production_file_path = null + ?string $_production_file_path = null, + ?bool $_development_styles_in_head = false ) { - $this->_version = $this->check_string( $_version, null ); - $this->_asset_manifest_path = $this->check_string( $_asset_manifest_path, '' ); - $this->_handle_prefix = $this->check_string( $_handle_prefix, self::DEFAULT_PREFIX ); - $this->_script_path = $this->check_string( $_script_path, self::DEFAULT_SCRIPT_PATH ); - $this->_static_path = $this->check_string( $_static_path, self::DEFAULT_STATIC_PATH ); - $this->_style_path = $this->check_string( $_style_path, self::DEFAULT_STYLE_PATH ); - $this->_production_domains = is_array( $_production_domains ) ? $_production_domains : []; - $this->_production_file_path = $this->check_string( $_production_file_path, '' ); + $this->_version = $this->check_string( $_version, null ); + $this->_asset_manifest_path = $this->check_string( $_asset_manifest_path, '' ); + $this->_handle_prefix = $this->check_string( $_handle_prefix, self::DEFAULT_PREFIX ); + $this->_script_path = $this->check_string( $_script_path, self::DEFAULT_SCRIPT_PATH ); + $this->_static_path = $this->check_string( $_static_path, self::DEFAULT_STATIC_PATH ); + $this->_style_path = $this->check_string( $_style_path, self::DEFAULT_STYLE_PATH ); + $this->_production_domains = is_array( $_production_domains ) ? $_production_domains : []; + $this->_production_file_path = $this->check_string( $_production_file_path, '' ); + $this->_development_styles_in_head = $_development_styles_in_head; } - /** + /** * @param ?string $_entry * @param ?string $_default * * @return ?string */ - protected function check_string( ?string $_entry, ?string $_default ) : ?string { + protected function check_string( ?string $_entry, ?string $_default ): ?string { return empty( trim( $_entry ?? '' ) ) ? $_default : trim( $_entry ); } @@ -103,56 +110,65 @@ protected function check_string( ?string $_entry, ?string $_default ) : ?string * * @return string */ - public function asset_manifest_path() : string { + public function asset_manifest_path(): string { return $this->_asset_manifest_path; } - /** - * Prefix to namespace, placed before all enqueued asset handles - */ - public function handle_prefix() : string { - return $this->_handle_prefix; - } - - /** - * Set of production domains to prevent development mode - */ - public function production_domains() : array { - return $this->_production_domains; - } - - /** - * Url path fragment before scripts - */ - public function production_file_path() : string { - return $this->_production_file_path; - } - - /** - * Url path fragment before scripts - */ - public function script_path() : string { - return $this->_script_path; - } - - /** - * Url path fragment before static files - */ - public function static_path() : string { - return $this->_static_path; - } - - /** - * Url path fragment before stylesheets - */ - public function style_path() : string { - return $this->_style_path; - } - - /** - * Asset version passed to enqueue calls - */ - public function version() : string { - return $this->_version; - } + /** + * Whether to place all Development stylesheets (that are loaded as scripts) in the document head + * + * @return bool + */ + public function development_styles_in_head(): bool { + return $this->_development_styles_in_head; + } + + /** + * Prefix to namespace, placed before all enqueued asset handles + */ + public function handle_prefix(): string { + return $this->_handle_prefix; + } + + /** + * Set of production domains to prevent development mode + */ + public function production_domains(): array { + return $this->_production_domains; + } + + /** + * Url path fragment before scripts + */ + public function production_file_path(): string { + return $this->_production_file_path; + } + + /** + * Url path fragment before scripts + */ + public function script_path(): string { + return $this->_script_path; + } + + /** + * Url path fragment before static files + */ + public function static_path(): string { + return $this->_static_path; + } + + /** + * Url path fragment before stylesheets + */ + public function style_path(): string { + return $this->_style_path; + } + + /** + * Asset version passed to enqueue calls + */ + public function version(): string { + return $this->_version; + } } diff --git a/src/Registry/WordPress.php b/src/Registry/WordPress.php index 10a1495..c497a64 100644 --- a/src/Registry/WordPress.php +++ b/src/Registry/WordPress.php @@ -34,6 +34,20 @@ class WordPress { */ protected string $_production_url; + /** + * Priority used by the enqueue_block_editor_assets back-end hook, default of 10 + * + * @var int + */ + protected int $priority_block_editor = 10; + + /** + * Priority used by the wp_enqueue_scripts front-end hook, default of 10 + * + * @var int + */ + protected int $priority_frontend = 10; + /** * Asset Loader instance for this instance */ @@ -174,10 +188,13 @@ public function static_asset_url( string $_file_path ): string { * @param callable $_script_registration Callable function passed the current WordPress instance */ public function register_frontend_scripts( callable $_script_registration ) { - add_action( 'wp_enqueue_scripts', + add_action( + 'wp_enqueue_scripts', function () use ( $_script_registration ) { call_user_func_array( $_script_registration, [ $this ] ); - } ); + }, + $this->priority_frontend + ); } /** @@ -186,10 +203,38 @@ function () use ( $_script_registration ) { * @param callable $_script_registration Callable function passed the current WordPress instance */ public function register_block_editor_scripts( callable $_script_registration ) { - add_action( 'enqueue_block_editor_assets', + add_action( + 'enqueue_block_editor_assets', function () use ( $_script_registration ) { call_user_func_array( $_script_registration, [ $this ] ); - } + }, + $this->priority_block_editor ); } + + /** + * Sets the priority (positive, non-zero integer, sets 10 if invalid) for block editor script registration + * + * @param int $_priority Positive, non-zero integer priority + * + * @return WordPress Updated instance + */ + public function update_block_editor_priority( int $_priority ): WordPress { + $this->priority_block_editor = 0 < $_priority ? $_priority : 10; + + return $this; + } + + /** + * Sets the priority (positive, non-zero integer, sets 10 if invalid) for front-end script registration + * + * @param int $_priority Positive, non-zero integer priority + * + * @return WordPress Updated instance + */ + public function update_frontend_priority( int $_priority ): WordPress { + $this->priority_frontend = 0 < $_priority ? $_priority : 10; + + return $this; + } }