Skip to content

Commit 8e90e92

Browse files
authored
feat: allow computed properties in @wire if they are constants or primitives @W-14785085 (#3955)
* refactor(@wire): split parameter validation into individual methods * chore(tests): remove duplicate tests duplicate of decorator-accepts-a-member-expression * test(@wire): add tests for spreads, methods, and numbers * test(@wire): update test for computed identifiers and primitives * feat: allow computed properties that are constants or primitive literals * refactor: less nesting * test(@wire): add test for template literal computed prop * test(@wire): add test for let variable computed prop * test(@wire): add test for expression in computed prop * test(@wire): add test for regexp literal computed prop * test(@wire): add test for bigint literal computed prop * chore: add github issues to TODOs
1 parent 69a6766 commit 8e90e92

File tree

20 files changed

+281
-48
lines changed

20 files changed

+281
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { wire, LightningElement } from "lwc";
2-
import { Foo } from "data-service";
2+
import { getFoo } from "data-service";
33
export default class Test extends LightningElement {
4-
@wire(Foo.Bar, {}) wiredProp;
4+
@wire(getFoo, {
5+
method() {}
6+
})
7+
wiredProp;
58
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { registerDecorators as _registerDecorators, registerComponent as _registerComponent, LightningElement } from "lwc";
22
import _tmpl from "./test.html";
3-
import { Foo } from "data-service";
3+
import { getFoo } from "data-service";
44
class Test extends LightningElement {
55
wiredProp;
66
/*LWC compiler vX.X.X*/
77
}
88
_registerDecorators(Test, {
99
wire: {
1010
wiredProp: {
11-
adapter: Foo.Bar,
11+
adapter: getFoo,
1212
dynamic: [],
1313
config: function ($cmp) {
14-
return {};
14+
return {
15+
method() {}
16+
};
1517
}
1618
}
1719
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
const spreadMe = {
4+
key1: "$prop2"
5+
}
6+
export default class Test extends LightningElement {
7+
@wire(getFoo, {
8+
...spreadMe,
9+
...({key2: "$prop2"})
10+
})
11+
wiredProp;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { registerDecorators as _registerDecorators, registerComponent as _registerComponent, LightningElement } from "lwc";
2+
import _tmpl from "./test.html";
3+
import { getFoo } from "data-service";
4+
const spreadMe = {
5+
key1: "$prop2"
6+
};
7+
class Test extends LightningElement {
8+
wiredProp;
9+
/*LWC compiler vX.X.X*/
10+
}
11+
_registerDecorators(Test, {
12+
wire: {
13+
wiredProp: {
14+
adapter: getFoo,
15+
dynamic: [],
16+
config: function ($cmp) {
17+
return {
18+
...spreadMe,
19+
...{
20+
key2: "$prop2"
21+
}
22+
};
23+
}
24+
}
25+
}
26+
});
27+
export default _registerComponent(Test, {
28+
tmpl: _tmpl,
29+
sel: "lwc-test",
30+
apiVersion: 9999999
31+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
const symbol = Symbol.for("key");
4+
export default class Test extends LightningElement {
5+
// accidentally an array expression = oops!
6+
@wire(getFoo, { [[symbol]]: "$prop1", key2: ["fixed", "array"] })
7+
wiredFoo;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"message": "LWC1200: Computed property in @wire config must be a constant or primitive literal.",
3+
"loc": {
4+
"line": 6,
5+
"column": 19,
6+
"start": 237,
7+
"length": 8
8+
},
9+
"filename": "test.js"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
let key1 = 'key1'
4+
export default class Test extends LightningElement {
5+
@wire(getFoo, { [key1]: "$prop1", key2: ["fixed", "array"] })
6+
wiredFoo;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"message": "LWC1200: Computed property in @wire config must be a constant or primitive literal.",
3+
"loc": {
4+
"line": 5,
5+
"column": 19,
6+
"start": 175,
7+
"length": 4
8+
},
9+
"filename": "test.js"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
export default class Test extends LightningElement {
4+
@wire(getFoo, { [/key1/]: "$prop1", key2: ["fixed", "array"] })
5+
wiredFoo;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"message": "LWC1200: Computed property in @wire config must be a constant or primitive literal.",
3+
"loc": {
4+
"line": 4,
5+
"column": 19,
6+
"start": 157,
7+
"length": 6
8+
},
9+
"filename": "test.js"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
export default class Test extends LightningElement {
4+
@wire(getFoo, { [`key1`]: "$prop1", key2: ["fixed", "array"] })
5+
wiredFoo;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"message": "LWC1199: Cannot use a template literal as a computed property key. Instead, use a string or extract the value to a constant.",
3+
"loc": {
4+
"line": 4,
5+
"column": 19,
6+
"start": 157,
7+
"length": 6
8+
},
9+
"filename": "test.js"
10+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { wire, LightningElement } from "lwc";
22
import { getFoo, getBar } from "data-service";
3-
const key1 = Symbol.for("key");
4-
export default class Test extends LightningElement {
5-
@wire(getBar, { [key1]: "$prop1", key2: ["fixed", "array"] })
6-
wiredBar;
73

8-
// eslint-disable-next-line no-useless-computed-key
9-
@wire(getFoo, { ["key1"]: "$prop1", key2: ["fixed", "array"] })
10-
wiredFoo;
4+
const symbol = Symbol.for("key");
5+
export default class Test extends LightningElement {
6+
@wire(getFoo, {
7+
[symbol]: '$prop'
8+
})
9+
wiredIdentifier;
10+
11+
@wire(getBar, {
12+
['computedStringLiteral']: '$prop',
13+
[123n]: '$prop',
14+
[321]: '$prop',
15+
[null]: '$prop',
16+
[undefined]: '$prop'
17+
})
18+
wiredPrimitives;
1119
}

packages/@lwc/babel-plugin-component/src/__tests__/fixtures/wire-decorator/transforms-computed-properties/expected.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
import { registerDecorators as _registerDecorators, registerComponent as _registerComponent, LightningElement } from "lwc";
22
import _tmpl from "./test.html";
33
import { getFoo, getBar } from "data-service";
4-
const key1 = Symbol.for("key");
4+
const symbol = Symbol.for("key");
55
class Test extends LightningElement {
6-
wiredBar;
7-
8-
// eslint-disable-next-line no-useless-computed-key
9-
wiredFoo;
6+
wiredIdentifier;
7+
wiredPrimitives;
108
/*LWC compiler vX.X.X*/
119
}
1210
_registerDecorators(Test, {
1311
wire: {
14-
wiredBar: {
15-
adapter: getBar,
16-
dynamic: [key1],
12+
wiredIdentifier: {
13+
adapter: getFoo,
14+
dynamic: [symbol],
1715
config: function ($cmp) {
1816
return {
19-
key2: ["fixed", "array"],
20-
[key1]: $cmp.prop1
17+
[symbol]: $cmp.prop
2118
};
2219
}
2320
},
24-
wiredFoo: {
25-
adapter: getFoo,
26-
dynamic: ["key1"],
21+
wiredPrimitives: {
22+
adapter: getBar,
23+
dynamic: ["computedStringLiteral", "123", "321", "null", undefined],
2724
config: function ($cmp) {
2825
return {
29-
key2: ["fixed", "array"],
30-
["key1"]: $cmp.prop1
26+
['computedStringLiteral']: $cmp.prop,
27+
[123n]: $cmp.prop,
28+
[321]: $cmp.prop,
29+
[null]: $cmp.prop,
30+
[undefined]: $cmp.prop
3131
};
3232
}
3333
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { wire, LightningElement } from "lwc";
2+
import { getFoo } from "data-service";
3+
export default class Test extends LightningElement {
4+
// Did you know numeric literals can be used as property keys? This becomes "123"!
5+
@wire(getFoo, { 1.2_3e2: "$prop" })
6+
wiredProp;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { registerDecorators as _registerDecorators, registerComponent as _registerComponent, LightningElement } from "lwc";
2+
import _tmpl from "./test.html";
3+
import { getFoo } from "data-service";
4+
class Test extends LightningElement {
5+
// Did you know numeric literals can be used as property keys? This becomes "123"!
6+
wiredProp;
7+
/*LWC compiler vX.X.X*/
8+
}
9+
_registerDecorators(Test, {
10+
wire: {
11+
wiredProp: {
12+
adapter: getFoo,
13+
dynamic: ["123"],
14+
config: function ($cmp) {
15+
return {
16+
1.2_3e2: $cmp.prop
17+
};
18+
}
19+
}
20+
}
21+
});
22+
export default _registerComponent(Test, {
23+
tmpl: _tmpl,
24+
sel: "lwc-test",
25+
apiVersion: 9999999
26+
});

packages/@lwc/babel-plugin-component/src/decorators/wire/transform.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, salesforce.com, inc.
2+
* Copyright (c) 2024, salesforce.com, inc.
33
* All rights reserved.
44
* SPDX-License-Identifier: MIT
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
@@ -175,12 +175,22 @@ function buildWireConfigValue(t: BabelTypes, wiredValues: WiredValue[]) {
175175

176176
if (wiredValue.params) {
177177
const dynamicParamNames = wiredValue.params.map((p) => {
178-
const value = t.isIdentifier(p.key)
179-
? p.key.name
180-
: (p.key as types.StringLiteral).value;
181-
return p.computed && t.isIdentifier(p.key)
182-
? t.identifier(value)
183-
: t.stringLiteral(value);
178+
if (t.isIdentifier(p.key)) {
179+
return p.computed ? t.identifier(p.key.name) : t.stringLiteral(p.key.name);
180+
} else if (
181+
t.isLiteral(p.key) &&
182+
// Template literals may contain expressions, so they are not allowed
183+
!t.isTemplateLiteral(p.key) &&
184+
// RegExp are not primitives, so they are not allowed
185+
!t.isRegExpLiteral(p.key)
186+
) {
187+
const value = t.isNullLiteral(p.key) ? null : p.key.value;
188+
return t.stringLiteral(String(value));
189+
}
190+
// If it's not an identifier or primitive literal then it's a computed expression
191+
throw new TypeError(
192+
`Expected object property key to be an identifier or a literal, but instead saw "${p.key.type}".`
193+
);
184194
});
185195
wireConfig.push(
186196
t.objectProperty(t.identifier('dynamic'), t.arrayExpression(dynamicParamNames))

0 commit comments

Comments
 (0)