Skip to content
Open
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
125 changes: 125 additions & 0 deletions apps/docs/lib/mdx-scopes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* Type definitions for MDX scope variables.
*
* Each MDX content file receives a typed scope object via renderMDX.
* This file defines the scope interfaces for each MDX file and exports
* a registry mapping file paths to their expected scope types.
*/
import type {
PreloadMultiFileDiffResult,
PreloadedFileResult,
} from '@pierre/diffs/ssr';

import type { PackageManager } from '../app/docs/Installation/constants';

// =============================================================================
// Individual Scope Interfaces
// =============================================================================

export interface InstallationScope {
installationExamples: Record<PackageManager, PreloadedFileResult<undefined>>;
}

export interface CoreTypesScope {
fileContentsType: PreloadedFileResult<undefined>;
fileDiffMetadataType: PreloadedFileResult<undefined>;
parseDiffFromFileExample: PreloadedFileResult<undefined>;
parsePatchFilesExample: PreloadedFileResult<undefined>;
}

export interface OverviewScope {
initialDiffProps: PreloadMultiFileDiffResult<undefined>;
reactSingleFile: PreloadedFileResult<undefined>;
reactPatchFile: PreloadedFileResult<undefined>;
vanillaSingleFile: PreloadedFileResult<undefined>;
vanillaPatchFile: PreloadedFileResult<undefined>;
}

export interface ReactAPIScope {
reactAPIMultiFileDiff: PreloadedFileResult<undefined>;
reactAPIPatch: PreloadedFileResult<undefined>;
reactAPIFileDiff: PreloadedFileResult<undefined>;
reactAPIFile: PreloadedFileResult<undefined>;
sharedDiffOptions: PreloadedFileResult<undefined>;
sharedDiffRenderProps: PreloadedFileResult<undefined>;
sharedFileOptions: PreloadedFileResult<undefined>;
sharedFileRenderProps: PreloadedFileResult<undefined>;
}

export interface VanillaAPIScope {
fileDiffExample: PreloadedFileResult<undefined>;
fileExample: PreloadedFileResult<undefined>;
fileDiffProps: PreloadedFileResult<undefined>;
fileProps: PreloadedFileResult<undefined>;
customHunk: PreloadedFileResult<undefined>;
diffHunksRenderer: PreloadedFileResult<undefined>;
diffHunksRendererPatch: PreloadedFileResult<undefined>;
fileRenderer: PreloadedFileResult<undefined>;
}

export interface UtilitiesScope {
diffAcceptReject: PreloadedFileResult<undefined>;
diffAcceptRejectReact: PreloadedFileResult<undefined>;
disposeHighlighter: PreloadedFileResult<undefined>;
getSharedHighlighter: PreloadedFileResult<undefined>;
parseDiffFromFile: PreloadedFileResult<undefined>;
parsePatchFiles: PreloadedFileResult<undefined>;
preloadHighlighter: PreloadedFileResult<undefined>;
registerCustomTheme: PreloadedFileResult<undefined>;
setLanguageOverride: PreloadedFileResult<undefined>;
}

export interface StylingScope {
stylingGlobal: PreloadedFileResult<undefined>;
stylingInline: PreloadedFileResult<undefined>;
stylingUnsafe: PreloadedFileResult<undefined>;
}

export interface SSRScope {
usageServer: PreloadedFileResult<undefined>;
usageClient: PreloadedFileResult<undefined>;
preloadFileDiff: PreloadedFileResult<undefined>;
preloadMultiFileDiff: PreloadedFileResult<undefined>;
preloadPatchDiff: PreloadedFileResult<undefined>;
preloadFileResult: PreloadedFileResult<undefined>;
preloadPatchFile: PreloadedFileResult<undefined>;
}

export interface WorkerPoolScope {
helperVite: PreloadedFileResult<undefined>;
helperNextJS: PreloadedFileResult<undefined>;
vscodeLocalRoots: PreloadedFileResult<undefined>;
vscodeWorkerUri: PreloadedFileResult<undefined>;
vscodeInlineScript: PreloadedFileResult<undefined>;
vscodeCsp: PreloadedFileResult<undefined>;
vscodeGlobal: PreloadedFileResult<undefined>;
vscodeBlobUrl: PreloadedFileResult<undefined>;
vscodeFactory: PreloadedFileResult<undefined>;
helperWebpack: PreloadedFileResult<undefined>;
helperESBuild: PreloadedFileResult<undefined>;
helperStatic: PreloadedFileResult<undefined>;
helperVanilla: PreloadedFileResult<undefined>;
vanillaUsage: PreloadedFileResult<undefined>;
reactUsage: PreloadedFileResult<undefined>;
apiReference: PreloadedFileResult<undefined>;
cachingExample: PreloadedFileResult<undefined>;
architectureASCII: PreloadedFileResult<undefined>;
}

// =============================================================================
// Scope Registry - Maps file paths to their expected scope types
// =============================================================================

export interface MDXScopeRegistry {
'docs/Installation/content.mdx': InstallationScope;
'docs/CoreTypes/content.mdx': CoreTypesScope;
'docs/Overview/content.mdx': OverviewScope;
'docs/ReactAPI/content.mdx': ReactAPIScope;
'docs/VanillaAPI/content.mdx': VanillaAPIScope;
'docs/Utilities/content.mdx': UtilitiesScope;
'docs/Styling/content.mdx': StylingScope;
'docs/SSR/content.mdx': SSRScope;
'docs/WorkerPool/content.mdx': WorkerPoolScope;
}

export type MDXFilePath = keyof MDXScopeRegistry;
19 changes: 14 additions & 5 deletions apps/docs/lib/mdx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
VanillaComponentTabs,
VanillaPropTabs,
} from '../app/docs/VanillaAPI/ComponentTabs';
import type { MDXFilePath, MDXScopeRegistry } from './mdx-scopes';
import rehypeHierarchicalSlug from './rehype-hierarchical-slug';
import remarkTocIgnore from './remark-toc-ignore';

Expand Down Expand Up @@ -62,18 +63,24 @@ const defaultComponents = {
VanillaPropTabs,
};

interface RenderMDXOptions {
interface RenderMDXOptions<P extends MDXFilePath> {
/** Path to MDX file relative to app directory */
filePath: string;
filePath: P;
/** Data passed to MDX scope - available as variables in MDX */
scope?: Record<string, unknown>;
scope: MDXScopeRegistry[P];
}

/**
* Render an MDX file with components and scope data.
* Works in React Server Components with Turbopack.
*
* The scope parameter is type-checked against the MDXScopeRegistry
* to ensure the correct variables are passed for each MDX file.
*/
export async function renderMDX({ filePath, scope = {} }: RenderMDXOptions) {
export async function renderMDX<P extends MDXFilePath>({
filePath,
scope,
}: RenderMDXOptions<P>) {
const fullPath = join(process.cwd(), 'app', filePath);
const source = await readFile(fullPath, 'utf-8');

Expand All @@ -86,7 +93,9 @@ export async function renderMDX({ filePath, scope = {} }: RenderMDXOptions) {
remarkPlugins: [remarkGfm, remarkTocIgnore],
rehypePlugins: [[rehypeHierarchicalSlug, { levels: [2, 3, 4] }]],
},
scope,
// Cast to Record<string, unknown> for compileMDX compatibility
// Type safety is enforced at the renderMDX call site via MDXScopeRegistry
scope: scope as unknown as Record<string, unknown>,
},
});

Expand Down
4 changes: 4 additions & 0 deletions apps/docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"app/docs/**/*.mdx",
".next/types/**/*.ts",
"types/**/*.d.ts"
],
Expand Down Expand Up @@ -38,5 +39,8 @@
"paths": {
"@/*": ["./*"]
}
},
"mdx": {
"checkMdx": true
}
}
100 changes: 100 additions & 0 deletions apps/docs/types/mdx-globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Global type declarations for MDX scope variables.
*
* These variables are injected into MDX files via renderMDX's scope parameter.
* Declaring them globally allows the MDX language server to type-check their usage.
*/
import type {
PreloadMultiFileDiffResult,
PreloadedFileResult,
} from '@pierre/diffs/ssr';

import type { PackageManager } from '../app/docs/Installation/constants';

declare global {
// Installation scope
const installationExamples: Record<
PackageManager,
PreloadedFileResult<undefined>
>;

// CoreTypes scope
const fileContentsType: PreloadedFileResult<undefined>;
const fileDiffMetadataType: PreloadedFileResult<undefined>;
const parseDiffFromFileExample: PreloadedFileResult<undefined>;
const parsePatchFilesExample: PreloadedFileResult<undefined>;

// Overview scope
const initialDiffProps: PreloadMultiFileDiffResult<undefined>;
const reactSingleFile: PreloadedFileResult<undefined>;
const reactPatchFile: PreloadedFileResult<undefined>;
const vanillaSingleFile: PreloadedFileResult<undefined>;
const vanillaPatchFile: PreloadedFileResult<undefined>;

// ReactAPI scope
const reactAPIMultiFileDiff: PreloadedFileResult<undefined>;
const reactAPIPatch: PreloadedFileResult<undefined>;
const reactAPIFileDiff: PreloadedFileResult<undefined>;
const reactAPIFile: PreloadedFileResult<undefined>;
const sharedDiffOptions: PreloadedFileResult<undefined>;
const sharedDiffRenderProps: PreloadedFileResult<undefined>;
const sharedFileOptions: PreloadedFileResult<undefined>;
const sharedFileRenderProps: PreloadedFileResult<undefined>;

// VanillaAPI scope
const fileDiffExample: PreloadedFileResult<undefined>;
const fileExample: PreloadedFileResult<undefined>;
const fileDiffProps: PreloadedFileResult<undefined>;
const fileProps: PreloadedFileResult<undefined>;
const customHunk: PreloadedFileResult<undefined>;
const diffHunksRenderer: PreloadedFileResult<undefined>;
const diffHunksRendererPatch: PreloadedFileResult<undefined>;
const fileRenderer: PreloadedFileResult<undefined>;

// Utilities scope
const diffAcceptReject: PreloadedFileResult<undefined>;
const diffAcceptRejectReact: PreloadedFileResult<undefined>;
const disposeHighlighter: PreloadedFileResult<undefined>;
const getSharedHighlighter: PreloadedFileResult<undefined>;
const parseDiffFromFile: PreloadedFileResult<undefined>;
const parsePatchFiles: PreloadedFileResult<undefined>;
const preloadHighlighter: PreloadedFileResult<undefined>;
const registerCustomTheme: PreloadedFileResult<undefined>;
const setLanguageOverride: PreloadedFileResult<undefined>;

// Styling scope
const stylingGlobal: PreloadedFileResult<undefined>;
const stylingInline: PreloadedFileResult<undefined>;
const stylingUnsafe: PreloadedFileResult<undefined>;

// SSR scope
const usageServer: PreloadedFileResult<undefined>;
const usageClient: PreloadedFileResult<undefined>;
const preloadFileDiff: PreloadedFileResult<undefined>;
const preloadMultiFileDiff: PreloadedFileResult<undefined>;
const preloadPatchDiff: PreloadedFileResult<undefined>;
const preloadFileResult: PreloadedFileResult<undefined>;
const preloadPatchFile: PreloadedFileResult<undefined>;

// WorkerPool scope
const helperVite: PreloadedFileResult<undefined>;
const helperNextJS: PreloadedFileResult<undefined>;
const vscodeLocalRoots: PreloadedFileResult<undefined>;
const vscodeWorkerUri: PreloadedFileResult<undefined>;
const vscodeInlineScript: PreloadedFileResult<undefined>;
const vscodeCsp: PreloadedFileResult<undefined>;
const vscodeGlobal: PreloadedFileResult<undefined>;
const vscodeBlobUrl: PreloadedFileResult<undefined>;
const vscodeFactory: PreloadedFileResult<undefined>;
const helperWebpack: PreloadedFileResult<undefined>;
const helperESBuild: PreloadedFileResult<undefined>;
const helperStatic: PreloadedFileResult<undefined>;
const helperVanilla: PreloadedFileResult<undefined>;
const vanillaUsage: PreloadedFileResult<undefined>;
const reactUsage: PreloadedFileResult<undefined>;
const apiReference: PreloadedFileResult<undefined>;
const cachingExample: PreloadedFileResult<undefined>;
const architectureASCII: PreloadedFileResult<undefined>;
}

export {};