Skip to content

Commit

Permalink
Update rule
Browse files Browse the repository at this point in the history
Based on improvements in WordPress/gutenberg#27301
  • Loading branch information
sirreal committed Nov 26, 2020
1 parent 3b74e81 commit 1cab570
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 190 deletions.
37 changes: 16 additions & 21 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,29 +392,24 @@ module.exports = {
// Force packages to declare their dependencies
'import/no-extraneous-dependencies': 'error',

'wpcalypso/wp-no-unsafe-features': [
'wpcalypso/no-unsafe-wp-apis': [
'error',
{
allowedImports: {
'@wordpress/block-editor': [
'__experimentalBlock',
'__experimentalInserterMenuExtension',
],
'@wordpress/components': [
'__experimentalAlignmentMatrixControl',
'__experimentalBoxControl',
'__experimentalInputControl',
'__experimentalRadio',
'__experimentalRadioGroup',
'__experimentalText',
'__experimentalToolbarItem',
'__experimentalTreeGrid',
'__experimentalTreeGridCell',
'__experimentalTreeGridRow',
],
'@wordpress/date': [ '__experimentalGetSettings' ],
'@wordpress/interface': [ '__experimentalMainDashboardButton' ],
},
'@wordpress/block-editor': [ '__experimentalBlock', '__experimentalInserterMenuExtension' ],
'@wordpress/components': [
'__experimentalAlignmentMatrixControl',
'__experimentalBoxControl',
'__experimentalInputControl',
'__experimentalRadio',
'__experimentalRadioGroup',
'__experimentalText',
'__experimentalToolbarItem',
'__experimentalTreeGrid',
'__experimentalTreeGridCell',
'__experimentalTreeGridRow',
],
'@wordpress/date': [ '__experimentalGetSettings' ],
'@wordpress/interface': [ '__experimentalMainDashboardButton' ],
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config-wpcalypso/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ module.exports = {
'wpcalypso/jsx-gridicon-size': 'error',
'wpcalypso/jsx-classname-namespace': 'error',
'wpcalypso/redux-no-bound-selectors': 'error',
'wpcalypso/wp-no-unsafe-features': 'error',
'wpcalypso/no-unsafe-wp-apis': 'error',

yoda: 'off',

Expand Down
45 changes: 45 additions & 0 deletions packages/eslint-plugin-wpcalypso/docs/rules/no-unsafe-wp-apis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!-- eslint-disable wpcalypso/no-unsafe-wp-apis -->

# Prevent unsafe API usage (no-unsafe-wp-apis)

Prevent unsafe APIs from `@wordpress/*` packages from being imported.

This includes experimental and unstable APIs which are expected to change and likely to cause issues in application code.
See the [documentation](https://github.com/WordPress/gutenberg/blob/master/docs/contributors/coding-guidelines.md#experimental-and-unstable-apis).

> **There is no support commitment for experimental and unstable APIs.** They can and will be removed or changed without advance warning, including as part of a minor or patch release. As an external consumer, you should avoid these APIs.
>
>
> - An **experimental API** is one which is planned for eventual public availability, but is subject to further experimentation, testing, and discussion.
> - An **unstable API** is one which serves as a means to an end. It is not desired to ever be converted into a public API.
## Rule details

Examples of **incorrect** code for this rule:

```js
import { __experimentalFeature } from '@wordpress/foo';
import { __unstableFeature } from '@wordpress/bar';
```

Examples of **correct** code for this rule:

```js
import { registerBlockType } from '@wordpress/blocks';
```

## Options

The rule can be configured via an object.
This should be an object where the keys are import package names and the values are arrays of allowed unsafe imports.

#### Example configuration

```json
{
"wpcalypso/no-unsafe-wp-apis": [
"error",
{ "@wordpress/block-editor": [ "__experimentalBlock" ] }
]
}
```

This file was deleted.

2 changes: 1 addition & 1 deletion packages/eslint-plugin-wpcalypso/lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ module.exports = {
'post-message-no-wildcard-targets': require( './post-message-no-wildcard-targets' ),
'redux-no-bound-selectors': require( './redux-no-bound-selectors' ),
'no-package-relative-imports': require( './no-package-relative-imports' ),
'wp-no-unsafe-features': require( './wp-no-unsafe-features' ),
'no-unsafe-wp-apis': require( './no-unsafe-wp-apis' ),
};
92 changes: 92 additions & 0 deletions packages/eslint-plugin-wpcalypso/lib/rules/no-unsafe-wp-apis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
type: 'problem',
meta: {
messages: {
noUnsafeFeatures: 'Usage of `{{importedName}}` from `{{sourceModule}}` is not allowed',
},
schema: [
{
type: 'object',
additionalProperties: false,
patternProperties: {
'^@wordpress\\/[a-zA-Z0-9_-]+$': {
type: 'array',
uniqueItems: true,
minItems: 1,
items: {
type: 'string',
pattern: '^(?:__experimental|__unstable)',
},
},
},
},
],
},
create( context ) {
/** @type {AllowedImportsMap} */
const allowedImports =
( context.options && typeof context.options[ 0 ] === 'object' && context.options[ 0 ] ) || {};
const reporter = makeListener( { allowedImports, context } );

return { ImportDeclaration: reporter };
},
};

/**
* @param {Object} _ options
* @param {AllowedImportsMap} _.allowedImports Allowed imports
* @param {import('eslint').Rule.RuleContext} _.context Context
*
* @returns {(node: Node) => void} Listener function
*/
function makeListener( { allowedImports, context } ) {
return function reporter( node ) {
if ( node.type !== 'ImportDeclaration' ) {
return;
}
if ( typeof node.source.value !== 'string' ) {
return;
}

const sourceModule = node.source.value.trim();

// Ignore non-WordPress packages
if ( ! sourceModule.startsWith( '@wordpress/' ) ) {
return;
}

const allowedImportNames = allowedImports[ sourceModule ] || [];

node.specifiers.forEach( ( specifierNode ) => {
if ( specifierNode.type !== 'ImportSpecifier' ) {
return;
}

const importedName = specifierNode.imported.name;

if (
! importedName.startsWith( '__unstable' ) &&
! importedName.startsWith( '__experimental' )
) {
return;
}

if ( allowedImportNames.includes( importedName ) ) {
return;
}

context.report( {
messageId: 'noUnsafeFeatures',
node: specifierNode,
data: {
sourceModule,
importedName,
},
} );
} );
};
}

/** @typedef {import('estree').Node} Node */
/** @typedef {Object<string, string[]|undefined>} AllowedImportsMap */
Original file line number Diff line number Diff line change
@@ -1,36 +1,23 @@
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
/**
* External dependencies
*/
import { RuleTester } from 'eslint';

const rule = require( '../wp-no-unsafe-features' );
const RuleTester = require( 'eslint' ).RuleTester;
/**
* Internal dependencies
*/
import rule from '../no-unsafe-wp-apis';

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const options = [
{
allowedImports: {
// Allowed
'@wordpress/safe': [
'__experimentalSafe',

// __unstable Cannot be allowed. Never intended for public use.
'__unstableIsNeverSafe',
],
// Nothing allowed
'@wordpress/quux': [],
},
},
];

new RuleTester( {
const ruleTester = new RuleTester( {
parserOptions: {
ecmaVersion: '2015',
sourceType: 'module',
ecmaVersion: 6,
},
} ).run( 'wp-no-unsafe-features', rule, {
} );

const options = [ { '@wordpress/package': [ '__experimentalSafe', '__unstableSafe' ] } ];

ruleTester.run( 'no-unsafe-wp-apis', rule, {
valid: [
{ code: "import _ from 'lodash';", options },
{ code: "import { map } from 'lodash';", options },
Expand All @@ -46,21 +33,34 @@ new RuleTester( {
{ code: "import _, { __unstableFoo } from './x';", options },
{ code: "import * as _ from './x';", options },

{ code: "import s from '@wordpress/safe';", options },
{ code: "import { feature } from '@wordpress/safe';", options },
{ code: "import { __experimentalSafe } from '@wordpress/safe';", options },
{ code: "import { feature, __experimentalSafe } from '@wordpress/safe';", options },
{ code: "import s, { __experimentalSafe } from '@wordpress/safe';", options },
{ code: "import * as s from '@wordpress/safe';", options },
{ code: "import s from '@wordpress/package';", options },
{ code: "import { feature } from '@wordpress/package';", options },
{
code: "import { __experimentalSafe } from '@wordpress/package';",
options,
},
{
code: "import { __unstableSafe } from '@wordpress/package';",
options,
},
{
code: "import { feature, __experimentalSafe } from '@wordpress/package';",
options,
},
{
code: "import s, { __experimentalSafe } from '@wordpress/package';",
options,
},
{ code: "import * as s from '@wordpress/package';", options },
],

invalid: [
{
code: "import { __experimentalUnsafe } from '@wordpress/safe';",
code: "import { __experimentalUnsafe } from '@wordpress/package';",
options,
errors: [
{
message: 'Usage of `__experimentalUnsafe` from `@wordpress/safe` is not allowed',
message: 'Usage of `__experimentalUnsafe` from `@wordpress/package` is not allowed',
type: 'ImportSpecifier',
},
],
Expand All @@ -86,21 +86,21 @@ new RuleTester( {
],
},
{
code: "import s, { __experimentalUnsafe } from '@wordpress/safe';",
code: "import s, { __experimentalUnsafe } from '@wordpress/package';",
options,
errors: [
{
message: 'Usage of `__experimentalUnsafe` from `@wordpress/safe` is not allowed',
message: 'Usage of `__experimentalUnsafe` from `@wordpress/package` is not allowed',
type: 'ImportSpecifier',
},
],
},
{
code: "import { __unstableIsNeverSafe } from '@wordpress/safe';",
code: "import { __unstableFeature } from '@wordpress/package';",
options,
errors: [
{
message: 'Usage of `__unstableIsNeverSafe` from `@wordpress/safe` is not allowed',
message: 'Usage of `__unstableFeature` from `@wordpress/package` is not allowed',
type: 'ImportSpecifier',
},
],
Expand Down
Loading

0 comments on commit 1cab570

Please sign in to comment.