diff --git a/includes/core/classes/class-assets.php b/includes/core/classes/class-assets.php index 057414a4b..5bcc703f2 100644 --- a/includes/core/classes/class-assets.php +++ b/includes/core/classes/class-assets.php @@ -12,7 +12,9 @@ // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore +use GatherPress\Core\Block; use GatherPress\Core\Traits\Singleton; +use Error; /** * Class Assets. @@ -87,6 +89,8 @@ protected function setup_hooks(): void { add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'enqueue_block_assets', array( $this, 'enqueue_scripts' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'editor_enqueue_scripts' ) ); + add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_variation_assets' ) ); + add_action( 'init', array( $this, 'register_variation_assets' ) ); add_action( 'wp_head', array( $this, 'add_global_object' ), PHP_INT_MIN ); // Set priority to 11 to not conflict with media modal. add_action( 'admin_footer', array( $this, 'event_communication_modal' ), 11 ); @@ -418,14 +422,144 @@ protected function unregister_blocks(): array { * * @since 1.0.0 * - * @param string $asset The file name of the asset. + * @param string $asset The file name of the asset. + * @param ?string $path (Optional) The absolute path to the asset file + * or null to use the path based on the default naming scheme. * @return array An array containing asset-related data. */ - protected function get_asset_data( string $asset ): array { + protected function get_asset_data( string $asset, string $path = null ): array { + $path = $path ?? $this->path . sprintf( '%s.asset.php', $asset ); if ( empty( $this->asset_data[ $asset ] ) ) { - $this->asset_data[ $asset ] = require_once $this->path . sprintf( '%s.asset.php', $asset ); + $this->asset_data[ $asset ] = require_once $path; } return (array) $this->asset_data[ $asset ]; } + + /** + * Register all assets. + * + * @since 1.0.0 + * + * @return void + */ + public function register_variation_assets(): void { + array_map( + array( $this, 'register_asset' ), + Block::get_instance()->get_block_variations() + ); + } + + /** + * Enqueue all assets. + * + * @since 1.0.0 + * + * @return void + */ + public function enqueue_variation_assets(): void { + array_map( + array( $this, 'enqueue_asset' ), + Block::get_instance()->get_block_variations() + ); + } + + /** + * Register a new script and sets translated strings for the script. + + * @since 1.0.0 + * + * @param string $folder_name Slug of the block to register scripts and translations for. + * @param string $build_dir Name of the folder ro register assets from, relative to the plugins root directory. + * + * @return void + */ + protected function register_asset( string $folder_name, $build_dir = 'variations/' ): void { + $slug = sprintf( 'gatherpress-%s', $folder_name ); + $folders = sprintf( '%1$s%2$s', $build_dir, $folder_name ); + $dir = sprintf( '%1$s%2$s', $this->path, $folders ); + $path_php = sprintf( '%1$s/index.asset.php', $dir ); + $path_css = sprintf( '%1$s/index.css', $dir ); + $url_js = sprintf( '%s/index.js', $this->build ); + $url_css = sprintf( '%s/index.css', $this->build ); + + if ( ! $this->asset_exists( $path_php, $folder_name ) ) { + return; + } + + $asset = $this->get_asset_data( $folder_name, $path_php ); + + wp_register_script( + $slug, + $url_js, + $asset['dependencies'], + $asset['version'], + true + ); + + wp_set_script_translations( $slug, 'gatherpress' ); + + if ( $this->asset_exists( $path_css, $folder_name ) ) { + wp_register_style( + $slug, + $url_css, + array( 'global-styles' ), + $asset['version'], + 'screen' + ); + } + } + + /** + * Enqueue a script and a style with the same name, if registered. + * + * @since 1.0.0 + * + * @param string $folder_name Slug of the block to load the frontend scripts for. + * + * @return void + */ + protected function enqueue_asset( string $folder_name ): void { + $slug = sprintf( 'gatherpress-%s', $folder_name ); + wp_enqueue_script( $slug ); + + if ( wp_style_is( $slug, 'registered' ) ) { + wp_enqueue_style( $slug ); + } + } + + /** + * A better file_exists with built-in error handling. + * + * @since 1.0.0 + * + * @throws Error Throws error for non-existent file with given path, + * if this is a development environment, + * returns false for all other environments. + * + * @param string $path Absolute path to the file to check. + * @param string $name Name of the file. + * + * @return bool + */ + protected function asset_exists( string $path, string $name ): bool { + if ( ! file_exists( $path ) ) { + $error_message = sprintf( + /* Translators: %s Name of a block-asset */ + __( + 'You need to run `npm start` or `npm run build` for the "%s" block-asset first.', + 'gatherpress' + ), + $name + ); + + if ( in_array( wp_get_environment_type(), array( 'local', 'development' ), true ) ) { + throw new Error( esc_html( $error_message ) ); + } else { + // Should write to the \error_log( $error_message ); if possible. + return false; + } + } + return true; + } } diff --git a/includes/core/classes/class-autoloader.php b/includes/core/classes/class-autoloader.php index b1edef7f6..9616f622a 100644 --- a/includes/core/classes/class-autoloader.php +++ b/includes/core/classes/class-autoloader.php @@ -87,6 +87,7 @@ static function ( string $class_string = '' ): void { array_unshift( $structure, 'includes' ); switch ( $class_type ) { + case 'blocks': case 'commands': case 'settings': case 'traits': diff --git a/includes/core/classes/class-block.php b/includes/core/classes/class-block.php index 79789ff71..7c5dbab0e 100644 --- a/includes/core/classes/class-block.php +++ b/includes/core/classes/class-block.php @@ -28,6 +28,14 @@ class Block { */ use Singleton; + /** + * An array used to cache block variation names. + * + * @since 1.0.0 + * @var array + */ + protected array $block_variation_names = array(); + /** * Class constructor. * @@ -49,6 +57,8 @@ protected function __construct() { * @return void */ protected function setup_hooks(): void { + // Priority 9 needed to allow the Block_Variation(s) to register their assets on init:10, without worries. + add_action( 'init', array( $this, 'register_block_variations' ), 9 ); add_action( 'init', array( $this, 'register_block_patterns' ) ); // Priority 11 needed for block.json translations of title and description. add_action( 'init', array( $this, 'register_blocks' ), 11 ); @@ -74,6 +84,70 @@ public function register_blocks(): void { } } + /** + * Require files & instantiate block-variation classes. + * + * @return void + */ + public function register_block_variations(): void { + foreach ( $this->get_block_variations() as $block ) { + // Prepare namespaced class-name + // in the following shape: "GatherPress\Core\Blocks\Block_Variation" (example). + $name = join( + '\\', + array( + __NAMESPACE__, + 'Blocks', + $this->get_classname_from_foldername( $block ), + ) + ); + + if ( class_exists( $name ) ) { + $name::get_instance(); + } + } + } + + /** + * Get a list of subfolder names from the /build/variations/ directory. + * + * @since 1.0.0 + * + * @return string[] List of block-variations foldernames. + */ + public function get_block_variations(): array { + $variations_directory = sprintf( '%1$s/build/variations/', GATHERPRESS_CORE_PATH ); + + if ( ! file_exists( $variations_directory ) ) { + return array(); + } + + if ( empty( $this->block_variation_names ) ) { + $this->block_variation_names = array_values( + array_diff( + scandir( $variations_directory ), + array( '..', '.' ) + ) + ); + } + return array_filter( $this->block_variation_names ); + } + + /** + * Get class name from folder name. + * + * @todo maybe better in the Utility class? + * + * @param string $foldername String with name of a folder. + * + * @return string Class name that reflects the given foldername. + */ + protected static function get_classname_from_foldername( string $foldername ): string { + $foldername = basename( $foldername ); + + return ucwords( str_replace( '-', '_', $foldername ), '_' ); + } + /** * Register block patterns. * diff --git a/package-lock.json b/package-lock.json index cfa0149da..af39264e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9024,6 +9024,110 @@ "dev": true, "license": "MIT" }, + "node_modules/@wordpress/scripts/node_modules/copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/@wordpress/scripts/node_modules/copy-webpack-plugin/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wordpress/scripts/node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wordpress/scripts/node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wordpress/scripts/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, "node_modules/@wordpress/scripts/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9051,6 +9155,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@wordpress/scripts/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@wordpress/scripts/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11724,78 +11840,6 @@ "dev": true, "license": "MIT" }, - "node_modules/copy-webpack-plugin": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", - "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.1", - "globby": "^12.0.2", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 12.20.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", - "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.9", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/core-js": { "version": "3.38.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", @@ -12049,55 +12093,6 @@ "node": ">=12 || >=16" } }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 39eaf25ad..320835a33 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -25,5 +25,5 @@ parameters: ignoreErrors: # core/classes/class-setup.php # - # A dev-only error, which can occur if the gatherpress is symlinked into a WP instance or called via wp-env or Playground. + # A dev-only errors, which can occur if the gatherpress is symlinked into a WP instance or called via wp-env or Playground. - '#^Path in require_once\(\) "\./wp-admin/includes/upgrade\.php" is not a file or it does not exist\.$#' diff --git a/test/unit/php/includes/core/classes/class-test-block.php b/test/unit/php/includes/core/classes/class-test-block.php index 88ed29c52..c282bf129 100644 --- a/test/unit/php/includes/core/classes/class-test-block.php +++ b/test/unit/php/includes/core/classes/class-test-block.php @@ -11,12 +11,14 @@ use GatherPress\Core\Block; use PMC\Unit_Test\Base; use PMC\Unit_Test\Utility; +use WP_Block_Patterns_Registry; use WP_Block_Type_Registry; /** * Class Test_Block. * * @coversDefaultClass \GatherPress\Core\Block + * @group blocks */ class Test_Block extends Base { /** @@ -30,6 +32,18 @@ class Test_Block extends Base { public function test_setup_hooks(): void { $instance = Block::get_instance(); $hooks = array( + array( + 'type' => 'action', + 'name' => 'init', + 'priority' => 9, + 'callback' => array( $instance, 'register_block_variations' ), + ), + array( + 'type' => 'action', + 'name' => 'init', + 'priority' => 10, + 'callback' => array( $instance, 'register_block_patterns' ), + ), array( 'type' => 'action', 'name' => 'init', @@ -71,4 +85,109 @@ public function test_register_blocks(): void { $this->assertSame( $blocks, $expected ); } + + /** + * Coverage for register_block_variations. + * + * @covers ::register_block_variations + * + * @return void + + public function test_register_block_variations(): void { + + // $block_instance = Utility::get_hidden_static_property( 'GatherPress\Core\Blocks\Add_To_Calendar', 'instance' ); + // // var_export(Utility::has_property('GatherPress\Core\Block\Add_To_Calendar', 'instance') ); + + // // Assert that it's still null (meaning the singleton is not instantiated). + // $this->assertNull( $block_instance, 'Failed to assert, the block-variation singleton should not be instantiated yet.' ); + + // $instance = Block::get_instance(); + // // Register our block variations. + // $instance->register_block_variations(); + + // Assert that it's still null (meaning the singleton is not instantiated). + // $this->assertNotNull($block_instance, 'Failed to assert, the block-variation singleton should be instantiated now.'); + } */ + + /** + * Coverage for get_block_variations. + * + * @covers ::get_block_variations + * + * @return void + */ + public function test_get_block_variations(): void { + $instance = Block::get_instance(); + + $this->assertSame( + array( + // 'add-to-calendar', + ), + $instance->get_block_variations(), + 'Failed to assert, to get all block variations from the "/src" directory.' + ); + } + + /** + * Coverage for get_classname_from_foldername. + * + * @covers ::get_classname_from_foldername + * + * @return void + */ + public function test_get_classname_from_foldername(): void { + $instance = Block::get_instance(); + + $this->assertSame( + 'Unit_Test', + Utility::invoke_hidden_method( $instance, 'get_classname_from_foldername', array( '/src/variations/unit-test' ) ), + 'Failed to assert, to get class name from foldername.' + ); + } + + /** + * Coverage for register_block_patterns. + * + * @covers ::register_block_patterns + * + * @return void + */ + public function test_register_block_patterns(): void { + $instance = Block::get_instance(); + $block_patterns = array( + 'gatherpress/event-template', + 'gatherpress/venue-template', + 'gatherpress/venue-details', + ); + $block_pattern_registry = WP_Block_Patterns_Registry::get_instance(); + + // Clear out registered block patterns. + Utility::set_and_get_hidden_property( $block_pattern_registry, 'registered_patterns', array() ); + + // Register our block patterns. + $instance->register_block_patterns(); + + $expected = wp_list_pluck( $block_pattern_registry->get_all_registered(), 'name' ); + + $this->assertSame( $block_patterns, $expected ); + } + + /** + * Coverage for existence of pattern slugs in developer docs. + * + * @return void + */ + public function test_docs_contain_patterns(): void { + + $doc_file = file_get_contents( // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + sprintf( + '%s/docs/%s', + GATHERPRESS_CORE_PATH, + 'developer/blocks/hookable-patterns/README.md' + ) + ); + + $this->assertStringContainsString( '`gatherpress/event-template`', $doc_file ); + $this->assertStringContainsString( '`gatherpress/venue-template`', $doc_file ); + } } diff --git a/webpack.config.js b/webpack.config.js index 2b6896e69..eca3d21a7 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ /** * External Dependencies */ +const fs = require('fs'); const path = require('path'); /** @@ -8,6 +9,43 @@ const path = require('path'); */ const defaultConfig = require('@wordpress/scripts/config/webpack.config.js'); +/** + * Retrieves the entry points for variation JavaScript files located in the + * 'src/variations' directory. + * + * This function checks if the 'variations' directory exists in the current + * working directory. If it does, it reads the subdirectories within it and + * maps each variation's `index.js` file to an entry point, where the key + * is in the format `variations/{variation}/index` and the value is the + * path to the `index.js` file. + * + * @return {Object} An object where each key is a variation entry path + * (e.g., `variations/{variation}/index`) and each value + * is the corresponding path to the `index.js` file for + * that variation. Returns an empty object if the + * 'variations' directory does not exist. + */ +function getVariationEntries() { + const variationsDir = path.resolve(process.cwd(), 'src', 'variations'); + const entries = {}; + + if (!fs.existsSync(variationsDir)) { + return entries; + } + + const variationDirs = fs.readdirSync(variationsDir); + + for (const variation of variationDirs) { + const variationPath = path.join(variationsDir, variation); + entries[`variations/${variation}/index`] = path.join( + variationPath, + 'index.js' + ); + } + + return entries; +} + module.exports = [ ...defaultConfig, { @@ -30,6 +68,7 @@ module.exports = [ 'src/profile', 'style.scss' ), + ...getVariationEntries(), }, module: { ...defaultConfig[0].module,