@docusaurus/mdx-loader 3.5.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +0 -1
  2. package/lib/createMDXLoader.d.ts +18 -0
  3. package/lib/createMDXLoader.d.ts.map +1 -0
  4. package/lib/createMDXLoader.js +48 -0
  5. package/lib/createMDXLoader.js.map +1 -0
  6. package/lib/index.d.ts +3 -1
  7. package/lib/index.d.ts.map +1 -1
  8. package/lib/index.js +4 -0
  9. package/lib/index.js.map +1 -1
  10. package/lib/loader.d.ts +1 -23
  11. package/lib/loader.d.ts.map +1 -1
  12. package/lib/loader.js +120 -139
  13. package/lib/loader.js.map +1 -1
  14. package/lib/options.d.ts +33 -0
  15. package/lib/options.d.ts.map +1 -0
  16. package/lib/options.js +9 -0
  17. package/lib/options.js.map +1 -0
  18. package/lib/preprocessor.d.ts +1 -1
  19. package/lib/preprocessor.d.ts.map +1 -1
  20. package/lib/processor.d.ts +12 -6
  21. package/lib/processor.d.ts.map +1 -1
  22. package/lib/processor.js +19 -15
  23. package/lib/processor.js.map +1 -1
  24. package/lib/remark/transformImage/index.d.ts.map +1 -1
  25. package/lib/remark/transformImage/index.js +11 -9
  26. package/lib/remark/transformImage/index.js.map +1 -1
  27. package/lib/remark/transformLinks/index.d.ts.map +1 -1
  28. package/lib/remark/transformLinks/index.js +11 -9
  29. package/lib/remark/transformLinks/index.js.map +1 -1
  30. package/lib/remark/unusedDirectives/index.d.ts.map +1 -1
  31. package/lib/remark/unusedDirectives/index.js +1 -0
  32. package/lib/remark/unusedDirectives/index.js.map +1 -1
  33. package/lib/utils.d.ts +41 -0
  34. package/lib/utils.d.ts.map +1 -0
  35. package/lib/utils.js +108 -0
  36. package/lib/utils.js.map +1 -0
  37. package/package.json +6 -6
  38. package/src/createMDXLoader.ts +67 -0
  39. package/src/index.ts +4 -1
  40. package/src/loader.ts +163 -198
  41. package/src/options.ts +32 -0
  42. package/src/preprocessor.ts +1 -1
  43. package/src/processor.ts +35 -31
  44. package/src/remark/transformImage/index.ts +17 -15
  45. package/src/remark/transformLinks/index.ts +19 -15
  46. package/src/remark/unusedDirectives/index.ts +1 -0
  47. package/src/types.d.mts +3 -1
  48. package/src/utils.ts +151 -0
@@ -12,8 +12,8 @@ import {
12
12
  toMessageRelativeFilePath,
13
13
  posixPath,
14
14
  escapePath,
15
- getFileLoaderUtils,
16
15
  findAsyncSequential,
16
+ getFileLoaderUtils,
17
17
  } from '@docusaurus/utils';
18
18
  import escapeHtml from 'escape-html';
19
19
  import {assetRequireAttributeValue, transformNode} from '../utils';
@@ -24,10 +24,6 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
24
24
  import type {Parent} from 'unist';
25
25
  import type {Link, Literal} from 'mdast';
26
26
 
27
- const {
28
- loaders: {inlineMarkdownLinkFileLoader},
29
- } = getFileLoaderUtils();
30
-
31
27
  type PluginOptions = {
32
28
  staticDirs: string[];
33
29
  siteDir: string;
@@ -35,6 +31,7 @@ type PluginOptions = {
35
31
 
36
32
  type Context = PluginOptions & {
37
33
  filePath: string;
34
+ inlineMarkdownLinkFileLoader: string;
38
35
  };
39
36
 
40
37
  type Target = [node: Link, index: number, parent: Parent];
@@ -45,7 +42,7 @@ type Target = [node: Link, index: number, parent: Parent];
45
42
  async function toAssetRequireNode(
46
43
  [node]: Target,
47
44
  assetPath: string,
48
- filePath: string,
45
+ context: Context,
49
46
  ) {
50
47
  // MdxJsxTextElement => see https://github.com/facebook/docusaurus/pull/8288#discussion_r1125871405
51
48
  const jsxNode = node as unknown as MdxJsxTextElement;
@@ -53,7 +50,7 @@ async function toAssetRequireNode(
53
50
 
54
51
  // require("assets/file.pdf") means requiring from a package called assets
55
52
  const relativeAssetPath = `./${posixPath(
56
- path.relative(path.dirname(filePath), assetPath),
53
+ path.relative(path.dirname(context.filePath), assetPath),
57
54
  )}`;
58
55
 
59
56
  const parsedUrl = url.parse(node.url);
@@ -65,7 +62,9 @@ async function toAssetRequireNode(
65
62
  path.extname(relativeAssetPath) === '.json'
66
63
  ? `${relativeAssetPath.replace('.json', '.raw')}!=`
67
64
  : ''
68
- }${inlineMarkdownLinkFileLoader}${escapePath(relativeAssetPath) + search}`;
65
+ }${context.inlineMarkdownLinkFileLoader}${
66
+ escapePath(relativeAssetPath) + search
67
+ }`;
69
68
 
70
69
  attributes.push({
71
70
  type: 'mdxJsxAttribute',
@@ -196,7 +195,7 @@ async function processLinkNode(target: Target, context: Context) {
196
195
  context,
197
196
  );
198
197
  if (assetPath) {
199
- await toAssetRequireNode(target, assetPath, context.filePath);
198
+ await toAssetRequireNode(target, assetPath, context);
200
199
  }
201
200
  }
202
201
 
@@ -204,14 +203,19 @@ export default function plugin(options: PluginOptions): Transformer {
204
203
  return async (root, vfile) => {
205
204
  const {visit} = await import('unist-util-visit');
206
205
 
206
+ const fileLoaderUtils = getFileLoaderUtils(
207
+ vfile.data.compilerName === 'server',
208
+ );
209
+ const context: Context = {
210
+ ...options,
211
+ filePath: vfile.path!,
212
+ inlineMarkdownLinkFileLoader:
213
+ fileLoaderUtils.loaders.inlineMarkdownLinkFileLoader,
214
+ };
215
+
207
216
  const promises: Promise<void>[] = [];
208
217
  visit(root, 'link', (node: Link, index, parent) => {
209
- promises.push(
210
- processLinkNode([node, index, parent!], {
211
- ...options,
212
- filePath: vfile.path!,
213
- }),
214
- );
218
+ promises.push(processLinkNode([node, index, parent!], context));
215
219
  });
216
220
  await Promise.all(promises);
217
221
  };
@@ -155,6 +155,7 @@ const plugin: Plugin = function plugin(this: Processor): Transformer {
155
155
  // We only enable these warnings for the client compiler
156
156
  // This avoids emitting duplicate warnings in prod mode
157
157
  // Note: the client compiler is used in both dev/prod modes
158
+ // Also: the client compiler is what gets used when using crossCompilerCache
158
159
  if (file.data.compilerName === 'client') {
159
160
  logUnusedDirectivesWarning({
160
161
  directives: unusedDirectives,
package/src/types.d.mts CHANGED
@@ -28,7 +28,9 @@ declare module 'unist' {
28
28
  }
29
29
 
30
30
  declare module 'mdast' {
31
- interface HeadingData {
31
+ interface Data {
32
32
  id?: string;
33
+ hName?: string;
34
+ hProperties?: Record<string, unknown>;
33
35
  }
34
36
  }
package/src/utils.ts ADDED
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import logger from '@docusaurus/logger';
9
+ import {escapePath, type WebpackCompilerName} from '@docusaurus/utils';
10
+ import {getProcessor, type SimpleProcessorResult} from './processor';
11
+ import {validateMDXFrontMatter} from './frontMatter';
12
+ import preprocessor from './preprocessor';
13
+ import type {Options} from './options';
14
+
15
+ /**
16
+ * Converts assets an object with Webpack require calls code.
17
+ * This is useful for mdx files to reference co-located assets using relative
18
+ * paths. Those assets should enter the Webpack assets pipeline and be hashed.
19
+ * For now, we only handle that for images and paths starting with `./`:
20
+ *
21
+ * `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
22
+ */
23
+ export function createAssetsExportCode({
24
+ assets,
25
+ inlineMarkdownAssetImageFileLoader,
26
+ }: {
27
+ assets: unknown;
28
+ inlineMarkdownAssetImageFileLoader: string;
29
+ }): string {
30
+ if (
31
+ typeof assets !== 'object' ||
32
+ !assets ||
33
+ Object.keys(assets).length === 0
34
+ ) {
35
+ return 'undefined';
36
+ }
37
+
38
+ // TODO implementation can be completed/enhanced
39
+ function createAssetValueCode(assetValue: unknown): string | undefined {
40
+ if (Array.isArray(assetValue)) {
41
+ const arrayItemCodes = assetValue.map(
42
+ (item: unknown) => createAssetValueCode(item) ?? 'undefined',
43
+ );
44
+ return `[${arrayItemCodes.join(', ')}]`;
45
+ }
46
+ // Only process string values starting with ./
47
+ // We could enhance this logic and check if file exists on disc?
48
+ if (typeof assetValue === 'string' && assetValue.startsWith('./')) {
49
+ // TODO do we have other use-cases than image assets?
50
+ // Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708)
51
+ return `require("${inlineMarkdownAssetImageFileLoader}${escapePath(
52
+ assetValue,
53
+ )}").default`;
54
+ }
55
+ return undefined;
56
+ }
57
+
58
+ const assetEntries = Object.entries(assets);
59
+
60
+ const codeLines = assetEntries
61
+ .map(([key, value]: [string, unknown]) => {
62
+ const assetRequireCode = createAssetValueCode(value);
63
+ return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined;
64
+ })
65
+ .filter(Boolean);
66
+
67
+ return `{\n${codeLines.join('\n')}\n}`;
68
+ }
69
+
70
+ /**
71
+ * data.contentTitle is set by the remark contentTitle plugin
72
+ */
73
+ export function extractContentTitleData(data: {
74
+ [key: string]: unknown;
75
+ }): string | undefined {
76
+ return data.contentTitle as string | undefined;
77
+ }
78
+
79
+ export async function compileToJSX({
80
+ filePath,
81
+ fileContent,
82
+ frontMatter,
83
+ options,
84
+ compilerName,
85
+ }: {
86
+ filePath: string;
87
+ fileContent: string;
88
+ frontMatter: Record<string, unknown>;
89
+ options: Options;
90
+ compilerName: WebpackCompilerName;
91
+ }): Promise<SimpleProcessorResult> {
92
+ const preprocessedFileContent = preprocessor({
93
+ fileContent,
94
+ filePath,
95
+ admonitions: options.admonitions,
96
+ markdownConfig: options.markdownConfig,
97
+ });
98
+
99
+ const mdxFrontMatter = validateMDXFrontMatter(frontMatter.mdx);
100
+
101
+ const processor = await getProcessor({
102
+ filePath,
103
+ options,
104
+ mdxFrontMatter,
105
+ });
106
+
107
+ try {
108
+ return await processor.process({
109
+ content: preprocessedFileContent,
110
+ filePath,
111
+ frontMatter,
112
+ compilerName,
113
+ });
114
+ } catch (errorUnknown) {
115
+ const error = errorUnknown as Error;
116
+
117
+ // MDX can emit errors that have useful extra attributes
118
+ const errorJSON = JSON.stringify(error, null, 2);
119
+ const errorDetails =
120
+ errorJSON === '{}'
121
+ ? // regular JS error case: print stacktrace
122
+ error.stack ?? 'N/A'
123
+ : // MDX error: print extra attributes + stacktrace
124
+ `${errorJSON}\n${error.stack}`;
125
+
126
+ throw new Error(
127
+ `MDX compilation failed for file ${logger.path(filePath)}\nCause: ${
128
+ error.message
129
+ }\nDetails:\n${errorDetails}`,
130
+ // TODO error cause doesn't seem to be used by Webpack stats.errors :s
131
+ {cause: error},
132
+ );
133
+ }
134
+ }
135
+
136
+ // TODO Docusaurus v4, remove temporary polyfill when upgrading to Node 22+
137
+ export interface PromiseWithResolvers<T> {
138
+ promise: Promise<T>;
139
+ resolve: (value: T | PromiseLike<T>) => void;
140
+ reject: (reason?: any) => void;
141
+ }
142
+ // TODO Docusaurus v4, remove temporary polyfill when upgrading to Node 22+
143
+ export function promiseWithResolvers<T>(): PromiseWithResolvers<T> {
144
+ // @ts-expect-error: it's fine
145
+ const out: PromiseWithResolvers<T> = {};
146
+ out.promise = new Promise((resolve, reject) => {
147
+ out.resolve = resolve;
148
+ out.reject = reject;
149
+ });
150
+ return out;
151
+ }