From 72bf78476d2e112b380db9c529b953199610b639 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Thu, 23 Feb 2023 13:55:00 -0700 Subject: [PATCH] [CLEANUP] Remove deprecated `AutoLocation` class Replace `Location` object with an interface. We no longer need the object (it does not do anything) and in fact exporting it would just troll people. However, we *do* need the interface internally, and it is useful to allow people to name the 'any Location type' type, so export that in its place. Along the way, delete a test which prevented the DI registry type from working correctly in Ember's internals, and simplify a few of the type lookups there. --- packages/@ember/-internals/owner/index.ts | 29 +- .../-internals/owner/type-tests/owner.test.ts | 2 +- packages/@ember/application/index.ts | 2 - .../application/tests/application_test.js | 1 - packages/@ember/routing/auto-location.ts | 375 ----------------- packages/@ember/routing/hash-location.ts | 8 +- packages/@ember/routing/history-location.ts | 8 +- packages/@ember/routing/lib/location-utils.ts | 46 --- packages/@ember/routing/location.ts | 185 +++++---- packages/@ember/routing/none-location.ts | 8 +- packages/@ember/routing/router-service.ts | 9 +- packages/@ember/routing/router.ts | 72 +--- .../tests/location/auto_location_test.js | 380 ------------------ .../routing/tests/location/util_test.js | 84 ---- .../routing/tests/system/router_test.js | 63 --- .../routing/type-tests/auto-location.test.ts | 14 - .../routing/type-tests/hash-location.test.ts | 4 +- .../type-tests/history-location.test.ts | 4 +- .../routing/type-tests/location.test.ts | 37 +- .../routing/type-tests/none-location.test.ts | 4 +- .../routing/type-tests/router-service.test.ts | 2 +- .../routing/type-tests/router/index.test.ts | 4 +- packages/@ember/service/index.ts | 2 +- packages/ember/index.ts | 8 - packages/ember/tests/reexports_test.js | 6 - tests/docs/expected.js | 5 +- .../preview/@ember/routing/auto-location.d.ts | 8 - .../@ember/routing/router-service.d.ts | 8 + types/preview/@ember/routing/router.d.ts | 10 +- 29 files changed, 205 insertions(+), 1183 deletions(-) delete mode 100644 packages/@ember/routing/auto-location.ts delete mode 100644 packages/@ember/routing/tests/location/auto_location_test.js delete mode 100644 packages/@ember/routing/type-tests/auto-location.test.ts delete mode 100644 types/preview/@ember/routing/auto-location.d.ts diff --git a/packages/@ember/-internals/owner/index.ts b/packages/@ember/-internals/owner/index.ts index 5afaa31edb1..919f386f386 100644 --- a/packages/@ember/-internals/owner/index.ts +++ b/packages/@ember/-internals/owner/index.ts @@ -72,19 +72,28 @@ export type FullName< @private */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DIRegistry extends Record> {} +export interface DIRegistry {} // Convenience utility for pulling a specific factory manager off `DIRegistry` // if one exists, or falling back to the default definition otherwise. /** @private */ -type ResolveFactoryManager< - Type extends string, - Name extends string -> = DIRegistry[Type][Name] extends infer RegistryEntry extends object - ? FactoryManager - : FactoryManager | undefined; +type ResolveFactoryManager = Type extends ValidType + ? Name extends ValidName + ? DIRegistry[Type][Name] extends infer RegistryEntry extends object + ? FactoryManager + : FactoryManagerDefault + : FactoryManagerDefault + : FactoryManagerDefault; + +type FactoryManagerDefault = FactoryManager | undefined; + +type Lookup = Type extends ValidType + ? Name extends ValidName + ? DIRegistry[Type][Name] + : unknown + : unknown; /** The common interface for the ability to `register()` an item, shared by the @@ -230,10 +239,10 @@ interface BasicContainer { @param {RegisterOptions} options @return {any} */ - lookup>( + lookup( fullName: FullName, options?: RegisterOptions - ): DIRegistry[Type][Name]; + ): Lookup; /** Given a `FullName`, of the form `"type:name"` return a `FactoryManager`. @@ -277,7 +286,7 @@ interface BasicContainer { @param {string} fullName @return {FactoryManager} */ - factoryFor>( + factoryFor( fullName: FullName ): ResolveFactoryManager; } diff --git a/packages/@ember/-internals/owner/type-tests/owner.test.ts b/packages/@ember/-internals/owner/type-tests/owner.test.ts index 9bf17d7c487..96762e3cf9f 100644 --- a/packages/@ember/-internals/owner/type-tests/owner.test.ts +++ b/packages/@ember/-internals/owner/type-tests/owner.test.ts @@ -99,7 +99,7 @@ owner.lookup('non-namespace-string'); expectTypeOf(owner.lookup('namespace@type:name')).toEqualTypeOf(); // Arbitrary registration patterns work, as here. -declare module '@ember/-internals/owner' { +declare module '@ember/owner' { export interface DIRegistry { etc: { 'my-type-test': ConstructThis; diff --git a/packages/@ember/application/index.ts b/packages/@ember/application/index.ts index 3594bc551e9..79a13f43b21 100644 --- a/packages/@ember/application/index.ts +++ b/packages/@ember/application/index.ts @@ -15,7 +15,6 @@ import { RSVP } from '@ember/-internals/runtime'; import { EventDispatcher } from '@ember/-internals/views'; import Route from '@ember/routing/route'; import Router from '@ember/routing/router'; -import AutoLocation from '@ember/routing/auto-location'; import HashLocation from '@ember/routing/hash-location'; import HistoryLocation from '@ember/routing/history-location'; import NoneLocation from '@ember/routing/none-location'; @@ -1205,7 +1204,6 @@ function commonSetupRegistry(registry: Registry) { registry.register('route:basic', Route); registry.register('event_dispatcher:main', EventDispatcher); - registry.register('location:auto', AutoLocation); registry.register('location:hash', HashLocation); registry.register('location:history', HistoryLocation); registry.register('location:none', NoneLocation); diff --git a/packages/@ember/application/tests/application_test.js b/packages/@ember/application/tests/application_test.js index f3281cbaefd..71fdd5c2fa7 100644 --- a/packages/@ember/application/tests/application_test.js +++ b/packages/@ember/application/tests/application_test.js @@ -128,7 +128,6 @@ moduleFor( verifyRegistration(assert, application, 'route:basic'); verifyRegistration(assert, application, 'event_dispatcher:main'); - verifyRegistration(assert, application, 'location:auto'); verifyRegistration(assert, application, 'location:hash'); verifyRegistration(assert, application, 'location:history'); verifyRegistration(assert, application, 'location:none'); diff --git a/packages/@ember/routing/auto-location.ts b/packages/@ember/routing/auto-location.ts deleted file mode 100644 index 0deb19bacf9..00000000000 --- a/packages/@ember/routing/auto-location.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { history, location, userAgent, window } from '@ember/-internals/browser-environment'; -import { getOwner } from '@ember/-internals/owner'; -import EmberObject, { set } from '@ember/object'; -import type { AnyFn, MethodNamesOf } from '@ember/-internals/utility-types'; -import { assert } from '@ember/debug'; -import type { ILocation as EmberLocation, UpdateCallback } from '@ember/routing/location'; -import { - getFullPath, - getHash, - getPath, - getQuery, - replacePath, - supportsHashChange, - supportsHistory, -} from './lib/location-utils'; - -/** -@module @ember/routing/auto-location -*/ - -/** - AutoLocation will select the best location option based off browser - support with the priority order: history, hash, none. - - Clean pushState paths accessed by hashchange-only browsers will be redirected - to the hash-equivalent and vice versa so future transitions are consistent. - - Keep in mind that since some of your users will use `HistoryLocation`, your - server must serve the Ember app at all the routes you define. - - Browsers that support the `history` API will use `HistoryLocation`, those that - do not, but still support the `hashchange` event will use `HashLocation`, and - in the rare case neither is supported will use `NoneLocation`. - - Example: - - ```app/router.js - Router.map(function() { - this.route('posts', function() { - this.route('new'); - }); - }); - - Router.reopen({ - location: 'auto' - }); - ``` - - This will result in a posts.new url of `/posts/new` for modern browsers that - support the `history` api or `/#/posts/new` for older ones, like Internet - Explorer 9 and below. - - When a user visits a link to your application, they will be automatically - upgraded or downgraded to the appropriate `Location` class, with the URL - transformed accordingly, if needed. - - Keep in mind that since some of your users will use `HistoryLocation`, your - server must serve the Ember app at all the routes you define. - - @class AutoLocation - @static - @protected -*/ -export default class AutoLocation extends EmberObject implements EmberLocation { - declare getURL: () => string; - declare setURL: (url: string) => void; - declare onUpdateURL: (callback: UpdateCallback) => void; - declare formatURL: (url: string) => string; - - concreteImplementation?: EmberLocation; - - implementation = 'auto'; - - // FIXME: This is never set - // See https://github.com/emberjs/ember.js/issues/19515 - /** @internal */ - documentMode: number | undefined; - - /** - @private - - Will be pre-pended to path upon state change. - - @since 1.5.1 - @property rootURL - @default '/' - */ - // Added in reopen to allow overriding via extend - declare rootURL: string; - - /** - @private - - The browser's `location` object. This is typically equivalent to - `window.location`, but may be overridden for testing. - - @property location - @default environment.location - */ - // Added in reopen to allow overriding via extend - declare location: Location; - - /** - @private - - The browser's `history` object. This is typically equivalent to - `window.history`, but may be overridden for testing. - - @since 1.5.1 - @property history - @default environment.history - */ - // Added in reopen to allow overriding via extend - declare history: any; - - /** - @private - - The user agent's global variable. In browsers, this will be `window`. - - @since 1.11 - @property global - @default window - */ - // Added in reopen to allow overriding via extend - declare global: any; - - /** - @private - - The browser's `userAgent`. This is typically equivalent to - `navigator.userAgent`, but may be overridden for testing. - - @since 1.5.1 - @property userAgent - @default environment.history - */ - // Added in reopen to allow overriding via extend - declare userAgent: string; - - /** - @private - - This property is used by the router to know whether to cancel the routing - setup process, which is needed while we redirect the browser. - - @since 1.5.1 - @property cancelRouterSetup - @default false - */ - // Added in reopen to allow overriding via extend - declare cancelRouterSetup: boolean; - - /** - Called by the router to instruct the location to do any feature detection - necessary. In the case of AutoLocation, we detect whether to use history - or hash concrete implementations. - - @private - */ - detect(): void { - let rootURL = this.rootURL; - - assert( - 'rootURL must end with a trailing forward slash e.g. "/app/"', - rootURL.charAt(rootURL.length - 1) === '/' - ); - - let implementation = detectImplementation({ - location: this.location, - history: this.history, - userAgent: this.userAgent, - rootURL, - documentMode: this.documentMode, - global: this.global, - }); - - if (implementation === false) { - set(this, 'cancelRouterSetup', true); - implementation = 'none'; - } - - let owner = getOwner(this); - assert('AutoLocation is unexpectedly missing an owner', owner); - let concrete = owner.lookup(`location:${implementation}`) as EmberLocation; - assert(`Could not find location '${implementation}'.`, concrete !== undefined); - - set(concrete, 'rootURL', rootURL); - set(this, 'concreteImplementation', concrete); - } - - willDestroy(): void { - let { concreteImplementation } = this; - - if (concreteImplementation) { - concreteImplementation.destroy(); - } - } -} - -AutoLocation.reopen({ - rootURL: '/', - - initState: delegateToConcreteImplementation('initState'), - getURL: delegateToConcreteImplementation('getURL'), - setURL: delegateToConcreteImplementation('setURL'), - replaceURL: delegateToConcreteImplementation('replaceURL'), - onUpdateURL: delegateToConcreteImplementation('onUpdateURL'), - formatURL: delegateToConcreteImplementation('formatURL'), - - location: location, - - history: history, - - global: window, - - userAgent: userAgent, - - cancelRouterSetup: false, -}); - -function delegateToConcreteImplementation>>( - methodName: N -) { - return function (this: AutoLocation, ...args: Parameters[N]>) { - let { concreteImplementation } = this; - assert( - "AutoLocation's detect() method should be called before calling any other hooks.", - concreteImplementation - ); - - // We need this cast because `Parameters` is deferred so that it is not - // possible for TS to see it will always produce the right type. However, - // since `AnyFn` has a rest type, it is allowed. See discussion on [this - // issue](https://github.com/microsoft/TypeScript/issues/47615). - return (concreteImplementation[methodName] as AnyFn | undefined)?.(...args); - }; -} - -/* - Given the browser's `location`, `history` and `userAgent`, and a configured - root URL, this function detects whether the browser supports the [History - API](https://developer.mozilla.org/en-US/docs/Web/API/History) and returns a - string representing the Location object to use based on its determination. - - For example, if the page loads in an evergreen browser, this function would - return the string "history", meaning the history API and thus HistoryLocation - should be used. If the page is loaded in IE8, it will return the string - "hash," indicating that the History API should be simulated by manipulating the - hash portion of the location. - -*/ - -interface DetectionOptions { - location: Location; - history: History; - userAgent: string; - rootURL: string; - documentMode: number | undefined; - global: Window | null; -} - -function detectImplementation(options: DetectionOptions) { - let { location, userAgent, history, documentMode, global, rootURL } = options; - - let implementation = 'none'; - let cancelRouterSetup = false; - let currentPath = getFullPath(location); - - if (supportsHistory(userAgent, history)) { - let historyPath = getHistoryPath(rootURL, location); - - // If the browser supports history and we have a history path, we can use - // the history location with no redirects. - if (currentPath === historyPath) { - implementation = 'history'; - } else if (currentPath.substring(0, 2) === '/#') { - history.replaceState({ path: historyPath }, '', historyPath); - implementation = 'history'; - } else { - cancelRouterSetup = true; - replacePath(location, historyPath); - } - } else if (supportsHashChange(documentMode, global)) { - let hashPath = getHashPath(rootURL, location); - - // Be sure we're using a hashed path, otherwise let's switch over it to so - // we start off clean and consistent. We'll count an index path with no - // hash as "good enough" as well. - if (currentPath === hashPath || (currentPath === '/' && hashPath === '/#/')) { - implementation = 'hash'; - } else { - // Our URL isn't in the expected hash-supported format, so we want to - // cancel the router setup and replace the URL to start off clean - cancelRouterSetup = true; - replacePath(location, hashPath); - } - } - - if (cancelRouterSetup) { - return false; - } - - return implementation; -} - -/** - @private - - Returns the current path as it should appear for HistoryLocation supported - browsers. This may very well differ from the real current path (e.g. if it - starts off as a hashed URL) -*/ -export function getHistoryPath(rootURL: string, location: Location): string { - let path = getPath(location); - let hash = getHash(location); - let query = getQuery(location); - let rootURLIndex = path.indexOf(rootURL); - - let routeHash: string; - let hashParts; - - assert(`Path ${path} does not start with the provided rootURL ${rootURL}`, rootURLIndex === 0); - - // By convention, Ember.js routes using HashLocation are required to start - // with `#/`. Anything else should NOT be considered a route and should - // be passed straight through, without transformation. - if (hash.substring(0, 2) === '#/') { - // There could be extra hash segments after the route - hashParts = hash.substring(1).split('#'); - // The first one is always the route url - routeHash = hashParts.shift() as string; - - // If the path already has a trailing slash, remove the one - // from the hashed route so we don't double up. - if (path.charAt(path.length - 1) === '/') { - routeHash = routeHash.substring(1); - } - - // This is the "expected" final order - path += routeHash + query; - - if (hashParts.length) { - path += `#${hashParts.join('#')}`; - } - } else { - path += query + hash; - } - - return path; -} - -/** - @private - - Returns the current path as it should appear for HashLocation supported - browsers. This may very well differ from the real current path. - - @method _getHashPath -*/ -export function getHashPath(rootURL: string, location: Location): string { - let path = rootURL; - let historyPath = getHistoryPath(rootURL, location); - let routePath = historyPath.substring(rootURL.length); - - if (routePath !== '') { - if (routePath[0] !== '/') { - routePath = `/${routePath}`; - } - - path += `#${routePath}`; - } - - return path; -} diff --git a/packages/@ember/routing/hash-location.ts b/packages/@ember/routing/hash-location.ts index b545e203d9d..aa7d3b35f3a 100644 --- a/packages/@ember/routing/hash-location.ts +++ b/packages/@ember/routing/hash-location.ts @@ -1,6 +1,6 @@ import EmberObject, { set } from '@ember/object'; import { bind } from '@ember/runloop'; -import type { ILocation as EmberLocation, UpdateCallback } from '@ember/routing/location'; +import type { Location as EmberLocation, UpdateCallback } from '@ember/routing/location'; import { getHash } from './lib/location-utils'; /** @@ -174,3 +174,9 @@ export default class HashLocation extends EmberObject implements EmberLocation { } } } + +declare module '@ember/routing/location' { + export interface Registry { + hash: HashLocation; + } +} diff --git a/packages/@ember/routing/history-location.ts b/packages/@ember/routing/history-location.ts index 8d459888511..2ab98310bce 100644 --- a/packages/@ember/routing/history-location.ts +++ b/packages/@ember/routing/history-location.ts @@ -1,5 +1,5 @@ import EmberObject, { set } from '@ember/object'; -import type { ILocation as EmberLocation, UpdateCallback } from '@ember/routing/location'; +import type { Location as EmberLocation, UpdateCallback } from '@ember/routing/location'; import { getHash } from './lib/location-utils'; /** @@ -279,3 +279,9 @@ export default class HistoryLocation extends EmberObject implements EmberLocatio } } } + +declare module '@ember/routing/location' { + export interface Registry { + history: HistoryLocation; + } +} diff --git a/packages/@ember/routing/lib/location-utils.ts b/packages/@ember/routing/lib/location-utils.ts index 5817ff2a0c1..64dd6e3a705 100644 --- a/packages/@ember/routing/lib/location-utils.ts +++ b/packages/@ember/routing/lib/location-utils.ts @@ -54,52 +54,6 @@ export function getOrigin(location: Location): string { return origin; } -/* - `documentMode` only exist in Internet Explorer, and it's tested because IE8 running in - IE7 compatibility mode claims to support `onhashchange` but actually does not. - - `global` is an object that may have an `onhashchange` property. - - @private - @function supportsHashChange -*/ -export function supportsHashChange( - documentMode: number | undefined, - global: Window | null -): boolean { - return Boolean( - global && 'onhashchange' in global && (documentMode === undefined || documentMode > 7) - ); -} - -/* - `userAgent` is a user agent string. We use user agent testing here, because - the stock Android browser is known to have buggy versions of the History API, - in some Android versions. - - @private - @function supportsHistory -*/ -export function supportsHistory(userAgent: string, history: History): boolean { - // Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js - // The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support - // Unfortunately support is really buggy and there is no clean way to detect - // these bugs, so we fall back to a user agent sniff :( - - // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies - // itself as 'Mobile Safari' as well, nor Windows Phone. - if ( - (userAgent.indexOf('Android 2.') !== -1 || userAgent.indexOf('Android 4.0') !== -1) && - userAgent.indexOf('Mobile Safari') !== -1 && - userAgent.indexOf('Chrome') === -1 && - userAgent.indexOf('Windows Phone') === -1 - ) { - return false; - } - - return Boolean(history && 'pushState' in history); -} - /** Replaces the current location, making sure we explicitly include the origin to prevent redirecting to a different origin. diff --git a/packages/@ember/routing/location.ts b/packages/@ember/routing/location.ts index 0138328c497..43b6b40eb06 100644 --- a/packages/@ember/routing/location.ts +++ b/packages/@ember/routing/location.ts @@ -1,53 +1,37 @@ -import { assert, deprecate } from '@ember/debug'; - -export interface ILocation { - implementation: string; - cancelRouterSetup?: boolean; - getURL(): string; - setURL(url: string): void; - replaceURL?(url: string): void; - onUpdateURL(callback: UpdateCallback): void; - formatURL(url: string): string; - detect?(): void; - initState?(): void; - destroy(): void; -} - -export type UpdateCallback = (url: string) => void; /** -@module @ember/routing/location + @module @ember/routing/location */ /** - Location returns an instance of the correct implementation of - the `location` API. + `Location` defines an interface to be implemented by `location` APIs. It is + not user-constructible; the only valid way to get a `Location` is via one of + its concrete implementations. ## Implementations - You can pass an implementation name (`hash`, `history`, `none`, `auto`) to force a + You can pass an implementation name (`hash`, `history`, `none`) to force a particular implementation to be used in your application. - See [HashLocation](/ember/release/classes/HashLocation). - See [HistoryLocation](/ember/release/classes/HistoryLocation). - See [NoneLocation](/ember/release/classes/NoneLocation). - See [AutoLocation](/ember/release/classes/AutoLocation). + - See [HashLocation](/ember/release/classes/HashLocation). + - See [HistoryLocation](/ember/release/classes/HistoryLocation). + - See [NoneLocation](/ember/release/classes/NoneLocation). ## Location API Each location implementation must provide the following methods: - * implementation: returns the string name used to reference the implementation. - * getURL: returns the current URL. - * setURL(path): sets the current URL. - * replaceURL(path): replace the current URL (optional). - * onUpdateURL(callback): triggers the callback when the URL changes. - * formatURL(url): formats `url` to be placed into `href` attribute. - * detect() (optional): instructs the location to do any feature detection + * `implementation`: returns the string name used to reference the implementation. + * `getURL`: returns the current URL. + * `setURL(path)`: sets the current URL. + * `replaceURL(path)`: replace the current URL (optional). + * `onUpdateURL(callback)`: triggers the callback when the URL changes. + * `formatURL(url)`: formats `url` to be placed into `href` attribute. + * `detect()` (optional): instructs the location to do any feature detection necessary. If the location needs to redirect to a different URL, it can cancel routing by setting the `cancelRouterSetup` property on itself to `false`. - Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + Calling `setURL` or `replaceURL` will not trigger onUpdateURL callbacks. ## Custom implementation @@ -75,56 +59,93 @@ export type UpdateCallback = (url: string) => void; } ``` + @for @ember/routing/location @class Location - @private + @since 5.0.0 + @public */ -export default { +export interface Location { + /** + * Returns the string name used to reference the implementation. + * + * @property implementation + * @type String + * @public + */ + implementation: keyof Registry & string; + /** - This is deprecated in favor of using the container to lookup the location - implementation as desired. - - For example: - - ```javascript - // Given a location registered as follows: - container.register('location:history-test', HistoryTestLocation); - - // You could create a new instance via: - container.lookup('location:history-test'); - ``` - - @method create - @param {Object} options - @return {Object} an instance of an implementation of the `location` API - @deprecated Use the container to lookup the location implementation that you - need. - @private - */ - create(options?: { implementation: string }): ILocation { - let implementation = options?.implementation; - assert("Location.create: you must specify a 'implementation' option", implementation); - - let implementationClass = this.implementations[implementation]; - - assert(`Location.create: ${implementation} is not a valid implementation`, implementationClass); - - deprecate( - "Calling `create` on Location class is deprecated. Instead, use `container.lookup('location:my-location')` to lookup the location you need.", - false, - { - id: 'deprecate-auto-location', - until: '5.0.0', - url: 'https://emberjs.com/deprecations/v4.x#toc_deprecate-auto-location', - for: 'ember-source', - since: { - available: '4.1.0', - enabled: '4.1.0', - }, - } - ); - - return implementationClass.create(...arguments); - }, - - implementations: {} as Record ILocation }>, -}; + * If the location needs to redirect to a different URL, it can cancel routing + * by setting the `cancelRouterSetup` property on itself to false. + * @property cancelRouterSetup + * @type Boolean + * @optional + * @default true + * @public + */ + cancelRouterSetup?: boolean; + + /** + * The current URL. + * @property + * @type String + * @public + */ + getURL(): string; + + /** + * Sets the current URL. Calling `setURL` will not trigger `onUpdateURL` + * callbacks. + * + * @public + * @method + * @param {String} url the new URL to update to. + */ + setURL(url: string): void; + + /** + * Replace the current URL (optional). Calling `replaceURL` will not trigger + * `onUpdateURL` callbacks. + * + * @public + * @method + * @param {String} url the new URL to replace the current URL with. + */ + replaceURL?(url: string): void; + + /** + * triggers the callback when the URL changes. + * @param {(newUrl: string) => void} callback A function to run when the URL + * changes. The the new URL string is provided as the only argument. + */ + onUpdateURL(callback: UpdateCallback): void; + + /** + * Formats url to be placed into href attribute. + * + * @public + * @method + * @param {String} url the url to format + */ + formatURL(url: string): string; + /** + * Instructs the location to do any feature detection necessary. If the + * location needs to redirect to a different URL, it can cancel routing by + * setting the cancelRouterSetup property on itself to false. + */ + detect?(): void; + + initState?(): void; + destroy(): void; +} + +export type UpdateCallback = (url: string) => void; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Registry extends Record {} + +declare module '@ember/owner' { + export interface DIRegistry { + location: Registry; + } +} diff --git a/packages/@ember/routing/none-location.ts b/packages/@ember/routing/none-location.ts index 23202fd56c1..5939920666c 100644 --- a/packages/@ember/routing/none-location.ts +++ b/packages/@ember/routing/none-location.ts @@ -1,6 +1,6 @@ import EmberObject, { set } from '@ember/object'; import { assert } from '@ember/debug'; -import type { ILocation as EmberLocation, UpdateCallback } from '@ember/routing/location'; +import type { Location as EmberLocation, UpdateCallback } from '@ember/routing/location'; /** @module @ember/routing/none-location @@ -133,3 +133,9 @@ NoneLocation.reopen({ path: '', rootURL: '/', }); + +declare module '@ember/routing/location' { + export interface Registry { + none: NoneLocation; + } +} diff --git a/packages/@ember/routing/router-service.ts b/packages/@ember/routing/router-service.ts index 139331df1d0..8e6d9e555da 100644 --- a/packages/@ember/routing/router-service.ts +++ b/packages/@ember/routing/router-service.ts @@ -668,12 +668,11 @@ class RouterService extends Service.extend(Evented) { ``` The following location types are available by default: - `auto`, `hash`, `history`, `none`. + `hash`, `history`, `none`. See [HashLocation](/ember/release/classes/HashLocation). See [HistoryLocation](/ember/release/classes/HistoryLocation). See [NoneLocation](/ember/release/classes/NoneLocation). - See [AutoLocation](/ember/release/classes/AutoLocation). @property location @default 'hash' @@ -746,3 +745,9 @@ class RouterService extends Service.extend(Evented) { } export { RouterService as default, RouteInfo, RouteInfoWithAttributes }; + +declare module '@ember/service' { + interface Registry { + router: RouterService; + } +} diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index 1aa886edc9f..99d6c208d84 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -16,13 +16,16 @@ import { resemblesURL, } from './lib/utils'; import type { RouteArgs, RouteOptions } from './lib/utils'; -import EmberLocation, { type ILocation as IEmberLocation } from '@ember/routing/location'; +import { + type Location as EmberLocation, + type Registry as LocationRegistry, +} from '@ember/routing/location'; import type RouterService from '@ember/routing/router-service'; import EmberObject from '@ember/object'; import { A as emberA } from '@ember/array'; import { typeOf } from '@ember/utils'; import Evented from '@ember/object/evented'; -import { assert, deprecate, info } from '@ember/debug'; +import { assert, info } from '@ember/debug'; import { cancel, once, run, scheduleOnce } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; import type { QueryParamMeta, RenderOptions } from '@ember/routing/route'; @@ -182,7 +185,7 @@ class EmberRouter extends EmberObject.extend(Evented) i @public */ // Set with reopen to allow overriding via extend - declare location: string | IEmberLocation; + declare location: (keyof LocationRegistry & string) | EmberLocation; _routerMicrolib!: Router; _didSetupRouter = false; @@ -328,7 +331,7 @@ class EmberRouter extends EmberObject.extend(Evented) i } _initRouterJs(): void { - let location = get(this, 'location') as IEmberLocation; + let location = get(this, 'location') as EmberLocation; let router = this; const owner = getOwner(this); assert('Router is unexpectedly missing an owner', owner); @@ -570,7 +573,7 @@ class EmberRouter extends EmberObject.extend(Evented) i if (this.setupRouter()) { let initialURL = get(this, 'initialURL') as string | undefined; if (initialURL === undefined) { - initialURL = (get(this, 'location') as IEmberLocation).getURL(); + initialURL = (get(this, 'location') as EmberLocation).getURL(); } let initialTransition = this.handleURL(initialURL); if (initialTransition && initialTransition.error) { @@ -586,7 +589,7 @@ class EmberRouter extends EmberObject.extend(Evented) i this._didSetupRouter = true; this._setupLocation(); - let location = get(this, 'location') as IEmberLocation; + let location = get(this, 'location') as EmberLocation; // Allow the Location class to cancel the router setup while it refreshes // the page @@ -899,35 +902,10 @@ class EmberRouter extends EmberObject.extend(Evented) i assert('Router is unexpectedly missing an owner', owner); if ('string' === typeof location) { - let resolvedLocation = owner.lookup(`location:${location}`) as IEmberLocation; - - if (location === 'auto') { - deprecate( - "Router location 'auto' is deprecated. Most users will want to set `locationType` to 'history' in config/environment.js for no change in behavior. See deprecation docs for details.", - false, - { - id: 'deprecate-auto-location', - until: '5.0.0', - url: 'https://emberjs.com/deprecations/v4.x#toc_deprecate-auto-location', - for: 'ember-source', - since: { - available: '4.1.0', - enabled: '4.1.0', - }, - } - ); - } + let resolvedLocation = owner.lookup(`location:${location}`); - if (resolvedLocation !== undefined) { - location = set(this, 'location', resolvedLocation); - } else { - // Allow for deprecated registration of custom location API's - let options = { - implementation: location, - }; - - location = set(this, 'location', EmberLocation.create(options)); - } + assert(`Could not resolve a location class at 'location:${location}'`, resolvedLocation); + location = set(this, 'location', resolvedLocation); } if (location !== null && typeof location === 'object') { @@ -935,30 +913,6 @@ class EmberRouter extends EmberObject.extend(Evented) i set(location, 'rootURL', rootURL); } - // Allow the location to do any feature detection, such as AutoLocation - // detecting history support. This gives it a chance to set its - // `cancelRouterSetup` property which aborts routing. - if (typeof location.detect === 'function') { - if (this.location !== 'auto') { - deprecate( - 'The `detect` method on the Location object is deprecated. If you need detection you can run your detection code in app.js, before setting the location type.', - false, - { - id: 'deprecate-auto-location', - until: '5.0.0', - url: 'https://emberjs.com/deprecations/v4.x#toc_deprecate-auto-location', - for: 'ember-source', - since: { - available: '4.1.0', - enabled: '4.1.0', - }, - } - ); - } - - location.detect(); - } - // ensure that initState is called AFTER the rootURL is set on // the location instance if (typeof location.initState === 'function') { @@ -1946,7 +1900,7 @@ EmberRouter.reopen({ // FIXME: Does this need to be overrideable via extend? url: computed(function (this: EmberRouter) { - let location = get(this, 'location') as string | IEmberLocation; + let location = get(this, 'location') as string | EmberLocation; if (typeof location === 'string') { return undefined; diff --git a/packages/@ember/routing/tests/location/auto_location_test.js b/packages/@ember/routing/tests/location/auto_location_test.js deleted file mode 100644 index f535f0717ba..00000000000 --- a/packages/@ember/routing/tests/location/auto_location_test.js +++ /dev/null @@ -1,380 +0,0 @@ -import { window } from '@ember/-internals/browser-environment'; -import { run } from '@ember/runloop'; -import { get } from '@ember/object'; -import { setOwner } from '@glimmer/owner'; -import AutoLocation, { getHistoryPath, getHashPath } from '@ember/routing/auto-location'; -import HistoryLocation from '@ember/routing/history-location'; -import HashLocation from '@ember/routing/hash-location'; -import NoneLocation from '@ember/routing/none-location'; -import { buildOwner, moduleFor, AbstractTestCase } from 'internal-test-helpers'; - -function mockBrowserLocation(overrides, assert) { - return Object.assign( - { - href: 'http://test.com/', - pathname: '/', - hash: '', - search: '', - replace() { - assert.ok(false, 'location.replace should not be called during testing'); - }, - }, - overrides - ); -} - -function mockBrowserHistory(overrides, assert) { - return Object.assign( - { - pushState() { - assert.ok(false, 'history.pushState should not be called during testing'); - }, - replaceState() { - assert.ok(false, 'history.replaceState should not be called during testing'); - }, - }, - overrides - ); -} - -function createLocation(location, history) { - owner = buildOwner(); - - owner.register('location:history', HistoryLocation); - owner.register('location:hash', HashLocation); - owner.register('location:none', NoneLocation); - - let props = { - location: location, - history: history, - global: {}, - }; - - setOwner(props, owner); - - let autolocation = AutoLocation.create(props); - - return autolocation; -} - -let location, owner; - -moduleFor( - 'AutoLocation', - class extends AbstractTestCase { - teardown() { - if (owner) { - run(owner, 'destroy'); - owner = location = undefined; - } - } - - ['@test AutoLocation should have the `global`'](assert) { - let location = AutoLocation.create(); - - assert.ok(location.global, 'has a global defined'); - assert.strictEqual(location.global, window, 'has the environments window global'); - } - - ["@test AutoLocation should return concrete implementation's value for `getURL`"](assert) { - let browserLocation = mockBrowserLocation({}, assert); - let browserHistory = mockBrowserHistory({}, assert); - - location = createLocation(browserLocation, browserHistory); - location.detect(); - - let concreteImplementation = get(location, 'concreteImplementation'); - - concreteImplementation.getURL = function () { - return '/lincoln/park'; - }; - - assert.equal(location.getURL(), '/lincoln/park'); - } - - ['@test AutoLocation should use a HistoryLocation instance when pushStates is supported']( - assert - ) { - let browserLocation = mockBrowserLocation({}, assert); - let browserHistory = mockBrowserHistory({}, assert); - - location = createLocation(browserLocation, browserHistory); - location.detect(); - - assert.ok(get(location, 'concreteImplementation') instanceof HistoryLocation); - } - - ['@test AutoLocation should use a HashLocation instance when pushStates are not supported, but hashchange events are and the URL is already in the HashLocation format']( - assert - ) { - let browserLocation = mockBrowserLocation( - { - hash: '#/testd', - }, - assert - ); - - location = createLocation(browserLocation); - location.global = { - onhashchange() {}, - }; - - location.detect(); - assert.ok(get(location, 'concreteImplementation') instanceof HashLocation); - } - - ['@test AutoLocation should use a NoneLocation instance when neither history nor hashchange are supported.']( - assert - ) { - location = createLocation(mockBrowserLocation({}, assert)); - location.detect(); - - assert.ok(get(location, 'concreteImplementation') instanceof NoneLocation); - } - - ["@test AutoLocation should use an index path (i.e. '/') without any location.hash as OK for HashLocation"]( - assert - ) { - let browserLocation = mockBrowserLocation( - { - href: 'http://test.com/', - pathname: '/', - hash: '', - search: '', - replace() { - assert.ok(false, 'location.replace should not be called'); - }, - }, - assert - ); - - location = createLocation(browserLocation); - location.global = { - onhashchange() {}, - }; - - location.detect(); - - assert.ok( - get(location, 'concreteImplementation') instanceof HashLocation, - 'uses a HashLocation' - ); - } - - ['@test AutoLocation should transform the URL for hashchange-only browsers viewing a HistoryLocation-formatted path']( - assert - ) { - assert.expect(3); - - let browserLocation = mockBrowserLocation( - { - hash: '', - hostname: 'test.com', - href: 'http://test.com/test', - pathname: '/test', - protocol: 'http:', - port: '', - search: '', - replace(path) { - assert.equal( - path, - 'http://test.com/#/test', - 'location.replace should be called with normalized HashLocation path' - ); - }, - }, - assert - ); - - let location = createLocation(browserLocation); - location.global = { - onhashchange() {}, - }; - - location.detect(); - - assert.ok( - get(location, 'concreteImplementation') instanceof NoneLocation, - 'NoneLocation should be used while we attempt to location.replace()' - ); - assert.equal( - get(location, 'cancelRouterSetup'), - true, - 'cancelRouterSetup should be set so the router knows.' - ); - } - - ['@test AutoLocation should replace the URL for pushState-supported browsers viewing a HashLocation-formatted url']( - assert - ) { - assert.expect(2); - let browserLocation = mockBrowserLocation( - { - hash: '#/test', - hostname: 'test.com', - href: 'http://test.com/#/test', - pathname: '/', - protocol: 'http:', - port: '', - search: '', - }, - assert - ); - - let browserHistory = mockBrowserHistory( - { - replaceState(state, title, path) { - assert.equal( - path, - '/test', - 'history.replaceState should be called with normalized HistoryLocation url' - ); - }, - }, - assert - ); - - let location = createLocation(browserLocation, browserHistory); - location.detect(); - - assert.ok(get(location, 'concreteImplementation'), HistoryLocation); - } - - ['@test AutoLocation requires any rootURL given to end in a trailing forward slash'](assert) { - let browserLocation = mockBrowserLocation({}, assert); - let expectedMsg = /rootURL must end with a trailing forward slash e.g. "\/app\/"/; - - location = createLocation(browserLocation); - location.rootURL = 'app'; - - expectAssertion(function () { - location.detect(); - }, expectedMsg); - - location.rootURL = '/app'; - expectAssertion(function () { - location.detect(); - }, expectedMsg); - - // Note the trailing whitespace - location.rootURL = '/app/ '; - expectAssertion(function () { - location.detect(); - }, expectedMsg); - } - - ['@test AutoLocation provides its rootURL to the concreteImplementation'](assert) { - let browserLocation = mockBrowserLocation( - { - pathname: '/some/subdir/derp', - }, - assert - ); - let browserHistory = mockBrowserHistory({}, assert); - - location = createLocation(browserLocation, browserHistory); - location.rootURL = '/some/subdir/'; - - location.detect(); - - let concreteLocation = get(location, 'concreteImplementation'); - assert.equal(location.rootURL, concreteLocation.rootURL); - } - - ['@test getHistoryPath() should return a normalized, HistoryLocation-supported path'](assert) { - let browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/about?foo=bar#foo', - pathname: '/app/about', - search: '?foo=bar', - hash: '#foo', - }, - assert - ); - - assert.equal( - getHistoryPath('/app/', browserLocation), - '/app/about?foo=bar#foo', - 'URLs already in HistoryLocation form should come out the same' - ); - - browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/#/about?foo=bar#foo', - pathname: '/app/', - search: '', - hash: '#/about?foo=bar#foo', - }, - assert - ); - assert.equal( - getHistoryPath('/app/', browserLocation), - '/app/about?foo=bar#foo', - 'HashLocation formed URLs should be normalized' - ); - - browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/#about?foo=bar#foo', - pathname: '/app/', - search: '', - hash: '#about?foo=bar#foo', - }, - assert - ); - assert.equal( - getHistoryPath('/app', browserLocation), - '/app/#about?foo=bar#foo', - "URLs with a hash not following #/ convention shouldn't be normalized as a route" - ); - } - - ['@test getHashPath() should return a normalized, HashLocation-supported path'](assert) { - let browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/#/about?foo=bar#foo', - pathname: '/app/', - search: '', - hash: '#/about?foo=bar#foo', - }, - assert - ); - assert.equal( - getHashPath('/app/', browserLocation), - '/app/#/about?foo=bar#foo', - 'URLs already in HistoryLocation form should come out the same' - ); - - browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/about?foo=bar#foo', - pathname: '/app/about', - search: '?foo=bar', - hash: '#foo', - }, - assert - ); - assert.equal( - getHashPath('/app/', browserLocation), - '/app/#/about?foo=bar#foo', - 'HistoryLocation formed URLs should be normalized' - ); - - browserLocation = mockBrowserLocation( - { - href: 'http://test.com/app/#about?foo=bar#foo', - pathname: '/app/', - search: '', - hash: '#about?foo=bar#foo', - }, - assert - ); - - assert.equal( - getHashPath('/app/', browserLocation), - '/app/#/#about?foo=bar#foo', - "URLs with a hash not following #/ convention shouldn't be normalized as a route" - ); - } - } -); diff --git a/packages/@ember/routing/tests/location/util_test.js b/packages/@ember/routing/tests/location/util_test.js index ba957725058..8f5ef8eeff4 100644 --- a/packages/@ember/routing/tests/location/util_test.js +++ b/packages/@ember/routing/tests/location/util_test.js @@ -1,5 +1,4 @@ import { replacePath, getPath, getQuery, getFullPath } from '../../lib/location-utils'; -import { supportsHistory, supportsHashChange } from '../../lib/location-utils'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; function mockBrowserLocation(overrides, assert) { @@ -71,88 +70,5 @@ moduleFor( assert.equal(getFullPath(location), '/about?foo=bar#foo'); } - - ['@test Feature-Detecting onhashchange'](assert) { - assert.equal( - supportsHashChange(undefined, { onhashchange() {} }), - true, - 'When not in IE, use onhashchange existence as evidence of the feature' - ); - assert.equal( - supportsHashChange(undefined, {}), - false, - 'When not in IE, use onhashchange absence as evidence of the feature absence' - ); - assert.equal( - supportsHashChange(7, { onhashchange() {} }), - false, - 'When in IE7 compatibility mode, never report existence of the feature' - ); - assert.equal( - supportsHashChange(8, { onhashchange() {} }), - true, - 'When in IE8+, use onhashchange existence as evidence of the feature' - ); - } - - ['@test Feature-detecting the history API'](assert) { - assert.equal( - supportsHistory('', { pushState: true }), - true, - 'returns true if not Android Gingerbread and history.pushState exists' - ); - assert.equal( - supportsHistory('', {}), - false, - "returns false if history.pushState doesn't exist" - ); - assert.equal(supportsHistory('', undefined), false, "returns false if history doesn't exist"); - - assert.equal( - supportsHistory( - 'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', - { pushState: true } - ), - false, - 'returns false if Android 2.x stock browser (not Chrome) claiming to support pushState' - ); - - assert.equal( - supportsHistory( - 'Mozilla/5.0 (Linux; U; Android 4.0.3; nl-nl; GT-N7000 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', - { pushState: true } - ), - false, - 'returns false for Android 4.0.x stock browser (not Chrome) claiming to support pushState' - ); - - assert.equal( - supportsHistory( - 'Mozilla/5.0 (Linux; U; Android 20.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', - { pushState: true } - ), - true, - 'returns true if Android version begins with 2, but is greater than 2' - ); - - assert.equal( - supportsHistory( - 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19', - { pushState: true } - ), - true, - 'returns true for Chrome (not stock browser) on Android 4.0.x' - ); - - // Windows Phone UA and History API: https://github.com/Modernizr/Modernizr/issues/1471 - assert.equal( - supportsHistory( - 'Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; Microsoft; Virtual) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537', - { pushState: true } - ), - true, - 'returns true for Windows Phone 8.1 with misleading user agent string' - ); - } } ); diff --git a/packages/@ember/routing/tests/system/router_test.js b/packages/@ember/routing/tests/system/router_test.js index 3cc34559398..17e8bef12fb 100644 --- a/packages/@ember/routing/tests/system/router_test.js +++ b/packages/@ember/routing/tests/system/router_test.js @@ -1,6 +1,5 @@ import HashLocation from '@ember/routing/hash-location'; import HistoryLocation from '@ember/routing/history-location'; -import AutoLocation from '@ember/routing/auto-location'; import NoneLocation from '@ember/routing/none-location'; import Router, { triggerEvent } from '@ember/routing/router'; import { runDestroy, buildOwner, moduleFor, AbstractTestCase } from 'internal-test-helpers'; @@ -31,7 +30,6 @@ moduleFor( //register the HashLocation (the default) owner.register('location:hash', HashLocation); owner.register('location:history', HistoryLocation); - owner.register('location:auto', AutoLocation); owner.register('location:none', NoneLocation); } @@ -97,36 +95,6 @@ moduleFor( assert.equal(location.get('rootURL'), '/rootdir/'); } - ['@test replacePath should be called with the right path'](assert) { - assert.expect(2); - - let location = owner.lookup('location:auto'); - - let browserLocation = { - href: 'http://test.com/rootdir/welcome', - origin: 'http://test.com', - pathname: '/rootdir/welcome', - hash: '', - search: '', - replace(url) { - assert.equal(url, 'http://test.com/rootdir/#/welcome'); - }, - }; - - location.location = browserLocation; - location.global = { onhashchange() {} }; - location.history = null; - - expectDeprecation(() => { - createRouter({ - settings: { - location: 'auto', - rootURL: '/rootdir/', - }, - }); - }); - } - ['@test Router._routePath should consume identical prefixes'](assert) { createRouter(); @@ -178,37 +146,6 @@ moduleFor( router.startRouting(); } - ["@test AutoLocation should replace the url when it's not in the preferred format"](assert) { - assert.expect(2); - - let location = owner.lookup('location:auto'); - - location.location = { - href: 'http://test.com/rootdir/welcome', - origin: 'http://test.com', - pathname: '/rootdir/welcome', - hash: '', - search: '', - replace(url) { - assert.equal(url, 'http://test.com/rootdir/#/welcome'); - }, - }; - - location.history = null; - location.global = { - onhashchange() {}, - }; - - expectDeprecation(() => { - createRouter({ - settings: { - location: 'auto', - rootURL: '/rootdir/', - }, - }); - }); - } - ['@test Router#handleURL should remove any #hashes before doing URL transition'](assert) { assert.expect(2); diff --git a/packages/@ember/routing/type-tests/auto-location.test.ts b/packages/@ember/routing/type-tests/auto-location.test.ts deleted file mode 100644 index 808fa6eeba5..00000000000 --- a/packages/@ember/routing/type-tests/auto-location.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type Owner from '@ember/owner'; -import type EmberObject from '@ember/object'; -import AutoLocation from '@ember/routing/auto-location'; -import type { ILocation } from '@ember/routing/location'; -import { expectTypeOf } from 'expect-type'; - -// Good enough for tests -let owner = {} as Owner; - -// This doesn't have any public API - -let location = new AutoLocation(owner); -expectTypeOf(location).toMatchTypeOf(); -expectTypeOf(location).toMatchTypeOf(); diff --git a/packages/@ember/routing/type-tests/hash-location.test.ts b/packages/@ember/routing/type-tests/hash-location.test.ts index 03ded832375..65ab93f952f 100644 --- a/packages/@ember/routing/type-tests/hash-location.test.ts +++ b/packages/@ember/routing/type-tests/hash-location.test.ts @@ -1,4 +1,4 @@ -import type { ILocation } from '@ember/routing/location'; +import type { Location } from '@ember/routing/location'; import type EmberObject from '@ember/object'; import HashLocation from '@ember/routing/hash-location'; import { expectTypeOf } from 'expect-type'; @@ -11,4 +11,4 @@ let owner = {} as Owner; let location = new HashLocation(owner); expectTypeOf(location).toMatchTypeOf(); -expectTypeOf(location).toMatchTypeOf(); +expectTypeOf(location).toMatchTypeOf(); diff --git a/packages/@ember/routing/type-tests/history-location.test.ts b/packages/@ember/routing/type-tests/history-location.test.ts index dd6d1ef2c42..ef1c3592728 100644 --- a/packages/@ember/routing/type-tests/history-location.test.ts +++ b/packages/@ember/routing/type-tests/history-location.test.ts @@ -1,7 +1,7 @@ import type Owner from '@ember/owner'; import type EmberObject from '@ember/object'; import HistoryLocation from '@ember/routing/history-location'; -import type { ILocation } from '@ember/routing/location'; +import type { Location } from '@ember/routing/location'; import { expectTypeOf } from 'expect-type'; // Good enough for tests @@ -11,4 +11,4 @@ let owner = {} as Owner; let location = new HistoryLocation(owner); expectTypeOf(location).toMatchTypeOf(); -expectTypeOf(location).toMatchTypeOf(); +expectTypeOf(location).toMatchTypeOf(); diff --git a/packages/@ember/routing/type-tests/location.test.ts b/packages/@ember/routing/type-tests/location.test.ts index 9bfae6cebb7..4957a13c042 100644 --- a/packages/@ember/routing/type-tests/location.test.ts +++ b/packages/@ember/routing/type-tests/location.test.ts @@ -1,24 +1,24 @@ -import type { ILocation } from '@ember/routing/location'; -import Location from '@ember/routing/location'; +import type { Location } from '@ember/routing/location'; import { expectTypeOf } from 'expect-type'; - -// This is deprecated so let's not bother with more testing -expectTypeOf(Location.create).toBeFunction(); +import type Owner from '@ember/owner'; +import type NoneLocation from '../none-location'; +import type HashLocation from '../hash-location'; +import type HistoryLocation from '../history-location'; // Interface -expectTypeOf().toEqualTypeOf(); -expectTypeOf().toEqualTypeOf(); -expectTypeOf().toEqualTypeOf<() => string>(); -expectTypeOf().toEqualTypeOf<(url: string) => void>(); -expectTypeOf().toEqualTypeOf<((url: string) => void) | undefined>(); -expectTypeOf().toEqualTypeOf<(callback: (url: string) => void) => void>(); -expectTypeOf().toEqualTypeOf<(url: string) => string>(); -expectTypeOf().toEqualTypeOf<(() => void) | undefined>(); -expectTypeOf().toEqualTypeOf<(() => void) | undefined>(); -expectTypeOf().toEqualTypeOf<() => void>(); +expectTypeOf().toEqualTypeOf(); +expectTypeOf().toEqualTypeOf(); +expectTypeOf().toEqualTypeOf<() => string>(); +expectTypeOf().toEqualTypeOf<(url: string) => void>(); +expectTypeOf().toEqualTypeOf<((url: string) => void) | undefined>(); +expectTypeOf().toEqualTypeOf<(callback: (url: string) => void) => void>(); +expectTypeOf().toEqualTypeOf<(url: string) => string>(); +expectTypeOf().toEqualTypeOf<(() => void) | undefined>(); +expectTypeOf().toEqualTypeOf<(() => void) | undefined>(); +expectTypeOf().toEqualTypeOf<() => void>(); // Minimal implementation -class TestLocation implements ILocation { +class TestLocation implements Location { implementation = 'test'; getURL() { @@ -39,3 +39,8 @@ class TestLocation implements ILocation { } new TestLocation(); + +declare let owner: Owner; +expectTypeOf(owner.lookup('location:none')).toEqualTypeOf(); +expectTypeOf(owner.lookup('location:hash')).toEqualTypeOf(); +expectTypeOf(owner.lookup('location:history')).toEqualTypeOf(); diff --git a/packages/@ember/routing/type-tests/none-location.test.ts b/packages/@ember/routing/type-tests/none-location.test.ts index 5ef9fe920c0..48e8ff77b16 100644 --- a/packages/@ember/routing/type-tests/none-location.test.ts +++ b/packages/@ember/routing/type-tests/none-location.test.ts @@ -1,6 +1,6 @@ import type EmberObject from '@ember/object'; import NoneLocation from '@ember/routing/none-location'; -import type { ILocation } from '@ember/routing/location'; +import type { Location } from '@ember/routing/location'; import { expectTypeOf } from 'expect-type'; import type Owner from '@ember/owner'; @@ -11,4 +11,4 @@ let owner = {} as Owner; let location = new NoneLocation(owner); expectTypeOf(location).toMatchTypeOf(); -expectTypeOf(location).toMatchTypeOf(); +expectTypeOf(location).toMatchTypeOf(); diff --git a/packages/@ember/routing/type-tests/router-service.test.ts b/packages/@ember/routing/type-tests/router-service.test.ts index 2208d46992c..035aa95acfd 100644 --- a/packages/@ember/routing/type-tests/router-service.test.ts +++ b/packages/@ember/routing/type-tests/router-service.test.ts @@ -1,7 +1,7 @@ /* eslint-disable no-self-assign */ import type Owner from '@ember/owner'; -import type { ILocation as EmberLocation } from '@ember/routing/location'; +import type { Location as EmberLocation } from '@ember/routing/location'; import type Route from '@ember/routing/route'; import type { RouteInfo, RouteInfoWithAttributes } from '@ember/routing/router-service'; import RouterService from '@ember/routing/router-service'; diff --git a/packages/@ember/routing/type-tests/router/index.test.ts b/packages/@ember/routing/type-tests/router/index.test.ts index 3d2dcd9cae8..81d0c724b46 100644 --- a/packages/@ember/routing/type-tests/router/index.test.ts +++ b/packages/@ember/routing/type-tests/router/index.test.ts @@ -1,7 +1,7 @@ import type Owner from '@ember/owner'; import type EmberObject from '@ember/object'; import type Evented from '@ember/object/evented'; -import type { ILocation } from '@ember/routing/location'; +import type { Location } from '@ember/routing/location'; import Router from '@ember/routing/router'; import { expectTypeOf } from 'expect-type'; import type { Transition } from 'router_js'; @@ -15,7 +15,7 @@ let router = new Router(owner); expectTypeOf(router.rootURL).toEqualTypeOf(); -expectTypeOf(router.location).toEqualTypeOf(); +expectTypeOf(router.location).toEqualTypeOf(); let aPost = {}; let aComment = {}; diff --git a/packages/@ember/service/index.ts b/packages/@ember/service/index.ts index 199b0765cd6..e5b271fec30 100644 --- a/packages/@ember/service/index.ts +++ b/packages/@ember/service/index.ts @@ -99,7 +99,7 @@ export default class Service extends FrameworkObject { lookups resolve to the correct type. */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface Registry extends Record {} +export interface Registry {} declare module '@ember/owner' { export interface DIRegistry { diff --git a/packages/ember/index.ts b/packages/ember/index.ts index 16f9e3aeb1e..269f27c1c81 100644 --- a/packages/ember/index.ts +++ b/packages/ember/index.ts @@ -93,11 +93,9 @@ import { addObserver, removeObserver } from '@ember/object/observers'; import ObjectProxy from '@ember/object/proxy'; import PromiseProxyMixin from '@ember/object/promise-proxy-mixin'; import { assign } from '@ember/polyfills'; -import AutoLocation from '@ember/routing/auto-location'; import HashLocation from '@ember/routing/hash-location'; import HistoryLocation from '@ember/routing/history-location'; import NoneLocation from '@ember/routing/none-location'; -import EmberLocation from '@ember/routing/location'; import Route from '@ember/routing/route'; import Router from '@ember/routing/router'; import { @@ -368,18 +366,12 @@ const PartialEmber = { generateController, generateControllerFactory, - // ****@ember/routing/auto-location**** - AutoLocation, - // ****@ember/routing/hash-location**** HashLocation, // ****@ember/routing/history-location**** HistoryLocation, - // ****@ember/routing/location**** - Location: EmberLocation, - // ****@ember/routing/none-location**** NoneLocation, diff --git a/packages/ember/tests/reexports_test.js b/packages/ember/tests/reexports_test.js index 0c0cdb5912a..376e6c606ca 100644 --- a/packages/ember/tests/reexports_test.js +++ b/packages/ember/tests/reexports_test.js @@ -264,18 +264,12 @@ let allExports = [ ['assign', '@ember/polyfills', 'assign'], ['platform.hasPropertyAccessors', '@ember/polyfills', 'hasPropertyAccessors'], - // @ember/routing/auto-location - ['AutoLocation', '@ember/routing/auto-location', 'default'], - // @ember/routing/hash-location ['HashLocation', '@ember/routing/hash-location', 'default'], // @ember/routing/history-location ['HistoryLocation', '@ember/routing/history-location', 'default'], - // @ember/routing/location - ['Location', '@ember/routing/location', 'default'], - // @ember/routing/none-location ['NoneLocation', '@ember/routing/none-location', 'default'], diff --git a/tests/docs/expected.js b/tests/docs/expected.js index 8049c62aa3a..74f52f6b0f8 100644 --- a/tests/docs/expected.js +++ b/tests/docs/expected.js @@ -22,7 +22,6 @@ module.exports = { '_deserializeQueryParams', '_document', '_fullyScopeQueryParams', - '_getHashPath', '_getObjectsOnNamespaces', '_getQPMeta', '_globalsMode', @@ -241,7 +240,6 @@ module.exports = { 'getViewElement', 'getViewId', 'getViewRange', - 'global', 'gt', 'gte', 'guidFor', @@ -258,9 +256,9 @@ module.exports = { 'hasRoute', 'helper', 'helperContainer', - 'history', 'htmlSafe', 'if', + 'implementation', 'in-element', 'includes', 'incrementProperty', @@ -534,7 +532,6 @@ module.exports = { 'unsubscribe', 'url', 'urlFor', - 'userAgent', 'validationCache', 'visit', 'w', diff --git a/types/preview/@ember/routing/auto-location.d.ts b/types/preview/@ember/routing/auto-location.d.ts deleted file mode 100644 index ef226556850..00000000000 --- a/types/preview/@ember/routing/auto-location.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module '@ember/routing/auto-location' { - import EmberObject from '@ember/object'; - - /** - * AutoLocation will select the best location option based off browser support with the priority order: history, hash, none. - */ - export default class AutoLocation extends EmberObject {} -} diff --git a/types/preview/@ember/routing/router-service.d.ts b/types/preview/@ember/routing/router-service.d.ts index 35b16927e85..0d7a1ce318f 100644 --- a/types/preview/@ember/routing/router-service.d.ts +++ b/types/preview/@ember/routing/router-service.d.ts @@ -349,3 +349,11 @@ declare module '@ember/routing/router-service' { refresh(pivotRouteName?: string): Transition; } } + +declare module '@ember/service' { + import RouterService from '@ember/routing/router-service'; + + interface Registry { + router: RouterService; + } +} diff --git a/types/preview/@ember/routing/router.d.ts b/types/preview/@ember/routing/router.d.ts index 299dc3f43df..bba5ac4b9fb 100644 --- a/types/preview/@ember/routing/router.d.ts +++ b/types/preview/@ember/routing/router.d.ts @@ -37,13 +37,5 @@ declare module '@ember/routing/router' { transitionTo(name: string, options: {}): Transition; } - export default interface RouterService extends Evented {} -} - -declare module '@ember/service' { - import RouterService from '@ember/routing/router-service'; - - interface Registry { - router: RouterService; - } + export default interface Router extends Evented {} }