ESLint: Support private API component denylist#78451
Conversation
There was a problem hiding this comment.
I'm going to say this doesn't need a changelog entry because it's just adding a new component to the ruleset that happens to be private.
There was a problem hiding this comment.
I was going to say this feels like a worthwhile thing to note as closing a gap, but I suppose this doesn't really impact the typical external consumer of the ESLint plugin?
There was a problem hiding this comment.
Right, it won't matter to consumers because this rule is a black box, and there were no existing gaps that were closed. use-import-as is consumer-configurable and thus not a black box, but private API support was already added (and changelogged) earlier.
|
Size Change: 0 B Total Size: 7.98 MB ℹ️ View Unchanged
|
| const name = getImportedName( specifier ); | ||
|
|
||
| if ( denylistEntry && name === 'privateApis' ) { | ||
| privateApisSources.set( specifier.local.name, source ); |
There was a problem hiding this comment.
Do we have guarantees on the ordering that this would be called before the variable declarator visitor is called below?
There was a problem hiding this comment.
Yes. ESLint visits AST nodes in source order, so ImportDeclaration fires before any later VariableDeclarator. Since the destructuring consumes the imported privateApis, it always comes after the import in practice.
|
Flaky tests detected in ab42edd. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26183866776
|
| { | ||
| code: 'import { "VisuallyHidden" as WCVisuallyHidden } from \'@wordpress/components\';', | ||
| options, | ||
| }, |
There was a problem hiding this comment.
Removed support for this import style, as it was too much.
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
aduth
left a comment
There was a problem hiding this comment.
LGTM 👍 Also tested locally on some sample code changes and confirmed expected errors using denied components from private APIs.
| ], | ||
| }, | ||
| { | ||
| code: "import { privateApis } from '@wordpress/components'; import { unlock as open } from '../../lock-unlock'; const { Tabs } = open( privateApis );", |
There was a problem hiding this comment.
Heh, I would hope people wouldn't be doing this, but I guess it doesn't hurt to cover it 😄 (Though it does look like it adds some complexity to the implementation)
There was a problem hiding this comment.
Let's go ahead and remove this too to simplify 688b783
| * @param {import('estree').Expression|import('estree').PrivateIdentifier} key | ||
| * @return {string|null} Static name of an object pattern property key. | ||
| */ | ||
| function getPropertyName( key ) { |
There was a problem hiding this comment.
This doesn't feel particularly related to private APIs and isn't used anywhere in this file. Since we still have some general utility files in use-recommended-components.js, I wonder if it would make sense to keep there?
There was a problem hiding this comment.
Fair, but since this helper is now shared by both use-recommended-components and use-import-as, moving it back into use-recommended-components.js would require duplicating it.
I'm inclined to keep it here for now since it supports the private API destructuring flow, even if the helper itself is generic.
| if ( node.parent.type !== 'VariableDeclaration' ) { | ||
| return null; | ||
| } | ||
|
|
There was a problem hiding this comment.
A note that came up in AI-assisted review:
node.parent.type !== 'VariableDeclaration'is redundant. A VariableDeclarator's parent is always a VariableDeclaration per the ESTree spec, so the guard never fires. Harmless but removable.
It doesn't look like the TypeScript types are detailed enough to guarantee this, so maybe it's a good extra safety to keep.
| if ( node.parent.type !== 'VariableDeclaration' ) { | |
| return null; | |
| } |
There was a problem hiding this comment.
I was going to say this feels like a worthwhile thing to note as closing a gap, but I suppose this doesn't really impact the typical external consumer of the ESLint plugin?
What?
Adds private API unlock support to the
@wordpress/use-recommended-componentsESLint rule for denylisted components.Also extracts the private-API-related logic into a shared utility file, so it can be shared with other rules (currently
use-import-as).Why?
Some component usages come from
unlock( privateApis )rather than direct named imports. Those usages should be covered by the same denylist guidance as regular imports so the rule catches existing and future@wordpress/componentsusages consistently.Testing Instructions
Open a file with a
Tabsviolation and see that it's erroring correctly.