mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-23 21:59:28 -05:00
fix: js package linting (#2868)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -30,5 +30,5 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: ["node_modules/", "dist/", "*.config.js", "*.d.ts"],
|
||||
ignorePatterns: ["node_modules/", "dist/", "*.config.js", "*.config.ts", "*.d.ts"],
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@next/eslint-plugin-next": "^14.2.4",
|
||||
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
||||
"@typescript-eslint/parser": "^7.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
||||
"@typescript-eslint/parser": "^7.16.0",
|
||||
"@vercel/style-guide": "^6.0.0",
|
||||
"eslint-config-next": "^14.2.4",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
||||
@@ -16,7 +16,7 @@ import { cn } from "@formbricks/lib/cn";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
|
||||
import { isLight, mixColor } from "@formbricks/lib/utils/colors";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { type TSurvey, TSurveyQuestionTypeEnum, type TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { RatingSmiley } from "@formbricks/ui/RatingSmiley";
|
||||
import { getNPSOptionColor, getRatingNumberOptionColor } from "../../utils";
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@ import { getQuestionResponseMapping } from "@formbricks/lib/responses";
|
||||
import { getOriginalFileNameFromUrl } from "@formbricks/lib/storage/utils";
|
||||
import type { TOrganization } from "@formbricks/types/organizations";
|
||||
import type { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestionType, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
type TSurvey,
|
||||
type TSurveyQuestionType,
|
||||
TSurveyQuestionTypeEnum,
|
||||
} from "@formbricks/types/surveys/types";
|
||||
import { EmailButton } from "../general/email-button";
|
||||
|
||||
export const renderEmailResponseValue = (response: string | string[], questionType: TSurveyQuestionType) => {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-library.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: ["@formbricks/eslint-config/library.js"],
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"@formbricks/js-core": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"terser": "^5.31.1",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-dts": "^3.9.1"
|
||||
|
||||
+10
-5
@@ -1,18 +1,23 @@
|
||||
import { TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
import { loadFormbricksToProxy } from "./shared/loadFormbricks";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment --
|
||||
* Required because it doesn't work without building otherwise
|
||||
*/
|
||||
import { type TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { type TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
import { loadFormbricksToProxy } from "./shared/load-formbricks";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite;
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const formbricksProxyHandler: ProxyHandler<TFormbricksApp> = {
|
||||
get(_target, prop, _receiver) {
|
||||
return (...args: any[]) => loadFormbricksToProxy(prop as string, "app", ...args);
|
||||
return (...args: unknown[]) => loadFormbricksToProxy(prop as string, "app", ...args);
|
||||
},
|
||||
};
|
||||
|
||||
const formbricksApp: TFormbricksApp = new Proxy({} as TFormbricksApp, formbricksProxyHandler);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export -- Required for UMD
|
||||
export default formbricksApp;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Simple queue for formbricks methods
|
||||
|
||||
export class MethodQueue {
|
||||
private queue: (() => Promise<void>)[] = [];
|
||||
private queue: (() => Promise<unknown>)[] = [];
|
||||
private isExecuting = false;
|
||||
|
||||
add = (method: () => Promise<void>) => {
|
||||
add = (method: () => Promise<unknown>): void => {
|
||||
this.queue.push(method);
|
||||
this.run();
|
||||
void this.run();
|
||||
};
|
||||
|
||||
private runNext = async () => {
|
||||
private runNext = async (): Promise<void> => {
|
||||
if (this.isExecuting) return;
|
||||
|
||||
const method = this.queue.shift();
|
||||
@@ -20,19 +20,19 @@ export class MethodQueue {
|
||||
} finally {
|
||||
this.isExecuting = false;
|
||||
if (this.queue.length > 0) {
|
||||
this.runNext();
|
||||
void this.runNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
run = async () => {
|
||||
run = async (): Promise<void> => {
|
||||
if (!this.isExecuting && this.queue.length > 0) {
|
||||
await this.runNext();
|
||||
}
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
clear = (): void => {
|
||||
this.queue = [];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access --
|
||||
* Required for dynamic function calls
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call --
|
||||
* Required for dynamic function calls
|
||||
*/
|
||||
|
||||
/*
|
||||
eslint-disable no-console --
|
||||
* Required for logging errors
|
||||
*/
|
||||
import { type Result, wrapThrowsAsync } from "@formbricks/types/error-handlers";
|
||||
import { MethodQueue } from "../method-queue";
|
||||
|
||||
let isInitializing = false;
|
||||
let isInitialized = false;
|
||||
const methodQueue = new MethodQueue();
|
||||
|
||||
// Load the SDK, return the result
|
||||
const loadFormbricksSDK = async (apiHost: string, sdkType: "app" | "website"): Promise<Result<void>> => {
|
||||
if (!window.formbricks) {
|
||||
const res = await fetch(`${apiHost}/api/packages/${sdkType}`);
|
||||
|
||||
// Failed to fetch the app package
|
||||
if (!res.ok) {
|
||||
return { ok: false, error: new Error(`Failed to load Formbricks ${sdkType} SDK`) };
|
||||
}
|
||||
|
||||
const sdkScript = await res.text();
|
||||
const scriptTag = document.createElement("script");
|
||||
scriptTag.innerHTML = sdkScript;
|
||||
document.head.appendChild(scriptTag);
|
||||
|
||||
const getFormbricks = async (): Promise<void> =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
reject(new Error(`Formbricks ${sdkType} SDK loading timed out`));
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
try {
|
||||
await getFormbricks();
|
||||
return { ok: true, data: undefined };
|
||||
} catch (error) {
|
||||
const err = error as { message?: string };
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(err.message ?? `Failed to load Formbricks ${sdkType} SDK`),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, data: undefined };
|
||||
};
|
||||
|
||||
// TODO: @pandeymangg - Fix these types
|
||||
// type FormbricksAppMethods = {
|
||||
// [K in keyof TFormbricksApp]: TFormbricksApp[K] extends Function ? K : never;
|
||||
// }[keyof TFormbricksApp];
|
||||
|
||||
// type FormbricksWebsiteMethods = {
|
||||
// [K in keyof TFormbricksWebsite]: TFormbricksWebsite[K] extends Function ? K : never;
|
||||
// }[keyof TFormbricksWebsite];
|
||||
|
||||
export const loadFormbricksToProxy = async (
|
||||
prop: string,
|
||||
sdkType: "app" | "website",
|
||||
...args: unknown[]
|
||||
// eslint-disable-next-line @typescript-eslint/require-await -- Required for dynamic function calls
|
||||
): Promise<void> => {
|
||||
const executeMethod = async (): Promise<unknown> => {
|
||||
try {
|
||||
if (window.formbricks) {
|
||||
// @ts-expect-error -- window.formbricks is a dynamic function
|
||||
return (await window.formbricks[prop](...args)) as unknown;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error("🧱 Formbricks - Global error: ", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
if (!isInitialized) {
|
||||
if (isInitializing) {
|
||||
methodQueue.add(executeMethod);
|
||||
} else if (prop === "init") {
|
||||
isInitializing = true;
|
||||
|
||||
const initialize = async (): Promise<unknown> => {
|
||||
const { apiHost } = args[0] as { apiHost: string };
|
||||
const loadSDKResult = (await wrapThrowsAsync(loadFormbricksSDK)(apiHost, sdkType)) as unknown as {
|
||||
ok: boolean;
|
||||
error: Error;
|
||||
};
|
||||
|
||||
if (!loadSDKResult.ok) {
|
||||
isInitializing = false;
|
||||
console.error(`🧱 Formbricks - Global error: ${loadSDKResult.error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (window.formbricks) {
|
||||
// @ts-expect-error -- args is an array
|
||||
await window.formbricks[prop](...args);
|
||||
isInitialized = true;
|
||||
isInitializing = false;
|
||||
}
|
||||
} catch (error) {
|
||||
isInitializing = false;
|
||||
console.error("🧱 Formbricks - Global error: ", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
methodQueue.add(initialize);
|
||||
} else {
|
||||
console.error(
|
||||
"🧱 Formbricks - Global error: You need to call formbricks.init before calling any other method"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// @ts-expect-error -- window.formbricks is a dynamic function
|
||||
if (window.formbricks && typeof window.formbricks[prop] !== "function") {
|
||||
console.error(
|
||||
`🧱 Formbricks - Global error: Formbricks ${sdkType} SDK does not support method ${String(prop)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
methodQueue.add(executeMethod);
|
||||
}
|
||||
};
|
||||
@@ -1,122 +0,0 @@
|
||||
import { Result, wrapThrowsAsync } from "../../../types/error-handlers";
|
||||
import { MethodQueue } from "../methodQueue";
|
||||
|
||||
let isInitializing = false;
|
||||
let isInitialized = false;
|
||||
const methodQueue = new MethodQueue();
|
||||
|
||||
// Load the SDK, return the result
|
||||
const loadFormbricksSDK = async (apiHost: string, sdkType: "app" | "website"): Promise<Result<void>> => {
|
||||
if (!window.formbricks) {
|
||||
const res = await fetch(`${apiHost}/api/packages/${sdkType}`);
|
||||
|
||||
// Failed to fetch the app package
|
||||
if (!res.ok) {
|
||||
return { ok: false, error: new Error(`Failed to load Formbricks ${sdkType} SDK`) };
|
||||
}
|
||||
|
||||
const sdkScript = await res.text();
|
||||
const scriptTag = document.createElement("script");
|
||||
scriptTag.innerHTML = sdkScript;
|
||||
document.head.appendChild(scriptTag);
|
||||
|
||||
const getFormbricks = async () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
reject(new Error(`Formbricks ${sdkType} SDK loading timed out`));
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
try {
|
||||
await getFormbricks();
|
||||
return { ok: true, data: undefined };
|
||||
} catch (error: any) {
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(error.message ?? `Failed to load Formbricks ${sdkType} SDK`),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, data: undefined };
|
||||
};
|
||||
|
||||
// TODO: @pandeymangg - Fix these types
|
||||
// type FormbricksAppMethods = {
|
||||
// [K in keyof TFormbricksApp]: TFormbricksApp[K] extends Function ? K : never;
|
||||
// }[keyof TFormbricksApp];
|
||||
|
||||
// type FormbricksWebsiteMethods = {
|
||||
// [K in keyof TFormbricksWebsite]: TFormbricksWebsite[K] extends Function ? K : never;
|
||||
// }[keyof TFormbricksWebsite];
|
||||
|
||||
export const loadFormbricksToProxy = async (prop: string, sdkType: "app" | "website", ...args: any[]) => {
|
||||
const executeMethod = async () => {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
return await (window.formbricks[prop] as Function)(...args);
|
||||
} catch (error) {
|
||||
console.error(`🧱 Formbricks - Global error: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
if (!isInitialized) {
|
||||
if (isInitializing) {
|
||||
methodQueue.add(executeMethod);
|
||||
} else {
|
||||
if (prop === "init") {
|
||||
isInitializing = true;
|
||||
|
||||
const initialize = async () => {
|
||||
const { apiHost } = args[0];
|
||||
const loadSDKResult = await wrapThrowsAsync(loadFormbricksSDK)(apiHost, sdkType);
|
||||
|
||||
if (!loadSDKResult.ok) {
|
||||
isInitializing = false;
|
||||
console.error(`🧱 Formbricks - Global error: ${loadSDKResult.error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await (window.formbricks[prop] as Function)(...args);
|
||||
isInitialized = true;
|
||||
isInitializing = false;
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
isInitializing = false;
|
||||
console.error(`🧱 Formbricks - Global error: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
methodQueue.add(initialize);
|
||||
} else {
|
||||
console.error(
|
||||
"🧱 Formbricks - Global error: You need to call formbricks.init before calling any other method"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
if (window.formbricks && typeof window.formbricks[prop] !== "function") {
|
||||
console.error(
|
||||
`🧱 Formbricks - Global error: Formbricks ${sdkType} SDK does not support method ${String(prop)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
methodQueue.add(executeMethod);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -1,18 +1,23 @@
|
||||
import { TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
import { loadFormbricksToProxy } from "./shared/loadFormbricks";
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment --
|
||||
* Required because it doesn't work without building otherwise
|
||||
*/
|
||||
import { type TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { type TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
import { loadFormbricksToProxy } from "./shared/load-formbricks";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite;
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const formbricksProxyHandler: ProxyHandler<TFormbricksWebsite> = {
|
||||
get(_target, prop, _receiver) {
|
||||
return (...args: any[]) => loadFormbricksToProxy(prop as string, "website", ...args);
|
||||
return (...args: unknown[]) => loadFormbricksToProxy(prop as string, "website", ...args);
|
||||
},
|
||||
};
|
||||
|
||||
const formbricksWebsite: TFormbricksWebsite = new Proxy({} as TFormbricksWebsite, formbricksProxyHandler);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export -- Required for UMD
|
||||
export default formbricksWebsite;
|
||||
|
||||
Reference in New Issue
Block a user