eslint-plugin-lit
Version:
lit-html support for ESLint
85 lines (84 loc) • 3.69 kB
JavaScript
/**
* @fileoverview Disallows invalid binding positions in templates
* @author James Garbutt <https://github.com/43081j>
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
export const rule = {
meta: {
docs: {
description: 'Disallows invalid binding positions in templates',
recommended: false,
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/binding-positions.md'
},
schema: [],
messages: {
noBindingTagName: 'Bindings cannot be used in place of tag names.',
noBindingAttributeName: 'Bindings cannot be used in place of attribute names.',
noBindingSelfClosingTag: 'Bindings at the end of a self-closing tag must be' +
' followed by a space or quoted',
noBindingHTMLComment: 'Bindings cannot be used inside HTML comments.'
}
},
create(context) {
// variables should be defined here
const tagPattern = /<\/?$/;
const attrPattern = /^=/;
const selfClosingPattern = /^\/>/;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
/**
* Determines if a given TemplateElement is contained within
* a HTML comment.
*
* @param {ESTree.TemplateElement=} expr Expression to test
* @return {boolean}
*/
function isInsideComment(expr) {
return (expr !== undefined &&
expr.value.raw.lastIndexOf('<!--') > expr.value.raw.lastIndexOf('-->'));
}
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
TaggedTemplateExpression: (node) => {
if (node.type === 'TaggedTemplateExpression' &&
node.tag.type === 'Identifier' &&
node.tag.name === 'html') {
for (let i = 0; i < node.quasi.expressions.length; i++) {
const expr = node.quasi.expressions[i];
const prev = node.quasi.quasis[i];
const next = node.quasi.quasis[i + 1];
if (tagPattern.test(prev.value.raw)) {
context.report({
node: expr,
messageId: 'noBindingTagName'
});
}
else if (next && attrPattern.test(next.value.raw)) {
context.report({
node: expr,
messageId: 'noBindingAttributeName'
});
}
else if (next && selfClosingPattern.test(next.value.raw)) {
context.report({
node: expr,
messageId: 'noBindingSelfClosingTag'
});
}
else if (isInsideComment(prev)) {
context.report({
node: expr,
messageId: 'noBindingHTMLComment'
});
}
}
}
}
};
}
};