eslint-plugin-lit
Version:
lit-html support for ESLint
116 lines (115 loc) • 3.88 kB
JavaScript
/**
* @fileoverview Enforces calling `super` in lifecycle methods
* @author James Garbutt <https://github.com/43081j>
*/
import { isLitClass } from '../util.js';
const methodNames = ['connectedCallback', 'disconnectedCallback', 'update'];
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
export const rule = {
meta: {
docs: {
description: 'Enforces calling `super` in lifecycle methods',
recommended: false,
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/lifecycle-super.md'
},
schema: [],
messages: {
callSuper: 'You must call `super.{{method}}` to avoid interrupting ' +
'the lit rendering lifecycle'
}
},
create(context) {
// variables should be defined here
let inElement = false;
let currentMethod = null;
let superSeen = false;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
/**
* Class entered
*
* @param {ESTree.Class} node Node entered
* @return {void}
*/
function classEnter(node) {
if (!isLitClass(node)) {
return;
}
inElement = true;
}
/**
* Class exited
*
* @return {void}
*/
function classExit() {
inElement = false;
}
/**
* Method entered
*
* @param {ESTree.MethodDefinition} node Node entered
* @return {void}
*/
function methodEnter(node) {
if (!inElement ||
node.static === true ||
node.kind !== 'method' ||
node.key.type !== 'Identifier' ||
!methodNames.includes(node.key.name)) {
return;
}
currentMethod = node.key.name;
}
/**
* Method exited
*
* @param {ESTree.MethodDefinition} node Node entered
* @return {void}
*/
function methodExit(node) {
if (currentMethod !== null && !superSeen) {
context.report({
node,
messageId: 'callSuper',
data: {
method: currentMethod
}
});
}
currentMethod = null;
superSeen = false;
}
/**
* Call expression entered
* @param {ESTree.CallExpression} node Node entered
* @return {void}
*/
function callExpressionEnter(node) {
if (currentMethod === null) {
return;
}
if (node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Super' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === currentMethod) {
superSeen = true;
}
}
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
ClassExpression: (node) => classEnter(node),
ClassDeclaration: (node) => classEnter(node),
'ClassExpression:exit': classExit,
'ClassDeclaration:exit': classExit,
MethodDefinition: (node) => methodEnter(node),
'MethodDefinition:exit': methodExit,
CallExpression: (node) => callExpressionEnter(node)
};
}
};