Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
feat: Support Clerk's env variables (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
panteliselef authored Jul 7, 2024
1 parent 3a33f0f commit ba4214c
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-wasps-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'clerk-solid': patch
---

Support Clerk's env variables
7 changes: 5 additions & 2 deletions dev/root-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { ParentComponent, Show } from 'solid-js'
import { ParentComponent, Show, createEffect } from 'solid-js'
import { A } from '@solidjs/router'
import { Clerk, SignedIn, SignedOut, UserButton, useAuth } from 'src'

const RootLayout: ParentComponent = props => {
const auth = useAuth()
createEffect(() => {
console.log('Auth:', auth())
})
return (
<>
<nav>
Expand All @@ -16,7 +19,7 @@ const RootLayout: ParentComponent = props => {
<UserButton />
</SignedIn>
</nav>
<Clerk publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY} />
<Clerk />
{props.children}
</>
)
Expand Down
28 changes: 23 additions & 5 deletions env.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
interface InternalEnv {
readonly VITE_CLERK_FRONTEND_API?: string
readonly VITE_CLERK_PUBLISHABLE_KEY?: string
readonly VITE_CLERK_JS_URL?: string
readonly VITE_CLERK_JS_VARIANT?: 'headless' | ''
readonly VITE_CLERK_JS_VERSION?: string
readonly CLERK_API_KEY?: string
readonly CLERK_API_URL?: string
readonly CLERK_API_VERSION?: string
readonly CLERK_JWT_KEY?: string
readonly CLERK_SECRET_KEY?: string
readonly VITE_CLERK_DOMAIN?: string
readonly VITE_CLERK_IS_SATELLITE?: string
readonly VITE_CLERK_PROXY_URL?: string
readonly VITE_CLERK_SIGN_IN_URL?: string
readonly VITE_CLERK_SIGN_UP_URL?: string
readonly VITE_CLERK_SIGN_IN_FORCE_REDIRECT_URL?: string
readonly VITE_CLERK_SIGN_UP_FORCE_REDIRECT_URL?: string
readonly VITE_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL?: string
readonly VITE_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL?: string
}

declare global {
interface ImportMeta {
env: {
env: InternalEnv & {
NODE_ENV: 'production' | 'development'
PROD: boolean
DEV: boolean
}
}
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'production' | 'development'
PROD: boolean
DEV: boolean
}
}
}
Expand Down
81 changes: 68 additions & 13 deletions src/clerk.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { Component } from 'solid-js'
import { parsePublishableKey } from '@clerk/shared/keys'
import { createScriptLoader } from './script-loader'
import { ClerkOptions, MultiDomainAndOrProxy, SDKMetadata, Without } from '@clerk/types'
import { CSRStore, setClerk, setCsrStore } from './stores'
import type { ClerkOptions, MultiDomainAndOrProxy, SDKMetadata, Without } from '@clerk/types'
import type { Component } from 'solid-js'
import { createDevOrStagingUrlCache, parsePublishableKey } from '@clerk/shared/keys'
import { handleValueOrFn } from '@clerk/shared/handleValueOrFn'
import { reconcile } from 'solid-js/store'
import { useNavigate } from '@solidjs/router'
import { addClerkPrefix } from '@clerk/shared/url'
import { isValidProxyUrl, proxyUrlToAbsoluteURL } from '@clerk/shared/proxy'

import { CSRStore, setClerk, setCsrStore } from './stores'
import { createScriptLoader } from './script-loader'
import { getSafeEnv } from './utils/get-env'

export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
// Clerk?: ClerkProp;
clerkJSUrl?: string
clerkJSVariant?: 'headless' | ''
clerkJSVersion?: string
sdkMetadata?: SDKMetadata
publishableKey: string
publishableKey?: string
} & MultiDomainAndOrProxy

export type ClerkProviderProps = IsomorphicClerkOptions & {
Expand All @@ -21,48 +26,98 @@ export type ClerkProviderProps = IsomorphicClerkOptions & {
}

type BuildClerkJsScriptOptions = {
proxyUrl: string
domain: string
clerkJSUrl?: string
clerkJSVariant?: 'headless' | ''
clerkJSVersion?: string
publishableKey: string
}
const { isDevOrStagingUrl } = createDevOrStagingUrlCache()

const clerkJsScriptUrl = (opts: BuildClerkJsScriptOptions) => {
const { clerkJSUrl, clerkJSVariant, publishableKey } = opts
const { clerkJSUrl, clerkJSVariant, clerkJSVersion, proxyUrl, domain, publishableKey } = opts

if (clerkJSUrl) {
return clerkJSUrl
}

let scriptHost = ''
scriptHost = parsePublishableKey(publishableKey)?.frontendApi || ''
if (!!proxyUrl && isValidProxyUrl(proxyUrl)) {
scriptHost = proxyUrlToAbsoluteURL(proxyUrl).replace(/http(s)?:\/\//, '')
} else if (domain && !isDevOrStagingUrl(parsePublishableKey(publishableKey)?.frontendApi || '')) {
scriptHost = addClerkPrefix(domain)
} else {
scriptHost = parsePublishableKey(publishableKey)?.frontendApi || ''
}

const variant = clerkJSVariant ? `${clerkJSVariant.replace(/\.+$/, '')}.` : ''
const version = '5'
const version = clerkJSVersion || '5'
return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.${variant}browser.js`
}

function buildClerkHotloadScript(options: BuildClerkJsScriptOptions) {
return clerkJsScriptUrl(options)
}

export const mergeSolidClerkPropsWithEnv = (props: Omit<ClerkProviderProps, 'children'>) => {
return {
...props,
publishableKey: props.publishableKey || getSafeEnv().publishableKey || '',
clerkJSUrl: props.clerkJSUrl || getSafeEnv().clerkJsUrl,
clerkJSVersion: props.clerkJSVersion || getSafeEnv().clerkJsVersion,
proxyUrl: props.proxyUrl || getSafeEnv().proxyUrl || '',
domain: props.domain || getSafeEnv().domain || '',
isSatellite: props.isSatellite || getSafeEnv().isSatellite,
signInUrl: props.signInUrl || getSafeEnv().signInUrl || '',
signUpUrl: props.signUpUrl || getSafeEnv().signUpUrl || '',
signInForceRedirectUrl:
props.signInForceRedirectUrl || getSafeEnv().signInForceRedirectUrl || '',
signUpForceRedirectUrl:
props.signUpForceRedirectUrl || getSafeEnv().signInForceRedirectUrl || '',
signInFallbackRedirectUrl:
props.signInFallbackRedirectUrl || getSafeEnv().signInFallbackRedirectUrl || '',
signUpFallbackRedirectUrl:
props.signUpFallbackRedirectUrl || getSafeEnv().signUpFallbackRedirectUrl || '',
// Not supporting the following as they are deprecated
// afterSignInUrl: props.afterSignInUrl,
// afterSignUpUrl: props.afterSignUpUrl,
}
}

export const Clerk: Component<ClerkProviderProps> = props => {
const navigate = useNavigate()
const mergedProps = mergeSolidClerkPropsWithEnv(props)
const proxyUrlStr = handleValueOrFn(mergedProps.proxyUrl, new URL(window.location.href), '')
const domainStr = handleValueOrFn(mergedProps.domain, new URL(window.location.href), '')

// Avoid re-initialization due to HMR
if (!window.Clerk) {
createScriptLoader({
src: buildClerkHotloadScript(props),
'data-clerk-publishable-key': props.publishableKey,
src: buildClerkHotloadScript({
...mergedProps,
proxyUrl: proxyUrlStr,
domain: domainStr,
}),
...(mergedProps.publishableKey
? { 'data-clerk-publishable-key': mergedProps.publishableKey }
: {}),
...(domainStr ? { 'data-clerk-domain': domainStr } : {}),
...(proxyUrlStr ? { 'data-clerk-proxy-url': proxyUrlStr } : {}),
async onLoad() {
if (!window.Clerk) {
return
}

const clerkJSInstance = window.Clerk

await window.Clerk.load({
await clerkJSInstance.load({
...mergedProps,
routerPush: to => navigate(to),
routerReplace: to => navigate(to, { replace: true }),
})

setClerk(window.Clerk)
setClerk(clerkJSInstance)

// TODO: add nano stores solid
// TODO: Create google one tap for solid
Expand Down
8 changes: 4 additions & 4 deletions src/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export type CSRStore = {

const [csrStore, setCsrStore] = createStore<CSRStore>({
isLoaded: false,
client: null,
user: null,
session: null,
organization: null,
client: undefined,
user: undefined,
session: undefined,
organization: undefined,
})

const authStore = createMemo(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ClerkOptions, ClientResource, LoadedClerk, Without } from '@clerk/types

declare global {
interface Window {
Clerk: HeadlessBrowserClerk
Clerk?: HeadlessBrowserClerk
}
}

Expand Down
43 changes: 43 additions & 0 deletions src/utils/get-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
function getContextEnvVar(envVarName: keyof ImportMeta['env']): string | undefined {
return import.meta.env[envVarName]
}

function getSafeEnv() {
return {
domain: getContextEnvVar('VITE_CLERK_DOMAIN'),
isSatellite: getContextEnvVar('VITE_CLERK_IS_SATELLITE') === 'true',
proxyUrl: getContextEnvVar('VITE_CLERK_PROXY_URL'),
publishableKey: getContextEnvVar('VITE_CLERK_PUBLISHABLE_KEY'),
secretKey: getContextEnvVar('CLERK_SECRET_KEY'),
signInUrl: getContextEnvVar('VITE_CLERK_SIGN_IN_URL'),
signUpUrl: getContextEnvVar('VITE_CLERK_SIGN_UP_URL'),
signInForceRedirectUrl: getContextEnvVar('VITE_CLERK_SIGN_IN_FORCE_REDIRECT_URL'),
signUpForceRedirectUrl: getContextEnvVar('VITE_CLERK_SIGN_UP_FORCE_REDIRECT_URL'),
signInFallbackRedirectUrl: getContextEnvVar('VITE_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL'),
signUpFallbackRedirectUrl: getContextEnvVar('VITE_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL'),
clerkJsUrl: getContextEnvVar('VITE_CLERK_JS_URL'),
clerkJsVariant: getContextEnvVar('VITE_CLERK_JS_VARIANT') as 'headless' | '' | undefined,
clerkJsVersion: getContextEnvVar('VITE_CLERK_JS_VERSION'),

// apiVersion: getContextEnvVar('CLERK_API_VERSION'),
// apiUrl: getContextEnvVar('CLERK_API_URL'),
}
}

/**
* This should be used in order to pass environment variables from the server safely to the client.
* When running an application with `wrangler pages dev` client side environment variables are not attached to `import.meta.env.*`
* This is not the case when deploying to cloudflare pages directly
* This is a way to get around it.
*/
function getClientSafeEnv() {
return {
domain: getContextEnvVar('VITE_CLERK_DOMAIN'),
isSatellite: getContextEnvVar('VITE_CLERK_IS_SATELLITE') === 'true',
proxyUrl: getContextEnvVar('VITE_CLERK_PROXY_URL'),
signInUrl: getContextEnvVar('VITE_CLERK_SIGN_IN_URL'),
signUpUrl: getContextEnvVar('VITE_CLERK_SIGN_UP_URL'),
}
}

export { getSafeEnv, getClientSafeEnv }

0 comments on commit ba4214c

Please sign in to comment.