diff --git a/packages/@lwc/engine-core/src/framework/decorators/register.ts b/packages/@lwc/engine-core/src/framework/decorators/register.ts
index 5c3ee03970..a6094a3dcd 100644
--- a/packages/@lwc/engine-core/src/framework/decorators/register.ts
+++ b/packages/@lwc/engine-core/src/framework/decorators/register.ts
@@ -254,7 +254,7 @@ export function registerDecorators(
validateMethodDecoratedWithWire(Ctor, fieldOrMethodName, descriptor);
}
if (isUndefined(descriptor)) {
- throw new Error();
+ throw new Error(`Missing descriptor for wired method "${fieldOrMethodName}".`);
}
wiredMethods[fieldOrMethodName] = descriptor;
storeWiredMethodMeta(descriptor, adapter, configCallback, dynamic);
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/errors/throws-on-computed-method/error.txt b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/errors/throws-on-computed-method/error.txt
index 9991fca2d0..08ea7669d8 100644
--- a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/errors/throws-on-computed-method/error.txt
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/errors/throws-on-computed-method/error.txt
@@ -1 +1 @@
-An empty error occurred?!
\ No newline at end of file
+Missing descriptor for wired method "sym".
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/config.json b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/config.json
new file mode 100644
index 0000000000..6ef2ee2454
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/config.json
@@ -0,0 +1,3 @@
+{
+ "entry": "x/wire"
+}
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/error.txt b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/error.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/expected.html b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/expected.html
new file mode 100644
index 0000000000..0e83a81401
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/expected.html
@@ -0,0 +1,8 @@
+
+
+ wired field value: {
+ key1: foo,
+ key2: [fixed,array],
+}
+
+
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/adapter/adapter.js b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/adapter/adapter.js
new file mode 100644
index 0000000000..90c408d45a
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/adapter/adapter.js
@@ -0,0 +1,21 @@
+export default class adapter {
+ constructor(dataCallback) {
+ this.dc = dataCallback;
+ }
+
+ connect() {}
+
+ update(config) {
+ // JSON.stringify serializes differently for engine-server/ssr-compiler, so we do it manually
+ const output = Object.entries(config)
+ .sort(([a], [b]) => a.localeCompare(b))
+ .map(([key, value]) => ` ${key}: ${JSON.stringify(value)},`)
+ .join('\n')
+ // Quotes are encoded as " in the output, which makes it harder to read...
+ .replace(/"/g, '');
+
+ this.dc(`{\n${output}\n}`);
+ }
+
+ disconnect() {}
+}
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.html b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.html
new file mode 100644
index 0000000000..1db03c7f9f
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.html
@@ -0,0 +1,3 @@
+
+ wired field value: {wiredProp}
+
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.js b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.js
new file mode 100644
index 0000000000..5a2d4b2dc8
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-field/modules/x/wire/wire.js
@@ -0,0 +1,9 @@
+import { wire, LightningElement } from 'lwc';
+import WireAdapter from 'x/adapter';
+export default class Test extends LightningElement {
+ @wire(WireAdapter, { key1: '$prop1', key2: ['fixed', 'array'] })
+ // Accidentally marking a wired prop as static doesn't matter
+ static wiredProp;
+
+ prop1 = 'foo';
+}
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/config.json b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/config.json
new file mode 100644
index 0000000000..0eb66f7c86
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/config.json
@@ -0,0 +1,6 @@
+{
+ "entry": "x/wire",
+ "ssrFiles": {
+ "error": "error-ssr.txt"
+ }
+}
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error-ssr.txt b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error-ssr.txt
new file mode 100644
index 0000000000..aac60ef4a9
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error-ssr.txt
@@ -0,0 +1 @@
+instance.wiredMethod is not a function
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error.txt b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error.txt
new file mode 100644
index 0000000000..746021a392
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/error.txt
@@ -0,0 +1 @@
+Missing descriptor for wired method "wiredMethod".
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/expected.html b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/expected.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/adapter/adapter.js b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/adapter/adapter.js
new file mode 100644
index 0000000000..90c408d45a
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/adapter/adapter.js
@@ -0,0 +1,21 @@
+export default class adapter {
+ constructor(dataCallback) {
+ this.dc = dataCallback;
+ }
+
+ connect() {}
+
+ update(config) {
+ // JSON.stringify serializes differently for engine-server/ssr-compiler, so we do it manually
+ const output = Object.entries(config)
+ .sort(([a], [b]) => a.localeCompare(b))
+ .map(([key, value]) => ` ${key}: ${JSON.stringify(value)},`)
+ .join('\n')
+ // Quotes are encoded as " in the output, which makes it harder to read...
+ .replace(/"/g, '');
+
+ this.dc(`{\n${output}\n}`);
+ }
+
+ disconnect() {}
+}
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.html b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.html
new file mode 100644
index 0000000000..c70725ad81
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.html
@@ -0,0 +1,3 @@
+
+ wired field value: {externalProp}
+
\ No newline at end of file
diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.js b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.js
new file mode 100644
index 0000000000..53ad54e9ae
--- /dev/null
+++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/static-method/modules/x/wire/wire.js
@@ -0,0 +1,12 @@
+import { wire, LightningElement } from 'lwc';
+import WireAdapter from 'x/adapter';
+export default class Test extends LightningElement {
+ @wire(WireAdapter, { key1: '$prop1', key2: ['fixed', 'array'] })
+ // Accidentally static 💣
+ static wiredMethod(value) {
+ this.externalProp = value;
+ }
+
+ prop1 = 'foo';
+ externalProp;
+}
diff --git a/packages/@lwc/integration-karma/test/component/decorators/api/index.spec.js b/packages/@lwc/integration-karma/test/component/decorators/api/index.spec.js
index b26a7077da..e141a962f7 100644
--- a/packages/@lwc/integration-karma/test/component/decorators/api/index.spec.js
+++ b/packages/@lwc/integration-karma/test/component/decorators/api/index.spec.js
@@ -11,7 +11,7 @@ import Methods from 'x/methods';
import Inheritance from 'x/inheritance';
import NullInitialValue from 'x/nullInitialValue';
import ExtendsMixin from 'x/extendsMixin';
-
+import StaticProperty from 'x/staticProperty';
import duplicatePropertyTemplate from 'x/duplicatePropertyTemplate';
import NoSetter from 'x/noSetter';
@@ -61,6 +61,15 @@ describe('properties', () => {
});
}).toThrowError();
});
+
+ it("probably shouldn't work on a static prop, but it does", () => {
+ const elm = createElement('x-static-property', { is: StaticProperty });
+ expect(StaticProperty.staticProperty).toBe('wot');
+ expect(elm.staticProperty).toBe(undefined);
+ elm.staticProperty = 'weird';
+ expect(StaticProperty.staticProperty).toBe('wot');
+ expect(elm.staticProperty).toBe('weird');
+ });
});
describe('getter/setter', () => {
diff --git a/packages/@lwc/integration-karma/test/component/decorators/api/x/staticProperty/staticProperty.js b/packages/@lwc/integration-karma/test/component/decorators/api/x/staticProperty/staticProperty.js
new file mode 100644
index 0000000000..a4676223f0
--- /dev/null
+++ b/packages/@lwc/integration-karma/test/component/decorators/api/x/staticProperty/staticProperty.js
@@ -0,0 +1,6 @@
+import { LightningElement, api } from 'lwc';
+
+export default class StaticProperty extends LightningElement {
+ // People probably shouldn't do this, but they can...
+ @api static staticProperty = 'wot';
+}
diff --git a/packages/@lwc/integration-karma/test/component/decorators/track/index.spec.js b/packages/@lwc/integration-karma/test/component/decorators/track/index.spec.js
index f809cd1dc2..4fda4d97df 100644
--- a/packages/@lwc/integration-karma/test/component/decorators/track/index.spec.js
+++ b/packages/@lwc/integration-karma/test/component/decorators/track/index.spec.js
@@ -4,7 +4,7 @@ import Properties from 'x/properties';
import SideEffect from 'x/sideEffect';
import NonObservable from 'x/nonObservable';
import SetTrackedValueToNull from 'x/setTrackedValueToNull';
-
+import StaticProperty from 'x/staticProperty';
import duplicatePropertyTemplate from 'x/duplicatePropertyTemplate';
it('rerenders the component when a track property is updated - literal', () => {
@@ -31,6 +31,11 @@ it('rerenders the component when a track property is updated - object', () => {
});
});
+it("doesn't work for decorated static props", () => {
+ const elm = createElement('x-static-property', { is: StaticProperty });
+ expect(() => elm.increment()).toThrowError(/undefined/);
+});
+
describe('restrictions', () => {
it('logs an error when updating a track property during render', () => {
const elm = createElement('x-side-effect', { is: SideEffect });
diff --git a/packages/@lwc/integration-karma/test/component/decorators/track/x/staticProperty/staticProperty.js b/packages/@lwc/integration-karma/test/component/decorators/track/x/staticProperty/staticProperty.js
new file mode 100644
index 0000000000..0cbb49d5cd
--- /dev/null
+++ b/packages/@lwc/integration-karma/test/component/decorators/track/x/staticProperty/staticProperty.js
@@ -0,0 +1,8 @@
+import { LightningElement, api, track } from 'lwc';
+
+export default class Properties extends LightningElement {
+ @track static obj = { value: 0 };
+ @api increment() {
+ this.obj.value += 1;
+ }
+}