Build NapCat Artifacts / Windows Loader Test (push) Canceled after 0s
Build NapCat Artifacts / Type Check & Test (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Framework (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Shell (push) Canceled after 0s
162 lines
5.6 KiB
Plaintext
162 lines
5.6 KiB
Plaintext
"use strict"
|
|
|
|
const { getPropertyName } = require("@eslint-community/eslint-utils")
|
|
const { buildObjectTypeChecker } = require("./object-type-checker")
|
|
const { buildObjectTypeCheckerForTS } = require("./object-type-checker-for-ts")
|
|
const { getSourceCode } = require("eslint-compat-utils")
|
|
|
|
/**
|
|
* @typedef {object} CreateReportArgument
|
|
* @property {true | 'aggressive'} objectTypeResult
|
|
* @property {string} className
|
|
* @property {string} propertyName
|
|
* @property {MemberExpression} node
|
|
*/
|
|
/**
|
|
* @typedef {object} Options
|
|
* @property { (arg: CreateReportArgument) => ReportDescriptor } [Options.createReport]
|
|
*/
|
|
|
|
/**
|
|
* Define handlers to disallow prototype methods.
|
|
* @param {RuleContext} context The rule context.
|
|
* @param {Record<string, readonly string[]>} nameMap The method names to disallow. The key is class names and that value is method names.
|
|
* @param {Options} [options] The options.
|
|
* @returns {Record<string, (node: ASTNode) => void>} The defined handlers.
|
|
*/
|
|
function definePrototypeMethodHandler(context, nameMap, options) {
|
|
const sourceCode = getSourceCode(context)
|
|
const aggressiveOption = getAggressiveOption(context)
|
|
const aggressiveResult = aggressiveOption ? "aggressive" : false
|
|
|
|
const objectTypeChecker =
|
|
buildObjectTypeCheckerForTS(context, aggressiveResult) ||
|
|
buildObjectTypeChecker(context, aggressiveResult)
|
|
|
|
/**
|
|
* Check if the type of the given node is one of given class or not.
|
|
* @param {MemberExpression} memberAccessNode The MemberExpression node.
|
|
* @param {string} className The class name to disallow.
|
|
* @returns {boolean | "aggressive"} `true` if should disallow it.
|
|
*/
|
|
function checkObjectType(memberAccessNode, className) {
|
|
// If it's obvious, shortcut.
|
|
if (memberAccessNode.object.type === "ArrayExpression") {
|
|
return className === "Array"
|
|
}
|
|
if (
|
|
memberAccessNode.object.type === "Literal" &&
|
|
memberAccessNode.object.regex
|
|
) {
|
|
return className === "RegExp"
|
|
}
|
|
if (
|
|
(memberAccessNode.object.type === "Literal" &&
|
|
typeof memberAccessNode.object.value === "string") ||
|
|
memberAccessNode.object.type === "TemplateLiteral"
|
|
) {
|
|
return className === "String"
|
|
}
|
|
if (
|
|
memberAccessNode.object.type === "FunctionExpression" ||
|
|
memberAccessNode.object.type === "ArrowFunctionExpression"
|
|
) {
|
|
return className === "Function"
|
|
}
|
|
|
|
// Test object type.
|
|
return objectTypeChecker(memberAccessNode, className)
|
|
}
|
|
|
|
// For performance
|
|
const nameMapEntries = Object.entries(nameMap)
|
|
if (nameMapEntries.length === 1) {
|
|
const [[className, methodNames]] = nameMapEntries
|
|
return {
|
|
MemberExpression(node) {
|
|
const propertyName = getPropertyName(
|
|
node,
|
|
sourceCode.getScope(node),
|
|
)
|
|
let objectTypeResult = undefined
|
|
if (
|
|
methodNames.includes(propertyName) &&
|
|
(objectTypeResult = checkObjectType(node, className))
|
|
) {
|
|
context.report({
|
|
node,
|
|
messageId: "forbidden",
|
|
data: {
|
|
name: `${className}.prototype.${propertyName}`,
|
|
},
|
|
...((options &&
|
|
options.createReport &&
|
|
options.createReport({
|
|
objectTypeResult,
|
|
className,
|
|
propertyName,
|
|
node,
|
|
})) ||
|
|
{}),
|
|
})
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
return {
|
|
MemberExpression(node) {
|
|
const propertyName = getPropertyName(
|
|
node,
|
|
sourceCode.getScope(node),
|
|
)
|
|
for (const [className, methodNames] of nameMapEntries) {
|
|
let objectTypeResult = undefined
|
|
if (
|
|
methodNames.includes(propertyName) &&
|
|
(objectTypeResult = checkObjectType(node, className))
|
|
) {
|
|
context.report({
|
|
node,
|
|
messageId: "forbidden",
|
|
data: {
|
|
name: `${className}.prototype.${propertyName}`,
|
|
},
|
|
...((options &&
|
|
options.createReport &&
|
|
options.createReport({
|
|
objectTypeResult,
|
|
className,
|
|
propertyName,
|
|
node,
|
|
})) ||
|
|
{}),
|
|
})
|
|
return
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get `aggressive` option value.
|
|
* @param {RuleContext} context The rule context.
|
|
* @returns {boolean} The gotten `aggressive` option value.
|
|
*/
|
|
function getAggressiveOption(context) {
|
|
const options = context.options[0]
|
|
const globalOptions = context.settings["es-x"]
|
|
|
|
if (options && typeof options.aggressive === "boolean") {
|
|
return options.aggressive
|
|
}
|
|
if (globalOptions && typeof globalOptions.aggressive === "boolean") {
|
|
return globalOptions.aggressive
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
module.exports = { definePrototypeMethodHandler }
|