scope
Ast Rule: html element
scope
const dom = [
['a', {
reserved: false,
}],
['abbr', {
reserved: false,
}],
['acronym', {
reserved: false,
}],
['address', {
reserved: false,
}],
['applet', {
reserved: false,
}],
['area', {
reserved: false,
}],
['article', {
reserved: false,
}],
['aside', {
reserved: false,
}],
['audio', {
reserved: false,
}],
['b', {
reserved: false,
}],
['base', {
reserved: true
}],
['bdi', {
reserved: false,
}],
['bdo', {
reserved: false,
}],
['big', {
reserved: false,
}],
['blink', {
reserved: false,
}],
['blockquote', {
reserved: false,
}],
['body', {
reserved: false,
}],
['br', {
reserved: false,
}],
['button', {
reserved: false,
}],
['canvas', {
reserved: false,
}],
['caption', {
reserved: false,
}],
['center', {
reserved: false,
}],
['cite', {
reserved: false,
}],
['code', {
reserved: false,
}],
['col', {
reserved: true
}],
['colgroup', {
reserved: true
}],
['content', {
reserved: false,
}],
['data', {
reserved: false,
}],
['datalist', {
reserved: false,
}],
['dd', {
reserved: false,
}],
['del', {
reserved: false,
}],
['details', {
reserved: false,
}],
['dfn', {
reserved: false,
}],
['dialog', {
reserved: false,
}],
['dir', {
reserved: false,
}],
['div', {
reserved: false,
}],
['dl', {
reserved: false,
}],
['dt', {
reserved: false,
}],
['em', {
reserved: false,
}],
['embed', {
reserved: false,
}],
['fieldset', {
reserved: false,
}],
['figcaption', {
reserved: false,
}],
['figure', {
reserved: false,
}],
['font', {
reserved: false,
}],
['footer', {
reserved: false,
}],
['form', {
reserved: false,
}],
['frame', {
reserved: false,
}],
['frameset', {
reserved: false,
}],
['h1', {
reserved: false,
}],
['h2', {
reserved: false,
}],
['h3', {
reserved: false,
}],
['h4', {
reserved: false,
}],
['h5', {
reserved: false,
}],
['h6', {
reserved: false,
}],
['head', {
reserved: true
}],
['header', {
reserved: false,
}],
['hgroup', {
reserved: false,
}],
['hr', {
reserved: false,
}],
['html', {
reserved: true
}],
['i', {
reserved: false,
}],
['iframe', {
reserved: false,
}],
['img', {
reserved: false,
}],
['input', {
reserved: false,
}],
['ins', {
reserved: false,
}],
['kbd', {
reserved: false,
}],
['keygen', {
reserved: false,
}],
['label', {
reserved: false,
}],
['legend', {
reserved: false,
}],
['li', {
reserved: false,
}],
['link', {
reserved: true
}],
['main', {
reserved: false,
}],
['map', {
reserved: false,
}],
['mark', {
reserved: false,
}],
['marquee', {
reserved: false,
}],
['menu', {
reserved: false,
}],
['menuitem', {
reserved: false,
}],
['meta', {
reserved: true
}],
['meter', {
reserved: false,
}],
['nav', {
reserved: false,
}],
['noembed', {
reserved: true
}],
['noscript', {
reserved: true
}],
['object', {
reserved: false,
}],
['ol', {
reserved: false,
}],
['optgroup', {
reserved: false,
}],
['option', {
reserved: false,
}],
['output', {
reserved: false,
}],
['p', {
reserved: false,
}],
['param', {
reserved: true
}],
['picture', {
reserved: true
}],
['pre', {
reserved: false,
}],
['progress', {
reserved: false,
}],
['q', {
reserved: false,
}],
['rp', {
reserved: false,
}],
['rt', {
reserved: false,
}],
['rtc', {
reserved: false,
}],
['ruby', {
reserved: false,
}],
['s', {
reserved: false,
}],
['samp', {
reserved: false,
}],
['script', {
reserved: true
}],
['section', {
reserved: false,
}],
['select', {
reserved: false,
}],
['small', {
reserved: false,
}],
['source', {
reserved: true
}],
['spacer', {
reserved: false,
}],
['span', {
reserved: false,
}],
['strike', {
reserved: false,
}],
['strong', {
reserved: false,
}],
['style', {
reserved: true
}],
['sub', {
reserved: false,
}],
['summary', {
reserved: false,
}],
['sup', {
reserved: false,
}],
['table', {
reserved: false,
}],
['tbody', {
reserved: false,
}],
['td', {
reserved: false,
}],
['textarea', {
reserved: false,
}],
['tfoot', {
reserved: false,
}],
['th', {
reserved: false,
}],
['thead', {
reserved: false,
}],
['time', {
reserved: false,
}],
['title', {
reserved: true
}],
['tr', {
reserved: false,
}],
['track', {
reserved: true
}],
['tt', {
reserved: false,
}],
['u', {
reserved: false,
}],
['ul', {
reserved: false,
}],
['var', {
reserved: false,
}],
['video', {
reserved: false,
}],
['wbr', {
reserved: false,
}],
['xmp', {
reserved: false,
}],
];
const TYPES = {
string: getPropStringValue,
sequence: getPropSequenceValue,
array: getPropArrayValue,
object: getPropObjectValue,
object_element: getPropObjectValue,
functiondefinition: () => {},
};
function getTag(node) {
if (node && node.tag) {
return node.tag.value;
}
}
function getProp(attributes = [], prop = "") {
if (!prop) return;
return attributes.find((attribute) => {
if (attribute && attribute.name && attribute.name.value) {
return attribute.name.value === prop;
}
});
}
function getPropStringValue(value) {
if (value.value === "true") {
return true;
}
if (value.value === "false") {
return false;
}
return value.value.replace(/^\"/g, "").replace(/\"$/g, "");
}
function getPropArrayValue(value) {
const array = [];
const arrayElements = value.elements;
if (arrayElements.length) {
for (const element of arrayElements) {
array.push(TYPES[element.astType](element));
}
}
return array;
}
function getPropObjectValue(value) {
const object = {};
const objectElements = value.elements;
if (objectElements.length) {
for (const element of objectElements) {
object[element.name.value] = TYPES[element.value.astType](element.value);
}
}
return object;
}
function getPropSequenceValue(value) {
if (value.elements.length) {
const element = value.elements[0];
if (element) {
return TYPES[element.astType](element);
}
}
}
function extractValue(attribute, extractor) {
if (attribute) {
// Null valued attributes imply truthiness.
// For example: <div aria-hidden />
if (attribute.value === null) return true;
return extractor(attribute.value);
}
}
function getValue(value) {
return TYPES[value.astType](value);
}
function getPropValue(attribute) {
return extractValue(attribute, getValue);
}
function propHasValue(attribute) {
const value = getPropValue(attribute);
if (typeof value !== "boolean") {
return !!value;
}
return false;
}
function isDom(tag) {
return Boolean(dom.find(([def]) => def === tag));
}
function visit(node) {
const tag = getTag(node);
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (!isDom(tag)) return;
if (tag === "th") return;
if (getProp(node.attributes, "scope")) {
const error = buildError(
node.start.line,
node.start.col,
node.end.line,
node.end.col,
'Enforce `scope` prop is only used on `<th>` elements.',
"ERROR",
"BEST_PRACTICES"
);
addError(error);
}
}
good.jsx
Expected test result: no error
bad.jsx
Expected test result: has error