eslint-plugin-lit
Version:
lit-html support for ESLint
79 lines (78 loc) • 4.06 kB
JavaScript
/**
* @fileoverview Disallows unencoded HTML entities in attribute values
* @author James Garbutt <https://github.com/43081j>
*/
import { TemplateAnalyzer } from '../template-analyzer.js';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
export const rule = {
meta: {
docs: {
description: 'Disallows unencoded HTML entities in attribute values',
recommended: false,
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/attribute-value-entities.md'
},
schema: [],
messages: {
unencoded: 'Attribute values may not contain unencoded HTML ' +
'entities, e.g. use `>` instead of `>`',
doubleQuotes: 'Attributes delimited by double quotes may not contain ' +
'unencoded double quotes (e.g. `attr="bad"quote"`)',
singleQuotes: 'Attributes delimited by single quotes may not contain ' +
"unencoded single quotes (e.g. `attr='bad'quote'`)"
}
},
create(context) {
const source = context.getSourceCode();
const disallowedPattern = /([<>]|&(?!(#\d+|[a-z]+);))/;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
TaggedTemplateExpression: (node) => {
if (node.type === 'TaggedTemplateExpression' &&
node.tag.type === 'Identifier' &&
node.tag.name === 'html') {
const analyzer = TemplateAnalyzer.create(node);
analyzer.traverse({
enterElement: (element) => {
var _a, _b, _c, _d;
// eslint-disable-next-line guard-for-in
for (const attr in element.attribs) {
const loc = analyzer.getLocationForAttribute(element, attr, source);
const rawValue = analyzer.getRawAttributeValue(element, attr);
if (!loc || !(rawValue === null || rawValue === void 0 ? void 0 : rawValue.value)) {
continue;
}
if (disallowedPattern.test(rawValue.value)) {
context.report({
loc: loc,
messageId: 'unencoded'
});
}
else if (((_a = rawValue.quotedValue) === null || _a === void 0 ? void 0 : _a.startsWith('"')) &&
((_b = rawValue.value) === null || _b === void 0 ? void 0 : _b.includes('"'))) {
context.report({
loc: loc,
messageId: 'doubleQuotes'
});
}
else if (((_c = rawValue.quotedValue) === null || _c === void 0 ? void 0 : _c.startsWith("'")) &&
((_d = rawValue.value) === null || _d === void 0 ? void 0 : _d.includes("'"))) {
context.report({
loc: loc,
messageId: 'singleQuotes'
});
}
}
}
});
}
}
};
}
};