Skip to content

Commit

Permalink
feat (lifecycle-super): add new lifecycle-super rule (#173)
Browse files Browse the repository at this point in the history
  • Loading branch information
43081j authored May 2, 2023
1 parent fa60fa2 commit 7e40fca
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ If you want more fine-grained configuration, you can instead add a snippet like
- [lit/attribute-value-entities](docs/rules/attribute-value-entities.md)
- [lit/ban-attributes](docs/rules/ban-attributes.md)
- [lit/binding-positions](docs/rules/binding-positions.md)
- [lit/lifecycle-super](docs/rules/lifecycle-super.md)
- [lit/no-duplicate-template-bindings](docs/rules/no-duplicate-template-bindings.md)
- [lit/no-invalid-escape-sequences](docs/rules/no-invalid-escape-sequences.md)
- [lit/no-invalid-html](docs/rules/no-invalid-html.md)
Expand Down
40 changes: 40 additions & 0 deletions docs/rules/lifecycle-super.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Enforces calling `super` in lifecycle methods (lifecycle-super)

Enforces that `super` is called in lifecycle methods which require it.

For example, the `connectedCallback` should call `super.connectedCallback()` to
avoid interrupting lit's rendering.

## Rule Details

This rule enforces calling of `super` in the following lifecycle methods:

- `update`
- `connectedCallback`
- `disconnectedCallback`

The following patterns are considered warnings:

```ts
class Foo extends LitElement {
connectedCallback() {
doSomething();
}
}
```

The following patterns are not warnings:

```ts
class Foo extends LitElement {
connectedCallback() {
super.connectedCallback();
doSomething();
}
}
```

## When Not To Use It

If you want to override lit's default implementation of a lifecycle method,
you should disable this rule.
3 changes: 2 additions & 1 deletion src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ const config = {
rules: {
'lit/attribute-value-entities': 'error',
'lit/binding-positions': 'error',
'lit/lifecycle-super': 'error',
'lit/no-duplicate-template-bindings': 'error',
'lit/no-invalid-escape-sequences': 'error',
'lit/no-invalid-html': 'error',
'lit/no-legacy-imports': 'error',
'lit/no-legacy-template-syntax': 'error',
'lit/no-native-attributes': 'error',
'lit/no-private-properties': 'error',
'lit/no-property-change-update': 'error',
'lit/no-template-arrow': 'error',
'lit/no-template-bind': 'error',
'lit/no-native-attributes': 'error',
'lit/no-template-map': 'error',
'lit/no-this-assign-in-render': 'error',
'lit/no-useless-template-literals': 'error',
Expand Down
148 changes: 148 additions & 0 deletions src/rules/lifecycle-super.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* @fileoverview Enforces calling `super` in lifecycle methods
* @author James Garbutt <https://github.com/43081j>
*/

import {Rule} from 'eslint';
import * as ESTree from 'estree';

const methodNames = ['connectedCallback', 'disconnectedCallback', 'update'];

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

const rule: Rule.RuleModule = {
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): Rule.RuleListener {
// variables should be defined here
let inElement = false;
let currentMethod: string | null = null;
let superSeen = false;

//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------

/**
* Class entered
*
* @param {ESTree.Class} node Node entered
* @return {void}
*/
function classEnter(node: ESTree.Class): void {
if (
!node.superClass ||
node.superClass.type !== 'Identifier' ||
node.superClass.name !== 'LitElement'
) {
return;
}

inElement = true;
}

/**
* Class exited
*
* @return {void}
*/
function classExit(): void {
inElement = false;
}

/**
* Method entered
*
* @param {ESTree.MethodDefinition} node Node entered
* @return {void}
*/
function methodEnter(node: ESTree.MethodDefinition): void {
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: ESTree.MethodDefinition): void {
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: ESTree.CallExpression): void {
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: ESTree.Node): void =>
classEnter(node as ESTree.Class),
ClassDeclaration: (node: ESTree.Node): void =>
classEnter(node as ESTree.Class),
'ClassExpression:exit': classExit,
'ClassDeclaration:exit': classExit,
MethodDefinition: (node: ESTree.Node): void =>
methodEnter(node as ESTree.MethodDefinition),
'MethodDefinition:exit': methodExit,
CallExpression: (node: ESTree.CallExpression): void =>
callExpressionEnter(node)
};
}
};

export = rule;
Loading

0 comments on commit 7e40fca

Please sign in to comment.