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 @@ + + + \ 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 @@ + \ 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 @@ + \ 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; + } +}