fix: js package linting (#2868)

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Anshuman Pandey
2024-07-10 17:58:27 +05:30
committed by GitHub
parent 9d5a7b7dbd
commit 31c3fac7f5
13 changed files with 360 additions and 179 deletions
+1 -1
View File
@@ -30,5 +30,5 @@ module.exports = {
},
},
},
ignorePatterns: ["node_modules/", "dist/", "*.config.js", "*.d.ts"],
ignorePatterns: ["node_modules/", "dist/", "*.config.js", "*.config.ts", "*.d.ts"],
};
+2 -2
View File
@@ -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) => {
+5 -2
View File
@@ -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,
},
};
+1
View File
@@ -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
View File
@@ -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 = [];
};
}
+143
View File
@@ -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);
}
};
-122
View File
@@ -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;
}
};
+10 -5
View File
@@ -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;