Files
NapCatQQ/.pnpm-store/v3/files/92/731f84b993fa2127d8b7a301af042939962119d6cc212b224a77161f6b20d6f043655f2071115e87464d646606f695d06c5ee79ff9017ae9d845ed4409294b
T
TanTan e311ca3275
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
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

191 lines
4.7 KiB
Plaintext

'use strict';
const doctrine = require('doctrine');
const pragmaUtil = require('./pragma');
const eslintUtil = require('./eslint');
const getScope = eslintUtil.getScope;
const getSourceCode = eslintUtil.getSourceCode;
const getText = eslintUtil.getText;
// eslint-disable-next-line valid-jsdoc
/**
* @template {(_: object) => any} T
* @param {T} fn
* @returns {T}
*/
function memoize(fn) {
const cache = new WeakMap();
// @ts-ignore
return function memoizedFn(arg) {
const cachedValue = cache.get(arg);
if (cachedValue !== undefined) {
return cachedValue;
}
const v = fn(arg);
cache.set(arg, v);
return v;
};
}
const getPragma = memoize(pragmaUtil.getFromContext);
const getCreateClass = memoize(pragmaUtil.getCreateClassFromContext);
/**
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
function isES5Component(node, context) {
const pragma = getPragma(context);
const createClass = getCreateClass(context);
if (!node.parent || !node.parent.callee) {
return false;
}
const callee = node.parent.callee;
// React.createClass({})
if (callee.type === 'MemberExpression') {
return callee.object.name === pragma && callee.property.name === createClass;
}
// createClass({})
if (callee.type === 'Identifier') {
return callee.name === createClass;
}
return false;
}
/**
* Check if the node is explicitly declared as a descendant of a React Component
* @param {any} node
* @param {Context} context
* @returns {boolean}
*/
function isExplicitComponent(node, context) {
const sourceCode = getSourceCode(context);
let comment;
// Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes.
// Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27
// eslint-disable-next-line no-warning-comments
// FIXME: Remove try/catch when https://github.com/eslint/eslint-scope/issues/27 is implemented.
try {
comment = sourceCode.getJSDocComment(node);
} catch (e) {
comment = null;
}
if (comment === null) {
return false;
}
let commentAst;
try {
commentAst = doctrine.parse(comment.value, {
unwrap: true,
tags: ['extends', 'augments'],
});
} catch (e) {
// handle a bug in the archived `doctrine`, see #2596
return false;
}
const relevantTags = commentAst.tags.filter((tag) => tag.name === 'React.Component' || tag.name === 'React.PureComponent');
return relevantTags.length > 0;
}
/**
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
function isES6Component(node, context) {
const pragma = getPragma(context);
if (isExplicitComponent(node, context)) {
return true;
}
if (!node.superClass) {
return false;
}
if (node.superClass.type === 'MemberExpression') {
return node.superClass.object.name === pragma
&& /^(Pure)?Component$/.test(node.superClass.property.name);
}
if (node.superClass.type === 'Identifier') {
return /^(Pure)?Component$/.test(node.superClass.name);
}
return false;
}
/**
* Get the parent ES5 component node from the current scope
* @param {Context} context
* @param {ASTNode} node
* @returns {ASTNode|null}
*/
function getParentES5Component(context, node) {
let scope = getScope(context, node);
while (scope) {
// @ts-ignore
node = scope.block && scope.block.parent && scope.block.parent.parent;
if (node && isES5Component(node, context)) {
return node;
}
scope = scope.upper;
}
return null;
}
/**
* Get the parent ES6 component node from the current scope
* @param {Context} context
* @param {ASTNode} node
* @returns {ASTNode | null}
*/
function getParentES6Component(context, node) {
let scope = getScope(context, node);
while (scope && scope.type !== 'class') {
scope = scope.upper;
}
node = scope && scope.block;
if (!node || !isES6Component(node, context)) {
return null;
}
return node;
}
/**
* Checks if a component extends React.PureComponent
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
function isPureComponent(node, context) {
const pragma = getPragma(context);
if (node.superClass) {
return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(getText(context, node.superClass));
}
return false;
}
/**
* @param {ASTNode} node
* @returns {boolean}
*/
function isStateMemberExpression(node) {
return node.type === 'MemberExpression'
&& node.object.type === 'ThisExpression'
&& node.property.name === 'state';
}
module.exports = {
isES5Component,
isES6Component,
getParentES5Component,
getParentES6Component,
isExplicitComponent,
isPureComponent,
isStateMemberExpression,
};