Ajout de promotion et de commande
This commit is contained in:
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=boolean-prop-naming.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"boolean-prop-naming.d.ts","sourceRoot":"","sources":["boolean-prop-naming.js"],"names":[],"mappings":"wBAyCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* @fileoverview Enforces consistent naming for boolean props
|
||||
* @author Ev Haus
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const flatMap = require('array.prototype.flatmap');
|
||||
const values = require('object.values');
|
||||
|
||||
const Components = require('../util/Components');
|
||||
const propsUtil = require('../util/props');
|
||||
const astUtil = require('../util/ast');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const propWrapperUtil = require('../util/propWrapper');
|
||||
const report = require('../util/report');
|
||||
const eslintUtil = require('../util/eslint');
|
||||
|
||||
const getSourceCode = eslintUtil.getSourceCode;
|
||||
const getText = eslintUtil.getText;
|
||||
|
||||
/**
|
||||
* Checks if prop is nested
|
||||
* @param {Object} prop Property object, single prop type declaration
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function nestedPropTypes(prop) {
|
||||
return (
|
||||
prop.type === 'Property'
|
||||
&& astUtil.isCallExpression(prop.value)
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
patternMismatch: 'Prop name `{{propName}}` doesn’t match rule `{{pattern}}`',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
category: 'Stylistic Issues',
|
||||
description: 'Enforces consistent naming for boolean props',
|
||||
recommended: false,
|
||||
url: docsUrl('boolean-prop-naming'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
propTypeNames: {
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
minItems: 1,
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
},
|
||||
rule: {
|
||||
default: '^(is|has)[A-Z]([A-Za-z0-9]?)+',
|
||||
minLength: 1,
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
minLength: 1,
|
||||
type: 'string',
|
||||
},
|
||||
validateNested: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
}],
|
||||
},
|
||||
|
||||
create: Components.detect((context, components, utils) => {
|
||||
const config = context.options[0] || {};
|
||||
const rule = config.rule ? new RegExp(config.rule) : null;
|
||||
const propTypeNames = config.propTypeNames || ['bool'];
|
||||
|
||||
// Remembers all Flowtype object definitions
|
||||
const objectTypeAnnotations = new Map();
|
||||
|
||||
/**
|
||||
* Returns the prop key to ensure we handle the following cases:
|
||||
* propTypes: {
|
||||
* full: React.PropTypes.bool,
|
||||
* short: PropTypes.bool,
|
||||
* direct: bool,
|
||||
* required: PropTypes.bool.isRequired
|
||||
* }
|
||||
* @param {Object} node The node we're getting the name of
|
||||
* @returns {string | null}
|
||||
*/
|
||||
function getPropKey(node) {
|
||||
// Check for `ExperimentalSpreadProperty` (eslint 3/4) and `SpreadElement` (eslint 5)
|
||||
// so we can skip validation of those fields.
|
||||
// Otherwise it will look for `node.value.property` which doesn't exist and breaks eslint.
|
||||
if (node.type === 'ExperimentalSpreadProperty' || node.type === 'SpreadElement') {
|
||||
return null;
|
||||
}
|
||||
if (node.value && node.value.property) {
|
||||
const name = node.value.property.name;
|
||||
if (name === 'isRequired') {
|
||||
if (node.value.object && node.value.object.property) {
|
||||
return node.value.object.property.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
if (node.value && node.value.type === 'Identifier') {
|
||||
return node.value.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the given node (prop)
|
||||
* @param {Object} node The node we're getting the name of
|
||||
* @returns {string}
|
||||
*/
|
||||
function getPropName(node) {
|
||||
// Due to this bug https://github.com/babel/babel-eslint/issues/307
|
||||
// we can't get the name of the Flow object key name. So we have
|
||||
// to hack around it for now.
|
||||
if (node.type === 'ObjectTypeProperty') {
|
||||
return getSourceCode(context).getFirstToken(node).value;
|
||||
}
|
||||
|
||||
return node.key.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if prop is declared in flow way
|
||||
* @param {Object} prop Property object, single prop type declaration
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function flowCheck(prop) {
|
||||
return (
|
||||
prop.type === 'ObjectTypeProperty'
|
||||
&& prop.value.type === 'BooleanTypeAnnotation'
|
||||
&& rule.test(getPropName(prop)) === false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if prop is declared in regular way
|
||||
* @param {Object} prop Property object, single prop type declaration
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function regularCheck(prop) {
|
||||
const propKey = getPropKey(prop);
|
||||
return (
|
||||
propKey
|
||||
&& propTypeNames.indexOf(propKey) >= 0
|
||||
&& rule.test(getPropName(prop)) === false
|
||||
);
|
||||
}
|
||||
|
||||
function tsCheck(prop) {
|
||||
if (prop.type !== 'TSPropertySignature') return false;
|
||||
const typeAnnotation = (prop.typeAnnotation || {}).typeAnnotation;
|
||||
return (
|
||||
typeAnnotation
|
||||
&& typeAnnotation.type === 'TSBooleanKeyword'
|
||||
&& rule.test(getPropName(prop)) === false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs recursive check on all proptypes
|
||||
* @param {Array} proptypes A list of Property object (for each proptype defined)
|
||||
* @param {Function} addInvalidProp callback to run for each error
|
||||
*/
|
||||
function runCheck(proptypes, addInvalidProp) {
|
||||
if (proptypes) {
|
||||
proptypes.forEach((prop) => {
|
||||
if (config.validateNested && nestedPropTypes(prop)) {
|
||||
runCheck(prop.value.arguments[0].properties, addInvalidProp);
|
||||
return;
|
||||
}
|
||||
if (flowCheck(prop) || regularCheck(prop) || tsCheck(prop)) {
|
||||
addInvalidProp(prop);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and mark props with invalid naming
|
||||
* @param {Object} node The component node we're testing
|
||||
* @param {Array} proptypes A list of Property object (for each proptype defined)
|
||||
*/
|
||||
function validatePropNaming(node, proptypes) {
|
||||
const component = components.get(node) || node;
|
||||
const invalidProps = component.invalidProps || [];
|
||||
|
||||
runCheck(proptypes, (prop) => {
|
||||
invalidProps.push(prop);
|
||||
});
|
||||
|
||||
components.set(node, {
|
||||
invalidProps,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports invalid prop naming
|
||||
* @param {Object} component The component to process
|
||||
*/
|
||||
function reportInvalidNaming(component) {
|
||||
component.invalidProps.forEach((propNode) => {
|
||||
const propName = getPropName(propNode);
|
||||
report(context, config.message || messages.patternMismatch, !config.message && 'patternMismatch', {
|
||||
node: propNode,
|
||||
data: {
|
||||
component: propName,
|
||||
propName,
|
||||
pattern: config.rule,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkPropWrapperArguments(node, args) {
|
||||
if (!node || !Array.isArray(args)) {
|
||||
return;
|
||||
}
|
||||
args.filter((arg) => arg.type === 'ObjectExpression').forEach((object) => validatePropNaming(node, object.properties));
|
||||
}
|
||||
|
||||
function getComponentTypeAnnotation(component) {
|
||||
// If this is a functional component that uses a global type, check it
|
||||
if (
|
||||
(component.node.type === 'FunctionDeclaration' || component.node.type === 'ArrowFunctionExpression')
|
||||
&& component.node.params
|
||||
&& component.node.params.length > 0
|
||||
&& component.node.params[0].typeAnnotation
|
||||
) {
|
||||
return component.node.params[0].typeAnnotation.typeAnnotation;
|
||||
}
|
||||
|
||||
if (
|
||||
!component.node.parent
|
||||
|| component.node.parent.type !== 'VariableDeclarator'
|
||||
|| !component.node.parent.id
|
||||
|| component.node.parent.id.type !== 'Identifier'
|
||||
|| !component.node.parent.id.typeAnnotation
|
||||
|| !component.node.parent.id.typeAnnotation.typeAnnotation
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const annotationTypeArguments = propsUtil.getTypeArguments(
|
||||
component.node.parent.id.typeAnnotation.typeAnnotation
|
||||
);
|
||||
if (
|
||||
annotationTypeArguments && (
|
||||
annotationTypeArguments.type === 'TSTypeParameterInstantiation'
|
||||
|| annotationTypeArguments.type === 'TypeParameterInstantiation'
|
||||
)
|
||||
) {
|
||||
return annotationTypeArguments.params.find(
|
||||
(param) => param.type === 'TSTypeReference' || param.type === 'GenericTypeAnnotation'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function findAllTypeAnnotations(identifier, node) {
|
||||
if (node.type === 'TSTypeLiteral' || node.type === 'ObjectTypeAnnotation' || node.type === 'TSInterfaceBody') {
|
||||
const currentNode = [].concat(
|
||||
objectTypeAnnotations.get(identifier.name) || [],
|
||||
node
|
||||
);
|
||||
objectTypeAnnotations.set(identifier.name, currentNode);
|
||||
} else if (
|
||||
node.type === 'TSParenthesizedType'
|
||||
&& (
|
||||
node.typeAnnotation.type === 'TSIntersectionType'
|
||||
|| node.typeAnnotation.type === 'TSUnionType'
|
||||
)
|
||||
) {
|
||||
node.typeAnnotation.types.forEach((type) => {
|
||||
findAllTypeAnnotations(identifier, type);
|
||||
});
|
||||
} else if (
|
||||
node.type === 'TSIntersectionType'
|
||||
|| node.type === 'TSUnionType'
|
||||
|| node.type === 'IntersectionTypeAnnotation'
|
||||
|| node.type === 'UnionTypeAnnotation'
|
||||
) {
|
||||
node.types.forEach((type) => {
|
||||
findAllTypeAnnotations(identifier, type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
'ClassProperty, PropertyDefinition'(node) {
|
||||
if (!rule || !propsUtil.isPropTypesDeclaration(node)) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
node.value
|
||||
&& astUtil.isCallExpression(node.value)
|
||||
&& propWrapperUtil.isPropWrapperFunction(
|
||||
context,
|
||||
getText(context, node.value.callee)
|
||||
)
|
||||
) {
|
||||
checkPropWrapperArguments(node, node.value.arguments);
|
||||
}
|
||||
if (node.value && node.value.properties) {
|
||||
validatePropNaming(node, node.value.properties);
|
||||
}
|
||||
if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
|
||||
validatePropNaming(node, node.typeAnnotation.typeAnnotation.properties);
|
||||
}
|
||||
},
|
||||
|
||||
MemberExpression(node) {
|
||||
if (!rule || !propsUtil.isPropTypesDeclaration(node)) {
|
||||
return;
|
||||
}
|
||||
const component = utils.getRelatedComponent(node);
|
||||
if (!component || !node.parent.right) {
|
||||
return;
|
||||
}
|
||||
const right = node.parent.right;
|
||||
if (
|
||||
astUtil.isCallExpression(right)
|
||||
&& propWrapperUtil.isPropWrapperFunction(
|
||||
context,
|
||||
getText(context, right.callee)
|
||||
)
|
||||
) {
|
||||
checkPropWrapperArguments(component.node, right.arguments);
|
||||
return;
|
||||
}
|
||||
validatePropNaming(component.node, node.parent.right.properties);
|
||||
},
|
||||
|
||||
ObjectExpression(node) {
|
||||
if (!rule) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for the proptypes declaration
|
||||
node.properties.forEach((property) => {
|
||||
if (!propsUtil.isPropTypesDeclaration(property)) {
|
||||
return;
|
||||
}
|
||||
validatePropNaming(node, property.value.properties);
|
||||
});
|
||||
},
|
||||
|
||||
TypeAlias(node) {
|
||||
findAllTypeAnnotations(node.id, node.right);
|
||||
},
|
||||
|
||||
TSTypeAliasDeclaration(node) {
|
||||
findAllTypeAnnotations(node.id, node.typeAnnotation);
|
||||
},
|
||||
|
||||
TSInterfaceDeclaration(node) {
|
||||
findAllTypeAnnotations(node.id, node.body);
|
||||
},
|
||||
|
||||
// eslint-disable-next-line object-shorthand
|
||||
'Program:exit'() {
|
||||
if (!rule) {
|
||||
return;
|
||||
}
|
||||
|
||||
values(components.list()).forEach((component) => {
|
||||
const annotation = getComponentTypeAnnotation(component);
|
||||
|
||||
if (annotation) {
|
||||
let propType;
|
||||
if (annotation.type === 'GenericTypeAnnotation') {
|
||||
propType = objectTypeAnnotations.get(annotation.id.name);
|
||||
} else if (annotation.type === 'ObjectTypeAnnotation' || annotation.type === 'TSTypeLiteral') {
|
||||
propType = annotation;
|
||||
} else if (annotation.type === 'TSTypeReference') {
|
||||
propType = objectTypeAnnotations.get(annotation.typeName.name);
|
||||
} else if (annotation.type === 'TSIntersectionType') {
|
||||
propType = flatMap(annotation.types, (type) => (
|
||||
type.type === 'TSTypeReference'
|
||||
? objectTypeAnnotations.get(type.typeName.name)
|
||||
: type
|
||||
));
|
||||
}
|
||||
|
||||
if (propType) {
|
||||
[].concat(propType).filter(Boolean).forEach((prop) => {
|
||||
validatePropNaming(
|
||||
component.node,
|
||||
prop.properties || prop.members || prop.body
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (component.invalidProps && component.invalidProps.length > 0) {
|
||||
reportInvalidNaming(component);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset cache
|
||||
objectTypeAnnotations.clear();
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=button-has-type.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"button-has-type.d.ts","sourceRoot":"","sources":["button-has-type.js"],"names":[],"mappings":"wBA8BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @fileoverview Forbid "button" element without an explicit "type" attribute
|
||||
* @author Filipp Riabchun
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const getProp = require('jsx-ast-utils/getProp');
|
||||
const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const isCreateElement = require('../util/isCreateElement');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const optionDefaults = {
|
||||
button: true,
|
||||
submit: true,
|
||||
reset: true,
|
||||
};
|
||||
|
||||
const messages = {
|
||||
missingType: 'Missing an explicit type attribute for button',
|
||||
complexType: 'The button type attribute must be specified by a static string or a trivial ternary expression',
|
||||
invalidValue: '"{{value}}" is an invalid value for button type attribute',
|
||||
forbiddenValue: '"{{value}}" is an invalid value for button type attribute',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow usage of `button` elements without an explicit `type` attribute',
|
||||
category: 'Possible Errors',
|
||||
recommended: false,
|
||||
url: docsUrl('button-has-type'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
button: {
|
||||
default: optionDefaults.button,
|
||||
type: 'boolean',
|
||||
},
|
||||
submit: {
|
||||
default: optionDefaults.submit,
|
||||
type: 'boolean',
|
||||
},
|
||||
reset: {
|
||||
default: optionDefaults.reset,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const configuration = Object.assign({}, optionDefaults, context.options[0]);
|
||||
|
||||
function reportMissing(node) {
|
||||
report(context, messages.missingType, 'missingType', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
|
||||
function reportComplex(node) {
|
||||
report(context, messages.complexType, 'complexType', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
|
||||
function checkValue(node, value) {
|
||||
if (!(value in configuration)) {
|
||||
report(context, messages.invalidValue, 'invalidValue', {
|
||||
node,
|
||||
data: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
} else if (!configuration[value]) {
|
||||
report(context, messages.forbiddenValue, 'forbiddenValue', {
|
||||
node,
|
||||
data: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkExpression(node, expression) {
|
||||
switch (expression.type) {
|
||||
case 'Literal':
|
||||
checkValue(node, expression.value);
|
||||
return;
|
||||
case 'TemplateLiteral':
|
||||
if (expression.expressions.length === 0) {
|
||||
checkValue(node, expression.quasis[0].value.raw);
|
||||
} else {
|
||||
reportComplex(expression);
|
||||
}
|
||||
return;
|
||||
case 'ConditionalExpression':
|
||||
checkExpression(node, expression.consequent);
|
||||
checkExpression(node, expression.alternate);
|
||||
return;
|
||||
default:
|
||||
reportComplex(expression);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
JSXElement(node) {
|
||||
if (node.openingElement.name.name !== 'button') {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeProp = getProp(node.openingElement.attributes, 'type');
|
||||
|
||||
if (!typeProp) {
|
||||
reportMissing(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeProp.value && typeProp.value.type === 'JSXExpressionContainer') {
|
||||
checkExpression(node, typeProp.value.expression);
|
||||
return;
|
||||
}
|
||||
|
||||
const propValue = getLiteralPropValue(typeProp);
|
||||
checkValue(node, propValue);
|
||||
},
|
||||
CallExpression(node) {
|
||||
if (!isCreateElement(context, node) || node.arguments.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.arguments[0].type !== 'Literal' || node.arguments[0].value !== 'button') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.arguments[1] || node.arguments[1].type !== 'ObjectExpression') {
|
||||
reportMissing(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const props = node.arguments[1].properties;
|
||||
const typeProp = props.find((prop) => (
|
||||
'key' in prop
|
||||
&& prop.key
|
||||
&& 'name' in prop.key
|
||||
&& prop.key.name === 'type'
|
||||
));
|
||||
|
||||
if (!typeProp) {
|
||||
reportMissing(node);
|
||||
return;
|
||||
}
|
||||
|
||||
checkExpression(node, 'value' in typeProp ? typeProp.value : undefined);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=checked-requires-onchange-or-readonly.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"checked-requires-onchange-or-readonly.d.ts","sourceRoot":"","sources":["checked-requires-onchange-or-readonly.js"],"names":[],"mappings":"wBA2CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
Generated
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @fileoverview Enforce the use of the 'onChange' or 'readonly' attribute when 'checked' is used'
|
||||
* @author Jaesoekjjang
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ASTUtils = require('jsx-ast-utils');
|
||||
const flatMap = require('array.prototype.flatmap');
|
||||
const isCreateElement = require('../util/isCreateElement');
|
||||
const report = require('../util/report');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
|
||||
const messages = {
|
||||
missingProperty: '`checked` should be used with either `onChange` or `readOnly`.',
|
||||
exclusiveCheckedAttribute: 'Use either `checked` or `defaultChecked`, but not both.',
|
||||
};
|
||||
|
||||
const targetPropSet = new Set(['checked', 'onChange', 'readOnly', 'defaultChecked']);
|
||||
|
||||
const defaultOptions = {
|
||||
ignoreMissingProperties: false,
|
||||
ignoreExclusiveCheckedAttribute: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {object[]} properties
|
||||
* @param {string} keyName
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
function extractTargetProps(properties, keyName) {
|
||||
return new Set(
|
||||
flatMap(
|
||||
properties,
|
||||
(prop) => (
|
||||
prop[keyName] && targetPropSet.has(prop[keyName].name)
|
||||
? [prop[keyName].name]
|
||||
: []
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce using `onChange` or `readonly` attribute when `checked` is used',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('checked-requires-onchange-or-readonly'),
|
||||
},
|
||||
messages,
|
||||
schema: [{
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
ignoreMissingProperties: {
|
||||
type: 'boolean',
|
||||
},
|
||||
ignoreExclusiveCheckedAttribute: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
create(context) {
|
||||
const options = Object.assign({}, defaultOptions, context.options[0]);
|
||||
|
||||
function reportMissingProperty(node) {
|
||||
report(
|
||||
context,
|
||||
messages.missingProperty,
|
||||
'missingProperty',
|
||||
{ node }
|
||||
);
|
||||
}
|
||||
|
||||
function reportExclusiveCheckedAttribute(node) {
|
||||
report(
|
||||
context,
|
||||
messages.exclusiveCheckedAttribute,
|
||||
'exclusiveCheckedAttribute',
|
||||
{ node }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ASTNode} node
|
||||
* @param {Set<string>} propSet
|
||||
* @returns {void}
|
||||
*/
|
||||
const checkAttributesAndReport = (node, propSet) => {
|
||||
if (!propSet.has('checked')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.ignoreExclusiveCheckedAttribute && propSet.has('defaultChecked')) {
|
||||
reportExclusiveCheckedAttribute(node);
|
||||
}
|
||||
|
||||
if (
|
||||
!options.ignoreMissingProperties
|
||||
&& !(propSet.has('onChange') || propSet.has('readOnly'))
|
||||
) {
|
||||
reportMissingProperty(node);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
JSXOpeningElement(node) {
|
||||
if (ASTUtils.elementType(node) !== 'input') {
|
||||
return;
|
||||
}
|
||||
|
||||
const propSet = extractTargetProps(node.attributes, 'name');
|
||||
checkAttributesAndReport(node, propSet);
|
||||
},
|
||||
CallExpression(node) {
|
||||
if (!isCreateElement(context, node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstArg = node.arguments[0];
|
||||
const secondArg = node.arguments[1];
|
||||
if (
|
||||
!firstArg
|
||||
|| firstArg.type !== 'Literal'
|
||||
|| firstArg.value !== 'input'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!secondArg || secondArg.type !== 'ObjectExpression') {
|
||||
return;
|
||||
}
|
||||
|
||||
const propSet = extractTargetProps(secondArg.properties, 'key');
|
||||
checkAttributesAndReport(node, propSet);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=default-props-match-prop-types.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"default-props-match-prop-types.d.ts","sourceRoot":"","sources":["default-props-match-prop-types.js"],"names":[],"mappings":"wBAuBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @fileOverview Enforce all defaultProps are defined in propTypes
|
||||
* @author Vitor Balocco
|
||||
* @author Roy Sutton
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const values = require('object.values');
|
||||
|
||||
const Components = require('../util/Components');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
requiredHasDefault: 'defaultProp "{{name}}" defined for isRequired propType.',
|
||||
defaultHasNoType: 'defaultProp "{{name}}" has no corresponding propTypes declaration.',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce all defaultProps have a corresponding non-required PropType',
|
||||
category: 'Best Practices',
|
||||
url: docsUrl('default-props-match-prop-types'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
allowRequiredDefaults: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create: Components.detect((context, components) => {
|
||||
const configuration = context.options[0] || {};
|
||||
const allowRequiredDefaults = configuration.allowRequiredDefaults || false;
|
||||
|
||||
/**
|
||||
* Reports all defaultProps passed in that don't have an appropriate propTypes counterpart.
|
||||
* @param {Object[]} propTypes Array of propTypes to check.
|
||||
* @param {Object} defaultProps Object of defaultProps to check. Keys are the props names.
|
||||
* @return {void}
|
||||
*/
|
||||
function reportInvalidDefaultProps(propTypes, defaultProps) {
|
||||
// If this defaultProps is "unresolved" or the propTypes is undefined, then we should ignore
|
||||
// this component and not report any errors for it, to avoid false-positives with e.g.
|
||||
// external defaultProps/propTypes declarations or spread operators.
|
||||
if (defaultProps === 'unresolved' || !propTypes || Object.keys(propTypes).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(defaultProps).forEach((defaultPropName) => {
|
||||
const defaultProp = defaultProps[defaultPropName];
|
||||
const prop = propTypes[defaultPropName];
|
||||
|
||||
if (prop && (allowRequiredDefaults || !prop.isRequired)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop) {
|
||||
report(context, messages.requiredHasDefault, 'requiredHasDefault', {
|
||||
node: defaultProp.node,
|
||||
data: {
|
||||
name: defaultPropName,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
report(context, messages.defaultHasNoType, 'defaultHasNoType', {
|
||||
node: defaultProp.node,
|
||||
data: {
|
||||
name: defaultPropName,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public API
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
'Program:exit'() {
|
||||
// If no defaultProps could be found, we don't report anything.
|
||||
values(components.list())
|
||||
.filter((component) => component.defaultProps)
|
||||
.forEach((component) => {
|
||||
reportInvalidDefaultProps(
|
||||
component.declaredPropTypes,
|
||||
component.defaultProps || {}
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=destructuring-assignment.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"destructuring-assignment.d.ts","sourceRoot":"","sources":["destructuring-assignment.js"],"names":[],"mappings":"wBA2DW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* @fileoverview Enforce consistent usage of destructuring assignment of props, state, and context.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Components = require('../util/Components');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const eslintUtil = require('../util/eslint');
|
||||
const isAssignmentLHS = require('../util/ast').isAssignmentLHS;
|
||||
const report = require('../util/report');
|
||||
|
||||
const getScope = eslintUtil.getScope;
|
||||
const getText = eslintUtil.getText;
|
||||
|
||||
const DEFAULT_OPTION = 'always';
|
||||
|
||||
function createSFCParams() {
|
||||
const queue = [];
|
||||
|
||||
return {
|
||||
push(params) {
|
||||
queue.unshift(params);
|
||||
},
|
||||
pop() {
|
||||
queue.shift();
|
||||
},
|
||||
propsName() {
|
||||
const found = queue.find((params) => {
|
||||
const props = params[0];
|
||||
return props && !props.destructuring && props.name;
|
||||
});
|
||||
return found && found[0] && found[0].name;
|
||||
},
|
||||
contextName() {
|
||||
const found = queue.find((params) => {
|
||||
const context = params[1];
|
||||
return context && !context.destructuring && context.name;
|
||||
});
|
||||
return found && found[1] && found[1].name;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function evalParams(params) {
|
||||
return params.map((param) => ({
|
||||
destructuring: param.type === 'ObjectPattern',
|
||||
name: param.type === 'Identifier' && param.name,
|
||||
}));
|
||||
}
|
||||
|
||||
const messages = {
|
||||
noDestructPropsInSFCArg: 'Must never use destructuring props assignment in SFC argument',
|
||||
noDestructContextInSFCArg: 'Must never use destructuring context assignment in SFC argument',
|
||||
noDestructAssignment: 'Must never use destructuring {{type}} assignment',
|
||||
useDestructAssignment: 'Must use destructuring {{type}} assignment',
|
||||
destructureInSignature: 'Must destructure props in the function signature.',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce consistent usage of destructuring assignment of props, state, and context',
|
||||
category: 'Stylistic Issues',
|
||||
recommended: false,
|
||||
url: docsUrl('destructuring-assignment'),
|
||||
},
|
||||
fixable: 'code',
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'string',
|
||||
enum: [
|
||||
'always',
|
||||
'never',
|
||||
],
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
ignoreClassFields: {
|
||||
type: 'boolean',
|
||||
},
|
||||
destructureInSignature: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'always',
|
||||
'ignore',
|
||||
],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create: Components.detect((context, components, utils) => {
|
||||
const configuration = context.options[0] || DEFAULT_OPTION;
|
||||
const ignoreClassFields = (context.options[1] && (context.options[1].ignoreClassFields === true)) || false;
|
||||
const destructureInSignature = (context.options[1] && context.options[1].destructureInSignature) || 'ignore';
|
||||
const sfcParams = createSFCParams();
|
||||
|
||||
/**
|
||||
* @param {ASTNode} node We expect either an ArrowFunctionExpression,
|
||||
* FunctionDeclaration, or FunctionExpression
|
||||
*/
|
||||
function handleStatelessComponent(node) {
|
||||
const params = evalParams(node.params);
|
||||
|
||||
const SFCComponent = components.get(getScope(context, node).block);
|
||||
if (!SFCComponent) {
|
||||
return;
|
||||
}
|
||||
sfcParams.push(params);
|
||||
|
||||
if (params[0] && params[0].destructuring && components.get(node) && configuration === 'never') {
|
||||
report(context, messages.noDestructPropsInSFCArg, 'noDestructPropsInSFCArg', {
|
||||
node,
|
||||
});
|
||||
} else if (params[1] && params[1].destructuring && components.get(node) && configuration === 'never') {
|
||||
report(context, messages.noDestructContextInSFCArg, 'noDestructContextInSFCArg', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleStatelessComponentExit(node) {
|
||||
const SFCComponent = components.get(getScope(context, node).block);
|
||||
if (SFCComponent) {
|
||||
sfcParams.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSFCUsage(node) {
|
||||
const propsName = sfcParams.propsName();
|
||||
const contextName = sfcParams.contextName();
|
||||
// props.aProp || context.aProp
|
||||
const isPropUsed = (
|
||||
(propsName && node.object.name === propsName)
|
||||
|| (contextName && node.object.name === contextName)
|
||||
)
|
||||
&& !isAssignmentLHS(node);
|
||||
if (isPropUsed && configuration === 'always' && !node.optional) {
|
||||
report(context, messages.useDestructAssignment, 'useDestructAssignment', {
|
||||
node,
|
||||
data: {
|
||||
type: node.object.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isInClassProperty(node) {
|
||||
let curNode = node.parent;
|
||||
while (curNode) {
|
||||
if (curNode.type === 'ClassProperty' || curNode.type === 'PropertyDefinition') {
|
||||
return true;
|
||||
}
|
||||
curNode = curNode.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleClassUsage(node) {
|
||||
// this.props.Aprop || this.context.aProp || this.state.aState
|
||||
const isPropUsed = (
|
||||
node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression'
|
||||
&& (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state')
|
||||
&& !isAssignmentLHS(node)
|
||||
);
|
||||
|
||||
if (
|
||||
isPropUsed && configuration === 'always'
|
||||
&& !(ignoreClassFields && isInClassProperty(node))
|
||||
) {
|
||||
report(context, messages.useDestructAssignment, 'useDestructAssignment', {
|
||||
node,
|
||||
data: {
|
||||
type: node.object.property.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// valid-jsdoc cannot read function types
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Find a parent that satisfy the given predicate
|
||||
* @param {ASTNode} node
|
||||
* @param {(node: ASTNode) => boolean} predicate
|
||||
* @returns {ASTNode | undefined}
|
||||
*/
|
||||
function findParent(node, predicate) {
|
||||
let n = node;
|
||||
while (n) {
|
||||
if (predicate(n)) {
|
||||
return n;
|
||||
}
|
||||
n = n.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
FunctionDeclaration: handleStatelessComponent,
|
||||
|
||||
ArrowFunctionExpression: handleStatelessComponent,
|
||||
|
||||
FunctionExpression: handleStatelessComponent,
|
||||
|
||||
'FunctionDeclaration:exit': handleStatelessComponentExit,
|
||||
|
||||
'ArrowFunctionExpression:exit': handleStatelessComponentExit,
|
||||
|
||||
'FunctionExpression:exit': handleStatelessComponentExit,
|
||||
|
||||
MemberExpression(node) {
|
||||
const SFCComponent = utils.getParentStatelessComponent(node);
|
||||
if (SFCComponent) {
|
||||
handleSFCUsage(node);
|
||||
}
|
||||
|
||||
const classComponent = utils.getParentComponent(node);
|
||||
if (classComponent) {
|
||||
handleClassUsage(node);
|
||||
}
|
||||
},
|
||||
|
||||
TSQualifiedName(node) {
|
||||
if (configuration !== 'always') {
|
||||
return;
|
||||
}
|
||||
// handle `typeof props.a.b`
|
||||
if (node.left.type === 'Identifier'
|
||||
&& node.left.name === sfcParams.propsName()
|
||||
&& findParent(node, (n) => n.type === 'TSTypeQuery')
|
||||
&& utils.getParentStatelessComponent(node)
|
||||
) {
|
||||
report(context, messages.useDestructAssignment, 'useDestructAssignment', {
|
||||
node,
|
||||
data: {
|
||||
type: 'props',
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
VariableDeclarator(node) {
|
||||
const classComponent = utils.getParentComponent(node);
|
||||
const SFCComponent = components.get(getScope(context, node).block);
|
||||
|
||||
const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
|
||||
// let {foo} = props;
|
||||
const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
|
||||
// let {foo} = this.props;
|
||||
const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
|
||||
node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
|
||||
);
|
||||
|
||||
if (SFCComponent && destructuringSFC && configuration === 'never') {
|
||||
report(context, messages.noDestructAssignment, 'noDestructAssignment', {
|
||||
node,
|
||||
data: {
|
||||
type: node.init.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
classComponent && destructuringClass && configuration === 'never'
|
||||
&& !(ignoreClassFields && (node.parent.type === 'ClassProperty' || node.parent.type === 'PropertyDefinition'))
|
||||
) {
|
||||
report(context, messages.noDestructAssignment, 'noDestructAssignment', {
|
||||
node,
|
||||
data: {
|
||||
type: node.init.property.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
SFCComponent
|
||||
&& destructuringSFC
|
||||
&& configuration === 'always'
|
||||
&& destructureInSignature === 'always'
|
||||
&& node.init.name === 'props'
|
||||
) {
|
||||
const scopeSetProps = getScope(context, node).set.get('props');
|
||||
const propsRefs = scopeSetProps && scopeSetProps.references;
|
||||
if (!propsRefs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if props is used elsewhere
|
||||
if (propsRefs.length > 1) {
|
||||
return;
|
||||
}
|
||||
report(context, messages.destructureInSignature, 'destructureInSignature', {
|
||||
node,
|
||||
fix(fixer) {
|
||||
const param = SFCComponent.node.params[0];
|
||||
if (!param) {
|
||||
return;
|
||||
}
|
||||
const replaceRange = [
|
||||
param.range[0],
|
||||
param.typeAnnotation ? param.typeAnnotation.range[0] : param.range[1],
|
||||
];
|
||||
return [
|
||||
fixer.replaceTextRange(replaceRange, getText(context, node.id)),
|
||||
fixer.remove(node.parent),
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=display-name.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"display-name.d.ts","sourceRoot":"","sources":["display-name.js"],"names":[],"mappings":"wBA6BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* @fileoverview Prevent missing displayName in a React component definition
|
||||
* @author Yannick Croissant
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const values = require('object.values');
|
||||
const filter = require('es-iterator-helpers/Iterator.prototype.filter');
|
||||
const forEach = require('es-iterator-helpers/Iterator.prototype.forEach');
|
||||
|
||||
const Components = require('../util/Components');
|
||||
const isCreateContext = require('../util/isCreateContext');
|
||||
const astUtil = require('../util/ast');
|
||||
const componentUtil = require('../util/componentUtil');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const testReactVersion = require('../util/version').testReactVersion;
|
||||
const propsUtil = require('../util/props');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
noDisplayName: 'Component definition is missing display name',
|
||||
noContextDisplayName: 'Context definition is missing display name',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow missing displayName in a React component definition',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
url: docsUrl('display-name'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
ignoreTranspilerName: {
|
||||
type: 'boolean',
|
||||
},
|
||||
checkContextObjects: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create: Components.detect((context, components, utils) => {
|
||||
const config = context.options[0] || {};
|
||||
const ignoreTranspilerName = config.ignoreTranspilerName || false;
|
||||
const checkContextObjects = (config.checkContextObjects || false) && testReactVersion(context, '>= 16.3.0');
|
||||
|
||||
const contextObjects = new Map();
|
||||
|
||||
/**
|
||||
* Mark a prop type as declared
|
||||
* @param {ASTNode} node The AST node being checked.
|
||||
*/
|
||||
function markDisplayNameAsDeclared(node) {
|
||||
components.set(node, {
|
||||
hasDisplayName: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if React.forwardRef is nested inside React.memo
|
||||
* @param {ASTNode} node The AST node being checked.
|
||||
* @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not.
|
||||
*/
|
||||
function isNestedMemo(node) {
|
||||
return astUtil.isCallExpression(node)
|
||||
&& node.arguments
|
||||
&& astUtil.isCallExpression(node.arguments[0])
|
||||
&& utils.isPragmaComponentWrapper(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports missing display name for a given component
|
||||
* @param {Object} component The component to process
|
||||
*/
|
||||
function reportMissingDisplayName(component) {
|
||||
if (
|
||||
testReactVersion(context, '^0.14.10 || ^15.7.0 || >= 16.12.0')
|
||||
&& isNestedMemo(component.node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
report(context, messages.noDisplayName, 'noDisplayName', {
|
||||
node: component.node,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports missing display name for a given context object
|
||||
* @param {Object} contextObj The context object to process
|
||||
*/
|
||||
function reportMissingContextDisplayName(contextObj) {
|
||||
report(context, messages.noContextDisplayName, 'noContextDisplayName', {
|
||||
node: contextObj.node,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the component have a name set by the transpiler
|
||||
* @param {ASTNode} node The AST node being checked.
|
||||
* @returns {boolean} True if component has a name, false if not.
|
||||
*/
|
||||
function hasTranspilerName(node) {
|
||||
const namedObjectAssignment = (
|
||||
node.type === 'ObjectExpression'
|
||||
&& node.parent
|
||||
&& node.parent.parent
|
||||
&& node.parent.parent.type === 'AssignmentExpression'
|
||||
&& (
|
||||
!node.parent.parent.left.object
|
||||
|| node.parent.parent.left.object.name !== 'module'
|
||||
|| node.parent.parent.left.property.name !== 'exports'
|
||||
)
|
||||
);
|
||||
const namedObjectDeclaration = (
|
||||
node.type === 'ObjectExpression'
|
||||
&& node.parent
|
||||
&& node.parent.parent
|
||||
&& node.parent.parent.type === 'VariableDeclarator'
|
||||
);
|
||||
const namedClass = (
|
||||
(node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
|
||||
&& node.id
|
||||
&& !!node.id.name
|
||||
);
|
||||
|
||||
const namedFunctionDeclaration = (
|
||||
(node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression')
|
||||
&& node.id
|
||||
&& !!node.id.name
|
||||
);
|
||||
|
||||
const namedFunctionExpression = (
|
||||
astUtil.isFunctionLikeExpression(node)
|
||||
&& node.parent
|
||||
&& (node.parent.type === 'VariableDeclarator' || node.parent.type === 'Property' || node.parent.method === true)
|
||||
&& (!node.parent.parent || !componentUtil.isES5Component(node.parent.parent, context))
|
||||
);
|
||||
|
||||
if (
|
||||
namedObjectAssignment || namedObjectDeclaration
|
||||
|| namedClass
|
||||
|| namedFunctionDeclaration || namedFunctionExpression
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
ExpressionStatement(node) {
|
||||
if (checkContextObjects && isCreateContext(node)) {
|
||||
contextObjects.set(node.expression.left.name, { node, hasDisplayName: false });
|
||||
}
|
||||
},
|
||||
VariableDeclarator(node) {
|
||||
if (checkContextObjects && isCreateContext(node)) {
|
||||
contextObjects.set(node.id.name, { node, hasDisplayName: false });
|
||||
}
|
||||
},
|
||||
'ClassProperty, PropertyDefinition'(node) {
|
||||
if (!propsUtil.isDisplayNameDeclaration(node)) {
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(node);
|
||||
},
|
||||
|
||||
MemberExpression(node) {
|
||||
if (!propsUtil.isDisplayNameDeclaration(node.property)) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
checkContextObjects
|
||||
&& node.object
|
||||
&& node.object.name
|
||||
&& contextObjects.has(node.object.name)
|
||||
) {
|
||||
contextObjects.get(node.object.name).hasDisplayName = true;
|
||||
}
|
||||
const component = utils.getRelatedComponent(node);
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(astUtil.unwrapTSAsExpression(component.node));
|
||||
},
|
||||
|
||||
'FunctionExpression, FunctionDeclaration, ArrowFunctionExpression'(node) {
|
||||
if (ignoreTranspilerName || !hasTranspilerName(node)) {
|
||||
return;
|
||||
}
|
||||
if (components.get(node)) {
|
||||
markDisplayNameAsDeclared(node);
|
||||
}
|
||||
},
|
||||
|
||||
MethodDefinition(node) {
|
||||
if (!propsUtil.isDisplayNameDeclaration(node.key)) {
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(node);
|
||||
},
|
||||
|
||||
'ClassExpression, ClassDeclaration'(node) {
|
||||
if (ignoreTranspilerName || !hasTranspilerName(node)) {
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(node);
|
||||
},
|
||||
|
||||
ObjectExpression(node) {
|
||||
if (!componentUtil.isES5Component(node, context)) {
|
||||
return;
|
||||
}
|
||||
if (ignoreTranspilerName || !hasTranspilerName(node)) {
|
||||
// Search for the displayName declaration
|
||||
node.properties.forEach((property) => {
|
||||
if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(node);
|
||||
});
|
||||
return;
|
||||
}
|
||||
markDisplayNameAsDeclared(node);
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (!utils.isPragmaComponentWrapper(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
|
||||
// Skip over React.forwardRef declarations that are embedded within
|
||||
// a React.memo i.e. React.memo(React.forwardRef(/* ... */))
|
||||
// This means that we raise a single error for the call to React.memo
|
||||
// instead of one for React.memo and one for React.forwardRef
|
||||
const isWrappedInAnotherPragma = utils.getPragmaComponentWrapper(node);
|
||||
if (
|
||||
!isWrappedInAnotherPragma
|
||||
&& (ignoreTranspilerName || !hasTranspilerName(node.arguments[0]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (components.get(node)) {
|
||||
markDisplayNameAsDeclared(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'Program:exit'() {
|
||||
const list = components.list();
|
||||
// Report missing display name for all components
|
||||
values(list).filter((component) => !component.hasDisplayName).forEach((component) => {
|
||||
reportMissingDisplayName(component);
|
||||
});
|
||||
if (checkContextObjects) {
|
||||
// Report missing display name for all context objects
|
||||
forEach(
|
||||
filter(contextObjects.values(), (v) => !v.hasDisplayName),
|
||||
(contextObj) => reportMissingContextDisplayName(contextObj)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forbid-component-props.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forbid-component-props.d.ts","sourceRoot":"","sources":["forbid-component-props.js"],"names":[],"mappings":"wBAyBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* @fileoverview Forbid certain props on components
|
||||
* @author Joe Lencioni
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const minimatch = require('minimatch');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULTS = ['className', 'style'];
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
propIsForbidden: 'Prop "{{prop}}" is forbidden on Components',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow certain props on components',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('forbid-component-props'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
forbid: {
|
||||
type: 'array',
|
||||
items: {
|
||||
anyOf: [
|
||||
{ type: 'string' },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
propName: { type: 'string' },
|
||||
allowedFor: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
allowedForPatterns: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
message: { type: 'string' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
propName: { type: 'string' },
|
||||
disallowedFor: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
disallowedForPatterns: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
message: { type: 'string' },
|
||||
},
|
||||
anyOf: [
|
||||
{ required: ['disallowedFor'] },
|
||||
{ required: ['disallowedForPatterns'] },
|
||||
],
|
||||
additionalProperties: false,
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
propNamePattern: { type: 'string' },
|
||||
allowedFor: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
allowedForPatterns: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
message: { type: 'string' },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
propNamePattern: { type: 'string' },
|
||||
disallowedFor: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
disallowedForPatterns: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
message: { type: 'string' },
|
||||
},
|
||||
anyOf: [
|
||||
{ required: ['disallowedFor'] },
|
||||
{ required: ['disallowedForPatterns'] },
|
||||
],
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const configuration = context.options[0] || {};
|
||||
const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
|
||||
const propName = typeof value === 'string' ? value : value.propName;
|
||||
const propPattern = value.propNamePattern;
|
||||
const prop = propName || propPattern;
|
||||
const options = {
|
||||
allowList: [].concat(value.allowedFor || []),
|
||||
allowPatternList: [].concat(value.allowedForPatterns || []),
|
||||
disallowList: [].concat(value.disallowedFor || []),
|
||||
disallowPatternList: [].concat(value.disallowedForPatterns || []),
|
||||
message: typeof value === 'string' ? null : value.message,
|
||||
isPattern: !!value.propNamePattern,
|
||||
};
|
||||
return [prop, options];
|
||||
}));
|
||||
|
||||
function getPropOptions(prop) {
|
||||
// Get config options having pattern
|
||||
const propNamePatternArray = Array.from(forbid.entries()).filter((propEntry) => propEntry[1].isPattern);
|
||||
// Match current prop with pattern options, return if matched
|
||||
const propNamePattern = propNamePatternArray.find((propPatternVal) => minimatch(prop, propPatternVal[0]));
|
||||
// Get options for matched propNamePattern
|
||||
const propNamePatternOptions = propNamePattern && propNamePattern[1];
|
||||
|
||||
const options = forbid.get(prop) || propNamePatternOptions;
|
||||
return options;
|
||||
}
|
||||
|
||||
function isForbidden(prop, tagName) {
|
||||
const options = getPropOptions(prop);
|
||||
if (!options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkIsTagForbiddenByAllowOptions() {
|
||||
if (options.allowList.indexOf(tagName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.allowPatternList.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return options.allowPatternList.every(
|
||||
(pattern) => !minimatch(tagName, pattern)
|
||||
);
|
||||
}
|
||||
|
||||
function checkIsTagForbiddenByDisallowOptions() {
|
||||
if (options.disallowList.indexOf(tagName) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.disallowPatternList.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return options.disallowPatternList.some(
|
||||
(pattern) => minimatch(tagName, pattern)
|
||||
);
|
||||
}
|
||||
|
||||
const hasDisallowOptions = options.disallowList.length > 0 || options.disallowPatternList.length > 0;
|
||||
|
||||
// disallowList should have a least one item (schema configuration)
|
||||
const isTagForbidden = hasDisallowOptions
|
||||
? checkIsTagForbiddenByDisallowOptions()
|
||||
: checkIsTagForbiddenByAllowOptions();
|
||||
|
||||
// if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
|
||||
return typeof tagName === 'undefined' || isTagForbidden;
|
||||
}
|
||||
|
||||
return {
|
||||
JSXAttribute(node) {
|
||||
const parentName = node.parent.name;
|
||||
// Extract a component name when using a "namespace", e.g. `<AntdLayout.Content />`.
|
||||
const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`;
|
||||
const componentName = parentName.name || parentName.property.name;
|
||||
if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) {
|
||||
// This is a DOM node, not a Component, so exit.
|
||||
return;
|
||||
}
|
||||
|
||||
const prop = node.name.name;
|
||||
|
||||
if (!isForbidden(prop, tag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customMessage = getPropOptions(prop).message;
|
||||
|
||||
report(context, customMessage || messages.propIsForbidden, !customMessage && 'propIsForbidden', {
|
||||
node,
|
||||
data: {
|
||||
prop,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forbid-dom-props.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forbid-dom-props.d.ts","sourceRoot":"","sources":["forbid-dom-props.js"],"names":[],"mappings":"wBAuCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @fileoverview Forbid certain props on DOM Nodes
|
||||
* @author David Vázquez
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULTS = [];
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @param {Map<string, object>} forbidMap // { disallowList: null | string[], message: null | string }
|
||||
* @param {string} prop
|
||||
* @param {string} tagName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isForbidden(forbidMap, prop, tagName) {
|
||||
const options = forbidMap.get(prop);
|
||||
return options && (
|
||||
typeof tagName === 'undefined'
|
||||
|| !options.disallowList
|
||||
|| options.disallowList.indexOf(tagName) !== -1
|
||||
);
|
||||
}
|
||||
|
||||
const messages = {
|
||||
propIsForbidden: 'Prop "{{prop}}" is forbidden on DOM Nodes',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow certain props on DOM Nodes',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('forbid-dom-props'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
forbid: {
|
||||
type: 'array',
|
||||
items: {
|
||||
anyOf: [{
|
||||
type: 'string',
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
propName: {
|
||||
type: 'string',
|
||||
},
|
||||
disallowedFor: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
}],
|
||||
minLength: 1,
|
||||
},
|
||||
uniqueItems: true,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const configuration = context.options[0] || {};
|
||||
const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
|
||||
const propName = typeof value === 'string' ? value : value.propName;
|
||||
return [propName, {
|
||||
disallowList: typeof value === 'string' ? null : (value.disallowedFor || null),
|
||||
message: typeof value === 'string' ? null : value.message,
|
||||
}];
|
||||
}));
|
||||
|
||||
return {
|
||||
JSXAttribute(node) {
|
||||
const tag = node.parent.name.name;
|
||||
if (!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())) {
|
||||
// This is a Component, not a DOM node, so exit.
|
||||
return;
|
||||
}
|
||||
|
||||
const prop = node.name.name;
|
||||
|
||||
if (!isForbidden(forbid, prop, tag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customMessage = forbid.get(prop).message;
|
||||
|
||||
report(context, customMessage || messages.propIsForbidden, !customMessage && 'propIsForbidden', {
|
||||
node,
|
||||
data: {
|
||||
prop,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forbid-elements.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forbid-elements.d.ts","sourceRoot":"","sources":["forbid-elements.js"],"names":[],"mappings":"wBAsBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @fileoverview Forbid certain elements
|
||||
* @author Kenneth Chung
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const has = require('hasown');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const getText = require('../util/eslint').getText;
|
||||
const isCreateElement = require('../util/isCreateElement');
|
||||
const report = require('../util/report');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
forbiddenElement: '<{{element}}> is forbidden',
|
||||
forbiddenElement_message: '<{{element}}> is forbidden, {{message}}',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow certain elements',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('forbid-elements'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
forbid: {
|
||||
type: 'array',
|
||||
items: {
|
||||
anyOf: [
|
||||
{ type: 'string' },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
element: { type: 'string' },
|
||||
message: { type: 'string' },
|
||||
},
|
||||
required: ['element'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const configuration = context.options[0] || {};
|
||||
const forbidConfiguration = configuration.forbid || [];
|
||||
|
||||
/** @type {Record<string, { element: string, message?: string }>} */
|
||||
const indexedForbidConfigs = {};
|
||||
|
||||
forbidConfiguration.forEach((item) => {
|
||||
if (typeof item === 'string') {
|
||||
indexedForbidConfigs[item] = { element: item };
|
||||
} else {
|
||||
indexedForbidConfigs[item.element] = item;
|
||||
}
|
||||
});
|
||||
|
||||
function reportIfForbidden(element, node) {
|
||||
if (has(indexedForbidConfigs, element)) {
|
||||
const message = indexedForbidConfigs[element].message;
|
||||
|
||||
report(
|
||||
context,
|
||||
message ? messages.forbiddenElement_message : messages.forbiddenElement,
|
||||
message ? 'forbiddenElement_message' : 'forbiddenElement',
|
||||
{
|
||||
node,
|
||||
data: {
|
||||
element,
|
||||
message,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
JSXOpeningElement(node) {
|
||||
reportIfForbidden(getText(context, node.name), node.name);
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (!isCreateElement(context, node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const argument = node.arguments[0];
|
||||
if (!argument) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (argument.type === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
|
||||
reportIfForbidden(argument.name, argument);
|
||||
} else if (argument.type === 'Literal' && /^[a-z][^.]*$/.test(String(argument.value))) {
|
||||
reportIfForbidden(argument.value, argument);
|
||||
} else if (argument.type === 'MemberExpression') {
|
||||
reportIfForbidden(getText(context, argument), argument);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forbid-foreign-prop-types.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forbid-foreign-prop-types.d.ts","sourceRoot":"","sources":["forbid-foreign-prop-types.js"],"names":[],"mappings":"wBAeW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @fileoverview Forbid using another component's propTypes
|
||||
* @author Ian Christian Myers
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const ast = require('../util/ast');
|
||||
const report = require('../util/report');
|
||||
|
||||
const messages = {
|
||||
forbiddenPropType: 'Using propTypes from another component is not safe because they may be removed in production builds',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow using another component\'s propTypes',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('forbid-foreign-prop-types'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
allowInPropTypes: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const config = context.options[0] || {};
|
||||
const allowInPropTypes = config.allowInPropTypes || false;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function findParentAssignmentExpression(node) {
|
||||
let parent = node.parent;
|
||||
|
||||
while (parent && parent.type !== 'Program') {
|
||||
if (parent.type === 'AssignmentExpression') {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findParentClassProperty(node) {
|
||||
let parent = node.parent;
|
||||
|
||||
while (parent && parent.type !== 'Program') {
|
||||
if (parent.type === 'ClassProperty' || parent.type === 'PropertyDefinition') {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isAllowedAssignment(node) {
|
||||
if (!allowInPropTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const assignmentExpression = findParentAssignmentExpression(node);
|
||||
|
||||
if (
|
||||
assignmentExpression
|
||||
&& assignmentExpression.left
|
||||
&& assignmentExpression.left.property
|
||||
&& assignmentExpression.left.property.name === 'propTypes'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const classProperty = findParentClassProperty(node);
|
||||
|
||||
if (
|
||||
classProperty
|
||||
&& classProperty.key
|
||||
&& classProperty.key.name === 'propTypes'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
(node.property
|
||||
&& (
|
||||
!node.computed
|
||||
&& node.property.type === 'Identifier'
|
||||
&& node.property.name === 'propTypes'
|
||||
&& !ast.isAssignmentLHS(node)
|
||||
&& !isAllowedAssignment(node)
|
||||
)) || (
|
||||
// @ts-expect-error: The JSXText type is not present in the estree type definitions
|
||||
(node.property.type === 'Literal' || node.property.type === 'JSXText')
|
||||
&& 'value' in node.property
|
||||
&& node.property.value === 'propTypes'
|
||||
&& !ast.isAssignmentLHS(node)
|
||||
&& !isAllowedAssignment(node)
|
||||
)
|
||||
) {
|
||||
report(context, messages.forbiddenPropType, 'forbiddenPropType', {
|
||||
node: node.property,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
ObjectPattern(node) {
|
||||
const propTypesNode = node.properties.find((property) => (
|
||||
property.type === 'Property'
|
||||
&& 'name' in property.key
|
||||
&& property.key.name === 'propTypes'
|
||||
));
|
||||
|
||||
if (propTypesNode) {
|
||||
report(context, messages.forbiddenPropType, 'forbiddenPropType', {
|
||||
node: propTypesNode,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forbid-prop-types.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forbid-prop-types.d.ts","sourceRoot":"","sources":["forbid-prop-types.js"],"names":[],"mappings":"wBA4BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* @fileoverview Forbid certain propTypes
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const variableUtil = require('../util/variable');
|
||||
const propsUtil = require('../util/props');
|
||||
const astUtil = require('../util/ast');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const propWrapperUtil = require('../util/propWrapper');
|
||||
const report = require('../util/report');
|
||||
const getText = require('../util/eslint').getText;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULTS = ['any', 'array', 'object'];
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
const messages = {
|
||||
forbiddenPropType: 'Prop type "{{target}}" is forbidden',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow certain propTypes',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('forbid-prop-types'),
|
||||
},
|
||||
|
||||
messages,
|
||||
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
forbid: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
checkContextTypes: {
|
||||
type: 'boolean',
|
||||
},
|
||||
checkChildContextTypes: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: true,
|
||||
}],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const configuration = context.options[0] || {};
|
||||
const checkContextTypes = configuration.checkContextTypes || false;
|
||||
const checkChildContextTypes = configuration.checkChildContextTypes || false;
|
||||
let propTypesPackageName = null;
|
||||
let reactPackageName = null;
|
||||
let isForeignPropTypesPackage = false;
|
||||
|
||||
function isPropTypesPackage(node) {
|
||||
return (
|
||||
node.type === 'Identifier'
|
||||
&& (
|
||||
node.name === null
|
||||
|| node.name === propTypesPackageName
|
||||
|| !isForeignPropTypesPackage
|
||||
)
|
||||
) || (
|
||||
node.type === 'MemberExpression'
|
||||
&& (
|
||||
node.object.name === null
|
||||
|| node.object.name === reactPackageName
|
||||
|| !isForeignPropTypesPackage
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isForbidden(type) {
|
||||
const forbid = configuration.forbid || DEFAULTS;
|
||||
return forbid.indexOf(type) >= 0;
|
||||
}
|
||||
|
||||
function reportIfForbidden(type, declaration, target) {
|
||||
if (isForbidden(type)) {
|
||||
report(context, messages.forbiddenPropType, 'forbiddenPropType', {
|
||||
node: declaration,
|
||||
data: {
|
||||
target,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function shouldCheckContextTypes(node) {
|
||||
if (checkContextTypes && propsUtil.isContextTypesDeclaration(node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function shouldCheckChildContextTypes(node) {
|
||||
if (checkChildContextTypes && propsUtil.isChildContextTypesDeclaration(node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if propTypes declarations are forbidden
|
||||
* @param {Array} declarations The array of AST nodes being checked.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkProperties(declarations) {
|
||||
if (declarations) {
|
||||
declarations.forEach((declaration) => {
|
||||
if (declaration.type !== 'Property') {
|
||||
return;
|
||||
}
|
||||
let target;
|
||||
let value = declaration.value;
|
||||
if (
|
||||
value.type === 'MemberExpression'
|
||||
&& value.property
|
||||
&& value.property.name
|
||||
&& value.property.name === 'isRequired'
|
||||
) {
|
||||
value = value.object;
|
||||
}
|
||||
if (astUtil.isCallExpression(value)) {
|
||||
if (!isPropTypesPackage(value.callee)) {
|
||||
return;
|
||||
}
|
||||
value.arguments.forEach((arg) => {
|
||||
const name = arg.type === 'MemberExpression' ? arg.property.name : arg.name;
|
||||
reportIfForbidden(name, declaration, name);
|
||||
});
|
||||
value = value.callee;
|
||||
}
|
||||
if (!isPropTypesPackage(value)) {
|
||||
return;
|
||||
}
|
||||
if (value.property) {
|
||||
target = value.property.name;
|
||||
} else if (value.type === 'Identifier') {
|
||||
target = value.name;
|
||||
}
|
||||
reportIfForbidden(target, declaration, target);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkNode(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type === 'ObjectExpression') {
|
||||
checkProperties(node.properties);
|
||||
} else if (node.type === 'Identifier') {
|
||||
const propTypesObject = variableUtil.findVariableByName(context, node, node.name);
|
||||
if (propTypesObject && propTypesObject.properties) {
|
||||
checkProperties(propTypesObject.properties);
|
||||
}
|
||||
} else if (astUtil.isCallExpression(node)) {
|
||||
const innerNode = node.arguments && node.arguments[0];
|
||||
if (
|
||||
propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee))
|
||||
&& innerNode
|
||||
) {
|
||||
checkNode(innerNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
|
||||
if (node.specifiers.length > 0) {
|
||||
propTypesPackageName = node.specifiers[0].local.name;
|
||||
}
|
||||
} else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
|
||||
if (node.specifiers.length > 0) {
|
||||
reactPackageName = node.specifiers[0].local.name; // guard against accidental anonymous `import "react"`
|
||||
}
|
||||
if (node.specifiers.length >= 1) {
|
||||
const propTypesSpecifier = node.specifiers.find((specifier) => (
|
||||
'imported' in specifier
|
||||
&& specifier.imported
|
||||
&& 'name' in specifier.imported
|
||||
&& specifier.imported.name === 'PropTypes'
|
||||
));
|
||||
if (propTypesSpecifier) {
|
||||
propTypesPackageName = propTypesSpecifier.local.name;
|
||||
}
|
||||
}
|
||||
} else { // package is not imported from "react" or "prop-types"
|
||||
// eslint-disable-next-line no-lonely-if
|
||||
if (node.specifiers.some((x) => x.local.name === 'PropTypes')) { // assert: node.specifiers.length > 1
|
||||
isForeignPropTypesPackage = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'ClassProperty, PropertyDefinition'(node) {
|
||||
if (
|
||||
!propsUtil.isPropTypesDeclaration(node)
|
||||
&& !isPropTypesPackage(node)
|
||||
&& !shouldCheckContextTypes(node)
|
||||
&& !shouldCheckChildContextTypes(node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
checkNode(node.value);
|
||||
},
|
||||
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
!propsUtil.isPropTypesDeclaration(node)
|
||||
&& !isPropTypesPackage(node)
|
||||
&& !shouldCheckContextTypes(node)
|
||||
&& !shouldCheckChildContextTypes(node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNode('right' in node.parent && node.parent.right);
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type === 'MemberExpression'
|
||||
&& node.callee.object
|
||||
&& !isPropTypesPackage(node.callee.object)
|
||||
&& !propsUtil.isPropTypesDeclaration(node.callee)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
node.arguments.length > 0
|
||||
&& (
|
||||
('name' in node.callee && node.callee.name === 'shape')
|
||||
|| astUtil.getPropertyName(node.callee) === 'shape'
|
||||
)
|
||||
) {
|
||||
checkProperties('properties' in node.arguments[0] && node.arguments[0].properties);
|
||||
}
|
||||
},
|
||||
|
||||
MethodDefinition(node) {
|
||||
if (
|
||||
!propsUtil.isPropTypesDeclaration(node)
|
||||
&& !isPropTypesPackage(node)
|
||||
&& !shouldCheckContextTypes(node)
|
||||
&& !shouldCheckChildContextTypes(node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const returnStatement = astUtil.findReturnStatement(node);
|
||||
|
||||
if (returnStatement && returnStatement.argument) {
|
||||
checkNode(returnStatement.argument);
|
||||
}
|
||||
},
|
||||
|
||||
ObjectExpression(node) {
|
||||
node.properties.forEach((property) => {
|
||||
if (!('key' in property) || !property.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!propsUtil.isPropTypesDeclaration(property)
|
||||
&& !isPropTypesPackage(property)
|
||||
&& !shouldCheckContextTypes(property)
|
||||
&& !shouldCheckChildContextTypes(property)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (property.value.type === 'ObjectExpression') {
|
||||
checkProperties(property.value.properties);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=forward-ref-uses-ref.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"forward-ref-uses-ref.d.ts","sourceRoot":"","sources":["forward-ref-uses-ref.js"],"names":[],"mappings":"wBA2CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @fileoverview Require all forwardRef components include a ref parameter
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const isParenthesized = require('../util/ast').isParenthesized;
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const report = require('../util/report');
|
||||
const getMessageData = require('../util/message');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @param {ASTNode} node
|
||||
* @returns {boolean} If the node represents the identifier `forwardRef`.
|
||||
*/
|
||||
function isForwardRefIdentifier(node) {
|
||||
return node.type === 'Identifier' && node.name === 'forwardRef';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ASTNode} node
|
||||
* @returns {boolean} If the node represents a function call `forwardRef()` or `React.forwardRef()`.
|
||||
*/
|
||||
function isForwardRefCall(node) {
|
||||
return (
|
||||
node.type === 'CallExpression'
|
||||
&& (
|
||||
isForwardRefIdentifier(node.callee)
|
||||
|| (node.callee.type === 'MemberExpression' && isForwardRefIdentifier(node.callee.property))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const messages = {
|
||||
missingRefParameter: 'forwardRef is used with this component but no ref parameter is set',
|
||||
addRefParameter: 'Add a ref parameter',
|
||||
removeForwardRef: 'Remove forwardRef wrapper',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Require all forwardRef components include a ref parameter',
|
||||
category: 'Possible Errors',
|
||||
recommended: false,
|
||||
url: docsUrl('forward-ref-uses-ref'),
|
||||
},
|
||||
messages,
|
||||
schema: [],
|
||||
type: 'suggestion',
|
||||
hasSuggestions: true,
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
return {
|
||||
'FunctionExpression, ArrowFunctionExpression'(node) {
|
||||
if (!isForwardRefCall(node.parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.params.length === 1) {
|
||||
report(context, messages.missingRefParameter, 'missingRefParameter', {
|
||||
node,
|
||||
suggest: [
|
||||
Object.assign(
|
||||
getMessageData('addRefParameter', messages.addRefParameter),
|
||||
{
|
||||
fix(fixer) {
|
||||
const param = node.params[0];
|
||||
// If using shorthand arrow function syntax, add parentheses around the new parameter pair
|
||||
const shouldAddParentheses = node.type === 'ArrowFunctionExpression' && !isParenthesized(context, param);
|
||||
return [].concat(
|
||||
shouldAddParentheses ? fixer.insertTextBefore(param, '(') : [],
|
||||
fixer.insertTextAfter(param, `, ref${shouldAddParentheses ? ')' : ''}`)
|
||||
);
|
||||
},
|
||||
}
|
||||
),
|
||||
Object.assign(
|
||||
getMessageData('removeForwardRef', messages.removeForwardRef),
|
||||
{
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(node.parent, sourceCode.getText(node));
|
||||
},
|
||||
}
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=function-component-definition.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"function-component-definition.d.ts","sourceRoot":"","sources":["function-component-definition.js"],"names":[],"mappings":"wBAqHW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* @fileoverview Standardize the way function component get defined
|
||||
* @author Stefan Wullems
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const arrayIncludes = require('array-includes');
|
||||
const Components = require('../util/Components');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const reportC = require('../util/report');
|
||||
const getText = require('../util/eslint').getText;
|
||||
const propsUtil = require('../util/props');
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
function buildFunction(template, parts) {
|
||||
return Object.keys(parts).reduce(
|
||||
(acc, key) => acc.replace(`{${key}}`, () => parts[key] || ''),
|
||||
template
|
||||
);
|
||||
}
|
||||
|
||||
const NAMED_FUNCTION_TEMPLATES = {
|
||||
'function-declaration': 'function {name}{typeParams}({params}){returnType} {body}',
|
||||
'arrow-function': '{varType} {name}{typeAnnotation} = {typeParams}({params}){returnType} => {body}',
|
||||
'function-expression': '{varType} {name}{typeAnnotation} = function{typeParams}({params}){returnType} {body}',
|
||||
};
|
||||
|
||||
const UNNAMED_FUNCTION_TEMPLATES = {
|
||||
'function-expression': 'function{typeParams}({params}){returnType} {body}',
|
||||
'arrow-function': '{typeParams}({params}){returnType} => {body}',
|
||||
};
|
||||
|
||||
function hasOneUnconstrainedTypeParam(node) {
|
||||
const nodeTypeArguments = propsUtil.getTypeArguments(node);
|
||||
|
||||
return nodeTypeArguments
|
||||
&& nodeTypeArguments.params
|
||||
&& nodeTypeArguments.params.length === 1
|
||||
&& !nodeTypeArguments.params[0].constraint;
|
||||
}
|
||||
|
||||
function hasName(node) {
|
||||
return (
|
||||
node.type === 'FunctionDeclaration'
|
||||
|| node.parent.type === 'VariableDeclarator'
|
||||
);
|
||||
}
|
||||
|
||||
function getNodeText(prop, source) {
|
||||
if (!prop) return null;
|
||||
return source.slice(prop.range[0], prop.range[1]);
|
||||
}
|
||||
|
||||
function getName(node) {
|
||||
if (node.type === 'FunctionDeclaration') {
|
||||
return node.id.name;
|
||||
}
|
||||
|
||||
if (
|
||||
node.type === 'ArrowFunctionExpression'
|
||||
|| node.type === 'FunctionExpression'
|
||||
) {
|
||||
return hasName(node) && node.parent.id.name;
|
||||
}
|
||||
}
|
||||
|
||||
function getParams(node, source) {
|
||||
if (node.params.length === 0) return null;
|
||||
return source.slice(
|
||||
node.params[0].range[0],
|
||||
node.params[node.params.length - 1].range[1]
|
||||
);
|
||||
}
|
||||
|
||||
function getBody(node, source) {
|
||||
const range = node.body.range;
|
||||
|
||||
if (node.body.type !== 'BlockStatement') {
|
||||
return ['{', ` return ${source.slice(range[0], range[1])}`, '}'].join('\n');
|
||||
}
|
||||
|
||||
return source.slice(range[0], range[1]);
|
||||
}
|
||||
|
||||
function getTypeAnnotation(node, source) {
|
||||
if (!hasName(node) || node.type === 'FunctionDeclaration') return;
|
||||
|
||||
if (
|
||||
node.type === 'ArrowFunctionExpression'
|
||||
|| node.type === 'FunctionExpression'
|
||||
) {
|
||||
return getNodeText(node.parent.id.typeAnnotation, source);
|
||||
}
|
||||
}
|
||||
|
||||
function isUnfixableBecauseOfExport(node) {
|
||||
return (
|
||||
node.type === 'FunctionDeclaration'
|
||||
&& node.parent
|
||||
&& node.parent.type === 'ExportDefaultDeclaration'
|
||||
);
|
||||
}
|
||||
|
||||
function isFunctionExpressionWithName(node) {
|
||||
return node.type === 'FunctionExpression' && node.id && node.id.name;
|
||||
}
|
||||
|
||||
const messages = {
|
||||
'function-declaration': 'Function component is not a function declaration',
|
||||
'function-expression': 'Function component is not a function expression',
|
||||
'arrow-function': 'Function component is not an arrow function',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce a specific function type for function components',
|
||||
category: 'Stylistic Issues',
|
||||
recommended: false,
|
||||
url: docsUrl('function-component-definition'),
|
||||
},
|
||||
fixable: 'code',
|
||||
|
||||
messages,
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
namedComponents: {
|
||||
anyOf: [
|
||||
{
|
||||
enum: [
|
||||
'function-declaration',
|
||||
'arrow-function',
|
||||
'function-expression',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'function-declaration',
|
||||
'arrow-function',
|
||||
'function-expression',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
unnamedComponents: {
|
||||
anyOf: [
|
||||
{ enum: ['arrow-function', 'function-expression'] },
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: ['arrow-function', 'function-expression'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
create: Components.detect((context, components) => {
|
||||
const configuration = context.options[0] || {};
|
||||
let fileVarType = 'var';
|
||||
|
||||
const namedConfig = [].concat(
|
||||
configuration.namedComponents || 'function-declaration'
|
||||
);
|
||||
const unnamedConfig = [].concat(
|
||||
configuration.unnamedComponents || 'function-expression'
|
||||
);
|
||||
|
||||
function getFixer(node, options) {
|
||||
const source = getText(context);
|
||||
|
||||
const typeAnnotation = getTypeAnnotation(node, source);
|
||||
|
||||
if (options.type === 'function-declaration' && typeAnnotation) {
|
||||
return;
|
||||
}
|
||||
if (options.type === 'arrow-function' && hasOneUnconstrainedTypeParam(node)) {
|
||||
return;
|
||||
}
|
||||
if (isUnfixableBecauseOfExport(node)) return;
|
||||
if (isFunctionExpressionWithName(node)) return;
|
||||
let varType = fileVarType;
|
||||
if (
|
||||
(node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression')
|
||||
&& node.parent.type === 'VariableDeclarator'
|
||||
) {
|
||||
varType = node.parent.parent.kind;
|
||||
}
|
||||
|
||||
const nodeTypeArguments = propsUtil.getTypeArguments(node);
|
||||
return (fixer) => fixer.replaceTextRange(
|
||||
options.range,
|
||||
buildFunction(options.template, {
|
||||
typeAnnotation,
|
||||
typeParams: getNodeText(nodeTypeArguments, source),
|
||||
params: getParams(node, source),
|
||||
returnType: getNodeText(node.returnType, source),
|
||||
body: getBody(node, source),
|
||||
name: getName(node),
|
||||
varType,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function report(node, options) {
|
||||
reportC(context, messages[options.messageId], options.messageId, {
|
||||
node,
|
||||
fix: getFixer(node, options.fixerOptions),
|
||||
});
|
||||
}
|
||||
|
||||
function validate(node, functionType) {
|
||||
if (!components.get(node)) return;
|
||||
|
||||
if (node.parent && node.parent.type === 'Property') return;
|
||||
|
||||
if (hasName(node) && !arrayIncludes(namedConfig, functionType)) {
|
||||
report(node, {
|
||||
messageId: namedConfig[0],
|
||||
fixerOptions: {
|
||||
type: namedConfig[0],
|
||||
template: NAMED_FUNCTION_TEMPLATES[namedConfig[0]],
|
||||
range:
|
||||
node.type === 'FunctionDeclaration'
|
||||
? node.range
|
||||
: node.parent.parent.range,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!hasName(node) && !arrayIncludes(unnamedConfig, functionType)) {
|
||||
report(node, {
|
||||
messageId: unnamedConfig[0],
|
||||
fixerOptions: {
|
||||
type: unnamedConfig[0],
|
||||
template: UNNAMED_FUNCTION_TEMPLATES[unnamedConfig[0]],
|
||||
range: node.range,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public
|
||||
// --------------------------------------------------------------------------
|
||||
const validatePairs = [];
|
||||
let hasES6OrJsx = false;
|
||||
return {
|
||||
FunctionDeclaration(node) {
|
||||
validatePairs.push([node, 'function-declaration']);
|
||||
},
|
||||
ArrowFunctionExpression(node) {
|
||||
validatePairs.push([node, 'arrow-function']);
|
||||
},
|
||||
FunctionExpression(node) {
|
||||
validatePairs.push([node, 'function-expression']);
|
||||
},
|
||||
VariableDeclaration(node) {
|
||||
hasES6OrJsx = hasES6OrJsx || node.kind === 'const' || node.kind === 'let';
|
||||
},
|
||||
'Program:exit'() {
|
||||
if (hasES6OrJsx) fileVarType = 'const';
|
||||
validatePairs.forEach((pair) => validate(pair[0], pair[1]));
|
||||
},
|
||||
'ImportDeclaration, ExportNamedDeclaration, ExportDefaultDeclaration, ExportAllDeclaration, ExportSpecifier, ExportDefaultSpecifier, JSXElement, TSExportAssignment, TSImportEqualsDeclaration'() {
|
||||
hasES6OrJsx = true;
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=hook-use-state.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"hook-use-state.d.ts","sourceRoot":"","sources":["hook-use-state.js"],"names":[],"mappings":"wBA4BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* @fileoverview Ensure symmetric naming of useState hook value and setter variables
|
||||
* @author Duncan Beevers
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Components = require('../util/Components');
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const report = require('../util/report');
|
||||
const getMessageData = require('../util/message');
|
||||
const getText = require('../util/eslint').getText;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
function isNodeDestructuring(node) {
|
||||
return node && (node.type === 'ArrayPattern' || node.type === 'ObjectPattern');
|
||||
}
|
||||
|
||||
const messages = {
|
||||
useStateErrorMessage: 'useState call is not destructured into value + setter pair',
|
||||
useStateErrorMessageOrAddOption: 'useState call is not destructured into value + setter pair (you can allow destructuring by enabling "allowDestructuredState" option)',
|
||||
suggestPair: 'Destructure useState call into value + setter pair',
|
||||
suggestMemo: 'Replace useState call with useMemo',
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Ensure destructuring and symmetric naming of useState hook value and setter variables',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('hook-use-state'),
|
||||
},
|
||||
messages,
|
||||
schema: [{
|
||||
type: 'object',
|
||||
properties: {
|
||||
allowDestructuredState: {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
}],
|
||||
type: 'suggestion',
|
||||
hasSuggestions: true,
|
||||
},
|
||||
|
||||
create: Components.detect((context, components, util) => {
|
||||
const configuration = context.options[0] || {};
|
||||
const allowDestructuredState = configuration.allowDestructuredState || false;
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const isImmediateReturn = node.parent
|
||||
&& node.parent.type === 'ReturnStatement';
|
||||
|
||||
if (isImmediateReturn || !util.isReactHookCall(node, ['useState'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDestructuringDeclarator = node.parent
|
||||
&& node.parent.type === 'VariableDeclarator'
|
||||
&& node.parent.id.type === 'ArrayPattern';
|
||||
|
||||
if (!isDestructuringDeclarator) {
|
||||
report(
|
||||
context,
|
||||
messages.useStateErrorMessage,
|
||||
'useStateErrorMessage',
|
||||
{
|
||||
node,
|
||||
suggest: false,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const variableNodes = node.parent.id.elements;
|
||||
const valueVariable = variableNodes[0];
|
||||
const setterVariable = variableNodes[1];
|
||||
const isOnlyValueDestructuring = isNodeDestructuring(valueVariable) && !isNodeDestructuring(setterVariable);
|
||||
|
||||
if (allowDestructuredState && isOnlyValueDestructuring) {
|
||||
return;
|
||||
}
|
||||
|
||||
const valueVariableName = valueVariable
|
||||
? valueVariable.name
|
||||
: undefined;
|
||||
|
||||
const setterVariableName = setterVariable
|
||||
? setterVariable.name
|
||||
: undefined;
|
||||
|
||||
const caseCandidateMatch = valueVariableName ? valueVariableName.match(/(^[a-z]+)(.*)/) : undefined;
|
||||
const upperCaseCandidatePrefix = caseCandidateMatch ? caseCandidateMatch[1] : undefined;
|
||||
const caseCandidateSuffix = caseCandidateMatch ? caseCandidateMatch[2] : undefined;
|
||||
const expectedSetterVariableNames = upperCaseCandidatePrefix ? [
|
||||
`set${upperCaseCandidatePrefix.charAt(0).toUpperCase()}${upperCaseCandidatePrefix.slice(1)}${caseCandidateSuffix}`,
|
||||
`set${upperCaseCandidatePrefix.toUpperCase()}${caseCandidateSuffix}`,
|
||||
] : [];
|
||||
|
||||
const isSymmetricGetterSetterPair = valueVariable
|
||||
&& setterVariable
|
||||
&& expectedSetterVariableNames.indexOf(setterVariableName) !== -1
|
||||
&& variableNodes.length === 2;
|
||||
|
||||
if (!isSymmetricGetterSetterPair) {
|
||||
const suggestions = [
|
||||
Object.assign(
|
||||
getMessageData('suggestPair', messages.suggestPair),
|
||||
{
|
||||
fix(fixer) {
|
||||
if (expectedSetterVariableNames.length > 0) {
|
||||
return fixer.replaceTextRange(
|
||||
node.parent.id.range,
|
||||
`[${valueVariableName}, ${expectedSetterVariableNames[0]}]`
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
];
|
||||
|
||||
const defaultReactImports = components.getDefaultReactImports();
|
||||
const defaultReactImportSpecifier = defaultReactImports
|
||||
? defaultReactImports[0]
|
||||
: undefined;
|
||||
|
||||
const defaultReactImportName = defaultReactImportSpecifier
|
||||
? defaultReactImportSpecifier.local.name
|
||||
: undefined;
|
||||
|
||||
const namedReactImports = components.getNamedReactImports();
|
||||
const useStateReactImportSpecifier = namedReactImports
|
||||
? namedReactImports.find((specifier) => specifier.imported.name === 'useState')
|
||||
: undefined;
|
||||
|
||||
const isSingleGetter = valueVariable && variableNodes.length === 1;
|
||||
const isUseStateCalledWithSingleArgument = node.arguments.length === 1;
|
||||
if (isSingleGetter && isUseStateCalledWithSingleArgument) {
|
||||
const useMemoReactImportSpecifier = namedReactImports
|
||||
&& namedReactImports.find((specifier) => specifier.imported.name === 'useMemo');
|
||||
|
||||
let useMemoCode;
|
||||
if (useMemoReactImportSpecifier) {
|
||||
useMemoCode = useMemoReactImportSpecifier.local.name;
|
||||
} else if (defaultReactImportName) {
|
||||
useMemoCode = `${defaultReactImportName}.useMemo`;
|
||||
} else {
|
||||
useMemoCode = 'useMemo';
|
||||
}
|
||||
|
||||
suggestions.unshift(Object.assign(
|
||||
getMessageData('suggestMemo', messages.suggestMemo),
|
||||
{
|
||||
fix: (fixer) => [
|
||||
// Add useMemo import, if necessary
|
||||
useStateReactImportSpecifier
|
||||
&& (!useMemoReactImportSpecifier || defaultReactImportName)
|
||||
&& fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
|
||||
// Convert single-value destructure to simple assignment
|
||||
fixer.replaceTextRange(node.parent.id.range, valueVariableName),
|
||||
// Convert useState call to useMemo + arrow function + dependency array
|
||||
fixer.replaceTextRange(
|
||||
node.range,
|
||||
`${useMemoCode}(() => ${getText(context, node.arguments[0])}, [])`
|
||||
),
|
||||
].filter(Boolean),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
if (isOnlyValueDestructuring) {
|
||||
report(
|
||||
context,
|
||||
messages.useStateErrorMessageOrAddOption,
|
||||
'useStateErrorMessageOrAddOption',
|
||||
{
|
||||
node: node.parent.id,
|
||||
suggest: false,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
report(
|
||||
context,
|
||||
messages.useStateErrorMessage,
|
||||
'useStateErrorMessage',
|
||||
{
|
||||
node: node.parent.id,
|
||||
suggest: suggestions,
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=iframe-missing-sandbox.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"iframe-missing-sandbox.d.ts","sourceRoot":"","sources":["iframe-missing-sandbox.js"],"names":[],"mappings":"wBA+GW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @fileoverview TBD
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const docsUrl = require('../util/docsUrl');
|
||||
const isCreateElement = require('../util/isCreateElement');
|
||||
const report = require('../util/report');
|
||||
|
||||
const messages = {
|
||||
attributeMissing: 'An iframe element is missing a sandbox attribute',
|
||||
invalidValue: 'An iframe element defines a sandbox attribute with invalid value "{{ value }}"',
|
||||
invalidCombination: 'An iframe element defines a sandbox attribute with both allow-scripts and allow-same-origin which is invalid',
|
||||
};
|
||||
|
||||
const ALLOWED_VALUES = [
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox
|
||||
'',
|
||||
'allow-downloads-without-user-activation',
|
||||
'allow-downloads',
|
||||
'allow-forms',
|
||||
'allow-modals',
|
||||
'allow-orientation-lock',
|
||||
'allow-pointer-lock',
|
||||
'allow-popups',
|
||||
'allow-popups-to-escape-sandbox',
|
||||
'allow-presentation',
|
||||
'allow-same-origin',
|
||||
'allow-scripts',
|
||||
'allow-storage-access-by-user-activation',
|
||||
'allow-top-navigation',
|
||||
'allow-top-navigation-by-user-activation',
|
||||
];
|
||||
|
||||
function validateSandboxAttribute(context, node, attribute) {
|
||||
if (typeof attribute !== 'string') {
|
||||
// Only string literals are supported for now
|
||||
return;
|
||||
}
|
||||
const values = attribute.split(' ');
|
||||
let allowScripts = false;
|
||||
let allowSameOrigin = false;
|
||||
values.forEach((attributeValue) => {
|
||||
const trimmedAttributeValue = attributeValue.trim();
|
||||
if (ALLOWED_VALUES.indexOf(trimmedAttributeValue) === -1) {
|
||||
report(context, messages.invalidValue, 'invalidValue', {
|
||||
node,
|
||||
data: {
|
||||
value: trimmedAttributeValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (trimmedAttributeValue === 'allow-scripts') {
|
||||
allowScripts = true;
|
||||
}
|
||||
if (trimmedAttributeValue === 'allow-same-origin') {
|
||||
allowSameOrigin = true;
|
||||
}
|
||||
});
|
||||
if (allowScripts && allowSameOrigin) {
|
||||
report(context, messages.invalidCombination, 'invalidCombination', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkAttributes(context, node) {
|
||||
let sandboxAttributeFound = false;
|
||||
node.attributes.forEach((attribute) => {
|
||||
if (attribute.type === 'JSXAttribute'
|
||||
&& attribute.name
|
||||
&& attribute.name.type === 'JSXIdentifier'
|
||||
&& attribute.name.name === 'sandbox'
|
||||
) {
|
||||
sandboxAttributeFound = true;
|
||||
if (
|
||||
attribute.value
|
||||
&& attribute.value.type === 'Literal'
|
||||
&& attribute.value.value
|
||||
) {
|
||||
validateSandboxAttribute(context, node, attribute.value.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!sandboxAttributeFound) {
|
||||
report(context, messages.attributeMissing, 'attributeMissing', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkProps(context, node) {
|
||||
let sandboxAttributeFound = false;
|
||||
if (node.arguments.length > 1) {
|
||||
const props = node.arguments[1];
|
||||
const sandboxProp = props.properties && props.properties.find((x) => x.type === 'Property' && x.key.name === 'sandbox');
|
||||
if (sandboxProp) {
|
||||
sandboxAttributeFound = true;
|
||||
if (sandboxProp.value && sandboxProp.value.type === 'Literal' && sandboxProp.value.value) {
|
||||
validateSandboxAttribute(context, node, sandboxProp.value.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sandboxAttributeFound) {
|
||||
report(context, messages.attributeMissing, 'attributeMissing', {
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce sandbox attribute on iframe elements',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: docsUrl('iframe-missing-sandbox'),
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages,
|
||||
},
|
||||
|
||||
create(context) {
|
||||
return {
|
||||
'JSXOpeningElement[name.name="iframe"]'(node) {
|
||||
checkAttributes(context, node);
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (isCreateElement(context, node) && node.arguments && node.arguments.length > 0) {
|
||||
const tag = node.arguments[0];
|
||||
if (tag.type === 'Literal' && tag.value === 'iframe') {
|
||||
checkProps(context, node);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
export = rules;
|
||||
/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
|
||||
declare const rules: {
|
||||
'boolean-prop-naming': import("eslint").Rule.RuleModule;
|
||||
'button-has-type': import("eslint").Rule.RuleModule;
|
||||
'checked-requires-onchange-or-readonly': import("eslint").Rule.RuleModule;
|
||||
'default-props-match-prop-types': import("eslint").Rule.RuleModule;
|
||||
'destructuring-assignment': import("eslint").Rule.RuleModule;
|
||||
'display-name': import("eslint").Rule.RuleModule;
|
||||
'forbid-component-props': import("eslint").Rule.RuleModule;
|
||||
'forbid-dom-props': import("eslint").Rule.RuleModule;
|
||||
'forbid-elements': import("eslint").Rule.RuleModule;
|
||||
'forbid-foreign-prop-types': import("eslint").Rule.RuleModule;
|
||||
'forbid-prop-types': import("eslint").Rule.RuleModule;
|
||||
'forward-ref-uses-ref': import("eslint").Rule.RuleModule;
|
||||
'function-component-definition': import("eslint").Rule.RuleModule;
|
||||
'hook-use-state': import("eslint").Rule.RuleModule;
|
||||
'iframe-missing-sandbox': import("eslint").Rule.RuleModule;
|
||||
'jsx-boolean-value': import("eslint").Rule.RuleModule;
|
||||
'jsx-child-element-spacing': import("eslint").Rule.RuleModule;
|
||||
'jsx-closing-bracket-location': import("eslint").Rule.RuleModule;
|
||||
'jsx-closing-tag-location': import("eslint").Rule.RuleModule;
|
||||
'jsx-curly-spacing': import("eslint").Rule.RuleModule;
|
||||
'jsx-curly-newline': import("eslint").Rule.RuleModule;
|
||||
'jsx-equals-spacing': import("eslint").Rule.RuleModule;
|
||||
'jsx-filename-extension': import("eslint").Rule.RuleModule;
|
||||
'jsx-first-prop-new-line': import("eslint").Rule.RuleModule;
|
||||
'jsx-handler-names': import("eslint").Rule.RuleModule;
|
||||
'jsx-indent': import("eslint").Rule.RuleModule;
|
||||
'jsx-indent-props': import("eslint").Rule.RuleModule;
|
||||
'jsx-key': import("eslint").Rule.RuleModule;
|
||||
'jsx-max-depth': import("eslint").Rule.RuleModule;
|
||||
'jsx-max-props-per-line': import("eslint").Rule.RuleModule;
|
||||
'jsx-newline': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-bind': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-comment-textnodes': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-constructed-context-values': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-duplicate-props': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-leaked-render': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-literals': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-script-url': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-target-blank': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-useless-fragment': import("eslint").Rule.RuleModule;
|
||||
'jsx-one-expression-per-line': import("eslint").Rule.RuleModule;
|
||||
'jsx-no-undef': import("eslint").Rule.RuleModule;
|
||||
'jsx-curly-brace-presence': import("eslint").Rule.RuleModule;
|
||||
'jsx-pascal-case': import("eslint").Rule.RuleModule;
|
||||
'jsx-fragments': import("eslint").Rule.RuleModule;
|
||||
'jsx-props-no-multi-spaces': import("eslint").Rule.RuleModule;
|
||||
'jsx-props-no-spreading': import("eslint").Rule.RuleModule;
|
||||
'jsx-props-no-spread-multi': import("eslint").Rule.RuleModule;
|
||||
'jsx-sort-default-props': import("eslint").Rule.RuleModule;
|
||||
'jsx-sort-props': import("eslint").Rule.RuleModule;
|
||||
'jsx-space-before-closing': import("eslint").Rule.RuleModule;
|
||||
'jsx-tag-spacing': import("eslint").Rule.RuleModule;
|
||||
'jsx-uses-react': import("eslint").Rule.RuleModule;
|
||||
'jsx-uses-vars': import("eslint").Rule.RuleModule;
|
||||
'jsx-wrap-multilines': import("eslint").Rule.RuleModule;
|
||||
'no-invalid-html-attribute': import("eslint").Rule.RuleModule;
|
||||
'no-access-state-in-setstate': import("eslint").Rule.RuleModule;
|
||||
'no-adjacent-inline-elements': import("eslint").Rule.RuleModule;
|
||||
'no-array-index-key': import("eslint").Rule.RuleModule;
|
||||
'no-arrow-function-lifecycle': import("eslint").Rule.RuleModule;
|
||||
'no-children-prop': import("eslint").Rule.RuleModule;
|
||||
'no-danger': import("eslint").Rule.RuleModule;
|
||||
'no-danger-with-children': import("eslint").Rule.RuleModule;
|
||||
'no-deprecated': import("eslint").Rule.RuleModule;
|
||||
'no-did-mount-set-state': import("eslint").Rule.RuleModule;
|
||||
'no-did-update-set-state': import("eslint").Rule.RuleModule;
|
||||
'no-direct-mutation-state': import("eslint").Rule.RuleModule;
|
||||
'no-find-dom-node': import("eslint").Rule.RuleModule;
|
||||
'no-is-mounted': import("eslint").Rule.RuleModule;
|
||||
'no-multi-comp': import("eslint").Rule.RuleModule;
|
||||
'no-namespace': import("eslint").Rule.RuleModule;
|
||||
'no-set-state': import("eslint").Rule.RuleModule;
|
||||
'no-string-refs': import("eslint").Rule.RuleModule;
|
||||
'no-redundant-should-component-update': import("eslint").Rule.RuleModule;
|
||||
'no-render-return-value': import("eslint").Rule.RuleModule;
|
||||
'no-this-in-sfc': import("eslint").Rule.RuleModule;
|
||||
'no-typos': import("eslint").Rule.RuleModule;
|
||||
'no-unescaped-entities': import("eslint").Rule.RuleModule;
|
||||
'no-unknown-property': import("eslint").Rule.RuleModule;
|
||||
'no-unsafe': import("eslint").Rule.RuleModule;
|
||||
'no-unstable-nested-components': import("eslint").Rule.RuleModule;
|
||||
'no-unused-class-component-methods': import("eslint").Rule.RuleModule;
|
||||
'no-unused-prop-types': import("eslint").Rule.RuleModule;
|
||||
'no-unused-state': import("eslint").Rule.RuleModule;
|
||||
'no-object-type-as-default-prop': import("eslint").Rule.RuleModule;
|
||||
'no-will-update-set-state': import("eslint").Rule.RuleModule;
|
||||
'prefer-es6-class': import("eslint").Rule.RuleModule;
|
||||
'prefer-exact-props': import("eslint").Rule.RuleModule;
|
||||
'prefer-read-only-props': import("eslint").Rule.RuleModule;
|
||||
'prefer-stateless-function': import("eslint").Rule.RuleModule;
|
||||
'prop-types': import("eslint").Rule.RuleModule;
|
||||
'react-in-jsx-scope': import("eslint").Rule.RuleModule;
|
||||
'require-default-props': import("eslint").Rule.RuleModule;
|
||||
'require-optimization': import("eslint").Rule.RuleModule;
|
||||
'require-render-return': import("eslint").Rule.RuleModule;
|
||||
'self-closing-comp': import("eslint").Rule.RuleModule;
|
||||
'sort-comp': import("eslint").Rule.RuleModule;
|
||||
'sort-default-props': import("eslint").Rule.RuleModule;
|
||||
'sort-prop-types': import("eslint").Rule.RuleModule;
|
||||
'state-in-constructor': import("eslint").Rule.RuleModule;
|
||||
'static-property-placement': import("eslint").Rule.RuleModule;
|
||||
'style-prop-object': import("eslint").Rule.RuleModule;
|
||||
'void-dom-elements-no-children': import("eslint").Rule.RuleModule;
|
||||
};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AAIA,oEAAoE;AACpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwGE"}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint global-require: 0 */
|
||||
|
||||
/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
|
||||
const rules = {
|
||||
'boolean-prop-naming': require('./boolean-prop-naming'),
|
||||
'button-has-type': require('./button-has-type'),
|
||||
'checked-requires-onchange-or-readonly': require('./checked-requires-onchange-or-readonly'),
|
||||
'default-props-match-prop-types': require('./default-props-match-prop-types'),
|
||||
'destructuring-assignment': require('./destructuring-assignment'),
|
||||
'display-name': require('./display-name'),
|
||||
'forbid-component-props': require('./forbid-component-props'),
|
||||
'forbid-dom-props': require('./forbid-dom-props'),
|
||||
'forbid-elements': require('./forbid-elements'),
|
||||
'forbid-foreign-prop-types': require('./forbid-foreign-prop-types'),
|
||||
'forbid-prop-types': require('./forbid-prop-types'),
|
||||
'forward-ref-uses-ref': require('./forward-ref-uses-ref'),
|
||||
'function-component-definition': require('./function-component-definition'),
|
||||
'hook-use-state': require('./hook-use-state'),
|
||||
'iframe-missing-sandbox': require('./iframe-missing-sandbox'),
|
||||
'jsx-boolean-value': require('./jsx-boolean-value'),
|
||||
'jsx-child-element-spacing': require('./jsx-child-element-spacing'),
|
||||
'jsx-closing-bracket-location': require('./jsx-closing-bracket-location'),
|
||||
'jsx-closing-tag-location': require('./jsx-closing-tag-location'),
|
||||
'jsx-curly-spacing': require('./jsx-curly-spacing'),
|
||||
'jsx-curly-newline': require('./jsx-curly-newline'),
|
||||
'jsx-equals-spacing': require('./jsx-equals-spacing'),
|
||||
'jsx-filename-extension': require('./jsx-filename-extension'),
|
||||
'jsx-first-prop-new-line': require('./jsx-first-prop-new-line'),
|
||||
'jsx-handler-names': require('./jsx-handler-names'),
|
||||
'jsx-indent': require('./jsx-indent'),
|
||||
'jsx-indent-props': require('./jsx-indent-props'),
|
||||
'jsx-key': require('./jsx-key'),
|
||||
'jsx-max-depth': require('./jsx-max-depth'),
|
||||
'jsx-max-props-per-line': require('./jsx-max-props-per-line'),
|
||||
'jsx-newline': require('./jsx-newline'),
|
||||
'jsx-no-bind': require('./jsx-no-bind'),
|
||||
'jsx-no-comment-textnodes': require('./jsx-no-comment-textnodes'),
|
||||
'jsx-no-constructed-context-values': require('./jsx-no-constructed-context-values'),
|
||||
'jsx-no-duplicate-props': require('./jsx-no-duplicate-props'),
|
||||
'jsx-no-leaked-render': require('./jsx-no-leaked-render'),
|
||||
'jsx-no-literals': require('./jsx-no-literals'),
|
||||
'jsx-no-script-url': require('./jsx-no-script-url'),
|
||||
'jsx-no-target-blank': require('./jsx-no-target-blank'),
|
||||
'jsx-no-useless-fragment': require('./jsx-no-useless-fragment'),
|
||||
'jsx-one-expression-per-line': require('./jsx-one-expression-per-line'),
|
||||
'jsx-no-undef': require('./jsx-no-undef'),
|
||||
'jsx-curly-brace-presence': require('./jsx-curly-brace-presence'),
|
||||
'jsx-pascal-case': require('./jsx-pascal-case'),
|
||||
'jsx-fragments': require('./jsx-fragments'),
|
||||
'jsx-props-no-multi-spaces': require('./jsx-props-no-multi-spaces'),
|
||||
'jsx-props-no-spreading': require('./jsx-props-no-spreading'),
|
||||
'jsx-props-no-spread-multi': require('./jsx-props-no-spread-multi'),
|
||||
'jsx-sort-default-props': require('./jsx-sort-default-props'),
|
||||
'jsx-sort-props': require('./jsx-sort-props'),
|
||||
'jsx-space-before-closing': require('./jsx-space-before-closing'),
|
||||
'jsx-tag-spacing': require('./jsx-tag-spacing'),
|
||||
'jsx-uses-react': require('./jsx-uses-react'),
|
||||
'jsx-uses-vars': require('./jsx-uses-vars'),
|
||||
'jsx-wrap-multilines': require('./jsx-wrap-multilines'),
|
||||
'no-invalid-html-attribute': require('./no-invalid-html-attribute'),
|
||||
'no-access-state-in-setstate': require('./no-access-state-in-setstate'),
|
||||
'no-adjacent-inline-elements': require('./no-adjacent-inline-elements'),
|
||||
'no-array-index-key': require('./no-array-index-key'),
|
||||
'no-arrow-function-lifecycle': require('./no-arrow-function-lifecycle'),
|
||||
'no-children-prop': require('./no-children-prop'),
|
||||
'no-danger': require('./no-danger'),
|
||||
'no-danger-with-children': require('./no-danger-with-children'),
|
||||
'no-deprecated': require('./no-deprecated'),
|
||||
'no-did-mount-set-state': require('./no-did-mount-set-state'),
|
||||
'no-did-update-set-state': require('./no-did-update-set-state'),
|
||||
'no-direct-mutation-state': require('./no-direct-mutation-state'),
|
||||
'no-find-dom-node': require('./no-find-dom-node'),
|
||||
'no-is-mounted': require('./no-is-mounted'),
|
||||
'no-multi-comp': require('./no-multi-comp'),
|
||||
'no-namespace': require('./no-namespace'),
|
||||
'no-set-state': require('./no-set-state'),
|
||||
'no-string-refs': require('./no-string-refs'),
|
||||
'no-redundant-should-component-update': require('./no-redundant-should-component-update'),
|
||||
'no-render-return-value': require('./no-render-return-value'),
|
||||
'no-this-in-sfc': require('./no-this-in-sfc'),
|
||||
'no-typos': require('./no-typos'),
|
||||
'no-unescaped-entities': require('./no-unescaped-entities'),
|
||||
'no-unknown-property': require('./no-unknown-property'),
|
||||
'no-unsafe': require('./no-unsafe'),
|
||||
'no-unstable-nested-components': require('./no-unstable-nested-components'),
|
||||
'no-unused-class-component-methods': require('./no-unused-class-component-methods'),
|
||||
'no-unused-prop-types': require('./no-unused-prop-types'),
|
||||
'no-unused-state': require('./no-unused-state'),
|
||||
'no-object-type-as-default-prop': require('./no-object-type-as-default-prop'),
|
||||
'no-will-update-set-state': require('./no-will-update-set-state'),
|
||||
'prefer-es6-class': require('./prefer-es6-class'),
|
||||
'prefer-exact-props': require('./prefer-exact-props'),
|
||||
'prefer-read-only-props': require('./prefer-read-only-props'),
|
||||
'prefer-stateless-function': require('./prefer-stateless-function'),
|
||||
'prop-types': require('./prop-types'),
|
||||
'react-in-jsx-scope': require('./react-in-jsx-scope'),
|
||||
'require-default-props': require('./require-default-props'),
|
||||
'require-optimization': require('./require-optimization'),
|
||||
'require-render-return': require('./require-render-return'),
|
||||
'self-closing-comp': require('./self-closing-comp'),
|
||||
'sort-comp': require('./sort-comp'),
|
||||
'sort-default-props': require('./sort-default-props'),
|
||||
'sort-prop-types': require('./sort-prop-types'),
|
||||
'state-in-constructor': require('./state-in-constructor'),
|
||||
'static-property-placement': require('./static-property-placement'),
|
||||
'style-prop-object': require('./style-prop-object'),
|
||||
'void-dom-elements-no-children': require('./void-dom-elements-no-children'),
|
||||
};
|
||||
|
||||
module.exports = rules;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const _exports: import('eslint').Rule.RuleModule;
|
||||
export = _exports;
|
||||
//# sourceMappingURL=jsx-boolean-value.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"jsx-boolean-value.d.ts","sourceRoot":"","sources":["jsx-boolean-value.js"],"names":[],"mappings":"wBAwDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user