Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,6 @@ public/og-images/*
yalc.lock
/public/doctree.json
/public/doctree-dev.json

# Claude Code local files
.claude/settings.local.json
2 changes: 1 addition & 1 deletion app/[[...path]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Fragment, useMemo} from 'react';
import * as Sentry from '@sentry/nextjs';
import {getMDXComponent} from 'mdx-bundler/client';
import {Metadata} from 'next';
import {notFound} from 'next/navigation';

Expand All @@ -20,6 +19,7 @@ import {
getPreviousNode,
nodeForPath,
} from 'sentry-docs/docTree';
import {getMDXComponent} from 'sentry-docs/getMDXComponent';
import {isDeveloperDocs} from 'sentry-docs/isDeveloperDocs';
import {
getDevDocsFrontMatter,
Expand Down
104 changes: 33 additions & 71 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,47 @@ import {REMOTE_IMAGE_PATTERNS} from './src/config/images';
import {redirects} from './redirects.js';

// Exclude build-time-only dependencies from serverless function bundles to stay under
// Vercel's 250MB limit. These packages (esbuild, mdx-bundler, sharp, etc.) are only
// needed during the build process to compile MDX and optimize assets. The compiled
// output is used at runtime, so bundling these ~150-200MB of dependencies would bloat
// functions unnecessarily and cause deployment failures.
//
// Note: mdx-bundler/client (getMDXComponent) is a tiny runtime module needed by
// app/[[...path]]/page.tsx, so we exclude only the build parts (dist/!(client)*).
// Vercel's 250MB limit. These packages are only needed during build to compile MDX and
// optimize assets. We use a local getMDXComponent (src/getMDXComponent.ts) instead of
// mdx-bundler/client to avoid CJS/ESM compatibility issues at runtime.
const sharedExcludes = [
'**/*.map',
'./.git/**/*',
'./apps/**/*',
'./.next/cache/mdx-bundler/**/*',
'./.next/cache/md-exports/**/*',
// Heavy build dependencies
'node_modules/@esbuild/**/*',
'node_modules/esbuild/**/*',
'node_modules/@aws-sdk/**/*',
'node_modules/@google-cloud/**/*',
'node_modules/prettier/**/*',
'node_modules/@prettier/**/*',
'node_modules/sharp/**/*',
'node_modules/mermaid/**/*',
// MDX processing dependencies (local getMDXComponent replaces mdx-bundler/client)
'node_modules/mdx-bundler/**/*',
'node_modules/rehype-preset-minify/**/*',
'node_modules/rehype-prism-plus/**/*',
'node_modules/rehype-prism-diff/**/*',
'node_modules/remark-gfm/**/*',
'node_modules/remark-mdx-images/**/*',
'node_modules/unified/**/*',
'node_modules/rollup/**/*',
];

const outputFileTracingExcludes = process.env.NEXT_PUBLIC_DEVELOPER_DOCS
? {
'/**/*': [
'**/*.map',
'./.git/**/*',
'./apps/**/*',
'./.next/cache/mdx-bundler/**/*',
'./.next/cache/md-exports/**/*',
'docs/**/*',
// Exclude heavy build dependencies
'node_modules/@esbuild/**/*',
'node_modules/esbuild/**/*',
'node_modules/@aws-sdk/**/*',
'node_modules/@google-cloud/**/*',
'node_modules/prettier/**/*',
'node_modules/@prettier/**/*',
'node_modules/sharp/**/*',
'node_modules/mermaid/**/*',
// Exclude MDX processing dependencies (but keep mdx-bundler/client for runtime)
'node_modules/mdx-bundler/dist/!(client*)',
'node_modules/mdx-bundler/node_modules/**/*',
'node_modules/rehype-preset-minify/**/*',
'node_modules/rehype-prism-plus/**/*',
'node_modules/rehype-prism-diff/**/*',
'node_modules/remark-gfm/**/*',
'node_modules/remark-mdx-images/**/*',
'node_modules/unified/**/*',
'node_modules/rollup/**/*',
],
}
? {'/**/*': [...sharedExcludes, 'docs/**/*']}
: {
'/**/*': [
'**/*.map',
'./.git/**/*',
'./.next/cache/mdx-bundler/**/*',
'./.next/cache/md-exports/**/*',
'./apps/**/*',
'develop-docs/**/*',
// Exclude heavy build dependencies
'node_modules/@esbuild/**/*',
'node_modules/esbuild/**/*',
'node_modules/@aws-sdk/**/*',
'node_modules/@google-cloud/**/*',
'node_modules/prettier/**/*',
'node_modules/@prettier/**/*',
'node_modules/sharp/**/*',
'node_modules/mermaid/**/*',
// Exclude MDX processing dependencies (but keep mdx-bundler/client for runtime)
'node_modules/mdx-bundler/dist/!(client*)',
'node_modules/mdx-bundler/node_modules/**/*',
'node_modules/rehype-preset-minify/**/*',
'node_modules/rehype-prism-plus/**/*',
'node_modules/rehype-prism-diff/**/*',
'node_modules/remark-gfm/**/*',
'node_modules/remark-mdx-images/**/*',
'node_modules/unified/**/*',
'node_modules/rollup/**/*',
],
'/**/*': [...sharedExcludes, 'develop-docs/**/*'],
'/platform-redirect': [
'**/*.gif',
'public/mdx-images/**/*',
'public/og-images/**/*',
'**/*.pdf',
],
'\\[\\[\\.\\.\\.path\\]\\]': [
// Exclude docs to save ~156MB, but allow specific files via outputFileTracingIncludes
// for pages that may be accessed at runtime (error pages, cold starts, etc.)
// Exclude docs to save ~156MB, allow specific files via outputFileTracingIncludes
'docs/**/*',
'node_modules/prettier/plugins',
'node_modules/rollup/dist',
Expand Down Expand Up @@ -138,12 +105,7 @@ const nextConfig = {
'@esbuild/linux-arm64',
'@esbuild/linux-x64',
'@esbuild/win32-x64',
// Note: mdx-bundler is intentionally NOT in serverExternalPackages.
// The package is ESM-only ("type": "module") and cannot be require()'d at runtime.
// Keeping it out allows webpack to bundle mdx-bundler/client properly while
// outputFileTracingExcludes still prevents the heavy build-time parts from
// being included in the serverless function bundle.
// Fixes: DOCS-A0W
// mdx-bundler fully excluded via outputFileTracingExcludes
'sharp',
'@aws-sdk/client-s3',
'@google-cloud/storage',
Expand Down
2 changes: 1 addition & 1 deletion src/components/include.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useMemo} from 'react';
import {getMDXComponent} from 'mdx-bundler/client';

import {getMDXComponent} from 'sentry-docs/getMDXComponent';
import {getFileBySlugWithCache} from 'sentry-docs/mdx';
import {mdxComponents} from 'sentry-docs/mdxComponents';

Expand Down
2 changes: 1 addition & 1 deletion src/components/platformContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import fs from 'fs';

import {cache, useMemo} from 'react';
import {getMDXComponent} from 'mdx-bundler/client';

import {getCurrentGuide, getDocsRootNode, getPlatform} from 'sentry-docs/docTree';
import {getMDXComponent} from 'sentry-docs/getMDXComponent';
import {getFileBySlugWithCache} from 'sentry-docs/mdx';
import {mdxComponents} from 'sentry-docs/mdxComponents';
import {serverContext} from 'sentry-docs/serverContext';
Expand Down
43 changes: 43 additions & 0 deletions src/getMDXComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Local implementation of getMDXComponent replacing mdx-bundler/client.
*
* Eliminates the runtime dependency on mdx-bundler/client which has CJS/ESM
* compatibility issues in Vercel serverless functions. Since getMDXComponent
* only needs React at runtime, we can safely inline this implementation.
*
* @see https://github.com/kentcdodds/mdx-bundler/blob/main/src/client.js
*/
import type {ComponentType, FunctionComponent} from 'react';
// Namespace imports required - MDX runtime expects React, ReactDOM, jsx_runtime in scope
// eslint-disable-next-line no-restricted-imports
import * as React from 'react';
import * as jsxRuntime from 'react/jsx-runtime';
// eslint-disable-next-line no-restricted-imports
import * as ReactDOM from 'react-dom';

export interface MDXContentProps {
[key: string]: unknown;
components?: Record<string, ComponentType>;
}

/**
* Takes compiled MDX code from bundleMDX and returns a React component.
*/
export function getMDXComponent(
code: string,
globals?: Record<string, unknown>
): FunctionComponent<MDXContentProps> {
return getMDXExport(code, globals).default;
}

/**
* Takes compiled MDX code from bundleMDX and returns all exports.
*/
export function getMDXExport<
ExportedObject = {default: FunctionComponent<MDXContentProps>},
>(code: string, globals?: Record<string, unknown>): ExportedObject {
const scope = {React, ReactDOM, _jsx_runtime: jsxRuntime, ...globals};
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
const fn = new Function(...Object.keys(scope), code);
return fn(...Object.values(scope));
}
Loading