eslint-plugin-lit 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/custom_types/espree.d.ts +0 -0
- package/custom_types/requireindex.d.ts +0 -0
- package/docs/rules/attribute-value-entities.md +0 -0
- package/docs/rules/ban-attributes.md +0 -0
- package/docs/rules/binding-positions.md +0 -0
- package/docs/rules/lifecycle-super.md +0 -0
- package/docs/rules/no-classfield-shadowing.md +34 -0
- package/docs/rules/no-duplicate-template-bindings.md +0 -0
- package/docs/rules/no-invalid-escape-sequences.md +0 -0
- package/docs/rules/no-invalid-html.md +0 -0
- package/docs/rules/no-legacy-imports.md +0 -0
- package/docs/rules/no-legacy-template-syntax.md +0 -0
- package/docs/rules/no-native-attributes.md +0 -0
- package/docs/rules/no-private-properties.md +0 -0
- package/docs/rules/no-property-change-update.md +0 -0
- package/docs/rules/no-template-arrow.md +0 -0
- package/docs/rules/no-template-bind.md +0 -0
- package/docs/rules/no-template-map.md +0 -0
- package/docs/rules/no-this-assign-in-render.md +0 -0
- package/docs/rules/no-useless-template-literals.md +0 -0
- package/docs/rules/no-value-attribute.md +0 -0
- package/docs/rules/prefer-nothing.md +0 -0
- package/docs/rules/prefer-static-styles.md +0 -0
- package/docs/rules/quoted-expressions.md +0 -0
- package/docs/rules/value-after-constraints.md +0 -0
- package/lib/configs/all.d.ts +27 -0
- package/lib/configs/all.js +29 -0
- package/lib/configs/recommended.d.ts +12 -0
- package/lib/configs/recommended.js +14 -0
- package/lib/index.d.ts +0 -0
- package/lib/index.js +0 -0
- package/lib/rules/attribute-value-entities.d.ts +7 -0
- package/lib/rules/attribute-value-entities.js +80 -0
- package/lib/rules/ban-attributes.d.ts +7 -0
- package/lib/rules/ban-attributes.js +64 -0
- package/lib/rules/binding-positions.d.ts +7 -0
- package/lib/rules/binding-positions.js +86 -0
- package/lib/rules/lifecycle-super.d.ts +7 -0
- package/lib/rules/lifecycle-super.js +118 -0
- package/lib/rules/no-classfield-shadowing.d.ts +7 -0
- package/lib/rules/no-classfield-shadowing.js +43 -0
- package/lib/rules/no-duplicate-template-bindings.d.ts +7 -0
- package/lib/rules/no-duplicate-template-bindings.js +51 -0
- package/lib/rules/no-invalid-escape-sequences.d.ts +7 -0
- package/lib/rules/no-invalid-escape-sequences.js +54 -0
- package/lib/rules/no-invalid-html.d.ts +7 -0
- package/lib/rules/no-invalid-html.js +55 -0
- package/lib/rules/no-legacy-imports.d.ts +7 -0
- package/lib/rules/no-legacy-imports.js +91 -0
- package/lib/rules/no-legacy-template-syntax.d.ts +7 -0
- package/lib/rules/no-legacy-template-syntax.js +76 -0
- package/lib/rules/no-native-attributes.d.ts +7 -0
- package/lib/rules/no-native-attributes.js +79 -0
- package/lib/rules/no-private-properties.d.ts +7 -0
- package/lib/rules/no-private-properties.js +81 -0
- package/lib/rules/no-property-change-update.d.ts +7 -0
- package/lib/rules/no-property-change-update.js +134 -0
- package/lib/rules/no-template-arrow.d.ts +7 -0
- package/lib/rules/no-template-arrow.js +68 -0
- package/lib/rules/no-template-bind.d.ts +7 -0
- package/lib/rules/no-template-bind.js +70 -0
- package/lib/rules/no-template-map.d.ts +7 -0
- package/lib/rules/no-template-map.js +51 -0
- package/lib/rules/no-this-assign-in-render.d.ts +7 -0
- package/lib/rules/no-this-assign-in-render.js +98 -0
- package/lib/rules/no-useless-template-literals.d.ts +7 -0
- package/lib/rules/no-useless-template-literals.js +87 -0
- package/lib/rules/no-value-attribute.d.ts +7 -0
- package/lib/rules/no-value-attribute.js +64 -0
- package/lib/rules/prefer-nothing.d.ts +7 -0
- package/lib/rules/prefer-nothing.js +51 -0
- package/lib/rules/prefer-static-styles.d.ts +7 -0
- package/lib/rules/prefer-static-styles.js +91 -0
- package/lib/rules/quoted-expressions.d.ts +7 -0
- package/lib/rules/quoted-expressions.js +98 -0
- package/lib/rules/value-after-constraints.d.ts +7 -0
- package/lib/rules/value-after-constraints.js +82 -0
- package/lib/template-analyzer.d.ts +0 -0
- package/lib/template-analyzer.js +0 -0
- package/lib/util.d.ts +12 -0
- package/lib/util.js +55 -1
- package/package.json +10 -9
package/README.md
CHANGED
|
File without changes
|
package/custom_types/espree.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Disallows class fields with same name as static properties
|
|
2
|
+
|
|
3
|
+
Class fields set with same names as static properties will not trigger updates as expected. They will overwrite
|
|
4
|
+
accessors used for detecting changes. See https://lit.dev/msg/class-field-shadowing for more information.
|
|
5
|
+
|
|
6
|
+
## Rule Details
|
|
7
|
+
|
|
8
|
+
This rule disallows class fields with same name as static properties.
|
|
9
|
+
|
|
10
|
+
The following patterns are considered warnings:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
class MyEl extends LitElement {
|
|
14
|
+
foo;
|
|
15
|
+
|
|
16
|
+
static properties = {
|
|
17
|
+
foo: {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The following patterns are not warnings:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
class MyEl extends LitElement {
|
|
26
|
+
static properties = {
|
|
27
|
+
foo: {}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## When Not To Use It
|
|
33
|
+
|
|
34
|
+
If you don't care about class fields with same name as static properties.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
declare const config: {
|
|
2
|
+
plugins: string[];
|
|
3
|
+
rules: {
|
|
4
|
+
'lit/attribute-value-entities': string;
|
|
5
|
+
'lit/binding-positions': string;
|
|
6
|
+
'lit/lifecycle-super': string;
|
|
7
|
+
'lit/no-duplicate-template-bindings': string;
|
|
8
|
+
'lit/no-invalid-escape-sequences': string;
|
|
9
|
+
'lit/no-invalid-html': string;
|
|
10
|
+
'lit/no-legacy-imports': string;
|
|
11
|
+
'lit/no-legacy-template-syntax': string;
|
|
12
|
+
'lit/no-native-attributes': string;
|
|
13
|
+
'lit/no-private-properties': string;
|
|
14
|
+
'lit/no-property-change-update': string;
|
|
15
|
+
'lit/no-template-arrow': string;
|
|
16
|
+
'lit/no-template-bind': string;
|
|
17
|
+
'lit/no-template-map': string;
|
|
18
|
+
'lit/no-this-assign-in-render': string;
|
|
19
|
+
'lit/no-useless-template-literals': string;
|
|
20
|
+
'lit/no-value-attribute': string;
|
|
21
|
+
'lit/prefer-nothing': string;
|
|
22
|
+
'lit/prefer-static-styles': string;
|
|
23
|
+
'lit/quoted-expressions': string;
|
|
24
|
+
'lit/value-after-constraints': string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export default config;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config = {
|
|
4
|
+
plugins: ['lit'],
|
|
5
|
+
rules: {
|
|
6
|
+
'lit/attribute-value-entities': 'error',
|
|
7
|
+
'lit/binding-positions': 'error',
|
|
8
|
+
'lit/lifecycle-super': 'error',
|
|
9
|
+
'lit/no-duplicate-template-bindings': 'error',
|
|
10
|
+
'lit/no-invalid-escape-sequences': 'error',
|
|
11
|
+
'lit/no-invalid-html': 'error',
|
|
12
|
+
'lit/no-legacy-imports': 'error',
|
|
13
|
+
'lit/no-legacy-template-syntax': 'error',
|
|
14
|
+
'lit/no-native-attributes': 'error',
|
|
15
|
+
'lit/no-private-properties': 'error',
|
|
16
|
+
'lit/no-property-change-update': 'error',
|
|
17
|
+
'lit/no-template-arrow': 'error',
|
|
18
|
+
'lit/no-template-bind': 'error',
|
|
19
|
+
'lit/no-template-map': 'error',
|
|
20
|
+
'lit/no-this-assign-in-render': 'error',
|
|
21
|
+
'lit/no-useless-template-literals': 'error',
|
|
22
|
+
'lit/no-value-attribute': 'error',
|
|
23
|
+
'lit/prefer-nothing': 'error',
|
|
24
|
+
'lit/prefer-static-styles': 'error',
|
|
25
|
+
'lit/quoted-expressions': 'error',
|
|
26
|
+
'lit/value-after-constraints': 'error'
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.default = config;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const config: {
|
|
2
|
+
plugins: string[];
|
|
3
|
+
rules: {
|
|
4
|
+
'lit/attribute-value-entities': string;
|
|
5
|
+
'lit/binding-positions': string;
|
|
6
|
+
'lit/no-duplicate-template-bindings': string;
|
|
7
|
+
'lit/no-invalid-html': string;
|
|
8
|
+
'lit/no-legacy-template-syntax': string;
|
|
9
|
+
'lit/no-property-change-update': string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export default config;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config = {
|
|
4
|
+
plugins: ['lit'],
|
|
5
|
+
rules: {
|
|
6
|
+
'lit/attribute-value-entities': 'error',
|
|
7
|
+
'lit/binding-positions': 'error',
|
|
8
|
+
'lit/no-duplicate-template-bindings': 'error',
|
|
9
|
+
'lit/no-invalid-html': 'error',
|
|
10
|
+
'lit/no-legacy-template-syntax': 'error',
|
|
11
|
+
'lit/no-property-change-update': 'error'
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
exports.default = config;
|
package/lib/index.d.ts
CHANGED
|
File without changes
|
package/lib/index.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Disallows unencoded HTML entities in attribute values
|
|
4
|
+
* @author James Garbutt <https://github.com/43081j>
|
|
5
|
+
*/
|
|
6
|
+
const template_analyzer_1 = require("../template-analyzer");
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
const rule = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Disallows unencoded HTML entities in attribute values',
|
|
14
|
+
recommended: false,
|
|
15
|
+
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/attribute-value-entities.md'
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
unencoded: 'Attribute values may not contain unencoded HTML ' +
|
|
20
|
+
'entities, e.g. use `>` instead of `>`',
|
|
21
|
+
doubleQuotes: 'Attributes delimited by double quotes may not contain ' +
|
|
22
|
+
'unencoded double quotes (e.g. `attr="bad"quote"`)',
|
|
23
|
+
singleQuotes: 'Attributes delimited by single quotes may not contain ' +
|
|
24
|
+
"unencoded single quotes (e.g. `attr='bad'quote'`)"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
create(context) {
|
|
28
|
+
const source = context.getSourceCode();
|
|
29
|
+
const disallowedPattern = /([<>]|&(?!(#\d+|[a-z]+);))/;
|
|
30
|
+
//----------------------------------------------------------------------
|
|
31
|
+
// Helpers
|
|
32
|
+
//----------------------------------------------------------------------
|
|
33
|
+
//----------------------------------------------------------------------
|
|
34
|
+
// Public
|
|
35
|
+
//----------------------------------------------------------------------
|
|
36
|
+
return {
|
|
37
|
+
TaggedTemplateExpression: (node) => {
|
|
38
|
+
if (node.type === 'TaggedTemplateExpression' &&
|
|
39
|
+
node.tag.type === 'Identifier' &&
|
|
40
|
+
node.tag.name === 'html') {
|
|
41
|
+
const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
|
|
42
|
+
analyzer.traverse({
|
|
43
|
+
enterElement: (element) => {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
// eslint-disable-next-line guard-for-in
|
|
46
|
+
for (const attr in element.attribs) {
|
|
47
|
+
const loc = analyzer.getLocationForAttribute(element, attr, source);
|
|
48
|
+
const rawValue = analyzer.getRawAttributeValue(element, attr);
|
|
49
|
+
if (!loc || !(rawValue === null || rawValue === void 0 ? void 0 : rawValue.value)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (disallowedPattern.test(rawValue.value)) {
|
|
53
|
+
context.report({
|
|
54
|
+
loc: loc,
|
|
55
|
+
messageId: 'unencoded'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (((_a = rawValue.quotedValue) === null || _a === void 0 ? void 0 : _a.startsWith('"')) &&
|
|
59
|
+
((_b = rawValue.value) === null || _b === void 0 ? void 0 : _b.includes('"'))) {
|
|
60
|
+
context.report({
|
|
61
|
+
loc: loc,
|
|
62
|
+
messageId: 'doubleQuotes'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else if (((_c = rawValue.quotedValue) === null || _c === void 0 ? void 0 : _c.startsWith("'")) &&
|
|
66
|
+
((_d = rawValue.value) === null || _d === void 0 ? void 0 : _d.includes("'"))) {
|
|
67
|
+
context.report({
|
|
68
|
+
loc: loc,
|
|
69
|
+
messageId: 'singleQuotes'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
module.exports = rule;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Disallows a set of attributes from being used in templates
|
|
4
|
+
* @author James Garbutt <https://github.com/43081j>
|
|
5
|
+
*/
|
|
6
|
+
const template_analyzer_1 = require("../template-analyzer");
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
const rule = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Disallows a set of attributes from being used in templates',
|
|
14
|
+
recommended: false,
|
|
15
|
+
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/ban-attributes.md'
|
|
16
|
+
},
|
|
17
|
+
schema: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
items: {
|
|
20
|
+
type: 'string'
|
|
21
|
+
},
|
|
22
|
+
uniqueItems: true
|
|
23
|
+
},
|
|
24
|
+
messages: {
|
|
25
|
+
denied: 'The attribute "{{ attr }}" is not allowed in templates'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
create(context) {
|
|
29
|
+
const source = context.getSourceCode();
|
|
30
|
+
const userDisallowedAttributes = context.options;
|
|
31
|
+
const disallowedAttributes = userDisallowedAttributes.map((attr) => attr.toLowerCase());
|
|
32
|
+
return {
|
|
33
|
+
TaggedTemplateExpression: (node) => {
|
|
34
|
+
if (node.type === 'TaggedTemplateExpression' &&
|
|
35
|
+
node.tag.type === 'Identifier' &&
|
|
36
|
+
node.tag.name === 'html') {
|
|
37
|
+
const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
|
|
38
|
+
analyzer.traverse({
|
|
39
|
+
enterElement: (element) => {
|
|
40
|
+
// eslint-disable-next-line guard-for-in
|
|
41
|
+
for (const attr in element.attribs) {
|
|
42
|
+
let attrNormalised = attr.toLowerCase();
|
|
43
|
+
if (attrNormalised.startsWith('?')) {
|
|
44
|
+
attrNormalised = attrNormalised.slice(1);
|
|
45
|
+
}
|
|
46
|
+
if (disallowedAttributes.includes(attrNormalised)) {
|
|
47
|
+
const loc = analyzer.getLocationForAttribute(element, attr, source);
|
|
48
|
+
if (loc) {
|
|
49
|
+
context.report({
|
|
50
|
+
loc: loc,
|
|
51
|
+
messageId: 'denied',
|
|
52
|
+
data: { attr: attrNormalised }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
module.exports = rule;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Disallows invalid binding positions in templates
|
|
4
|
+
* @author James Garbutt <https://github.com/43081j>
|
|
5
|
+
*/
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
// Rule Definition
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
const rule = {
|
|
10
|
+
meta: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Disallows invalid binding positions in templates',
|
|
13
|
+
recommended: false,
|
|
14
|
+
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/binding-positions.md'
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
noBindingTagName: 'Bindings cannot be used in place of tag names.',
|
|
19
|
+
noBindingAttributeName: 'Bindings cannot be used in place of attribute names.',
|
|
20
|
+
noBindingSelfClosingTag: 'Bindings at the end of a self-closing tag must be' +
|
|
21
|
+
' followed by a space or quoted',
|
|
22
|
+
noBindingHTMLComment: 'Bindings cannot be used inside HTML comments.'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
create(context) {
|
|
26
|
+
// variables should be defined here
|
|
27
|
+
const tagPattern = /<\/?$/;
|
|
28
|
+
const attrPattern = /^=/;
|
|
29
|
+
const selfClosingPattern = /^\/>/;
|
|
30
|
+
//----------------------------------------------------------------------
|
|
31
|
+
// Helpers
|
|
32
|
+
//----------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Determines if a given TemplateElement is contained within
|
|
35
|
+
* a HTML comment.
|
|
36
|
+
*
|
|
37
|
+
* @param {ESTree.TemplateElement=} expr Expression to test
|
|
38
|
+
* @return {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isInsideComment(expr) {
|
|
41
|
+
return (expr !== undefined &&
|
|
42
|
+
expr.value.raw.lastIndexOf('<!--') > expr.value.raw.lastIndexOf('-->'));
|
|
43
|
+
}
|
|
44
|
+
//----------------------------------------------------------------------
|
|
45
|
+
// Public
|
|
46
|
+
//----------------------------------------------------------------------
|
|
47
|
+
return {
|
|
48
|
+
TaggedTemplateExpression: (node) => {
|
|
49
|
+
if (node.type === 'TaggedTemplateExpression' &&
|
|
50
|
+
node.tag.type === 'Identifier' &&
|
|
51
|
+
node.tag.name === 'html') {
|
|
52
|
+
for (let i = 0; i < node.quasi.expressions.length; i++) {
|
|
53
|
+
const expr = node.quasi.expressions[i];
|
|
54
|
+
const prev = node.quasi.quasis[i];
|
|
55
|
+
const next = node.quasi.quasis[i + 1];
|
|
56
|
+
if (tagPattern.test(prev.value.raw)) {
|
|
57
|
+
context.report({
|
|
58
|
+
node: expr,
|
|
59
|
+
messageId: 'noBindingTagName'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (next && attrPattern.test(next.value.raw)) {
|
|
63
|
+
context.report({
|
|
64
|
+
node: expr,
|
|
65
|
+
messageId: 'noBindingAttributeName'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (next && selfClosingPattern.test(next.value.raw)) {
|
|
69
|
+
context.report({
|
|
70
|
+
node: expr,
|
|
71
|
+
messageId: 'noBindingSelfClosingTag'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (isInsideComment(prev)) {
|
|
75
|
+
context.report({
|
|
76
|
+
node: expr,
|
|
77
|
+
messageId: 'noBindingHTMLComment'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
module.exports = rule;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Enforces calling `super` in lifecycle methods
|
|
4
|
+
* @author James Garbutt <https://github.com/43081j>
|
|
5
|
+
*/
|
|
6
|
+
const methodNames = ['connectedCallback', 'disconnectedCallback', 'update'];
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
const rule = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Enforces calling `super` in lifecycle methods',
|
|
14
|
+
recommended: false,
|
|
15
|
+
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/lifecycle-super.md'
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
callSuper: 'You must call `super.{{method}}` to avoid interrupting ' +
|
|
20
|
+
'the lit rendering lifecycle'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
// variables should be defined here
|
|
25
|
+
let inElement = false;
|
|
26
|
+
let currentMethod = null;
|
|
27
|
+
let superSeen = false;
|
|
28
|
+
//----------------------------------------------------------------------
|
|
29
|
+
// Helpers
|
|
30
|
+
//----------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Class entered
|
|
33
|
+
*
|
|
34
|
+
* @param {ESTree.Class} node Node entered
|
|
35
|
+
* @return {void}
|
|
36
|
+
*/
|
|
37
|
+
function classEnter(node) {
|
|
38
|
+
if (!node.superClass ||
|
|
39
|
+
node.superClass.type !== 'Identifier' ||
|
|
40
|
+
node.superClass.name !== 'LitElement') {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
inElement = true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Class exited
|
|
47
|
+
*
|
|
48
|
+
* @return {void}
|
|
49
|
+
*/
|
|
50
|
+
function classExit() {
|
|
51
|
+
inElement = false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Method entered
|
|
55
|
+
*
|
|
56
|
+
* @param {ESTree.MethodDefinition} node Node entered
|
|
57
|
+
* @return {void}
|
|
58
|
+
*/
|
|
59
|
+
function methodEnter(node) {
|
|
60
|
+
if (!inElement ||
|
|
61
|
+
node.static === true ||
|
|
62
|
+
node.kind !== 'method' ||
|
|
63
|
+
node.key.type !== 'Identifier' ||
|
|
64
|
+
!methodNames.includes(node.key.name)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
currentMethod = node.key.name;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Method exited
|
|
71
|
+
*
|
|
72
|
+
* @param {ESTree.MethodDefinition} node Node entered
|
|
73
|
+
* @return {void}
|
|
74
|
+
*/
|
|
75
|
+
function methodExit(node) {
|
|
76
|
+
if (currentMethod !== null && !superSeen) {
|
|
77
|
+
context.report({
|
|
78
|
+
node,
|
|
79
|
+
messageId: 'callSuper',
|
|
80
|
+
data: {
|
|
81
|
+
method: currentMethod
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
currentMethod = null;
|
|
86
|
+
superSeen = false;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Call expression entered
|
|
90
|
+
* @param {ESTree.CallExpression} node Node entered
|
|
91
|
+
* @return {void}
|
|
92
|
+
*/
|
|
93
|
+
function callExpressionEnter(node) {
|
|
94
|
+
if (currentMethod === null) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
98
|
+
node.callee.object.type === 'Super' &&
|
|
99
|
+
node.callee.property.type === 'Identifier' &&
|
|
100
|
+
node.callee.property.name === currentMethod) {
|
|
101
|
+
superSeen = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//----------------------------------------------------------------------
|
|
105
|
+
// Public
|
|
106
|
+
//----------------------------------------------------------------------
|
|
107
|
+
return {
|
|
108
|
+
ClassExpression: (node) => classEnter(node),
|
|
109
|
+
ClassDeclaration: (node) => classEnter(node),
|
|
110
|
+
'ClassExpression:exit': classExit,
|
|
111
|
+
'ClassDeclaration:exit': classExit,
|
|
112
|
+
MethodDefinition: (node) => methodEnter(node),
|
|
113
|
+
'MethodDefinition:exit': methodExit,
|
|
114
|
+
CallExpression: (node) => callExpressionEnter(node)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
module.exports = rule;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Disallows properties shadowed as class fields
|
|
4
|
+
* @author Michel Langeveld <https://github.com/michellangeveld>
|
|
5
|
+
*/
|
|
6
|
+
const util_1 = require("../util");
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
const rule = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Disallows properties shadowed as class fields',
|
|
14
|
+
recommended: true,
|
|
15
|
+
url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-classfield-shadowing.md'
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
noClassfieldShadowing: 'The {{ prop }} property is a class field which has the same name as ' +
|
|
20
|
+
'static property which could have unintended side-effects.'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
ClassDeclaration: (node) => {
|
|
26
|
+
if ((0, util_1.isLitClass)(node)) {
|
|
27
|
+
const propertyMap = (0, util_1.getPropertyMap)(node);
|
|
28
|
+
const classMembers = (0, util_1.getClassFields)(node);
|
|
29
|
+
for (const [prop, { key }] of propertyMap.entries()) {
|
|
30
|
+
if (classMembers.has(prop)) {
|
|
31
|
+
context.report({
|
|
32
|
+
node: key,
|
|
33
|
+
messageId: 'noClassfieldShadowing',
|
|
34
|
+
data: { prop }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
module.exports = rule;
|