Add MDX sourcemaps support

This commit is contained in:
sebastien 2025-04-11 10:01:03 +02:00
parent 29d19a6884
commit 109a0ebe60
4 changed files with 25 additions and 6 deletions

View file

@ -35,6 +35,7 @@
"remark-emoji": "^4.0.0", "remark-emoji": "^4.0.0",
"remark-frontmatter": "^5.0.0", "remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"source-map": "^0.7.4",
"stringify-object": "^3.3.0", "stringify-object": "^3.3.0",
"tslib": "^2.6.0", "tslib": "^2.6.0",
"unified": "^11.0.3", "unified": "^11.0.3",

View file

@ -22,6 +22,7 @@ import {
import type {WebpackCompilerName} from '@docusaurus/utils'; import type {WebpackCompilerName} from '@docusaurus/utils';
import type {Options} from './options'; import type {Options} from './options';
import type {LoaderContext} from 'webpack'; import type {LoaderContext} from 'webpack';
import type {Map as SourceMap} from 'vfile';
// TODO as of April 2023, no way to import/re-export this ESM type easily :/ // TODO as of April 2023, no way to import/re-export this ESM type easily :/
// This might change soon, likely after TS 5.2 // This might change soon, likely after TS 5.2
@ -30,6 +31,11 @@ type Pluggable = any; // TODO fix this asap
export type MDXPlugin = Pluggable; export type MDXPlugin = Pluggable;
type LoadMDXResult = {
content: string;
sourceMap: SourceMap | null | undefined;
};
async function loadMDX({ async function loadMDX({
fileContent, fileContent,
filePath, filePath,
@ -40,7 +46,7 @@ async function loadMDX({
filePath: string; filePath: string;
options: Options; options: Options;
compilerName: WebpackCompilerName; compilerName: WebpackCompilerName;
}): Promise<string> { }): Promise<LoadMDXResult> {
const {frontMatter} = await options.markdownConfig.parseFrontMatter({ const {frontMatter} = await options.markdownConfig.parseFrontMatter({
filePath, filePath,
fileContent, fileContent,
@ -120,7 +126,10 @@ ${exportsCode}
${result.content} ${result.content}
`; `;
return code; return {
content: code,
sourceMap: result.map,
};
} }
// Note: we cache promises instead of strings // Note: we cache promises instead of strings
@ -138,7 +147,7 @@ async function loadMDXWithCaching({
fileContent: string; fileContent: string;
options: Options; options: Options;
compilerName: WebpackCompilerName; compilerName: WebpackCompilerName;
}): Promise<string> { }): Promise<LoadMDXResult> {
const {crossCompilerCache} = options; const {crossCompilerCache} = options;
if (!crossCompilerCache) { if (!crossCompilerCache) {
return loadMDX({ return loadMDX({
@ -202,7 +211,7 @@ async function loadMDXWithCaching({
deleteCacheEntry(); deleteCacheEntry();
return cacheEntry.promise; return cacheEntry.promise;
} else { } else {
const {promise, resolve, reject} = promiseWithResolvers<string>(); const {promise, resolve, reject} = promiseWithResolvers<LoadMDXResult>();
crossCompilerCache.set(cacheKey, {promise, resolve, reject}); crossCompilerCache.set(cacheKey, {promise, resolve, reject});
return promise; return promise;
} }
@ -227,7 +236,7 @@ export async function mdxLoader(
options, options,
compilerName, compilerName,
}); });
return callback(null, result); return callback(null, result.content, result.sourceMap || undefined);
} catch (error) { } catch (error) {
return callback(error as Error); return callback(error as Error);
} }

View file

@ -9,6 +9,7 @@ import type {MDXOptions, SimpleProcessors} from './processor';
import type {MarkdownConfig} from '@docusaurus/types'; import type {MarkdownConfig} from '@docusaurus/types';
import type {ResolveMarkdownLink} from './remark/resolveMarkdownLinks'; import type {ResolveMarkdownLink} from './remark/resolveMarkdownLinks';
import type {PromiseWithResolvers} from './utils'; import type {PromiseWithResolvers} from './utils';
import type {Map as SourceMap} from 'vfile';
export type Options = Partial<MDXOptions> & { export type Options = Partial<MDXOptions> & {
dependencies?: string[]; dependencies?: string[];
@ -31,4 +32,7 @@ export type Options = Partial<MDXOptions> & {
crossCompilerCache?: Map<string, CrossCompilerCacheEntry>; // MDX => Promise<JSX> cache crossCompilerCache?: Map<string, CrossCompilerCacheEntry>; // MDX => Promise<JSX> cache
}; };
type CrossCompilerCacheEntry = PromiseWithResolvers<string>; type CrossCompilerCacheEntry = PromiseWithResolvers<{
content: string;
sourceMap: SourceMap | null | undefined;
}>;

View file

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {SourceMapGenerator} from 'source-map';
import headings from './remark/headings'; import headings from './remark/headings';
import contentTitle from './remark/contentTitle'; import contentTitle from './remark/contentTitle';
import toc from './remark/toc'; import toc from './remark/toc';
@ -23,6 +24,7 @@ import type {MDXFrontMatter} from './frontMatter';
import type {Options} from './options'; import type {Options} from './options';
import type {AdmonitionOptions} from './remark/admonitions'; import type {AdmonitionOptions} from './remark/admonitions';
import type {ProcessorOptions} from '@mdx-js/mdx'; import type {ProcessorOptions} from '@mdx-js/mdx';
import type {Map as SourceMap} from 'vfile';
// TODO as of April 2023, no way to import/re-export this ESM type easily :/ // TODO as of April 2023, no way to import/re-export this ESM type easily :/
// This might change soon, likely after TS 5.2 // This might change soon, likely after TS 5.2
@ -31,6 +33,7 @@ type Pluggable = any; // TODO fix this asap
export type SimpleProcessorResult = { export type SimpleProcessorResult = {
content: string; content: string;
map: SourceMap | null | undefined;
data: {[key: string]: unknown}; data: {[key: string]: unknown};
}; };
@ -187,6 +190,7 @@ async function createProcessorFactory() {
...processorOptions, ...processorOptions,
remarkRehypeOptions: options.markdownConfig.remarkRehypeOptions, remarkRehypeOptions: options.markdownConfig.remarkRehypeOptions,
format, format,
SourceMapGenerator,
}); });
return { return {
@ -202,6 +206,7 @@ async function createProcessorFactory() {
return mdxProcessor.process(vfile).then((result) => ({ return mdxProcessor.process(vfile).then((result) => ({
content: result.toString(), content: result.toString(),
data: result.data, data: result.data,
map: result.map,
})); }));
}, },
}; };