diff --git a/.changeset/eighty-swans-trade.md b/.changeset/eighty-swans-trade.md new file mode 100644 index 00000000000..addb3926466 --- /dev/null +++ b/.changeset/eighty-swans-trade.md @@ -0,0 +1,9 @@ +--- +'@clerk/shared': patch +--- + +Apply deprecation warnings: +- `OrganizationContext` +- `organizationList` +- `useOrganizations` +- `getRequestUrl` diff --git a/packages/shared/src/hooks/contexts.tsx b/packages/shared/src/hooks/contexts.tsx index a03ae983324..634b0bb4c04 100644 --- a/packages/shared/src/hooks/contexts.tsx +++ b/packages/shared/src/hooks/contexts.tsx @@ -10,6 +10,7 @@ import type { import type { PropsWithChildren } from 'react'; import React from 'react'; +import { deprecated } from '../utils'; import { disableSWRDevtools } from './clerk-swr'; disableSWRDevtools(); import { SWRConfig } from './clerk-swr'; @@ -65,7 +66,10 @@ const OrganizationProvider = ({ /** * @deprecated use OrganizationProvider instead */ -export const OrganizationContext = OrganizationProvider; +export const OrganizationContext = (...args: Parameters) => { + deprecated('OrganizationContext', 'Use `OrganizationProvider` instead'); + return OrganizationProvider(...args); +}; export { ClientContext, diff --git a/packages/shared/src/hooks/useOrganizationList.tsx b/packages/shared/src/hooks/useOrganizationList.tsx index adc019cface..e536b57b8a5 100644 --- a/packages/shared/src/hooks/useOrganizationList.tsx +++ b/packages/shared/src/hooks/useOrganizationList.tsx @@ -11,6 +11,7 @@ import type { UserOrganizationInvitationResource, } from '@clerk/types'; +import { deprecatedObjectProperty } from '../utils'; import { useClerkInstanceContext, useUserContext } from './contexts'; import type { PaginatedResources, PaginatedResourcesWithDefault } from './types'; import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite'; @@ -210,7 +211,7 @@ export const useOrganizationList: UseOrganizationList = params => { }; } - return { + const result = { isLoaded: isClerkLoaded, organizationList: createOrganizationList(user.organizationMemberships), setActive: clerk.setActive, @@ -219,6 +220,9 @@ export const useOrganizationList: UseOrganizationList = params => { userInvitations: invitations, userSuggestions: suggestions, }; + deprecatedObjectProperty(result, 'organizationList', 'Use `userMemberships` instead.'); + + return result; }; function createOrganizationList(organizationMemberships: OrganizationMembershipResource[]) { diff --git a/packages/shared/src/hooks/useOrganizations.tsx b/packages/shared/src/hooks/useOrganizations.tsx index 8b227b47d9f..41439235b9e 100644 --- a/packages/shared/src/hooks/useOrganizations.tsx +++ b/packages/shared/src/hooks/useOrganizations.tsx @@ -1,5 +1,6 @@ import type { CreateOrganizationParams, OrganizationMembershipResource, OrganizationResource } from '@clerk/types'; +import { deprecated } from '../utils'; import { useClerkInstanceContext } from './contexts'; type UseOrganizationsReturn = @@ -51,6 +52,7 @@ type UseOrganizations = () => UseOrganizationsReturn; * @deprecated Use useOrganizationList, useOrganization, or useClerk instead */ export const useOrganizations: UseOrganizations = () => { + deprecated('useOrganizations', 'Use useOrganizationList, useOrganization, or useClerk instead.'); const clerk = useClerkInstanceContext(); if (!clerk.loaded) { return { diff --git a/packages/shared/src/utils/deprecated.test.ts b/packages/shared/src/utils/deprecated.test.ts index 168eccdfb2a..907f9ced45e 100644 --- a/packages/shared/src/utils/deprecated.test.ts +++ b/packages/shared/src/utils/deprecated.test.ts @@ -5,7 +5,7 @@ jest.mock('./runtimeEnvironment', () => { }; }); -import { deprecated, deprecatedProperty } from './deprecated'; +import { deprecated, deprecatedObjectProperty, deprecatedProperty } from './deprecated'; import { isProductionEnvironment, isTestEnvironment } from './runtimeEnvironment'; describe('deprecated(fnName, warning)', () => { @@ -310,3 +310,74 @@ describe('deprecatedProperty(cls, propName, warning, isStatic = false)', () => { }); }); }); + +describe('deprecatedObjectProperty(obj, propName, warning)', () => { + let consoleWarnSpy; + + beforeEach(() => { + consoleWarnSpy = jest.spyOn(global.console, 'warn').mockImplementation(); + }); + afterEach(() => { + consoleWarnSpy.mockRestore(); + }); + + test('deprecate object property shows warning', () => { + const example = { objectProperty: 'objectProperty-value' }; + + deprecatedObjectProperty(example, 'objectProperty', 'Use `objectPropertyElse` instead.'); + + expect(consoleWarnSpy).not.toBeCalled(); + expect(example.objectProperty).toEqual('objectProperty-value'); + expect(consoleWarnSpy).toBeCalledTimes(1); + expect(consoleWarnSpy).toBeCalledWith( + 'DEPRECATION WARNING: "objectProperty" is deprecated and will be removed in the next major release.\nUse `objectPropertyElse` instead.', + ); + + expect(example.objectProperty).toEqual('objectProperty-value'); + expect(consoleWarnSpy).toBeCalledTimes(1); + }); + + describe('for test environment', () => { + beforeEach(() => { + (isTestEnvironment as jest.Mock).mockReturnValue(true); + }); + afterEach(() => { + (isTestEnvironment as jest.Mock).mockReturnValue(false); + }); + + test('deprecate object property does not show warning', () => { + const example = { objectPropertyInTest: 'objectPropertyInTest-value' }; + + deprecatedObjectProperty(example, 'objectPropertyInTest', 'Use `objectPropertyInTestElse` instead.'); + + expect(consoleWarnSpy).not.toBeCalled(); + expect(example.objectPropertyInTest).toEqual('objectPropertyInTest-value'); + // call it twice to verify that it's never called + expect(example.objectPropertyInTest).toEqual('objectPropertyInTest-value'); + expect(example.objectPropertyInTest).toEqual('objectPropertyInTest-value'); + expect(consoleWarnSpy).toBeCalledTimes(0); + }); + }); + + describe('for production environment', () => { + beforeEach(() => { + (isProductionEnvironment as jest.Mock).mockReturnValue(true); + }); + afterEach(() => { + (isProductionEnvironment as jest.Mock).mockReturnValue(false); + }); + + test('deprecate object property does not show warning', () => { + const example = { objectPropertyInProd: 'objectPropertyInProd-value' }; + + deprecatedObjectProperty(example, 'objectPropertyInProd', 'Use `objectPropertyInProdElse` instead.'); + + expect(consoleWarnSpy).not.toBeCalled(); + expect(example.objectPropertyInProd).toEqual('objectPropertyInProd-value'); + // call it twice to verify that it's never called + expect(example.objectPropertyInProd).toEqual('objectPropertyInProd-value'); + expect(example.objectPropertyInProd).toEqual('objectPropertyInProd-value'); + expect(consoleWarnSpy).toBeCalledTimes(0); + }); + }); +}); diff --git a/packages/shared/src/utils/deprecated.ts b/packages/shared/src/utils/deprecated.ts index 31238f52553..1392fb2a534 100644 --- a/packages/shared/src/utils/deprecated.ts +++ b/packages/shared/src/utils/deprecated.ts @@ -1,9 +1,8 @@ import { isProductionEnvironment, isTestEnvironment } from './runtimeEnvironment'; /** - * Mark class methods or functions as deprecated. + * Mark class method / function as deprecated. * - * A console WARNING will be displayed when class methods - * or functions are invoked. + * A console WARNING will be displayed when class method / function is invoked. * * Examples * 1. Deprecate class method @@ -34,9 +33,9 @@ export const deprecated = (fnName: string, warning: string, key?: string): void ); }; /** - * Mark class properties as deprecated. + * Mark class property as deprecated. * - * A console WARNING will be displayed when class properties are being accessed. + * A console WARNING will be displayed when class property is being accessed. * * 1. Deprecate class property * class Example { @@ -60,13 +59,28 @@ type AnyClass = new (...args: any[]) => any; export const deprecatedProperty = (cls: AnyClass, propName: string, warning: string, isStatic = false): void => { const target = isStatic ? cls : cls.prototype; + let value = target[propName]; Object.defineProperty(target, propName, { get() { deprecated(propName, warning, `${cls.name}:${propName}`); - return this['_' + propName]; + return value; }, set(v: unknown) { - this['_' + propName] = v; + value = v; }, }); }; + +/** + * Mark object property as deprecated. + * + * A console WARNING will be displayed when object property is being accessed. + * + * 1. Deprecate object property + * const obj = { something: 'aloha' }; + * + * deprecatedObjectProperty(obj, 'something', 'Use `somethingElse` instead.'); + */ +export const deprecatedObjectProperty = (obj: Record, propName: string, warning: string): void => { + deprecatedProperty(obj as any, propName, warning, true); +}; diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index da232ae2529..82919828e36 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -23,4 +23,4 @@ export * from './isomorphicAtob'; export * from './globs'; export * from './loadScript'; export * from './runtimeEnvironment'; -export { deprecated, deprecatedProperty } from './deprecated'; +export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated'; diff --git a/packages/shared/src/utils/proxy.ts b/packages/shared/src/utils/proxy.ts index fcfe4b17770..6d877d9bbd4 100644 --- a/packages/shared/src/utils/proxy.ts +++ b/packages/shared/src/utils/proxy.ts @@ -1,3 +1,5 @@ +import { deprecated } from './deprecated'; + export function isValidProxyUrl(key: string | undefined) { if (!key) { return true; @@ -25,6 +27,7 @@ export function proxyUrlToAbsoluteURL(url: string | undefined): string { * @deprecated Use `buildRequestUrl` from @clerk/backend */ export function getRequestUrl({ request, relativePath }: { request: Request; relativePath?: string }): URL { + deprecated('getRequestUrl', 'Use `buildRequestUrl` from @clerk/backend instead.'); const { headers, url: initialUrl } = request; const url = new URL(initialUrl); const host = headers.get('X-Forwarded-Host') ?? headers.get('host') ?? (headers as any)['host'] ?? url.host;