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

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

Merged
merged 9 commits into from
May 7, 2022
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' ),
],
},
],
};
};
9 changes: 9 additions & 0 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,15 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht
- **Supports:** align (full, wide), anchor, inserter, spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, textColor

## Navigation Area

Define a navigation area for your theme. The navigation block associated with this area will be automatically displayed. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/navigation-area))

- **Name:** core/navigation-area
- **Category:** theme
- **Supports:** ~~html~~, ~~inserter~~
- **Attributes:** area

## Custom Link

Add a page, link, or another item to your navigation. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/navigation-link))
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
151 changes: 151 additions & 0 deletions packages/block-library/babel-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* 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.
*/
function createBabelPlugin( shouldProcessImport ) {
if ( ! shouldProcessImport ) {
shouldProcessImport = isImportDeclarationAnExperimentalBlock;
}
/**
* 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 } ) {
// process.env.npm_package_config_IS_GUTENBERG_PLUGIN is a string, not a boolean
if (
String( process.env.npm_package_config_IS_GUTENBERG_PLUGIN ) ===
'true'
) {
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
74 changes: 48 additions & 26 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 @@ -106,6 +115,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 @@ -121,23 +132,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 @@ -151,6 +156,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 Down Expand Up @@ -228,6 +238,21 @@ export const __experimentalGetCoreBlocks = () => [
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 @@ -267,22 +292,19 @@ export const registerCoreBlocks = (
export const __experimentalRegisterExperimentalCoreBlocks = process.env
.IS_GUTENBERG_PLUGIN
? ( { enableFSEBlocks } = {} ) => {
[
// Experimental blocks.
postAuthorName,
...( 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
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