diff --git a/projects/packages/forms/.gitattributes b/projects/packages/forms/.gitattributes index eb51200b5c29..1e0f7ea5c730 100644 --- a/projects/packages/forms/.gitattributes +++ b/projects/packages/forms/.gitattributes @@ -9,7 +9,11 @@ package.json export-ignore /src/**/*.min.css production-include /src/**/*.rtl.css production-include dist/** production-include -build/** production-include +/build/**/*.php production-include +/build/**/*.min.js production-include +/build/**/loader.js production-include +/build/**/*.min.css production-include +/build/**/*.rtl.css production-include # Files to exclude from the mirror repo, but included in the monorepo. # Remember to end all directories with `/**` to properly tag every file. @@ -27,3 +31,4 @@ tools/** production-exclude tsconfig.json production-exclude declarations.d.ts production-exclude global.d.ts production-exclude +/routes/** production-exclude diff --git a/projects/packages/forms/changelog/update-forms-assets b/projects/packages/forms/changelog/update-forms-assets new file mode 100644 index 000000000000..2678e320af86 --- /dev/null +++ b/projects/packages/forms/changelog/update-forms-assets @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Exclude unminified build artifacts from production distribution to reduce plugin size. diff --git a/projects/packages/forms/src/dashboard/class-dashboard.php b/projects/packages/forms/src/dashboard/class-dashboard.php index 0c25663f6656..5daad2882bf9 100644 --- a/projects/packages/forms/src/dashboard/class-dashboard.php +++ b/projects/packages/forms/src/dashboard/class-dashboard.php @@ -30,6 +30,10 @@ public static function load_wp_build() { $wp_build_index = dirname( __DIR__, 2 ) . '/build/build.php'; if ( file_exists( $wp_build_index ) ) { + // Predefine registration functions with file_exists fallback for + // SCRIPT_DEBUG, so production distributions that exclude unminified + // JS files don't break when SCRIPT_DEBUG is enabled. + require_once __DIR__ . '/wp-build-script-debug-fallback.php'; require_once $wp_build_index; // Re-add core's registration only when Gutenberg isn't providing it diff --git a/projects/packages/forms/src/dashboard/wp-build-script-debug-fallback.php b/projects/packages/forms/src/dashboard/wp-build-script-debug-fallback.php new file mode 100644 index 000000000000..53476fa06025 --- /dev/null +++ b/projects/packages/forms/src/dashboard/wp-build-script-debug-fallback.php @@ -0,0 +1,277 @@ +query( $handle, 'registered' ); + if ( $script ) { + $script->src = $src; + $script->deps = $deps; + $script->ver = $ver; + $script->args = $in_footer ? 1 : null; + } else { + $scripts->add( $handle, $src, $deps, $ver, ( $in_footer ? 1 : null ) ); + } + + if ( in_array( 'wp-i18n', $deps, true ) ) { + $scripts->set_translations( $handle ); + } + } +} + +if ( ! function_exists( 'jetpack_forms_register_package_scripts' ) ) { + /** + * Register all package scripts with SCRIPT_DEBUG file_exists fallback. + * + * @param WP_Scripts $scripts WP_Scripts instance. + */ + function jetpack_forms_register_package_scripts( $scripts ) { + $build_dir = dirname( __DIR__, 2 ) . '/build'; + $build_constants = require $build_dir . '/constants.php'; + + $scripts_dir = $build_dir . '/scripts'; + $scripts_file = $scripts_dir . '/registry.php'; + + if ( ! file_exists( $scripts_file ) ) { + return; + } + + $scripts_data = require $scripts_file; + + $extension = '.min.js'; + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { + if ( ! empty( $scripts_data[0]['path'] ) ) { + $unminified_path = $scripts_dir . '/' . $scripts_data[0]['path'] . '.js'; + if ( file_exists( $unminified_path ) ) { + $extension = '.js'; + } + } + } + + $default_version = ( $extension === '.js' ) ? time() : $build_constants['version']; + + foreach ( $scripts_data as $script_data ) { + $asset_file = $scripts_dir . '/' . $script_data['asset']; + $asset = file_exists( $asset_file ) ? require $asset_file : array(); + $dependencies = $asset['dependencies'] ?? array(); + $version = $asset['version'] ?? $default_version; + + jetpack_forms_override_script( + $scripts, + $script_data['handle'], + $build_constants['build_url'] . 'scripts/' . $script_data['path'] . $extension, + $dependencies, + $version, + true + ); + } + } + + add_action( 'wp_default_scripts', 'jetpack_forms_register_package_scripts' ); +} + +if ( ! function_exists( 'jetpack_forms_register_script_modules' ) ) { + /** + * Register all script modules with SCRIPT_DEBUG file_exists fallback. + */ + function jetpack_forms_register_script_modules() { + $build_dir = dirname( __DIR__, 2 ) . '/build'; + $build_constants = require $build_dir . '/constants.php'; + $modules_dir = $build_dir . '/modules'; + $modules_file = $modules_dir . '/registry.php'; + + if ( ! file_exists( $modules_file ) ) { + return; + } + + $modules = require $modules_file; + $base_url = $build_constants['build_url'] . 'modules/'; + $extension = '.min.js'; + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { + if ( ! empty( $modules[0]['path'] ) ) { + $unminified_path = $modules_dir . '/' . $modules[0]['path'] . '.js'; + if ( file_exists( $unminified_path ) ) { + $extension = '.js'; + } + } + } + + foreach ( $modules as $module ) { + $asset_path = $modules_dir . '/' . $module['asset']; + $asset = file_exists( $asset_path ) ? require $asset_path : array(); + + wp_register_script_module( + $module['id'], + $base_url . $module['path'] . $extension, + $asset['module_dependencies'] ?? array(), + $asset['version'] ?? false + ); + } + } + + add_action( 'wp_default_scripts', 'jetpack_forms_register_script_modules' ); +} + +if ( ! function_exists( 'jetpack_forms_register_page_routes' ) ) { + /** + * Register routes for a page with SCRIPT_DEBUG file_exists fallback. + * + * @param array $page_routes Array of route data for the page. + * @param string $register_function_name Name of the function to call for registering each route. + */ + function jetpack_forms_register_page_routes( $page_routes, $register_function_name ) { + $build_dir = dirname( __DIR__, 2 ) . '/build'; + $build_constants = require $build_dir . '/constants.php'; + + foreach ( $page_routes as $route ) { + $content_handle = null; + $route_handle = null; + + // Register content module if exists. + if ( $route['has_content'] ) { + $content_asset_path = $build_dir . "/routes/{$route['name']}/content.min.asset.php"; + if ( file_exists( $content_asset_path ) ) { + $content_asset = require $content_asset_path; + $content_handle = 'jetpack-forms/routes/' . $route['name'] . '/content'; + $extension = '.min.js'; + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { + $content_unminified = $build_dir . '/routes/' . $route['name'] . '/content.js'; + if ( file_exists( $content_unminified ) ) { + $extension = '.js'; + } + } + wp_register_script_module( + $content_handle, + $build_constants['build_url'] . 'routes/' . $route['name'] . '/content' . $extension, + $content_asset['module_dependencies'] ?? array(), + $content_asset['version'] ?? false + ); + } + } + + // Register route module if exists. + if ( $route['has_route'] ) { + $route_asset_path = $build_dir . "/routes/{$route['name']}/route.min.asset.php"; + if ( file_exists( $route_asset_path ) ) { + $route_asset = require $route_asset_path; + $route_handle = 'jetpack-forms/routes/' . $route['name'] . '/route'; + $extension = '.min.js'; + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { + $route_unminified = $build_dir . '/routes/' . $route['name'] . '/route.js'; + if ( file_exists( $route_unminified ) ) { + $extension = '.js'; + } + } + wp_register_script_module( + $route_handle, + $build_constants['build_url'] . 'routes/' . $route['name'] . '/route' . $extension, + $route_asset['module_dependencies'] ?? array(), + $route_asset['version'] ?? false + ); + } + } + + // Register route with page. + if ( function_exists( $register_function_name ) ) { + call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle ); + } + } + } +} + +if ( ! function_exists( 'jetpack_forms_override_style' ) ) { + /** + * Registers a style, deregistering any existing style by the same handle first. + * + * @param WP_Styles $styles WP_Styles instance. + * @param string $handle Stylesheet handle. + * @param string $src Full URL or root-relative path. + * @param array $deps Dependencies. + * @param string|bool|null $ver Version string. + * @param string $media Media type. + */ + function jetpack_forms_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { + $style = $styles->query( $handle, 'registered' ); + if ( $style ) { + $styles->remove( $handle ); + } + $styles->add( $handle, $src, $deps, $ver, $media ); + } +} + +if ( ! function_exists( 'jetpack_forms_register_package_styles' ) ) { + /** + * Register all package styles with SCRIPT_DEBUG file_exists fallback. + * + * @param WP_Styles $styles WP_Styles instance. + */ + function jetpack_forms_register_package_styles( $styles ) { + $build_dir = dirname( __DIR__, 2 ) . '/build'; + $build_constants = require $build_dir . '/constants.php'; + + $styles_dir = $build_dir . '/styles'; + $styles_file = $styles_dir . '/registry.php'; + + if ( ! file_exists( $styles_file ) ) { + return; + } + + $styles_data = require $styles_file; + + $suffix = '.min'; + if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { + if ( ! empty( $styles_data[0]['path'] ) ) { + $unminified_path = $styles_dir . '/' . $styles_data[0]['path'] . '.css'; + if ( file_exists( $unminified_path ) ) { + $suffix = ''; + } + } + } + + $default_version = ( $suffix === '' ) ? time() : $build_constants['version']; + + foreach ( $styles_data as $style_data ) { + jetpack_forms_override_style( + $styles, + $style_data['handle'], + $build_constants['build_url'] . 'styles/' . $style_data['path'] . $suffix . '.css', + $style_data['dependencies'], + $default_version + ); + + // Enable RTL support. + $styles->add_data( $style_data['handle'], 'rtl', 'replace' ); + $styles->add_data( $style_data['handle'], 'suffix', $suffix ); + } + } + + add_action( 'wp_default_styles', 'jetpack_forms_register_package_styles' ); +} diff --git a/projects/packages/forms/tests/php/dashboard/Dashboard_Test.php b/projects/packages/forms/tests/php/dashboard/Dashboard_Test.php index d1d740927164..00ebc56e143a 100644 --- a/projects/packages/forms/tests/php/dashboard/Dashboard_Test.php +++ b/projects/packages/forms/tests/php/dashboard/Dashboard_Test.php @@ -188,4 +188,56 @@ public function test_get_forms_admin_url_wp_build_with_screen_id_equivalents() { remove_filter( 'jetpack_forms_alpha', '__return_true' ); } + + /** + * Test load_wp_build does nothing when page parameter is wrong. + */ + public function test_load_wp_build_wrong_page() { + $_GET['page'] = 'some-other-page'; + + Dashboard::load_wp_build(); + + $this->assertFalse( + has_action( 'wp_default_scripts', 'jetpack_forms_register_package_scripts' ), + 'Fallback file should not be loaded for wrong page.' + ); + + unset( $_GET['page'] ); + } + + /** + * Test load_wp_build does nothing when page parameter is missing. + */ + public function test_load_wp_build_no_page() { + unset( $_GET['page'] ); + + Dashboard::load_wp_build(); + + $this->assertFalse( + has_action( 'wp_default_scripts', 'jetpack_forms_register_package_scripts' ), + 'Fallback file should not be loaded when page is missing.' + ); + } + + /** + * Test load_wp_build does nothing when build.php does not exist. + */ + public function test_load_wp_build_correct_page_missing_build() { + $ref = new \ReflectionClass( Dashboard::class ); + $build_php = dirname( $ref->getFileName(), 3 ) . '/build/build.php'; + if ( file_exists( $build_php ) ) { + $this->markTestSkipped( 'build/build.php exists in this environment.' ); + } + + $_GET['page'] = Dashboard::FORMS_WPBUILD_ADMIN_SLUG; + + Dashboard::load_wp_build(); + + $this->assertFalse( + has_action( 'wp_default_scripts', 'jetpack_forms_register_package_scripts' ), + 'Fallback file should not be loaded when build.php is missing.' + ); + + unset( $_GET['page'] ); + } }