WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.51k stars 4.2k forks source link

Build Tools: Reduce redundancy (and file size) for block registration #42327

Open Chrico opened 2 years ago

Chrico commented 2 years ago

What problem does this address?

Right now Gutenberg allows to register via block.json-file the Blocks on server side. This data is printed as inline script in /lib/experimental/editor-settings.php#L71 to wp.blocks.unstable__bootstrapServerSideBlockDefinitions().

On client side, this function is being used to merge the client side code with the server side code, to be in sync. A common usage is:

client side

/**
 * WordPress dependencies
 */
import { postCommentsCount as icon } from '@wordpress/icons';

/**
 * Internal dependencies
 */
import metadata from './block.json';
import edit from './edit';

const { name } = metadata;
export { metadata, name };

export const settings = {
    edit,
    icon,
};

while the exported data of this index.js is being used in the end in registerBlockType( { name, ...metadata }, settings );.

There is some data redundancy on client side when the block.json-file is loaded again via import metadata from './block.json' and part of the final JS build, while the data is already loaded on server side, localized and availble.

What is your proposed solution?

The proposed solution is to not import the complete block.json again and build into the final JS file. Instead it is enough to simply have:

registerBlockType({name: blockName}, settings);

The rest of the block.json metadata is filled up from localized data comming from server side.

I'm wondering if there is any chance to still have something like import {name} from './block.json'; and optimize the build script to strip the JSON data down to just contain the {name: 'core/....'} assigned to the blockName-variable.

Additional info

Doing some quick sum up of block.json-files (94 files) from /block-library/blocks/ brings us to nearly 100 Kb (not minified) of JSON data which is build into the final JS file:

\gutenberg\build\block-library\blocks> du -ac --bytes  | grep ".json$" | awk '{ print; total += $1 }; END { print "total: ", total, " Bytes" }'    

550     ./archives/block.json
998     ./audio/block.json
986     ./avatar/block.json
487     ./block/block.json
2069    ./button/block.json
741     ./buttons/block.json
428     ./calendar/block.json
731     ./categories/block.json
1114    ./code/block.json
1066    ./column/block.json
1302    ./columns/block.json
915     ./comment-author-avatar/block.json
1096    ./comment-author-name/block.json
937     ./comment-content/block.json
1023    ./comment-date/block.json
1059    ./comment-edit-link/block.json
901     ./comment-reply-link/block.json
495     ./comment-template/block.json
646     ./comments/block.json
962     ./comments-pagination/block.json
870     ./comments-pagination-next/block.json
400     ./comments-pagination-numbers/block.json
882     ./comments-pagination-previous/block.json
1342    ./comments-title/block.json
1812    ./cover/block.json
804     ./embed/block.json
1289    ./file/block.json
437     ./freeform/block.json
2549    ./gallery/block.json
1511    ./group/block.json
1482    ./heading/block.json
714     ./home-link/block.json
473     ./html/block.json
1953    ./image/block.json
749     ./latest-comments/block.json
1699    ./latest-posts/block.json
1475    ./list/block.json
544     ./list-item/block.json
510     ./loginout/block.json
1961    ./media-text/block.json
564     ./missing/block.json
564     ./more/block.json
2860    ./navigation/block.json
1215    ./navigation-link/block.json
1186    ./navigation-submenu/block.json
455     ./nextpage/block.json
775     ./page-list/block.json
1249    ./paragraph/block.json
324     ./pattern/block.json
1240    ./post-author/block.json
881     ./post-author-biography/block.json
988     ./post-author-name/block.json
474     ./post-comment/block.json
1030    ./post-comments/block.json
767     ./post-comments-count/block.json
952     ./post-comments-form/block.json
845     ./post-comments-link/block.json
439     ./post-content/block.json
950     ./post-date/block.json
1071    ./post-excerpt/block.json
1017    ./post-featured-image/block.json
920     ./post-navigation-link/block.json
665     ./post-template/block.json
1035    ./post-terms/block.json
1304    ./post-title/block.json
946     ./preformatted/block.json
1456    ./pullquote/block.json
1214    ./query/block.json
483     ./query-no-results/block.json
958     ./query-pagination/block.json
861     ./query-pagination-next/block.json
853     ./query-pagination-numbers/block.json
873     ./query-pagination-previous/block.json
1027    ./query-title/block.json
1441    ./quote/block.json
1210    ./read-more/block.json
905     ./rss/block.json
1412    ./search/block.json
969     ./separator/block.json
465     ./shortcode/block.json
1283    ./site-logo/block.json
1083    ./site-tagline/block.json
1401    ./site-title/block.json
636     ./social-link/block.json
1736    ./social-links/block.json
507     ./spacer/block.json
3408    ./table/block.json
611     ./table-of-contents/block.json
843     ./tag-cloud/block.json
597     ./template-part/block.json
728     ./term-description/block.json
730     ./text-columns/block.json
1159    ./verse/block.json
1613    ./video/block.json
total:  97140  Bytes
gziolo commented 2 years ago

Import Assertions - proposal for syntax to import ES modules with assertions for JSON is at stage 3, which means it will become a standard feature of the language pretty soon. I hope that it gets a nice integration with bundlers and that all unused properties of the imported JSON files would get eliminated during the phase of code minification.

You are correct about the WordPress core and the fact that only the name is being used on the client. It's also reflected in the official documentation for building custom blocks:

import { registerBlockType } from '@wordpress/blocks';

import './style.scss';

import Edit from './edit';
import save from './save';
import metadata from './block.json';

registerBlockType( metadata.name, {
    /**
     * @see ./edit.js
     */
    edit: Edit,
    /**
     * @see ./save.js
     */
    save,
} );

Still, we don't have anything in place that would eliminate all other properties loaded from the block.json file.

It's more nuanced for the Gutenberg plugin, where we sometimes have to read some newly added fields from block.json until they are exposed from the server with wp.blocks.unstable__bootstrapServerSideBlockDefinitions() in all supported WordPress versions. The interesting bit is when you use the standalone version of the block editor (with mobile apps based on React Native, or in Storybook) then all block settings are sourced from block.json.

Concluding, it's definitely possible to remove unused code loaded from block.json in the context of WordPress core but it might be quite a challenge to do it in a fashion that accounts for the edge cases I mentioned above.

Chrico commented 2 years ago

Thanks for your feedback.

One question which comes up: Does your code example even work? When looking into the /packages/blocks/src/api/registration.js#L265-L269 function i can see, that only values are merged when you provide an object as first param to registerBlockType(). So your example should be:

import { registerBlockType } from '@wordpress/blocks';

import './style.scss';

import Edit from './edit';
import save from './save';
import metadata from './block.json';

registerBlockType( {name: metadata.name}, {
    /**
     * @see ./edit.js
     */
    edit: Edit,
    /**
     * @see ./save.js
     */
    save,
} );
gziolo commented 2 years ago

https://github.com/WordPress/gutenberg/blob/f744f4fe0eb7e09e611b40d29a929d7b3831e249/packages/blocks/src/api/registration.js#L238

The first param takes the block name (string) or the block metadata (object). Both examples would work the same after all.

Chrico commented 2 years ago

The first param takes the block name (string) or the block metadata (object). Both examples would work the same after all.

yes, but the sync with the server side settings via unstable__bootstrapServerSideBlockDefinitions() only works when blockNameOrMetadata is an object, right? :)

gziolo commented 2 years ago

The first param takes the block name (string) or the block metadata (object). Both examples would work the same after all.

yes, but the sync with the server side settings via unstable__bootstrapServerSideBlockDefinitions() only works when blockNameOrMetadata is an object, right? :)

It's called on the server first. If it's called from registerBlockType for the second time, it's doing some checks to ensure it doesn't override values set on the server.