Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
34.21% covered (danger)
34.21%
13 / 38
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_WP_Build_Page
33.33% covered (danger)
33.33%
12 / 36
25.00% covered (danger)
25.00%
1 / 4
102.63
0.00% covered (danger)
0.00%
0 / 1
 load
12.50% covered (danger)
12.50%
3 / 24
0.00% covered (danger)
0.00%
0 / 1
63.26
 alias_screen_id
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 restore_screen_id
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 enqueue_i18n_loader
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Loads the Jetpack plugin's wp-build output for one admin page.
4 *
5 * @package automattic/jetpack
6 */
7
8use Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills;
9
10if ( ! defined( 'ABSPATH' ) ) {
11    exit( 0 );
12}
13
14/**
15 * Loads `build/build.php` for the admin page that renders a wp-build route.
16 *
17 * @since 16.3
18 */
19class Jetpack_WP_Build_Page {
20
21    /**
22     * The route page id the screen ID is aliased to.
23     *
24     * @var string|null
25     */
26    private static $page_id = null;
27
28    /**
29     * The screen ID alias_screen_id() replaced, until it is restored.
30     *
31     * @var string|null
32     */
33    private static $original_screen_id = null;
34
35    /**
36     * Load wp-build for one page. Call it at `admin_menu` priority 1, and only on that page's request.
37     *
38     * Scoped to the page so WP_Build_Polyfills does not replace core scripts everywhere else.
39     *
40     * @param string $page_id The route's page id. It must not be the menu slug: the generated
41     *                        standalone page.php intercepts `admin_init` for its own id and exits.
42     * @param bool   $alias   Whether to alias the screen ID. Pass false to ready the page's routes on another page, which must not boot it.
43     * @return bool Whether the build output exists and can render the page.
44     */
45    public static function load( $page_id, $alias = true ) {
46        $build_index = JETPACK__PLUGIN_DIR . 'build/build.php';
47        if ( ! file_exists( $build_index ) ) {
48            return false;
49        }
50
51        // Hooked on either side of the require, so the alias holds only for the generated
52        // enqueue check it registers at the same priority.
53        if ( $alias ) {
54            self::$page_id = $page_id;
55            add_action( 'admin_enqueue_scripts', array( __CLASS__, 'alias_screen_id' ) );
56        }
57        require_once $build_index;
58
59        // A stale or partial build can't render the page, so don't swap core's scripts for it.
60        $renders = function_exists( 'jetpack_plugin_' . str_replace( '-', '_', $page_id ) . '_wp_admin_render_page' );
61        if ( $alias && $renders ) {
62            add_action( 'admin_enqueue_scripts', array( __CLASS__, 'restore_screen_id' ) );
63        } elseif ( $alias ) {
64            remove_action( 'admin_enqueue_scripts', array( __CLASS__, 'alias_screen_id' ) );
65            self::$page_id = null;
66        }
67        if ( ! $renders ) {
68            return false;
69        }
70        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_i18n_loader' ) );
71
72        // wp-build hooks module registration to wp_default_scripts, which has already fired by
73        // admin_menu — call it directly or the init module never reaches the import map.
74        if ( function_exists( 'jetpack_plugin_register_script_modules' ) ) {
75            jetpack_plugin_register_script_modules(); // @phan-suppress-current-line PhanUndeclaredFunction -- Checked with function_exists(); defined in the generated build/modules.php, which Phan excludes.
76        }
77
78        if ( class_exists( WP_Build_Polyfills::class ) ) {
79            WP_Build_Polyfills::register(
80                'jetpack',
81                array_merge( WP_Build_Polyfills::SCRIPT_HANDLES, WP_Build_Polyfills::MODULE_IDS )
82            );
83        }
84
85        return true;
86    }
87
88    /**
89     * Point the screen ID at the wp-build page while its generated enqueue check runs.
90     *
91     * @return void
92     */
93    public static function alias_screen_id() {
94        $screen = get_current_screen();
95        if ( ! $screen || null === self::$page_id ) {
96            return;
97        }
98
99        self::$original_screen_id = $screen->id;
100        $screen->id               = self::$page_id;
101    }
102
103    /**
104     * Undo alias_screen_id(), since JITM builds its message path from the screen ID.
105     *
106     * @return void
107     */
108    public static function restore_screen_id() {
109        $screen = get_current_screen();
110        if ( ! $screen || null === self::$original_screen_id ) {
111            return;
112        }
113
114        $screen->id               = self::$original_screen_id;
115        self::$original_screen_id = null;
116    }
117
118    /**
119     * Enqueue the JS translation loader the route bundles need.
120     *
121     * Registered on every admin page but only enqueued when depended on, and the esbuild
122     * bundles don't pull it in — without it every string renders untranslated.
123     *
124     * @return void
125     */
126    public static function enqueue_i18n_loader() {
127        if ( wp_script_is( 'wp-jp-i18n-loader', 'registered' ) ) {
128            wp_enqueue_script( 'wp-jp-i18n-loader' );
129        }
130    }
131}