Files
NapCatQQ/.pnpm-store/v3/files/86/cfee798aecde95019583a2b42b3038e39fab8b0691245f2fef3c1b2701ce4174489dbdc319d54c37cb6c112f4a770c945be309a346d6340a6f62b1c10ce4be
T
TanTan e311ca3275
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

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 }