eslint-plugin-lit
Version:
lit-html support for ESLint
131 lines (130 loc) • 4.23 kB
JavaScript
/**
* @fileoverview Disallows property changes in the `update` lifecycle method
* @author James Garbutt <https://github.com/43081j>
*/
import { getPropertyMap, isLitClass } from '../util.js';
const superUpdateQuery = 'CallExpression' +
'[callee.object.type = "Super"]' +
'[callee.property.name = "update"]';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
export const rule = {
meta: {
docs: {
description: 'Disallows property changes in the `update` lifecycle method',
recommended: false,
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-property-change-update.md'
},
schema: [],
messages: {
propertyChange: 'Properties should not be changed in the update lifecycle method as' +
' they will not trigger re-renders'
}
},
create(context) {
// variables should be defined here
let propertyMap = null;
let inUpdate = false;
let superSeen = false;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
/**
* Class entered
*
* @param {ESTree.Class} node Node entered
* @return {void}
*/
function classEnter(node) {
if (!isLitClass(node)) {
return;
}
const props = getPropertyMap(node);
if (props) {
propertyMap = props;
}
}
/**
* Class exited
*
* @return {void}
*/
function classExit() {
propertyMap = null;
}
/**
* Method entered
*
* @param {ESTree.MethodDefinition} node Node entered
* @return {void}
*/
function methodEnter(node) {
if (!propertyMap ||
node.static === true ||
node.kind !== 'method' ||
node.key.type !== 'Identifier' ||
node.key.name !== 'update') {
return;
}
inUpdate = true;
}
/**
* Method exited
*
* @return {void}
*/
function methodExit() {
inUpdate = false;
superSeen = false;
}
/**
* Assignment expression entered
*
* @param {ESTree.AssignmentExpression} node Node entered
* @return {void}
*/
function assignmentFound(node) {
if (!superSeen ||
!propertyMap ||
!inUpdate ||
node.left.type !== 'MemberExpression' ||
node.left.object.type !== 'ThisExpression' ||
node.left.property.type !== 'Identifier') {
return;
}
const hasProp = propertyMap.has(node.left.property.name);
if (!hasProp) {
return;
}
context.report({
node: node,
messageId: 'propertyChange'
});
}
/**
* `super.update()` call found
*
* @return {void}
*/
function superUpdateFound() {
if (!inUpdate) {
return;
}
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,
AssignmentExpression: (node) => assignmentFound(node),
[superUpdateQuery]: () => superUpdateFound()
};
}
};