Skip to content

Importing @wordpress/core-abilities requires additional delay before abilities are available for use #77253

@westonruter

Description

@westonruter

Description

The documentation for @wordpress/core-abilities says:

Simply import the package to initialize the WordPress abilities:
```js
import '@wordpress/core-abilities';
```

But this is not accurate because you actually have to wait for two REST API requests to finish:

  • /wp-json/wp-abilities/v1/categories?per_page=100&context=edit&_locale=user
  • /wp-json/wp-abilities/v1/abilities?per_page=100&context=edit&_locale=user

Once they finish, then calling getAbilities() will return the list of abilities and calls to executeAbility() will work. Otherwise, I have to add a setTimeout() to introduce a delay:

add_action( 'admin_enqueue_scripts', static function () {
	wp_register_script(
		'abilities-test',
		false,
		array(),
		false,
		array(
			'module_dependencies' => array(
				'@wordpress/core-abilities',
				'@wordpress/abilities'
			),
		)
	);
	wp_enqueue_script( 'abilities-test' );

	add_action( 'admin_print_footer_scripts', function () {
		wp_print_inline_script_tag(
			<<<'JS'
				import '@wordpress/core-abilities';
				import { getAbilities, executeAbility } from '@wordpress/abilities';
				setTimeout( 
					async () => {
						console.log( getAbilities() );
						console.info( await executeAbility( "core/get-site-info" ) );
					}, 
					1000 // 👈 👈 👈 Fails if zero. Timeout is not reliable.
				);
			JS,
			array( 'type' => 'module' )
		);
	} );
} );

It seems that the current @wordpress/core-abilities package is assuming all usage will be done via React and the data store. But it should be usable independently. For example, a Promise should be exported by @wordpress/core-abilities which can be awaited for before attempting to access abilities.

Claude's analysis:

Look at the last two lines of core-abilities/src/index.ts:

// Auto-initialize on import
initialize();

initialize() is called fire-and-forget — it's not awaited and the returned promise is discarded. So when your inline script imports @wordpress/abilities and calls getAbilities(), the two REST API fetches (/wp-abilities/v1/categories and /wp-abilities/v1/abilities) are still in-flight. The 1000ms setTimeout just happens to be long enough for those network calls to complete.

The proper fix would be for @wordpress/core-abilities to export its initialization promise so consumers can await it:

// In core-abilities/src/index.ts
export const ready: Promise< void > = initialize();

Then your inline script can do:

const { getAbilities, executeAbility } = await import( '@wordpress/abilities' );
const { ready } = await import( '@wordpress/core-abilities' );
await ready; // wait for REST API fetches to complete

console.log( getAbilities() ); // now populated

No setTimeout needed at all. The await import() resolves once the module is loaded, but that doesn't mean its async side-effects (the REST API calls) are done. Exporting ready gives callers a proper hook into that async initialization.

Additionally, it seems like the core abilities should actually be exported with PHP via the script_module_data_@wordpress/core-abilities filter. The abilities data would then be available in a SCRIPT[type=application/json] script which would eliminate the need to do two additional REST API calls every time you load a page which uses @wordpress/core-abilities.

Step-by-step reproduction instructions

See plugin code in description.

Screenshots, screen recording, code snippet

No response

Environment info

WordPress 7.1-alpha-62161-src (latest trunk) on wordpress-develop

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

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Metadata

Metadata

Assignees

Labels

[Package] Abilities/packages/abilities[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

Type

No type
No fields configured for issues without a type.

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions