Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensions: add a new option to deliver an "Experimental" subset of blocks. #14104

Merged
merged 11 commits into from
Dec 19, 2019
Merged
22 changes: 15 additions & 7 deletions class.jetpack-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -1809,15 +1809,17 @@ private function partner_provision_error( $error ) {
* --slug: Specific slug to identify the block that overrides the one generated based on the title.
* --description: Allows to provide a text description of the block.
* --keywords: Provide up to three keywords separated by comma so users can find this block when they search in Gutenberg's inserter.
* --variation: Allows to decide whether the block should be a production block, experimental, or beta. Defaults to Beta when arg not provided.
*
* ## BLOCK TYPE EXAMPLES
*
* wp jetpack scaffold block "Cool Block"
* wp jetpack scaffold block "Amazing Rock" --slug="good-music" --description="Rock the best music on your site"
* wp jetpack scaffold block "Jukebox" --keywords="music, audio, media"
* wp jetpack scaffold block "Jukebox" --variation="experimental"
*
* @subcommand scaffold block
* @synopsis <type> <title> [--slug] [--description] [--keywords]
* @synopsis <type> <title> [--slug] [--description] [--keywords] [--variation]
*
* @param array $args Positional parameters, when strings are passed, wrap them in quotes.
* @param array $assoc_args Associative parameters like --slug="nice-block".
Expand Down Expand Up @@ -1853,6 +1855,11 @@ public function block( $args, $assoc_args ) {
? $assoc_args['slug']
: sanitize_title( $title );

$variation_options = array( 'production', 'experimental', 'beta' );
$variation = ( isset( $assoc_args['variation'] ) && in_array( $assoc_args['variation'], $variation_options, true ) )
? $assoc_args['variation']
: 'beta';

if ( preg_match( '#^jetpack/#', $slug ) ) {
$slug = preg_replace( '#^jetpack/#', '', $slug );
}
Expand Down Expand Up @@ -1939,15 +1946,15 @@ function( $keyword ) {
if ( empty( $files_written ) ) {
WP_CLI::log( esc_html__( 'No files were created', 'jetpack' ) );
} else {
// Load index.json and insert the slug of the new block in the production array
// Load index.json and insert the slug of the new block in its block variation array.
$block_list_path = JETPACK__PLUGIN_DIR . 'extensions/index.json';
$block_list = $wp_filesystem->get_contents( $block_list_path );
if ( empty( $block_list ) ) {
/* translators: %s is the path to the file with the block list */
WP_CLI::error( sprintf( esc_html__( 'Error fetching contents of %s', 'jetpack' ), $block_list_path ) );
} elseif ( false === stripos( $block_list, $slug ) ) {
$new_block_list = json_decode( $block_list );
$new_block_list->beta[] = $slug;
$new_block_list = json_decode( $block_list );
$new_block_list->{ $variation }[] = $slug;

// Format the JSON to match our coding standards.
$new_block_list_formatted = wp_json_encode( $new_block_list, JSON_PRETTY_PRINT ) . "\n";
Expand All @@ -1973,16 +1980,17 @@ function ( $matches ) {
esc_html__( 'Successfully created block %1$s with slug %2$s', 'jetpack' ) . ' 🎉' . "\n" .
"--------------------------------------------------------------------------------------------------------------------\n" .
/* translators: the placeholder is a directory path */
esc_html__( 'The files were created at %s', 'jetpack' ) . "\n" .
esc_html__( 'The files were created at %3$s', 'jetpack' ) . "\n" .
esc_html__( 'To start using the block, build the blocks with yarn run build-extensions', 'jetpack' ) . "\n" .
/* translators: the placeholder is a file path */
esc_html__( 'The block slug has been added to the beta list at %s', 'jetpack' ) . "\n" .
esc_html__( 'The block slug has been added to the %4$s list at %5$s', 'jetpack' ) . "\n" .
esc_html__( 'To load the block, add the constant JETPACK_BETA_BLOCKS as true to your wp-config.php file', 'jetpack' ) . "\n" .
jeherve marked this conversation as resolved.
Show resolved Hide resolved
/* translators: the placeholder is a URL */
"\n" . esc_html__( 'Read more at %s', 'jetpack' ) . "\n",
"\n" . esc_html__( 'Read more at %6$s', 'jetpack' ) . "\n",
$title,
$slug,
$path,
$variation,
$block_list_path,
'https://github.com/Automattic/jetpack/blob/master/extensions/README.md#develop-new-blocks'
) . '--------------------------------------------------------------------------------------------------------------------'
Expand Down
114 changes: 99 additions & 15 deletions class.jetpack-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,16 +346,12 @@ public static function get_preset( $preset ) {
* @return array A list of blocks: eg [ 'publicize', 'markdown' ]
*/
public static function get_jetpack_gutenberg_extensions_whitelist() {
$preset_extensions_manifest = self::preset_exists( 'index' ) ? self::get_preset( 'index' ) : (object) array();
$preset_extensions_manifest = self::preset_exists( 'index' )
? self::get_preset( 'index' )
: (object) array();
$blocks_variation = self::blocks_variation();

$preset_extensions = isset( $preset_extensions_manifest->production ) ? (array) $preset_extensions_manifest->production : array();

if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
$beta_extensions = isset( $preset_extensions_manifest->beta ) ? (array) $preset_extensions_manifest->beta : array();
return array_unique( array_merge( $preset_extensions, $beta_extensions ) );
}

return $preset_extensions;
return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation );
}

/**
Expand Down Expand Up @@ -585,14 +581,20 @@ public static function enqueue_block_editor_assets() {
wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
}

$rtl = is_rtl() ? '.rtl' : '';
$beta = Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
$blocks_dir = self::get_blocks_directory();
$rtl = is_rtl() ? '.rtl' : '';
$blocks_dir = self::get_blocks_directory();
$blocks_variation = self::blocks_variation();

if ( 'production' !== $blocks_variation ) {
$blocks_env = '-' . esc_attr( $blocks_variation );
} else {
$blocks_env = '';
}

$editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
$editor_style = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
$editor_script = plugins_url( "{$blocks_dir}editor{$blocks_env}.js", JETPACK__PLUGIN_FILE );
$editor_style = plugins_url( "{$blocks_dir}editor{$blocks_env}{$rtl}.css", JETPACK__PLUGIN_FILE );

$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$beta}.asset.php";
$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$blocks_env}.asset.php";
$editor_deps = array( 'wp-polyfill' );
if ( file_exists( $editor_deps_path ) ) {
$asset_manifest = include $editor_deps_path;
Expand Down Expand Up @@ -717,4 +719,86 @@ public static function block_classes( $slug = '', $attr, $extra = array() ) {

return implode( ' ', $classes );
}

/**
* Determine whether a site should use the default set of blocks, or a custom set.
* Possible variations are currently beta, experimental, and production.
*
* @since 8.1.0
*
* @return string $block_varation production|beta|experimental
*/
public static function blocks_variation() {
// Default to production blocks.
$block_varation = 'production';

if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
$block_varation = 'beta';
}

/*
* Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant.
*/
if ( Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' ) ) {
$block_varation = 'experimental';
}

/**
* Allow customizing the variation of blocks in use on a site.
*
* @since 8.1.0
*
* @param string $block_variation Can be beta, experimental, and production. Defaults to production.
*/
return apply_filters( 'jetpack_blocks_variation', $block_varation );
}

/**
* Get a list of extensions available for the variation you chose.
*
* @since 8.1.0
*
* @param obj $preset_extensions_manifest List of extensions available in Jetpack.
* @param string $blocks_variation Subset of blocks. production|beta|experimental.
*
* @return array $preset_extensions Array of extensions for that variation
*/
public static function get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ) {
$preset_extensions = isset( $preset_extensions_manifest->{ $blocks_variation } )
? (array) $preset_extensions_manifest->{ $blocks_variation }
: array();

/*
* Experimental and Beta blocks need the production blocks as well.
*/
if (
'experimental' === $blocks_variation
|| 'beta' === $blocks_variation
) {
$production_extensions = isset( $preset_extensions_manifest->production )
? (array) $preset_extensions_manifest->production
: array();

$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
}

/*
* Beta blocks need the experimental blocks as well.
*
* If you've chosen to see Beta blocks,
* we want to make all blocks available to you:
* - Production
* - Experimental
* - Beta
*/
if ( 'beta' === $blocks_variation ) {
$production_extensions = isset( $preset_extensions_manifest->experimental )
? (array) $preset_extensions_manifest->experimental
: array();

$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
}

return $preset_extensions;
}
}
9 changes: 8 additions & 1 deletion extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ Generally, all new extensions should start out as a beta.
- Once you've successfully beta tested your new extension, you can open new PR to make your extension live!
- Simply move the extension's slug out of the beta array and into the production array in `extensions/index.json`.

jeherve marked this conversation as resolved.
Show resolved Hide resolved
### Experimental Extensions

We also offer an "experimental" state for extensions. Those extensions will be made available to anyone having the `JETPACK_EXPERIMENTAL_BLOCKS` constant defined in `wp-config.php`.
jeherve marked this conversation as resolved.
Show resolved Hide resolved

### Testing

Run `yarn test-extensions [--watch]` to run tests written in [Jest](https://jestjs.io/en/).
Expand All @@ -69,7 +73,7 @@ Note that adding [Jest snapshot tests](https://jestjs.io/docs/en/snapshot-testin

We have a command in WP-CLI that allows to scaffold Jetpack blocks. Its syntax is as follows:

`wp jetpack scaffold <type> <title> [--slug] [--description] [--keywords]`
`wp jetpack scaffold <type> <title> [--slug] [--description] [--keywords] [--variation]`

**Currently the only `type` is `block`.**

Expand All @@ -79,6 +83,7 @@ We have a command in WP-CLI that allows to scaffold Jetpack blocks. Its syntax i
- **--slug**: Specific slug to identify the block that overrides the one generated base don the title.
- **--description**: Allows to provide a text description of the block.
- **--keywords**: Provide up to three keywords separated by a comma so users when they search for a block in the editor.
- **--variation**: Allows to decide whether the block should be a production block, experimental, or beta. Defaults to Beta when arg not provided.

### Files

Expand All @@ -102,6 +107,8 @@ Since it's added to the beta array, you need to load the beta blocks as explaine

`wp jetpack scaffold block "Jukebox" --keywords="music, audio, media"`

`wp jetpack scaffold block "Jukebox" --variation="experimental"`

### Can I use Jurassic Ninja to test blocks?

Yes! Just like any other changes in Jetpack, also blocks work in Jurassic Ninja.
Expand Down
24 changes: 17 additions & 7 deletions webpack.config.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,46 @@ function blockScripts( type, inputDir, presetBlocks ) {

const presetPath = path.join( __dirname, 'extensions', 'index.json' );
const presetIndex = require( presetPath );
const presetBlocks = _.get( presetIndex, [ 'production' ], [] );
const presetBetaBlocks = _.get( presetIndex, [ 'beta' ], [] );
const allPresetBlocks = [ ...presetBlocks, ...presetBetaBlocks ];
const presetProductionBlocks = _.get( presetIndex, [ 'production' ], [] );
const presetExperimentalBlocks = [
...presetProductionBlocks,
..._.get( presetIndex, [ 'experimental' ], [] ),
];
const presetBetaBlocks = [ ...presetExperimentalBlocks, ..._.get( presetIndex, [ 'beta' ], [] ) ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, when I hear "experimental", I think almost like it is "alpha" code and so seeing that experimental blocks are included in beta at first feels like the opposite of expectation.

With the block set meant to roll on special platforms (e.g. our Atomic infra), it makes sense since we don't want to roll beta out to them.

If there isn't a better word other than experimental, let's be very explicit that beta blocks includes experimental everywhere we discuss the concept or the constant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, "experimental" may not be the best word for it. That's all I could come up with though :\


// Helps split up each block into its own folder view script
const viewBlocksScripts = allPresetBlocks.reduce( ( viewBlocks, block ) => {
const viewBlocksScripts = presetBetaBlocks.reduce( ( viewBlocks, block ) => {
const viewScriptPath = path.join( __dirname, 'extensions', 'blocks', block, 'view.js' );
if ( fs.existsSync( viewScriptPath ) ) {
viewBlocks[ block + '/view' ] = [ viewSetup, ...[ viewScriptPath ] ];
}
return viewBlocks;
}, {} );

// Combines all the different blocks into one editor.js script
// Combines all the different production blocks into one editor.js script
const editorScript = [
editorSetup,
...blockScripts( 'editor', path.join( __dirname, 'extensions' ), presetBlocks ),
...blockScripts( 'editor', path.join( __dirname, 'extensions' ), presetProductionBlocks ),
];

// Combines all the different Experimental blocks into one editor.js script
const editorExperimentalScript = [
editorSetup,
...blockScripts( 'editor', path.join( __dirname, 'extensions' ), presetExperimentalBlocks ),
];

// Combines all the different blocks into one editor-beta.js script
const editorBetaScript = [
editorSetup,
...blockScripts( 'editor', path.join( __dirname, 'extensions' ), allPresetBlocks ),
...blockScripts( 'editor', path.join( __dirname, 'extensions' ), presetBetaBlocks ),
];

const extensionsWebpackConfig = getBaseWebpackConfig(
{ WP: true },
{
entry: {
editor: editorScript,
'editor-experimental': editorExperimentalScript,
'editor-beta': editorBetaScript,
...viewBlocksScripts,
},
Expand Down