Skip to content

Commit

Permalink
assets: Provide the wp-jp-i18n-state script
Browse files Browse the repository at this point in the history
This script is the one expected by the default configuration of the new
`@automattic/i18n-loader-webpack-plugin`.

Note the `domainMap` is currently empty. Code to fill that in will come
later, as part of #21690, once we figure out the exact form of it.
  • Loading branch information
anomiex committed Dec 2, 2021
1 parent bd36756 commit 8d4343e
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 5 deletions.
19 changes: 19 additions & 0 deletions projects/packages/assets/actions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
/**
* Action Hooks for Jetpack Assets module.
*
* @package automattic/jetpack-assets
*/

// If WordPress's plugin API is available already, use it. If not,
// drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`.
if ( function_exists( 'add_action' ) ) {
add_action( 'wp_default_scripts', array( Automattic\Jetpack\Assets::class, 'wp_default_scripts_hook' ) );
} else {
global $wp_filter;
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_filter['wp_default_scripts'][10][] = array(
'accepted_args' => 1,
'function' => array( Automattic\Jetpack\Assets::class, 'wp_default_scripts_hook' ),
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Generate `wp-jp-i18n-state` script.
3 changes: 3 additions & 0 deletions projects/packages/assets/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"automattic/jetpack-changelogger": "^3.0"
},
"autoload": {
"files": [
"actions.php"
],
"classmap": [
"src/"
]
Expand Down
55 changes: 55 additions & 0 deletions projects/packages/assets/src/class-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,4 +403,59 @@ public static function enqueue_script( $handle ) {
}
}

/**
* 'wp_default_scripts' action handler.
*
* This registers the `wp-jp-i18n-state` script for use by Webpack bundles built with
* `@automattic/i18n-loader-webpack-plugin`.
*
* @since $$next-version$$
* @param \WP_Scripts $wp_scripts WP_Scripts instance.
*/
public static function wp_default_scripts_hook( $wp_scripts ) {
$data = array(
'baseUrl' => false,
'locale' => determine_locale(),
'domainMap' => array(),
);

$lang_dir = Jetpack_Constants::get_constant( 'WP_LANG_DIR' );
$abspath = Jetpack_Constants::get_constant( 'ABSPATH' );

if ( strpos( $lang_dir, $abspath ) === 0 ) {
$data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) );
}

/**
* Filters the i18n state data for use by Webpack bundles built with
* `@automattic/i18n-loader-webpack-plugin`.
*
* @since $$next-version$$
* @package assets
* @param array $data The state data to generate. Expected fields are:
* - `baseUrl`: (string|false) The URL to the languages directory. False if no URL could be determined.
* - `locale`: (string) The locale for the page.
* - `domainMap`: (string[]) A mapping from Composer package textdomains to the corresponding
* `plugins/textdomain` or `themes/textdomain` (or core `textdomain`, but that's unlikely).
*/
$data = apply_filters( 'jetpack_i18n_state', $data );

if ( ! is_array( $data ) ||
! isset( $data['baseUrl'] ) || ! ( is_string( $data['baseUrl'] ) || false === $data['baseUrl'] ) ||
! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] )
) {
$js = 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );';
} elseif ( ! $data['baseUrl'] ) {
$js = 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );';
} else {
$data['domainMap'] = (object) $data['domainMap']; // Ensure it becomes a json object.
$js = 'wp.jpI18nState = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';';
}

// Depend on wp-i18n to ensure global `wp` exists and because anything needing this will need that too.
$wp_scripts->add( 'wp-jp-i18n-state', null, array( 'wp-i18n' ) );
$wp_scripts->add_inline_script( 'wp-jp-i18n-state', $js, 'before' );
}

}
162 changes: 157 additions & 5 deletions projects/packages/assets/tests/php/test-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,21 @@ public function set_up() {

Functions\stubs(
array(
'wp_parse_url' => 'parse_url',
'__' => function ( $text ) {
'wp_parse_url' => 'parse_url',
'wp_json_encode' => 'json_encode',
'__' => function ( $text ) {
return $text;
},
'esc_html' => function ( $text ) {
'esc_html' => function ( $text ) {
return htmlspecialchars( $text, ENT_QUOTES );
},
'add_query_arg' => function ( ...$args ) {
'add_query_arg' => function ( ...$args ) {
$this->assertCount( 3, $args );
list( $k, $v, $url ) = $args;
$url .= ( strpos( $url, '?' ) === false ? '?' : '&' ) . "$k=$v";
return $url;
},
'plugins_url' => function ( $path, $plugin_path ) use ( $plugin_dir ) {
'plugins_url' => function ( $path, $plugin_path ) use ( $plugin_dir ) {
$plugin_path = dirname( $plugin_path );
$this->stringStartsWith( $plugin_dir, $plugin_path );
return 'http://www.example.com/wp-content/plugins/jetpack/' . substr( $plugin_path, strlen( $plugin_dir ) ) . '/' . $path;
Expand All @@ -61,6 +62,7 @@ public function set_up() {
*/
public function tear_down() {
Monkey\tearDown();
Jetpack_Constants::clear_constants();

// Clear the instance.
$rc = new \ReflectionClass( Assets::class );
Expand Down Expand Up @@ -622,4 +624,154 @@ public static function provide_register_script() {
),
);
}

/**
* Test wp_default_scripts_hook.
*
* @dataProvider provide_wp_default_scripts_hook
* @param array $expect_filter Expected filter.
* @param string $expect_js Expected JS.
* @param array $options Options for the test.
*/
public function test_wp_default_scripts_hook( $expect_filter, $expect_js, $options = array() ) {
$options += array(
'constants' => array(),
'locale' => 'en_US',
);

$constants = $options['constants'] + array(
'ABSPATH' => '/path/to/wordpress/',
'WP_LANG_DIR' => '/path/to/wordpress/wp-content/languages',
);
foreach ( $constants as $k => $v ) {
Jetpack_Constants::set_constant( $k, $v );
}

Functions\expect( 'determine_locale' )->andReturn( $options['locale'] );
Functions\expect( 'site_url' )->andReturnUsing(
function ( $v ) {
return "http://example.com$v";
}
);

$obj = Filters\expectApplied( 'jetpack_i18n_state' )->once()->with( $expect_filter );
if ( array_key_exists( 'filter', $options ) ) {
$obj->andReturn( $options['filter'] );
}

$mock = $this->getMockBuilder( stdClass::class )
->setMethods( array( 'add', 'add_inline_script' ) )
->getMock();
$mock->expects( $this->once() )->method( 'add' )
->with( 'wp-jp-i18n-state', null, array( 'wp-i18n' ) );
$mock->expects( $this->once() )->method( 'add_inline_script' )
->with( 'wp-jp-i18n-state', $expect_js, 'before' );

Assets::wp_default_scripts_hook( $mock );
}

/** Data provider for test_wp_default_scripts_hook. */
public function provide_wp_default_scripts_hook() {
$expect_filter = array(
'baseUrl' => 'http://example.com/wp-content/languages/',
'locale' => 'en_US',
'domainMap' => array(),
);

return array(
'Basic test' => array(
$expect_filter,
'wp.jpI18nState = {"baseUrl":"http://example.com/wp-content/languages/","locale":"en_US","domainMap":{}};',
),
'Basic test (2)' => array(
array(
'baseUrl' => 'http://example.com/wp-includes/languages/',
'locale' => 'de_DE',
'domainMap' => array(),
),
'wp.jpI18nState = {"baseUrl":"http://example.com/wp-includes/languages/","locale":"de_DE","domainMap":{}};',
array(
'constants' => array( 'WP_LANG_DIR' => '/path/to/wordpress/wp-includes/languages' ),
'locale' => 'de_DE',
),
),
'Bad WP_LANG_DIR' => array(
array( 'baseUrl' => false ) + $expect_filter,
'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );',
array(
'constants' => array( 'WP_LANG_DIR' => '/not/path/to/wordpress/wp-content/languages' ),
),
),
'Filter' => array(
array( 'baseUrl' => false ) + $expect_filter,
'wp.jpI18nState = {"baseUrl":"http://example.org/languages/","locale":"klingon","domainMap":{"foo":"plugins/bar"}};',
array(
'constants' => array( 'WP_LANG_DIR' => '/not/path/to/wordpress/wp-content/languages' ),
'filter' => array(
'baseUrl' => 'http://example.org/languages/',
'locale' => 'klingon',
'domainMap' => array( 'foo' => 'plugins/bar' ),
),
),
),
'Bad filter: not array' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array( 'filter' => null ),
),
'Bad filter: baseUrl is not set' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array(
'filter' => array(
'locale' => 'en_US',
'domainMap' => array(),
),
),
),
'Bad filter: locale is not set' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array(
'filter' => array(
'baseUrl' => 'http://example.com/wp-content/languages/',
'domainMap' => array(),
),
),
),
'Bad filter: locale is bad' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array(
'filter' => array(
'baseUrl' => 'http://example.com/wp-content/languages/',
'locale' => false,
'domainMap' => array(),
),
),
),
'Bad filter: domainMap is not set' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array(
'filter' => array(
'baseUrl' => 'http://example.com/wp-content/languages/',
'locale' => 'en_US',
),
),
),
'Bad filter: domainMap is bad' => array(
$expect_filter,
'console.warn( "I18n state deleted by jetpack_i18n_state hook" );',
array(
'filter' => array(
'baseUrl' => 'http://example.com/wp-content/languages/',
'locale' => 'en_US',
'domainMap' => (object) array(),
),
),
),
);
}

}

0 comments on commit 8d4343e

Please sign in to comment.