Socket
Socket
Sign inDemoInstall

eslint-plugin-jsx-a11y

Package Overview
Dependencies
Maintainers
1
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-jsx-a11y - npm Package Compare versions

Comparing version 0.5.2 to 0.5.3

TODO.md

8

lib/rules/img-uses-alt.js

@@ -15,2 +15,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
var _getNodeType = require('../util/getNodeType');

@@ -38,5 +42,7 @@

var hasAltProp = (0, _hasAttribute2.default)(node.attributes, 'alt');
var altProp = hasAltProp ? (0, _getAttributeValue2.default)(hasAltProp) : undefined;
var isInvalid = hasAltProp === false || Boolean(altProp) === false;
// alt must have a value.
if (hasAltProp === false || hasAltProp === null) {
if (isInvalid) {
context.report({

@@ -43,0 +49,0 @@ node: node,

10

lib/rules/label-uses-for.js

@@ -15,2 +15,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
var _getNodeType = require('../util/getNodeType');

@@ -35,5 +39,7 @@

var hasHtmlForAttr = (0, _hasAttribute2.default)(node.attributes, 'htmlFor');
var htmlForAttr = (0, _hasAttribute2.default)(node.attributes, 'htmlFor');
var htmlForValue = (0, _getAttributeValue2.default)(htmlForAttr);
var isInvalid = htmlForAttr === false || htmlForValue === null || htmlForValue === undefined;
if (hasHtmlForAttr === false) {
if (isInvalid) {
context.report({

@@ -40,0 +46,0 @@ node: node,

@@ -16,2 +16,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -29,5 +33,9 @@

var hasOnMouseOver = (0, _hasAttribute2.default)(attributes, 'onMouseOver');
if (Boolean(hasOnMouseOver) === true) {
var onMouseOverValue = (0, _getAttributeValue2.default)(hasOnMouseOver);
if (Boolean(hasOnMouseOver) === true && (onMouseOverValue !== null || onMouseOverValue !== undefined)) {
var hasOnFocus = (0, _hasAttribute2.default)(attributes, 'onFocus');
if (hasOnFocus === false) {
var onFocusValue = (0, _getAttributeValue2.default)(hasOnFocus);
if (hasOnFocus === false || onFocusValue === null || onFocusValue === undefined) {
context.report({

@@ -42,5 +50,8 @@ node: node,

var hasOnMouseOut = (0, _hasAttribute2.default)(attributes, 'onMouseOut');
if (Boolean(hasOnMouseOut) === true) {
var onMouseOutValue = (0, _getAttributeValue2.default)(hasOnMouseOut);
if (Boolean(hasOnMouseOut) === true && (onMouseOutValue !== null || onMouseOutValue !== undefined)) {
var hasOnBlur = (0, _hasAttribute2.default)(attributes, 'onBlur');
if (hasOnBlur === false) {
var onBlurValue = (0, _getAttributeValue2.default)(hasOnBlur);
if (hasOnBlur === false || onBlurValue === null || onBlurValue === undefined) {
context.report({

@@ -47,0 +58,0 @@ node: node,

@@ -15,5 +15,9 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = 'No access key attribute allowed. Incosistencies ' + 'between keyboard shortcuts and keyboard comments used by screenreader ' + 'and keyboard only users create a11y complications.';
var errorMessage = 'No access key attribute allowed. Inconsistencies ' + 'between keyboard shortcuts and keyboard comments used by screenreader ' + 'and keyboard only users create a11y complications.';

@@ -24,4 +28,5 @@ module.exports = function (context) {

var hasAccessKey = (0, _hasAttribute2.default)(node.attributes, 'accesskey');
var accessKeyValue = (0, _getAttributeValue2.default)(hasAccessKey);
if (Boolean(hasAccessKey) === true) {
if (Boolean(hasAccessKey) === true && Boolean(accessKeyValue) === true) {
context.report({

@@ -28,0 +33,0 @@ node: node,

@@ -15,2 +15,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
var _getNodeType = require('../util/getNodeType');

@@ -36,4 +40,5 @@

var href = (0, _hasAttribute2.default)(node.attributes, 'href');
var value = (0, _getAttributeValue2.default)(href);
if (href === '#') {
if (href && value === '#') {
context.report({

@@ -40,0 +45,0 @@ node: node,

@@ -20,2 +20,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
var _getNodeType = require('../util/getNodeType');

@@ -43,3 +47,4 @@

var isNonInteractive = (0, _isInteractiveElement2.default)((0, _getNodeType2.default)(node), attributes) === false;
var noRoleAttribute = (0, _hasAttribute2.default)(attributes, 'role') === false;
var roleAttribute = (0, _hasAttribute2.default)(attributes, 'role');
var noRoleAttribute = roleAttribute === false || Boolean((0, _getAttributeValue2.default)(roleAttribute)) === false;

@@ -46,0 +51,0 @@ // Visible, non-interactive elements require role attribute.

@@ -15,2 +15,6 @@ /**

var _getAttributeValue = require('../util/getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');

@@ -30,2 +34,4 @@

var validTypes = ['LITERAL', 'TEMPLATELITERAL'];
module.exports = function (context) {

@@ -40,7 +46,20 @@ return {

var altProp = (0, _hasAttribute2.default)(node.attributes, 'alt');
// Return if alt prop is not present.
if (altProp === false) {
return;
}
// Only check literals, as we should not enforce variable names :P
var normalizedType = altProp.value && altProp.value.type.toUpperCase() === 'JSXEXPRESSIONCONTAINER' ? altProp.value.expression.type.toUpperCase() : altProp.value.type.toUpperCase();
if (validTypes.indexOf(normalizedType) === -1) {
return;
}
var value = (0, _getAttributeValue2.default)(altProp);
var isVisible = (0, _isHiddenFromScreenReader2.default)(node.attributes) === false;
if (Boolean(altProp) && typeof altProp === 'string' && isVisible) {
if (Boolean(value) && typeof value === 'string' && isVisible) {
var hasRedundancy = REDUNDANT_WORDS.some(function (word) {
return Boolean(altProp.match(new RegExp('(?!{)' + word + '(?!})', 'gi')));
return Boolean(value.match(new RegExp('(?!{)' + word + '(?!})', 'gi')));
});

@@ -47,0 +66,0 @@

@@ -21,2 +21,3 @@ 'use strict';

switch (expression.type) {

@@ -35,3 +36,12 @@ case 'Literal':

case 'LogicalExpression':
return getValue(expression.left) && getValue(expression.right);
var operator = expression.operator;
var left = expression.left;
var right = expression.right;
var leftVal = getValue(left);
var rightVal = getValue(right);
return operator == '&&' ? leftVal && rightVal : leftVal || rightVal;
case 'MemberExpression':
return getValue(expression.object) + '.' + expression.property;
default:

@@ -38,0 +48,0 @@ return undefined;

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getAttributeValue = require('./getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Returns the value of the attribute or false, indicating the attribute
* is not present on the JSX opening element. This skips over spread attributes
* Returns the JSXAttribute itself or false, indicating the attribute
* is not present on the JSXOpeningElement. This skips over spread attributes
* as the purpose of this linter is to do hard checks of explicit JSX props.
*
* This treats undefined values as missing props, as they will not be used for
* rendering on elements that live closest to the DOM (pure html JSX elements).
*/
Object.defineProperty(exports, "__esModule", {
value: true
});
var hasAttribute = function hasAttribute(attributes, attribute) {
var value = false;
var nodeAttribute = undefined;

@@ -32,6 +24,4 @@ var hasAttr = attributes.some(function (attr) {

if (attr.name.name.toUpperCase() === attribute.toUpperCase()) {
value = (0, _getAttributeValue2.default)(attr);
// If the value is undefined, it doesn't really have the attribute.
return value !== undefined;
nodeAttribute = attr;
return true;
}

@@ -42,5 +32,5 @@

return hasAttr ? value : false;
return hasAttr ? nodeAttribute : false;
};
exports.default = hasAttribute;

@@ -11,2 +11,6 @@ 'use strict';

var _getAttributeValue = require('./getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -21,6 +25,6 @@

var isHiddenFromScreenReader = function isHiddenFromScreenReader(attributes) {
var hasAriaHidden = (0, _hasAttribute2.default)(attributes, 'aria-hidden');
return hasAriaHidden && (hasAriaHidden === true || hasAriaHidden === null);
var ariaHidden = (0, _getAttributeValue2.default)((0, _hasAttribute2.default)(attributes, 'aria-hidden'));
return ariaHidden === true || ariaHidden === null;
};
exports.default = isHiddenFromScreenReader;

@@ -11,2 +11,6 @@ 'use strict';

var _getAttributeValue = require('./getAttributeValue');
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -24,4 +28,4 @@

input: function input(attributes) {
var hasTypeAttr = (0, _hasAttribute2.default)(attributes, 'type');
return hasTypeAttr ? hasTypeAttr.toUpperCase() !== 'HIDDEN' : true;
var typeAttr = (0, _getAttributeValue2.default)((0, _hasAttribute2.default)(attributes, 'type'));
return typeAttr ? typeAttr.toUpperCase() !== 'HIDDEN' : true;
},

@@ -28,0 +32,0 @@ option: function option() {

{
"name": "eslint-plugin-jsx-a11y",
"version": "0.5.2",
"version": "0.5.3",
"description": "A static analysis linter of jsx and their accessibility with screen readers.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -12,2 +12,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
import getNodeType from '../util/getNodeType';

@@ -28,5 +29,7 @@

const hasAltProp = hasAttribute(node.attributes, 'alt');
const altProp = hasAltProp ? getAttributeValue(hasAltProp) : undefined;
const isInvalid = hasAltProp === false || Boolean(altProp) === false;
// alt must have a value.
if (hasAltProp === false || hasAltProp === null) {
if (isInvalid) {
context.report({

@@ -33,0 +36,0 @@ node,

@@ -12,2 +12,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
import getNodeType from '../util/getNodeType';

@@ -28,5 +29,7 @@

const hasHtmlForAttr = hasAttribute(node.attributes, 'htmlFor');
const htmlForAttr = hasAttribute(node.attributes, 'htmlFor');
const htmlForValue = getAttributeValue(htmlForAttr);
const isInvalid = htmlForAttr === false || htmlForValue === null || htmlForValue === undefined;
if (hasHtmlForAttr === false) {
if (isInvalid) {
context.report({

@@ -33,0 +36,0 @@ node,

@@ -13,2 +13,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';

@@ -24,5 +25,9 @@ const mouseOverErrorMessage = 'onMouseOver must be accompanied by onFocus for accessibility.';

const hasOnMouseOver = hasAttribute(attributes, 'onMouseOver');
if (Boolean(hasOnMouseOver) === true) {
const onMouseOverValue = getAttributeValue(hasOnMouseOver);
if (Boolean(hasOnMouseOver) === true && (onMouseOverValue !== null || onMouseOverValue !== undefined)) {
const hasOnFocus = hasAttribute(attributes, 'onFocus');
if (hasOnFocus === false) {
const onFocusValue = getAttributeValue(hasOnFocus);
if (hasOnFocus === false || onFocusValue === null || onFocusValue === undefined) {
context.report({

@@ -37,5 +42,8 @@ node,

const hasOnMouseOut = hasAttribute(attributes, 'onMouseOut');
if (Boolean(hasOnMouseOut) === true) {
const onMouseOutValue = getAttributeValue(hasOnMouseOut);
if (Boolean(hasOnMouseOut) === true && (onMouseOutValue !== null || onMouseOutValue !== undefined)) {
const hasOnBlur = hasAttribute(attributes, 'onBlur');
if (hasOnBlur === false) {
const onBlurValue = getAttributeValue(hasOnBlur);
if (hasOnBlur === false || onBlurValue === null || onBlurValue === undefined) {
context.report({

@@ -42,0 +50,0 @@ node,

@@ -12,4 +12,5 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
const errorMessage = 'No access key attribute allowed. Incosistencies ' +
const errorMessage = 'No access key attribute allowed. Inconsistencies ' +
'between keyboard shortcuts and keyboard comments used by screenreader ' +

@@ -21,4 +22,5 @@ 'and keyboard only users create a11y complications.';

const hasAccessKey = hasAttribute(node.attributes, 'accesskey');
const accessKeyValue = getAttributeValue(hasAccessKey);
if (Boolean(hasAccessKey) === true) {
if (Boolean(hasAccessKey) === true && Boolean(accessKeyValue) === true) {
context.report({

@@ -25,0 +27,0 @@ node,

@@ -12,2 +12,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
import getNodeType from '../util/getNodeType';

@@ -28,4 +29,5 @@

const href = hasAttribute(node.attributes, 'href');
const value = getAttributeValue(href);
if (href === '#') {
if (href && value === '#') {
context.report({

@@ -32,0 +34,0 @@ node,

@@ -11,2 +11,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
import getNodeType from '../util/getNodeType';

@@ -30,3 +31,4 @@

const isNonInteractive = isInteractiveElement(getNodeType(node), attributes) === false;
const noRoleAttribute = hasAttribute(attributes, 'role') === false;
const roleAttribute = hasAttribute(attributes, 'role');
const noRoleAttribute = roleAttribute === false || Boolean(getAttributeValue(roleAttribute)) === false;

@@ -33,0 +35,0 @@ // Visible, non-interactive elements require role attribute.

@@ -12,2 +12,3 @@ /**

import hasAttribute from '../util/hasAttribute';
import getAttributeValue from '../util/getAttributeValue';
import isHiddenFromScreenReader from '../util/isHiddenFromScreenReader';

@@ -25,2 +26,7 @@ import getNodeType from '../util/getNodeType';

const validTypes = [
'LITERAL',
'TEMPLATELITERAL'
];
module.exports = context => ({

@@ -34,7 +40,22 @@ JSXOpeningElement: node => {

const altProp = hasAttribute(node.attributes, 'alt');
// Return if alt prop is not present.
if (altProp === false) {
return;
}
// Only check literals, as we should not enforce variable names :P
const normalizedType = altProp.value && altProp.value.type.toUpperCase() === 'JSXEXPRESSIONCONTAINER' ?
altProp.value.expression.type.toUpperCase() :
altProp.value.type.toUpperCase();
if (validTypes.indexOf(normalizedType) === -1) {
return;
}
const value = getAttributeValue(altProp);
const isVisible = isHiddenFromScreenReader(node.attributes) === false;
if (Boolean(altProp) && typeof altProp === 'string' && isVisible) {
if (Boolean(value) && typeof value === 'string' && isVisible) {
const hasRedundancy = REDUNDANT_WORDS
.some(word => Boolean(altProp.match(new RegExp(`(?!{)${word}(?!})`, 'gi'))));
.some(word => Boolean(value.match(new RegExp(`(?!{)${word}(?!})`, 'gi'))));

@@ -41,0 +62,0 @@ if (hasRedundancy === true) {

@@ -11,3 +11,3 @@ 'use strict';

} else if (value.type === 'JSXExpressionContainer') {
const expression = value.expression;
const { expression } = value;

@@ -25,3 +25,9 @@ switch (expression.type) {

case 'LogicalExpression':
return getValue(expression.left) && getValue(expression.right);
const { operator, left, right } = expression;
const leftVal = getValue(left);
const rightVal = getValue(right);
return operator == '&&' ? leftVal && rightVal : leftVal || rightVal;
case 'MemberExpression':
return `${getValue(expression.object)}.${expression.property}`;
default:

@@ -28,0 +34,0 @@ return undefined;

'use strict';
import getAttributeValue from './getAttributeValue';
/**
* Returns the value of the attribute or false, indicating the attribute
* is not present on the JSX opening element. This skips over spread attributes
* Returns the JSXAttribute itself or false, indicating the attribute
* is not present on the JSXOpeningElement. This skips over spread attributes
* as the purpose of this linter is to do hard checks of explicit JSX props.
*
* This treats undefined values as missing props, as they will not be used for
* rendering on elements that live closest to the DOM (pure html JSX elements).
*/
const hasAttribute = (attributes, attribute) => {
let value = false;
let nodeAttribute = undefined;

@@ -24,6 +21,4 @@ const hasAttr = attributes.some(attr => {

if (attr.name.name.toUpperCase() === attribute.toUpperCase()) {
value = getAttributeValue(attr);
// If the value is undefined, it doesn't really have the attribute.
return value !== undefined;
nodeAttribute = attr;
return true;
}

@@ -34,5 +29,5 @@

return hasAttr ? value : false;
return hasAttr ? nodeAttribute : false;
};
export default hasAttribute;
'use strict';
import hasAttribute from './hasAttribute';
import getAttributeValue from './getAttributeValue';

@@ -12,6 +13,6 @@ /**

const isHiddenFromScreenReader = attributes => {
const hasAriaHidden = hasAttribute(attributes, 'aria-hidden');
return hasAriaHidden && (hasAriaHidden === true || hasAriaHidden === null);
const ariaHidden = getAttributeValue(hasAttribute(attributes, 'aria-hidden'));
return ariaHidden === true || ariaHidden === null;
};
export default isHiddenFromScreenReader;
'use strict';
import hasAttribute from './hasAttribute';
import getAttributeValue from './getAttributeValue';

@@ -13,4 +14,4 @@ const interactiveMap = {

input: attributes => {
const hasTypeAttr = hasAttribute(attributes, 'type');
return hasTypeAttr ? hasTypeAttr.toUpperCase() !== 'HIDDEN' : true;
const typeAttr = getAttributeValue(hasAttribute(attributes, 'type'));
return typeAttr ? typeAttr.toUpperCase() !== 'HIDDEN' : true;
},

@@ -17,0 +18,0 @@ option: () => true,

@@ -59,2 +59,4 @@ /**

{ code: '<img alt={alt || "Alt text" } />', parserOptions },
{ code: '<img alt={photo.caption} />;', parserOptions },
{ code: '<img alt=" " />', parserOptions }, // For decorative images.

@@ -61,0 +63,0 @@ // CUSTOM ELEMENT TESTS FOR STRING OPTION

@@ -29,3 +29,3 @@ /**

const expectedError = {
message: 'No access key attribute allowed. Incosistencies ' +
message: 'No access key attribute allowed. Inconsistencies ' +
'between keyboard shortcuts and keyboard comments used by screenreader ' +

@@ -32,0 +32,0 @@ 'and keyboard only users create a11y complications.',

@@ -58,3 +58,4 @@ /**

{ code: '<img aria-hidden={false} alt="Doing cool things." />', parserOptions },
{ code: '<UX.Layout>test</UX.Layout>', parserOptions }
{ code: '<UX.Layout>test</UX.Layout>', parserOptions },
{ code: '<img alt={imageAlt} />', parserOptions }
],

@@ -75,9 +76,9 @@ invalid: [

{ code: '<img alt="picture" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`picture doing ${things}`}" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`photo doing ${things}`}" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`image doing ${things}`}" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`picture doing ${picture}`}" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`photo doing ${photo}`}" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="{`image doing ${image}`}" {...this.props} />', errors: [ expectedError ], parserOptions }
{ code: '<img alt={`picture doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`photo doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`image doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`picture doing ${picture}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`photo doing ${photo}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`image doing ${image}`} {...this.props} />', errors: [ expectedError ], parserOptions }
]
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc