Skip to content

Commit

Permalink
Ensure waiters are discovered even if multiple versions exist
Browse files Browse the repository at this point in the history
Currently, we rely heavily on the highlander logic to ensure that we can
**only** ever have one version of `@ember/test-waiters` in the build
outpu. The reason for this is so that when someone calls
`hasPendingWaiters()` we can reliably ensure that _**ALL**_ waiters that
have been registered (regardless of version mismatches and whatnot) are
found.

Unfortunately, we cannot actually rely on the highlander logic to
guarantee this "waiter map" uniqueness in Embroider builds (because the
highlander logic is broken by the isolated nature of Embroiders
compatibility layer).

This change migrates from a strict reliance on the highlander logic to
guarantee the module scoped variable is shared globally to a mechanism
of sharing the state on `globalThis` (with fallbacks for various other
scenarios). Using this pattern allows us to avoid "caring" if the
highlander code actually enforces a single version, while still ensuring
that `hasPendingWaiters` will always return the right value.

It is likely that future changes will remove the usage of the highlander
logic as of a certain version (that includes this change) and higher.
  • Loading branch information
rwjblue committed Apr 4, 2022
1 parent dc58c3c commit e414136
Showing 1 changed file with 35 additions and 1 deletion.
36 changes: 35 additions & 1 deletion addon/@ember/test-waiters/waiter-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,41 @@ import { PendingWaiterState, Waiter, WaiterName } from './types';
import Ember from 'ember';
import { registerWaiter } from '@ember/test';

const WAITERS: Map<WaiterName, Waiter> = new Map<WaiterName, Waiter>();
type Indexable = Record<any, unknown>;

// this ensures that if @ember/test-waiters exists in multiple places in the
// build output we will still use a single map of waiters (there really should
// only be one of them, or else `settled` will not work at all)
const WAITERS: Map<WaiterName, Waiter> = (function() {
const HAS_SYMBOL = typeof Symbol !== 'undefined';

let symbolName = 'LIFELINE_QUEUED_POLL_TASKS';
let symbol = HAS_SYMBOL ? Symbol.for(symbolName) as any : symbolName;

let global = getGlobal();

let waiters = global[symbol];
if (waiters === undefined) {
waiters = global[symbol] = new Map<WaiterName, Waiter>();
}

return waiters as Map<WaiterName, Waiter>;
})();


function indexable<T extends object>(input: T): T & Indexable {
return input as T & Indexable;
}

function getGlobal(): Indexable {
// eslint-disable-next-line node/no-unsupported-features/es-builtins
if (typeof globalThis !== 'undefined') return indexable(globalThis);
if (typeof self !== 'undefined') return indexable(self);
if (typeof window !== 'undefined') return indexable(window);
if (typeof global !== 'undefined') return indexable(global);

throw new Error('unable to locate global object');
}

/**
* Backwards compatibility with legacy waiters system.
Expand Down

0 comments on commit e414136

Please sign in to comment.