| /** |
| * @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' |
| }); |
| } |
| } |
| } |
| }); |
| } |
| } |
| }; |
| } |
| }; |