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
2 changes: 0 additions & 2 deletions .eslintignore

This file was deleted.

13 changes: 0 additions & 13 deletions .eslintrc.cjs

This file was deleted.

16 changes: 0 additions & 16 deletions apps/storybook/.eslintrc.cjs

This file was deleted.

1 change: 1 addition & 0 deletions apps/storybook/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const __dirname = dirname(__filename);
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Storybook boilerplate; the return value feeds differently-typed addon/framework fields (migration ENG-1677)
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}
Expand Down
30 changes: 30 additions & 0 deletions apps/storybook/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import js from "@eslint/js";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import storybook from "eslint-plugin-storybook";
import globals from "globals";
import tseslint from "typescript-eslint";

export default [
{ ignores: ["dist/**", "node_modules/**", "storybook-static/**"] },
js.configs.recommended,
...tseslint.configs.recommended,
// eslint-plugin-react-hooks v7 exposes flat configs under `configs.flat.*`;
// the top-level `recommended-latest` is the legacy (eslintrc) format and crashes ESLint 9.
reactHooks.configs.flat.recommended,
...storybook.configs["flat/recommended"],
{
languageOptions: {
globals: {
...globals.browser,
...globals.es2020,
},
},
plugins: {
"react-refresh": reactRefresh,
},
rules: {
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
},
},
];
8 changes: 5 additions & 3 deletions apps/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"lint": "eslint . --config .eslintrc.cjs --ext .ts,.tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"typecheck": "tsc --noEmit",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
Expand All @@ -13,19 +13,21 @@
},
"devDependencies": {
"@chromatic-com/storybook": "5.0.2",
"@eslint/js": "9.39.5",
"@storybook/addon-a11y": "10.3.6",
"@storybook/addon-docs": "10.3.6",
"@storybook/addon-links": "10.3.6",
"@storybook/addon-onboarding": "10.3.6",
"@storybook/react-vite": "10.3.6",
"@tailwindcss/vite": "4.2.4",
"@typescript-eslint/eslint-plugin": "8.57.2",
"@typescript-eslint/parser": "8.57.2",
"@vitejs/plugin-react": "5.1.4",
"eslint-plugin-react-hooks": "7.1.1",
"eslint-plugin-react-refresh": "0.4.26",
"eslint-plugin-storybook": "10.3.6",
"globals": "16.5.0",
"storybook": "10.3.6",
"typescript": "5.9.3",
"typescript-eslint": "8.63.0",
"vite": "7.3.5"
}
}
7 changes: 0 additions & 7 deletions apps/web/.eslintignore

This file was deleted.

4 changes: 0 additions & 4 deletions apps/web/.eslintrc.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
const ts = new Date(trialEnd).getTime();
if (!Number.isFinite(ts)) return null;
const msPerDay = 86_400_000;
// eslint-disable-next-line react-hooks/purity -- migration ENG-1677
return Math.ceil((ts - Date.now()) / msPerDay);
}, [
isFormbricksCloud,
Expand Down Expand Up @@ -337,7 +338,7 @@
}
});
},
[router, organization.id, workspace.id]

Check warning on line 341 in apps/web/app/(app)/workspaces/[workspaceId]/components/MainNavigation.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useCallback has an unnecessary dependency: 'workspace.id'. Either exclude it or remove the dependency array
);

const switcherTriggerClasses = cn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@
break;
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Check warning on line 117 in apps/web/app/(app)/workspaces/[workspaceId]/settings/account/notifications/components/NotificationSwitch.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useEffect has missing dependencies: 'autoDisableNotificationElementId', 'autoDisableNotificationType', 'handleSwitchChange', 'isChecked', 'notificationSettings', 'notificationType', 'surveyOrWorkspaceOrOrganizationId', and 't'. Either include them or remove the dependency array

return (
<Switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,7 @@
} else {
reset();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditMode]);

Check warning on line 219 in apps/web/app/(app)/workspaces/[workspaceId]/settings/workspace/integrations/airtable/components/AddIntegrationModal.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useEffect has missing dependencies: 'defaultData', 'fetchTable', 'reset', and 'setIncludeVariables'. Either include them or remove the dependency array

const survey = watch("survey");
const includeVariables = watch("includeVariables");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@
type: dbProperties[fieldKey].type,
})) || []
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDatabase?.id]);

Check warning on line 113 in apps/web/app/(app)/workspaces/[workspaceId]/settings/workspace/integrations/notion/components/AddIntegrationModal.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useMemo has a missing dependency: 'selectedDatabase'. Either include it or remove the dependency array

const elementItems = useMemo(() => {
const mappedElements = selectedSurvey
Expand Down Expand Up @@ -156,8 +155,7 @@
}));

return [...mappedElements, ...variables, ...hiddenFields, ...Metadata, ...createdAt, ...personAttributes];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedSurvey?.id, contactAttributeKeys]);

Check warning on line 158 in apps/web/app/(app)/workspaces/[workspaceId]/settings/workspace/integrations/notion/components/AddIntegrationModal.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useMemo has missing dependencies: 'elements', 'selectedSurvey', and 't'. Either include them or remove the dependency array

useEffect(() => {
if (selectedIntegration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ const MappingErrorMessage = ({
default:
return null;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [error, col, elem, t]);

if (!error) return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@
const filters = useMemo(
() => getFormattedFilters(survey, selectedFilter, dateRange),

// eslint-disable-next-line react-hooks/exhaustive-deps
[selectedFilter, dateRange]

Check warning on line 54 in apps/web/app/(app)/workspaces/[workspaceId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useMemo has a missing dependency: 'survey'. Either include it or remove the dependency array
);

const searchParams = useSearchParams();
Expand Down Expand Up @@ -108,7 +107,7 @@
} finally {
setIsFetchingFirstPage(false);
}
}, [filters, responsesPerPage, surveyId]);

Check warning on line 110 in apps/web/app/(app)/workspaces/[workspaceId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array

useEffect(() => {
return registerAnalysisRefreshHandler(refetchResponses);
Expand Down Expand Up @@ -163,8 +162,7 @@
};
fetchFilteredResponses();
// page is intentionally omitted to avoid refetching after the initial page setup.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters, responsesPerPage, selectedFilter, dateRange, surveyId]);

Check warning on line 165 in apps/web/app/(app)/workspaces/[workspaceId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useEffect has missing dependencies: 'hasFilters' and 'page'. Either include them or remove the dependency array

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@
const filters = useMemo(
() => getFormattedFilters(survey, selectedFilter, dateRange),

// eslint-disable-next-line react-hooks/exhaustive-deps
[selectedFilter, dateRange]

Check warning on line 161 in apps/web/app/(app)/workspaces/[workspaceId]/surveys/[surveyId]/components/CustomFilter.tsx

View workflow job for this annotation

GitHub Actions / Run Linters / Linters

React Hook useMemo has a missing dependency: 'survey'. Either include it or remove the dependency array
);

const datePickerRef = useRef<HTMLDivElement>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ export const ElementFilterComboBox = ({
/>
) : (
<Command ref={commandRef} className="relative h-fit w-full min-w-0 overflow-visible bg-transparent">
{/* eslint-disable-next-line jsx-a11y/prefer-tag-over-role */}
<div
role="button"
tabIndex={isComboBoxDisabled ? -1 : 0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ export const ElementsComboBox = ({ options, selected, onChangeValue }: ElementCo
<Command
ref={commandRef}
className="relative h-fit w-full overflow-visible rounded-md border border-slate-300 bg-white hover:border-slate-400">
{/* eslint-disable-next-line jsx-a11y/prefer-tag-over-role */}
<div
role="button"
tabIndex={0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
// session cookie → Next.js route refresh → new `survey` reference; depending on it here would re-fire
// getSurveyFilterDataAction on every refresh while the popover is open, i.e. an infinite loop.
const surveyRef = useRef(survey);
// eslint-disable-next-line react-hooks/refs -- intentional latest-value ref (see above); the effect reads surveyRef.current, never renders from it
surveyRef.current = survey;

useEffect(() => {
Expand Down Expand Up @@ -160,7 +161,6 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
if (!isOpen) {
clearItem();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen]);

const handleAddNewFilter = () => {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/app/api/v1/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ describe("handleErrorResponse", () => {
});

test("returns 409 conflict for UniqueConstraintError", async () => {
const { response } = handleErrorResponse(new UniqueConstraintError("Action with name foo already exists"));
const { response } = handleErrorResponse(
new UniqueConstraintError("Action with name foo already exists")
);
expect(response.status).toBe(409);
const body = await response.json();
expect(body.code).toBe("conflict");
Expand Down
14 changes: 14 additions & 0 deletions apps/web/app/api/v3/feedbackRecords/[[...path]]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NextRequest } from "next/server";
import { proxyFeedbackRecordsRequest } from "@/modules/hub/feedback-records-proxy";

const handler = async (request: NextRequest): Promise<Response> => {
return await proxyFeedbackRecordsRequest(request);
};

export const GET = handler;
export const POST = handler;
export const PUT = handler;
export const PATCH = handler;
export const DELETE = handler;
export const HEAD = handler;
export const OPTIONS = handler;
1 change: 0 additions & 1 deletion apps/web/app/sentry/SentryProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export const SentryProvider = ({
});
}
// We only want to run this once
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return <>{children}</>;
Expand Down
14 changes: 14 additions & 0 deletions apps/web/app/v1/feedback-records/[[...path]]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NextRequest } from "next/server";
import { proxyFeedbackRecordsRequest } from "@/modules/hub/feedback-records-proxy";

const handler = async (request: NextRequest): Promise<Response> => {
return await proxyFeedbackRecordsRequest(request);
};

export const GET = handler;
export const POST = handler;
export const PUT = handler;
export const PATCH = handler;
export const DELETE = handler;
export const HEAD = handler;
export const OPTIONS = handler;
33 changes: 33 additions & 0 deletions apps/web/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import next from "@formbricks/eslint-config/next";

const config = [
// carried over from the legacy .eslintignore / ignorePatterns
{
ignores: [
".next/**",
"public/**",
"playwright/**",
"vendor/**",
"**/package.json",
"**/tsconfig.json",
],
},
...next,
{
rules: {
// runtime-only env read in integration/gen-boolean-client.mjs; hashing it in turbo.json is tracked separately (ENG-1682)
"turbo/no-undeclared-env-vars": ["error", { allowList: ["PATH"] }],
// TODO(ENG-1677): enable incrementally — pre-existing violations from the React Compiler-era react-hooks rules
"react-hooks/set-state-in-effect": "off",
"react-hooks/incompatible-library": "off",
"react-hooks/error-boundaries": "off",
"react-hooks/immutability": "off",
"react-hooks/preserve-manual-memoization": "off",
// Kept as a warning (not off): exhaustive-deps is the main guard against stale closures, and the
// web lint script has no `--max-warnings 0`, so it surfaces violations without blocking (ENG-1677).
"react-hooks/exhaustive-deps": "warn",
},
},
];

export default config;
1 change: 0 additions & 1 deletion apps/web/lib/useDocumentVisibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ export const useDocumentVisibility = (onVisible: () => void) => {
return () => {
document.removeEventListener("visibilitychange", listener);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};
1 change: 0 additions & 1 deletion apps/web/lib/utils/action-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const actionClient = createSafeActionClient({
},
});

// eslint-disable-next-line no-console -- This error needs to be logged for debugging server-side errors
logger.withContext({ eventId }).error(e, "SERVER ERROR");
return DEFAULT_SERVER_ERROR_MESSAGE;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const getSmiley = ({
const containerSize = size * 2;

const icon = (
// eslint-disable-next-line @next/next/no-img-element -- migration ENG-1677
<img
data-testid={faceIcons[iconIdx]}
src={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { beforeEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
import { resetDb } from "@/integration/reset-db";
import { hashSecret } from "@/lib/crypto";
// eslint-disable-next-line import/first
import { auth } from "@/modules/auth/lib/auth";
// eslint-disable-next-line import/first -- must import after the vi.mock above is registered
import * as auditHandler from "@/modules/ee/audit-logs/lib/handler";

// Capture audit emission without running the real background audit logging (DB writes via setImmediate).
Expand Down
8 changes: 3 additions & 5 deletions apps/web/modules/core/rate-limit/rate-limit-load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ let isRedisAvailable = false;
// Test Redis availability
async function checkRedisAvailability() {
try {
// eslint-disable-next-line @typescript-eslint/await-thenable
const redis = await cache.getRedisClient();
if (redis === null) {
console.log("Redis client is null - Redis not available");
Expand Down Expand Up @@ -147,7 +146,7 @@ describe("Rate Limiter Load Tests - Race Conditions", () => {
console.log("🟢 Rate Limiter Load Tests: Redis available - tests will run");

// Clear any existing test keys
// eslint-disable-next-line @typescript-eslint/await-thenable

const redis = await cache.getRedisClient();
if (redis) {
const testKeys = await redis.keys("fb:rate_limit:test:*");
Expand All @@ -166,7 +165,6 @@ describe("Rate Limiter Load Tests - Race Conditions", () => {
return;
}

// eslint-disable-next-line @typescript-eslint/await-thenable
const redis = await cache.getRedisClient();
if (redis) {
const testKeys = await redis.keys("fb:rate_limit:test:*");
Expand Down Expand Up @@ -329,7 +327,7 @@ describe("Rate Limiter Load Tests - Race Conditions", () => {
const identifier = "stress-test";

// Clear any existing keys first to ensure clean state
// eslint-disable-next-line @typescript-eslint/await-thenable

const redis = await cache.getRedisClient();
if (redis) {
const existingKeys = await redis.keys(`fb:rate_limit:${config.namespace}:*`);
Expand Down Expand Up @@ -458,7 +456,7 @@ describe("Rate Limiter Load Tests - Race Conditions", () => {
const identifier = "ttl-test-user";

// Clear any existing keys first
// eslint-disable-next-line @typescript-eslint/await-thenable

const redis = await cache.getRedisClient();
if (redis) {
const existingKeys = await redis.keys(`fb:rate_limit:${config.namespace}:*`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export function ChartRow({ chart, workspaceId, isReadOnly, directories }: Readon
return (
<>
{/* Cannot use native <button>; row contains dropdown trigger (nested interactive invalid) */}
{/* eslint-disable-next-line jsx-a11y/prefer-tag-over-role, jsx-a11y/no-static-element-interactions */}
<div
role={isReadOnly ? undefined : "button"}
tabIndex={isReadOnly ? undefined : 0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function AddExistingChartsDialog({
const [isAdding, setIsAdding] = useState(false);

const existingChartIdsRef = useRef(existingChartIds);
// eslint-disable-next-line react-hooks/refs -- migration ENG-1677
existingChartIdsRef.current = existingChartIds;

const loadCharts = useCallback(async () => {
Expand Down
Loading
Loading