Files
Alaister Young 80e44df4be fix(studio): load integration overview markdown in the TanStack build
Every integration detail page's overview text was blank under the Vite
dev/TanStack build: the two MarkdownContent components loaded their copy
via a template-literal dynamic import
(import(`@/static-data/integrations/${id}/overview.md`)), which webpack
turns into a context module but Vite/Rolldown leaves untouched, so the
browser threw "TypeError: Failed to resolve module specifier" for every
integration (data_api, cron, queues, vault, graphiql, ...).

Replace the template-literal import with an explicit registry of literal
dynamic imports (static-data/integrations/overviews.ts) that both
bundlers can statically analyze and code-split, and add an mdRawLoader
Vite plugin mirroring next.config.ts's raw-loader rule so .md files
resolve to their text content under Vite without query-suffixed
specifiers (which would break the turbopack rule). A unit test keeps the
registry in sync with the overview.md files on disk, and types/markdown.d.ts
types the literal .md imports.

Claude-Session: https://claude.ai/code/session_01MLH8WVg8zBJnmZTYTV3ooo
2026-07-07 04:11:22 +08:00

64 lines
4.0 KiB
TypeScript

/**
* Registry of integration overview markdown, keyed by integration id
* (= directory name under `static-data/integrations/`).
*
* The import specifiers must stay as string literals: both bundlers can only
* code-split and apply their md-as-string loaders (raw-loader rule in
* next.config.ts, `mdRawLoader` plugin in vite.config.ts) to imports they can
* statically analyze. A template-literal specifier like
* ``import(`@/static-data/integrations/${id}/overview.md`)`` happens to work
* under webpack/turbopack (context modules) but is left untouched by
* Vite/Rolldown and throws `TypeError: Failed to resolve module specifier`
* at runtime in the TanStack build.
*
* `overviews.test.ts` asserts this map stays in sync with the files on disk —
* when adding a new `overview.md`, add its entry here.
*/
const INTEGRATION_OVERVIEWS = {
airtable_wrapper: () => import('@/static-data/integrations/airtable_wrapper/overview.md'),
auth0_wrapper: () => import('@/static-data/integrations/auth0_wrapper/overview.md'),
bigquery_wrapper: () => import('@/static-data/integrations/bigquery_wrapper/overview.md'),
cal_wrapper: () => import('@/static-data/integrations/cal_wrapper/overview.md'),
calendly_wrapper: () => import('@/static-data/integrations/calendly_wrapper/overview.md'),
cfd1_wrapper: () => import('@/static-data/integrations/cfd1_wrapper/overview.md'),
clerk_wrapper: () => import('@/static-data/integrations/clerk_wrapper/overview.md'),
clickhouse_wrapper: () => import('@/static-data/integrations/clickhouse_wrapper/overview.md'),
cognito_wrapper: () => import('@/static-data/integrations/cognito_wrapper/overview.md'),
cron: () => import('@/static-data/integrations/cron/overview.md'),
data_api: () => import('@/static-data/integrations/data_api/overview.md'),
firebase_wrapper: () => import('@/static-data/integrations/firebase_wrapper/overview.md'),
graphiql: () => import('@/static-data/integrations/graphiql/overview.md'),
hubspot_wrapper: () => import('@/static-data/integrations/hubspot_wrapper/overview.md'),
iceberg_wrapper: () => import('@/static-data/integrations/iceberg_wrapper/overview.md'),
logflare_wrapper: () => import('@/static-data/integrations/logflare_wrapper/overview.md'),
mssql_wrapper: () => import('@/static-data/integrations/mssql_wrapper/overview.md'),
notion_wrapper: () => import('@/static-data/integrations/notion_wrapper/overview.md'),
orb_wrapper: () => import('@/static-data/integrations/orb_wrapper/overview.md'),
paddle_wrapper: () => import('@/static-data/integrations/paddle_wrapper/overview.md'),
queues: () => import('@/static-data/integrations/queues/overview.md'),
redis_wrapper: () => import('@/static-data/integrations/redis_wrapper/overview.md'),
s3_vectors_wrapper: () => import('@/static-data/integrations/s3_vectors_wrapper/overview.md'),
s3_wrapper: () => import('@/static-data/integrations/s3_wrapper/overview.md'),
slack_wrapper: () => import('@/static-data/integrations/slack_wrapper/overview.md'),
snowflake_wrapper: () => import('@/static-data/integrations/snowflake_wrapper/overview.md'),
stripe_sync_engine: () => import('@/static-data/integrations/stripe_sync_engine/overview.md'),
stripe_wrapper: () => import('@/static-data/integrations/stripe_wrapper/overview.md'),
vault: () => import('@/static-data/integrations/vault/overview.md'),
webhooks: () => import('@/static-data/integrations/webhooks/overview.md'),
} satisfies Record<string, () => Promise<{ default: string }>>
export const INTEGRATION_OVERVIEW_IDS = Object.keys(
INTEGRATION_OVERVIEWS
) as (keyof typeof INTEGRATION_OVERVIEWS)[]
/**
* Loads the overview markdown for an integration. Resolves to `null` when the
* integration has no bundled overview (e.g. marketplace apps).
*/
export async function loadIntegrationOverview(integrationId: string): Promise<string | null> {
const load = INTEGRATION_OVERVIEWS[integrationId as keyof typeof INTEGRATION_OVERVIEWS]
if (!load) return null
const mod = await load()
return String(mod.default)
}