Skip to content

Commit

Permalink
Declare blocks as __experimental in block.json to automate syncing Gu…
Browse files Browse the repository at this point in the history
…tenberg packages to WordPress (#40655)

* Declare blocks as __experimental in block.json to automate syncing Gutenberg packages to WordPress

* Only apply the plugin to block-library/src/index.js via babel.config.js

* Remove navigation area documentation that got added by accident

* Switch to replacing imports with null variable declarations

* Fix broken test and remove nav area documentation

* Update packages/block-library/src/index.js

Co-authored-by: Greg Ziółkowski <[email protected]>

* Add a changelog entry

Co-authored-by: Greg Ziółkowski <[email protected]>
  • Loading branch information
adamziel and gziolo committed May 7, 2022
1 parent c2c3c2d commit ef4da2c
Show file tree
Hide file tree
Showing 15 changed files with 342 additions and 31 deletions.
8 changes: 8 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,13 @@ module.exports = ( api ) => {
return {
presets: [ '@wordpress/babel-preset-default' ],
plugins: [ '@emotion/babel-plugin', 'babel-plugin-inline-json-import' ],
overrides: [
{
test: 'packages/block-library/src/index.js',
plugins: [
require.resolve( '@wordpress/block-library/babel-plugin' ),
],
},
],
};
};
4 changes: 4 additions & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancement

- Declare blocks as __experimental in block.json to automate syncing Gutenberg packages to WordPress ([#40655](https://github.com/WordPress/gutenberg/pull/40655)).

## 7.5.0 (2022-05-04)

## 7.4.0 (2022-04-21)
Expand Down
5 changes: 3 additions & 2 deletions packages/block-library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ To find out more about contributing to this package or Gutenberg as a whole, ple
// packages/block-library/src/index.js
import * as blinkingParagraph from './blinking-paragraph';

// Then add `blinkingParagraph` to either `__experimentalGetCoreBlocks()`
// or `__experimentalRegisterExperimentalCoreBlocks()`
// Then add `blinkingParagraph` to `getAllBlocks()`
// If it's experimental, add the following property to block.json:
__experimental: 'true';
```

2. Register your block in the `gutenberg_reregister_core_block_types()` function of the [`lib/blocks.php`](https://github.com/WordPress/gutenberg/blob/trunk/lib/blocks.php) file. Add it to the `block_folders` array if it's a [static block](https://developer.wordpress.org/block-editor/explanations/glossary/#static-block) or to the `block_names` array if it's a [dynamic block](https://developer.wordpress.org/block-editor/explanations/glossary/#dynamic-block).
Expand Down
154 changes: 154 additions & 0 deletions packages/block-library/babel-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* External dependencies
*/
const fs = require( 'fs' );

/**
* Internal dependencies
*/
const isBlockMetadataExperimental = require( './src/is-block-metadata-experimental' );

/**
* Creates a babel plugin that replaces experimental block imports with
* null variable declarations.
*
* For example:
* import * as experimentalBlock from "./experimental-block";
* On build becomes:
* const experimentalBlock = null;
*
* This ensures the dead code elimination removes the experimental blocks modules
* during the production build.
*
* For more context, see https://github.com/WordPress/gutenberg/pull/40655/
*
* @param {Function} shouldProcessImport Optional callback to decide whether a given import should be processed.
* @param {boolean} isGutenbergPlugin Whether to run the plugin.
*/
function createBabelPlugin( shouldProcessImport, isGutenbergPlugin ) {
if ( ! shouldProcessImport ) {
shouldProcessImport = isImportDeclarationAnExperimentalBlock;
}
if ( isGutenbergPlugin === undefined ) {
// process.env.npm_package_config_IS_GUTENBERG_PLUGIN is a string, not a boolean
isGutenbergPlugin =
String( process.env.npm_package_config_IS_GUTENBERG_PLUGIN ) ===
'true';
}
/**
* The babel plugin created by createBabelPlugin.
*
* @see createBabelPlugin.
* @param {import('@babel/core')} babel Current Babel object.
* @return {import('@babel/core').PluginObj} Babel plugin object.
*/
return function babelPlugin( { types: t } ) {
if ( isGutenbergPlugin ) {
return {};
}

return {
visitor: {
ImportDeclaration( path ) {
// Only process the experimental blocks.
if ( ! shouldProcessImport( path ) ) {
return;
}

// Get the imported variable name.
const namespaceSpecifier = path.node.specifiers.find(
( specifier ) =>
specifier.type === 'ImportNamespaceSpecifier'
);
const { name } = namespaceSpecifier.local;

path.replaceWith(
t.variableDeclaration( 'const', [
t.variableDeclarator(
t.identifier( name ),
t.nullLiteral()
),
] )
);
},
},
};
};
}

/**
* Tests whether an import declaration refers to an experimental block.
* In broad strokes, it's a block that says "__experimental" in its block.json file.
* For details, check the implementation.
*
* @param {Object} path Babel.js AST path representing the import declaration,
* @return {boolean} Whether the import represents an experimental block.
*/
function isImportDeclarationAnExperimentalBlock( path ) {
// Only look for wildcard imports like import * as a from "source":
const { node } = path;
const namespaceSpecifier = node.specifiers.find(
( specifier ) => specifier.type === 'ImportNamespaceSpecifier'
);
if ( ! namespaceSpecifier || ! namespaceSpecifier.local ) {
return;
}

// Only look for imports starting with ./ and without additional slashes in the path.
const importedPath = node.source.value;
if (
! importedPath ||
! importedPath.startsWith( './' ) ||
importedPath.split( '/' ).length > 2
) {
return false;
}

// Check the imported directory has a related block.json file.
const blockJsonPath = __dirname + '/src/' + importedPath + '/block.json';
if ( ! fs.existsSync( blockJsonPath ) ) {
return false;
}

// Read the block.json file related to this block
const { name } = namespaceSpecifier.local;
let blockJSONBuffer;
try {
blockJSONBuffer = fs.readFileSync( blockJsonPath );
} catch ( e ) {
process.stderr.write(
'Could not read block.json for the module "' +
importedPath +
'" imported under name "' +
name +
'" from path "' +
blockJsonPath +
'"'
);
throw e;
}
let blockJSON;
try {
blockJSON = JSON.parse( blockJSONBuffer );
} catch ( e ) {
process.stderr.write(
'Could not parse block.json for the module "' +
importedPath +
'" imported under name "' +
name +
'" read from path "' +
blockJsonPath +
'"'
);
throw e;
}
if ( ! isBlockMetadataExperimental( blockJSON ) ) {
return false;
}

return true;
}

const babelPlugin = createBabelPlugin();
babelPlugin.createBabelPlugin = createBabelPlugin;
module.exports = babelPlugin;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": "fse",
"name": "core/comment-author-avatar",
"title": "Comment Author Avatar (deprecated)",
"category": "theme",
Expand Down
76 changes: 49 additions & 27 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ import {
/**
* Internal dependencies
*/
// When IS_GUTENBERG_PLUGIN is set to false, imports of experimental blocks
// are transformed by packages/block-library/src/index.js as follows:
// import * as experimentalBlock from './experimental-block'
// becomes
// const experimentalBlock = null;
// This enables webpack to eliminate the experimental blocks code from the
// production build to make the final bundle smaller.
//
// See https://github.com/WordPress/gutenberg/pull/40655 for more context.
import * as archives from './archives';
import * as avatar from './avatar';
import * as audio from './audio';
Expand Down Expand Up @@ -107,6 +116,8 @@ import * as textColumns from './text-columns';
import * as verse from './verse';
import * as video from './video';

import isBlockMetadataExperimental from './is-block-metadata-experimental';

/**
* Function to register an individual block.
*
Expand All @@ -122,23 +133,17 @@ const registerBlock = ( block ) => {
};

/**
* Function to get all the core blocks in an array.
*
* @example
* ```js
* import { __experimentalGetCoreBlocks } from '@wordpress/block-library';
*
* const coreBlocks = __experimentalGetCoreBlocks();
* ```
* Function to get all the block-library blocks in an array
*/
export const __experimentalGetCoreBlocks = () => [
const getAllBlocks = () => [
// Common blocks are grouped at the top to prioritize their display
// in various contexts — like the inserter and auto-complete components.
paragraph,
image,
heading,
gallery,
list,
listItem,
quote,

// Register all remaining core blocks.
Expand All @@ -152,6 +157,7 @@ export const __experimentalGetCoreBlocks = () => [
code,
column,
columns,
commentAuthorAvatar,
cover,
embed,
file,
Expand Down Expand Up @@ -196,6 +202,10 @@ export const __experimentalGetCoreBlocks = () => [
postFeaturedImage,
postContent,
postAuthor,
postAuthorName,
postComment,
postCommentsCount,
postCommentsLink,
postDate,
postTerms,
postNavigationLink,
Expand All @@ -220,13 +230,29 @@ export const __experimentalGetCoreBlocks = () => [
commentsPaginationPrevious,
postComments,
postCommentsForm,
tableOfContents,
homeLink,
logInOut,
termDescription,
queryTitle,
postAuthorBiography,
];

/**
* Function to get all the core blocks in an array.
*
* @example
* ```js
* import { __experimentalGetCoreBlocks } from '@wordpress/block-library';
*
* const coreBlocks = __experimentalGetCoreBlocks();
* ```
*/
export const __experimentalGetCoreBlocks = () =>
getAllBlocks().filter(
( { metadata } ) => ! isBlockMetadataExperimental( metadata )
);

/**
* Function to register core blocks provided by the block editor.
*
Expand Down Expand Up @@ -266,23 +292,19 @@ export const registerCoreBlocks = (
export const __experimentalRegisterExperimentalCoreBlocks = process.env
.IS_GUTENBERG_PLUGIN
? ( { enableFSEBlocks } = {} ) => {
[
// Experimental blocks.
postAuthorName,
tableOfContents,
...( window.__experimentalEnableListBlockV2
? [ listItem ]
: [] ),

// Full Site Editing blocks.
...( enableFSEBlocks
? [
commentAuthorAvatar,
postComment,
postCommentsCount,
postCommentsLink,
]
: [] ),
].forEach( registerBlock );
const enabledExperiments = [
window.__experimentalEnableListBlockV2 ? 'list-v2' : null,
enableFSEBlocks ? 'fse' : null,
];
getAllBlocks()
.filter( ( { metadata } ) =>
isBlockMetadataExperimental( metadata )
)
.filter(
( { metadata: { __experimental } } ) =>
__experimental === true ||
enabledExperiments.includes( __experimental )
)
.forEach( registerBlock );
}
: undefined;
19 changes: 19 additions & 0 deletions packages/block-library/src/is-block-metadata-experimental.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Checks if the block is experimental based on the metadata loaded
* from block.json.
*
* This function is in a separate file and uses the older JS syntax so
* that it can be imported in both:
* – block-library/src/index.js
* – block-library/src/babel-plugin.js
*
* @param {Object} metadata Parsed block.json metadata.
* @return {boolean} Is the block experimental?
*/
module.exports = function isBlockMetadataExperimental( metadata ) {
return (
metadata &&
'__experimental' in metadata &&
metadata.__experimental !== false
);
};
1 change: 1 addition & 0 deletions packages/block-library/src/list-item/block.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": "list-v2",
"name": "core/list-item",
"title": "List item",
"category": "text",
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/post-author-name/block.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": true,
"name": "core/post-author-name",
"title": "Post Author Name",
"category": "theme",
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/post-comment/block.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": "fse",
"name": "core/post-comment",
"title": "Post Comment (deprecated)",
"category": "theme",
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/post-comments-count/block.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": "fse",
"name": "core/post-comments-count",
"title": "Post Comments Count",
"category": "theme",
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/post-comments-link/block.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": "fse",
"name": "core/post-comments-link",
"title": "Post Comments Link",
"category": "theme",
Expand Down
Loading

0 comments on commit ef4da2c

Please sign in to comment.