eslint-plugin-jsx-a11y
Advanced tools
Comparing version 0.6.2 to 1.0.0
@@ -67,3 +67,2 @@ module.exports = { | ||
} ], | ||
"new-cap": 2, | ||
"no-alert": 2, | ||
@@ -148,2 +147,3 @@ "no-confusing-arrow": 2, | ||
"prefer-template": 2, | ||
'quotes': [2, 'single', 'avoid-escape'], | ||
"radix": 2, | ||
@@ -150,0 +150,0 @@ "semi": 2, |
@@ -0,1 +1,21 @@ | ||
1.0.0 / 2016-04-19 | ||
================== | ||
- [breaking] Rename `img-uses-alt` to `img-has-alt` | ||
- [breaking] Rename `onlick-uses-role` to `onclick-has-role` | ||
- [breaking] Rename `mouse-events-map-to-key-events` to `mouse-events-have-key-events` | ||
- [breaking] Rename `use-onblur-not-onchange` to `no-onchange` | ||
- [breaking] Rename `label-uses-for` to `label-has-for` | ||
- [breaking] Rename `redundant-alt` to `img-redundant-alt` | ||
- [breaking] Rename `no-hash-href` to `href-no-hash` | ||
- [breaking] Rename `valid-aria-role` to `aria-role` | ||
- [new] Implement `aria-props` rule | ||
- [new] Implement `aria-proptypes` rule | ||
- [new] Implement `aria-unsupported-elements` rule | ||
- [new] Implement `onclick-has-focus` rule | ||
- [new] Implement `role-has-required-aria-props` rule | ||
- [new] Implement `role-supports-aria-props` rule | ||
- [new] Implement `tabindex-no-positive` rule | ||
0.6.2 / 2016-04-08 | ||
@@ -2,0 +22,0 @@ ================== |
@@ -5,11 +5,18 @@ 'use strict'; | ||
rules: { | ||
'img-uses-alt': require('./rules/img-uses-alt'), | ||
'redundant-alt': require('./rules/redundant-alt'), | ||
'onclick-uses-role': require('./rules/onclick-uses-role'), | ||
'mouse-events-map-to-key-events': require('./rules/mouse-events-map-to-key-events'), | ||
'use-onblur-not-onchange': require('./rules/use-onblur-not-onchange'), | ||
'aria-props': require('./rules/aria-props'), | ||
'aria-proptypes': require('./rules/aria-proptypes'), | ||
'aria-role': require('./rules/aria-role'), | ||
'aria-unsupported-elements': require('./rules/aria-unsupported-elements'), | ||
'href-no-hash': require('./rules/href-no-hash'), | ||
'img-has-alt': require('./rules/img-has-alt'), | ||
'img-redundant-alt': require('./rules/img-redundant-alt'), | ||
'label-has-for': require('./rules/label-has-for'), | ||
'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'), | ||
'no-access-key': require('./rules/no-access-key'), | ||
'label-uses-for': require('./rules/label-uses-for'), | ||
'no-hash-href': require('./rules/no-hash-href'), | ||
'valid-aria-role': require('./rules/valid-aria-role') | ||
'no-onchange': require('./rules/no-onchange'), | ||
'onclick-has-focus': require('./rules/onclick-has-focus'), | ||
'onclick-has-role': require('./rules/onclick-has-role'), | ||
'role-has-required-aria-props': require('./rules/role-has-required-aria-props'), | ||
'role-supports-aria-props': require('./rules/role-supports-aria-props'), | ||
'tabindex-no-positive': require('./rules/tabindex-no-positive') | ||
}, | ||
@@ -24,11 +31,18 @@ configs: { | ||
rules: { | ||
"jsx-a11y/img-uses-alt": 2, | ||
"jsx-a11y/redundant-alt": 2, | ||
"jsx-a11y/onclick-uses-role": 2, | ||
"jsx-a11y/mouse-events-map-to-key-events": 2, | ||
"jsx-a11y/use-onblur-not-onchange": 2, | ||
"jsx-a11y/no-access-key": 2, | ||
"jsx-a11y/label-uses-for": 2, | ||
"jsx-a11y/no-hash-href": 2, | ||
"jsx-a11y/valid-aria-role": 2 | ||
'jsx-a11y/aria-props': 2, | ||
'jsx-a11y/aria-proptypes': 2, | ||
'jsx-a11y/aria-role': 2, | ||
'jsx-a11y/aria-unsupported-elements': 2, | ||
'jsx-a11y/href-no-hash': 2, | ||
'jsx-a11y/img-has-alt': 2, | ||
'jsx-a11y/img-redundant-alt': 2, | ||
'jsx-a11y/label-has-for': 2, | ||
'jsx-a11y/mouse-events-have-key-events': 2, | ||
'jsx-a11y/no-access-key': 2, | ||
'jsx-a11y/no-onchange': 2, | ||
'jsx-a11y/onclick-hs-focus': 2, | ||
'jsx-a11y/onclick-has-role': 2, | ||
'jsx-a11y/role-has-required-aria-props': 2, | ||
'jsx-a11y/role-supports-aria-props': 2, | ||
'jsx-a11y/tabindex-no-positive': 2 | ||
} | ||
@@ -35,0 +49,0 @@ } |
@@ -11,5 +11,5 @@ /** | ||
var _hasAttribute = require('../util/hasAttribute'); | ||
var _getAttribute = require('../util/getAttribute'); | ||
var _hasAttribute2 = _interopRequireDefault(_hasAttribute); | ||
var _getAttribute2 = _interopRequireDefault(_getAttribute); | ||
@@ -27,6 +27,6 @@ var _getAttributeValue = require('../util/getAttributeValue'); | ||
JSXOpeningElement: function JSXOpeningElement(node) { | ||
var hasAccessKey = (0, _hasAttribute2.default)(node.attributes, 'accesskey'); | ||
var accessKeyValue = (0, _getAttributeValue2.default)(hasAccessKey); | ||
var accessKey = (0, _getAttribute2.default)(node.attributes, 'accesskey'); | ||
var accessKeyValue = (0, _getAttributeValue2.default)(accessKey); | ||
if (Boolean(hasAccessKey) === true && Boolean(accessKeyValue) === true) { | ||
if (accessKey && accessKeyValue) { | ||
context.report({ | ||
@@ -33,0 +33,0 @@ node: node, |
@@ -6,51 +6,24 @@ 'use strict'; | ||
}); | ||
exports.getLiteralAttributeValue = undefined; | ||
exports.default = getAttributeValue; | ||
var _buildTemplateLiteral = require('./buildTemplateLiteral'); | ||
var _values = require('./values'); | ||
var _buildTemplateLiteral2 = _interopRequireDefault(_buildTemplateLiteral); | ||
var _values2 = _interopRequireDefault(_values); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var getValue = function getValue(value) { | ||
if (value.type === 'Literal') { | ||
return value.value; | ||
} else if (value.type === 'Identifier') { | ||
return value.name === "" ? undefined : value.name; | ||
} else if (value.type === 'JSXElement') { | ||
return undefined; // For now, just so things don't break. | ||
var extractValue = function extractValue(attribute, extractor) { | ||
if (attribute && attribute.type === 'JSXAttribute') { | ||
if (attribute.value === null) { | ||
// Null valued attributes imply truthiness. | ||
// For example: <div aria-hidden /> | ||
// See: https://facebook.github.io/react/docs/jsx-in-depth.html#boolean-attributes | ||
return true; | ||
} | ||
return extractor(attribute.value); | ||
} | ||
var expression = value.expression; | ||
var type = expression ? expression.type : value.type; | ||
var obj = expression || value; | ||
switch (type) { | ||
case 'Literal': | ||
return obj.value; | ||
case 'TemplateLiteral': | ||
return (0, _buildTemplateLiteral2.default)(obj); | ||
case 'Identifier': | ||
return obj.name == 'undefined' ? undefined : obj.name; | ||
case 'ArrowFunctionExpression': | ||
case 'FunctionExpression': | ||
return function () { | ||
return void 0; | ||
}; | ||
case 'LogicalExpression': | ||
var operator = obj.operator; | ||
var left = obj.left; | ||
var right = obj.right; | ||
var leftVal = getValue(left); | ||
var rightVal = getValue(right); | ||
return operator == '&&' ? leftVal && rightVal : leftVal || rightVal; | ||
case 'MemberExpression': | ||
return getValue(obj.object) + '.' + getValue(obj.property); | ||
case 'CallExpression': | ||
return getValue(obj.callee); | ||
default: | ||
return undefined; | ||
} | ||
return undefined; | ||
}; | ||
@@ -65,13 +38,22 @@ | ||
* value with the intention of the JSX. | ||
* | ||
* @param attribute - The JSXAttribute collected by AST parser. | ||
*/ | ||
var getAttributeValue = function getAttributeValue(attribute) { | ||
if (attribute.value === null) { | ||
return null; | ||
} else if (attribute.type === 'JSXAttribute') { | ||
return getValue(attribute.value); | ||
} | ||
function getAttributeValue(attribute) { | ||
return extractValue(attribute, _values2.default); | ||
} | ||
return undefined; | ||
}; | ||
exports.default = getAttributeValue; | ||
/** | ||
* Returns the value of a given attribute. | ||
* Different types of attributes have their associated | ||
* values in different properties on the object. | ||
* | ||
* This function should return a value only if we can extract | ||
* a literal value from its attribute (i.e. values that have generic | ||
* types in JavaScript - strings, numbers, booleans, etc.) | ||
* | ||
* @param attribute - The JSXAttribute collected by AST parser. | ||
*/ | ||
var getLiteralAttributeValue = exports.getLiteralAttributeValue = function getLiteralAttributeValue(attribute) { | ||
return extractValue(attribute, _values.getLiteralValue); | ||
}; |
'use strict'; | ||
/** | ||
* Returns the tagName associated with a JSXElement. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var getNodeType = function getNodeType(node) { | ||
exports.default = getNodeType; | ||
function getNodeType(node) { | ||
var name = node.name; | ||
@@ -18,4 +23,2 @@ | ||
return node.name.name; | ||
}; | ||
exports.default = getNodeType; | ||
} |
@@ -7,5 +7,5 @@ 'use strict'; | ||
var _hasAttribute = require('./hasAttribute'); | ||
var _getAttribute = require('./getAttribute'); | ||
var _hasAttribute2 = _interopRequireDefault(_hasAttribute); | ||
var _getAttribute2 = _interopRequireDefault(_getAttribute); | ||
@@ -20,11 +20,20 @@ var _getAttributeValue = require('./getAttributeValue'); | ||
* Returns boolean indicating that the aria-hidden prop | ||
* is present or the value is true. | ||
* is present or the value is true. Will also return true if | ||
* there is an input with type='hidden'. | ||
* | ||
* <div aria-hidden /> is equivalent to the DOM as <div aria-hidden=true />. | ||
*/ | ||
var isHiddenFromScreenReader = function isHiddenFromScreenReader(attributes) { | ||
var ariaHidden = (0, _getAttributeValue2.default)((0, _hasAttribute2.default)(attributes, 'aria-hidden')); | ||
return ariaHidden === true || ariaHidden === null; | ||
var isHiddenFromScreenReader = function isHiddenFromScreenReader(type, attributes) { | ||
if (type.toUpperCase() === 'INPUT') { | ||
var hidden = (0, _getAttributeValue.getLiteralAttributeValue)((0, _getAttribute2.default)(attributes, 'type')); | ||
if (hidden && hidden.toUpperCase() == 'HIDDEN') { | ||
return true; | ||
} | ||
} | ||
var ariaHidden = (0, _getAttributeValue2.default)((0, _getAttribute2.default)(attributes, 'aria-hidden')); | ||
return ariaHidden === true; | ||
}; | ||
exports.default = isHiddenFromScreenReader; |
@@ -7,20 +7,25 @@ 'use strict'; | ||
var _hasAttribute = require('./hasAttribute'); | ||
var _getAttribute = require('./getAttribute'); | ||
var _hasAttribute2 = _interopRequireDefault(_hasAttribute); | ||
var _getAttribute2 = _interopRequireDefault(_getAttribute); | ||
var _getAttributeValue = require('./getAttributeValue'); | ||
var _getAttributeValue2 = _interopRequireDefault(_getAttributeValue); | ||
var _DOM = require('./attributes/DOM'); | ||
var _DOM2 = _interopRequireDefault(_DOM); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var DOMElements = ["a", "abbr", "address", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "main", "map", "mark", "menu", "menuitem", "meta", "meter", "nav", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "u", "ul", "var", "video", "wbr"]; | ||
// Map of tagNames to functions that return whether that element is interactive or not. | ||
var interactiveMap = { | ||
a: function a(attributes) { | ||
var hasHref = (0, _hasAttribute2.default)(attributes, 'href'); | ||
var hasTabIndex = (0, _hasAttribute2.default)(attributes, 'tabIndex'); | ||
return Boolean(hasHref) || !hasHref && Boolean(hasTabIndex); | ||
var href = (0, _getAttribute2.default)(attributes, 'href'); | ||
var tabIndex = (0, _getAttribute2.default)(attributes, 'tabIndex'); | ||
return Boolean(href) || !href && Boolean(tabIndex); | ||
}, | ||
// This is same as `a` interactivity function | ||
area: function area(attributes) { | ||
return interactiveMap.a(attributes); | ||
}, | ||
button: function button() { | ||
@@ -30,3 +35,3 @@ return true; | ||
input: function input(attributes) { | ||
var typeAttr = (0, _getAttributeValue2.default)((0, _hasAttribute2.default)(attributes, 'type')); | ||
var typeAttr = (0, _getAttributeValue.getLiteralAttributeValue)((0, _getAttribute2.default)(attributes, 'type')); | ||
return typeAttr ? typeAttr.toUpperCase() !== 'HIDDEN' : true; | ||
@@ -54,3 +59,3 @@ }, | ||
// low-level DOM element this maps to. | ||
if (DOMElements.indexOf(tagName) === -1) { | ||
if (Object.keys(_DOM2.default).indexOf(tagName.toUpperCase()) === -1) { | ||
return true; | ||
@@ -57,0 +62,0 @@ } |
{ | ||
"name": "eslint-plugin-jsx-a11y", | ||
"version": "0.6.2", | ||
"version": "1.0.0", | ||
"description": "A static analysis linter of jsx and their accessibility with screen readers.", | ||
@@ -23,5 +23,5 @@ "keywords": [ | ||
"coveralls": "cat ./reports/coverage/lcov.info | coveralls", | ||
"lint": "eslint --config .eslintrc.js src tests", | ||
"lint": "eslint --config .eslintrc.js .", | ||
"pretest": "npm run lint", | ||
"test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/**/*.js -- --compilers js:babel-core/register --reporter nyan" | ||
"test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/**/*.js -- --compilers js:babel-core/register --reporter dot" | ||
}, | ||
@@ -42,3 +42,6 @@ "devDependencies": { | ||
}, | ||
"license": "MIT" | ||
"license": "MIT", | ||
"dependencies": { | ||
"object-assign": "^4.0.1" | ||
} | ||
} |
@@ -17,2 +17,6 @@ <p align="center"> | ||
</a> | ||
<a href='https://npmjs.org/package/eslint-plugin-jsx-a11y'> | ||
<img src='https://img.shields.io/npm/dt/eslint-plugin-jsx-a11y.svg?maxAge=2592000' | ||
alt='Total npm downloads' /> | ||
</a> | ||
</p> | ||
@@ -33,3 +37,3 @@ | ||
``` | ||
```sh | ||
$ npm i eslint --save-dev | ||
@@ -40,3 +44,3 @@ ``` | ||
``` | ||
```sh | ||
$ npm install eslint-plugin-jsx-a11y --save-dev | ||
@@ -53,5 +57,5 @@ ``` | ||
{ | ||
"plugins": [ | ||
"jsx-a11y" | ||
] | ||
"plugins": [ | ||
"jsx-a11y" | ||
] | ||
} | ||
@@ -65,5 +69,5 @@ ``` | ||
{ | ||
"rules": { | ||
"jsx-a11y/rule-name": 2 | ||
} | ||
"rules": { | ||
"jsx-a11y/rule-name": 2 | ||
} | ||
} | ||
@@ -74,17 +78,21 @@ ``` | ||
- [img-uses-alt](docs/rules/img-uses-alt.md): Enforce that img jsx elements use the alt attribute. | ||
- [onclick-uses-role](docs/rules/onclick-uses-role.md): Enforce that non-interactive, visible elements (such as div) that have click handlers use the role attribute. | ||
- [mouse-events-map-to-key-events](docs/rules/mouse-events-map-to-key-events.md): Enforce that onMouseOver/onMouseOut are accompanied by onFocus/onBlur for strictly keyboard users. | ||
- [use-onblur-not-onchange](docs/rules/use-onblur-not-onchange.md): Enforce that onBlur is used instead of onChange. | ||
- [no-access-key](docs/rules/no-access-key.md): Enforce that the accessKey prop is not used on any element to avoid complications with keyboard commands used by a screenreader. | ||
- [label-uses-for](docs/rules/label-uses-for.md): Enforce that label elements have the htmlFor attribute | ||
- [redundant-alt](docs/rules/redundant-alt.md): Enforce img alt attribute does not contain the word image, picture, or photo. | ||
- [no-hash-href](docs/rules/no-hash-href.md): Enforce an anchor element's href prop value is not just #. | ||
- [valid-aria-role](docs/rules/valid-aria-role.md): Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role. | ||
- [aria-props](docs/rules/aria-props.md): Enforce all `aria-*` props are valid. | ||
- [aria-proptypes](docs/rules/aria-proptypes.md): Enforce ARIA state and property values are valid. | ||
- [aria-role](docs/rules/aria-role.md): Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role. | ||
- [aria-unsupported-elements](docs/rules/aria-unsupported-elements.md): Enforce that elements that do not support ARIA roles, states, and properties do not have those attributes. | ||
- [href-no-hash](docs/rules/href-no-hash.md): Enforce an anchor element's `href` prop value is not just `#`. | ||
- [img-has-alt](docs/rules/img-has-alt.md): Enforce that `<img>` JSX elements use the `alt` prop. | ||
- [img-redundant-alt](docs/rules/img-redundant-alt.md): Enforce `<img>` alt prop does not contain the word "image", "picture", or "photo". | ||
- [label-has-for](docs/rules/label-has-for.md): Enforce that `<label>` elements have the `htmlFor` prop. | ||
- [mouse-events-have-key-events](docs/rules/mouse-events-have-key-events.md): Enforce that `onMouseOver`/`onMouseOut` are accompanied by `onFocus`/`onBlur` for keyboard-only users. | ||
- [no-access-key](docs/rules/no-access-key.md): Enforce that the `accessKey` prop is not used on any element to avoid complications with keyboard commands used by a screenreader. | ||
- [no-onchange](docs/rules/no-onchange.md): Enforce that `onBlur` is used instead of `onChange`. | ||
- [onclick-has-focus](docs/rules/onclick-has-focus.md): Enforce that elements with `onClick` handlers must be focusable. | ||
- [onclick-has-role](docs/rules/onclick-has-role.md): Enforce that non-interactive, visible elements (such as `<div>`) that have click handlers use the role attribute. | ||
- [role-has-required-aria-props](docs/rules/role-has-required-aria-props.md): Enforce that elements with ARIA roles must have all required attributes for that role. | ||
- [role-supports-aria-props](docs/rules/role-supports-aria-props.md): Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. | ||
- [tabindex-no-positive](docs/rules/tabindex-no-positive.md): Enforce `tabIndex` value is not greater than zero. | ||
## Contributing | ||
Feel free to contribute! I am currently using [Google Chrome's Audit Rules](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules) to map out as rules for this plugin. | ||
## License | ||
eslint-plugin-jsx-a11y is licensed under the [MIT License](LICENSE.md). |
@@ -5,11 +5,18 @@ 'use strict'; | ||
rules: { | ||
'img-uses-alt': require('./rules/img-uses-alt'), | ||
'redundant-alt': require('./rules/redundant-alt'), | ||
'onclick-uses-role': require('./rules/onclick-uses-role'), | ||
'mouse-events-map-to-key-events': require('./rules/mouse-events-map-to-key-events'), | ||
'use-onblur-not-onchange': require('./rules/use-onblur-not-onchange'), | ||
'aria-props': require('./rules/aria-props'), | ||
'aria-proptypes': require('./rules/aria-proptypes'), | ||
'aria-role': require('./rules/aria-role'), | ||
'aria-unsupported-elements': require('./rules/aria-unsupported-elements'), | ||
'href-no-hash': require('./rules/href-no-hash'), | ||
'img-has-alt': require('./rules/img-has-alt'), | ||
'img-redundant-alt': require('./rules/img-redundant-alt'), | ||
'label-has-for': require('./rules/label-has-for'), | ||
'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'), | ||
'no-access-key': require('./rules/no-access-key'), | ||
'label-uses-for': require('./rules/label-uses-for'), | ||
'no-hash-href': require('./rules/no-hash-href'), | ||
'valid-aria-role': require('./rules/valid-aria-role') | ||
'no-onchange': require('./rules/no-onchange'), | ||
'onclick-has-focus': require('./rules/onclick-has-focus'), | ||
'onclick-has-role': require('./rules/onclick-has-role'), | ||
'role-has-required-aria-props': require('./rules/role-has-required-aria-props'), | ||
'role-supports-aria-props': require('./rules/role-supports-aria-props'), | ||
'tabindex-no-positive': require('./rules/tabindex-no-positive') | ||
}, | ||
@@ -24,11 +31,18 @@ configs: { | ||
rules: { | ||
"jsx-a11y/img-uses-alt": 2, | ||
"jsx-a11y/redundant-alt": 2, | ||
"jsx-a11y/onclick-uses-role": 2, | ||
"jsx-a11y/mouse-events-map-to-key-events": 2, | ||
"jsx-a11y/use-onblur-not-onchange": 2, | ||
"jsx-a11y/no-access-key": 2, | ||
"jsx-a11y/label-uses-for": 2, | ||
"jsx-a11y/no-hash-href": 2, | ||
"jsx-a11y/valid-aria-role": 2 | ||
'jsx-a11y/aria-props': 2, | ||
'jsx-a11y/aria-proptypes': 2, | ||
'jsx-a11y/aria-role': 2, | ||
'jsx-a11y/aria-unsupported-elements': 2, | ||
'jsx-a11y/href-no-hash': 2, | ||
'jsx-a11y/img-has-alt': 2, | ||
'jsx-a11y/img-redundant-alt': 2, | ||
'jsx-a11y/label-has-for': 2, | ||
'jsx-a11y/mouse-events-have-key-events': 2, | ||
'jsx-a11y/no-access-key': 2, | ||
'jsx-a11y/no-onchange': 2, | ||
'jsx-a11y/onclick-hs-focus': 2, | ||
'jsx-a11y/onclick-has-role': 2, | ||
'jsx-a11y/role-has-required-aria-props': 2, | ||
'jsx-a11y/role-supports-aria-props': 2, | ||
'jsx-a11y/tabindex-no-positive': 2 | ||
} | ||
@@ -35,0 +49,0 @@ } |
@@ -11,3 +11,3 @@ /** | ||
import hasAttribute from '../util/hasAttribute'; | ||
import getAttribute from '../util/getAttribute'; | ||
import getAttributeValue from '../util/getAttributeValue'; | ||
@@ -21,6 +21,6 @@ | ||
JSXOpeningElement: node => { | ||
const hasAccessKey = hasAttribute(node.attributes, 'accesskey'); | ||
const accessKeyValue = getAttributeValue(hasAccessKey); | ||
const accessKey = getAttribute(node.attributes, 'accesskey'); | ||
const accessKeyValue = getAttributeValue(accessKey); | ||
if (Boolean(hasAccessKey) === true && Boolean(accessKeyValue) === true) { | ||
if (accessKey && accessKeyValue) { | ||
context.report({ | ||
@@ -27,0 +27,0 @@ node, |
'use strict'; | ||
import buildTemplateLiteral from './buildTemplateLiteral'; | ||
import getValue, { getLiteralValue } from './values'; | ||
const getValue = value => { | ||
if (value.type === 'Literal') { | ||
return value.value; | ||
} else if (value.type === 'Identifier') { | ||
return value.name === "" ? undefined : value.name; | ||
} else if (value.type === 'JSXElement') { | ||
return undefined; // For now, just so things don't break. | ||
} | ||
const { expression } = value; | ||
const type = expression ? expression.type : value.type; | ||
const obj = expression || value; | ||
switch (type) { | ||
case 'Literal': | ||
return obj.value; | ||
case 'TemplateLiteral': | ||
return buildTemplateLiteral(obj); | ||
case 'Identifier': | ||
return obj.name == 'undefined' ? undefined : obj.name; | ||
case 'ArrowFunctionExpression': | ||
case 'FunctionExpression': | ||
return () => void 0; | ||
case 'LogicalExpression': | ||
const { operator, left, right } = obj; | ||
const leftVal = getValue(left); | ||
const rightVal = getValue(right); | ||
const extractValue = (attribute, extractor) => { | ||
if (attribute && attribute.type === 'JSXAttribute') { | ||
if (attribute.value === null) { | ||
// Null valued attributes imply truthiness. | ||
// For example: <div aria-hidden /> | ||
// See: https://facebook.github.io/react/docs/jsx-in-depth.html#boolean-attributes | ||
return true; | ||
} | ||
return operator == '&&' ? leftVal && rightVal : leftVal || rightVal; | ||
case 'MemberExpression': | ||
return `${getValue(obj.object)}.${getValue(obj.property)}`; | ||
case 'CallExpression': | ||
return getValue(obj.callee); | ||
default: | ||
return undefined; | ||
return extractor(attribute.value); | ||
} | ||
return undefined; | ||
}; | ||
@@ -51,13 +29,23 @@ | ||
* value with the intention of the JSX. | ||
* | ||
* @param attribute - The JSXAttribute collected by AST parser. | ||
*/ | ||
const getAttributeValue = attribute => { | ||
if (attribute.value === null) { | ||
return null; | ||
} else if (attribute.type === 'JSXAttribute') { | ||
return getValue(attribute.value); | ||
} | ||
export default function getAttributeValue(attribute) { | ||
return extractValue(attribute, getValue); | ||
} | ||
return undefined; | ||
/** | ||
* Returns the value of a given attribute. | ||
* Different types of attributes have their associated | ||
* values in different properties on the object. | ||
* | ||
* This function should return a value only if we can extract | ||
* a literal value from its attribute (i.e. values that have generic | ||
* types in JavaScript - strings, numbers, booleans, etc.) | ||
* | ||
* @param attribute - The JSXAttribute collected by AST parser. | ||
*/ | ||
export const getLiteralAttributeValue = function getLiteralAttributeValue(attribute) { | ||
return extractValue(attribute, getLiteralValue); | ||
}; | ||
export default getAttributeValue; |
'use strict'; | ||
const getNodeType = node => { | ||
/** | ||
* Returns the tagName associated with a JSXElement. | ||
*/ | ||
export default function getNodeType(node) { | ||
const { name } = node; | ||
@@ -12,5 +15,3 @@ | ||
return node.name.name; | ||
}; | ||
} | ||
export default getNodeType; | ||
'use strict'; | ||
import hasAttribute from './hasAttribute'; | ||
import getAttributeValue from './getAttributeValue'; | ||
import getAttribute from './getAttribute'; | ||
import getAttributeValue, { getLiteralAttributeValue } from './getAttributeValue'; | ||
/** | ||
* Returns boolean indicating that the aria-hidden prop | ||
* is present or the value is true. | ||
* is present or the value is true. Will also return true if | ||
* there is an input with type='hidden'. | ||
* | ||
* <div aria-hidden /> is equivalent to the DOM as <div aria-hidden=true />. | ||
*/ | ||
const isHiddenFromScreenReader = attributes => { | ||
const ariaHidden = getAttributeValue(hasAttribute(attributes, 'aria-hidden')); | ||
return ariaHidden === true || ariaHidden === null; | ||
const isHiddenFromScreenReader = (type, attributes) => { | ||
if (type.toUpperCase() === 'INPUT') { | ||
const hidden = getLiteralAttributeValue(getAttribute(attributes, 'type')); | ||
if (hidden && hidden.toUpperCase() == 'HIDDEN') { | ||
return true; | ||
} | ||
} | ||
const ariaHidden = getAttributeValue(getAttribute(attributes, 'aria-hidden')); | ||
return ariaHidden === true; | ||
}; | ||
export default isHiddenFromScreenReader; |
'use strict'; | ||
import hasAttribute from './hasAttribute'; | ||
import getAttributeValue from './getAttributeValue'; | ||
import getAttribute from './getAttribute'; | ||
import { getLiteralAttributeValue } from './getAttributeValue'; | ||
import DOMElements from './attributes/DOM'; | ||
const DOMElements = [ | ||
"a", "abbr", "address", "area", "article", | ||
"aside", "audio", "b", "base", "bdi", "bdo", "big", | ||
"blockquote", "body", "br", "button", "canvas", "caption", | ||
"cite", "code", "col", "colgroup", "data", "datalist", | ||
"dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", | ||
"em", "embed", "fieldset", "figcaption", "figure", "footer", | ||
"form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", | ||
"hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", | ||
"kbd", "keygen", "label", "legend", "li", "link", "main", "map", | ||
"mark", "menu", "menuitem", "meta", "meter", "nav", "noscript", | ||
"object", "ol", "optgroup", "option", "output", "p", "param", | ||
"picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", | ||
"samp", "script", "section", "select", "small", "source", "span", | ||
"strong", "style", "sub", "summary", "sup", "table", "tbody", | ||
"td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", | ||
"track", "u", "ul", "var", "video", "wbr" | ||
]; | ||
// Map of tagNames to functions that return whether that element is interactive or not. | ||
const interactiveMap = { | ||
a: attributes => { | ||
const hasHref = hasAttribute(attributes, 'href'); | ||
const hasTabIndex = hasAttribute(attributes, 'tabIndex'); | ||
return (Boolean(hasHref) || !hasHref && Boolean(hasTabIndex)); | ||
const href = getAttribute(attributes, 'href'); | ||
const tabIndex = getAttribute(attributes, 'tabIndex'); | ||
return (Boolean(href) || (!href && Boolean(tabIndex))); | ||
}, | ||
// This is same as `a` interactivity function | ||
area: attributes => interactiveMap.a(attributes), | ||
button: () => true, | ||
input: attributes => { | ||
const typeAttr = getAttributeValue(hasAttribute(attributes, 'type')); | ||
const typeAttr = getLiteralAttributeValue(getAttribute(attributes, 'type')); | ||
return typeAttr ? typeAttr.toUpperCase() !== 'HIDDEN' : true; | ||
@@ -50,3 +37,3 @@ }, | ||
// low-level DOM element this maps to. | ||
if (DOMElements.indexOf(tagName) === -1) { | ||
if (Object.keys(DOMElements).indexOf(tagName.toUpperCase()) === -1) { | ||
return true; | ||
@@ -53,0 +40,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
277410
189
7215
1
93
1
1
+ Addedobject-assign@^4.0.1
+ Addedobject-assign@4.1.1(transitive)