eslint-plugin-lit
Version:
lit-html support for ESLint
86 lines (85 loc) • 3.92 kB
JavaScript
/**
* @fileoverview Disallows redundant literal values in templates
* @author James Garbutt <https://github.com/43081j>
*/
import { TemplateAnalyzer } from '../template-analyzer.js';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
export const rule = {
meta: {
docs: {
description: 'Disallows redundant literal values in templates',
recommended: false,
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-useless-template-literals.md'
},
schema: [],
messages: {
doNotSubstituteTextBinding: 'Literals must not be substituted into text bindings',
doNotSubstituteAttributes: 'Literals must not be substituted into ' +
'attributes, set it directly instead (e.g. ' +
'attr="value")'
}
},
create(context) {
const source = context.getSourceCode();
const isAttr = /^[^\.\?]/;
const endsWithAttr = /=['"]?$/;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
const getExprAfterPosition = (pos, node) => {
for (const expr of node.quasi.expressions) {
if (expr.loc &&
expr.loc.start.line === pos.start.line &&
expr.loc.start.column > pos.start.column &&
((expr.loc.end.line === pos.end.line &&
expr.loc.end.column <= pos.end.column) ||
expr.loc.end.line < pos.end.line)) {
return expr;
}
}
return null;
};
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
TaggedTemplateExpression: (node) => {
if (node.type === 'TaggedTemplateExpression' &&
node.tag.type === 'Identifier' &&
node.tag.name === 'html') {
const analyzer = TemplateAnalyzer.create(node);
for (let i = 0; i < node.quasi.expressions.length; i++) {
const expr = node.quasi.expressions[i];
if (expr.type === 'Literal' &&
!endsWithAttr.test(node.quasi.quasis[i].value.raw)) {
context.report({
node: expr,
messageId: 'doNotSubstituteTextBinding'
});
}
}
analyzer.traverse({
enterElement: (element) => {
// eslint-disable-next-line guard-for-in
for (const attr in element.attribs) {
const loc = analyzer.getLocationForAttribute(element, attr, source);
if (!loc) {
continue;
}
const expr = getExprAfterPosition(loc, node);
if (isAttr.test(attr) && (expr === null || expr === void 0 ? void 0 : expr.type) === 'Literal') {
context.report({
node: expr,
messageId: 'doNotSubstituteAttributes'
});
}
}
}
});
}
}
};
}
};