1
1
"use strict"
2
2
3
- const { getPropertyName } = require ( "@eslint-community/eslint-utils" )
4
- const { optionalRequire } = require ( "./optional-require" )
5
-
3
+ const { optionalRequire } = require ( "../optional-require" )
6
4
/** @type {import("typescript") } */
7
5
const ts = optionalRequire ( require , "typescript" )
8
6
7
+ module . exports = { buildObjectTypeCheckerForTS }
8
+
9
9
/**
10
- * @typedef {object } CreateReportArgument
11
- * @property {true | 'aggressive' } objectTypeResult
12
- * @property {string } propertyName
13
- * @property {MemberExpression } node
14
- */
15
- /**
16
- * @typedef {object } Options
17
- * @property { (arg: CreateReportArgument) => ReportDescriptor } [Options.createReport]
10
+ * @typedef {import("eslint").Rule.RuleContext } RuleContext
11
+ * @typedef {import("estree").MemberExpression } MemberExpression
18
12
*/
19
-
20
13
/**
21
- * Define handlers to disallow prototype methods .
14
+ * Build object type checker for TypeScript .
22
15
* @param {RuleContext } context The rule context.
23
- * @param {Record<string, readonly string[]> } nameMap The method names to disallow. The key is class names and that value is method names.
24
- * @param {Options } [options] The options.
25
- * @returns {Record<string, (node: ASTNode) => void> } The defined handlers.
16
+ * @param {boolean } aggressiveResult The value to return if the type cannot be determined.
17
+ * @returns {((memberAccessNode: MemberExpression, className: string) => boolean | "aggressive") | null } Returns an object type checker. Returns null if TypeScript is not available.
26
18
*/
27
- function definePrototypeMethodHandler ( context , nameMap , options ) {
28
- const aggressiveOption = getAggressiveOption ( context )
29
- const aggressiveResult = aggressiveOption ? "aggressive" : false
30
-
19
+ function buildObjectTypeCheckerForTS ( context , aggressiveResult ) {
31
20
/** @type {ReadonlyMap<any, import("typescript").Node> } */
32
21
const tsNodeMap = context . parserServices . esTreeNodeToTSNodeMap
33
22
/** @type {import("typescript").TypeChecker } */
@@ -36,45 +25,16 @@ function definePrototypeMethodHandler(context, nameMap, options) {
36
25
context . parserServices . program . getTypeChecker ( )
37
26
38
27
const isTS = Boolean ( ts && tsNodeMap && checker )
39
- const hasFullType =
40
- isTS && context . parserServices . hasFullTypeInformation !== false
41
-
42
- /**
43
- * Check if the type of the given node is one of given class or not.
44
- * @param {MemberExpression } memberAccessNode The MemberExpression node.
45
- * @param {string } className The class name to disallow.
46
- * @returns {boolean | "aggressive" } `true` if should disallow it.
47
- */
48
- function checkObjectType ( memberAccessNode , className ) {
49
- // If it's obvious, shortcut.
50
- if ( memberAccessNode . object . type === "ArrayExpression" ) {
51
- return className === "Array"
52
- }
53
- if (
54
- memberAccessNode . object . type === "Literal" &&
55
- memberAccessNode . object . regex
56
- ) {
57
- return className === "RegExp"
58
- }
59
- if (
60
- ( memberAccessNode . object . type === "Literal" &&
61
- typeof memberAccessNode . object . value === "string" ) ||
62
- memberAccessNode . object . type === "TemplateLiteral"
63
- ) {
64
- return className === "String"
65
- }
66
- if (
67
- memberAccessNode . object . type === "FunctionExpression" ||
68
- memberAccessNode . object . type === "ArrowFunctionExpression"
69
- ) {
70
- return className === "Function"
71
- }
28
+ if ( ! isTS ) {
29
+ return null
30
+ }
31
+ const hasFullType = context . parserServices . hasFullTypeInformation !== false
72
32
73
- // Test object type.
74
- return isTS
75
- ? checkByPropertyDeclaration ( memberAccessNode , className ) ||
76
- checkByObjectExpressionType ( memberAccessNode , className )
77
- : aggressiveResult
33
+ return function ( memberAccessNode , className ) {
34
+ return (
35
+ checkByPropertyDeclaration ( memberAccessNode , className ) ||
36
+ checkByObjectExpressionType ( memberAccessNode , className )
37
+ )
78
38
}
79
39
80
40
/**
@@ -237,78 +197,6 @@ function definePrototypeMethodHandler(context, nameMap, options) {
237
197
}
238
198
return undefined
239
199
}
240
-
241
- // For performance
242
- const nameMapEntries = Object . entries ( nameMap )
243
- if ( nameMapEntries . length === 1 ) {
244
- const [ [ className , methodNames ] ] = nameMapEntries
245
- return {
246
- MemberExpression ( node ) {
247
- const propertyName = getPropertyName ( node , context . getScope ( ) )
248
- let objectTypeResult = undefined
249
- if (
250
- methodNames . includes ( propertyName ) &&
251
- ( objectTypeResult = checkObjectType ( node , className ) )
252
- ) {
253
- context . report ( {
254
- node,
255
- messageId : "forbidden" ,
256
- data : {
257
- name : `${ className } .prototype.${ propertyName } ` ,
258
- } ,
259
- ...( ( options &&
260
- options . createReport &&
261
- options . createReport ( {
262
- objectTypeResult,
263
- propertyName,
264
- node,
265
- } ) ) ||
266
- { } ) ,
267
- } )
268
- }
269
- } ,
270
- }
271
- }
272
-
273
- return {
274
- MemberExpression ( node ) {
275
- const propertyName = getPropertyName ( node , context . getScope ( ) )
276
- for ( const [ className , methodNames ] of nameMapEntries ) {
277
- if (
278
- methodNames . includes ( propertyName ) &&
279
- checkObjectType ( node , className )
280
- ) {
281
- context . report ( {
282
- node,
283
- messageId : "forbidden" ,
284
- data : {
285
- name : `${ className } .prototype.${ propertyName } ` ,
286
- } ,
287
- } )
288
- return
289
- }
290
- }
291
- } ,
292
- }
293
- }
294
-
295
- /**
296
- * Get `aggressive` option value.
297
- * @param {RuleContext } context The rule context.
298
- * @returns {boolean } The gotten `aggressive` option value.
299
- */
300
- function getAggressiveOption ( context ) {
301
- const options = context . options [ 0 ]
302
- const globalOptions = context . settings [ "es-x" ]
303
-
304
- if ( options && typeof options . aggressive === "boolean" ) {
305
- return options . aggressive
306
- }
307
- if ( globalOptions && typeof globalOptions . aggressive === "boolean" ) {
308
- return globalOptions . aggressive
309
- }
310
-
311
- return false
312
200
}
313
201
314
202
/**
@@ -429,5 +317,3 @@ function isFunction(type) {
429
317
const signatures = type . getCallSignatures ( )
430
318
return signatures . length > 0
431
319
}
432
-
433
- module . exports = { definePrototypeMethodHandler }
0 commit comments