Skip to content

Commit

Permalink
chore: add a flag for signals try/catch fix
Browse files Browse the repository at this point in the history
  • Loading branch information
nolanlawson committed Jan 9, 2025
1 parent a2c74f2 commit ec650b7
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 8 deletions.
10 changes: 7 additions & 3 deletions packages/@lwc/engine-core/src/framework/mutation-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ export function componentValueObserved(vm: VM, key: PropertyKey, target: any = {
lwcRuntimeFlags.ENABLE_EXPERIMENTAL_SIGNALS &&
isObject(target) &&
!isNull(target) &&
safeHasProp(target, 'value') &&
safeHasProp(target, 'subscribe') &&
isFunction(target.subscribe) &&
(lwcRuntimeFlags.USE_TRY_CATCH_FOR_SIGNALS_CHECK
? safeHasProp(target, 'value')
: 'value' in target) &&
(lwcRuntimeFlags.USE_TRY_CATCH_FOR_SIGNALS_CHECK
? safeHasProp(target, 'subscribe')
: 'subscribe' in target) &&
isFunction((target as any).subscribe) &&
isTrustedSignal(target) &&
// Only subscribe if a template is being rendered by the engine
tro.isObserving()
Expand Down
1 change: 1 addition & 0 deletions packages/@lwc/features/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const features: FeatureFlagMap = {
ENABLE_FORCE_SHADOW_MIGRATE_MODE: null,
ENABLE_EXPERIMENTAL_SIGNALS: null,
DISABLE_SYNTHETIC_SHADOW: null,
USE_TRY_CATCH_FOR_SIGNALS_CHECK: null,
};

if (!(globalThis as any).lwcRuntimeFlags) {
Expand Down
5 changes: 5 additions & 0 deletions packages/@lwc/features/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export interface FeatureFlagMap {
* native shadow mode.
*/
DISABLE_SYNTHETIC_SHADOW: FeatureFlagValue;

/**
* Use a try/catch when sniffing for whether an object is a signal or not
*/
USE_TRY_CATCH_FOR_SIGNALS_CHECK: FeatureFlagValue;
}

export type FeatureFlagName = keyof FeatureFlagMap;
50 changes: 45 additions & 5 deletions packages/@lwc/integration-karma/test/signal/protocol/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createElement, setFeatureFlagForTest } from 'lwc';
import { catchUnhandledRejectionsAndErrors } from 'test-utils';
import Reactive from 'x/reactive';
import NonReactive from 'x/nonReactive';
import Container from 'x/container';
Expand Down Expand Up @@ -214,13 +215,52 @@ describe('signal protocol', () => {
expect(subscribe).not.toHaveBeenCalled();
});

it('does not throw an error for objects that throw upon "in" checks', async () => {
const elm = createElement('x-throws', { is: Throws });
document.body.appendChild(elm);
describe('try/catch for the signals check', () => {
describe('flag on', () => {
beforeAll(() => {
setFeatureFlagForTest('USE_TRY_CATCH_FOR_SIGNALS_CHECK', true);
});

await Promise.resolve();
afterAll(() => {
setFeatureFlagForTest('USE_TRY_CATCH_FOR_SIGNALS_CHECK', false);
});

it('does not throw an error for objects that throw upon "in" checks', async () => {
const elm = createElement('x-throws', { is: Throws });
document.body.appendChild(elm);

await Promise.resolve();

expect(elm.shadowRoot.querySelector('h1').textContent).toBe('hello');
});
});

expect(elm.shadowRoot.querySelector('h1').textContent).toBe('hello');
describe('flag off', () => {
let caughtError;

catchUnhandledRejectionsAndErrors((error) => {
caughtError = error;
});

afterEach(() => {
caughtError = undefined;
});

it('does throw an error for objects that throw upon "in" checks', async () => {
const elm = createElement('x-throws', { is: Throws });
document.body.appendChild(elm);

// wait 2 ticks for all errors to be caught
await new Promise(setTimeout);
await new Promise(setTimeout);

// still renders
expect(elm.shadowRoot.querySelector('h1').textContent).toBe('hello');

// throws error
expect(caughtError).not.toBeUndefined();
});
});
});
});

Expand Down

0 comments on commit ec650b7

Please sign in to comment.