From 3ca98347b0a6e6ff154144106c4b47f232ec2366 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 29 Nov 2022 15:47:49 -0700 Subject: [PATCH] Introduce stable types for `@ember/owner` - Remove `@ember/-internals/owner` and `@ember/owner` from the list of excluded preview types in the types publishing script, so they now get emitted correctly into `types/stable`. - Remove `@ember/owner` from the preview types and put it in the stable types instead, so users don't get conflicting type dependencies. - Internally in `@ember/owner`, use absolute package paths, not relative. For referencing other (even internal) packages, it's important that we *not* use relative paths, so that (a) the published types work when wrapped in `declare module` statements but also (b) we have clearer boundaries for them, which will unlock further improvements to this infrastructure in the future. --- packages/@ember/owner/index.ts | 4 +- type-tests/stable/@ember/owner-tests.ts | 223 ++++++++++++ type-tests/stable/index.d.ts | 2 + type-tests/stable/tsconfig.json | 3 + types/preview/@ember/owner/index.d.ts | 423 ----------------------- types/preview/@ember/owner/tsconfig.json | 3 - types/preview/index.d.ts | 2 - types/publish.mjs | 6 +- 8 files changed, 232 insertions(+), 434 deletions(-) create mode 100644 type-tests/stable/@ember/owner-tests.ts create mode 100644 type-tests/stable/index.d.ts create mode 100644 type-tests/stable/tsconfig.json delete mode 100644 types/preview/@ember/owner/index.d.ts delete mode 100644 types/preview/@ember/owner/tsconfig.json diff --git a/packages/@ember/owner/index.ts b/packages/@ember/owner/index.ts index 2751e105a4b..25f6d58efbc 100644 --- a/packages/@ember/owner/index.ts +++ b/packages/@ember/owner/index.ts @@ -33,7 +33,7 @@ // We need to provide a narrower public interface to `getOwner` so that we only // expose the `Owner` type, *not* our richer `InternalOwner` type and its // various bits of private API. -import Owner, { getOwner as internalGetOwner } from '../-internals/owner'; +import Owner, { getOwner as internalGetOwner } from '@ember/-internals/owner'; // NOTE: this documentation appears here instead of at the definition site so // it can appear correctly in both API docs and for TS, while providing a richer @@ -99,4 +99,4 @@ export { KnownForTypeResult, Resolver, DIRegistry, -} from '../-internals/owner'; +} from '@ember/-internals/owner'; diff --git a/type-tests/stable/@ember/owner-tests.ts b/type-tests/stable/@ember/owner-tests.ts new file mode 100644 index 00000000000..532a1455bf4 --- /dev/null +++ b/type-tests/stable/@ember/owner-tests.ts @@ -0,0 +1,223 @@ +import Owner, { + Factory, + FactoryManager, + FullName, + RegisterOptions, + Resolver, + KnownForTypeResult, + getOwner, + setOwner, +} from '@ember/owner'; +import Component from '@glimmer/component'; +import { expectTypeOf } from 'expect-type'; +import { + getOwner as getOwnerApplication, + setOwner as setOwnerApplication, +} from '@ember/application'; + +expectTypeOf(getOwnerApplication).toEqualTypeOf(getOwner); +expectTypeOf(setOwnerApplication).toEqualTypeOf(setOwner); + +// Just a class we can construct in the Factory and FactoryManager tests +declare class ConstructThis { + hasProps: boolean; +} + +// ----- RegisterOptions ----- // +declare let regOptionsA: RegisterOptions; +expectTypeOf(regOptionsA.instantiate).toEqualTypeOf(); +expectTypeOf(regOptionsA.singleton).toEqualTypeOf(); + +// ----- Factory ----- // +// This gives us coverage for the cases where you are *casting*. +declare let aFactory: Factory; + +aFactory.create(); +aFactory.create({}); +aFactory.create({ + hasProps: true, +}); +aFactory.create({ + hasProps: false, +}); + +// NOTE: it would be nice if these could be rejected by way of EPC, but alas: it +// cannot, because the public contract for `create` allows implementors to +// define their `create` config object basically however they like. :-/ +aFactory.create({ unrelatedNonsense: 'yep yep yep' }); +aFactory.create({ hasProps: true, unrelatedNonsense: 'yep yep yep' }); + +// But this should be legal. +const goodPojo = { hasProps: true, unrelatedNonsense: 'also true' }; +aFactory.create(goodPojo); + +// This should also be rejected, though for *type error* reasons, not EPC; alas, +// it cannot, for the same reason. +const badPojo = { hasProps: 'huzzah', unrelatedNonsense: 'also true' }; +aFactory.create(badPojo); + +// ----- FactoryManager ----- // +declare let aFactoryManager: FactoryManager; +expectTypeOf(aFactoryManager.class).toEqualTypeOf>(); +expectTypeOf(aFactoryManager.create({})).toEqualTypeOf(); +expectTypeOf(aFactoryManager.create({ hasProps: true })).toEqualTypeOf(); +expectTypeOf(aFactoryManager.create({ hasProps: false })).toEqualTypeOf(); + +// Likewise with these. +aFactoryManager.create({ otherStuff: 'nope' }); +aFactoryManager.create({ hasProps: true, otherStuff: 'nope' }); +expectTypeOf(aFactoryManager.create(goodPojo)).toEqualTypeOf(); +aFactoryManager.create(badPojo); + +// ----- Resolver ----- // +declare let resolver: Resolver; +expectTypeOf().toEqualTypeOf< + ((fullName: FullName) => FullName) | undefined +>(); +expectTypeOf().toEqualTypeOf< + ((fullName: FullName) => string) | undefined +>(); +expectTypeOf(resolver.resolve('random:some-name')).toEqualTypeOf< + object | Factory | undefined +>(); +const knownForFoo = resolver.knownForType?.('foo'); +expectTypeOf(knownForFoo).toEqualTypeOf | undefined>(); +expectTypeOf(knownForFoo?.['foo:bar']).toEqualTypeOf(); +// @ts-expect-error -- there is no `blah` on `knownForFoo`, *only* `foo`. +knownForFoo?.blah; + +// This one is last so it can reuse the bits from above! +// ----- Owner ----- // +declare let owner: Owner; + +// @ts-expect-error +owner.lookup(); +expectTypeOf(owner.lookup('type:name')).toEqualTypeOf(); +// @ts-expect-error +owner.lookup('non-namespace-string'); +expectTypeOf(owner.lookup('namespace@type:name')).toEqualTypeOf(); + +// Arbitrary registration patterns work, as here. +declare module '@ember/owner' { + export interface DIRegistry { + etc: { + 'my-type-test': ConstructThis; + }; + } +} + +expectTypeOf(owner.lookup('etc:my-type-test')).toEqualTypeOf(); + +expectTypeOf(owner.register('type:name', aFactory)).toEqualTypeOf(); +expectTypeOf(owner.register('type:name', aFactory, {})).toEqualTypeOf(); +expectTypeOf(owner.register('type:name', aFactory, { instantiate: true })).toEqualTypeOf(); +expectTypeOf(owner.register('type:name', aFactory, { instantiate: false })).toEqualTypeOf(); +expectTypeOf(owner.register('type:name', aFactory, { singleton: true })).toEqualTypeOf(); +expectTypeOf(owner.register('type:name', aFactory, { singleton: false })).toEqualTypeOf(); +expectTypeOf( + owner.register('type:name', aFactory, { instantiate: true, singleton: true }) +).toEqualTypeOf(); +expectTypeOf( + owner.register('type:name', aFactory, { instantiate: true, singleton: false }) +).toEqualTypeOf(); +expectTypeOf( + owner.register('type:name', aFactory, { instantiate: false, singleton: true }) +).toEqualTypeOf(); +expectTypeOf( + owner.register('type:name', aFactory, { instantiate: false, singleton: false }) +).toEqualTypeOf(); +// @ts-expect-error +owner.register('non-namespace-string', aFactory); +expectTypeOf(owner.register('namespace@type:name', aFactory)).toEqualTypeOf(); + +expectTypeOf(owner.factoryFor('type:name')).toEqualTypeOf | undefined>(); +expectTypeOf(owner.factoryFor('type:name')?.class).toEqualTypeOf | undefined>(); +expectTypeOf(owner.factoryFor('type:name')?.create()).toEqualTypeOf(); +expectTypeOf(owner.factoryFor('type:name')?.create({})).toEqualTypeOf(); +expectTypeOf(owner.factoryFor('type:name')?.create({ anythingGoes: true })).toEqualTypeOf< + object | undefined +>(); +// @ts-expect-error +owner.factoryFor('non-namespace-string'); +expectTypeOf(owner.factoryFor('namespace@type:name')).toEqualTypeOf< + FactoryManager | undefined +>(); + +// Tests deal with the fact that string literals are a special case! `let` +// bindings will accordingly not "just work" as a result. The separate +// assignments both satisfy the linter and show why it matters. +let aName; +aName = 'type:name'; +// @ts-expect-error +owner.lookup(aName); + +let aTypedName: FullName; +aTypedName = 'type:name'; +expectTypeOf(owner.lookup(aTypedName)).toBeUnknown(); + +// Nor will callbacks work "out of the box". But they can work if they have the +// correct type. +declare const justStrings: string[]; +// @ts-expect-error +justStrings.map((aString) => owner.lookup(aString)); +declare let typedStrings: FullName[]; +typedStrings.map((aString) => owner.lookup(aString)); + +// Also make sure it keeps working with const bindings +const aConstName = 'type:name'; +expectTypeOf(owner.lookup(aConstName)).toBeUnknown(); + +// Check handling with Glimmer components carrying a Signature: they should +// properly resolve to `Owner`, *not* `Owner | undefined`. +interface Sig { + Args: { + name: string; + age: number; + extra: T; + }; + Element: HTMLParagraphElement; + Blocks: { + default: [greeting: string]; + extra: [T]; + }; +} + +class ExampleComponent extends Component> { + checkThis() { + expectTypeOf(getOwner(this)).toEqualTypeOf(); + } +} + +declare let example: ExampleComponent; +expectTypeOf(getOwner(example)).toEqualTypeOf(); + +// ----- Minimal further coverage for POJOs ----- // +// `Factory` and `FactoryManager` don't have to deal in actual classes. :sigh: +const Creatable = { + hasProps: true, +}; + +const pojoFactory: Factory = { + // If you want *real* safety here, alas: you cannot have it. The public + // contract for `create` allows implementors to define their `create` config + // object basically however they like. As a result, this is the safest version + // possible: Making it be `Partial` is *compatible* with `object`, and + // requires full checking *inside* the function body. It does not, alas, give + // any safety *outside* the class. A future rationalization of this would be + // very welcome. + create(initialValues?: Partial) { + const instance = Creatable; + if (initialValues) { + if (initialValues.hasProps) { + Object.defineProperty(instance, 'hasProps', { + value: initialValues.hasProps, + enumerable: true, + writable: true, + }); + } + } + return instance; + }, +}; + +expectTypeOf(pojoFactory.create()).toEqualTypeOf<{ hasProps: boolean }>(); diff --git a/type-tests/stable/index.d.ts b/type-tests/stable/index.d.ts new file mode 100644 index 00000000000..470164f7ef1 --- /dev/null +++ b/type-tests/stable/index.d.ts @@ -0,0 +1,2 @@ +// This is equivalent to do `import 'ember-source/types';`. +import '../../types/stable'; diff --git a/type-tests/stable/tsconfig.json b/type-tests/stable/tsconfig.json new file mode 100644 index 00000000000..a205f6579ab --- /dev/null +++ b/type-tests/stable/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/ember/tsconfig.json", +} diff --git a/types/preview/@ember/owner/index.d.ts b/types/preview/@ember/owner/index.d.ts deleted file mode 100644 index 559a31301b2..00000000000 --- a/types/preview/@ember/owner/index.d.ts +++ /dev/null @@ -1,423 +0,0 @@ -declare module '@ember/owner' { - /** - * The name for a factory consists of a namespace and the name of a specific - * type within that namespace, like `'service:session'`. - */ - export type FullName< - Type extends string = string, - Name extends string = string - > = `${Type}:${Name}`; - - /** - * A type registry for the DI system, which other participants in the DI system - * can register themselves into with declaration merging. The contract for this - * type is that its keys are the `Type` from a `FullName`, and each value for a - * `Type` is another registry whose keys are the `Name` from a `FullName`. The - * mechanic for providing a registry is [declaration merging][handbook]. - * - * [handbook]: https://www.typescriptlang.org/docs/handbook/declaration-merging.html - * - * For example, Ember's `Service` class uses this : - * - * ```ts - * export default class Service extends EmberObject {} - * - * // For concrete singleton classes to be merged into. - * interface Registry extends Record {} - * - * declare module '@ember/owner' { - * service: Registry; - * } - * ``` - * - * Declarations of services can then include the registry: - * - * ```ts - * import Service from '@ember/service'; - * - * export default class Session extends Service { - * login(username: string, password: string) { - * // ... - * } - * } - * - * declare module '@ember/service' { - * interface Registry { - * session: Session; - * } - * } - * ``` - * - * Then users of the `Owner` API will be able to reliably do things like this: - * - * ```ts - * getOwner(this)?.lookup('service:session').login("hello", "1234abcd"); - * ``` - */ - export interface DIRegistry extends Record> {} - - // Convenience utilities for pulling a specific factory manager off `DIRegistry` - // if one exists, or falling back to the default definition otherwise. - type ResolveFactoryManager< - Type extends ValidType, - Name extends ValidName - > = DIRegistry[Type][Name] extends object - ? FactoryManager - : FactoryManager | undefined; - - type ResolveFactory< - Type extends ValidType, - Name extends ValidName - > = DIRegistry[Type][Name] extends object - ? Factory - : Factory | object | undefined; - - // This type is shared between `Owner` and `RegistryProxy - interface BasicRegistry { - /** - Registers a factory that can be used for dependency injection (with - `inject`) or for service lookup. Each factory is registered with - a full name including two parts: `type:name`. - - A simple example: - - ```javascript - import Application from '@ember/application'; - import EmberObject from '@ember/object'; - - let App = Application.create(); - - App.Orange = EmberObject.extend(); - App.register('fruit:favorite', App.Orange); - ``` - - Ember will resolve factories from the `App` namespace automatically. - For example `App.CarsController` will be discovered and returned if - an application requests `controller:cars`. - - An example of registering a controller with a non-standard name: - - ```javascript - import Application from '@ember/application'; - import Controller from '@ember/controller'; - - let App = Application.create(); - let Session = Controller.extend(); - - App.register('controller:session', Session); - - // The Session controller can now be treated like a normal controller, - // despite its non-standard name. - App.ApplicationController = Controller.extend({ - needs: ['session'] - }); - ``` - - Registered factories are **instantiated** by having `create` - called on them. Additionally they are **singletons**, each time - they are looked up they return the same instance. - - Some examples modifying that default behavior: - - ```javascript - import Application from '@ember/application'; - import EmberObject from '@ember/object'; - - let App = Application.create(); - - App.Person = EmberObject.extend(); - App.Orange = EmberObject.extend(); - App.Email = EmberObject.extend(); - App.session = EmberObject.create(); - - App.register('model:user', App.Person, { singleton: false }); - App.register('fruit:favorite', App.Orange); - App.register('communication:main', App.Email, { singleton: false }); - App.register('session', App.session, { instantiate: false }); - ``` - - @method register - @param fullName {String} type:name (e.g., 'model:user') - @param factory {any} (e.g., App.Person) - @param options {Object} (optional) disable instantiation or singleton usage - @public - */ - // Dear future maintainer: yes, `Factory | object` is an exceedingly - // weird type here. We actually allow more or less *anything* to be passed - // here. In the future, we may possibly be able to update this to actually - // take advantage of the `FullName` here to require that the registered - // factory and corresponding options do the right thing (passing an *actual* - // factory, not needing `create` if `options.instantiate` is `false`, etc.) - // but doing so will require rationalizing Ember's own internals and may need - // a full Ember RFC. - register( - fullName: FullName, - factory: Factory | object, - options?: RegisterOptions - ): void; - } - - type ValidType = keyof DIRegistry & string; - type ValidName = keyof DIRegistry[Type] & string; - - interface BasicContainer { - /** - * Given a {@linkcode FullName} return a corresponding instance. - */ - lookup>( - fullName: FullName, - options?: RegisterOptions - ): DIRegistry[Type][Name]; - - /** - * Given a fullName of the form `'type:name'`, like `'route:application'`, - * return a corresponding factory manager. - * - * Any instances created via the factory's `.create()` method must be - * destroyed manually by the caller of `.create()`. Typically, this is done - * during the creating objects own `destroy` or `willDestroy` methods. - */ - factoryFor>( - fullName: FullName - ): ResolveFactoryManager; - } - - /** - * Framework objects in an Ember application (components, services, routes, - * etc.) are created via a factory and dependency injection system. Each of - * these objects is the responsibility of an "owner", which handles its - * instantiation and manages its lifetime. - */ - export default interface Owner extends BasicRegistry, BasicContainer {} - - export interface RegisterOptions { - instantiate?: boolean | undefined; - singleton?: boolean | undefined; - } - - /** - * Registered factories are instantiated by having create called on them. - * Additionally they are singletons by default, so each time they are looked up - * they return the same instance. - * - * However, that behavior can be modified with the `instantiate` and `singleton` - * options to the {@linkcode Owner.register} method. - */ - export interface Factory { - // NOTE: this does not check against the types of the target object in any - // way, unfortunately. However, we actually *cannot* constrain it further than - // this without going down a *very* deep rabbit hole (see the historic types - // for `.create()` on DefinitelyTyped if you're curious), because we need (for - // historical reasons) to support classes which implement this contract to be - // able to provide a *narrower* interface than "exactly the public fields on - // the class" while still falling back to the "exactly the public fields on - // the class" for the general case. :sigh: - /** - * A function that will create an instance of the class with any - * dependencies injected. - * - * @param initialValues Any values to set on an instance of the class - */ - create(initialValues?: object): T; - } - - /** - * A manager which can be used for introspection of the factory's class or for - * the creation of factory instances with initial properties. The manager is an - * object with the following properties: - * - * - `class` - The registered or resolved class. - * - `create` - A function that will create an instance of the class with any - * dependencies injected. - * - * @note `FactoryManager` is *not* user-constructible; the only legal way to get - * a `FactoryManager` is via {@linkcode Owner.factoryFor}. - */ - export interface FactoryManager extends Factory { - /** The registered or resolved class. */ - readonly class: Factory; - } - - /** - * A record mapping all known items of a given type: if the item is known it - * will be `true`; otherwise it will be `false` or `undefined`. - */ - export type KnownForTypeResult = { - [FullName in `${Type}:${string}`]: boolean | undefined; - }; - - /** - * A `Resolver` is the mechanism responsible for looking up code in your - * application and converting its naming conventions into the actual classes, - * functions, and templates that Ember needs to resolve its dependencies, for - * example, what template to render for a given route. It is a system that helps - * the app resolve the lookup of JavaScript modules agnostic of what kind of - * module system is used, which can be AMD, CommonJS or just plain globals. It - * is used to lookup routes, models, components, templates, or anything that is - * used in your Ember app. - * - * This interface represents the contract a custom resolver must implement. Most - * apps never need to think about this: the application's resolver is supplied by - * `ember-resolver` in the default blueprint. - */ - export interface Resolver { - resolve: >( - name: FullName - ) => ResolveFactory; - knownForType?: (type: Type) => KnownForTypeResult; - lookupDescription?: (fullName: FullName) => string; - makeToString?: (factory: Factory, fullName: FullName) => string; - normalize?: (fullName: FullName) => FullName; - } - - /** - * Framework objects in an Ember application (components, services, routes, etc.) - * are created via a factory and dependency injection system. Each of these - * objects is the responsibility of an "owner", which handled its - * instantiation and manages its lifetime. - * - * `getOwner` fetches the owner object responsible for an instance. This can - * be used to lookup or resolve other class instances, or register new factories - * into the owner. - * - * For example, this component dynamically looks up a service based on the - * `audioType` passed as an argument: - * - * ```app/components/play-audio.js - * import Component from '@glimmer/component'; - * import { action } from '@ember/object'; - * import { getOwner } from '@ember/application'; - * - * // Usage: - * // - * // - * // - * export default class PlayAudio extends Component { - * get audioService() { - * return getOwner(this)?.lookup(`service:${this.args.audioType}`); - * } - * - * @action - * onPlay() { - * this.audioService?.play(this.args.audioFile); - * } - * } - * ``` - */ - export function getOwner(object: object): Owner | undefined; - - /** - * `setOwner` forces a new owner on a given object instance. This is primarily - * useful in some testing cases. - * - * @param object An object instance. - * @param owner The new owner object of the object instance. - */ - export function setOwner(object: object, owner: Owner): void; - - export interface ContainerProxy extends BasicContainer { - /** - * Returns an object that can be used to provide an owner to a - * manually created instance. - * - * Example: - * - * ``` - * import { getOwner } from '@ember/application'; - * - * let owner = getOwner(this); - * - * User.create( - * owner.ownerInjection(), - * { username: 'rwjblue' } - * ) - * ``` - */ - ownerInjection(): object; - } - - export interface RegistryProxy extends BasicRegistry { - /** - * Given a fullName return the corresponding factory. - */ - resolveRegistration(fullName: FullName): Factory | object | undefined; - - /** - * Unregister a factory. - * - * - * ```javascript - * import Application from '@ember/application'; - * import EmberObject from '@ember/object'; - * let App = Application.create(); - * let User = EmberObject.extend(); - * App.register('model:user', User); - * - * App.resolveRegistration('model:user').create() instanceof User //=> true - * - * App.unregister('model:user') - * App.resolveRegistration('model:user') === undefined //=> true - * ``` - */ - unregister(fullName: FullName): void; - - /** - * Check if a factory is registered. - */ - hasRegistration(fullName: FullName): boolean; - - /** - * Return a specific registered option for a particular factory. - */ - registeredOption( - fullName: FullName, - optionName: K - ): RegisterOptions[K] | undefined; - - /** - * Register options for a particular factory. - */ - registerOptions(fullName: FullName, options: RegisterOptions): void; - - /** - * Return registered options for a particular factory. - */ - registeredOptions(fullName: FullName): RegisterOptions | undefined; - - /** - * Allow registering options for all factories of a type. - * - * ```javascript - * import Application from '@ember/application'; - * - * let App = Application.create(); - * let appInstance = App.buildInstance(); - * - * // if all of type `connection` must not be singletons - * appInstance.registerOptionsForType('connection', { singleton: false }); - * - * appInstance.register('connection:twitter', TwitterConnection); - * appInstance.register('connection:facebook', FacebookConnection); - * - * let twitter = appInstance.lookup('connection:twitter'); - * let twitter2 = appInstance.lookup('connection:twitter'); - * - * twitter === twitter2; // => false - * - * let facebook = appInstance.lookup('connection:facebook'); - * let facebook2 = appInstance.lookup('connection:facebook'); - * - * facebook === facebook2; // => false - * ``` - */ - registerOptionsForType(type: string, options: RegisterOptions): void; - - /** - * Return the registered options for all factories of a type. - */ - registeredOptionsForType(type: string): RegisterOptions | undefined; - } - - // Don't export things unless we *intend* to. - export {}; -} diff --git a/types/preview/@ember/owner/tsconfig.json b/types/preview/@ember/owner/tsconfig.json deleted file mode 100644 index 4082f16a5d9..00000000000 --- a/types/preview/@ember/owner/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.json" -} diff --git a/types/preview/index.d.ts b/types/preview/index.d.ts index 757259080c9..bf1fb1411b1 100644 --- a/types/preview/index.d.ts +++ b/types/preview/index.d.ts @@ -91,8 +91,6 @@ import './@ember/object/observers'; import './@ember/object/promise-proxy-mixin'; import './@ember/object/proxy'; -import './@ember/owner'; - import './@ember/polyfills'; import './@ember/polyfills/types'; diff --git a/types/publish.mjs b/types/publish.mjs index 86fd2666f96..25478f82cc3 100755 --- a/types/publish.mjs +++ b/types/publish.mjs @@ -149,7 +149,6 @@ const PREVIEW_MODULES = [ '@ember/-internals/metal/lib/tags.d.ts', '@ember/-internals/metal/lib/tracked.d.ts', '@ember/-internals/overrides/index.d.ts', - '@ember/-internals/owner/index.d.ts', '@ember/-internals/routing/index.d.ts', '@ember/-internals/runtime/index.d.ts', '@ember/-internals/runtime/lib/ext/rsvp.d.ts', @@ -243,7 +242,6 @@ const PREVIEW_MODULES = [ '@ember/object/observers.d.ts', '@ember/object/promise-proxy-mixin.d.ts', '@ember/object/proxy.d.ts', - '@ember/owner/index.d.ts', '@ember/polyfills/index.d.ts', '@ember/polyfills/lib/assign.d.ts', '@ember/renderer/index.d.ts', @@ -457,8 +455,8 @@ function wrapInDeclareModule(moduleName) { // that will be, it will be *way* more reliable. let string = new MagicString(contents); string - .replaceAll(/^export declare /gm, 'export ') // g for global, m for multiline - .replaceAll(/^declare /gm, '') // g for global, m for multiline + .replace(/^export declare /gm, 'export ') // g for global, m for multiline + .replace(/^declare /gm, '') // g for global, m for multiline .indent(' ') .prepend(`declare module '${moduleNameForDeclaration}' {\n`) .append('}\n');