Skip to content

Commit

Permalink
feat(expo): Only bundle Clerk in native platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
octoper committed Jul 2, 2024
1 parent 508b47c commit 8f5832c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 84 deletions.
6 changes: 6 additions & 0 deletions packages/expo/src/provider/ClerkProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export type ClerkProviderProps = React.ComponentProps<typeof ClerkReactProvider>
tokenCache?: TokenCache;
};

const SDK_METADATA = {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
};

export function ClerkProvider(props: ClerkProviderProps): JSX.Element {
const { children, tokenCache, publishableKey, ...rest } = props;
const pk = publishableKey || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || '';
Expand All @@ -21,6 +26,7 @@ export function ClerkProvider(props: ClerkProviderProps): JSX.Element {
key={pk}
{...rest}
publishableKey={pk}
sdkMetadata={SDK_METADATA}
Clerk={isNative() ? getClerkInstance({ publishableKey: pk, tokenCache }) : null}
standardBrowser={!isNative()}
>
Expand Down
84 changes: 0 additions & 84 deletions packages/expo/src/provider/singleton.ts

This file was deleted.

81 changes: 81 additions & 0 deletions packages/expo/src/provider/singleton/createClerkInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { FapiRequestInit, FapiResponse } from '@clerk/clerk-js/dist/types/core/fapiClient';
import type { Clerk } from '@clerk/clerk-js/headless';
import type { BrowserClerk, HeadlessBrowserClerk } from '@clerk/clerk-react';

import { MemoryTokenCache } from '../../caches/MemoryTokenCache';
import type { TokenCache } from '../../caches/types';
import { errorThrower } from '../../errorThrower';

type BuildClerkOptions = {
publishableKey?: string;
tokenCache?: TokenCache;
};

const KEY = '__clerk_client_jwt';

/**
* @deprecated Use `getClerkInstance` instead. `Clerk` will be removed in the next major version.
*/
export let clerk: HeadlessBrowserClerk | BrowserClerk;
let __internal_clerk: HeadlessBrowserClerk | BrowserClerk | undefined;

/**
* Access or create a Clerk instance outside of React.
* @example
* import { ClerkProvider, getClerkInstance } from "@clerk/expo"
*
* const clerkInstance = getClerkInstance({ publishableKey: 'xxxx' })
*
* // Always pass the `publishableKey` to `ClerkProvider`
* <ClerkProvider publishableKey={'xxxx'}>
* ...
* </ClerkProvider>
*
* // Somewhere in your code, outside of React you can do
* const token = await clerkInstance.session?.getToken();
* fetch('http://example.com/', {headers: {Authorization: token })
* @throws MissingPublishableKeyError publishableKey is missing and Clerk has not been initialized yet
* @returns HeadlessBrowserClerk
*/
export function createClerkInstance(ClerkClass: typeof Clerk) {
return (options?: BuildClerkOptions): HeadlessBrowserClerk | BrowserClerk => {
const { publishableKey = process.env.CLERK_PUBLISHABLE_KEY || '', tokenCache = MemoryTokenCache } = options || {};

if (!__internal_clerk && !publishableKey) {
errorThrower.throwMissingPublishableKeyError();
}

// Support "hot-swapping" the Clerk instance at runtime. See JS-598 for additional details.
const hasKeyChanged = __internal_clerk && !!publishableKey && publishableKey !== __internal_clerk.publishableKey;

if (!__internal_clerk || hasKeyChanged) {
if (hasKeyChanged) {
tokenCache.clearToken?.(KEY);
}

const getToken = tokenCache.getToken;
const saveToken = tokenCache.saveToken;
__internal_clerk = clerk = new ClerkClass(publishableKey);

// @ts-expect-error - This is an internal API
__internal_clerk.__unstable__onBeforeRequest(async (requestInit: FapiRequestInit) => {
// https://reactnative.dev/docs/0.61/network#known-issues-with-fetch-and-cookie-based-authentication
requestInit.credentials = 'omit';

requestInit.url?.searchParams.append('_is_native', '1');

const jwt = await getToken(KEY);
(requestInit.headers as Headers).set('authorization', jwt || '');
});

// @ts-expect-error - This is an internal API
__internal_clerk.__unstable__onAfterResponse(async (_: FapiRequestInit, response: FapiResponse<unknown>) => {
const authHeader = response.headers.get('authorization');
if (authHeader) {
await saveToken(KEY, authHeader);
}
});
}
return __internal_clerk;
};
}
1 change: 1 addition & 0 deletions packages/expo/src/provider/singleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './singleton';
9 changes: 9 additions & 0 deletions packages/expo/src/provider/singleton/singleton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Clerk } from '@clerk/clerk-js/headless';

import { createClerkInstance } from './createClerkInstance';

/**
* @deprecated Use `getClerkInstance` instead. `Clerk` will be removed in the next major version.
*/
export { clerk } from './createClerkInstance';
export const getClerkInstance = createClerkInstance(Clerk);
5 changes: 5 additions & 0 deletions packages/expo/src/provider/singleton/singleton.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @deprecated Use `getClerkInstance` instead. `Clerk` will be removed in the next major version.
*/
export const clerk = globalThis?.window?.Clerk;
export const getClerkInstance = globalThis?.window?.Clerk;

0 comments on commit 8f5832c

Please sign in to comment.