Build-time component capture and selective forwarding for nested MDX includes
A compile-time Unified/Recma package that analyzes compiled MDX modules, derives component requirements across nested include graphs, and rewrites MDX include callsites so each child receives only the components it actually renders.
- Compile-Time Capture: Collect local component usage and nested MDX include
edges from compiled
_createMdxContentoutput. - Reachability-Aware Graph Derivation: Build a canonical include graph from captured module facts, including shared children and cycles.
- Minimal Per-Include Forwarding: Rewrite each include boundary with only the components the direct child needs locally.
- Arbitrary Nested Depth: Preserve access to the root component map through
a hidden
__mdxRootComponentscarrier. - Small Runtime Surface: Generated output adds a compact
__pickMdxComponentshelper only when subset forwarding is needed.
By default, nested MDX includes often receive the full components map at
every boundary. That works, but it over-forwards unrelated components and makes
deep include chains harder to reason about.
This package splits the problem into three build-time stages:
- Capture per-module facts with
recmaCapture - Derive the reachable include graph with
buildResolvedComponentGraph - Rewrite include callsites with
recmaRewrite
The result is a boundary-local forwarding model:
page.mdx -> intro.mdxreceives onlyintro.mdx's local component needspage.mdx -> api.mdxreceives onlyapi.mdx's local component needsintro.mdx -> examples.mdxis resolved independentlyapi.mdx -> examples.mdxis resolved independently
This avoids transitive packing at parent boundaries while still supporting arbitrary nested depth.
npm install recma-component-resolver unified
# or
pnpm add recma-component-resolver unifiedThis package is recma-only. It expects compiled MDX JavaScript as input and is
typically used alongside an MDX compiler or bundler such as @mdx-js/mdx or
@mdx-js/esbuild.
If the goal is the shortest path to adoption:
- Run a first build with
recmaCapture - Derive the resolved component graph
- Run a second build with
recmaRewrite
import {
buildResolvedComponentGraph,
recmaCapture,
recmaRewrite,
type CapturedModule
} from 'recma-component-resolver';
const capturedModules: Record<string, CapturedModule> = {};
const runBuild = async (
entryPath: string,
recmaPlugins: Array<unknown>
): Promise<string> => {
// Run the existing MDX compile or bundle step here.
// Example integrations: @mdx-js/mdx, @mdx-js/esbuild, or a custom bundler.
return '';
};
await runBuild('/content/page.mdx', [
[
recmaCapture,
{
onModuleCapture: payload => {
capturedModules[payload.moduleId] = payload;
}
}
]
]);
const resolvedComponentGraph = buildResolvedComponentGraph(
'/content/page.mdx',
capturedModules
);
await runBuild('/content/page.mdx', [
[
recmaRewrite,
{
capturedModules,
requiredComponentsByImportIdentifier:
resolvedComponentGraph.requiredComponentsByImportIdentifier
}
]
]);resolvedComponentGraph.usedComponentNames can also be attached to emitted
metadata if the consuming runtime wants a complete list of reachable components.
This package has three explicit stages.
recmaCapture scans compiled _createMdxContent output and emits one
CapturedModule per compiled module.
Example source graph:
page.mdx
|
|-> intro.mdx -> examples.mdx
|
|-> api.mdx ---> examples.mdx (shared)Example local component usage:
page.mdxusesNoticeintro.mdxusesStepsapi.mdxusesCodeBlockandNoticeexamples.mdxusesPreview
Example capture result for page.mdx:
{
moduleId: '/content/page.mdx',
moduleReferencesByIdentifier: {
Intro: './intro.mdx',
Api: './api.mdx'
},
componentNames: ['Notice'],
includeSpecifiers: ['./intro.mdx', './api.mdx']
}Register the capture pass in the first build:
import {
recmaCapture,
type CapturedModule
} from 'recma-component-resolver';
const capturedModules: Record<string, CapturedModule> = {};
const capturePlugins = [
[
recmaCapture,
{
onModuleCapture: payload => {
capturedModules[payload.moduleId] = payload;
}
}
]
];buildResolvedComponentGraph(entryPath, capturedModules) converts raw capture
results into the data needed by the rewrite phase.
Example result for the graph above:
{
usedComponentNames: ['Notice', 'Steps', 'CodeBlock', 'Preview'],
requiredComponentsByImportIdentifier: {
'/content/page.mdx': {
Intro: ['Steps'],
Api: ['CodeBlock', 'Notice']
},
'/content/intro.mdx': {
Examples: ['Preview']
},
'/content/api.mdx': {
Examples: ['Preview']
},
'/content/examples.mdx': {}
}
}Important behavior:
- Reachability is discovered with BFS from the entry module
- Shared children are processed once
- Parent boundaries receive only the direct child's local requirements
- Grandchild requirements are not packed into parent-child subsets
recmaRewrite uses the captured modules plus the derived
requiredComponentsByImportIdentifier map to rewrite nested MDX include
callsites.
Register the rewrite pass in the second build:
import {
buildResolvedComponentGraph,
recmaRewrite,
type CapturedModule
} from 'recma-component-resolver';
const capturedModules: Record<string, CapturedModule> = {};
const resolvedComponentGraph = buildResolvedComponentGraph(
'/content/page.mdx',
capturedModules
);
const rewritePlugins = [
[
recmaRewrite,
{
capturedModules,
requiredComponentsByImportIdentifier:
resolvedComponentGraph.requiredComponentsByImportIdentifier
}
]
];Typical compiled output shape before rewrite:
_jsx(Api, { components: props.components })Typical compiled output shape after rewrite:
_jsx(
Api,
Object.assign({}, { components: props.components }, {
components: __pickMdxComponents(
props.__mdxRootComponents || props.components,
['CodeBlock', 'Notice']
),
__mdxRootComponents: props.__mdxRootComponents || props.components
})
)This keeps the direct child subset minimal while preserving a stable root component source for deeper nested boundaries.
import {
buildResolvedComponentGraph,
recmaCapture,
recmaRewrite,
type CapturedModule
} from 'recma-component-resolver';Recma plugin that captures per-module facts from compiled MDX output.
Options:
type CaptureOptions = {
onModuleCapture: (payload: CapturedModule) => void;
};Captured metadata for a single compiled content module.
type CapturedModule = {
moduleId: string;
moduleReferencesByIdentifier: Record<string, string>;
componentNames: Array<string>;
includeSpecifiers: Array<string>;
};Field meanings:
moduleId: canonical absolute module identifiermoduleReferencesByIdentifier: local include identifiers mapped to their original specifierscomponentNames: local custom component names in first-seen orderincludeSpecifiers: rendered nested include specifiers discovered in the module
Build-time derivation step that resolves the reachable include graph and computes per-boundary forwarding requirements.
Signature:
const resolvedComponentGraph = buildResolvedComponentGraph(
entryPath,
capturedModules
);Returned shape:
{
usedComponentNames: Array<string>,
requiredComponentsByImportIdentifier: Record<
string,
Record<string, Array<string>>
>
}Recma plugin that rewrites MDX include callsites with minimal component subsets.
Options:
type RewriteOptions = {
capturedModules: Record<string, CapturedModule>;
requiredComponentsByImportIdentifier?: Record<
string,
Record<string, Array<string>>
>;
};requiredComponentsByImportIdentifier is optional at the type level, but
selective forwarding depends on passing the derived map from
buildResolvedComponentGraph.
- Boundary-local subsets: Each include boundary receives only the direct child's local component requirements.
- No transitive packing: Parent boundaries do not accumulate grandchild or deeper descendant requirements.
- Root-direct access: Nested children resolve from
__mdxRootComponents || props.components, not through intermediate filtered subsets. - Shared include support: Shared children are handled correctly during graph derivation.
- Cycle tolerance: Reachability traversal processes each module once.
- Compiler-agnostic integration: Works with any MDX build pipeline that can register recma plugins and provide a stable module path per file.
- This package does not bundle MDX files by itself.
- This package does not resolve alias-based module specifiers.
- This package does not parse raw markdown; it operates on compiled MDX output.