Ajout de promotion et de commande

This commit is contained in:
Aubert Marvin
2026-04-25 15:28:39 +02:00
parent eddb103755
commit faa3d7718c
8428 changed files with 1126442 additions and 6 deletions
+76
View File
@@ -0,0 +1,76 @@
declare const _exports: typeof Components & {
detect(rule: any): (context?: any) => {
[_: string]: Function;
};
};
export = _exports;
/**
* Components
*/
declare class Components {
/**
* Add a node to the components list, or update it if it's already in the list
*
* @param {ASTNode} node The AST node being added.
* @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
* @returns {Object} Added component object
*/
add(node: ASTNode, confidence: number): any;
/**
* Find a component in the list using its node
*
* @param {ASTNode} node The AST node being searched.
* @returns {Object} Component object, undefined if the component is not found or has confidence value of 0.
*/
get(node: ASTNode): any;
/**
* Update a component in the list
*
* @param {ASTNode} node The AST node being updated.
* @param {Object} props Additional properties to add to the component.
*/
set(node: ASTNode, props: any): void;
/**
* Return the components list
* Components for which we are not confident are not returned
*
* @returns {Object} Components list
*/
list(): any;
/**
* Return the length of the components list
* Components for which we are not confident are not counted
*
* @returns {number} Components list length
*/
length(): number;
/**
* Return the node naming the default React import
* It can be used to determine the local name of import, even if it's imported
* with an unusual name.
*
* @returns {ASTNode} React default import node
*/
getDefaultReactImports(): ASTNode;
/**
* Return the nodes of all React named imports
*
* @returns {Object} The list of React named imports
*/
getNamedReactImports(): any;
/**
* Add the default React import specifier to the scope
*
* @param {ASTNode} specifier The AST Node of the default React import
* @returns {void}
*/
addDefaultReactImport(specifier: ASTNode): void;
/**
* Add a named React import specifier to the scope
*
* @param {ASTNode} specifier The AST Node of a named React import
* @returns {void}
*/
addNamedReactImport(specifier: ASTNode): void;
}
//# sourceMappingURL=Components.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Components.d.ts","sourceRoot":"","sources":["Components.js"],"names":[],"mappings":";;;;;;AA2DA;;GAEG;AACH;IAME;;;;;;OAMG;IACH,UAJW,OAAO,cACP,MAAM,OAmBhB;IAED;;;;;OAKG;IACH,UAHW,OAAO,OAUjB;IAED;;;;;OAKG;IACH,UAHW,OAAO,oBAwBjB;IAED;;;;;OAKG;IACH,YAoCC;IAED;;;;;OAKG;IACH,UAFa,MAAM,CAKlB;IAED;;;;;;OAMG;IACH,0BAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,4BAEC;IAED;;;;;OAKG;IACH,iCAHW,OAAO,GACL,IAAI,CAOhB;IAED;;;;;OAKG;IACH,+BAHW,OAAO,GACL,IAAI,CAOhB;CACF"}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
/**
* Checks if we are declaring a `props` argument with a flow type annotation.
* @param {ASTNode} node The AST node being checked.
* @param {Object} context
* @returns {boolean} True if the node is a type annotated props declaration, false if not.
*/
export function isAnnotatedFunctionPropsDeclaration(node: ASTNode, context: any): boolean;
//# sourceMappingURL=annotations.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["annotations.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,0DAJW,OAAO,iBAEL,OAAO,CAenB"}
+34
View File
@@ -0,0 +1,34 @@
/**
* @fileoverview Utility functions for type annotation detection.
* @author Yannick Croissant
* @author Vitor Balocco
*/
'use strict';
const getFirstTokens = require('./eslint').getFirstTokens;
/**
* Checks if we are declaring a `props` argument with a flow type annotation.
* @param {ASTNode} node The AST node being checked.
* @param {Object} context
* @returns {boolean} True if the node is a type annotated props declaration, false if not.
*/
function isAnnotatedFunctionPropsDeclaration(node, context) {
if (!node || !node.params || !node.params.length) {
return false;
}
const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0];
const tokens = getFirstTokens(context, typeNode, 2);
const isAnnotated = typeNode.typeAnnotation;
const isDestructuredProps = typeNode.type === 'ObjectPattern';
const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props');
return (isAnnotated && (isDestructuredProps || isProps));
}
module.exports = {
isAnnotatedFunctionPropsDeclaration,
};
+136
View File
@@ -0,0 +1,136 @@
/**
* Find a return statement in the current node
*
* @param {ASTNode} node The AST node being checked
* @returns {ASTNode | false}
*/
export function findReturnStatement(node: ASTNode): ASTNode | false;
/**
* Get properties for a given AST node
* @param {ASTNode} node The AST node being checked.
* @returns {Array} Properties array.
*/
export function getComponentProperties(node: ASTNode): any[];
/**
* Gets the first node in a line from the initial node, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {ASTNode} the first node in the line
*/
export function getFirstNodeInLine(context: any, node: ASTNode): ASTNode;
/**
* Retrieve the name of a key node
* @param {Context} context The AST node with the key.
* @param {any} node The AST node with the key.
* @return {string | undefined} the name of the key
*/
export function getKeyValue(context: Context, node: any): string | undefined;
/**
* Get properties name
* @param {Object} node - Property.
* @returns {string} Property name.
*/
export function getPropertyName(node: any): string;
/**
* Get node with property's name
* @param {Object} node - Property.
* @returns {Object} Property name node.
*/
export function getPropertyNameNode(node: any): any;
/**
* Check if we are in a class constructor
* @param {Context} context
* @param {ASTNode} node The AST node being checked.
* @return {boolean}
*/
export function inConstructor(context: Context, node: ASTNode): boolean;
/**
* Checks if a node is being assigned a value: props.bar = 'bar'
* @param {ASTNode} node The AST node being checked.
* @returns {boolean}
*/
export function isAssignmentLHS(node: ASTNode): boolean;
/**
* Matcher used to check whether given node is a `CallExpression`
* @param {ASTNode} node The AST node
* @returns {boolean} True if node is a `CallExpression`, false if not
*/
export function isCallExpression(node: ASTNode): boolean;
/**
* Checks if the node is a class.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a class
*/
export function isClass(node: ASTNode): boolean;
/**
* Checks if the node is a function.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function
*/
export function isFunction(node: ASTNode): boolean;
/**
* Checks if node is a function declaration or expression or arrow function.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function-like
*/
export function isFunctionLike(node: ASTNode): boolean;
/**
* Checks if the node is a function or arrow function expression.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function-like expression
*/
export function isFunctionLikeExpression(node: ASTNode): boolean;
/**
* Checks if the node is the first in its line, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {boolean} true if it's the first node in its line
*/
export function isNodeFirstInLine(context: any, node: ASTNode): boolean;
/**
* Checks if a node is surrounded by parenthesis.
*
* @param {object} context - Context from the rule
* @param {ASTNode} node - Node to be checked
* @returns {boolean}
*/
export function isParenthesized(context: object, node: ASTNode): boolean;
export function isTSAsExpression(node: any): boolean;
export function isTSFunctionType(node: any): boolean;
export function isTSInterfaceDeclaration(node: any): boolean;
export function isTSInterfaceHeritage(node: any): boolean;
export function isTSIntersectionType(node: any): boolean;
export function isTSParenthesizedType(node: any): boolean;
export function isTSTypeAliasDeclaration(node: any): boolean;
export function isTSTypeAnnotation(node: any): boolean;
export function isTSTypeDeclaration(node: any): boolean;
export function isTSTypeLiteral(node: any): boolean;
export function isTSTypeParameterInstantiation(node: any): boolean;
export function isTSTypeQuery(node: any): boolean;
export function isTSTypeReference(node: any): boolean;
/**
* Wrapper for estraverse.traverse
*
* @param {ASTNode} ASTnode The AST node being checked
* @param {Object} visitor Visitor Object for estraverse
*/
export function traverse(ASTnode: ASTNode, visitor: any): void;
/**
* Helper function for traversing "returns" (return statements or the
* returned expression in the case of an arrow function) of a function
*
* @param {ASTNode} ASTNode The AST node being checked
* @param {Context} context The context of `ASTNode`.
* @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn
* Function to execute for each returnStatement found
* @returns {undefined}
*/
export function traverseReturns(ASTNode: ASTNode, context: Context, onReturn: (returnValue: ASTNode, breakTraverse: () => void) => void): undefined;
/**
* Extracts the expression node that is wrapped inside a TS type assertion
*
* @param {ASTNode} node - potential TS node
* @returns {ASTNode} - unwrapped expression node
*/
export function unwrapTSAsExpression(node: ASTNode): ASTNode;
//# sourceMappingURL=ast.d.ts.map
File diff suppressed because one or more lines are too long
+483
View File
@@ -0,0 +1,483 @@
/**
* @fileoverview Utility functions for AST
*/
'use strict';
const estraverse = require('estraverse');
const eslintUtil = require('./eslint');
const getFirstTokens = eslintUtil.getFirstTokens;
const getScope = eslintUtil.getScope;
const getSourceCode = eslintUtil.getSourceCode;
// const pragmaUtil = require('./pragma');
/**
* Wrapper for estraverse.traverse
*
* @param {ASTNode} ASTnode The AST node being checked
* @param {Object} visitor Visitor Object for estraverse
*/
function traverse(ASTnode, visitor) {
const opts = Object.assign({}, {
fallback(node) {
return Object.keys(node).filter((key) => key === 'children' || key === 'argument');
},
}, visitor);
opts.keys = Object.assign({}, visitor.keys, {
JSXElement: ['children'],
JSXFragment: ['children'],
});
estraverse.traverse(ASTnode, opts);
}
function loopNodes(nodes) {
for (let i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].type === 'ReturnStatement') {
return nodes[i];
}
if (nodes[i].type === 'SwitchStatement') {
const j = nodes[i].cases.length - 1;
if (j >= 0) {
return loopNodes(nodes[i].cases[j].consequent);
}
}
}
return false;
}
/**
* Find a return statement in the current node
*
* @param {ASTNode} node The AST node being checked
* @returns {ASTNode | false}
*/
function findReturnStatement(node) {
if (
(!node.value || !node.value.body || !node.value.body.body)
&& (!node.body || !node.body.body)
) {
return false;
}
const bodyNodes = node.value ? node.value.body.body : node.body.body;
return loopNodes(bodyNodes);
}
// eslint-disable-next-line valid-jsdoc -- valid-jsdoc cannot parse function types.
/**
* Helper function for traversing "returns" (return statements or the
* returned expression in the case of an arrow function) of a function
*
* @param {ASTNode} ASTNode The AST node being checked
* @param {Context} context The context of `ASTNode`.
* @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn
* Function to execute for each returnStatement found
* @returns {undefined}
*/
function traverseReturns(ASTNode, context, onReturn) {
const nodeType = ASTNode.type;
if (nodeType === 'ReturnStatement') {
onReturn(ASTNode.argument, () => {});
return;
}
if (nodeType === 'ArrowFunctionExpression' && ASTNode.expression) {
onReturn(ASTNode.body, () => {});
return;
}
/* TODO: properly warn on React.forwardRefs having typo properties
if (astUtil.isCallExpression(ASTNode)) {
const callee = ASTNode.callee;
const pragma = pragmaUtil.getFromContext(context);
if (
callee.type === 'MemberExpression'
&& callee.object.type === 'Identifier'
&& callee.object.name === pragma
&& callee.property.type === 'Identifier'
&& callee.property.name === 'forwardRef'
&& ASTNode.arguments.length > 0
) {
return enterFunc(ASTNode.arguments[0]);
}
return;
}
*/
if (
nodeType !== 'FunctionExpression'
&& nodeType !== 'FunctionDeclaration'
&& nodeType !== 'ArrowFunctionExpression'
&& nodeType !== 'MethodDefinition'
) {
return;
}
traverse(ASTNode.body, {
enter(node) {
const breakTraverse = () => {
this.break();
};
switch (node.type) {
case 'ReturnStatement':
this.skip();
onReturn(node.argument, breakTraverse);
return;
case 'BlockStatement':
case 'IfStatement':
case 'ForStatement':
case 'WhileStatement':
case 'SwitchStatement':
case 'SwitchCase':
return;
default:
this.skip();
}
},
});
}
/**
* Get node with property's name
* @param {Object} node - Property.
* @returns {Object} Property name node.
*/
function getPropertyNameNode(node) {
if (
node.key
|| node.type === 'MethodDefinition'
|| node.type === 'Property'
) {
return node.key;
}
if (node.type === 'MemberExpression') {
return node.property;
}
return null;
}
/**
* Get properties name
* @param {Object} node - Property.
* @returns {string} Property name.
*/
function getPropertyName(node) {
const nameNode = getPropertyNameNode(node);
return nameNode ? nameNode.name : '';
}
/**
* Get properties for a given AST node
* @param {ASTNode} node The AST node being checked.
* @returns {Array} Properties array.
*/
function getComponentProperties(node) {
switch (node.type) {
case 'ClassDeclaration':
case 'ClassExpression':
return node.body.body;
case 'ObjectExpression':
return node.properties;
default:
return [];
}
}
/**
* Gets the first node in a line from the initial node, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {ASTNode} the first node in the line
*/
function getFirstNodeInLine(context, node) {
const sourceCode = getSourceCode(context);
let token = node;
let lines;
do {
token = sourceCode.getTokenBefore(token);
lines = token.type === 'JSXText'
? token.value.split('\n')
: null;
} while (
token.type === 'JSXText'
&& /^\s*$/.test(lines[lines.length - 1])
);
return token;
}
/**
* Checks if the node is the first in its line, excluding whitespace.
* @param {Object} context The node to check
* @param {ASTNode} node The node to check
* @return {boolean} true if it's the first node in its line
*/
function isNodeFirstInLine(context, node) {
const token = getFirstNodeInLine(context, node);
const startLine = node.loc.start.line;
const endLine = token ? token.loc.end.line : -1;
return startLine !== endLine;
}
/**
* Checks if the node is a function or arrow function expression.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function-like expression
*/
function isFunctionLikeExpression(node) {
return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
}
/**
* Checks if the node is a function.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function
*/
function isFunction(node) {
return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
}
/**
* Checks if node is a function declaration or expression or arrow function.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a function-like
*/
function isFunctionLike(node) {
return node.type === 'FunctionDeclaration' || isFunctionLikeExpression(node);
}
/**
* Checks if the node is a class.
* @param {ASTNode} node The node to check
* @return {boolean} true if it's a class
*/
function isClass(node) {
return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
}
/**
* Check if we are in a class constructor
* @param {Context} context
* @param {ASTNode} node The AST node being checked.
* @return {boolean}
*/
function inConstructor(context, node) {
let scope = getScope(context, node);
while (scope) {
// @ts-ignore
if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
return true;
}
scope = scope.upper;
}
return false;
}
/**
* Removes quotes from around an identifier.
* @param {string} string the identifier to strip
* @returns {string}
*/
function stripQuotes(string) {
return string.replace(/^'|'$/g, '');
}
/**
* Retrieve the name of a key node
* @param {Context} context The AST node with the key.
* @param {any} node The AST node with the key.
* @return {string | undefined} the name of the key
*/
function getKeyValue(context, node) {
if (node.type === 'ObjectTypeProperty') {
const tokens = getFirstTokens(context, node, 2);
return (tokens[0].value === '+' || tokens[0].value === '-'
? tokens[1].value
: stripQuotes(tokens[0].value)
);
}
if (node.type === 'GenericTypeAnnotation') {
return node.id.name;
}
if (node.type === 'ObjectTypeAnnotation') {
return;
}
const key = node.key || node.argument;
if (!key) {
return;
}
return key.type === 'Identifier' ? key.name : key.value;
}
/**
* Checks if a node is surrounded by parenthesis.
*
* @param {object} context - Context from the rule
* @param {ASTNode} node - Node to be checked
* @returns {boolean}
*/
function isParenthesized(context, node) {
const sourceCode = getSourceCode(context);
const previousToken = sourceCode.getTokenBefore(node);
const nextToken = sourceCode.getTokenAfter(node);
return !!previousToken && !!nextToken
&& previousToken.value === '(' && previousToken.range[1] <= node.range[0]
&& nextToken.value === ')' && nextToken.range[0] >= node.range[1];
}
/**
* Checks if a node is being assigned a value: props.bar = 'bar'
* @param {ASTNode} node The AST node being checked.
* @returns {boolean}
*/
function isAssignmentLHS(node) {
return (
node.parent
&& node.parent.type === 'AssignmentExpression'
&& node.parent.left === node
);
}
function isTSAsExpression(node) {
return node && node.type === 'TSAsExpression';
}
/**
* Matcher used to check whether given node is a `CallExpression`
* @param {ASTNode} node The AST node
* @returns {boolean} True if node is a `CallExpression`, false if not
*/
function isCallExpression(node) {
return node && node.type === 'CallExpression';
}
/**
* Extracts the expression node that is wrapped inside a TS type assertion
*
* @param {ASTNode} node - potential TS node
* @returns {ASTNode} - unwrapped expression node
*/
function unwrapTSAsExpression(node) {
return isTSAsExpression(node) ? node.expression : node;
}
function isTSTypeReference(node) {
if (!node) return false;
return node.type === 'TSTypeReference';
}
function isTSTypeAnnotation(node) {
if (!node) { return false; }
return node.type === 'TSTypeAnnotation';
}
function isTSTypeLiteral(node) {
if (!node) { return false; }
return node.type === 'TSTypeLiteral';
}
function isTSIntersectionType(node) {
if (!node) { return false; }
return node.type === 'TSIntersectionType';
}
function isTSInterfaceHeritage(node) {
if (!node) { return false; }
return node.type === 'TSInterfaceHeritage';
}
function isTSInterfaceDeclaration(node) {
if (!node) { return false; }
return (node.type === 'ExportNamedDeclaration' && node.declaration
? node.declaration.type
: node.type
) === 'TSInterfaceDeclaration';
}
function isTSTypeDeclaration(node) {
if (!node) { return false; }
const nodeToCheck = node.type === 'ExportNamedDeclaration' && node.declaration
? node.declaration
: node;
return nodeToCheck.type === 'VariableDeclaration' && nodeToCheck.kind === 'type';
}
function isTSTypeAliasDeclaration(node) {
if (!node) { return false; }
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
return node.declaration.type === 'TSTypeAliasDeclaration' && node.exportKind === 'type';
}
return node.type === 'TSTypeAliasDeclaration';
}
function isTSParenthesizedType(node) {
if (!node) { return false; }
return node.type === 'TSTypeAliasDeclaration';
}
function isTSFunctionType(node) {
if (!node) { return false; }
return node.type === 'TSFunctionType';
}
function isTSTypeQuery(node) {
if (!node) { return false; }
return node.type === 'TSTypeQuery';
}
function isTSTypeParameterInstantiation(node) {
if (!node) { return false; }
return node.type === 'TSTypeParameterInstantiation';
}
module.exports = {
findReturnStatement,
getComponentProperties,
getFirstNodeInLine,
getKeyValue,
getPropertyName,
getPropertyNameNode,
inConstructor,
isAssignmentLHS,
isCallExpression,
isClass,
isFunction,
isFunctionLike,
isFunctionLikeExpression,
isNodeFirstInLine,
isParenthesized,
isTSAsExpression,
isTSFunctionType,
isTSInterfaceDeclaration,
isTSInterfaceHeritage,
isTSIntersectionType,
isTSParenthesizedType,
isTSTypeAliasDeclaration,
isTSTypeAnnotation,
isTSTypeDeclaration,
isTSTypeLiteral,
isTSTypeParameterInstantiation,
isTSTypeQuery,
isTSTypeReference,
traverse,
traverseReturns,
unwrapTSAsExpression,
};
+46
View File
@@ -0,0 +1,46 @@
/**
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
export function isES5Component(node: ASTNode, context: Context): boolean;
/**
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
export function isES6Component(node: ASTNode, context: Context): boolean;
/**
* Get the parent ES5 component node from the current scope
* @param {Context} context
* @param {ASTNode} node
* @returns {ASTNode|null}
*/
export function getParentES5Component(context: Context, node: ASTNode): ASTNode | null;
/**
* Get the parent ES6 component node from the current scope
* @param {Context} context
* @param {ASTNode} node
* @returns {ASTNode | null}
*/
export function getParentES6Component(context: Context, node: ASTNode): ASTNode | null;
/**
* Check if the node is explicitly declared as a descendant of a React Component
* @param {any} node
* @param {Context} context
* @returns {boolean}
*/
export function isExplicitComponent(node: any, context: Context): boolean;
/**
* Checks if a component extends React.PureComponent
* @param {ASTNode} node
* @param {Context} context
* @returns {boolean}
*/
export function isPureComponent(node: ASTNode, context: Context): boolean;
/**
* @param {ASTNode} node
* @returns {boolean}
*/
export function isStateMemberExpression(node: ASTNode): boolean;
//# sourceMappingURL=componentUtil.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"componentUtil.d.ts","sourceRoot":"","sources":["componentUtil.js"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAyCD;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAC,IAAI,CAaxB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAG,IAAI,CAY1B;AAlGD;;;;;GAKG;AACH,0CAJW,GAAG,WACH,OAAO,GACL,OAAO,CAiCnB;AA+DD;;;;;GAKG;AACH,sCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAQnB;AAED;;;GAGG;AACH,8CAHW,OAAO,GACL,OAAO,CAMnB"}
+190
View File
@@ -0,0 +1,190 @@
'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,
};
+8
View File
@@ -0,0 +1,8 @@
declare function _exports(context: any, components: any, utils: any): {
MemberExpression(node: any): void;
MethodDefinition(node: any): void;
'ClassProperty, PropertyDefinition'(node: any): void;
ObjectExpression(node: any): void;
};
export = _exports;
//# sourceMappingURL=defaultProps.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"defaultProps.d.ts","sourceRoot":"","sources":["defaultProps.js"],"names":[],"mappings":"AAgBiB;;;;;EA0PhB"}
+267
View File
@@ -0,0 +1,267 @@
/**
* @fileoverview Common defaultProps detection functionality.
*/
'use strict';
const fromEntries = require('object.fromentries');
const astUtil = require('./ast');
const componentUtil = require('./componentUtil');
const propsUtil = require('./props');
const variableUtil = require('./variable');
const propWrapperUtil = require('./propWrapper');
const getText = require('./eslint').getText;
const QUOTES_REGEX = /^["']|["']$/g;
module.exports = function defaultPropsInstructions(context, components, utils) {
/**
* Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
* an Identifier, then the node is simply returned.
* @param {ASTNode} node The node to resolve.
* @returns {ASTNode|null} Return null if the value could not be resolved, ASTNode otherwise.
*/
function resolveNodeValue(node) {
if (node.type === 'Identifier') {
return variableUtil.findVariableByName(context, node, node.name);
}
if (
astUtil.isCallExpression(node)
&& propWrapperUtil.isPropWrapperFunction(context, node.callee.name)
&& node.arguments && node.arguments[0]
) {
return resolveNodeValue(node.arguments[0]);
}
return node;
}
/**
* Extracts a DefaultProp from an ObjectExpression node.
* @param {ASTNode} objectExpression ObjectExpression node.
* @returns {Object|string} Object representation of a defaultProp, to be consumed by
* `addDefaultPropsToComponent`, or string "unresolved", if the defaultProps
* from this ObjectExpression can't be resolved.
*/
function getDefaultPropsFromObjectExpression(objectExpression) {
const hasSpread = objectExpression.properties.find((property) => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement');
if (hasSpread) {
return 'unresolved';
}
return objectExpression.properties.map((defaultProp) => ({
name: getText(context, defaultProp.key).replace(QUOTES_REGEX, ''),
node: defaultProp,
}));
}
/**
* Marks a component's DefaultProps declaration as "unresolved". A component's DefaultProps is
* marked as "unresolved" if we cannot safely infer the values of its defaultProps declarations
* without risking false negatives.
* @param {Object} component The component to mark.
* @returns {void}
*/
function markDefaultPropsAsUnresolved(component) {
components.set(component.node, {
defaultProps: 'unresolved',
});
}
/**
* Adds defaultProps to the component passed in.
* @param {ASTNode} component The component to add the defaultProps to.
* @param {Object[]|'unresolved'} defaultProps defaultProps to add to the component or the string "unresolved"
* if this component has defaultProps that can't be resolved.
* @returns {void}
*/
function addDefaultPropsToComponent(component, defaultProps) {
// Early return if this component's defaultProps is already marked as "unresolved".
if (component.defaultProps === 'unresolved') {
return;
}
if (defaultProps === 'unresolved') {
markDefaultPropsAsUnresolved(component);
return;
}
const defaults = component.defaultProps || {};
const newDefaultProps = Object.assign(
{},
defaults,
fromEntries(defaultProps.map((prop) => [prop.name, prop]))
);
components.set(component.node, {
defaultProps: newDefaultProps,
});
}
return {
MemberExpression(node) {
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
if (!isDefaultProp) {
return;
}
// find component this defaultProps belongs to
const component = utils.getRelatedComponent(node);
if (!component) {
return;
}
// e.g.:
// MyComponent.propTypes = {
// foo: React.PropTypes.string.isRequired,
// bar: React.PropTypes.string
// };
//
// or:
//
// MyComponent.propTypes = myPropTypes;
if (node.parent.type === 'AssignmentExpression') {
const expression = resolveNodeValue(node.parent.right);
if (!expression || expression.type !== 'ObjectExpression') {
// If a value can't be found, we mark the defaultProps declaration as "unresolved", because
// we should ignore this component and not report any errors for it, to avoid false-positives
// with e.g. external defaultProps declarations.
if (isDefaultProp) {
markDefaultPropsAsUnresolved(component);
}
return;
}
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
return;
}
// e.g.:
// MyComponent.propTypes.baz = React.PropTypes.string;
if (node.parent.type === 'MemberExpression' && node.parent.parent
&& node.parent.parent.type === 'AssignmentExpression') {
addDefaultPropsToComponent(component, [{
name: node.parent.property.name,
node: node.parent.parent,
}]);
}
},
// e.g.:
// class Hello extends React.Component {
// static get defaultProps() {
// return {
// name: 'Dean'
// };
// }
// render() {
// return <div>Hello {this.props.name}</div>;
// }
// }
MethodDefinition(node) {
if (!node.static || node.kind !== 'get') {
return;
}
if (!propsUtil.isDefaultPropsDeclaration(node)) {
return;
}
// find component this propTypes/defaultProps belongs to
const component = components.get(componentUtil.getParentES6Component(context, node));
if (!component) {
return;
}
const returnStatement = utils.findReturnStatement(node);
if (!returnStatement) {
return;
}
const expression = resolveNodeValue(returnStatement.argument);
if (!expression || expression.type !== 'ObjectExpression') {
return;
}
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
},
// e.g.:
// class Greeting extends React.Component {
// render() {
// return (
// <h1>Hello, {this.props.foo} {this.props.bar}</h1>
// );
// }
// static defaultProps = {
// foo: 'bar',
// bar: 'baz'
// };
// }
'ClassProperty, PropertyDefinition'(node) {
if (!(node.static && node.value)) {
return;
}
const propName = astUtil.getPropertyName(node);
const isDefaultProp = propName === 'defaultProps' || propName === 'getDefaultProps';
if (!isDefaultProp) {
return;
}
// find component this propTypes/defaultProps belongs to
const component = components.get(componentUtil.getParentES6Component(context, node));
if (!component) {
return;
}
const expression = resolveNodeValue(node.value);
if (!expression || expression.type !== 'ObjectExpression') {
return;
}
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
},
// e.g.:
// React.createClass({
// render: function() {
// return <div>{this.props.foo}</div>;
// },
// getDefaultProps: function() {
// return {
// foo: 'default'
// };
// }
// });
ObjectExpression(node) {
// find component this propTypes/defaultProps belongs to
const component = componentUtil.isES5Component(node, context) && components.get(node);
if (!component) {
return;
}
// Search for the proptypes declaration
node.properties.forEach((property) => {
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
return;
}
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property);
if (isDefaultProp && property.value.type === 'FunctionExpression') {
const returnStatement = utils.findReturnStatement(property);
if (!returnStatement || returnStatement.argument.type !== 'ObjectExpression') {
return;
}
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(returnStatement.argument));
}
});
},
};
};
+3
View File
@@ -0,0 +1,3 @@
export = docsUrl;
declare function docsUrl(ruleName: any): string;
//# sourceMappingURL=docsUrl.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"docsUrl.d.ts","sourceRoot":"","sources":["docsUrl.js"],"names":[],"mappings":";AAEA,gDAEC"}
+7
View File
@@ -0,0 +1,7 @@
'use strict';
function docsUrl(ruleName) {
return `https://github.com/jsx-eslint/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`;
}
module.exports = docsUrl;
+7
View File
@@ -0,0 +1,7 @@
export = error;
/**
* Logs out a message if there is no format option set.
* @param {string} message - Message to log.
*/
declare function error(message: string): void;
//# sourceMappingURL=error.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["error.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,gCAFW,MAAM,QAOhB"}
+14
View File
@@ -0,0 +1,14 @@
'use strict';
/**
* Logs out a message if there is no format option set.
* @param {string} message - Message to log.
*/
function error(message) {
if (!/=-(f|-format)=/.test(process.argv.join('='))) {
// eslint-disable-next-line no-console
console.error(message);
}
}
module.exports = error;
+7
View File
@@ -0,0 +1,7 @@
export function getAncestors(context: any, node: any): any;
export function getFirstTokens(context: any, node: any, count: any): any;
export function getScope(context: any, node: any): any;
export function getSourceCode(context: any): any;
export function getText(context: any, ...args: any[]): any;
export function markVariableAsUsed(name: any, node: any, context: any): any;
//# sourceMappingURL=eslint.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["eslint.js"],"names":[],"mappings":"AAMA,2DAGC;AAkBD,yEAGC;AAnBD,uDAOC;AAhBD,iDAEC;AA4BD,2DAIC;AAhBD,4EAKC"}
+46
View File
@@ -0,0 +1,46 @@
'use strict';
function getSourceCode(context) {
return context.getSourceCode ? context.getSourceCode() : context.sourceCode;
}
function getAncestors(context, node) {
const sourceCode = getSourceCode(context);
return sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors();
}
function getScope(context, node) {
const sourceCode = getSourceCode(context);
if (sourceCode.getScope) {
return sourceCode.getScope(node);
}
return context.getScope();
}
function markVariableAsUsed(name, node, context) {
const sourceCode = getSourceCode(context);
return sourceCode.markVariableAsUsed
? sourceCode.markVariableAsUsed(name, node)
: context.markVariableAsUsed(name);
}
function getFirstTokens(context, node, count) {
const sourceCode = getSourceCode(context);
return sourceCode.getFirstTokens ? sourceCode.getFirstTokens(node, count) : context.getFirstTokens(node, count);
}
function getText(context) {
const sourceCode = getSourceCode(context);
const args = Array.prototype.slice.call(arguments, 1);
return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args);
}
module.exports = {
getAncestors,
getFirstTokens,
getScope,
getSourceCode,
getText,
markVariableAsUsed,
};
@@ -0,0 +1,8 @@
export = getTokenBeforeClosingBracket;
/**
* Find the token before the closing bracket.
* @param {ASTNode} node - The JSX element node.
* @returns {Token} The token before the closing bracket.
*/
declare function getTokenBeforeClosingBracket(node: ASTNode): Token;
//# sourceMappingURL=getTokenBeforeClosingBracket.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"getTokenBeforeClosingBracket.d.ts","sourceRoot":"","sources":["getTokenBeforeClosingBracket.js"],"names":[],"mappings":";AAEA;;;;GAIG;AACH,oDAHW,OAAO,GACL,KAAK,CAQjB"}
@@ -0,0 +1,16 @@
'use strict';
/**
* Find the token before the closing bracket.
* @param {ASTNode} node - The JSX element node.
* @returns {Token} The token before the closing bracket.
*/
function getTokenBeforeClosingBracket(node) {
const attributes = node.attributes;
if (!attributes || attributes.length === 0) {
return node.name;
}
return attributes[attributes.length - 1];
}
module.exports = getTokenBeforeClosingBracket;
+3
View File
@@ -0,0 +1,3 @@
declare function _exports(node: ASTNode): boolean;
export = _exports;
//# sourceMappingURL=isCreateContext.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"isCreateContext.d.ts","sourceRoot":"","sources":["isCreateContext.js"],"names":[],"mappings":"AASiB,gCAHN,OAAO,GACL,OAAO,CA8CnB"}
+54
View File
@@ -0,0 +1,54 @@
'use strict';
const astUtil = require('./ast');
/**
* Checks if the node is a React.createContext call
* @param {ASTNode} node - The AST node being checked.
* @returns {boolean} - True if node is a React.createContext call, false if not.
*/
module.exports = function isCreateContext(node) {
if (
node.init
&& node.init.callee
) {
if (
astUtil.isCallExpression(node.init)
&& node.init.callee.name === 'createContext'
) {
return true;
}
if (
node.init.callee.type === 'MemberExpression'
&& node.init.callee.property
&& node.init.callee.property.name === 'createContext'
) {
return true;
}
}
if (
node.expression
&& node.expression.type === 'AssignmentExpression'
&& node.expression.operator === '='
&& astUtil.isCallExpression(node.expression.right)
&& node.expression.right.callee
) {
const right = node.expression.right;
if (right.callee.name === 'createContext') {
return true;
}
if (
right.callee.type === 'MemberExpression'
&& right.callee.property
&& right.callee.property.name === 'createContext'
) {
return true;
}
}
return false;
};
+3
View File
@@ -0,0 +1,3 @@
declare function _exports(context: Context, node: ASTNode): boolean;
export = _exports;
//# sourceMappingURL=isCreateElement.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"isCreateElement.d.ts","sourceRoot":"","sources":["isCreateElement.js"],"names":[],"mappings":"AAWiB,mCAJN,OAAO,QACP,OAAO,GACL,OAAO,CAwBnB"}
+34
View File
@@ -0,0 +1,34 @@
'use strict';
const pragmaUtil = require('./pragma');
const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport');
/**
* Checks if the node is a createElement call
* @param {Context} context - The AST node being checked.
* @param {ASTNode} node - The AST node being checked.
* @returns {boolean} - True if node is a createElement call object literal, False if not.
*/
module.exports = function isCreateElement(context, node) {
if (!node.callee) {
return false;
}
if (
node.callee.type === 'MemberExpression'
&& node.callee.property.name === 'createElement'
&& node.callee.object
&& node.callee.object.name === pragmaUtil.getFromContext(context)
) {
return true;
}
if (
node.callee.name === 'createElement'
&& isDestructuredFromPragmaImport(context, node, 'createElement')
) {
return true;
}
return false;
};
@@ -0,0 +1,3 @@
declare function _exports(context: Context, node: ASTNode, variable: string, ...args: any[]): boolean;
export = _exports;
//# sourceMappingURL=isDestructuredFromPragmaImport.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"isDestructuredFromPragmaImport.d.ts","sourceRoot":"","sources":["isDestructuredFromPragmaImport.js"],"names":[],"mappings":"AAciB,mCALN,OAAO,QACP,OAAO,YACP,MAAM,mBACJ,OAAO,CAmEnB"}
@@ -0,0 +1,80 @@
'use strict';
const astUtil = require('./ast');
const pragmaUtil = require('./pragma');
const variableUtil = require('./variable');
/**
* Check if variable is destructured from pragma import
*
* @param {Context} context eslint context
* @param {ASTNode} node The AST node to check
* @param {string} variable The variable name to check
* @returns {boolean} True if createElement is destructured from the pragma
*/
module.exports = function isDestructuredFromPragmaImport(context, node, variable) {
const pragma = pragmaUtil.getFromContext(context);
const variableInScope = variableUtil.getVariableFromContext(context, node, variable);
if (variableInScope) {
const latestDef = variableUtil.getLatestVariableDefinition(variableInScope);
if (latestDef) {
// check if latest definition is a variable declaration: 'variable = value'
if (latestDef.node.type === 'VariableDeclarator' && latestDef.node.init) {
// check for: 'variable = pragma.variable'
if (
latestDef.node.init.type === 'MemberExpression'
&& latestDef.node.init.object.type === 'Identifier'
&& latestDef.node.init.object.name === pragma
) {
return true;
}
// check for: '{variable} = pragma'
if (
latestDef.node.init.type === 'Identifier'
&& latestDef.node.init.name === pragma
) {
return true;
}
// "require('react')"
let requireExpression = null;
// get "require('react')" from: "{variable} = require('react')"
if (astUtil.isCallExpression(latestDef.node.init)) {
requireExpression = latestDef.node.init;
}
// get "require('react')" from: "variable = require('react').variable"
if (
!requireExpression
&& latestDef.node.init.type === 'MemberExpression'
&& astUtil.isCallExpression(latestDef.node.init.object)
) {
requireExpression = latestDef.node.init.object;
}
// check proper require.
if (
requireExpression
&& requireExpression.callee
&& requireExpression.callee.name === 'require'
&& requireExpression.arguments[0]
&& requireExpression.arguments[0].value === pragma.toLocaleLowerCase()
) {
return true;
}
return false;
}
// latest definition is an import declaration: import {<variable>} from 'react'
if (
latestDef.parent
&& latestDef.parent.type === 'ImportDeclaration'
&& latestDef.parent.source.value === pragma.toLocaleLowerCase()
) {
return true;
}
}
}
return false;
};
@@ -0,0 +1,3 @@
declare function _exports(word: string): boolean;
export = _exports;
//# sourceMappingURL=isFirstLetterCapitalized.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"isFirstLetterCapitalized.d.ts","sourceRoot":"","sources":["isFirstLetterCapitalized.js"],"names":[],"mappings":"AAOiB,gCAHN,MAAM,GACJ,OAAO,CAQnB"}
+14
View File
@@ -0,0 +1,14 @@
'use strict';
/**
* Check if the first letter of a string is capitalized.
* @param {string} word String to check
* @returns {boolean} True if first letter is capitalized.
*/
module.exports = function isFirstLetterCapitalized(word) {
if (!word) {
return false;
}
const firstLetter = word.replace(/^_+/, '').charAt(0);
return firstLetter.toUpperCase() === firstLetter;
};
+51
View File
@@ -0,0 +1,51 @@
/**
* Checks if a node represents a DOM element according to React.
* @param {object} node - JSXOpeningElement to check.
* @returns {boolean} Whether or not the node corresponds to a DOM element.
*/
export function isDOMComponent(node: object): boolean;
/**
* Test whether a JSXElement is a fragment
* @param {JSXElement} node
* @param {string} reactPragma
* @param {string} fragmentPragma
* @returns {boolean}
*/
export function isFragment(node: JSXElement, reactPragma: string, fragmentPragma: string): boolean;
/**
* Checks if a node represents a JSX element or fragment.
* @param {object} node - node to check.
* @returns {boolean} Whether or not the node if a JSX element or fragment.
*/
export function isJSX(node: object): boolean;
/**
* Check if node is like `key={...}` as in `<Foo key={...} />`
* @param {ASTNode} node
* @returns {boolean}
*/
export function isJSXAttributeKey(node: ASTNode): boolean;
/**
* Check if value has only whitespaces
* @param {unknown} value
* @returns {boolean}
*/
export function isWhiteSpaces(value: unknown): boolean;
/**
* Check if the node is returning JSX or null
*
* @param {Context} context The context of `ASTNode`.
* @param {ASTNode} ASTnode The AST node being checked
* @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases
* @param {boolean} [ignoreNull] If true, null return values will be ignored
* @returns {boolean} True if the node is returning JSX or null, false if not
*/
export function isReturningJSX(context: Context, ASTnode: ASTNode, strict?: boolean, ignoreNull?: boolean): boolean;
/**
* Check if the node is returning only null values
*
* @param {ASTNode} ASTnode The AST node being checked
* @param {Context} context The context of `ASTNode`.
* @returns {boolean} True if the node is returning only null values
*/
export function isReturningOnlyNull(ASTnode: ASTNode, context: Context): boolean;
//# sourceMappingURL=jsx.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jsx.d.ts","sourceRoot":"","sources":["jsx.js"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,qCAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;;;GAMG;AACH,iCALW,UAAU,eACV,MAAM,kBACN,MAAM,GACJ,OAAO,CAsBnB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAInB;AAED;;;;GAIG;AACH,wCAHW,OAAO,GACL,OAAO,CAOnB;AAED;;;;GAIG;AACH,qCAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;;;;;GAQG;AACH,wCANW,OAAO,WACP,OAAO,WACP,OAAO,eACP,OAAO,GACL,OAAO,CAgDnB;AAED;;;;;;GAMG;AACH,6CAJW,OAAO,WACP,OAAO,GACL,OAAO,CAsCnB"}
+196
View File
@@ -0,0 +1,196 @@
/**
* @fileoverview Utility functions for JSX
*/
'use strict';
const elementType = require('jsx-ast-utils/elementType');
const astUtil = require('./ast');
const isCreateElement = require('./isCreateElement');
const variableUtil = require('./variable');
// See https://github.com/babel/babel/blob/ce420ba51c68591e057696ef43e028f41c6e04cd/packages/babel-types/src/validators/react/isCompatTag.js
// for why we only test for the first character
const COMPAT_TAG_REGEX = /^[a-z]/;
/**
* Checks if a node represents a DOM element according to React.
* @param {object} node - JSXOpeningElement to check.
* @returns {boolean} Whether or not the node corresponds to a DOM element.
*/
function isDOMComponent(node) {
const name = elementType(node);
return COMPAT_TAG_REGEX.test(name);
}
/**
* Test whether a JSXElement is a fragment
* @param {JSXElement} node
* @param {string} reactPragma
* @param {string} fragmentPragma
* @returns {boolean}
*/
function isFragment(node, reactPragma, fragmentPragma) {
const name = node.openingElement.name;
// <Fragment>
if (name.type === 'JSXIdentifier' && name.name === fragmentPragma) {
return true;
}
// <React.Fragment>
if (
name.type === 'JSXMemberExpression'
&& name.object.type === 'JSXIdentifier'
&& name.object.name === reactPragma
&& name.property.type === 'JSXIdentifier'
&& name.property.name === fragmentPragma
) {
return true;
}
return false;
}
/**
* Checks if a node represents a JSX element or fragment.
* @param {object} node - node to check.
* @returns {boolean} Whether or not the node if a JSX element or fragment.
*/
function isJSX(node) {
return node && ['JSXElement', 'JSXFragment'].indexOf(node.type) >= 0;
}
/**
* Check if node is like `key={...}` as in `<Foo key={...} />`
* @param {ASTNode} node
* @returns {boolean}
*/
function isJSXAttributeKey(node) {
return node.type === 'JSXAttribute'
&& node.name
&& node.name.type === 'JSXIdentifier'
&& node.name.name === 'key';
}
/**
* Check if value has only whitespaces
* @param {unknown} value
* @returns {boolean}
*/
function isWhiteSpaces(value) {
return typeof value === 'string' ? /^\s*$/.test(value) : false;
}
/**
* Check if the node is returning JSX or null
*
* @param {Context} context The context of `ASTNode`.
* @param {ASTNode} ASTnode The AST node being checked
* @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases
* @param {boolean} [ignoreNull] If true, null return values will be ignored
* @returns {boolean} True if the node is returning JSX or null, false if not
*/
function isReturningJSX(context, ASTnode, strict, ignoreNull) {
const isJSXValue = (node) => {
if (!node) {
return false;
}
switch (node.type) {
case 'ConditionalExpression':
if (strict) {
return isJSXValue(node.consequent) && isJSXValue(node.alternate);
}
return isJSXValue(node.consequent) || isJSXValue(node.alternate);
case 'LogicalExpression':
if (strict) {
return isJSXValue(node.left) && isJSXValue(node.right);
}
return isJSXValue(node.left) || isJSXValue(node.right);
case 'SequenceExpression':
return isJSXValue(node.expressions[node.expressions.length - 1]);
case 'JSXElement':
case 'JSXFragment':
return true;
case 'CallExpression':
return isCreateElement(context, node);
case 'Literal':
if (!ignoreNull && node.value === null) {
return true;
}
return false;
case 'Identifier': {
const variable = variableUtil.findVariableByName(context, node, node.name);
return isJSX(variable);
}
default:
return false;
}
};
let found = false;
astUtil.traverseReturns(ASTnode, context, (node, breakTraverse) => {
if (isJSXValue(node)) {
found = true;
breakTraverse();
}
});
return found;
}
/**
* Check if the node is returning only null values
*
* @param {ASTNode} ASTnode The AST node being checked
* @param {Context} context The context of `ASTNode`.
* @returns {boolean} True if the node is returning only null values
*/
function isReturningOnlyNull(ASTnode, context) {
let found = false;
let foundSomethingElse = false;
astUtil.traverseReturns(ASTnode, context, (node) => {
// Traverse return statement
astUtil.traverse(node, {
enter(childNode) {
const setFound = () => {
found = true;
this.skip();
};
const setFoundSomethingElse = () => {
foundSomethingElse = true;
this.skip();
};
switch (childNode.type) {
case 'ReturnStatement':
break;
case 'ConditionalExpression':
if (childNode.consequent.value === null && childNode.alternate.value === null) {
setFound();
}
break;
case 'Literal':
if (childNode.value === null) {
setFound();
}
break;
default:
setFoundSomethingElse();
}
},
});
});
return found && !foundSomethingElse;
}
module.exports = {
isDOMComponent,
isFragment,
isJSX,
isJSXAttributeKey,
isWhiteSpaces,
isReturningJSX,
isReturningOnlyNull,
};
+6
View File
@@ -0,0 +1,6 @@
declare const _exports: {
instance: string[];
static: string[];
};
export = _exports;
//# sourceMappingURL=lifecycleMethods.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"lifecycleMethods.d.ts","sourceRoot":"","sources":["lifecycleMethods.js"],"names":[],"mappings":""}
+30
View File
@@ -0,0 +1,30 @@
/**
* @fileoverview lifecycle methods
* @author Tan Nguyen
*/
'use strict';
module.exports = {
instance: [
'getDefaultProps',
'getInitialState',
'getChildContext',
'componentWillMount',
'UNSAFE_componentWillMount',
'componentDidMount',
'componentWillReceiveProps',
'UNSAFE_componentWillReceiveProps',
'shouldComponentUpdate',
'componentWillUpdate',
'UNSAFE_componentWillUpdate',
'getSnapshotBeforeUpdate',
'componentDidUpdate',
'componentDidCatch',
'componentWillUnmount',
'render',
],
static: [
'getDerivedStateFromProps',
],
};
+3
View File
@@ -0,0 +1,3 @@
export function getFormComponents(context: any): Map<any, any>;
export function getLinkComponents(context: any): Map<any, any>;
//# sourceMappingURL=linkComponents.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"linkComponents.d.ts","sourceRoot":"","sources":["linkComponents.js"],"names":[],"mappings":"AAmBA,+DAWC;AAED,+DAWC"}
+49
View File
@@ -0,0 +1,49 @@
/**
* @fileoverview Utility functions for propWrapperFunctions setting
*/
'use strict';
const iterFrom = require('es-iterator-helpers/Iterator.from');
const map = require('es-iterator-helpers/Iterator.prototype.map');
/** TODO: type {(string | { name: string, linkAttribute: string })[]} */
/** @type {any} */
const DEFAULT_LINK_COMPONENTS = ['a'];
const DEFAULT_LINK_ATTRIBUTE = 'href';
/** TODO: type {(string | { name: string, formAttribute: string })[]} */
/** @type {any} */
const DEFAULT_FORM_COMPONENTS = ['form'];
const DEFAULT_FORM_ATTRIBUTE = 'action';
function getFormComponents(context) {
const settings = context.settings || {};
const formComponents = /** @type {typeof DEFAULT_FORM_COMPONENTS} */ (
DEFAULT_FORM_COMPONENTS.concat(settings.formComponents || [])
);
return new Map(map(iterFrom(formComponents), (value) => {
if (typeof value === 'string') {
return [value, [DEFAULT_FORM_ATTRIBUTE]];
}
return [value.name, [].concat(value.formAttribute)];
}));
}
function getLinkComponents(context) {
const settings = context.settings || {};
const linkComponents = /** @type {typeof DEFAULT_LINK_COMPONENTS} */ (
DEFAULT_LINK_COMPONENTS.concat(settings.linkComponents || [])
);
return new Map(map(iterFrom(linkComponents), (value) => {
if (typeof value === 'string') {
return [value, [DEFAULT_LINK_ATTRIBUTE]];
}
return [value.name, [].concat(value.linkAttribute)];
}));
}
module.exports = {
getFormComponents,
getLinkComponents,
};
+7
View File
@@ -0,0 +1,7 @@
export = log;
/**
* Logs out a message if there is no format option set.
* @param {string} message - Message to log.
*/
declare function log(message: string): void;
//# sourceMappingURL=log.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["log.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,8BAFW,MAAM,QAOhB"}

Some files were not shown because too many files have changed in this diff Show More