@wordpress/eslint-plugin 25.2.1-next.v.202605131006.0 → 25.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/eslint-plugin",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.3.0",
|
|
4
4
|
"description": "ESLint plugin for WordPress development.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"@babel/eslint-parser": "^7.28.6",
|
|
45
45
|
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.0",
|
|
46
46
|
"@eslint/compat": "^2.0.0",
|
|
47
|
-
"@wordpress/babel-preset-default": "^8.
|
|
48
|
-
"@wordpress/prettier-config": "^4.
|
|
49
|
-
"@wordpress/theme": "^0.
|
|
47
|
+
"@wordpress/babel-preset-default": "^8.47.0",
|
|
48
|
+
"@wordpress/prettier-config": "^4.47.0",
|
|
49
|
+
"@wordpress/theme": "^0.14.0",
|
|
50
50
|
"cosmiconfig": "^7.0.0",
|
|
51
51
|
"eslint-config-prettier": "^10.0.0",
|
|
52
52
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"publishConfig": {
|
|
84
84
|
"access": "public"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "d653c5fd6161571a0c2ebde28553d6e25624eacc"
|
|
87
87
|
}
|
|
@@ -66,10 +66,6 @@ ruleTester.run( 'use-import-as', rule, {
|
|
|
66
66
|
code: "import { VisuallyHidden as WCVisuallyHidden } from '@wordpress/components';",
|
|
67
67
|
options,
|
|
68
68
|
},
|
|
69
|
-
{
|
|
70
|
-
code: 'import { "VisuallyHidden" as WCVisuallyHidden } from \'@wordpress/components\';',
|
|
71
|
-
options,
|
|
72
|
-
},
|
|
73
69
|
{
|
|
74
70
|
code: "import { Button, VisuallyHidden as WCVisuallyHidden } from '@wordpress/components';",
|
|
75
71
|
options,
|
|
@@ -95,9 +91,9 @@ ruleTester.run( 'use-import-as', rule, {
|
|
|
95
91
|
{
|
|
96
92
|
code: `
|
|
97
93
|
import { privateApis } from '@wordpress/components';
|
|
98
|
-
import { unlock
|
|
94
|
+
import { unlock } from '../../lock-unlock';
|
|
99
95
|
|
|
100
|
-
const { Badge: WCBadge = fallbackBadge } =
|
|
96
|
+
const { Badge: WCBadge = fallbackBadge } = unlock( privateApis );
|
|
101
97
|
`,
|
|
102
98
|
options,
|
|
103
99
|
},
|
|
@@ -161,17 +157,6 @@ ruleTester.run( 'use-import-as', rule, {
|
|
|
161
157
|
),
|
|
162
158
|
],
|
|
163
159
|
},
|
|
164
|
-
{
|
|
165
|
-
code: 'import { "VisuallyHidden" as Hidden } from \'@wordpress/components\';',
|
|
166
|
-
options,
|
|
167
|
-
errors: [
|
|
168
|
-
withSuggestions(
|
|
169
|
-
'`VisuallyHidden` from `@wordpress/components` must be imported as `WCVisuallyHidden`.',
|
|
170
|
-
'import { "VisuallyHidden" as WCVisuallyHidden } from \'@wordpress/components\';',
|
|
171
|
-
'Import as `WCVisuallyHidden`.'
|
|
172
|
-
),
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
160
|
{
|
|
176
161
|
code: "import { Button, VisuallyHidden } from '@wordpress/components';",
|
|
177
162
|
options,
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
1
|
import { RuleTester } from 'eslint';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Internal dependencies
|
|
8
|
-
*/
|
|
9
2
|
import rule, { ALLOWLIST, DENYLIST } from '../use-recommended-components';
|
|
10
3
|
|
|
11
4
|
const ruleTester = new RuleTester( {
|
|
@@ -27,10 +20,28 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
27
20
|
|
|
28
21
|
// Allowed @wordpress/ui components.
|
|
29
22
|
"import { Badge } from '@wordpress/ui';",
|
|
23
|
+
"import { Icon } from '@wordpress/ui';",
|
|
30
24
|
"import { Link } from '@wordpress/ui';",
|
|
31
25
|
"import { Stack } from '@wordpress/ui';",
|
|
32
26
|
"import { Text } from '@wordpress/ui';",
|
|
33
|
-
"import { Badge, Link, Stack, Text } from '@wordpress/ui';",
|
|
27
|
+
"import { Badge, Icon, Link, Stack, Tabs, Text } from '@wordpress/ui';",
|
|
28
|
+
|
|
29
|
+
// Unlocked private APIs are only checked for denied names.
|
|
30
|
+
"import { privateApis } from '@wordpress/components'; import { unlock } from '../../lock-unlock'; const { SomethingElse } = unlock( privateApis );",
|
|
31
|
+
`
|
|
32
|
+
import { privateApis } from '@wordpress/components';
|
|
33
|
+
import { unlock } from '../../lock-unlock';
|
|
34
|
+
|
|
35
|
+
function test() {
|
|
36
|
+
function unlock( value ) {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { Tabs } = unlock( privateApis );
|
|
41
|
+
|
|
42
|
+
return Tabs;
|
|
43
|
+
}
|
|
44
|
+
`,
|
|
34
45
|
],
|
|
35
46
|
|
|
36
47
|
invalid: [
|
|
@@ -76,6 +87,33 @@ ruleTester.run( 'use-recommended-components', rule, {
|
|
|
76
87
|
},
|
|
77
88
|
],
|
|
78
89
|
},
|
|
90
|
+
{
|
|
91
|
+
code: "import { Tabs, TabPanel } from '@wordpress/components';",
|
|
92
|
+
errors: [
|
|
93
|
+
{
|
|
94
|
+
message: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
message: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
code: "import { privateApis } from '@wordpress/components'; import { unlock } from '../../lock-unlock'; const { Tabs } = unlock( privateApis );",
|
|
103
|
+
errors: [
|
|
104
|
+
{
|
|
105
|
+
message: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
code: "import { privateApis as componentsPrivateApis } from '@wordpress/components'; import { unlock } from '../../lock-unlock'; const { Tabs: WCTabs } = unlock( componentsPrivateApis );",
|
|
111
|
+
errors: [
|
|
112
|
+
{
|
|
113
|
+
message: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
79
117
|
],
|
|
80
118
|
} );
|
|
81
119
|
|
package/rules/use-import-as.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createPrivateApisState,
|
|
3
|
+
trackPrivateApisSpecifier,
|
|
4
|
+
getPropertyName,
|
|
5
|
+
getUnlockDestructuring,
|
|
6
|
+
} = require( '../utils/private-apis' );
|
|
7
|
+
|
|
1
8
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
2
9
|
const rule = {
|
|
3
10
|
meta: {
|
|
@@ -34,8 +41,7 @@ const rule = {
|
|
|
34
41
|
typeof context.options[ 0 ] === 'object'
|
|
35
42
|
? context.options[ 0 ]
|
|
36
43
|
: {};
|
|
37
|
-
const
|
|
38
|
-
const trackedUnlockImports = new Set();
|
|
44
|
+
const privateApisState = createPrivateApisState();
|
|
39
45
|
|
|
40
46
|
return {
|
|
41
47
|
/** @param {import('estree').ImportDeclaration} node */
|
|
@@ -52,21 +58,24 @@ const rule = {
|
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
const importedName =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const importedName = specifier.imported.name;
|
|
62
|
+
trackPrivateApisSpecifier(
|
|
63
|
+
privateApisState,
|
|
64
|
+
specifier,
|
|
65
|
+
source,
|
|
66
|
+
!! sourceMap
|
|
67
|
+
);
|
|
59
68
|
|
|
60
69
|
if ( ! sourceMap ) {
|
|
61
70
|
return;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
if (
|
|
65
|
-
|
|
73
|
+
if ( ! sourceMap.hasOwnProperty( importedName ) ) {
|
|
74
|
+
return;
|
|
66
75
|
}
|
|
67
|
-
const localName = sourceMap[ importedName ];
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
const localName = sourceMap[ importedName ];
|
|
78
|
+
if ( specifier.local.name === localName ) {
|
|
70
79
|
return;
|
|
71
80
|
}
|
|
72
81
|
|
|
@@ -101,50 +110,31 @@ const rule = {
|
|
|
101
110
|
},
|
|
102
111
|
/** @param {import('estree').VariableDeclarator} node */
|
|
103
112
|
VariableDeclarator( node ) {
|
|
104
|
-
|
|
105
|
-
node
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
! isUnlockCall(
|
|
109
|
-
node.init,
|
|
110
|
-
context.sourceCode,
|
|
111
|
-
trackedUnlockImports
|
|
112
|
-
)
|
|
113
|
-
) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const privateApisIdentifier = node.init.arguments[ 0 ];
|
|
118
|
-
if ( privateApisIdentifier.type !== 'Identifier' ) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const source = privateApisSources.get(
|
|
123
|
-
privateApisIdentifier.name
|
|
113
|
+
const unlockDestructuring = getUnlockDestructuring(
|
|
114
|
+
node,
|
|
115
|
+
context.sourceCode,
|
|
116
|
+
privateApisState
|
|
124
117
|
);
|
|
125
|
-
if ( !
|
|
118
|
+
if ( ! unlockDestructuring ) {
|
|
126
119
|
return;
|
|
127
120
|
}
|
|
128
121
|
|
|
122
|
+
const { source, properties } = unlockDestructuring;
|
|
129
123
|
const sourceMap = importAsMap[ source ];
|
|
130
124
|
if ( ! sourceMap ) {
|
|
131
125
|
return;
|
|
132
126
|
}
|
|
133
127
|
|
|
134
|
-
|
|
135
|
-
if ( property.type !== 'Property' || property.computed ) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
128
|
+
properties.forEach( ( property ) => {
|
|
139
129
|
const importedName = getPropertyName( property.key );
|
|
140
|
-
if (
|
|
130
|
+
if (
|
|
131
|
+
! importedName ||
|
|
132
|
+
! sourceMap.hasOwnProperty( importedName )
|
|
133
|
+
) {
|
|
141
134
|
return;
|
|
142
135
|
}
|
|
143
136
|
|
|
144
137
|
const localName = sourceMap[ importedName ];
|
|
145
|
-
if ( ! localName ) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
138
|
|
|
149
139
|
const propertyLocalName = getPropertyLocalName(
|
|
150
140
|
property.value
|
|
@@ -189,16 +179,6 @@ const rule = {
|
|
|
189
179
|
},
|
|
190
180
|
};
|
|
191
181
|
|
|
192
|
-
/**
|
|
193
|
-
* @param {import('estree').ImportSpecifier} specifier
|
|
194
|
-
* @return {string} Imported name.
|
|
195
|
-
*/
|
|
196
|
-
function getImportedName( specifier ) {
|
|
197
|
-
return specifier.imported.type === 'Identifier'
|
|
198
|
-
? specifier.imported.name
|
|
199
|
-
: String( specifier.imported.value );
|
|
200
|
-
}
|
|
201
|
-
|
|
202
182
|
/**
|
|
203
183
|
* @param {import('estree').ImportSpecifier} specifier
|
|
204
184
|
* @param {import('eslint').SourceCode} sourceCode
|
|
@@ -209,52 +189,6 @@ function getImportSpecifierSuggestionText( specifier, sourceCode, localName ) {
|
|
|
209
189
|
return `${ sourceCode.getText( specifier.imported ) } as ${ localName }`;
|
|
210
190
|
}
|
|
211
191
|
|
|
212
|
-
/**
|
|
213
|
-
* @param {import('estree').CallExpression|import('estree').Expression|null} node
|
|
214
|
-
* @param {import('eslint').SourceCode} sourceCode
|
|
215
|
-
* @param {ReadonlySet<string>} trackedUnlockImports
|
|
216
|
-
* @return {node is import('estree').CallExpression} Whether this is an `unlock()` call with one argument.
|
|
217
|
-
*/
|
|
218
|
-
function isUnlockCall( node, sourceCode, trackedUnlockImports ) {
|
|
219
|
-
if (
|
|
220
|
-
node &&
|
|
221
|
-
node.type === 'CallExpression' &&
|
|
222
|
-
node.callee.type === 'Identifier' &&
|
|
223
|
-
node.arguments.length === 1
|
|
224
|
-
) {
|
|
225
|
-
if ( ! trackedUnlockImports.has( node.callee.name ) ) {
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const { references } = sourceCode.getScope( node.callee );
|
|
230
|
-
const reference = references.find(
|
|
231
|
-
( currentReference ) => currentReference.identifier === node.callee
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
return !! reference?.resolved?.defs.some(
|
|
235
|
-
( definition ) => definition.type === 'ImportBinding'
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* @param {import('estree').Expression|import('estree').PrivateIdentifier} key
|
|
244
|
-
* @return {string|null} Property name.
|
|
245
|
-
*/
|
|
246
|
-
function getPropertyName( key ) {
|
|
247
|
-
if ( key.type === 'Identifier' ) {
|
|
248
|
-
return key.name;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if ( key.type === 'Literal' ) {
|
|
252
|
-
return String( key.value );
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
192
|
/**
|
|
259
193
|
* @param {import('estree').Property} property
|
|
260
194
|
* @param {import('eslint').SourceCode} sourceCode
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createPrivateApisState,
|
|
3
|
+
trackPrivateApisSpecifier,
|
|
4
|
+
getPropertyName,
|
|
5
|
+
getUnlockDestructuring,
|
|
6
|
+
} = require( '../utils/private-apis' );
|
|
7
|
+
|
|
1
8
|
/**
|
|
2
9
|
* Allowlist: only the listed components are permitted from these packages.
|
|
3
10
|
* Any other named import will be flagged with the package's message.
|
|
@@ -14,8 +21,10 @@ const ALLOWLIST = {
|
|
|
14
21
|
'Collapsible',
|
|
15
22
|
'CollapsibleCard',
|
|
16
23
|
'EmptyState',
|
|
24
|
+
'Icon',
|
|
17
25
|
'Link',
|
|
18
26
|
'Stack',
|
|
27
|
+
'Tabs',
|
|
19
28
|
'Text',
|
|
20
29
|
'VisuallyHidden',
|
|
21
30
|
],
|
|
@@ -49,6 +58,8 @@ const DENYLIST = {
|
|
|
49
58
|
CardHeader:
|
|
50
59
|
'Use `Card.Header` (and optionally `Card.Title`) from `@wordpress/ui` instead.',
|
|
51
60
|
CardMedia: 'Use `Card.FullBleed` from `@wordpress/ui` instead.',
|
|
61
|
+
TabPanel: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
62
|
+
Tabs: 'Use `Tabs` from `@wordpress/ui` instead.',
|
|
52
63
|
VisuallyHidden: 'Use `{{ name }}` from `@wordpress/ui` instead.',
|
|
53
64
|
},
|
|
54
65
|
};
|
|
@@ -65,6 +76,8 @@ const rule = {
|
|
|
65
76
|
schema: [],
|
|
66
77
|
},
|
|
67
78
|
create( context ) {
|
|
79
|
+
const privateApisState = createPrivateApisState();
|
|
80
|
+
|
|
68
81
|
return {
|
|
69
82
|
/** @param {import('estree').ImportDeclaration} node */
|
|
70
83
|
ImportDeclaration( node ) {
|
|
@@ -77,16 +90,22 @@ const rule = {
|
|
|
77
90
|
const allowlistEntry = ALLOWLIST[ source ];
|
|
78
91
|
const denylistEntry = DENYLIST[ source ];
|
|
79
92
|
|
|
80
|
-
if ( ! allowlistEntry && ! denylistEntry ) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
93
|
node.specifiers.forEach( ( specifier ) => {
|
|
85
94
|
if ( specifier.type !== 'ImportSpecifier' ) {
|
|
86
95
|
return;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
const name = specifier.imported.name;
|
|
99
|
+
trackPrivateApisSpecifier(
|
|
100
|
+
privateApisState,
|
|
101
|
+
specifier,
|
|
102
|
+
source,
|
|
103
|
+
!! denylistEntry
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if ( ! allowlistEntry && ! denylistEntry ) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
90
109
|
|
|
91
110
|
if (
|
|
92
111
|
allowlistEntry &&
|
|
@@ -114,6 +133,39 @@ const rule = {
|
|
|
114
133
|
}
|
|
115
134
|
} );
|
|
116
135
|
},
|
|
136
|
+
/** @param {import('estree').VariableDeclarator} node */
|
|
137
|
+
VariableDeclarator( node ) {
|
|
138
|
+
const unlockDestructuring = getUnlockDestructuring(
|
|
139
|
+
node,
|
|
140
|
+
context.sourceCode,
|
|
141
|
+
privateApisState
|
|
142
|
+
);
|
|
143
|
+
if ( ! unlockDestructuring ) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const { source, properties } = unlockDestructuring;
|
|
148
|
+
const denylistEntry = DENYLIST[ source ];
|
|
149
|
+
if ( ! denylistEntry ) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
properties.forEach( ( property ) => {
|
|
154
|
+
const name = getPropertyName( property.key );
|
|
155
|
+
if ( ! name || ! denylistEntry.hasOwnProperty( name ) ) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
context.report( {
|
|
160
|
+
node: property.key,
|
|
161
|
+
message: resolveMessage(
|
|
162
|
+
denylistEntry[ name ],
|
|
163
|
+
name,
|
|
164
|
+
source
|
|
165
|
+
),
|
|
166
|
+
} );
|
|
167
|
+
} );
|
|
168
|
+
},
|
|
117
169
|
};
|
|
118
170
|
},
|
|
119
171
|
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for ESLint rules that inspect `unlock( privateApis )` usage.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{ privateApisSources: Map<string, string> }} PrivateApisState
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @return {PrivateApisState} Mutable state for tracking `unlock` and `privateApis` imports.
|
|
11
|
+
*/
|
|
12
|
+
function createPrivateApisState() {
|
|
13
|
+
return {
|
|
14
|
+
privateApisSources: new Map(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Records imported `privateApis` → package source when requested.
|
|
20
|
+
*
|
|
21
|
+
* @param {PrivateApisState} state
|
|
22
|
+
* @param {import('estree').ImportSpecifier} specifier
|
|
23
|
+
* @param {string} source
|
|
24
|
+
* @param {boolean} trackPrivateApis
|
|
25
|
+
*/
|
|
26
|
+
function trackPrivateApisSpecifier(
|
|
27
|
+
state,
|
|
28
|
+
specifier,
|
|
29
|
+
source,
|
|
30
|
+
trackPrivateApis
|
|
31
|
+
) {
|
|
32
|
+
const name = specifier.imported.name;
|
|
33
|
+
if ( trackPrivateApis && name === 'privateApis' ) {
|
|
34
|
+
state.privateApisSources.set( specifier.local.name, source );
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {import('estree').CallExpression|import('estree').Expression|null} node
|
|
40
|
+
* @param {import('eslint').SourceCode} sourceCode
|
|
41
|
+
* @return {node is import('estree').CallExpression} Whether this is an `unlock()` call with one argument.
|
|
42
|
+
*/
|
|
43
|
+
function isUnlockCall( node, sourceCode ) {
|
|
44
|
+
if (
|
|
45
|
+
node &&
|
|
46
|
+
node.type === 'CallExpression' &&
|
|
47
|
+
node.callee.type === 'Identifier' &&
|
|
48
|
+
node.callee.name === 'unlock' &&
|
|
49
|
+
node.arguments.length === 1
|
|
50
|
+
) {
|
|
51
|
+
const { references } = sourceCode.getScope( node.callee );
|
|
52
|
+
const reference = references.find(
|
|
53
|
+
( currentReference ) => currentReference.identifier === node.callee
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return !! reference?.resolved?.defs.some(
|
|
57
|
+
( definition ) => definition.type === 'ImportBinding'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {import('estree').Expression|import('estree').PrivateIdentifier} key
|
|
66
|
+
* @return {string|null} Static name of an object pattern property key.
|
|
67
|
+
*/
|
|
68
|
+
function getPropertyName( key ) {
|
|
69
|
+
if ( key.type === 'Identifier' ) {
|
|
70
|
+
return key.name;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ( key.type === 'Literal' ) {
|
|
74
|
+
return String( key.value );
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parses `const { … } = unlock( privateApis )` and returns the package source and properties.
|
|
82
|
+
*
|
|
83
|
+
* @param {import('estree').VariableDeclarator} node
|
|
84
|
+
* @param {import('eslint').SourceCode} sourceCode
|
|
85
|
+
* @param {PrivateApisState} state
|
|
86
|
+
* @return {{ source: string, properties: import('estree').Property[] }|null} Unlock destructuring context.
|
|
87
|
+
*/
|
|
88
|
+
function getUnlockDestructuring( node, sourceCode, state ) {
|
|
89
|
+
if (
|
|
90
|
+
node.id.type !== 'ObjectPattern' ||
|
|
91
|
+
! isUnlockCall( node.init, sourceCode )
|
|
92
|
+
) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const privateApisIdentifier = node.init.arguments[ 0 ];
|
|
97
|
+
if ( privateApisIdentifier.type !== 'Identifier' ) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const source = state.privateApisSources.get( privateApisIdentifier.name );
|
|
102
|
+
if ( ! source ) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const properties = node.id.properties.filter(
|
|
107
|
+
( property ) => property.type === 'Property' && ! property.computed
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return { source, properties };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
createPrivateApisState,
|
|
115
|
+
trackPrivateApisSpecifier,
|
|
116
|
+
getPropertyName,
|
|
117
|
+
getUnlockDestructuring,
|
|
118
|
+
};
|