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

New block created with @wordpress/scripts@28 don't show up on wordpress #62202

Closed
originaliko opened this issue Jun 1, 2024 · 73 comments
Closed
Labels
Developer Experience Ideas about improving block and theme developer experience Needs Dev Note Requires a developer note for a major WordPress release cycle [Priority] High Used to indicate top priority items that need quick attention [Tool] WP Scripts /packages/scripts [Type] Bug An existing feature does not function as intended

Comments

@originaliko
Copy link

Description

I tried to create a new plugin / block with create-block and the block is not available in the back-end. There's no build error, php error or javascript error.

Step-by-step reproduction instructions

1 : Create a new wordpress plugin "npx @wordpress/create-block test-plugin"
2: cd test-plugin
3 : npm start
3 : activate plugin test-plugin
4 : edit a new page in wp-admin
5 : try to add the new block "/test-plugin"
6: nothing append

step to correct

  • edit package.json set devedepencies "@wordpress/scripts" to "^27.9.0"
  • npm install
  • npm start
  • refresh and edit a page
  • add the new block "/test-plugin"

Screenshots, screen recording, code snippet

No response

Environment info

Wordpress 6.53.
@wordpress/scripts": "^28.0.0"
node 22.2

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

@originaliko originaliko added the [Type] Bug An existing feature does not function as intended label Jun 1, 2024
@balazske96
Copy link

+1

However if you put the block inside a template html file with it's block name it appears on the front end.

@t-hamano t-hamano added Needs Testing Needs further testing to be confirmed. [Tool] WP Scripts /packages/scripts labels Jun 2, 2024
@t-hamano
Copy link
Contributor

t-hamano commented Jun 2, 2024

Thanks for the report. I was able to reproduce this issue.

I built the block with scripts version 27 and 28 and found a difference in the contents of index.asset.php. react has been replaced with react-jsx-runtime.

version 27.9:

<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-i18n'), 'version' => 'd97fdb9bbdb79e4913a4');

version 28.0:

<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-i18n'), 'version' => 'b0e4f78143cfbd1f56d2');

If I manually change this to react, the block can be inserted into the editor, but a critical error occurs.

image

@t-hamano t-hamano removed the Needs Testing Needs further testing to be confirmed. label Jun 2, 2024
@realthemes
Copy link

With v28 scripts all my blocks in my custom block theme stop working, was there any drastic change that I had to take into account when moving from 27 to 28?

@t-hamano t-hamano added the [Priority] High Used to indicate top priority items that need quick attention label Jun 2, 2024
@erikyo
Copy link

erikyo commented Jun 2, 2024

Same here, after updating to v28 custom blocks completely disappeared.

node v20.13.1
npm 10.3.0

reverting to "@wordpress/scripts": "27.8.0" (my last know working version) fixes the issue

--- edit ---
As noticed by @t-hamano, it seems that the JSX development runtime is required but never enqueued in the editor. For that reason, the block will never be registered.

I just noticed that removing those lines:

case 'react/jsx-runtime':
case 'react/jsx-dev-runtime':
return 'ReactJSXRuntime';

solves the issue. Maybe this is something related to the-new-jsx-transform?

@akawsar711
Copy link

same problem here. If i replace pacakage-lock.json with 27.9.0 the block works but with 28.0.0 it disappears.

@mrleemon
Copy link
Contributor

mrleemon commented Jun 3, 2024

Same problem here. I downgraded to 27.9.0 to make my blocks work again.

@swissspidy
Copy link
Member

cc @youknowriad @Mamaduka who worked on / reviewed the original PR.

The way it's currently built is not compatible with WP < 6.6

@gziolo
Copy link
Member

gziolo commented Jun 3, 2024

The way it's currently built is not compatible with WP < 6.6

The problem is going to exist for quite some time because many plugins target older major versions of WordPress. In WP 6.5 and older there is no react-jsx-runtime so there is no automatic way to declare react as a dependency. I think it’s what happens for folks if they don’t reference React directly.

@youknowriad
Copy link
Contributor

Thanks for creating the issue and sharing your concern. It's clear that we overlooked a bit the impact of this change. Especially for the create-block package.

That said, the solution her is to actually stick to the old version of wp-scripts in the build tooling. Dev Tools and Production dependencies don't need to be updated at the same time. Plugin authors that already have a build tool in place don't need to touch their dependencies.

I think there's a couple of things we should do here:

1- First one is to fix @wordpress/create-block and stick to an old version of @wordpress/scripts in the generated code. At least until the release of WP 6.6 but maybe better until the release of WP 6.7 (Ensure the plugin support at least two WP versions)

2- Write a post on make/core that serves as a dev note quickly to explain the change and the potential impact on build tools.

@justlevine
Copy link

@youknowriad might I also suggest updating the CHANGELOG.md file?

@youknowriad
Copy link
Contributor

@justlevine The CHANGELOG already mentions the breaking change. Would you like to seem something else there?

@swissspidy
Copy link
Member

Use React's automatic runtime to transform JSX in the changelog doesn't really make it clear what the implications are when it comes to WordPress versions. It could link to the dev note or something.

@justlevine
Copy link

@justlevine The CHANGELOG already mentions the breaking change. Would you like to seem something else there?

If the minimum required WP version is changing to 6.6 (as I understood your comment to mean), this should be noted directly. Even if it was included in the PR description, I'd recommend this - more so since it seems to be a side effect that isnt called out explicitly in #61692

@anomiex
Copy link
Contributor

anomiex commented Jun 3, 2024

It's not just @wordpress/scripts. Updating @wordpress/dependency-extraction-webpack-plugin to v6 in existing projects breaks too.

I don't know whether updating the other packages but keeping @wordpress/dependency-extraction-webpack-plugin locked to v5 will break anything.

@stefanfisk
Copy link
Contributor

I also had to downgrade @wordpress/dependency-extraction-webpack-plugin because 6.0.0 broke my script deps. I'm using a plain @wordpress/babel-preset-default Babel config so I was quite surprised that the latest versions of these packages are not compatible with the latest version of WordPress.

@gziolo
Copy link
Member

gziolo commented Jun 4, 2024

The same issue is also being discussed on WordPress Slack (link requires registration at https://make.wordpress.org/chat/): https://wordpress.slack.com/archives/C02QB2JS7/p1717340775169699.

@acicovic
Copy link

acicovic commented Jun 4, 2024

In the same vein, what should package consumers do regarding @wordpress/babel-preset-default 8.0.0, which has the same Use React's automatic runtime to transform JSX changelog entry?

From some quick testing, it doesn't seem to break anything by itself, at least in our case.

@youknowriad
Copy link
Contributor

I've opened #62265 to add a clearer note to the different impacted packages per the discussion above. Thanks for the suggestions.

@erikyo
Copy link

erikyo commented Jun 4, 2024

To address this issue, it would be nice to create a postinstall script that checks the current versions of the installed packages.

What is needed is the post install script that parses the package.json and something akin to the engines field in package.json as described in the npm documentation, which specifies the versions of wordpress core and gutenberg that your package is compatible with.

In this case if I had specified in my package.json

wp:{ gutenberg: 17.7, core: 6.5 }

it should have thrown an error and said it is not compatible with my version of wp

What do you think? I think it would be easy to implement and would be extremely beneficial.

@acicovic
Copy link

@thelovekesh, I wasn't able to make the polyfill solution work. However, without any extra webpack config, just loading the file from the core directory worked for me in WP 5.5, like so:

if ( ! wp_script_is( 'react-jsx-runtime', 'registered' ) ) {
	wp_register_script(
		'react-jsx-runtime',
		'/wp-includes/js/dist/vendor/react-jsx-runtime.js',
		array( 'react' ),
		'18.2.0',
		true
	);
}

I'm trying to understand what the extra webpack config does and why we need it, since registering these files didn't seem to work in my case.

@jsnajdr
Copy link
Member

jsnajdr commented Sep 16, 2024

just loading the file from the core directory worked for me in WP 5.5

WP 5.5 doesn't have the /wp-includes/js/dist/vendor/react-jsx-runtime.js file, it's been added only 4 months ago in #61692. That's why you need to polyfill it.

The react-jsx-runtime script can also be polyfilled by the Gutenberg plugin, then it will point to a plugins/gutenberg location.

If possible, you should avoid harcoding <script> tags that load the script for a given location, because the location is not certain. Instead, the server-side PHP code should generate the script tag with wp_enqueue_script( 'react-jsx-runtime' ).

@acicovic
Copy link

Somehow this was working for me, but @jsnajdr, thanks for pointing this out, what you say makes sense.

@acicovic
Copy link

It seems that many people opt to stay on the old branch instead of adopting a polyfill solution, and I understand that.

However, the old branch doesn't receive security updates introduced later, which becomes problematic.

@TobiasBg, have you actually managed to make this work? I visited your repo and couldn't find the code.

@jsnajdr
Copy link
Member

jsnajdr commented Sep 17, 2024

Seeing how @acicovic and others struggle with adding a react-jsx-runtime polyfill to their plugins makes me think that the current situation with @wordpress/scripts using the new JSX transform by default is unacceptable.

We always encourage people to use the latest version of WordPress and everything related. It should be ok to use the latest version of @wordpress/scripts, too.

It's also common that plugins support several versions of WordPress back. Supporting only the very latest version is very rare if not nonexistent. Therefore, the standard version (i.e., the latest) of @wordpress/scripts should support creating standard (i.e., compatible few versions back) plugins. That's what the tool is here for.

Creating the react-jsx-runtime polyfill yourself is an expert task. You need to know how exactly the new React JSX runtime works and how to build it with webpack so that it's exposed on ReactJSXRuntime global. Very uncommon knowledge.

I don't know how to solve this yet. Currently the JSX createElement-like functions are also available in the react package, so @wordpress/scripts could get them from there. But with React 19 forward they won't be in that package anymore. For WordPress this is quite a big compatibility break. Normal users have the Babel transforms that hide the impl details from them, but in WordPress we're exposing the React APIs in a more direct way.

@thelovekesh
Copy link
Member

Maintaining backward compatibility can be challenging sometime for various reasons. In the current setup, everything should function correctly with the latest version of WordPress and the @wordpress/scripts package. If you're managing a plugin that supports multiple WordPress versions, having a little understanding of polyfills makes sense.

For those who need it, here is an example of a polyfill implementation: Polyfill Implementation.

Additionally, include this PHP code to ensure the polyfill is loaded correctly: PHP Code for Polyfill.

@acicovic
Copy link

@thelovekesh, thanks for the links, I'll take a look.

In the meantime, for anyone wanting to stay on the older version but get rid of the vulnerabilities, it's possible to add "puppeteer-core": "^23.1.0" in your package.json's overrides section:

	"overrides": {
		"puppeteer-core": "^23.1.0"
	},

You should test though that this change works well for your project.

@TobiasBg
Copy link
Contributor

@acicovic: Yes, I have the polyfill code working fine, but unfortunately, it's not yet in a released branch of my plugin, which is why it's not yet in that repo. Sorry :-( But it's pretty much what has been discussed above.

@acicovic
Copy link

The code I had written in my attempt to update is already pretty close to the examples I'm seeing here. So I can only assume that there's something specific in my config that doesn't work as expected, and that I can't see it. This is my first time messing with webpack configs, so this doesn't help either.

I think that @jsnajdr makes some great points. If many developers are postponing a pollyfil solution, what will happen when we're forced to go that route with React 19? I think there should be some official form of docs for that, and people who can help out on GitHub issues.

@jsnajdr
Copy link
Member

jsnajdr commented Sep 18, 2024

If you're managing a plugin that supports multiple WordPress versions, having a little understanding of polyfills makes sense.

More or less every WordPress plugin aims to support multiple versions, and I think it's not a reasonable ask that more or less everyone should understand the polyfills, be able to implement them themselves, or to be even aware that they are needed.

What if the @wordpress/scripts package itself added the common polyfills as part of the scaffolding and the build process it offers? Then a block author can install the latest @wordpress/scripts package, write only the value-adding code for their own block, and the package takes care of automatically producing a plugin that has some reasonable amount of backward-compatibility.

@johnhooks
Copy link
Contributor

What if the @wordpress/scripts package itself added the common polyfills as part of the scaffolding and the build process it offers?

I totally agree, this would be very helpful.

@gziolo
Copy link
Member

gziolo commented Sep 24, 2024

Would the following work for older versions of WordPress defined as an inline JS script with the react-jsx-runtime script handle?

<script>
globalThis.ReactJSXRuntime = {
    jsx: React.createElement,
    jsxs: React.createElement,
    Fragment: React.Fragment,
};
</script>

The alternative would be to copy and adjust the production version, which is 859 bytes:

https://unpkg.com/browse/[email protected]/cjs/react-jsx-runtime.production.min.js

'use strict';var f=require("react"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;

We could create a PHP file that conditionally enqueues such an inline script. It would live next to the webpack config in @wordpress/scripts and the build would copy it, if necessary, to the build folder and require_once from the asset file when needed.

@acicovic
Copy link

acicovic commented Oct 4, 2024

@gziolo, who would be the person that can give an answer to your question? Asking in case we can relay this to them more directly.

@jsnajdr
Copy link
Member

jsnajdr commented Oct 6, 2024

Would the following work for older versions of WordPress defined as an inline JS script with the react-jsx-runtime script handle?

Almost, but not quite 🙂 The API for jsx and createElement is similar, but significantly different. In how key and children are passed. createElement works like this:

createElement( 'div', { className: 'box', key: 'k' }, child1, child2, child3 );
// or
createElement( 'div', { className: 'box', key: 'k' }, [ child1, child2, child3 ] );

while jsx has children as one of the props and key is a separate argument:

jsx( 'div', { className: 'box', children: [ child1, child2, child3 ] }, 'k' );
// or for a single child
jsx( 'div', { className: 'box', children: child1 }, 'k' );

So, we'd need to do some extra work to implement the new runtime in terms of the old createElement. Other than that, the internal implementation are essentially identical. The checks and warnings in dev mode are slightly different, that's all I found.

@peterwilsoncc
Copy link
Contributor

@jsnajdr @gziolo Would something like this work?

import { createElement, Fragment as ReactFragment } from 'react';

const jsx = ( element, props = {}, key = '' ) => {
	let children = [];
	if ( props.children ) {
		children = Array.isArray( props.children ) ? props.children : [ props.children ];
		delete props.children;
	}

	if ( key ) {
		props.key = key;
	}

	return createElement( element, props, ...children );
};

const jsxs = ( element, props = {}, key = '' ) => {
	return jsx( element, props, key );
}

const Fragment = ReactFragment;

globalThis.ReactJSXRuntime = globalThis.ReactJSXRuntime || { jsx, jsxs, Fragment };

@jsnajdr
Copy link
Member

jsnajdr commented Oct 24, 2024

@peterwilsoncc Yes, but there is one additional caveat where we need to be careful. There is a difference between:

<div><br/><br/></div>

and

<div>{ [ <br/>, <br/> ] }</div> // such array is typically created with a .map

In the first syntax the children are "static" and they don't have to have keys. This syntax is transpiled to:

// classic runtime: children are spread
createElement( 'div', null, <br/>, <br/> )
// new runtime: uses jsxs function
jsxs( 'div', { children: [ <br/>, <br/> ] } )

With the second syntax children are checked for presence of a key. It's transpiled to:

// classic runtime: children are array
createElement( 'div', null, [ <br/>, <br/> ] )
// new runtime: uses jsx function
jsx( 'div', { children: [ <br/>, <br/> ] } )

We need to take this into account when mapping jsx/jsxs to createElement. Other than that, your polyfill should be fine.

@acicovic
Copy link

Hello! Just wanted to ask if there any news or work on this, also taking into account the recent release of React 19?

@gziolo
Copy link
Member

gziolo commented Dec 10, 2024

Hello! Just wanted to ask if there any news or work on this, also taking into account the recent release of React 19?

The changes to how JSX is handled were done in anticipation of changes in React 19. There are some other breaking changes coming in React 19, so the impact will have to be assessed separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Developer Experience Ideas about improving block and theme developer experience Needs Dev Note Requires a developer note for a major WordPress release cycle [Priority] High Used to indicate top priority items that need quick attention [Tool] WP Scripts /packages/scripts [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

No branches or pull requests