-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathcreate-clerk-instance.ts
More file actions
137 lines (114 loc) · 4.63 KB
/
create-clerk-instance.ts
File metadata and controls
137 lines (114 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import {
loadClerkJSScript,
loadClerkUIScript,
setClerkJSLoadingErrorPackageName,
shouldPrefetchClerkUI,
} from '@clerk/shared/loadClerkJsScript';
import type { ClerkOptions } from '@clerk/shared/types';
import type { ClerkUiConstructor } from '@clerk/shared/ui';
import type { Ui } from '@clerk/ui/internal';
import { $clerkStore } from '../stores/external';
import { $clerk, $csrState } from '../stores/internal';
import type { AstroClerkCreateInstanceParams, AstroClerkUpdateOptions } from '../types';
import { invokeClerkAstroJSFunctions } from './invoke-clerk-astro-js-functions';
import { mountAllClerkAstroJSComponents } from './mount-clerk-astro-js-components';
import { runOnce } from './run-once';
let initOptions: ClerkOptions | undefined;
setClerkJSLoadingErrorPackageName(PACKAGE_NAME);
function createNavigationHandler(
windowNav: typeof window.history.pushState | typeof window.history.replaceState,
): Exclude<ClerkOptions['routerPush'], undefined> | Exclude<ClerkOptions['routerReplace'], undefined> {
return (to, opts) => {
if (opts?.__internal_metadata?.navigationType === 'internal') {
windowNav(history.state, '', to);
} else {
opts?.windowNavigate(to);
}
};
}
/**
* Prevents firing clerk.load() multiple times
*/
const createClerkInstance = runOnce(createClerkInstanceInternal);
async function createClerkInstanceInternal<TUi extends Ui = Ui>(options?: AstroClerkCreateInstanceParams<TUi>) {
// Load clerk-js and clerk-ui in parallel.
// Both functions return early if the scripts are already loaded
// (e.g., via middleware-injected script tags in the HTML head).
const clerkJsChunk = getClerkJsEntryChunk(options);
const clerkUICtor = getClerkUIEntryChunk(options);
await clerkJsChunk;
if (!window.Clerk) {
throw new Error('Failed to download latest ClerkJS. Contact support@clerk.com.');
}
const clerkJSInstance = window.Clerk;
if (!$clerk.get()) {
$clerk.set(clerkJSInstance);
}
const clerkOptions = {
routerPush: createNavigationHandler(window.history.pushState.bind(window.history)),
routerReplace: createNavigationHandler(window.history.replaceState.bind(window.history)),
...options,
// Pass the clerk-ui constructor promise to clerk.load()
clerkUICtor,
} as unknown as ClerkOptions;
initOptions = clerkOptions;
return clerkJSInstance
.load(clerkOptions)
.then(() => {
$csrState.setKey('isLoaded', true);
// Notify subscribers that $clerkStore has been loaded.
// We're doing this because nanostores uses `===` for equality
// and just by setting the value to `window.Clerk` again won't trigger an update.
// We notify only once as this store is for advanced users.
$clerkStore.notify();
mountAllClerkAstroJSComponents();
invokeClerkAstroJSFunctions();
clerkJSInstance.addListener(payload => {
$csrState.setKey('client', payload.client);
$csrState.setKey('user', payload.user);
$csrState.setKey('session', payload.session);
$csrState.setKey('organization', payload.organization);
});
})
.catch(() => {});
}
function updateClerkOptions<TUi extends Ui = Ui>(options: AstroClerkUpdateOptions<TUi>) {
const clerk = $clerk.get();
if (!clerk) {
throw new Error('Missing clerk instance');
}
const updateOptions = {
options: { ...initOptions, ...options },
appearance: { ...initOptions?.appearance, ...options.appearance },
} as unknown as { options: ClerkOptions; appearance?: any };
// `__internal_updateProps` is not exposed as public API from `@clerk/types`
void (clerk as any).__internal_updateProps(updateOptions);
}
/**
* Loads clerk-js script if not already loaded.
* Returns early if window.Clerk already exists.
*/
async function getClerkJsEntryChunk<TUi extends Ui = Ui>(options?: AstroClerkCreateInstanceParams<TUi>): Promise<void> {
await loadClerkJSScript(options);
}
/**
* Gets the ClerkUI constructor, either from options or by loading the script.
* Returns early if window.__internal_ClerkUiCtor already exists.
* Returns undefined when prefetchUI={false} (no UI needed).
*/
async function getClerkUIEntryChunk<TUi extends Ui = Ui>(
options?: AstroClerkCreateInstanceParams<TUi>,
): Promise<ClerkUiConstructor | undefined> {
if (!shouldPrefetchClerkUI(options?.prefetchUI)) {
return undefined;
}
if (options?.clerkUICtor) {
return options.clerkUICtor;
}
await loadClerkUIScript(options as any);
if (!window.__internal_ClerkUiCtor) {
throw new Error('Failed to download latest Clerk UI. Contact support@clerk.com.');
}
return window.__internal_ClerkUiCtor;
}
export { createClerkInstance, updateClerkOptions };