From 2b02ecaa6f91c4908e32df195005e6c6308ffc1a Mon Sep 17 00:00:00 2001 From: SohamBhattacharjee2003 <125297948+SohamBhattacharjee2003@users.noreply.github.com> Date: Wed, 8 Apr 2026 06:47:31 +0530 Subject: [PATCH] perf: implement dynamic imports for connector configs and forms (#1142) - Replace 23 static connector config imports with dynamic imports - Replace 10 static connect form imports with dynamic imports - Add component caching to avoid recreating dynamic components - Significantly reduces connector popup chunk size - Only selected connector components load on demand --- .../connector-popup/connect-forms/index.tsx | 56 ++++----- .../connector-configs/index.tsx | 109 ++++++------------ 2 files changed, 59 insertions(+), 106 deletions(-) diff --git a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx index b6d813748..20762c1b3 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx @@ -1,14 +1,5 @@ +import dynamic from "next/dynamic"; import type { FC } from "react"; -import { BaiduSearchApiConnectForm } from "./components/baidu-search-api-connect-form"; -import { BookStackConnectForm } from "./components/bookstack-connect-form"; -import { CirclebackConnectForm } from "./components/circleback-connect-form"; -import { ElasticsearchConnectForm } from "./components/elasticsearch-connect-form"; -import { GithubConnectForm } from "./components/github-connect-form"; -import { LinkupApiConnectForm } from "./components/linkup-api-connect-form"; -import { LumaConnectForm } from "./components/luma-connect-form"; -import { MCPConnectForm } from "./components/mcp-connect-form"; -import { ObsidianConnectForm } from "./components/obsidian-connect-form"; -import { TavilyApiConnectForm } from "./components/tavily-api-connect-form"; export interface ConnectFormProps { onSubmit: (data: { @@ -33,32 +24,31 @@ export interface ConnectFormProps { export type ConnectFormComponent = FC; +const formMap: Record Promise<{ default: FC }>> = { + TAVILY_API: () => import("./components/tavily-api-connect-form").then(m => ({ default: m.TavilyApiConnectForm })), + LINKUP_API: () => import("./components/linkup-api-connect-form").then(m => ({ default: m.LinkupApiConnectForm })), + BAIDU_SEARCH_API: () => import("./components/baidu-search-api-connect-form").then(m => ({ default: m.BaiduSearchApiConnectForm })), + ELASTICSEARCH_CONNECTOR: () => import("./components/elasticsearch-connect-form").then(m => ({ default: m.ElasticsearchConnectForm })), + BOOKSTACK_CONNECTOR: () => import("./components/bookstack-connect-form").then(m => ({ default: m.BookStackConnectForm })), + GITHUB_CONNECTOR: () => import("./components/github-connect-form").then(m => ({ default: m.GithubConnectForm })), + LUMA_CONNECTOR: () => import("./components/luma-connect-form").then(m => ({ default: m.LumaConnectForm })), + CIRCLEBACK_CONNECTOR: () => import("./components/circleback-connect-form").then(m => ({ default: m.CirclebackConnectForm })), + MCP_CONNECTOR: () => import("./components/mcp-connect-form").then(m => ({ default: m.MCPConnectForm })), + OBSIDIAN_CONNECTOR: () => import("./components/obsidian-connect-form").then(m => ({ default: m.ObsidianConnectForm })), +}; + +const componentCache = new Map(); + /** * Factory function to get the appropriate connect form component for a connector type */ export function getConnectFormComponent(connectorType: string): ConnectFormComponent | null { - switch (connectorType) { - case "TAVILY_API": - return TavilyApiConnectForm; - case "LINKUP_API": - return LinkupApiConnectForm; - case "BAIDU_SEARCH_API": - return BaiduSearchApiConnectForm; - case "ELASTICSEARCH_CONNECTOR": - return ElasticsearchConnectForm; - case "BOOKSTACK_CONNECTOR": - return BookStackConnectForm; - case "GITHUB_CONNECTOR": - return GithubConnectForm; - case "LUMA_CONNECTOR": - return LumaConnectForm; - case "CIRCLEBACK_CONNECTOR": - return CirclebackConnectForm; - case "MCP_CONNECTOR": - return MCPConnectForm; - case "OBSIDIAN_CONNECTOR": - return ObsidianConnectForm; - default: - return null; + const loader = formMap[connectorType]; + if (!loader) return null; + + if (!componentCache.has(connectorType)) { + componentCache.set(connectorType, dynamic(loader, { ssr: false })); } + + return componentCache.get(connectorType)!; } diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx index a63435260..fc378612f 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx @@ -1,30 +1,8 @@ "use client"; +import dynamic from "next/dynamic"; import type { FC } from "react"; import type { SearchSourceConnector } from "@/contracts/types/connector.types"; -import { BaiduSearchApiConfig } from "./components/baidu-search-api-config"; -import { BookStackConfig } from "./components/bookstack-config"; -import { CirclebackConfig } from "./components/circleback-config"; -import { ClickUpConfig } from "./components/clickup-config"; -import { ComposioCalendarConfig } from "./components/composio-calendar-config"; -import { ComposioDriveConfig } from "./components/composio-drive-config"; -import { ComposioGmailConfig } from "./components/composio-gmail-config"; -import { ConfluenceConfig } from "./components/confluence-config"; -import { DiscordConfig } from "./components/discord-config"; -import { DropboxConfig } from "./components/dropbox-config"; -import { ElasticsearchConfig } from "./components/elasticsearch-config"; -import { GithubConfig } from "./components/github-config"; -import { GoogleDriveConfig } from "./components/google-drive-config"; -import { JiraConfig } from "./components/jira-config"; -import { LinkupApiConfig } from "./components/linkup-api-config"; -import { LumaConfig } from "./components/luma-config"; -import { MCPConfig } from "./components/mcp-config"; -import { ObsidianConfig } from "./components/obsidian-config"; -import { OneDriveConfig } from "./components/onedrive-config"; -import { SlackConfig } from "./components/slack-config"; -import { TavilyApiConfig } from "./components/tavily-api-config"; -import { TeamsConfig } from "./components/teams-config"; -import { WebcrawlerConfig } from "./components/webcrawler-config"; export interface ConnectorConfigProps { connector: SearchSourceConnector; @@ -35,61 +13,46 @@ export interface ConnectorConfigProps { export type ConnectorConfigComponent = FC; +const configMap: Record Promise<{ default: FC }>> = { + GOOGLE_DRIVE_CONNECTOR: () => import("./components/google-drive-config").then(m => ({ default: m.GoogleDriveConfig })), + TAVILY_API: () => import("./components/tavily-api-config").then(m => ({ default: m.TavilyApiConfig })), + LINKUP_API: () => import("./components/linkup-api-config").then(m => ({ default: m.LinkupApiConfig })), + BAIDU_SEARCH_API: () => import("./components/baidu-search-api-config").then(m => ({ default: m.BaiduSearchApiConfig })), + WEBCRAWLER_CONNECTOR: () => import("./components/webcrawler-config").then(m => ({ default: m.WebcrawlerConfig })), + ELASTICSEARCH_CONNECTOR: () => import("./components/elasticsearch-config").then(m => ({ default: m.ElasticsearchConfig })), + SLACK_CONNECTOR: () => import("./components/slack-config").then(m => ({ default: m.SlackConfig })), + DISCORD_CONNECTOR: () => import("./components/discord-config").then(m => ({ default: m.DiscordConfig })), + TEAMS_CONNECTOR: () => import("./components/teams-config").then(m => ({ default: m.TeamsConfig })), + DROPBOX_CONNECTOR: () => import("./components/dropbox-config").then(m => ({ default: m.DropboxConfig })), + ONEDRIVE_CONNECTOR: () => import("./components/onedrive-config").then(m => ({ default: m.OneDriveConfig })), + CONFLUENCE_CONNECTOR: () => import("./components/confluence-config").then(m => ({ default: m.ConfluenceConfig })), + BOOKSTACK_CONNECTOR: () => import("./components/bookstack-config").then(m => ({ default: m.BookStackConfig })), + GITHUB_CONNECTOR: () => import("./components/github-config").then(m => ({ default: m.GithubConfig })), + JIRA_CONNECTOR: () => import("./components/jira-config").then(m => ({ default: m.JiraConfig })), + CLICKUP_CONNECTOR: () => import("./components/clickup-config").then(m => ({ default: m.ClickUpConfig })), + LUMA_CONNECTOR: () => import("./components/luma-config").then(m => ({ default: m.LumaConfig })), + CIRCLEBACK_CONNECTOR: () => import("./components/circleback-config").then(m => ({ default: m.CirclebackConfig })), + MCP_CONNECTOR: () => import("./components/mcp-config").then(m => ({ default: m.MCPConfig })), + OBSIDIAN_CONNECTOR: () => import("./components/obsidian-config").then(m => ({ default: m.ObsidianConfig })), + COMPOSIO_GOOGLE_DRIVE_CONNECTOR: () => import("./components/composio-drive-config").then(m => ({ default: m.ComposioDriveConfig })), + COMPOSIO_GMAIL_CONNECTOR: () => import("./components/composio-gmail-config").then(m => ({ default: m.ComposioGmailConfig })), + COMPOSIO_GOOGLE_CALENDAR_CONNECTOR: () => import("./components/composio-calendar-config").then(m => ({ default: m.ComposioCalendarConfig })), +}; + +const componentCache = new Map(); + /** * Factory function to get the appropriate config component for a connector type */ export function getConnectorConfigComponent( connectorType: string ): ConnectorConfigComponent | null { - switch (connectorType) { - case "GOOGLE_DRIVE_CONNECTOR": - return GoogleDriveConfig; - case "TAVILY_API": - return TavilyApiConfig; - case "LINKUP_API": - return LinkupApiConfig; - case "BAIDU_SEARCH_API": - return BaiduSearchApiConfig; - case "WEBCRAWLER_CONNECTOR": - return WebcrawlerConfig; - case "ELASTICSEARCH_CONNECTOR": - return ElasticsearchConfig; - case "SLACK_CONNECTOR": - return SlackConfig; - case "DISCORD_CONNECTOR": - return DiscordConfig; - case "TEAMS_CONNECTOR": - return TeamsConfig; - case "DROPBOX_CONNECTOR": - return DropboxConfig; - case "ONEDRIVE_CONNECTOR": - return OneDriveConfig; - case "CONFLUENCE_CONNECTOR": - return ConfluenceConfig; - case "BOOKSTACK_CONNECTOR": - return BookStackConfig; - case "GITHUB_CONNECTOR": - return GithubConfig; - case "JIRA_CONNECTOR": - return JiraConfig; - case "CLICKUP_CONNECTOR": - return ClickUpConfig; - case "LUMA_CONNECTOR": - return LumaConfig; - case "CIRCLEBACK_CONNECTOR": - return CirclebackConfig; - case "MCP_CONNECTOR": - return MCPConfig; - case "OBSIDIAN_CONNECTOR": - return ObsidianConfig; - case "COMPOSIO_GOOGLE_DRIVE_CONNECTOR": - return ComposioDriveConfig; - case "COMPOSIO_GMAIL_CONNECTOR": - return ComposioGmailConfig; - case "COMPOSIO_GOOGLE_CALENDAR_CONNECTOR": - return ComposioCalendarConfig; - // OAuth connectors (Gmail, Calendar, Airtable, Notion) and others don't need special config UI - default: - return null; + const loader = configMap[connectorType]; + if (!loader) return null; + + if (!componentCache.has(connectorType)) { + componentCache.set(connectorType, dynamic(loader, { ssr: false })); } + + return componentCache.get(connectorType)!; }