next_tag( 'span' );
- $p->set_attribute( 'id', 'first' );
- $p->next_tag( 'span' );
- $p->set_attribute( 'id', 'second' );
+ $processor = new WP_HTML_Tag_Processor( self::HTML_MALFORMED );
+ $processor->next_tag( 'span' );
+ $processor->set_attribute( 'id', 'first' );
+ $processor->next_tag( 'span' );
+ $processor->set_attribute( 'id', 'second' );
$this->assertSame(
'
Back to notifications
',
- $p->get_updated_html(),
+ $processor->get_updated_html(),
'Did not add id attributes properly to malformed input'
);
}
@@ -2466,16 +2466,16 @@ public function test_updating_specific_attributes_in_malformed_html() {
* @param string $expected Expected updated HTML.
*/
public function test_updating_attributes( $html, $expected ) {
- $p = new WP_HTML_Tag_Processor( $html );
- $p->next_tag();
- $p->set_attribute( 'foo', 'bar' );
- $p->add_class( 'firstTag' );
- $p->next_tag();
- $p->add_class( 'secondTag' );
+ $processor = new WP_HTML_Tag_Processor( $html );
+ $processor->next_tag();
+ $processor->set_attribute( 'foo', 'bar' );
+ $processor->add_class( 'firstTag' );
+ $processor->next_tag();
+ $processor->add_class( 'secondTag' );
$this->assertSame(
$expected,
- $p->get_updated_html(),
+ $processor->get_updated_html(),
'Did not properly add attributes and class names'
);
}
@@ -2485,7 +2485,7 @@ public function test_updating_attributes( $html, $expected ) {
*
* @return array[]
*/
- public function data_updating_attributes() {
+ public static function data_updating_attributes() {
return array(
'tags inside of a comment' => array(
'input' => '
test',
@@ -2530,16 +2530,16 @@ public function data_updating_attributes() {
* @param string $expected Expected updated HTML.
*/
public function test_updating_attributes_in_malformed_html( $html, $expected ) {
- $p = new WP_HTML_Tag_Processor( $html );
- $this->assertTrue( $p->next_tag(), 'Could not find first tag.' );
- $p->set_attribute( 'foo', 'bar' );
- $p->add_class( 'firstTag' );
- $p->next_tag();
- $p->add_class( 'secondTag' );
+ $processor = new WP_HTML_Tag_Processor( $html );
+ $this->assertTrue( $processor->next_tag(), 'Could not find first tag.' );
+ $processor->set_attribute( 'foo', 'bar' );
+ $processor->add_class( 'firstTag' );
+ $processor->next_tag();
+ $processor->add_class( 'secondTag' );
$this->assertSame(
$expected,
- $p->get_updated_html(),
+ $processor->get_updated_html(),
'Did not properly update attributes and classnames given malformed input'
);
}
@@ -2549,7 +2549,7 @@ public function test_updating_attributes_in_malformed_html( $html, $expected ) {
*
* @return array[]
*/
- public function data_updating_attributes_in_malformed_html() {
+ public static function data_updating_attributes_in_malformed_html() {
return array(
'Invalid entity inside attribute value' => array(
'input' => '
test',
@@ -2702,8 +2702,8 @@ public function data_updating_attributes_in_malformed_html() {
* @covers WP_HTML_Tag_Processor::next_tag
*/
public function test_handles_malformed_taglike_open_short_html() {
- $p = new WP_HTML_Tag_Processor( '<' );
- $result = $p->next_tag();
+ $processor = new WP_HTML_Tag_Processor( '<' );
+ $result = $processor->next_tag();
$this->assertFalse( $result, 'Did not handle "<" html properly.' );
}
@@ -2711,8 +2711,20 @@ public function test_handles_malformed_taglike_open_short_html() {
* @covers WP_HTML_Tag_Processor::next_tag
*/
public function test_handles_malformed_taglike_close_short_html() {
- $p = new WP_HTML_Tag_Processor( ' ' );
- $result = $p->next_tag();
+ $processor = new WP_HTML_Tag_Processor( ' ' );
+ $result = $processor->next_tag();
$this->assertFalse( $result, 'Did not handle " " html properly.' );
}
+
+ /**
+ * Ensures that non-tag syntax starting with `<` is consumed inside a text node.
+ *
+ * @ticket 60385
+ */
+ public function test_single_text_node_with_taglike_text() {
+ $processor = new WP_HTML_Tag_Processor( 'test< /A>' );
+ $processor->next_token();
+ $this->assertSame( '#text', $processor->get_token_type(), 'Did not find text node.' );
+ $this->assertSame( 'test< /A>', $processor->get_modifiable_text(), 'Did not find complete text node.' );
+ }
}
diff --git a/tests/phpunit/tests/http/http.php b/tests/phpunit/tests/http/http.php
index 1648cca4eaf9f..77a5239819c64 100644
--- a/tests/phpunit/tests/http/http.php
+++ b/tests/phpunit/tests/http/http.php
@@ -661,4 +661,56 @@ function ( $response, $parsed_args, $url ) use ( &$pre_http_request_filter_has_r
$this->assertSame( 'PASS', wp_remote_retrieve_body( $redirect_response ), 'Redirect response body is expected to be PASS.' );
$this->assertTrue( $pre_http_request_filter_has_run, 'The pre_http_request filter is expected to run.' );
}
+
+ /**
+ * Test that WP_Http::normalize_cookies method correctly casts integer keys to string.
+ * @ticket 58566
+ *
+ * @covers WP_Http::normalize_cookies
+ */
+ public function test_normalize_cookies_casts_integer_keys_to_string() {
+ $http = _wp_http_get_object();
+
+ $cookies = array(
+ '1' => 'foo',
+ 2 => 'bar',
+ 'qux' => 7,
+ );
+
+ $cookie_jar = $http->normalize_cookies( $cookies );
+
+ $this->assertInstanceOf( 'WpOrg\Requests\Cookie\Jar', $cookie_jar );
+
+ foreach ( array_keys( $cookies ) as $cookie ) {
+ if ( is_string( $cookie ) ) {
+ $this->assertInstanceOf( 'WpOrg\Requests\Cookie', $cookie_jar[ $cookie ] );
+ } else {
+ $this->assertInstanceOf( 'WpOrg\Requests\Cookie', $cookie_jar[ (string) $cookie ] );
+ }
+ }
+ }
+
+ /**
+ * Test that WP_Http::normalize_cookies method correctly casts integer cookie names to strings.
+ * @ticket 58566
+ *
+ * @covers WP_Http::normalize_cookies
+ */
+ public function test_normalize_cookies_casts_cookie_name_integer_to_string() {
+ $http = _wp_http_get_object();
+
+ $cookies = array(
+ 'foo' => new WP_Http_Cookie(
+ array(
+ 'name' => 1,
+ 'value' => 'foo',
+ )
+ ),
+ );
+
+ $cookie_jar = $http->normalize_cookies( $cookies );
+
+ $this->assertInstanceOf( 'WpOrg\Requests\Cookie\Jar', $cookie_jar );
+ $this->assertInstanceOf( 'WpOrg\Requests\Cookie', $cookie_jar['1'] );
+ }
}
diff --git a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php
index db6a82527455f..ea6de4017be26 100644
--- a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php
+++ b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php
@@ -49,6 +49,7 @@ public function tear_down() {
$wp_textdomain_registry = new WP_Textdomain_Registry();
unload_textdomain( 'internationalized-plugin' );
+ unload_textdomain( 'internationalized-plugin-2' );
unload_textdomain( 'internationalized-theme' );
parent::tear_down();
@@ -86,6 +87,27 @@ public function test_plugin_translation_should_be_translated_without_calling_loa
$this->assertTrue( $is_textdomain_loaded_after );
}
+ /**
+ * @ticket 59656
+ *
+ * @covers ::is_textdomain_loaded
+ */
+ public function test_plugin_translation_should_be_translated_with_only_an_l10n_php_file() {
+ add_filter( 'locale', array( $this, 'filter_set_locale_to_german' ) );
+
+ require_once DIR_TESTDATA . '/plugins/internationalized-plugin-2.php';
+
+ $is_textdomain_loaded_before = is_textdomain_loaded( 'internationalized-plugin-2' );
+ $actual_output = i18n_plugin_2_test();
+ $is_textdomain_loaded_after = is_textdomain_loaded( 'internationalized-plugin-2' );
+
+ remove_filter( 'locale', array( $this, 'filter_set_locale_to_german' ) );
+
+ $this->assertFalse( $is_textdomain_loaded_before );
+ $this->assertSame( 'Das ist ein Dummy Plugin', $actual_output );
+ $this->assertTrue( $is_textdomain_loaded_after );
+ }
+
/**
* @ticket 34114
*
diff --git a/tests/phpunit/tests/l10n/wpTextdomainRegistry.php b/tests/phpunit/tests/l10n/wpTextdomainRegistry.php
index 19a239428d50a..dd9688e947488 100644
--- a/tests/phpunit/tests/l10n/wpTextdomainRegistry.php
+++ b/tests/phpunit/tests/l10n/wpTextdomainRegistry.php
@@ -151,36 +151,46 @@ public function test_invalidate_mo_files_cache() {
public function data_domains_locales() {
return array(
- 'Non-existent plugin' => array(
+ 'Non-existent plugin' => array(
'unknown-plugin',
'en_US',
false,
),
- 'Non-existent plugin with de_DE' => array(
+ 'Non-existent plugin with de_DE' => array(
'unknown-plugin',
'de_DE',
false,
),
- 'Available de_DE translations' => array(
+ 'Available de_DE translations' => array(
'internationalized-plugin',
'de_DE',
WP_LANG_DIR . '/plugins/',
),
- 'Available es_ES translations' => array(
+ 'Available es_ES translations' => array(
'internationalized-plugin',
'es_ES',
WP_LANG_DIR . '/plugins/',
),
- 'Unavailable fr_FR translations' => array(
+ 'Unavailable fr_FR translations' => array(
'internationalized-plugin',
'fr_FR',
false,
),
- 'Unavailable en_US translations' => array(
+ 'Unavailable en_US translations' => array(
'internationalized-plugin',
'en_US',
false,
),
+ 'Available de_DE translations (.l10n.php)' => array(
+ 'internationalized-plugin-2',
+ 'de_DE',
+ WP_LANG_DIR . '/plugins/',
+ ),
+ 'Available es_ES translations (.l10n.php)' => array(
+ 'internationalized-plugin-2',
+ 'es_ES',
+ WP_LANG_DIR . '/plugins/',
+ ),
);
}
}
diff --git a/tests/phpunit/tests/l10n/wpTranslations.php b/tests/phpunit/tests/l10n/wpTranslations.php
index f2ef4535a0810..70abc4fd3063d 100644
--- a/tests/phpunit/tests/l10n/wpTranslations.php
+++ b/tests/phpunit/tests/l10n/wpTranslations.php
@@ -71,8 +71,8 @@ public function test_get_entries_plural() {
array(
new Translation_Entry(
array(
+ 'is_plural' => true,
'singular' => 'one dragon',
- 'plural' => '%d dragons',
'translations' => array(
'oney dragoney',
'twoey dragoney',
@@ -119,7 +119,6 @@ public function test_get_entries_context() {
array(
'is_plural' => true,
'singular' => 'one dragon',
- 'plural' => '%d dragons',
'context' => 'dragonland',
'translations' => array(
'oney dragoney',
@@ -220,6 +219,52 @@ public function test_translate_plural() {
$this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' );
}
+ /**
+ * @covers ::translate_plural
+ * @covers WP_Translation_File::get_plural_form
+ */
+ public function test_translate_plural_complex() {
+ load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/l10n/plural-complex.mo' );
+
+ $this->assertSame( '%s razpoložljiva posodobitev', _n( '%s update available', '%s updates available', 101, 'wp-tests-domain' ) ); // 1, 101, 201
+ $this->assertSame( '%s razpoložljivi posodobitvi', _n( '%s update available', '%s updates available', 102, 'wp-tests-domain' ) ); // 2, 102, 202
+ $this->assertSame( '%s razpoložljive posodobitve', _n( '%s update available', '%s updates available', 103, 'wp-tests-domain' ) ); // 3, 4, 103
+ $this->assertSame( '%s razpoložljivih posodobitev', _n( '%s update available', '%s updates available', 5, 'wp-tests-domain' ) ); // 0, 5, 6
+ }
+
+ /**
+ * @covers ::translate_plural
+ * @covers WP_Translation_File::get_plural_form
+ */
+ public function test_translate_plural_complex_php() {
+ load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/l10n/plural-complex.php' );
+
+ $this->assertSame( '%s razpoložljiva posodobitev', _n( '%s update available', '%s updates available', 101, 'wp-tests-domain' ) ); // 1, 101, 201
+ $this->assertSame( '%s razpoložljivi posodobitvi', _n( '%s update available', '%s updates available', 102, 'wp-tests-domain' ) ); // 2, 102, 202
+ $this->assertSame( '%s razpoložljive posodobitve', _n( '%s update available', '%s updates available', 103, 'wp-tests-domain' ) ); // 3, 4, 103
+ $this->assertSame( '%s razpoložljivih posodobitev', _n( '%s update available', '%s updates available', 5, 'wp-tests-domain' ) ); // 0, 5, 6
+ }
+
+ /**
+ * @covers WP_Translation_File::get_plural_form
+ */
+ public function test_get_plural_form() {
+ $moe = WP_Translation_File::create( DIR_TESTDATA . '/l10n/plural-complex.mo' );
+
+ $this->assertSame( 0, $moe->get_plural_form( 1 ) );
+ $this->assertSame( 0, $moe->get_plural_form( 101 ) );
+ $this->assertSame( 0, $moe->get_plural_form( 201 ) );
+ $this->assertSame( 1, $moe->get_plural_form( 2 ) );
+ $this->assertSame( 1, $moe->get_plural_form( 102 ) );
+ $this->assertSame( 1, $moe->get_plural_form( 202 ) );
+ $this->assertSame( 2, $moe->get_plural_form( 3 ) );
+ $this->assertSame( 2, $moe->get_plural_form( 4 ) );
+ $this->assertSame( 2, $moe->get_plural_form( 103 ) );
+ $this->assertSame( 3, $moe->get_plural_form( 0 ) );
+ $this->assertSame( 3, $moe->get_plural_form( 5 ) );
+ $this->assertSame( 3, $moe->get_plural_form( 6 ) );
+ }
+
/**
* @covers ::translate_plural
*/
diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php
index 8f3aa41c05bb9..ea318eb329284 100644
--- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php
+++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php
@@ -173,6 +173,7 @@ public function test_create_invalid_filetype() {
* @covers ::translate_plural
* @covers ::locate_translation
* @covers ::get_files
+ * @covers WP_Translation_File::translate
*
* @dataProvider data_simple_example_files
*
@@ -198,6 +199,10 @@ public function test_simple_translation_files( string $file ) {
$this->assertSame( 'translation1 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) );
$this->assertSame( 'translation0 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) );
$this->assertSame( 'translation1 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) );
+
+ $this->assertSame( 'Produkt', $controller->translate( 'Product', '', 'unittest' ) );
+ $this->assertSame( 'Produkt', $controller->translate_plural( array( 'Product', 'Products' ), 1, '', 'unittest' ) );
+ $this->assertSame( 'Produkte', $controller->translate_plural( array( 'Product', 'Products' ), 2, '', 'unittest' ) );
}
/**
diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php
index 0e721258ed40a..44c4027579d56 100644
--- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php
@@ -1045,6 +1045,28 @@ public function test_create_item_ensure_relative_path() {
$this->assertStringNotContainsString( ABSPATH, get_post_meta( $attachment['id'], '_wp_attached_file', true ) );
}
+ /**
+ * @ticket 57897
+ *
+ * @requires function imagejpeg
+ */
+ public function test_create_item_with_terms() {
+ wp_set_current_user( self::$author_id );
+ register_taxonomy_for_object_type( 'category', 'attachment' );
+ $category = wp_insert_term( 'Media Category', 'category' );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/media' );
+ $request->set_header( 'Content-Type', 'image/jpeg' );
+ $request->set_header( 'Content-Disposition', 'attachment; filename=canola.jpg' );
+
+ $request->set_body( file_get_contents( self::$test_file ) );
+ $request->set_param( 'categories', array( $category['term_id'] ) );
+ $response = rest_get_server()->dispatch( $request );
+ $attachment = $response->get_data();
+
+ $term = wp_get_post_terms( $attachment['id'], 'category' );
+ $this->assertSame( $category['term_id'], $term[0]->term_id );
+ }
+
public function test_update_item() {
wp_set_current_user( self::$editor_id );
$attachment_id = self::factory()->attachment->create_object(
diff --git a/tests/phpunit/tests/rest-api/rest-block-type-controller.php b/tests/phpunit/tests/rest-api/rest-block-type-controller.php
index 59c77e03b68b2..6e314e6af4ab7 100644
--- a/tests/phpunit/tests/rest-api/rest-block-type-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-block-type-controller.php
@@ -207,6 +207,7 @@ public function test_get_item_invalid() {
'category' => true,
'parent' => 'invalid_parent',
'ancestor' => 'invalid_ancestor',
+ 'allowed_blocks' => 'invalid_allowed_blocks',
'icon' => true,
'description' => true,
'keywords' => 'invalid_keywords',
@@ -237,6 +238,7 @@ public function test_get_item_invalid() {
$this->assertNull( $data['category'] );
$this->assertSameSets( array( 'invalid_parent' ), $data['parent'] );
$this->assertSameSets( array( 'invalid_ancestor' ), $data['ancestor'] );
+ $this->assertSameSets( array( 'invalid_allowed_blocks' ), $data['allowed_blocks'] );
$this->assertNull( $data['icon'] );
$this->assertSame( '1', $data['description'] );
$this->assertSameSets( array( 'invalid_keywords' ), $data['keywords'] );
@@ -283,6 +285,7 @@ public function test_get_item_defaults() {
'category' => false,
'parent' => false,
'ancestor' => false,
+ 'allowed_blocks' => false,
'icon' => false,
'description' => false,
'keywords' => false,
@@ -313,6 +316,7 @@ public function test_get_item_defaults() {
$this->assertNull( $data['category'] );
$this->assertSameSets( array(), $data['parent'] );
$this->assertSameSets( array(), $data['ancestor'] );
+ $this->assertSameSets( array(), $data['allowed_blocks'] );
$this->assertNull( $data['icon'] );
$this->assertSame( '', $data['description'] );
$this->assertSameSets( array(), $data['keywords'] );
@@ -550,6 +554,7 @@ public function test_get_variation() {
* @ticket 47620
* @ticket 57585
* @ticket 59346
+ * @ticket 60403
*/
public function test_get_item_schema() {
wp_set_current_user( self::$admin_id );
@@ -557,13 +562,14 @@ public function test_get_item_schema() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
- $this->assertCount( 30, $properties );
+ $this->assertCount( 32, $properties );
$this->assertArrayHasKey( 'api_version', $properties );
$this->assertArrayHasKey( 'name', $properties );
$this->assertArrayHasKey( 'title', $properties );
$this->assertArrayHasKey( 'category', $properties );
$this->assertArrayHasKey( 'parent', $properties );
$this->assertArrayHasKey( 'ancestor', $properties );
+ $this->assertArrayHasKey( 'allowed_blocks', $properties );
$this->assertArrayHasKey( 'icon', $properties );
$this->assertArrayHasKey( 'description', $properties );
$this->assertArrayHasKey( 'keywords', $properties );
@@ -582,6 +588,7 @@ public function test_get_item_schema() {
$this->assertArrayHasKey( 'view_script_handles', $properties );
$this->assertArrayHasKey( 'editor_style_handles', $properties );
$this->assertArrayHasKey( 'style_handles', $properties );
+ $this->assertArrayHasKey( 'view_style_handles', $properties, 'schema must contain view_style_handles' );
$this->assertArrayHasKey( 'is_dynamic', $properties );
// Deprecated properties.
$this->assertArrayHasKey( 'editor_script', $properties );
@@ -695,6 +702,7 @@ protected function check_block_type_object( $block_type, $data, $links ) {
'category',
'parent',
'ancestor',
+ 'allowedBlocks',
'icon',
'description',
'keywords',
diff --git a/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
index 30e5b983eae9c..3773b5fd1f9e4 100644
--- a/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php
@@ -226,6 +226,7 @@ public function set_up() {
/**
* @ticket 58524
+ * @ticket 59810
*
* @covers WP_REST_Global_Styles_Controller::register_routes
*/
@@ -236,6 +237,11 @@ public function test_register_routes() {
$routes,
'Global style revisions based on the given parentID route does not exist.'
);
+ $this->assertArrayHasKey(
+ '/wp/v2/global-styles/(?P
[\d]+)/revisions/(?P[\d]+)',
+ $routes,
+ 'Single global style revisions based on the given parentID and revision ID route does not exist.'
+ );
}
/**
@@ -304,6 +310,38 @@ public function test_get_items() {
$this->check_get_revision_response( $data[2], $this->revision_1 );
}
+ /**
+ * @ticket 59810
+ *
+ * @covers WP_REST_Global_Styles_Controller::get_item
+ */
+ public function test_get_item() {
+ wp_set_current_user( self::$admin_id );
+
+ $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions/' . $this->revision_1_id );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+
+ $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
+ $this->check_get_revision_response( $data, $this->revision_1 );
+ }
+
+ /**
+ * @ticket 59810
+ *
+ * @covers WP_REST_Global_Styles_Controller::get_revision
+ */
+ public function test_get_item_invalid_revision_id_should_error() {
+ wp_set_current_user( self::$admin_id );
+
+ $expected_error = 'rest_post_invalid_id';
+ $expected_status = 404;
+ $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions/20000001' );
+ $response = rest_get_server()->dispatch( $request );
+
+ $this->assertErrorResponse( $expected_error, $response, $expected_status );
+ }
+
/**
* @ticket 58524
*
@@ -794,13 +832,6 @@ public function test_context_param() {
// Controller does not implement test_context_param().
}
- /**
- * @doesNotPerformAssertions
- */
- public function test_get_item() {
- // Controller does not implement get_item().
- }
-
/**
* @doesNotPerformAssertions
*/
diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php
index c53f887bc822c..3ff35260f0fb8 100644
--- a/tests/phpunit/tests/rest-api/rest-schema-setup.php
+++ b/tests/phpunit/tests/rest-api/rest-schema-setup.php
@@ -135,6 +135,7 @@ public function test_expected_routes_in_schema() {
'/wp/v2/comments/(?P[\\d]+)',
'/wp/v2/global-styles/(?P[\/\w-]+)',
'/wp/v2/global-styles/(?P[\d]+)/revisions',
+ '/wp/v2/global-styles/(?P[\d]+)/revisions/(?P[\d]+)',
'/wp/v2/global-styles/themes/(?P[\/\s%\w\.\(\)\[\]\@_\-]+)/variations',
'/wp/v2/global-styles/themes/(?P[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
'/wp/v2/search',
diff --git a/tests/phpunit/tests/script-modules/wpScriptModules.php b/tests/phpunit/tests/script-modules/wpScriptModules.php
index 1433b25c8a22f..26ca916140693 100644
--- a/tests/phpunit/tests/script-modules/wpScriptModules.php
+++ b/tests/phpunit/tests/script-modules/wpScriptModules.php
@@ -11,7 +11,8 @@
*
* @coversDefaultClass WP_Script_Modules
*/
-class Tests_WP_Script_Modules extends WP_UnitTestCase {
+class Tests_Script_Modules_WpScriptModules extends WP_UnitTestCase {
+
/**
* Instance of WP_Script_Modules.
*
@@ -24,6 +25,7 @@ class Tests_WP_Script_Modules extends WP_UnitTestCase {
*/
public function set_up() {
parent::set_up();
+ // Set up the WP_Script_Modules instance.
$this->script_modules = new WP_Script_Modules();
}
@@ -600,4 +602,37 @@ public function test_wp_enqueue_script_module_registers_all_params() {
$this->assertCount( 1, $import_map );
$this->assertStringStartsWith( '/dep.js', $import_map['dep'] );
}
+
+ /**
+ * @ticket 60348
+ *
+ * @covers ::print_import_map_polyfill()
+ */
+ public function test_wp_print_import_map_has_no_polyfill_when_no_modules_registered() {
+ $import_map_polyfill = get_echo( array( $this->script_modules, 'print_import_map' ) );
+
+ $this->assertEquals( '', $import_map_polyfill );
+ }
+
+ /**
+ * @ticket 60348
+ *
+ * @covers ::print_import_map_polyfill()
+ */
+ public function test_wp_print_import_map_has_polyfill_when_modules_registered() {
+ $script_name = 'wp-polyfill-importmap';
+ wp_register_script( $script_name, '/wp-polyfill-importmap.js' );
+
+ $this->script_modules->enqueue( 'foo', '/foo.js', array( 'dep' ), '1.0' );
+ $this->script_modules->register( 'dep', '/dep.js' );
+ $import_map_polyfill = get_echo( array( $this->script_modules, 'print_import_map' ) );
+
+ wp_deregister_script( $script_name );
+
+ $p = new WP_HTML_Tag_Processor( $import_map_polyfill );
+ $p->next_tag( array( 'tag' => 'SCRIPT' ) );
+ $id = $p->get_attribute( 'id' );
+
+ $this->assertEquals( 'wp-load-polyfill-importmap', $id );
+ }
}
diff --git a/tests/phpunit/tests/style-engine/styleEngine.php b/tests/phpunit/tests/style-engine/styleEngine.php
index b208e4ef0575a..794f540baf422 100644
--- a/tests/phpunit/tests/style-engine/styleEngine.php
+++ b/tests/phpunit/tests/style-engine/styleEngine.php
@@ -184,6 +184,24 @@ public function data_wp_style_engine_get_styles() {
),
),
+ 'inline_valid_aspect_ratio_style' => array(
+ 'block_styles' => array(
+ 'dimensions' => array(
+ 'aspectRatio' => '4/3',
+ 'minHeight' => 'unset',
+ ),
+ ),
+ 'options' => null,
+ 'expected_output' => array(
+ 'css' => 'aspect-ratio:4/3;min-height:unset;',
+ 'declarations' => array(
+ 'aspect-ratio' => '4/3',
+ 'min-height' => 'unset',
+ ),
+ 'classnames' => 'has-aspect-ratio',
+ ),
+ ),
+
'inline_valid_shadow_style' => array(
'block_styles' => array(
'shadow' => 'inset 5em 1em gold',
diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php
index 31a99b3f1ebca..ff98a98e0af65 100644
--- a/tests/phpunit/tests/theme/wpThemeJson.php
+++ b/tests/phpunit/tests/theme/wpThemeJson.php
@@ -279,7 +279,8 @@ public function test_get_settings_appearance_true_opts_in() {
'caption' => true,
),
'dimensions' => array(
- 'minHeight' => true,
+ 'aspectRatio' => true,
+ 'minHeight' => true,
),
'position' => array(
'sticky' => true,
@@ -316,7 +317,8 @@ public function test_get_settings_appearance_true_opts_in() {
'caption' => true,
),
'dimensions' => array(
- 'minHeight' => true,
+ 'aspectRatio' => true,
+ 'minHeight' => true,
),
'position' => array(
'sticky' => true,
@@ -495,6 +497,7 @@ public function test_get_stylesheet_renders_enabled_protected_properties() {
* @ticket 56611
* @ticket 58549
* @ticket 58550
+ * @ticket 60365
*/
public function test_get_stylesheet() {
$theme_json = new WP_Theme_JSON(
@@ -527,12 +530,19 @@ public function test_get_stylesheet() {
'typography' => array(
'fontFamilies' => array(
array(
- 'slug' => 'small',
- 'fontFamily' => '14px',
+ 'name' => 'Arial',
+ 'slug' => 'arial',
+ 'fontFamily' => 'Arial, serif',
),
+ ),
+ 'fontSizes' => array(
array(
- 'slug' => 'big',
- 'fontFamily' => '41px',
+ 'slug' => 'small',
+ 'size' => '14px',
+ ),
+ array(
+ 'slug' => 'big',
+ 'size' => '41px',
),
),
),
@@ -567,6 +577,11 @@ public function test_get_stylesheet() {
),
),
'blocks' => array(
+ 'core/cover' => array(
+ 'dimensions' => array(
+ 'aspectRatio' => '16/9',
+ ),
+ ),
'core/group' => array(
'color' => array(
'gradient' => 'var:preset|gradient|custom-gradient',
@@ -644,9 +659,9 @@ public function test_get_stylesheet() {
)
);
- $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--gradient--custom-gradient: linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%);--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
- $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-element-button, .wp-block-button__link{box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}.wp-block-group{background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-post-excerpt{column-count: 2;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.wp-block-image img, .wp-block-image .components-placeholder{filter: var(--wp--preset--duotone--custom-duotone);}';
- $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-custom-gradient-gradient-background{background: var(--wp--preset--gradient--custom-gradient) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}';
+ $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--gradient--custom-gradient: linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%);--wp--preset--font-size--small: 14px;--wp--preset--font-size--big: 41px;--wp--preset--font-family--arial: Arial, serif;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}';
+ $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-element-button, .wp-block-button__link{box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);}.wp-block-cover{min-height: unset;aspect-ratio: 16/9;}.wp-block-group{background: var(--wp--preset--gradient--custom-gradient);border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-post-excerpt{column-count: 2;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.wp-block-image img, .wp-block-image .components-placeholder{filter: var(--wp--preset--duotone--custom-duotone);}';
+ $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-custom-gradient-gradient-background{background: var(--wp--preset--gradient--custom-gradient) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-big-font-size{font-size: var(--wp--preset--font-size--big) !important;}.has-arial-font-family{font-family: var(--wp--preset--font-family--arial) !important;}';
$all = $variables . $styles . $presets;
$this->assertSame( $all, $theme_json->get_stylesheet() );
$this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
@@ -4970,4 +4985,139 @@ public function test_get_stylesheet_custom_root_selector() {
$actual
);
}
+
+ /**
+ * Tests that invalid properties are removed from the theme.json inside indexed arrays as settings.typography.fontFamilies.
+ *
+ * @ticket 60360
+ */
+ public function test_sanitize_indexed_arrays() {
+ $theme_json = new WP_Theme_JSON(
+ array(
+ 'version' => '2',
+ 'badKey2' => 'I am Evil!',
+ 'settings' => array(
+ 'badKey3' => 'I am Evil!',
+ 'typography' => array(
+ 'badKey4' => 'I am Evil!',
+ 'fontFamilies' => array(
+ 'custom' => array(
+ array(
+ 'badKey4' => 'I am Evil!',
+ 'name' => 'Arial',
+ 'slug' => 'arial',
+ 'fontFamily' => 'Arial, sans-serif',
+ ),
+ ),
+ 'theme' => array(
+ array(
+ 'badKey5' => 'I am Evil!',
+ 'name' => 'Piazzolla',
+ 'slug' => 'piazzolla',
+ 'fontFamily' => 'Piazzolla',
+ 'fontFace' => array(
+ array(
+ 'badKey6' => 'I am Evil!',
+ 'fontFamily' => 'Piazzolla',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ array(
+ 'badKey7' => 'I am Evil!',
+ 'fontFamily' => 'Piazzolla',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ ),
+ ),
+ array(
+ 'badKey8' => 'I am Evil!',
+ 'name' => 'Inter',
+ 'slug' => 'Inter',
+ 'fontFamily' => 'Inter',
+ 'fontFace' => array(
+ array(
+ 'badKey9' => 'I am Evil!',
+ 'fontFamily' => 'Inter',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ array(
+ 'badKey10' => 'I am Evil!',
+ 'fontFamily' => 'Inter',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+
+ $expected_sanitized = array(
+ 'version' => '2',
+ 'settings' => array(
+ 'typography' => array(
+ 'fontFamilies' => array(
+ 'custom' => array(
+ array(
+ 'name' => 'Arial',
+ 'slug' => 'arial',
+ 'fontFamily' => 'Arial, sans-serif',
+ ),
+ ),
+ 'theme' => array(
+ array(
+ 'name' => 'Piazzolla',
+ 'slug' => 'piazzolla',
+ 'fontFamily' => 'Piazzolla',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Piazzolla',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ array(
+ 'fontFamily' => 'Piazzolla',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'Inter',
+ 'slug' => 'Inter',
+ 'fontFamily' => 'Inter',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'Inter',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ array(
+ 'fontFamily' => 'Inter',
+ 'fontStyle' => 'italic',
+ 'fontWeight' => '400',
+ 'src' => 'https://example.com/font.ttf',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ $sanitized_theme_json = $theme_json->get_raw_data();
+ $this->assertSameSetsWithIndex( $expected_sanitized, $sanitized_theme_json, 'Sanitized theme.json does not match' );
+ }
}
diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js
index ef8e53d4bb508..6c961548eb8e2 100644
--- a/tests/qunit/fixtures/wp-api-generated.js
+++ b/tests/qunit/fixtures/wp-api-generated.js
@@ -10044,6 +10044,42 @@ mockedApiResponse.Schema = {
}
]
},
+ "/wp/v2/global-styles/(?P[\\d]+)/revisions/(?P[\\d]+)": {
+ "namespace": "wp/v2",
+ "methods": [
+ "GET"
+ ],
+ "endpoints": [
+ {
+ "methods": [
+ "GET"
+ ],
+ "args": {
+ "parent": {
+ "description": "The ID for the parent of the global styles revision.",
+ "type": "integer",
+ "required": false
+ },
+ "id": {
+ "description": "Unique identifier for the global styles revision.",
+ "type": "integer",
+ "required": false
+ },
+ "context": {
+ "description": "Scope under which the request is made; determines fields present in response.",
+ "type": "string",
+ "enum": [
+ "view",
+ "embed",
+ "edit"
+ ],
+ "default": "view",
+ "required": false
+ }
+ }
+ }
+ ]
+ },
"/wp/v2/global-styles/themes/(?P[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)/variations": {
"namespace": "wp/v2",
"methods": [
diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js
index 91e2cfafbdf84..f1c9d20cd8504 100644
--- a/tools/webpack/blocks.js
+++ b/tools/webpack/blocks.js
@@ -3,135 +3,135 @@
*/
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
-/**
- * WordPress dependencies
- */
-const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
-
/**
* Internal dependencies
*/
-const { baseDir, getBaseConfig, normalizeJoin, stylesTransform } = require( './shared' );
+const {
+ baseDir,
+ getBaseConfig,
+ normalizeJoin,
+ stylesTransform,
+} = require( './shared' );
const {
isDynamic,
toDirectoryName,
getStableBlocksMetadata,
} = require( '../release/sync-stable-blocks' );
-module.exports = function( env = { environment: 'production', watch: false, buildTarget: false } ) {
+module.exports = function (
+ env = { environment: 'production', watch: false, buildTarget: false }
+) {
const mode = env.environment;
const suffix = mode === 'production' ? '.min' : '';
- let buildTarget = env.buildTarget ? env.buildTarget : ( mode === 'production' ? 'build' : 'src' );
+ let buildTarget = env.buildTarget
+ ? env.buildTarget
+ : mode === 'production'
+ ? 'build'
+ : 'src';
buildTarget = buildTarget + '/wp-includes';
const blocks = getStableBlocksMetadata();
- const dynamicBlockFolders = blocks.filter( isDynamic ).map( toDirectoryName );
+ const dynamicBlockFolders = blocks
+ .filter( isDynamic )
+ .map( toDirectoryName );
const blockFolders = blocks.map( toDirectoryName );
const blockPHPFiles = {
- 'widgets/src/blocks/legacy-widget/index.php': 'wp-includes/blocks/legacy-widget.php',
- 'widgets/src/blocks/widget-group/index.php': 'wp-includes/blocks/widget-group.php',
+ 'widgets/src/blocks/legacy-widget/index.php':
+ 'wp-includes/blocks/legacy-widget.php',
+ 'widgets/src/blocks/widget-group/index.php':
+ 'wp-includes/blocks/widget-group.php',
...dynamicBlockFolders.reduce( ( files, blockName ) => {
- files[ `block-library/src/${ blockName }/index.php` ] = `wp-includes/blocks/${ blockName }.php`;
+ files[
+ `block-library/src/${ blockName }/index.php`
+ ] = `wp-includes/blocks/${ blockName }.php`;
return files;
}, {} ),
};
const blockMetadataFiles = {
- 'widgets/src/blocks/legacy-widget/block.json': 'wp-includes/blocks/legacy-widget/block.json',
- 'widgets/src/blocks/widget-group/block.json': 'wp-includes/blocks/widget-group/block.json',
+ 'widgets/src/blocks/legacy-widget/block.json':
+ 'wp-includes/blocks/legacy-widget/block.json',
+ 'widgets/src/blocks/widget-group/block.json':
+ 'wp-includes/blocks/widget-group/block.json',
...blockFolders.reduce( ( files, blockName ) => {
- files[ `block-library/src/${ blockName }/block.json` ] = `wp-includes/blocks/${ blockName }/block.json`;
+ files[
+ `block-library/src/${ blockName }/block.json`
+ ] = `wp-includes/blocks/${ blockName }/block.json`;
return files;
}, {} ),
};
const blockPHPCopies = Object.keys( blockPHPFiles ).map( ( filename ) => ( {
- from: normalizeJoin(baseDir, `node_modules/@wordpress/${ filename }` ),
- to: normalizeJoin(baseDir, `src/${ blockPHPFiles[ filename ] }` ),
+ from: normalizeJoin( baseDir, `node_modules/@wordpress/${ filename }` ),
+ to: normalizeJoin( baseDir, `src/${ blockPHPFiles[ filename ] }` ),
} ) );
- const blockMetadataCopies = Object.keys( blockMetadataFiles ).map( ( filename ) => ( {
- from: normalizeJoin(baseDir, `node_modules/@wordpress/${ filename }` ),
- to: normalizeJoin(baseDir, `src/${ blockMetadataFiles[ filename ] }` ),
- } ) );
+ const blockMetadataCopies = Object.keys( blockMetadataFiles ).map(
+ ( filename ) => ( {
+ from: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/${ filename }`
+ ),
+ to: normalizeJoin(
+ baseDir,
+ `src/${ blockMetadataFiles[ filename ] }`
+ ),
+ } )
+ );
const blockStylesheetCopies = blockFolders.map( ( blockName ) => ( {
- from: normalizeJoin(baseDir, `node_modules/@wordpress/block-library/build-style/${ blockName }/*.css` ),
- to: normalizeJoin(baseDir, `${ buildTarget }/blocks/${ blockName }/[name]${ suffix }.css` ),
+ from: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/block-library/build-style/${ blockName }/*.css`
+ ),
+ to: normalizeJoin(
+ baseDir,
+ `${ buildTarget }/blocks/${ blockName }/[name]${ suffix }.css`
+ ),
transform: stylesTransform( mode ),
noErrorOnMissing: true,
} ) );
+ // Todo: This list need of entry points need to be automatically fetched from the package
+ // We shouldn't have to maintain it manually.
+ const interactiveBlocks = [
+ 'navigation',
+ 'image',
+ 'query',
+ 'file',
+ 'search',
+ ];
+
const baseConfig = getBaseConfig( env );
const config = {
...baseConfig,
- entry: {
- 'navigation/view': normalizeJoin( baseDir, 'node_modules/@wordpress/block-library/build-module/navigation/view' ),
- 'image/view': normalizeJoin( baseDir, 'node_modules/@wordpress/block-library/build-module/image/view' ),
- 'query/view': normalizeJoin( baseDir, 'node_modules/@wordpress/block-library/build-module/query/view' ),
- 'file/view': normalizeJoin( baseDir, 'node_modules/@wordpress/block-library/build-module/file/view' ),
- 'search/view': normalizeJoin( baseDir, 'node_modules/@wordpress/block-library/build-module/search/view' ),
+ entry: interactiveBlocks.reduce(( memo, blockName ) => {
+ memo[ blockName ] = {
+ import: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/block-library/build-module/${ blockName }/view`
+ ),
+ };
+ return memo;
+ }, {}),
+ experiments: {
+ outputModule: true,
},
output: {
devtoolNamespace: 'wp',
- filename: `./blocks/[name]${ suffix }.js`,
+ filename: `./blocks/[name]/view${ suffix }.js`,
path: normalizeJoin( baseDir, buildTarget ),
- chunkLoadingGlobal: `__WordPressPrivateInteractivityAPI__`,
- },
- resolve: {
- alias: {
- '@wordpress/interactivity': normalizeJoin( baseDir, 'node_modules/@wordpress/interactivity/src/index.js' ),
+ library: {
+ type: 'module',
},
+ environment: { module: true },
},
- optimization: {
- ...baseConfig.optimization,
- runtimeChunk: {
- name: 'private-interactivity',
- },
- splitChunks: {
- cacheGroups: {
- interactivity: {
- name: 'private-interactivity',
- test: /^(?!.*[\\/]block-library[\\/]).*$/,
- filename: `./js/dist/interactivity${suffix}.js`,
- chunks: 'all',
- minSize: 0,
- priority: -10,
- },
- },
- },
- },
- module: {
- rules: [
- {
- test: /\.(j|t)sx?$/,
- use: [
- {
- loader: require.resolve( 'babel-loader' ),
- options: {
- cacheDirectory: process.env.BABEL_CACHE_DIRECTORY || true,
- babelrc: false,
- configFile: false,
- presets: [
- [
- '@babel/preset-react',
- {
- runtime: 'automatic',
- importSource: 'preact',
- },
- ],
- ],
- },
- },
- ],
- },
- ],
+ externalsType: 'module',
+ externals: {
+ '@wordpress/interactivity': '@wordpress/interactivity',
+ '@wordpress/interactivity-router': 'import @wordpress/interactivity-router',
},
plugins: [
...baseConfig.plugins,
- new DependencyExtractionPlugin( {
- injectPolyfill: false,
- useDefaults: false,
- } ),
new CopyWebpackPlugin( {
patterns: [
...blockPHPCopies,
diff --git a/tools/webpack/modules.js b/tools/webpack/modules.js
new file mode 100644
index 0000000000000..cfb19aedbb3a1
--- /dev/null
+++ b/tools/webpack/modules.js
@@ -0,0 +1,72 @@
+/**
+ * WordPress dependencies
+ */
+const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
+
+/**
+ * Internal dependencies
+ */
+const {
+ baseDir,
+ getBaseConfig,
+ normalizeJoin,
+ MODULES,
+ WORDPRESS_NAMESPACE,
+} = require( './shared' );
+
+module.exports = function (
+ env = { environment: 'production', watch: false, buildTarget: false }
+) {
+ const mode = env.environment;
+ const suffix = mode === 'production' ? '.min' : '';
+ let buildTarget = env.buildTarget
+ ? env.buildTarget
+ : mode === 'production'
+ ? 'build'
+ : 'src';
+ buildTarget = buildTarget + '/wp-includes';
+
+ const baseConfig = getBaseConfig( env );
+ const config = {
+ ...baseConfig,
+ entry: MODULES.map( ( packageName ) =>
+ packageName.replace( WORDPRESS_NAMESPACE, '' )
+ ).reduce( ( memo, packageName ) => {
+ memo[ packageName ] = {
+ import: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/${ packageName }`
+ ),
+ };
+
+ return memo;
+ }, {} ),
+ experiments: {
+ outputModule: true,
+ },
+ output: {
+ devtoolNamespace: 'wp',
+ filename: `[name]${ suffix }.js`,
+ path: normalizeJoin( baseDir, `${ buildTarget }/js/dist` ),
+ library: {
+ type: 'module',
+ },
+ environment: { module: true },
+ },
+ externalsType: 'module',
+ externals: {
+ '@wordpress/interactivity': '@wordpress/interactivity',
+ '@wordpress/interactivity-router':
+ 'import @wordpress/interactivity-router',
+ },
+ plugins: [
+ ...baseConfig.plugins,
+ new DependencyExtractionPlugin( {
+ injectPolyfill: false,
+ useDefaults: false,
+ } ),
+ ],
+ };
+
+ return config;
+};
diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js
index fa72d06982600..2b02852d206de 100644
--- a/tools/webpack/packages.js
+++ b/tools/webpack/packages.js
@@ -16,7 +16,15 @@ const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-we
/**
* Internal dependencies
*/
-const { baseDir, getBaseConfig, normalizeJoin, stylesTransform } = require( './shared' );
+const {
+ baseDir,
+ getBaseConfig,
+ normalizeJoin,
+ stylesTransform,
+ BUNDLED_PACKAGES,
+ MODULES,
+ WORDPRESS_NAMESPACE,
+} = require( './shared' );
const { dependencies } = require( '../../package' );
const exportDefaultPackages = [
@@ -40,37 +48,52 @@ const exportDefaultPackages = [
*/
function mapVendorCopies( vendors, buildTarget ) {
return Object.keys( vendors ).map( ( filename ) => ( {
- from: normalizeJoin(baseDir, `node_modules/${ vendors[ filename ] }` ),
- to: normalizeJoin(baseDir, `${ buildTarget }/js/dist/vendor/${ filename }` ),
+ from: normalizeJoin( baseDir, `node_modules/${ vendors[ filename ] }` ),
+ to: normalizeJoin(
+ baseDir,
+ `${ buildTarget }/js/dist/vendor/${ filename }`
+ ),
} ) );
}
-module.exports = function( env = { environment: 'production', watch: false, buildTarget: false } ) {
+module.exports = function (
+ env = { environment: 'production', watch: false, buildTarget: false }
+) {
const mode = env.environment;
const suffix = mode === 'production' ? '.min' : '';
- let buildTarget = env.buildTarget ? env.buildTarget : ( mode === 'production' ? 'build' : 'src' );
- buildTarget = buildTarget + '/wp-includes';
+ let buildTarget = env.buildTarget
+ ? env.buildTarget
+ : mode === 'production'
+ ? 'build'
+ : 'src';
+ buildTarget = buildTarget + '/wp-includes';
- const WORDPRESS_NAMESPACE = '@wordpress/';
- const BUNDLED_PACKAGES = [ '@wordpress/icons', '@wordpress/interface', '@wordpress/interactivity', '@wordpress/sync' ];
const packages = Object.keys( dependencies )
- .filter( ( packageName ) =>
- ! BUNDLED_PACKAGES.includes( packageName ) &&
- packageName.startsWith( WORDPRESS_NAMESPACE )
- )
- .map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) );
+ .filter(
+ ( packageName ) =>
+ ! BUNDLED_PACKAGES.includes( packageName ) &&
+ ! MODULES.includes( packageName ) &&
+ packageName.startsWith( WORDPRESS_NAMESPACE )
+ )
+ .map( ( packageName ) =>
+ packageName.replace( WORDPRESS_NAMESPACE, '' )
+ );
const vendors = {
'lodash.js': 'lodash/lodash.js',
'wp-polyfill.js': '@wordpress/babel-preset-default/build/polyfill.js',
'wp-polyfill-fetch.js': 'whatwg-fetch/dist/fetch.umd.js',
'wp-polyfill-element-closest.js': 'element-closest/browser.js',
- 'wp-polyfill-node-contains.js': 'polyfill-library/polyfills/__dist/Node.prototype.contains/raw.js',
+ 'wp-polyfill-node-contains.js':
+ 'polyfill-library/polyfills/__dist/Node.prototype.contains/raw.js',
'wp-polyfill-url.js': 'core-js-url-browser/url.js',
- 'wp-polyfill-dom-rect.js': 'polyfill-library/polyfills/__dist/DOMRect/raw.js',
+ 'wp-polyfill-dom-rect.js':
+ 'polyfill-library/polyfills/__dist/DOMRect/raw.js',
'wp-polyfill-formdata.js': 'formdata-polyfill/FormData.js',
- 'wp-polyfill-object-fit.js': 'objectFitPolyfill/src/objectFitPolyfill.js',
+ 'wp-polyfill-object-fit.js':
+ 'objectFitPolyfill/src/objectFitPolyfill.js',
'wp-polyfill-inert.js': 'wicg-inert/dist/inert.js',
+ 'wp-polyfill-importmap.js': 'es-module-shims/dist/es-module-shims.wasm.js',
'moment.js': 'moment/moment.js',
'react.js': 'react/umd/react.development.js',
'react-dom.js': 'react-dom/umd/react-dom.development.js',
@@ -79,11 +102,13 @@ module.exports = function( env = { environment: 'production', watch: false, buil
const minifiedVendors = {
'lodash.min.js': 'lodash/lodash.min.js',
- 'wp-polyfill.min.js': '@wordpress/babel-preset-default/build/polyfill.min.js',
+ 'wp-polyfill.min.js':
+ '@wordpress/babel-preset-default/build/polyfill.min.js',
'wp-polyfill-element-closest.min.js': 'element-closest/browser.js',
'wp-polyfill-formdata.min.js': 'formdata-polyfill/formdata.min.js',
'wp-polyfill-url.min.js': 'core-js-url-browser/url.min.js',
- 'wp-polyfill-object-fit.min.js': 'objectFitPolyfill/dist/objectFitPolyfill.min.js',
+ 'wp-polyfill-object-fit.min.js':
+ 'objectFitPolyfill/dist/objectFitPolyfill.min.js',
'wp-polyfill-inert.min.js': 'wicg-inert/dist/inert.min.js',
'moment.min.js': 'moment/min/moment.min.js',
'react.min.js': 'react/umd/react.production.min.js',
@@ -93,39 +118,56 @@ module.exports = function( env = { environment: 'production', watch: false, buil
const minifyVendors = {
'regenerator-runtime.min.js': 'regenerator-runtime/runtime.js',
'wp-polyfill-fetch.min.js': 'whatwg-fetch/dist/fetch.umd.js',
- 'wp-polyfill-node-contains.min.js': 'polyfill-library/polyfills/__dist/Node.prototype.contains/raw.js',
- 'wp-polyfill-dom-rect.min.js': 'polyfill-library/polyfills/__dist/DOMRect/raw.js',
+ 'wp-polyfill-node-contains.min.js':
+ 'polyfill-library/polyfills/__dist/Node.prototype.contains/raw.js',
+ 'wp-polyfill-dom-rect.min.js':
+ 'polyfill-library/polyfills/__dist/DOMRect/raw.js',
+ 'wp-polyfill-importmap.min.js': 'es-module-shims/dist/es-module-shims.wasm.js',
};
const phpFiles = {
- 'block-serialization-default-parser/class-wp-block-parser.php': 'wp-includes/class-wp-block-parser.php',
- 'block-serialization-default-parser/class-wp-block-parser-frame.php': 'wp-includes/class-wp-block-parser-frame.php',
- 'block-serialization-default-parser/class-wp-block-parser-block.php': 'wp-includes/class-wp-block-parser-block.php',
+ 'block-serialization-default-parser/class-wp-block-parser.php':
+ 'wp-includes/class-wp-block-parser.php',
+ 'block-serialization-default-parser/class-wp-block-parser-frame.php':
+ 'wp-includes/class-wp-block-parser-frame.php',
+ 'block-serialization-default-parser/class-wp-block-parser-block.php':
+ 'wp-includes/class-wp-block-parser-block.php',
};
const developmentCopies = mapVendorCopies( vendors, buildTarget );
const minifiedCopies = mapVendorCopies( minifiedVendors, buildTarget );
- const minifyCopies = mapVendorCopies( minifyVendors, buildTarget ).map( ( copyCommand ) => {
- return {
- ...copyCommand,
- transform: ( content ) => {
- return UglifyJS.minify( content.toString() ).code;
- },
- };
- } );
+ const minifyCopies = mapVendorCopies( minifyVendors, buildTarget ).map(
+ ( copyCommand ) => {
+ return {
+ ...copyCommand,
+ transform: ( content ) => {
+ return UglifyJS.minify( content.toString() ).code;
+ },
+ };
+ }
+ );
- let vendorCopies = mode === "development" ? developmentCopies : [ ...minifiedCopies, ...minifyCopies ];
+ let vendorCopies =
+ mode === 'development'
+ ? developmentCopies
+ : [ ...minifiedCopies, ...minifyCopies ];
let cssCopies = packages.map( ( packageName ) => ( {
- from: normalizeJoin(baseDir, `node_modules/@wordpress/${ packageName }/build-style/*.css` ),
- to: normalizeJoin(baseDir, `${ buildTarget }/css/dist/${ packageName }/[name]${ suffix }.css` ),
+ from: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/${ packageName }/build-style/*.css`
+ ),
+ to: normalizeJoin(
+ baseDir,
+ `${ buildTarget }/css/dist/${ packageName }/[name]${ suffix }.css`
+ ),
transform: stylesTransform( mode ),
noErrorOnMissing: true,
} ) );
const phpCopies = Object.keys( phpFiles ).map( ( filename ) => ( {
- from: normalizeJoin(baseDir, `node_modules/@wordpress/${ filename }` ),
- to: normalizeJoin(baseDir, `src/${ phpFiles[ filename ] }` ),
+ from: normalizeJoin( baseDir, `node_modules/@wordpress/${ filename }` ),
+ to: normalizeJoin( baseDir, `src/${ phpFiles[ filename ] }` ),
} ) );
const baseConfig = getBaseConfig( env );
@@ -133,7 +175,10 @@ module.exports = function( env = { environment: 'production', watch: false, buil
...baseConfig,
entry: packages.reduce( ( memo, packageName ) => {
memo[ packageName ] = {
- import: normalizeJoin(baseDir, `node_modules/@wordpress/${ packageName }` ),
+ import: normalizeJoin(
+ baseDir,
+ `node_modules/@wordpress/${ packageName }`
+ ),
library: {
name: [ 'wp', camelCaseDash( packageName ) ],
type: 'window',
@@ -148,7 +193,7 @@ module.exports = function( env = { environment: 'production', watch: false, buil
output: {
devtoolNamespace: 'wp',
filename: `[name]${ suffix }.js`,
- path: normalizeJoin(baseDir, `${ buildTarget }/js/dist` ),
+ path: normalizeJoin( baseDir, `${ buildTarget }/js/dist` ),
},
plugins: [
...baseConfig.plugins,
@@ -158,17 +203,17 @@ module.exports = function( env = { environment: 'production', watch: false, buil
combinedOutputFile: `../../assets/script-loader-packages${ suffix }.php`,
} ),
new CopyWebpackPlugin( {
- patterns: [
- ...vendorCopies,
- ...cssCopies,
- ...phpCopies,
- ],
+ patterns: [ ...vendorCopies, ...cssCopies, ...phpCopies ],
} ),
],
};
if ( config.mode === 'development' ) {
- config.plugins.push( new LiveReloadPlugin( { port: process.env.WORDPRESS_LIVE_RELOAD_PORT || 35729 } ) );
+ config.plugins.push(
+ new LiveReloadPlugin( {
+ port: process.env.WORDPRESS_LIVE_RELOAD_PORT || 35729,
+ } )
+ );
}
return config;
diff --git a/tools/webpack/shared.js b/tools/webpack/shared.js
index 5aea4204d31a2..6c1397db40f09 100644
--- a/tools/webpack/shared.js
+++ b/tools/webpack/shared.js
@@ -20,7 +20,7 @@ const getBaseConfig = ( env ) => {
new TerserPlugin( {
extractComments: false,
} ),
- ]
+ ],
},
module: {
rules: [
@@ -32,10 +32,7 @@ const getBaseConfig = ( env ) => {
],
},
resolve: {
- modules: [
- baseDir,
- 'node_modules',
- ],
+ modules: [ baseDir, 'node_modules' ],
alias: {
'lodash-es': 'lodash',
},
@@ -70,15 +67,20 @@ const getBaseConfig = ( env ) => {
const stylesTransform = ( mode ) => ( content ) => {
return postcss( [
require( 'cssnano' )( {
- preset: mode === 'production' ? 'default' : [
- 'default',
- {
- discardComments: {
- removeAll: ! content.includes( 'Copyright' ) && ! content.includes( 'License' ),
- },
- normalizeWhitespace: false,
- },
- ],
+ preset:
+ mode === 'production'
+ ? 'default'
+ : [
+ 'default',
+ {
+ discardComments: {
+ removeAll:
+ ! content.includes( 'Copyright' ) &&
+ ! content.includes( 'License' ),
+ },
+ normalizeWhitespace: false,
+ },
+ ],
} ),
] )
.process( content, { from: 'src/app.css', to: 'dest/app.css' } )
@@ -87,10 +89,25 @@ const stylesTransform = ( mode ) => ( content ) => {
const normalizeJoin = ( ...paths ) => join( ...paths ).replace( /\\/g, '/' );
+const BUNDLED_PACKAGES = [
+ '@wordpress/dataviews',
+ '@wordpress/icons',
+ '@wordpress/interface',
+ '@wordpress/interactivity',
+ '@wordpress/sync',
+];
+const MODULES = [
+ '@wordpress/interactivity',
+ '@wordpress/interactivity-router',
+];
+const WORDPRESS_NAMESPACE = '@wordpress/';
module.exports = {
baseDir,
getBaseConfig,
normalizeJoin,
stylesTransform,
+ BUNDLED_PACKAGES,
+ MODULES,
+ WORDPRESS_NAMESPACE,
};
diff --git a/webpack.config.js b/webpack.config.js
index 5e7c88a2cfc60..963117a7a52de 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -2,6 +2,7 @@ const blocksConfig = require( './tools/webpack/blocks' );
const developmentConfig = require( './tools/webpack/development' );
const mediaConfig = require( './tools/webpack/media' );
const packagesConfig = require( './tools/webpack/packages' );
+const modulesConfig = require( './tools/webpack/modules' );
module.exports = function( env = { environment: "production", watch: false, buildTarget: false } ) {
if ( ! env.watch ) {
@@ -17,6 +18,7 @@ module.exports = function( env = { environment: "production", watch: false, buil
...developmentConfig( env ),
mediaConfig( env ),
packagesConfig( env ),
+ modulesConfig( env ),
];
return config;