roots/acorn

Bug: Bud's inline runtime is lost in Gutenberg editor when a block is pre-rendered

LachlanArthur opened this issue · 1 comments

Terms

Description

Context

I'm using log1x/acf-composer to create ACF Gutenberg blocks.

What's wrong?

When a block enqueues its own Bud bundle, the inline runtime is lost when loading the Gutenberg editor.

This is due to WP pre-rendering the blocks via internal calls to the WP REST API early in the page lifecycle:

Call stack
Roots\Acorn\Assets\Bundle->js (vendor\roots\acorn\src\Roots\Acorn\Assets\Bundle.php:83)
Roots\Acorn\Assets\Bundle->enqueueJs (vendor\roots\acorn\src\Roots\Acorn\Assets\Concerns\Enqueuable.php:64)
Roots\Acorn\Assets\Bundle->enqueue (vendor\roots\acorn\src\Roots\Acorn\Assets\Concerns\Enqueuable.php:84)
App\Blocks\Testimonials->enqueue (web\app\themes\base\app\Blocks\Testimonials.php:165)
Log1x\AcfComposer\Block->Log1x\AcfComposer\{closure:/app/vendor/log1x/acf-composer/src/Block.php:282-284} (vendor\log1x\acf-composer\src\Block.php:283)
acf_enqueue_block_type_assets (web\app\plugins\advanced-custom-fields-pro\pro\blocks.php:795)
acf_render_block (web\app\plugins\advanced-custom-fields-pro\pro\blocks.php:636)
acf_rendered_block (web\app\plugins\advanced-custom-fields-pro\pro\blocks.php:569)
acf_render_block_callback (web\app\plugins\advanced-custom-fields-pro\pro\blocks.php:502)
WP_Block->render (web\wp\wp-includes\class-wp-block.php:256)
render_block (web\wp\wp-includes\blocks.php:1051)
do_blocks (web\wp\wp-includes\blocks.php:1089)
WP_Hook->apply_filters (web\wp\wp-includes\class-wp-hook.php:308)
apply_filters (web\wp\wp-includes\plugin.php:205)
WP_REST_Posts_Controller->prepare_item_for_response (web\wp\wp-includes\rest-api\endpoints\class-wp-rest-posts-controller.php:1857)
WP_REST_Posts_Controller->get_item (web\wp\wp-includes\rest-api\endpoints\class-wp-rest-posts-controller.php:568)
WP_REST_Server->respond_to_request (web\wp\wp-includes\rest-api\class-wp-rest-server.php:1171)
WP_REST_Server->dispatch (web\wp\wp-includes\rest-api\class-wp-rest-server.php:1018)
rest_do_request (web\wp\wp-includes\rest-api.php:535)
rest_preload_api_request (web\wp\wp-includes\rest-api.php:2884)
array_reduce (web\wp\wp-includes\block-editor.php:635)
block_editor_rest_api_preload (web\wp\wp-includes\block-editor.php:635)
require (web\wp\wp-admin\edit-form-blocks.php:77)
{main} (web\wp\wp-admin\post.php:187)

When the custom block is pre-rendered, the JS is enqueued, and the inline runtime is flagged as having been output.

Then the actual page renders, the inline runtime is not output because the Bundle class remembers inlining it when rendering the block earlier.

What have you tried? / Temporary workaround

For now I'm keeping track of when the REST API callbacks have finished, and using that in each bundle's conditional callback:

function handlingRestCallback(int $increment = 0)
{
    static $depth = 0;
    $depth += $increment;
    return $depth > 0;
}

add_filter('rest_request_before_callbacks', function ($data) {
    handlingRestCallback(1);
    return $data;
});

add_filter('rest_request_after_callbacks', function ($data) {
    handlingRestCallback(-1);
    return $data;
});

And the block conditionally enqueues using that function:

\Roots\bundle('blocks/testimonials')->when(fn () => ! handlingRestCallback())->enqueue();

What insights have you gained?

The pre-rendering of blocks is essential for a good editing experience, so it can't be disabled.

Possible solutions

The might be a simpler way to detect that the bundle is being enqueued via an internal REST request, but the workaround above is the only way I've found.

Another workaround (which doesn't really solve the underlying problem) is to include all the block JS in a single editor bundle. This would prevent it from being enqueued during the pre-rendering step.

Steps To Reproduce

  1. Install log1x/acf-composer and create an example block wp acorn acf:block TestBlock
  2. Create a script entrypoint for the block resources/scripts/blocks/test-block.js and add it to the bud.config.mjs under a new bundle name:
    app.entry( {
      "blocks/test-block": [ "@scripts/blocks/test-block" ],
    } )
  3. Enqueue the block's bundle in the block class:
    public function enqueue()
    {
        \Roots\bundle('blocks/test-block')->enqueue();
    }
  4. Build the theme npm run build
  5. Create a page and add the block to it.
  6. Edit the page and check the source, the inline runtime is now missing

Expected Behavior

The inline runtime should be included in the Gutenberg editor if the theme has enqueued an editor script.

Actual Behavior

The inline runtime is missing if a block with its own enqueued bundle was pre-rendered.

Relevant Log Output

No response

Versions

Acorn v3.1.0

I seem to be having the same issue.

My steps to reproduce are the same, but the way I test is different.
Using this: https://github.com/roots/bud/blob/main/examples/wordpress-editor/src/example.plugin.js

When another bundle has been enqueued, the example linked above does not unregister blocks.
Removing bundle('blocks/test-block')->enqueue() from the ACF block solves the issue.

I have also tested your temporary workaround @LachlanArthur and that works great.