Skip to content

Commit a604e2d

Browse files
committed
Only fail if the assignment is to a reactive property
1 parent 7d80d74 commit a604e2d

File tree

2 files changed

+104
-9
lines changed

2 files changed

+104
-9
lines changed

src/rules/no-this-assign-in-render.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import {Rule} from 'eslint';
77
import * as ESTree from 'estree';
8-
import {isLitClass} from '../util';
8+
import {getPropertyMap, isLitClass, PropertyMapEntry} from '../util';
99

1010
//------------------------------------------------------------------------------
1111
// Rule Definition
@@ -31,6 +31,7 @@ const rule: Rule.RuleModule = {
3131
create(context): Rule.RuleListener {
3232
let inRender = false;
3333
let inComponent = false;
34+
let propertyMap: ReadonlyMap<string, PropertyMapEntry> | null = null;
3435

3536
/**
3637
* Class entered
@@ -43,6 +44,12 @@ const rule: Rule.RuleModule = {
4344
return;
4445
}
4546

47+
const props = getPropertyMap(node);
48+
49+
if (props) {
50+
propertyMap = props;
51+
}
52+
4653
inComponent = true;
4754
}
4855

@@ -52,6 +59,7 @@ const rule: Rule.RuleModule = {
5259
* @return {void}
5360
*/
5461
function classExit(): void {
62+
propertyMap = null;
5563
inComponent = false;
5664
}
5765

@@ -105,17 +113,31 @@ const rule: Rule.RuleModule = {
105113
* @return {void}
106114
*/
107115
function assignmentFound(node: Rule.Node): void {
108-
if (!inRender || node.type !== 'MemberExpression') {
116+
if (!inRender ||
117+
!propertyMap ||
118+
node.type !== 'MemberExpression') {
109119
return;
110120
}
111121

112-
const nonMember = walkMembers(node);
113-
122+
const nonMember = walkMembers(node) as Rule.Node;
114123
if (nonMember.type === 'ThisExpression') {
124+
const parent = nonMember.parent as ESTree.MemberExpression;
125+
console.log(parent);
126+
127+
let propertyName = '';
128+
if (parent.property.type === 'Identifier' && !parent.computed) {
129+
propertyName = parent.property.name;
130+
} else if (parent.property.type === 'Literal') {
131+
propertyName = String(parent.property.value);
132+
}
133+
134+
if (propertyMap.has(propertyName) ||
135+
parent.property.type === 'Identifier' && parent.computed) {
115136
context.report({
116137
node: node.parent,
117138
messageId: 'noThis'
118139
});
140+
}
119141
}
120142
}
121143

@@ -129,7 +151,6 @@ const rule: Rule.RuleModule = {
129151
MethodDefinition: (node: ESTree.Node): void =>
130152
methodEnter(node as ESTree.MethodDefinition),
131153
'MethodDefinition:exit': methodExit,
132-
// eslint-disable-next-line max-len
133154
'AssignmentExpression > .left:has(ThisExpression)': (
134155
node: Rule.Node
135156
): void => assignmentFound(node)

src/test/rules/no-this-assign-in-render_test.ts

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,18 @@ ruleTester.run('no-this-assign-in-render', rule, {
4343
this.deep.prop = 5;
4444
}
4545
}`,
46+
`class Foo {
47+
static get properties() {
48+
return { prop: { type: Number } };
49+
}
50+
render() {
51+
this.deep.prop = 5;
52+
}
53+
}`,
4654
`class Foo extends LitElement {
55+
static get properties() {
56+
return { x: { type: Number } };
57+
}
4758
render() {
4859
const x = this.prop;
4960
}
@@ -54,108 +65,171 @@ ruleTester.run('no-this-assign-in-render', rule, {
5465
}
5566
}`,
5667
`class Foo extends LitElement {
68+
static get properties() {
69+
return { foo: { type: Number } };
70+
}
5771
static render() {
5872
this.foo = 5;
5973
}
6074
}`,
6175
`class Foo extends LitElement {
76+
static get properties() {
77+
return { prop: { type: Number } };
78+
}
6279
render() {
6380
let x;
6481
x = this.prop;
6582
}
6683
}`,
6784
`class Foo extends LitElement {
85+
static get properties() {
86+
return { x: { type: Number } };
87+
}
6888
render() {
6989
let x;
7090
x = 5;
7191
}
7292
}`,
7393
`class Foo extends LitElement {
94+
static get properties() {
95+
return { foo: { type: Number } };
96+
}
7497
render() {
7598
let x;
7699
x = this.foo || 123;
77100
}
78101
}`,
79102
`class Foo extends LitElement {
103+
static get properties() {
104+
return { prop: { type: Number } };
105+
}
80106
render() {
81107
const x = {};
82108
x[this.prop] = 123;
83109
}
84110
}`,
85111
`class Foo extends LitElement {
112+
static get properties() {
113+
return { prop: { type: Number } };
114+
}
86115
render() {
87116
const x = () => ({});
88117
x(this.prop).y = 123;
89118
}
119+
}`,
120+
`class Foo extends LitElement {
121+
static get properties() {
122+
return { prop: { type: Number } };
123+
}
124+
render() {
125+
this.unreactive = 123;
126+
}
127+
}`,
128+
`class Foo extends LitElement {
129+
static get properties() {
130+
return { prop: { type: Number } };
131+
}
132+
render() {
133+
this.unreactive.prop = 123;
134+
}
90135
}`
91136
],
92137

93138
invalid: [
94139
{
95140
code: `class Foo extends LitElement {
141+
static get properties() {
142+
return { prop: { type: String } };
143+
}
96144
render() {
97145
this.prop = 'foo';
98146
}
99147
}`,
100148
errors: [
101149
{
102150
messageId: 'noThis',
103-
line: 3,
151+
line: 6,
104152
column: 11
105153
}
106154
]
107155
},
108156
{
109157
code: `class Foo extends LitElement {
158+
static get properties() {
159+
return { deep: { type: Object } };
160+
}
110161
render() {
111162
this.deep.prop = 'foo';
112163
}
113164
}`,
114165
errors: [
115166
{
116167
messageId: 'noThis',
117-
line: 3,
168+
line: 6,
118169
column: 11
119170
}
120171
]
121172
},
122173
{
123174
code: `const foo = class extends LitElement {
175+
static get properties() {
176+
return { prop: { type: String } };
177+
}
124178
render() {
125179
this.prop = 'foo';
126180
}
127181
}`,
128182
errors: [
129183
{
130184
messageId: 'noThis',
131-
line: 3,
185+
line: 6,
132186
column: 11
133187
}
134188
]
135189
},
136190
{
137191
code: `class Foo extends LitElement {
192+
static get properties() {
193+
return { prop: { type: String } };
194+
}
138195
render() {
139196
this['prop'] = 'foo';
140197
}
141198
}`,
142199
errors: [
143200
{
144201
messageId: 'noThis',
145-
line: 3,
202+
line: 6,
146203
column: 11
147204
}
148205
]
149206
},
150207
{
151208
code: `@customElement('foo')
152209
class Foo extends FooElement {
210+
@property({ type: String })
211+
prop = '';
153212
render() {
154213
this['prop'] = 'foo';
155214
}
156215
}`,
157216
parser,
158217
parserOptions,
218+
errors: [
219+
{
220+
messageId: 'noThis',
221+
line: 6,
222+
column: 11
223+
}
224+
]
225+
},
226+
{
227+
code: `class Foo extends LitElement {
228+
render() {
229+
const x = 'prop';
230+
this[x] = 'foo';
231+
}
232+
}`,
159233
errors: [
160234
{
161235
messageId: 'noThis',

0 commit comments

Comments
 (0)