Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code by tmilewski · Pull Request #4551 · clerk/javascript · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/afraid-toes-sin.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
'@clerk/clerk-react': patch
---

Include **BUILD_DISABLE_RHC** to allow for builds which remove remotely hosted code as it is a requirement for browser extensions.
5 changes: 5 additions & 0 deletions .changeset/tidy-garlics-boil.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/chrome-extension': major
---

Consume packages with remotely hosted code removed as required by Manifest v3.
5 changes: 3 additions & 2 deletions packages/chrome-extension/src/background/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

Clerk.mountComponentRenderer = undefined;

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
Clerk.mountComponentRenderer = undefined;
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
Expand Down
6 changes: 5 additions & 1 deletion packages/chrome-extension/src/internal/clerk.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import { Clerk } from '@clerk/clerk-js';
import { Clerk } from '@clerk/clerk-js/no-rhc';
import { DEV_BROWSER_JWT_KEY } from '@clerk/shared/devBrowser';
import { parsePublishableKey } from '@clerk/shared/keys';
import browser from 'webextension-polyfill';
Expand DownExpand Up@@ -32,6 +32,10 @@ export async function createClerkClient({
storageCache = BrowserStorageCache,
syncHost,
}: CreateClerkClientOptions): Promise<Clerk> {
if (scope === SCOPE.BACKGROUND) {
Clerk.mountComponentRenderer = undefined;
}

// Don't cache background scripts as it can result in out-of-sync client information.
if (clerk && scope !== SCOPE.BACKGROUND) {
return clerk;
Expand Down
2 changes: 1 addition & 1 deletion packages/chrome-extension/src/react/ClerkProvider.tsx
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
import type { Clerk } from '@clerk/clerk-js';
import type { Clerk } from '@clerk/clerk-js/no-rhc';
import type { ClerkProviderProps as ClerkReactProviderProps } from '@clerk/clerk-react';
import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
Expand Down
7 changes: 5 additions & 2 deletions packages/chrome-extension/tsup.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,27 @@ import type { Options } from 'tsup';
import { defineConfig } from 'tsup';

import { runAfterLast } from '../../scripts/utils';
// @ts-ignore
import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isWatch = !!overrideOptions.watch;
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/internal/index.ts', './src/react/index.ts'],
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts'],
bundle: true,
clean: true,
minify: false,
sourcemap: true,
legacyOutput: true,
treeshake: true,
noExternal: ['@clerk/clerk-react'],
external: ['use-sync-external-store'],
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${isWatch}`,
__BUILD_DISABLE_RHC__: 'true',
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/no-rhc/index.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
export { Clerk } from '../dist/types/index';

export * from '../dist/types/index';
1 change: 1 addition & 0 deletions packages/clerk-js/no-rhc/index.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
module.exports = require('../dist/clerk.no-rhc');
3 changes: 2 additions & 1 deletion packages/clerk-js/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,8 @@
"types": "dist/types/index.d.ts",
"files": [
"dist",
"headless"
"headless",
"no-rhc"
],
"scripts": {
"build": "pnpm build:bundle && pnpm build:declarations",
Expand Down
65 changes: 63 additions & 2 deletions packages/clerk-js/rspack.config.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,13 +11,15 @@ const isDevelopment = mode => !isProduction(mode);

const variants = {
clerk: 'clerk',
clerkNoRHC: 'clerk.no-rhc', // Omit Remotely Hosted Code
clerkBrowser: 'clerk.browser',
clerkHeadless: 'clerk.headless',
clerkHeadlessBrowser: 'clerk.headless.browser',
};

const variantToSourceFile = {
[variants.clerk]: './src/index.ts',
[variants.clerkNoRHC]: './src/index.ts',
[variants.clerkBrowser]: './src/index.browser.ts',
[variants.clerkHeadless]: './src/index.headless.ts',
[variants.clerkHeadlessBrowser]: './src/index.headless.browser.ts',
Expand All@@ -27,9 +29,10 @@ const variantToSourceFile = {
*
* @param {object} config
* @param {'development'|'production'} config.mode
* @param {boolean} [config.disableRHC=false]
* @returns { import('@rspack/cli').Configuration }
*/
const common = ({ mode }) => {
const common = ({ mode, disableRHC = false }) => {
return {
mode,
resolve: {
Expand All@@ -39,6 +42,7 @@ const common = ({ mode }) => {
},
plugins: [
new rspack.DefinePlugin({
__BUILD_DISABLE_RHC__: JSON.stringify(disableRHC),
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
Expand DownExpand Up@@ -400,12 +404,63 @@ const prodConfig = ({ mode, env, analysis }) => {
},
});

const clerkEsmNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
experiments: {
outputModule: true,
},
output: {
filename: '[name].mjs',
libraryTarget: 'module',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

const clerkCjsNoRHC = merge(
entryForVariant(variants.clerkNoRHC),
common({ mode, disableRHC: true }),
commonForProd(),
commonForProdBundled(),
{
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
},
plugins: [
// Include the lazy chunks in the bundle as well
// so that the final bundle can be imported and bundled again
// by a different bundler, eg the webpack instance used by react-scripts
new rspack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
optimization: {
splitChunks: false,
},
},
);

// webpack-bundle-analyzer only supports a single build, use clerkBrowser as that's the default build we serve
if (analysis) {
return [clerkBrowser];
}

return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkCjs];
return [clerkBrowser, clerkHeadless, clerkHeadlessBrowser, clerkEsm, clerkEsmNoRHC, clerkCjs, clerkCjsNoRHC];
};

/**
Expand DownExpand Up@@ -478,6 +533,12 @@ const devConfig = ({ mode, env }) => {
common({ mode }),
commonForDev(),
),
// prettier-ignore
[variants.clerkBrowserNoRHC]: merge(
entryForVariant(variants.clerkBrowserNoRHC),
common({ mode, disableRHC: true }),
commonForDev(),
),
[variants.clerkHeadless]: merge(
entryForVariant(variants.clerkHeadless),
common({ mode }),
Expand Down
60 changes: 51 additions & 9 deletions packages/clerk-js/src/core/clerk.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,7 @@ import {
clerkMissingSignInUrlAsSatellite,
clerkOAuthCallbackDidNotCompleteSignInSignUp,
clerkRedirectUrlIsMissingScheme,
clerkUnsupportedEnvironmentWarning,
} from './errors';
import { eventBus, events } from './events';
import type { FapiClient, FapiRequestCallback } from './fapiClient';
Expand DownExpand Up@@ -175,7 +176,7 @@ export class Clerk implements ClerkInterface {
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;

#publishableKey: string = '';
#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
#authService?: AuthCookieService;
Expand DownExpand Up@@ -263,7 +264,9 @@ export class Clerk implements ClerkInterface {
const publishableKey = parsePublishableKey(this.publishableKey);

if (!publishableKey) {
return errorThrower.throwInvalidPublishableKeyError({ key: this.publishableKey });
return errorThrower.throwInvalidPublishableKeyError({
key: this.publishableKey,
});
}

return publishableKey.frontendApi;
Expand DownExpand Up@@ -557,7 +560,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignIn = (node: HTMLDivElement, props?: SignInProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand All@@ -583,7 +586,7 @@ export class Clerk implements ClerkInterface {
};

public mountSignUp = (node: HTMLDivElement, props?: SignUpProps): void => {
if (props && props.__experimental?.newComponents && this.__experimental_ui) {
if (props?.__experimental?.newComponents && this.__experimental_ui) {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
Expand DownExpand Up@@ -1325,7 +1328,13 @@ export class Clerk implements ClerkInterface {
signUp,
verifyEmailPath:
params.verifyEmailAddressUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }),
buildURL(
{
base: displayConfig.signUpUrl,
hashPath: '/verify-email-address',
},
{ stringify: true },
),
verifyPhonePath:
params.verifyPhoneNumberUrl ||
buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }),
Expand DownExpand Up@@ -1486,6 +1495,11 @@ export class Clerk implements ClerkInterface {
public authenticateWithGoogleOneTap = async (
params: AuthenticateWithGoogleOneTapParams,
): Promise<SignInResource | SignUpResource> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Google One Tap');
return this.client!.signIn; // TODO: Remove not null assertion
}

return this.client?.signIn
.create({
strategy: 'google_one_tap',
Expand All@@ -1504,11 +1518,27 @@ export class Clerk implements ClerkInterface {
};

public authenticateWithMetamask = async (props: AuthenticateWithMetamaskParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_metamask_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Metamask');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_metamask_signature',
});
};

public authenticateWithCoinbaseWallet = async (props: AuthenticateWithCoinbaseWalletParams = {}): Promise<void> => {
await this.authenticateWithWeb3({ ...props, strategy: 'web3_coinbase_wallet_signature' });
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Coinbase Wallet');
return;
}

await this.authenticateWithWeb3({
...props,
strategy: 'web3_coinbase_wallet_signature',
});
};

public authenticateWithWeb3 = async ({
Expand All@@ -1519,6 +1549,11 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (__BUILD_DISABLE_RHC__) {
clerkUnsupportedEnvironmentWarning('Web3');
return;
}

if (!this.client || !this.environment) {
return;
}
Expand All@@ -1532,7 +1567,11 @@ export class Clerk implements ClerkInterface {

let signInOrSignUp: SignInResource | SignUpResource;
try {
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({ identifier, generateSignature, strategy });
signInOrSignUp = await this.client.signIn.authenticateWithWeb3({
identifier,
generateSignature,
strategy,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
signInOrSignUp = await this.client.signUp.authenticateWithWeb3({
Expand DownExpand Up@@ -1642,7 +1681,10 @@ export class Clerk implements ClerkInterface {
// 2. clerk-js initializes propA with a default value
// 3. The customer update propB independently of propA and window.Clerk.updateProps is called
// 4. If we don't merge the new props with the current options, propA will be reset to undefined
const props = { ..._props, options: this.#initOptions({ ...this.#options, ..._props.options }) };
const props = {
..._props,
options: this.#initOptions({ ...this.#options, ..._props.options }),
};
return this.#componentControls?.ensureMounted().then(controls => controls.updateProps(props));
};

Expand Down
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/errors.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
const errorPrefix = 'ClerkJS:';

/**
* Used to log a warning when a Clerk feature is used in an unsupported environment.
* (Development Only)
*
* @param strategy The strategy that is not supported in the current environment.
* @returns void
* @note This is a warning and not an error because the application will still work, but the feature will not be available.
*/
export function clerkUnsupportedEnvironmentWarning(strategy: string) {
console.warn(`${errorPrefix} ${strategy} is not supported in this environment.`);
}

export function clerkNetworkError(url: string, e: Error): never {
throw new Error(`${errorPrefix} Network error at "${url}" - ${e}. Please try again.`);
}
Expand All@@ -8,7 +20,7 @@ export function clerkErrorInitFailed(): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk.`);
}

export function clerkErrorDevInitFailed(msg: string = ''): never {
export function clerkErrorDevInitFailed(msg = ''): never {
throw new Error(`${errorPrefix} Something went wrong initializing Clerk in development mode.${msg && ` ${msg}`}`);
}

Expand Down
Loading