From 89b6d9c6cfe957d704733d1290ce360effc52131 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 5 Jul 2024 14:58:01 -0400 Subject: [PATCH] Break eager cycle for "deprecate" function There is a module cycle involving `@ember/debug/index` and `@ember/debug/lib/deprecate`, and it's somewhat intentional since both are sharing mutable state with the other that's intended to allow users to replace the implementations. The cycle wouldn't be a big deal except for the fact that index **eagerly** consumes the `deprecate` function from `lib/deprecate`, which means that if you get unlucky in the evaluation order it just explodes. There's no need to eagerly consume it. This PR makes the consumption lazy. In general `export function` is resilient to cycles in a way that `export let` is not. There are a bunch more places in `@ember/debug` that are using `export let` when they should probably be using `export function` instead, especially because these function are supposed to *always* be present (even in prod they are no-ops, not missing). --- packages/@ember/debug/index.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/@ember/debug/index.ts b/packages/@ember/debug/index.ts index 162954b3149..9dbf41386d0 100644 --- a/packages/@ember/debug/index.ts +++ b/packages/@ember/debug/index.ts @@ -2,7 +2,7 @@ import { isChrome, isFirefox } from '@ember/-internals/browser-environment'; import type { AnyFn } from '@ember/-internals/utility-types'; import { DEBUG } from '@glimmer/env'; import type { DeprecateFunc, DeprecationOptions } from './lib/deprecate'; -import _deprecate from './lib/deprecate'; +import defaultDeprecate from './lib/deprecate'; import { isTesting } from './lib/testing'; import type { WarnFunc } from './lib/warn'; import _warn from './lib/warn'; @@ -75,7 +75,7 @@ let assert: AssertFunc = noop as unknown as AssertFunc; let info: InfoFunc = noop; let warn: WarnFunc = noop; let debug: DebugFunc = noop; -let deprecate: DeprecateFunc = noop; +let currentDeprecate: DeprecateFunc | undefined; let debugSeal: DebugSealFunc = noop; let debugFreeze: DebugFreezeFunc = noop; let runInDebug: RunInDebugFunc = noop; @@ -86,6 +86,10 @@ let deprecateFunc: DeprecateFuncFunc = function () { return arguments[arguments.length - 1]; }; +export function deprecate(...args: Parameters): ReturnType { + return (currentDeprecate ?? defaultDeprecate)(...args); +} + if (DEBUG) { setDebugFunction = function (type: DebugFunctionType, callback: Function) { switch (type) { @@ -98,7 +102,7 @@ if (DEBUG) { case 'debug': return (debug = callback as DebugFunc); case 'deprecate': - return (deprecate = callback as DeprecateFunc); + return (currentDeprecate = callback as DeprecateFunc); case 'debugSeal': return (debugSeal = callback as DebugSealFunc); case 'debugFreeze': @@ -314,8 +318,6 @@ if (DEBUG) { } }); - setDebugFunction('deprecate', _deprecate); - setDebugFunction('warn', _warn); } @@ -353,7 +355,6 @@ export { info, warn, debug, - deprecate, debugSeal, debugFreeze, runInDebug,