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

Add jsx-runtime entry for the new automatic JSX transform #34221

Closed
wants to merge 4 commits into from

Conversation

kevin940726
Copy link
Member

@kevin940726 kevin940726 commented Aug 23, 2021

Description

Related to #36142.

Part of the React 17 upgrade. Add jsx-runtime and jsx-dev-runtime entries in @wordpress/element to support the new JSX transform.

We can deprecate our own fork of @wordpress/babel-plugin-import-jsx-pragma and just use the official @babel/preset-react (which includes @babel/plugin-transform-react-jsx) for JSX transformation. Using jsx, jsxs and jsxDEV provides more debugging helpers and could potentially decrease the bundle sizes or speed up performance in the long term. More importantly, it plays more nicely with other libraries and tooling in the React/JS community (For instance, build tooling like SWC and esbuild, that don't support custom babel plugins).

How has this been tested?

  • Tests should pass.
  • React devtools should correctly show the components' names
  • Bundle size changes should be minimal

Types of changes

New feature

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • I've tested my changes with keyboard and screen readers.
  • 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 (please manually search all *.native.js files for terms that need renaming or removal).

@kevin940726 kevin940726 added [Package] Element /packages/element [Type] Feature New feature to highlight in changelogs. labels Aug 23, 2021
@github-actions
Copy link

github-actions bot commented Aug 23, 2021

Size Change: +491 B (0%)

Total Size: 1.04 MB

Filename Size Change
build/block-directory/index.min.js 6.16 kB -42 B (-1%)
build/block-editor/index.min.js 118 kB +82 B (0%)
build/block-library/blocks/navigation/style-rtl.css 1.66 kB +13 B (+1%)
build/block-library/blocks/navigation/style.css 1.65 kB +13 B (+1%)
build/block-library/index.min.js 150 kB +254 B (0%)
build/block-library/style-rtl.css 10.9 kB +13 B (0%)
build/block-library/style.css 11 kB +12 B (0%)
build/blocks/index.min.js 47 kB -55 B (0%)
build/components/index.min.js 209 kB +256 B (0%)
build/compose/index.min.js 10.1 kB -55 B (-1%)
build/core-data/index.min.js 12.3 kB +9 B (0%)
build/customize-widgets/index.min.js 11.1 kB -26 B (0%)
build/data/index.min.js 7.06 kB -29 B (0%)
build/edit-navigation/index.min.js 13.6 kB -1 B (0%)
build/edit-post/index.min.js 28.7 kB +88 B (0%)
build/edit-site/index.min.js 26.2 kB +7 B (0%)
build/edit-widgets/index.min.js 16.1 kB +24 B (0%)
build/editor/index.min.js 37.7 kB +24 B (0%)
build/format-library/index.min.js 5.38 kB +19 B (0%)
build/list-reusable-blocks/index.min.js 1.88 kB +24 B (+1%)
build/media-utils/index.min.js 2.89 kB +12 B (0%)
build/nux/index.min.js 2.05 kB +18 B (+1%)
build/plugins/index.min.js 1.78 kB -50 B (-3%)
build/primitives/index.min.js 940 B +19 B (+2%)
build/react-i18n/index.min.js 604 B -67 B (-10%) 👏
build/reusable-blocks/index.min.js 2.31 kB +26 B (+1%)
build/rich-text/index.min.js 10.6 kB +15 B (0%)
build/server-side-render/index.min.js 1.25 kB -65 B (-5%)
build/viewport/index.min.js 944 B -78 B (-8%)
build/widgets/index.min.js 6.3 kB +31 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 931 B
build/admin-manifest/index.min.js 1.09 kB
build/annotations/index.min.js 2.7 kB
build/api-fetch/index.min.js 2.19 kB
build/autop/index.min.js 2.08 kB
build/blob/index.min.js 459 B
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/style-rtl.css 13.8 kB
build/block-editor/style.css 13.8 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 58 B
build/block-library/blocks/audio/editor.css 58 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 474 B
build/block-library/blocks/button/editor.css 474 B
build/block-library/blocks/button/style-rtl.css 605 B
build/block-library/blocks/button/style.css 604 B
build/block-library/blocks/buttons/editor-rtl.css 315 B
build/block-library/blocks/buttons/editor.css 315 B
build/block-library/blocks/buttons/style-rtl.css 370 B
build/block-library/blocks/buttons/style.css 370 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 B
build/block-library/blocks/columns/editor-rtl.css 194 B
build/block-library/blocks/columns/editor.css 193 B
build/block-library/blocks/columns/style-rtl.css 474 B
build/block-library/blocks/columns/style.css 475 B
build/block-library/blocks/cover/editor-rtl.css 666 B
build/block-library/blocks/cover/editor.css 670 B
build/block-library/blocks/cover/style-rtl.css 1.23 kB
build/block-library/blocks/cover/style.css 1.23 kB
build/block-library/blocks/embed/editor-rtl.css 488 B
build/block-library/blocks/embed/editor.css 488 B
build/block-library/blocks/embed/style-rtl.css 400 B
build/block-library/blocks/embed/style.css 400 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 879 B
build/block-library/blocks/gallery/editor.css 876 B
build/block-library/blocks/gallery/style-rtl.css 1.7 kB
build/block-library/blocks/gallery/style.css 1.7 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 70 B
build/block-library/blocks/group/theme.css 70 B
build/block-library/blocks/heading/editor-rtl.css 152 B
build/block-library/blocks/heading/editor.css 152 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/home-link/style-rtl.css 247 B
build/block-library/blocks/home-link/style.css 247 B
build/block-library/blocks/html/editor-rtl.css 283 B
build/block-library/blocks/html/editor.css 284 B
build/block-library/blocks/image/editor-rtl.css 728 B
build/block-library/blocks/image/editor.css 728 B
build/block-library/blocks/image/style-rtl.css 482 B
build/block-library/blocks/image/style.css 487 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 63 B
build/block-library/blocks/list/style.css 63 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 488 B
build/block-library/blocks/media-text/style.css 485 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 474 B
build/block-library/blocks/navigation-link/editor.css 474 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation/editor-rtl.css 1.69 kB
build/block-library/blocks/navigation/editor.css 1.69 kB
build/block-library/blocks/navigation/view.min.js 2.52 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 310 B
build/block-library/blocks/page-list/editor.css 310 B
build/block-library/blocks/page-list/style-rtl.css 242 B
build/block-library/blocks/page-list/style.css 242 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 248 B
build/block-library/blocks/paragraph/style.css 248 B
build/block-library/blocks/post-author/editor-rtl.css 210 B
build/block-library/blocks/post-author/editor.css 210 B
build/block-library/blocks/post-author/style-rtl.css 182 B
build/block-library/blocks/post-author/style.css 181 B
build/block-library/blocks/post-comments-form/style-rtl.css 140 B
build/block-library/blocks/post-comments-form/style.css 140 B
build/block-library/blocks/post-comments/style-rtl.css 360 B
build/block-library/blocks/post-comments/style.css 359 B
build/block-library/blocks/post-content/editor-rtl.css 138 B
build/block-library/blocks/post-content/editor.css 138 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 398 B
build/block-library/blocks/post-featured-image/editor.css 398 B
build/block-library/blocks/post-featured-image/style-rtl.css 143 B
build/block-library/blocks/post-featured-image/style.css 143 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 378 B
build/block-library/blocks/post-template/style.css 379 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 60 B
build/block-library/blocks/post-title/style.css 60 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 361 B
build/block-library/blocks/pullquote/style.css 360 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 270 B
build/block-library/blocks/query-pagination/editor.css 262 B
build/block-library/blocks/query-pagination/style-rtl.css 168 B
build/block-library/blocks/query-pagination/style.css 168 B
build/block-library/blocks/query-title/editor-rtl.css 85 B
build/block-library/blocks/query-title/editor.css 85 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 169 B
build/block-library/blocks/quote/style.css 169 B
build/block-library/blocks/quote/theme-rtl.css 220 B
build/block-library/blocks/quote/theme.css 222 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 374 B
build/block-library/blocks/search/style.css 375 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 250 B
build/block-library/blocks/separator/style.css 250 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 462 B
build/block-library/blocks/site-logo/editor.css 464 B
build/block-library/blocks/site-logo/style-rtl.css 153 B
build/block-library/blocks/site-logo/style.css 153 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 165 B
build/block-library/blocks/social-link/editor.css 165 B
build/block-library/blocks/social-links/editor-rtl.css 812 B
build/block-library/blocks/social-links/editor.css 811 B
build/block-library/blocks/social-links/style-rtl.css 1.33 kB
build/block-library/blocks/social-links/style.css 1.33 kB
build/block-library/blocks/spacer/editor-rtl.css 307 B
build/block-library/blocks/spacer/editor.css 307 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 146 B
build/block-library/blocks/tag-cloud/style.css 146 B
build/block-library/blocks/template-part/editor-rtl.css 636 B
build/block-library/blocks/template-part/editor.css 635 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/editor-rtl.css 90 B
build/block-library/blocks/term-description/editor.css 90 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 1.29 kB
build/block-library/common.css 1.29 kB
build/block-library/editor-rtl.css 9.95 kB
build/block-library/editor.css 9.93 kB
build/block-library/reset-rtl.css 527 B
build/block-library/reset.css 527 B
build/block-library/theme-rtl.css 658 B
build/block-library/theme.css 663 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/components/style-rtl.css 15.7 kB
build/components/style.css 15.8 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 614 B
build/date/index.min.js 31.5 kB
build/deprecated/index.min.js 428 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.53 kB
build/edit-navigation/style-rtl.css 3.15 kB
build/edit-navigation/style.css 3.14 kB
build/edit-post/classic-rtl.css 492 B
build/edit-post/classic.css 494 B
build/edit-post/style-rtl.css 7.2 kB
build/edit-post/style.css 7.2 kB
build/edit-site/style-rtl.css 5.07 kB
build/edit-site/style.css 5.07 kB
build/edit-widgets/style-rtl.css 4.06 kB
build/edit-widgets/style.css 4.06 kB
build/editor/style-rtl.css 3.74 kB
build/editor/style.css 3.73 kB
build/element/index.min.js 3.17 kB
build/escape-html/index.min.js 517 B
build/format-library/style-rtl.css 668 B
build/format-library/style.css 669 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.59 kB
build/is-shallow-equal/index.min.js 501 B
build/jsx-dev-runtime/index.min.js 486 B
build/jsx-runtime/index.min.js 727 B
build/keyboard-shortcuts/index.min.js 1.49 kB
build/keycodes/index.min.js 1.25 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/notices/index.min.js 845 B
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/priority-queue/index.min.js 582 B
build/redux-routine/index.min.js 2.63 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/shortcode/index.min.js 1.48 kB
build/token-list/index.min.js 562 B
build/url/index.min.js 1.72 kB
build/warning/index.min.js 248 B
build/widgets/style-rtl.css 1.05 kB
build/widgets/style.css 1.05 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@kevin940726 kevin940726 marked this pull request as ready for review August 24, 2021 03:27
@kevin940726
Copy link
Member Author

Not sure if it's safe to update it in RN too. Might want to gather some feedbacks before doing so.

@gziolo gziolo added the [Package] Babel preset /packages/babel-preset-default label Aug 24, 2021
@@ -225,7 +225,7 @@
"build:packages": "npm run build:package-types && node ./bin/packages/build.js",
"build:package-types": "node ./bin/packages/validate-typescript-version.js && tsc --build",
"build:plugin-zip": "./bin/build-plugin-zip.sh",
"build": "npm run build:packages && wp-scripts build",
"build": "cross-env NODE_ENV=production npm run build:packages && wp-scripts build",
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure why we weren't doing this?

This let Babel knows that we're targeting production builds, and applies some prod-only enhancements. I guess it's because we didn't have any plugins that used this feature?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this was part of npm run build:packages internally no?

Copy link
Member Author

Choose a reason for hiding this comment

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

npm run build:packages is also used by npm run dev, so I think no.

Copy link
Member

@gziolo gziolo Aug 24, 2021

Choose a reason for hiding this comment

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

We use different environments in npm run build:packages:

  • main for build folders
  • module for build-module folders

I guess the current flow doesn't care whether it's a production or development env. We leave that part to webpack to decide.

As far as I can tell, the Babel preset for WordPress has a special handling only for the test env.

"@wordpress/browserslist-config": "file:../browserslist-config",
"@wordpress/element": "file:../element",
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think we need this as a dependency. Peer dependency, probably, but not a dependency.

Copy link
Member

Choose a reason for hiding this comment

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

Right, I don't remember why we set them as dependencies. Maybe for developers' convenience because they would need to add @wordpress/element as a dependency when using JSX anyway. I guess it's fine to remove, we just should mention it in the changelog.

};
}, {} ),
}
),
Copy link
Contributor

Choose a reason for hiding this comment

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

Changes here need to be backported to Core's webpack config later as well.

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

This is looking good for me, but this PR is probably worth having more eyes on.

@gziolo
Copy link
Member

gziolo commented Sep 2, 2021

I think that my comment #34221 (comment) is still valid. Do we really need to proxy all those JSX helpers through @wordpress/element? The way the plugin from the React team works is completely different than before. It doesn't bring createElement of Fragment exported from React anymore so we no longer have to proxy it through @wordpress/element. It probably would also align better with 3rd party React libraries that would use the same setup from now on.

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

The current Babel setup we have is very complex so we need to ensure that:

  • we can use all React development features when running npm run dev, so packages should be transpiled using jsx-dev-runtime
  • we use production version when running npm run build that is used when publishing packages to npm

@gziolo
Copy link
Member

gziolo commented Nov 5, 2021

We discussed this PR during the Core JS bi-weekly meeting this Tuesday:

https://wordpress.slack.com/archives/C5UNMSU4R/p1635865732011000
(link requires registration at https://make.wordpress.org/chat/)

I repeated my own concerns shared earlier:

I think that my comment #34221 (comment) is still valid. Do we really need to proxy all those JSX helpers through @wordpress/element? The way the plugin from the React team works is completely different than before. It doesn't bring createElement of Fragment exported from React anymore so we no longer have to proxy it through @wordpress/element. It probably would also align better with 3rd party React libraries that would use the same setup from now on.

We checked with @Mamaduka a few React component libraries to see how they consume JSX. It looks like all the examples we exercised still import createElement directly. We wanted to check whether libraries have already started using the recommended transform shipped with React 17. The assumption made was that at some point we will need to list react/jsx-runtime as an external or alias it to the one proposed in this PR. It doesn't seem to be a case yet, but we should expect that 3rd party developers might run into that issue sooner than later.

I'm even more inclined to keep all that JSX handling external to @wordpress/element since it isn't a part of React API. My understanding is that JSX is an optional tool to make writing React components easier. In practical terms, it means we could register react/jsx-runtime as a vendor script similar to how we do it for React:

function gutenberg_register_vendor_scripts( $scripts ) {
$suffix = SCRIPT_DEBUG ? '' : '.min';
$react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
gutenberg_register_vendor_script(
$scripts,
'react',
'https://unpkg.com/react@17.0.1/umd/react' . $react_suffix . '.js',
array( 'wp-polyfill' )
);
gutenberg_register_vendor_script(
$scripts,
'react-dom',
'https://unpkg.com/react-dom@17.0.1/umd/react-dom' . $react_suffix . '.js',
array( 'react' )
);
}

It would simplify the configuration in several places:

  • no need for new entry points in webpack config
  • we can remove custom overrides for importSource in Babel config
  • no need for proxy files in @wordpress/element

@kevin940726
Copy link
Member Author

It looks like all the examples we exercised still import createElement directly.

I think gutenberg is unique in a way that it's both a collection of libraries and an app. Each of the packages is a npm package, and it should have createElement as its jsx transformation target, this matches what third-party libraries are doing. However, for the actual plugin release, which is an app built by webpack, should probably use jsx-runtime as its transformation target.

This got me thinking that maybe the current way of doing things isn't ideal, we should instead separate apps and libraries from each others and apply different optimizations. But that's for another discussion.

In practical terms, it means we could register react/jsx-runtime as a vendor script similar to how we do it for React:

I failed to understand how this would work. jsx-runtime is, well, a runtime, which should only be used with a compiler (Babel for instance). If we register it as a vendor script in WP, then it means that we'll need to assign a global variable for it and ask developers to transpile their jsx to use that global variable we created. It seems overly complicated, and we're just shifting the troubles to the developers.

I don't think we should just tell the developers to use react/jsx-runtime directly either, as that would make react an implicit dependency rather than a direct dependency. Even though it works for most package managers and bundlers, however in my experience, implicit dependency doesn't play nice with some tools like Yarn PnP. Proxying the runtime under @wordpress/element doesn't prevent us from using react/jsx-runtime anyway either.

@gziolo
Copy link
Member

gziolo commented Nov 19, 2021

I tend to agree that react/jsx-runtime makes sense only for web apps and it looks like libraries should continue using createElement. I also confirmed in #28273 that we don't need to use react/jsx-runtime to have React Fast Refresh working with webpack 5 and React 17. It turned out that it works in Chrome and I was testing with Safari ...

@gziolo gziolo added the Needs Decision Needs a decision to be actionable or relevant label Nov 24, 2021
@youknowriad
Copy link
Contributor

I've been playing with tools like swc or esbuild and if we ever decide to move away from babel, we'll be facing an issue with the current way we do JSX.

  • Basically in the classic mode, these compilers expect React or a global in the scope while what we do right now is inject it using our babel-plugin-import-jsx-pragma. Meaning if we want to stay in classic mode, we'd have to write a similar plugin for any tool we use (which is not great as we'll be losing most performance optimizations)
  • The other approach could be to switch to the new jsx runtime for our packages which could allow us to change the module "source" to be imported.
  • The last approach would be to start requiring import * as Element from @wordpress/element` whenever we use JSX in a file.

I'm not entirely certain what's the best way forward for libraries using these tools (and there are already a ton). Any thoughts?

@jsnajdr
Copy link
Member

jsnajdr commented Dec 16, 2021

The new JSX runtime is fine for classic apps that bundle the React library and the JSX runtime into one webpack bundle, but for our setup where we externalize the react library and enqueue the react script it causes trouble.

While previously we could enqueue one react and one react-dom script and we had the entire React available, now we need to package and enqueue the tiny (few hundreds of bytes) jsx-runtime script, too.

The current PR can successfully externalize @wordpress/element/jsx-runtime import, but if there is a 3rd party library that uses the new transform and imports react/jsx-runtime, we don't direct it towards the jsx-runtime script: the react/jsx-runtime module will be bundled into the script that uses it, because there is no externalization rule for it.

What if we did the following. One, ship a new react-jsx-runtime script as part of Gutenberg and Core, alongside with the existing react and react-dom scripts. And transform imports like:

import element from 'react/jsx-runtime';

to be externalized to react-jsx-runtime.

Two, in @wordpress/element, reexport jsx, jsxs and Fragment bindings from react/jsx-runtime. These three bindings would be just another additional exports from the existing @wordpress/element package.

Third, configure Babel/SWC transpilation so that <div>hello</div> is transpiled as:

import { jsxs } from '@wordpress/element';
jsxs( 'div', null, 'hello' );

I.e., there is no @wordpress/element/jsx-runtime suffix, because in our world with externalized scripts that is undesirable. We don't want to create new packages because they are not as cheap as when you bundle it all with webpack.

It seems that the best we can get with Babel/SWC configuration is to insert an import:

import { jsxs } from '@wordpress/element/jsx-runtime';

because the /jsx-runtime suffix is hardcoded in Babel at least, but then we can configure the externals plugin to point that import to wp-element anyway.

Regarding SWC: if it can't support some JSX scenario we need, then learning Rust and submitting a patch sounds like a lot of fun, especially over the holidays? 🙂

Regarding libraries: in a few years, there will be probably many more libraries that use the new transform and import from react/jsx-runtime. At this moment, it's a not good deal for libraries because they would lose backward compatibility (no more React 16.x!) for a rather frivolous reason, and would get nothing in return, because the old createElement is still almost exactly the same thing as the new jsx/jsxs.

@gziolo
Copy link
Member

gziolo commented Dec 16, 2021

@jsnajdr very interesting ideas. I'm wondering if that can seamlessly work with the two-step build process that we have:

  1. We use Babel to generate build code for all individual packages.
  2. We use webpack to consume all transpiled WordPress packages as if they were installed from npm.

It means that in the WordPress packages shipped to npm we could have the following references to JSX runtime:

import { jsxs } from '@wordpress/element/jsx-runtime';

or

import { jsxs } from 'react/jsx-runtime';

In the step where webpack comes into play, we can set new externals (for now in Dependency Extraction Webpack Plugin) for react/jsx-runtime that point to new entry point react-jsx-runtime. Shouldn't we do the same for @wordpress/element/jsx-runtime?

I'm also curious how you would structure jsx-runtime.js inside @wordpress/element so it doesn't bundle JSX runtime twice in 3rd party apps consuming WordPress packages outside of WordPress core.

@jsnajdr
Copy link
Member

jsnajdr commented Dec 17, 2021

It means that in the WordPress packages shipped to npm we could have the following references to JSX runtime:

If the @wordpress/element NPM package exposes the jsx-runtime export, everything will be fine.

It becomes non-elegant only when you start externalizing all these packages and shipping them as wp_enqueue-able scripts. Because now the tiny jsx-runtime becomes kind of homeless and we need to create a new home for it, most likely a new react-jsx-runtime script. There's no longer one monolithic react script that would export one React object and would be available as window.React. The monolithic script might be bad for webpack tree shaking but it was good for WordPress scripts.

so it doesn't bundle JSX runtime twice in 3rd party apps

It will be simply a reexport from react/jsx-runtime. When bundling with webpack, this indirection will be entirely erased by webpack when producing the bundle. And there will be just one react/jsx-runtime module.

Yes, there will be actually two JSX runtimes in the bundle: the classic createElement and the new jsx/jsxs. But that will be the reality for most React apps for as long as they use at least one NPM package that ships JSX transpiled the old way.

But what if we transpiled JSX with the default config, importing from react/jsx-runtime instead of @wordpress/element? After all, React is guaranteed to be present, and the import is invisible to the programmer, just like the @babel/runtime/helpers are invisible. The main point of the new JSX transform is that it brings us closer to <jsx/> being a JS syntax like any other, without needing to import something we don't really use (`React from 'react').

I see only one reason to not do that: when there is a package that declares a @wordpress/element dependency:

"dependencies" : {
  "@wordpress/element": "^4.0.4"
}

and doesn't declare react, because it doesn't use it directly, then it's not really legal to do direct react imports in that package. Because the package manager is allowed to install react in a subdirectory:

node_modules/@wordpress/element/node_modules/react

but a module that's outside node_modules/@wordpress/element will not find it here. Yes, in practice the react module will always be hoisted to the top:

node_modules/@wordpress/element
node_modules/react

but that's not strictly guaranteed.

When externalizing to react and wp-element scripts, this objection doesn't exist.

@gziolo gziolo added [Status] In Progress Tracking issues with work in progress and removed Needs Decision Needs a decision to be actionable or relevant labels May 7, 2022
@ObliviousHarmony
Copy link
Contributor

Given that it has been quite some time since this pull request received any attention, I'm curious if a decision here was ever reached @gziolo? Being able to use the JSX transform would be a nice convenience in place of importing from @wordpress/element in every JSX file.

@gziolo
Copy link
Member

gziolo commented Mar 30, 2023

Given that it has been quite some time since this pull request received any attention, I'm curious if a decision here was ever reached @gziolo? Being able to use the JSX transform would be a nice convenience in place of importing from @wordpress/element in every JSX file.

As far as I remember, @jsnajdr shared some interesting ideas that could work for the project. It's mostly that the PR didn't get any updates since then.

There is no need to import createElement or Fragment in every file using JSX in the Gutenberg project with the existing Babel config. It also works the same way when using @wordpress/babel-preset-default outside of the project, so this PR is mostly about using jsx-runtime that React shipped as an official way along with React 17. It doesn't mean that approaches that projects used before had flaws. I'm curious how is adoption going for jsx-runtime as the last time I checked, it was difficult to find a project (that is usually a dependency) that had it integrated.

@kevin940726
Copy link
Member Author

Two, in @wordpress/element, reexport jsx, jsxs and Fragment bindings from react/jsx-runtime. These three bindings would be just another additional exports from the existing @wordpress/element package.

There's a reason why React decided to ship these bindings from a sub-path (react/jsx-runtime, react/jsx-dev-runtime) rather than directly from react: to prevent users from accidentally importing them in user code. I think it makes a lot of sense and I'd prefer us to follow that as well.

Third, configure Babel/SWC transpilation so that <div>hello</div> is transpiled as:

import { jsxs } from '@wordpress/element';
jsxs( 'div', null, 'hello' );

I don't think this is possible from the official plugins in any transpiler. We could write custom plugins for them but it will just make maintenance harder as we'll have to keep it up-to-date whenever there's a change. AFAIK, all projects that leverage custom JSX import use the default setup (emotion, preact, etc). Not following the community standard will likely make adopting any new feature harder and harder to land, just like how this PR is over two years ago already 😅 .

The current PR can successfully externalize @wordpress/element/jsx-runtime import, but if there is a 3rd party library that uses the new transform and imports react/jsx-runtime, we don't direct it towards the jsx-runtime script: the react/jsx-runtime module will be bundled into the script that uses it, because there is no externalization rule for it.

Will applying the externalization rule in the webpack config work? Is there anything I'm missing 🤔 ?

As I'll be away for some time, if anyone's interested in this PR, feel free to take it over or start a new PR!

@ObliviousHarmony
Copy link
Contributor

@gziolo

There is no need to import createElement or Fragment in every file using JSX in the Gutenberg project with the existing Babel config

This is only true if you're using Babel.

I'm curious how is adoption going for jsx-runtime as the last time I checked, it was difficult to find a project (that is usually a dependency) that had it integrated.

Do you have any specific projects in mind?

@ObliviousHarmony
Copy link
Contributor

As an aside, I don't think we can actually externalize react/jsx-runtime. It doesn't look like there is a UMD build available, and according to this issue, there likely won't be? The best we could do is provide @wordpress/external/jsx-runtime and encourage its usage over react/jsx-runtime, right?

On another note, now that Node v14 is the current LTS, should we use a subpath export instead of having separate package.json files? One concern here might be backward compatibility though; support for subpath exports wasn't added until Webpack 5.

@kevin940726
Copy link
Member Author

Oh, I don't mean actually externalizing jsx-runtime, but using alias to point react/jsx-runtime to @wordpress/element/jsx-runtime for third-party libs. Aliasing should be available in all major bundlers. The goal is to prevent react/jsx-runtime from bundling inside the build. IIRC, we already have the externalizing of @wordpress/jsx-runtime in place in this PR.

@ObliviousHarmony
Copy link
Contributor

Thanks for that clarification @kevin940726. It doesn't look like this PR aliases react/jsx-runtime or react/jsx-dev-runtime. Would that just require mapping those packages to [ 'wp', 'jsxRuntime' ] and [ 'wp', 'jsxDevRuntime' ] respectively?

@ObliviousHarmony
Copy link
Contributor

I would need to check with npm pack to be absolutely certain, but, I don't know if this package works correctly with pure ESM imports. Since Node mirrors browsers here, you can't actually import from @wordpress/element/jsx-runtime. You need to import from @wordpress/element/jsx-runtime.js, right? Given that the JSX is transpiled by the tool you're using, whether or not this works might be uncertain.

As far as I can tell, all of the other packages use of {package}/jsx-runtime use subpath exports to map the submodule to their JSX runtime. I haven't seen any cases where "exports" wasn't used. I think we might have to use one. I'm not totally sure what that looks like as far as including it in WordPress goes, so, I'll have to do some digging to figure that out too.

I think it might work with Webpack's module resolver, but, I'm not sure? I know it works with Webpack 5 because it supports subpath exports, but, I think otherwise it doesn't really matter. I'll put some time this week into digging further into all of this and playing with this PR both as the Gutenberg Plugin and as a consumed package downstream.

@kevin940726
Copy link
Member Author

Would that just require mapping those packages to [ 'wp', 'jsxRuntime' ] and [ 'wp', 'jsxDevRuntime' ] respectively?

I think so! I haven't tried that though 😅 .

I don't know if this package works correctly with pure ESM imports.

I think ESM is a whole different story though. We still have a long way to go to have full ESM support in our packages. But good point for thinking of this earlier! 👍

@Mamaduka
Copy link
Member

Performance improvements are coming to JSX in React 19. If I understand correctly, the "new" JSX runtime would be more efficient.

Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: kevin940726 <kevin940726@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: jsnajdr <jsnajdr@git.wordpress.org>
Co-authored-by: ObliviousHarmony <obliviousharmony@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@youknowriad
Copy link
Contributor

Merged in #61692

@youknowriad youknowriad deleted the add/jsx-runtime branch May 22, 2024 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Babel preset /packages/babel-preset-default [Package] Element /packages/element [Status] In Progress Tracking issues with work in progress [Type] Feature New feature to highlight in changelogs.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants