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

Site Editor: Add a HMR & React Fast Refresh development mode #26922

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from

Conversation

Copy link
Member

@david-szabo97 david-szabo97 commented Nov 12, 2020

Description

Related to #23013. This PR only adds HMR to the Site Editor. Unfortunately, the original PR has been abandoned: #23013. Big-big-big thanks to @epiqueras for working on this, hopefully, we can get this to the finish line this time!

Original PR had a few issues that have been fixed here. Happy to introduce this to edit-post and all the other editors once we got this into stabilized and merged.

Background

Webpack Hot Module Replacement (HMR) is fundamentally at odds with the way we do script overriding in Gutenberg.

After lots of experimentation and failed attempts, I found that the best way to support it is to provide an alternative development mode on a per-page/app basis. For example, the site editor is its own page/app, and this PR enables an HMR mode for its development.

It should be trivial to reuse the same configurations for the post, widgets, and navigation editors.

I also included the new React Fast Refresh experience. The developer experience with this is fantastic. Even error resilience and recovery are flawless, both for runtime and syntax errors. It's nice to be able to iterate on a screen without resetting state.

Implementation

This PR introduces a new Webpack config for running the new dev server as an extension of the one provided by @wordpress/scripts.

Pages that wish to support an HMR mode, need to create a hot.js entry point like this PR does for edit-site. This entry point should initialize the page and load its root stylesheet.

On the server, we use a new config global, GUTENBERG_HMR, to determine whether to load the new script from the dev server or not. We manipulate this config variable effortlessly in the scripts introduced by this PR by using wp-env. It will enable it before running the new development mode, and disable it before running the old one. This makes it easy to switch between the two, but it does assume you are using wp-env to manage your environment.

How to test this?

  • Set up your environment using wp-env if you haven't yet.
  • Run npm run dev:edit-site.
  • Go to the site editor and see how it updates in response to your code changes without reloading.

Screenshots

gif

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@github-actions
Copy link

@github-actions github-actions bot commented Nov 12, 2020

Size Change: 0 B

Total Size: 1.19 MB

ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/annotations/index.js 3.8 kB 0 B
build/api-fetch/index.js 3.42 kB 0 B
build/autop/index.js 2.84 kB 0 B
build/blob/index.js 664 B 0 B
build/block-directory/index.js 8.72 kB 0 B
build/block-directory/style-rtl.css 943 B 0 B
build/block-directory/style.css 942 B 0 B
build/block-editor/index.js 133 kB 0 B
build/block-editor/style-rtl.css 11.3 kB 0 B
build/block-editor/style.css 11.3 kB 0 B
build/block-library/editor-rtl.css 9.04 kB 0 B
build/block-library/editor.css 9.04 kB 0 B
build/block-library/index.js 147 kB 0 B
build/block-library/style-rtl.css 8.1 kB 0 B
build/block-library/style.css 8.1 kB 0 B
build/block-library/theme-rtl.css 792 B 0 B
build/block-library/theme.css 793 B 0 B
build/block-serialization-default-parser/index.js 1.87 kB 0 B
build/block-serialization-spec-parser/index.js 3.06 kB 0 B
build/blocks/index.js 48 kB 0 B
build/components/index.js 171 kB 0 B
build/components/style-rtl.css 15.3 kB 0 B
build/components/style.css 15.3 kB 0 B
build/compose/index.js 9.95 kB 0 B
build/core-data/index.js 14.8 kB 0 B
build/data-controls/index.js 827 B 0 B
build/data/index.js 8.8 kB 0 B
build/date/index.js 11.2 kB 0 B
build/deprecated/index.js 768 B 0 B
build/dom-ready/index.js 571 B 0 B
build/dom/index.js 4.92 kB 0 B
build/edit-navigation/index.js 11.2 kB 0 B
build/edit-navigation/style-rtl.css 881 B 0 B
build/edit-navigation/style.css 885 B 0 B
build/edit-post/index.js 306 kB 0 B
build/edit-post/style-rtl.css 6.48 kB 0 B
build/edit-post/style.css 6.47 kB 0 B
build/edit-site/index.js 23.2 kB 0 B
build/edit-site/style-rtl.css 3.89 kB 0 B
build/edit-site/style.css 3.89 kB 0 B
build/edit-widgets/index.js 26.4 kB 0 B
build/edit-widgets/style-rtl.css 3.16 kB 0 B
build/edit-widgets/style.css 3.16 kB 0 B
build/editor/editor-styles-rtl.css 476 B 0 B
build/editor/editor-styles.css 478 B 0 B
build/editor/index.js 42.5 kB 0 B
build/editor/style-rtl.css 3.85 kB 0 B
build/editor/style.css 3.85 kB 0 B
build/element/index.js 4.62 kB 0 B
build/escape-html/index.js 735 B 0 B
build/format-library/index.js 6.86 kB 0 B
build/format-library/style-rtl.css 547 B 0 B
build/format-library/style.css 548 B 0 B
build/hooks/index.js 2.16 kB 0 B
build/html-entities/index.js 623 B 0 B
build/i18n/index.js 3.57 kB 0 B
build/is-shallow-equal/index.js 698 B 0 B
build/keyboard-shortcuts/index.js 2.54 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.1 kB 0 B
build/list-reusable-blocks/style-rtl.css 476 B 0 B
build/list-reusable-blocks/style.css 476 B 0 B
build/media-utils/index.js 5.32 kB 0 B
build/notices/index.js 1.81 kB 0 B
build/nux/index.js 3.42 kB 0 B
build/nux/style-rtl.css 671 B 0 B
build/nux/style.css 668 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.43 kB 0 B
build/priority-queue/index.js 790 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/reusable-blocks/index.js 3.06 kB 0 B
build/rich-text/index.js 13.3 kB 0 B
build/server-side-render/index.js 2.77 kB 0 B
build/shortcode/index.js 1.69 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 4.05 kB 0 B
build/viewport/index.js 1.86 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.22 kB 0 B

compressed-size-action

@@ -0,0 +1,68 @@
/**
Copy link
Member

@noahtallen noahtallen Nov 12, 2020

Choose a reason for hiding this comment

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

Should this config file going into the @wordpress/scripts package?

Copy link
Member

@gziolo gziolo Nov 13, 2020

Choose a reason for hiding this comment

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

I think we can skip @wordpress/scripts for the time being. The existing Gutenberg config is completely independent and way more complex 😅 See: https://github.com/WordPress/gutenberg/blob/master/webpack.config.js. My bet is that we should move all custom logic related to React Fast Refresh there first.

Copy link
Member

@gziolo gziolo left a comment

Thank you for looking at React Fast Refresh. Do you think it would be possible to find a general approach to all pages that use the block editor? This PR covers only the edit site page.

@@ -0,0 +1,68 @@
/**
Copy link
Member

@gziolo gziolo Nov 13, 2020

Choose a reason for hiding this comment

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

I think we can skip @wordpress/scripts for the time being. The existing Gutenberg config is completely independent and way more complex 😅 See: https://github.com/WordPress/gutenberg/blob/master/webpack.config.js. My bet is that we should move all custom logic related to React Fast Refresh there first.

// externalize it to take advantage of the
// middleware bootstrapping Core does
// for us.
'@wordpress/api-fetch': 'wp.apiFetch',
Copy link
Member

@gziolo gziolo Nov 13, 2020

Choose a reason for hiding this comment

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

It's interesting that it removes all externals, does it mean that all wp globals aren't exposed?

Copy link
Member Author

@david-szabo97 david-szabo97 Nov 18, 2020

Choose a reason for hiding this comment

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

It means it loads the bundled version instead of the externalized scripts. This makes sure all the changes are hot reloaded.

Copy link
Member Author

@david-szabo97 david-szabo97 Nov 18, 2020

Choose a reason for hiding this comment

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

I think we can skip @wordpress/scripts for the time being. The existing Gutenberg config is completely independent and way more complex 😅 See: https://github.com/WordPress/gutenberg/blob/master/webpack.config.js. My bet is that we should move all custom logic related to React Fast Refresh there first.

I don't think it's a good idea to mix the two config files. The dev config file is only used for the webpack-dev-server and nothing else throughout the build process. Therefore it would be better to keep them separated.

@david-szabo97
Copy link
Member Author

@david-szabo97 david-szabo97 commented Nov 18, 2020

Thank you for looking at React Fast Refresh. Do you think it would be possible to find a general approach to all pages that use the block editor? This PR covers only the edit site page.

Block editors are initialized by WordPress injecting inline scripts to the page. This prevents us from using HMR, that's why we have a separate hot.js file.

On the other hand, post editor is initialized in WordPress core: https://github.com/WordPress/WordPress/blob/5da8c0fcebf0b7cab6d1821be8f464f4428f942f/wp-admin/edit-form-blocks.php#L417-L425 which makes things even more complicated.

We can add HMR to Widgets and Navigation editor the same way we do for the Site editor, but the post editor is complicated since we don't have any filters that we could use to manipulate the initialization of the editor.

@gziolo
Copy link
Member

@gziolo gziolo commented Nov 18, 2020

It's a very challenging task to make React Fast Refresh work with WordPress in general. It might work at the moment to ignore the wp globals for the edit site but it raises the question of how far it can get. It makes it impossible to enable any plugins that reference wp.element or any other variable under wp global, in practice most of them either use those globals directly or use @wordpress/scripts which does it during bundling. Some functionalities might not work as well, like Block Directory integration (might not be enabled yet) or RichText formatting controls:

  • wp_enqueue_script( 'wp-format-library' );

    It's also unclear what happens to all modification applied to blocks on the server:
    wp_add_inline_script(
    'wp-blocks',
    sprintf( 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions( %s );', wp_json_encode( get_block_editor_server_block_settings() ) ),
    'after'
    );
    wp_add_inline_script(
    'wp-blocks',
    sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $post ) ) ),
    'after'
    );

    As you can see it isn't only wp.apiFetch but other modules as well.

@david-szabo97
Copy link
Member Author

@david-szabo97 david-szabo97 commented Nov 19, 2020

I'm pretty sure we have limitations on how far we can go. On the other hand, HMR works well if we are developing in the core. It'd save us a lot of time. Refreshing after all the changes vs refreshing after 10% of the changes.

We could run this as an experimental feature and see how useful it is. If it's breaking too many times then we can revert it anytime.

Base automatically changed from master to trunk Mar 1, 2021
@iandunn
Copy link
Member

@iandunn iandunn commented Mar 16, 2021

Not sure if this is useful, but devs from Vue, Preact, and Snowpack collaborated to make a bundler-independent HMR spec. It looks like the only implementation so far is Snowpack's, though.

#25077 is somewhat related, since that could -- but wouldn't have to -- involve switching to a modern bundler. That might make this easier (including beyond the Site Editor, so that plugins could use it too).

@gziolo
Copy link
Member

@gziolo gziolo commented Mar 17, 2021

See #28273 (comment). I have a working proof of concepts for React Fast Refresh with @wordpress/scripts for block development.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Full site editing
  
Awaiting triage
Development

Successfully merging this pull request may close these issues.

None yet

5 participants