Skip to content

Commit 544aa92

Browse files
fix: js sdk sonar issues (#4)
* fix: sonar issues * fix sonar issues * feedback and fixed new issues * fix sonar issues * reverted change * feedback * empty commit * checks --------- Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
1 parent 7821961 commit 544aa92

5 files changed

Lines changed: 95 additions & 71 deletions

File tree

apps/playground/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { ReactNode } from "react";
33
import "../globals.css";
44

5-
export default function RootLayout({ children }: { children: ReactNode }) {
5+
export default function RootLayout({ children }: { readonly children: ReactNode }) {
66
return (
77
<html lang="en" className="h-full bg-slate-50">
88
<body className="h-full">{children}</body>

apps/playground/app/page.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,18 @@ export default function HomePage(): React.JSX.Element {
2626
useEffect(() => {
2727
const initFormbricks = () => {
2828
const addFormbricksDebugParam = (): void => {
29-
const urlParams = new URLSearchParams(window.location.search);
30-
if (!urlParams.has("formbricksDebug")) {
31-
urlParams.set("formbricksDebug", "true");
32-
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
33-
window.history.replaceState({}, "", newUrl);
29+
const url = new URL(globalThis.location.href);
30+
if (!url.searchParams.has("formbricksDebug")) {
31+
url.searchParams.set("formbricksDebug", "true");
32+
globalThis.history.replaceState({}, "", url.href);
3433
}
3534
};
3635
addFormbricksDebugParam();
3736
if (
3837
process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID &&
3938
process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST
4039
) {
41-
void formbricks.setup({
40+
formbricks.setup({
4241
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
4342
appUrl: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
4443
});
@@ -126,7 +125,7 @@ export default function HomePage(): React.JSX.Element {
126125
<button
127126
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
128127
type="button"
129-
onClick={() => void formbricks.setUserId(userId)}
128+
onClick={() => formbricks.setUserId(userId)}
130129
>
131130
Set user ID
132131
</button>
@@ -171,7 +170,7 @@ export default function HomePage(): React.JSX.Element {
171170
<div>
172171
<button
173172
type="button"
174-
onClick={() => void formbricks.setAttribute("Plan", "Free")}
173+
onClick={() => formbricks.setAttribute("Plan", "Free")}
175174
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
176175
>
177176
Set Plan to &apos;Free&apos;
@@ -197,7 +196,7 @@ export default function HomePage(): React.JSX.Element {
197196
<div>
198197
<button
199198
type="button"
200-
onClick={() => void formbricks.setAttribute("Plan", "Paid")}
199+
onClick={() => formbricks.setAttribute("Plan", "Paid")}
201200
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
202201
>
203202
Set Plan to &apos;Paid&apos;
@@ -223,7 +222,7 @@ export default function HomePage(): React.JSX.Element {
223222
<div>
224223
<button
225224
type="button"
226-
onClick={() => void formbricks.setEmail("test@web.com")}
225+
onClick={() => formbricks.setEmail("test@web.com")}
227226
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
228227
>
229228
Set Email
@@ -248,7 +247,7 @@ export default function HomePage(): React.JSX.Element {
248247
<div>
249248
<button
250249
type="button"
251-
onClick={() => void formbricks.setAttributes(userAttributes)}
250+
onClick={() => formbricks.setAttributes(userAttributes)}
252251
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
253252
>
254253
Set Multiple Attributes
@@ -273,7 +272,7 @@ export default function HomePage(): React.JSX.Element {
273272
<div>
274273
<button
275274
type="button"
276-
onClick={() => void formbricks.setLanguage("de")}
275+
onClick={() => formbricks.setLanguage("de")}
277276
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
278277
>
279278
Set Language to &apos;de&apos;
@@ -299,7 +298,7 @@ export default function HomePage(): React.JSX.Element {
299298
<button
300299
type="button"
301300
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
302-
onClick={() => void formbricks.track("code")}
301+
onClick={() => formbricks.track("code")}
303302
>
304303
Code Action
305304
</button>
@@ -332,7 +331,7 @@ export default function HomePage(): React.JSX.Element {
332331
<button
333332
type="button"
334333
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
335-
onClick={() => void formbricks.logout()}
334+
onClick={() => formbricks.logout()}
336335
>
337336
Logout
338337
</button>

apps/playground/components/layout-app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Sidebar } from "./sidebar";
22

3-
export function LayoutApp({ children }: { children: React.ReactNode }): React.JSX.Element {
3+
export function LayoutApp({ children }: { readonly children: React.ReactNode }): React.JSX.Element {
44
return (
55
<div className="min-h-full">
66
{/* Static sidebar for desktop */}

apps/playground/globals.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
@import 'tailwindcss';
22

3+
/* NOSONAR */
34
@plugin '@tailwindcss/forms';
45

6+
/* NOSONAR */
57
@custom-variant dark (&:is(.dark *));
68

79
/*
@@ -13,11 +15,12 @@
1315
color utility to any element that depends on these defaults.
1416
*/
1517
@layer base {
18+
1619
*,
1720
::after,
1821
::before,
1922
::backdrop,
2023
::file-selector-button {
2124
border-color: var(--color-gray-200, currentcolor);
2225
}
23-
}
26+
}

packages/js/src/lib/load-formbricks.ts

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@
33
* Required for logging errors
44
*/
55

6+
import type { TFormbricks } from "../types/formbricks";
7+
8+
declare global {
9+
var formbricks: TFormbricks & {
10+
[key: string]: (...args: any[]) => any;
11+
};
12+
}
13+
614
type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };
715

816
let isInitializing = false;
917
let isInitialized = false;
1018
// Load the SDK, return the result
1119
const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> => {
12-
if (!window.formbricks) {
20+
if (!globalThis.formbricks) {
1321
const scriptTag = document.createElement("script");
1422
scriptTag.type = "text/javascript";
1523
scriptTag.src = `${apiHostParam}/js/formbricks.umd.cjs`;
@@ -45,64 +53,78 @@ const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> =>
4553

4654
const functionsToProcess: { prop: string; args: unknown[] }[] = [];
4755

48-
export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => {
49-
// all of this should happen when not initialized:
50-
if (!isInitialized) {
51-
// We need to still support init for backwards compatibility
52-
// but we should log a warning that the init method is deprecated
53-
if (prop === "setup") {
54-
if (isInitializing) {
55-
console.warn("🧱 Formbricks - Warning: Formbricks is already initializing.");
56-
return;
57-
}
58-
// reset the initialization state
59-
isInitializing = true;
60-
const argsTyped = args[0] as { appUrl: string; environmentId: string };
61-
const { appUrl, environmentId } = argsTyped;
56+
const validateSetupArgs = (args: unknown[]): { appUrl: string; environmentId: string } | null => {
57+
const argsTyped = args[0] as { appUrl: string; environmentId: string };
58+
const { appUrl, environmentId } = argsTyped;
6259

63-
if (!appUrl) {
64-
console.error("🧱 Formbricks - Error: appUrl is required");
65-
return;
66-
}
60+
if (!appUrl) {
61+
console.error("🧱 Formbricks - Error: appUrl is required");
62+
return null;
63+
}
6764

68-
if (!environmentId) {
69-
console.error("🧱 Formbricks - Error: environmentId is required");
70-
return;
71-
}
65+
if (!environmentId) {
66+
console.error("🧱 Formbricks - Error: environmentId is required");
67+
return null;
68+
}
7269

73-
const loadSDKResult = await loadFormbricksSDK(appUrl);
74-
if (loadSDKResult.ok) {
75-
if (window.formbricks) {
76-
const formbricksInstance = window.formbricks;
77-
// @ts-expect-error -- Required for dynamic function calls
78-
void formbricksInstance.setup(...args);
79-
isInitializing = false;
80-
isInitialized = true;
81-
// process the queued functions
82-
for (const { prop: functionProp, args: functionArgs } of functionsToProcess) {
83-
if (typeof formbricksInstance[functionProp as keyof typeof formbricksInstance] !== "function") {
84-
console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`);
85-
continue;
86-
}
87-
// @ts-expect-error -- Required for dynamic function calls
88-
(formbricksInstance[functionProp] as unknown)(...functionArgs);
89-
}
90-
}
91-
}
92-
} else {
93-
console.warn(
94-
"🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization."
95-
);
70+
return { appUrl, environmentId };
71+
};
72+
73+
const processQueuedFunctions = (formbricksInstance: any): void => {
74+
for (const { prop: functionProp, args: functionArgs } of functionsToProcess) {
75+
if (typeof formbricksInstance[functionProp as keyof typeof formbricksInstance] !== "function") {
76+
console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`);
77+
continue;
78+
}
79+
// @ts-expect-error -- Required for dynamic function calls
80+
(formbricksInstance[functionProp] as unknown)(...functionArgs);
81+
}
82+
};
9683

97-
functionsToProcess.push({ prop, args });
84+
const handleSetupCall = async (args: unknown[]): Promise<void> => {
85+
if (isInitializing) {
86+
console.warn("🧱 Formbricks - Warning: Formbricks is already initializing.");
87+
return;
88+
}
89+
const validatedArgs = validateSetupArgs(args);
90+
if (!validatedArgs) return;
91+
isInitializing = true;
92+
try {
93+
const loadSDKResult = await loadFormbricksSDK(validatedArgs.appUrl);
94+
if (!loadSDKResult.ok || !globalThis.formbricks) {
95+
console.error("🧱 Formbricks - Error: Failed to load Formbricks SDK");
96+
return;
9897
}
99-
} else if (window.formbricks) {
100-
// Access the default export for initialized state too
101-
const formbricksInstance = window.formbricks;
102-
type Formbricks = typeof formbricksInstance;
103-
type FunctionProp = keyof Formbricks;
104-
const functionPropTyped = prop as FunctionProp;
98+
const formbricksInstance = globalThis.formbricks;
10599
// @ts-expect-error -- Required for dynamic function calls
106-
await formbricksInstance[functionPropTyped](...args);
100+
await formbricksInstance.setup(...args);
101+
isInitialized = true;
102+
processQueuedFunctions(formbricksInstance);
103+
} catch (err) {
104+
console.error("🧱 Formbricks - Error: setup failed", err);
105+
} finally {
106+
isInitializing = false;
107+
}
108+
};
109+
const executeFormbricksMethod = async (prop: string, args: unknown[]): Promise<void> => {
110+
if (!globalThis.formbricks) return;
111+
112+
const formbricksInstance = globalThis.formbricks;
113+
type Formbricks = typeof formbricksInstance;
114+
type FunctionProp = keyof Formbricks;
115+
const functionPropTyped = prop as FunctionProp;
116+
await formbricksInstance[functionPropTyped](...args);
117+
};
118+
119+
export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => {
120+
if (isInitialized) {
121+
await executeFormbricksMethod(prop, args)
122+
} else if (prop === "setup") {
123+
await handleSetupCall(args);
124+
} else {
125+
console.warn(
126+
"🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization."
127+
);
128+
functionsToProcess.push({ prop, args });
107129
}
108130
};

0 commit comments

Comments
 (0)