@docusaurus/mdx-loader 3.5.0 → 3.5.2-canary-6121

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
package/src/loader.ts CHANGED
@@ -5,22 +5,22 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import fs from 'fs-extra';
9
8
  import logger from '@docusaurus/logger';
10
9
  import {
10
+ aliasedSitePath,
11
11
  DEFAULT_PARSE_FRONT_MATTER,
12
- escapePath,
13
12
  getFileLoaderUtils,
14
13
  getWebpackLoaderCompilerName,
15
14
  } from '@docusaurus/utils';
16
15
  import stringifyObject from 'stringify-object';
17
- import preprocessor from './preprocessor';
18
- import {validateMDXFrontMatter} from './frontMatter';
19
- import {createProcessorCached} from './processor';
20
- import type {ResolveMarkdownLink} from './remark/resolveMarkdownLinks';
21
- import type {MDXOptions} from './processor';
22
-
23
- import type {MarkdownConfig} from '@docusaurus/types';
16
+ import {
17
+ compileToJSX,
18
+ createAssetsExportCode,
19
+ extractContentTitleData,
20
+ promiseWithResolvers,
21
+ } from './utils';
22
+ import type {WebpackCompilerName} from '@docusaurus/utils';
23
+ import type {Options} from './options';
24
24
  import type {LoaderContext} from 'webpack';
25
25
 
26
26
  // TODO as of April 2023, no way to import/re-export this ESM type easily :/
@@ -28,235 +28,91 @@ import type {LoaderContext} from 'webpack';
28
28
  // See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
29
29
  type Pluggable = any; // TODO fix this asap
30
30
 
31
- const {
32
- loaders: {inlineMarkdownAssetImageFileLoader},
33
- } = getFileLoaderUtils();
34
-
35
31
  export type MDXPlugin = Pluggable;
36
32
 
37
- export type Options = Partial<MDXOptions> & {
38
- markdownConfig: MarkdownConfig;
39
- staticDirs: string[];
40
- siteDir: string;
41
- isMDXPartial?: (filePath: string) => boolean;
42
- isMDXPartialFrontMatterWarningDisabled?: boolean;
43
- removeContentTitle?: boolean;
44
- metadataPath?: string | ((filePath: string) => string);
45
- createAssets?: (metadata: {
46
- frontMatter: {[key: string]: unknown};
47
- metadata: {[key: string]: unknown};
48
- }) => {[key: string]: unknown};
49
- resolveMarkdownLink?: ResolveMarkdownLink;
50
- };
51
-
52
- /**
53
- * When this throws, it generally means that there's no metadata file associated
54
- * with this MDX document. It can happen when using MDX partials (usually
55
- * starting with _). That's why it's important to provide the `isMDXPartial`
56
- * function in config
57
- */
58
- async function readMetadataPath(metadataPath: string) {
59
- try {
60
- return await fs.readFile(metadataPath, 'utf8');
61
- } catch (err) {
62
- logger.error`MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`;
63
- throw err;
64
- }
65
- }
66
-
67
- /**
68
- * Converts assets an object with Webpack require calls code.
69
- * This is useful for mdx files to reference co-located assets using relative
70
- * paths. Those assets should enter the Webpack assets pipeline and be hashed.
71
- * For now, we only handle that for images and paths starting with `./`:
72
- *
73
- * `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
74
- */
75
- function createAssetsExportCode(assets: unknown) {
76
- if (
77
- typeof assets !== 'object' ||
78
- !assets ||
79
- Object.keys(assets).length === 0
80
- ) {
81
- return 'undefined';
82
- }
83
-
84
- // TODO implementation can be completed/enhanced
85
- function createAssetValueCode(assetValue: unknown): string | undefined {
86
- if (Array.isArray(assetValue)) {
87
- const arrayItemCodes = assetValue.map(
88
- (item: unknown) => createAssetValueCode(item) ?? 'undefined',
89
- );
90
- return `[${arrayItemCodes.join(', ')}]`;
91
- }
92
- // Only process string values starting with ./
93
- // We could enhance this logic and check if file exists on disc?
94
- if (typeof assetValue === 'string' && assetValue.startsWith('./')) {
95
- // TODO do we have other use-cases than image assets?
96
- // Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708)
97
- return `require("${inlineMarkdownAssetImageFileLoader}${escapePath(
98
- assetValue,
99
- )}").default`;
100
- }
101
- return undefined;
102
- }
103
-
104
- const assetEntries = Object.entries(assets);
105
-
106
- const codeLines = assetEntries
107
- .map(([key, value]: [string, unknown]) => {
108
- const assetRequireCode = createAssetValueCode(value);
109
- return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined;
110
- })
111
- .filter(Boolean);
112
-
113
- return `{\n${codeLines.join('\n')}\n}`;
114
- }
115
-
116
- // TODO temporary, remove this after v3.1?
117
- // Some plugin authors use our mdx-loader, despite it not being public API
118
- // see https://github.com/facebook/docusaurus/issues/8298
119
- function ensureMarkdownConfig(reqOptions: Options) {
120
- if (!reqOptions.markdownConfig) {
121
- throw new Error(
122
- 'Docusaurus v3+ requires MDX loader options.markdownConfig - plugin authors using the MDX loader should make sure to provide that option',
123
- );
124
- }
125
- }
126
-
127
- /**
128
- * data.contentTitle is set by the remark contentTitle plugin
129
- */
130
- function extractContentTitleData(data: {
131
- [key: string]: unknown;
132
- }): string | undefined {
133
- return data.contentTitle as string | undefined;
134
- }
135
-
136
- export async function mdxLoader(
137
- this: LoaderContext<Options>,
138
- fileContent: string,
139
- ): Promise<void> {
140
- const compilerName = getWebpackLoaderCompilerName(this);
141
- const callback = this.async();
142
- const filePath = this.resourcePath;
143
- const reqOptions: Options = this.getOptions();
144
- const {query} = this;
145
-
146
- ensureMarkdownConfig(reqOptions);
147
-
148
- const {frontMatter} = await reqOptions.markdownConfig.parseFrontMatter({
33
+ async function loadMDX({
34
+ fileContent,
35
+ filePath,
36
+ options,
37
+ compilerName,
38
+ }: {
39
+ fileContent: string;
40
+ filePath: string;
41
+ options: Options;
42
+ compilerName: WebpackCompilerName;
43
+ }): Promise<string> {
44
+ const {frontMatter} = await options.markdownConfig.parseFrontMatter({
149
45
  filePath,
150
46
  fileContent,
151
47
  defaultParseFrontMatter: DEFAULT_PARSE_FRONT_MATTER,
152
48
  });
153
- const mdxFrontMatter = validateMDXFrontMatter(frontMatter.mdx);
154
-
155
- const preprocessedContent = preprocessor({
156
- fileContent,
157
- filePath,
158
- admonitions: reqOptions.admonitions,
159
- markdownConfig: reqOptions.markdownConfig,
160
- });
161
49
 
162
50
  const hasFrontMatter = Object.keys(frontMatter).length > 0;
163
51
 
164
- const processor = await createProcessorCached({
52
+ const result = await compileToJSX({
53
+ fileContent,
165
54
  filePath,
166
- reqOptions,
167
- query,
168
- mdxFrontMatter,
55
+ frontMatter,
56
+ options,
57
+ compilerName,
169
58
  });
170
59
 
171
- let result: {content: string; data: {[key: string]: unknown}};
172
- try {
173
- result = await processor.process({
174
- content: preprocessedContent,
175
- filePath,
176
- frontMatter,
177
- compilerName,
178
- });
179
- } catch (errorUnknown) {
180
- const error = errorUnknown as Error;
181
-
182
- // MDX can emit errors that have useful extra attributes
183
- const errorJSON = JSON.stringify(error, null, 2);
184
- const errorDetails =
185
- errorJSON === '{}'
186
- ? // regular JS error case: print stacktrace
187
- error.stack ?? 'N/A'
188
- : // MDX error: print extra attributes + stacktrace
189
- `${errorJSON}\n${error.stack}`;
190
-
191
- return callback(
192
- new Error(
193
- `MDX compilation failed for file ${logger.path(filePath)}\nCause: ${
194
- error.message
195
- }\nDetails:\n${errorDetails}`,
196
- // TODO error cause doesn't seem to be used by Webpack stats.errors :s
197
- {cause: error},
198
- ),
199
- );
200
- }
201
-
202
60
  const contentTitle = extractContentTitleData(result.data);
203
61
 
204
62
  // MDX partials are MDX files starting with _ or in a folder starting with _
205
63
  // Partial are not expected to have associated metadata files or front matter
206
- const isMDXPartial = reqOptions.isMDXPartial?.(filePath);
64
+ const isMDXPartial = options.isMDXPartial?.(filePath);
207
65
  if (isMDXPartial && hasFrontMatter) {
208
66
  const errorMessage = `Docusaurus MDX partial files should not contain front matter.
209
67
  Those partial files use the _ prefix as a convention by default, but this is configurable.
210
68
  File at ${filePath} contains front matter that will be ignored:
211
69
  ${JSON.stringify(frontMatter, null, 2)}`;
212
70
 
213
- if (!reqOptions.isMDXPartialFrontMatterWarningDisabled) {
71
+ if (!options.isMDXPartialFrontMatterWarningDisabled) {
214
72
  const shouldError = process.env.NODE_ENV === 'test' || process.env.CI;
215
73
  if (shouldError) {
216
- return callback(new Error(errorMessage));
74
+ throw new Error(errorMessage);
217
75
  }
218
76
  logger.warn(errorMessage);
219
77
  }
220
78
  }
221
79
 
222
- function getMetadataPath(): string | undefined {
80
+ const metadataPath = (function getMetadataPath() {
223
81
  if (!isMDXPartial) {
224
- // Read metadata for this MDX and export it.
225
- if (
226
- reqOptions.metadataPath &&
227
- typeof reqOptions.metadataPath === 'function'
228
- ) {
229
- return reqOptions.metadataPath(filePath);
230
- }
82
+ return options.metadataPath?.(filePath);
231
83
  }
232
84
  return undefined;
233
- }
234
-
235
- const metadataPath = getMetadataPath();
236
- if (metadataPath) {
237
- this.addDependency(metadataPath);
238
- }
239
-
240
- const metadataJsonString = metadataPath
241
- ? await readMetadataPath(metadataPath)
242
- : undefined;
243
-
244
- const metadata = metadataJsonString
245
- ? (JSON.parse(metadataJsonString) as {[key: string]: unknown})
246
- : undefined;
85
+ })();
247
86
 
248
87
  const assets =
249
- reqOptions.createAssets && metadata
250
- ? reqOptions.createAssets({frontMatter, metadata})
88
+ options.createAssets && !isMDXPartial
89
+ ? options.createAssets({filePath, frontMatter})
251
90
  : undefined;
252
91
 
92
+ const fileLoaderUtils = getFileLoaderUtils(compilerName === 'server');
93
+
253
94
  // TODO use remark plugins to insert extra exports instead of string concat?
254
95
  // cf how the toc is exported
255
96
  const exportsCode = `
256
97
  export const frontMatter = ${stringifyObject(frontMatter)};
257
98
  export const contentTitle = ${stringifyObject(contentTitle)};
258
- ${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''}
259
- ${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''}
99
+ ${
100
+ metadataPath
101
+ ? `export {default as metadata} from '${aliasedSitePath(
102
+ metadataPath,
103
+ options.siteDir,
104
+ )}'`
105
+ : ''
106
+ }
107
+ ${
108
+ assets
109
+ ? `export const assets = ${createAssetsExportCode({
110
+ assets,
111
+ inlineMarkdownAssetImageFileLoader:
112
+ fileLoaderUtils.loaders.inlineMarkdownAssetImageFileLoader,
113
+ })};`
114
+ : ''
115
+ }
260
116
  `;
261
117
 
262
118
  const code = `
@@ -264,5 +120,114 @@ ${exportsCode}
264
120
  ${result.content}
265
121
  `;
266
122
 
267
- return callback(null, code);
123
+ return code;
124
+ }
125
+
126
+ // Note: we cache promises instead of strings
127
+ // This is because client/server compilations might be triggered in parallel
128
+ // When this happens for the same file, we don't want to compile it twice
129
+ async function loadMDXWithCaching({
130
+ resource,
131
+ fileContent,
132
+ filePath,
133
+ options,
134
+ compilerName,
135
+ }: {
136
+ resource: string; // path?query#hash
137
+ filePath: string; // path
138
+ fileContent: string;
139
+ options: Options;
140
+ compilerName: WebpackCompilerName;
141
+ }): Promise<string> {
142
+ const {crossCompilerCache} = options;
143
+ if (!crossCompilerCache) {
144
+ return loadMDX({
145
+ fileContent,
146
+ filePath,
147
+ options,
148
+ compilerName,
149
+ });
150
+ }
151
+
152
+ // Note we "resource" as cache key, not "filePath" nor "fileContent"
153
+ // This is because:
154
+ // - the same file can be compiled in different variants (blog.mdx?truncated)
155
+ // - the same content can be processed differently (versioned docs links)
156
+ const cacheKey = resource;
157
+
158
+ // We can clean up the cache and free memory after cache entry consumption
159
+ // We know there are only 2 compilations for the same file
160
+ // Note: once we introduce RSCs we'll probably have 3 compilations
161
+ // Note: we can't use string keys in WeakMap
162
+ // But we could eventually use WeakRef for the values
163
+ const deleteCacheEntry = () => crossCompilerCache.delete(cacheKey);
164
+
165
+ const cacheEntry = crossCompilerCache?.get(cacheKey);
166
+
167
+ // When deduplicating client/server compilations, we always use the client
168
+ // compilation and not the server compilation
169
+ // This is important because the server compilation usually skips some steps
170
+ // Notably: the server compilation does not emit file-loader assets
171
+ // Using the server compilation otherwise leads to broken images
172
+ // See https://github.com/facebook/docusaurus/issues/10544#issuecomment-2390943794
173
+ // See https://github.com/facebook/docusaurus/pull/10553
174
+ // TODO a problem with this: server bundle will use client inline loaders
175
+ // This means server bundle will use ?emit=true for assets
176
+ // We should try to get rid of inline loaders to cleanup this caching logic
177
+ if (compilerName === 'client') {
178
+ const promise = loadMDX({
179
+ fileContent,
180
+ filePath,
181
+ options,
182
+ compilerName,
183
+ });
184
+ if (cacheEntry) {
185
+ promise.then(cacheEntry.resolve, cacheEntry.reject);
186
+ deleteCacheEntry();
187
+ } else {
188
+ const noop = () => {
189
+ throw new Error('this should never be called');
190
+ };
191
+ crossCompilerCache.set(cacheKey, {
192
+ promise,
193
+ resolve: noop,
194
+ reject: noop,
195
+ });
196
+ }
197
+ return promise;
198
+ }
199
+ // Server compilation always uses the result of the client compilation above
200
+ else if (compilerName === 'server') {
201
+ if (cacheEntry) {
202
+ deleteCacheEntry();
203
+ return cacheEntry.promise;
204
+ } else {
205
+ const {promise, resolve, reject} = promiseWithResolvers<string>();
206
+ crossCompilerCache.set(cacheKey, {promise, resolve, reject});
207
+ return promise;
208
+ }
209
+ } else {
210
+ throw new Error(`Unexpected compilerName=${compilerName}`);
211
+ }
212
+ }
213
+
214
+ export async function mdxLoader(
215
+ this: LoaderContext<Options>,
216
+ fileContent: string,
217
+ ): Promise<void> {
218
+ const compilerName = getWebpackLoaderCompilerName(this);
219
+ const callback = this.async();
220
+ const options: Options = this.getOptions();
221
+ try {
222
+ const result = await loadMDXWithCaching({
223
+ resource: this.resource,
224
+ filePath: this.resourcePath,
225
+ fileContent,
226
+ options,
227
+ compilerName,
228
+ });
229
+ return callback(null, result);
230
+ } catch (error) {
231
+ return callback(error as Error);
232
+ }
268
233
  }
package/src/options.ts ADDED
@@ -0,0 +1,32 @@
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 type {MDXOptions, SimpleProcessors} from './processor';
9
+ import type {MarkdownConfig} from '@docusaurus/types';
10
+ import type {ResolveMarkdownLink} from './remark/resolveMarkdownLinks';
11
+ import type {PromiseWithResolvers} from './utils';
12
+
13
+ export type Options = Partial<MDXOptions> & {
14
+ markdownConfig: MarkdownConfig;
15
+ staticDirs: string[];
16
+ siteDir: string;
17
+ isMDXPartial?: (filePath: string) => boolean;
18
+ isMDXPartialFrontMatterWarningDisabled?: boolean;
19
+ removeContentTitle?: boolean;
20
+ metadataPath?: (filePath: string) => string;
21
+ createAssets?: (metadata: {
22
+ filePath: string;
23
+ frontMatter: {[key: string]: unknown};
24
+ }) => {[key: string]: unknown};
25
+ resolveMarkdownLink?: ResolveMarkdownLink;
26
+
27
+ // Will usually be created by "createMDXLoaderItem"
28
+ processors?: SimpleProcessors;
29
+ crossCompilerCache?: Map<string, CrossCompilerCacheEntry>; // MDX => Promise<JSX> cache
30
+ };
31
+
32
+ type CrossCompilerCacheEntry = PromiseWithResolvers<string>;
@@ -11,7 +11,7 @@ import {
11
11
  admonitionTitleToDirectiveLabel,
12
12
  } from '@docusaurus/utils';
13
13
  import {normalizeAdmonitionOptions} from './remark/admonitions';
14
- import type {Options} from './loader';
14
+ import type {Options} from './options';
15
15
 
16
16
  /**
17
17
  * Preprocess the string before passing it to MDX
package/src/processor.ts CHANGED
@@ -20,7 +20,7 @@ import codeCompatPlugin from './remark/mdx1Compat/codeCompatPlugin';
20
20
  import {getFormat} from './format';
21
21
  import type {WebpackCompilerName} from '@docusaurus/utils';
22
22
  import type {MDXFrontMatter} from './frontMatter';
23
- import type {Options} from './loader';
23
+ import type {Options} from './options';
24
24
  import type {AdmonitionOptions} from './remark/admonitions';
25
25
 
26
26
  // @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
@@ -31,10 +31,13 @@ import type {ProcessorOptions} from '@mdx-js/mdx';
31
31
  // See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
32
32
  type Pluggable = any; // TODO fix this asap
33
33
 
34
- type SimpleProcessorResult = {content: string; data: {[key: string]: unknown}};
34
+ export type SimpleProcessorResult = {
35
+ content: string;
36
+ data: {[key: string]: unknown};
37
+ };
35
38
 
36
39
  // TODO alt interface because impossible to import type Processor (ESM + TS :/)
37
- type SimpleProcessor = {
40
+ export type SimpleProcessor = {
38
41
  process: ({
39
42
  content,
40
43
  filePath,
@@ -219,63 +222,64 @@ export async function createProcessorUncached(parameters: {
219
222
  }
220
223
 
221
224
  // We use different compilers depending on the file type (md vs mdx)
222
- type ProcessorsCacheEntry = {
225
+ export type SimpleProcessors = {
223
226
  mdProcessor: SimpleProcessor;
224
227
  mdxProcessor: SimpleProcessor;
225
228
  };
226
229
 
227
230
  // Compilers are cached so that Remark/Rehype plugins can run
228
231
  // expensive code during initialization
229
- const ProcessorsCache = new Map<string | Options, ProcessorsCacheEntry>();
232
+ const ProcessorsCache = new Map<string | Options, SimpleProcessors>();
230
233
 
231
- async function createProcessorsCacheEntry({
232
- query,
233
- reqOptions,
234
+ export async function createProcessors({
235
+ options,
234
236
  }: {
235
- query: string | Options;
236
- reqOptions: Options;
237
- }): Promise<ProcessorsCacheEntry> {
237
+ options: Options;
238
+ }): Promise<SimpleProcessors> {
238
239
  const {createProcessorSync} = await createProcessorFactory();
239
-
240
- const compilers = ProcessorsCache.get(query);
241
- if (compilers) {
242
- return compilers;
243
- }
244
-
245
- const compilerCacheEntry: ProcessorsCacheEntry = {
240
+ return {
246
241
  mdProcessor: createProcessorSync({
247
- options: reqOptions,
242
+ options,
248
243
  format: 'md',
249
244
  }),
250
245
  mdxProcessor: createProcessorSync({
251
- options: reqOptions,
246
+ options,
252
247
  format: 'mdx',
253
248
  }),
254
249
  };
250
+ }
255
251
 
256
- ProcessorsCache.set(query, compilerCacheEntry);
257
-
258
- return compilerCacheEntry;
252
+ async function createProcessorsCacheEntry({
253
+ options,
254
+ }: {
255
+ options: Options;
256
+ }): Promise<SimpleProcessors> {
257
+ const compilers = ProcessorsCache.get(options);
258
+ if (compilers) {
259
+ return compilers;
260
+ }
261
+ const processors = await createProcessors({options});
262
+ ProcessorsCache.set(options, processors);
263
+ return processors;
259
264
  }
260
265
 
261
- export async function createProcessorCached({
266
+ export async function getProcessor({
262
267
  filePath,
263
268
  mdxFrontMatter,
264
- query,
265
- reqOptions,
269
+ options,
266
270
  }: {
267
271
  filePath: string;
268
272
  mdxFrontMatter: MDXFrontMatter;
269
- query: string | Options;
270
- reqOptions: Options;
273
+ options: Options;
271
274
  }): Promise<SimpleProcessor> {
272
- const compilers = await createProcessorsCacheEntry({query, reqOptions});
275
+ const processors =
276
+ options.processors ?? (await createProcessorsCacheEntry({options}));
273
277
 
274
278
  const format = getFormat({
275
279
  filePath,
276
280
  frontMatterFormat: mdxFrontMatter.format,
277
- markdownConfigFormat: reqOptions.markdownConfig.format,
281
+ markdownConfigFormat: options.markdownConfig.format,
278
282
  });
279
283
 
280
- return format === 'md' ? compilers.mdProcessor : compilers.mdxProcessor;
284
+ return format === 'md' ? processors.mdProcessor : processors.mdxProcessor;
281
285
  }
@@ -13,8 +13,8 @@ import {
13
13
  toMessageRelativeFilePath,
14
14
  posixPath,
15
15
  escapePath,
16
- getFileLoaderUtils,
17
16
  findAsyncSequential,
17
+ getFileLoaderUtils,
18
18
  } from '@docusaurus/utils';
19
19
  import escapeHtml from 'escape-html';
20
20
  import sizeOf from 'image-size';
@@ -27,10 +27,6 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
27
27
  import type {Image} from 'mdast';
28
28
  import type {Parent} from 'unist';
29
29
 
30
- const {
31
- loaders: {inlineMarkdownImageFileLoader},
32
- } = getFileLoaderUtils();
33
-
34
30
  type PluginOptions = {
35
31
  staticDirs: string[];
36
32
  siteDir: string;
@@ -38,6 +34,7 @@ type PluginOptions = {
38
34
 
39
35
  type Context = PluginOptions & {
40
36
  filePath: string;
37
+ inlineMarkdownImageFileLoader: string;
41
38
  };
42
39
 
43
40
  type Target = [node: Image, index: number, parent: Parent];
@@ -45,21 +42,21 @@ type Target = [node: Image, index: number, parent: Parent];
45
42
  async function toImageRequireNode(
46
43
  [node]: Target,
47
44
  imagePath: 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;
52
49
  const attributes: MdxJsxTextElement['attributes'] = [];
53
50
 
54
51
  let relativeImagePath = posixPath(
55
- path.relative(path.dirname(filePath), imagePath),
52
+ path.relative(path.dirname(context.filePath), imagePath),
56
53
  );
57
54
  relativeImagePath = `./${relativeImagePath}`;
58
55
 
59
56
  const parsedUrl = url.parse(node.url);
60
57
  const hash = parsedUrl.hash ?? '';
61
58
  const search = parsedUrl.search ?? '';
62
- const requireString = `${inlineMarkdownImageFileLoader}${
59
+ const requireString = `${context.inlineMarkdownImageFileLoader}${
63
60
  escapePath(relativeImagePath) + search
64
61
  }`;
65
62
  if (node.alt) {
@@ -186,21 +183,26 @@ async function processImageNode(target: Target, context: Context) {
186
183
  // We try to convert image urls without protocol to images with require calls
187
184
  // going through webpack ensures that image assets exist at build time
188
185
  const imagePath = await getImageAbsolutePath(parsedUrl.pathname, context);
189
- await toImageRequireNode(target, imagePath, context.filePath);
186
+ await toImageRequireNode(target, imagePath, context);
190
187
  }
191
188
 
192
189
  export default function plugin(options: PluginOptions): Transformer {
193
190
  return async (root, vfile) => {
194
191
  const {visit} = await import('unist-util-visit');
195
192
 
193
+ const fileLoaderUtils = getFileLoaderUtils(
194
+ vfile.data.compilerName === 'server',
195
+ );
196
+ const context: Context = {
197
+ ...options,
198
+ filePath: vfile.path!,
199
+ inlineMarkdownImageFileLoader:
200
+ fileLoaderUtils.loaders.inlineMarkdownImageFileLoader,
201
+ };
202
+
196
203
  const promises: Promise<void>[] = [];
197
204
  visit(root, 'image', (node: Image, index, parent) => {
198
- promises.push(
199
- processImageNode([node, index, parent!], {
200
- ...options,
201
- filePath: vfile.path!,
202
- }),
203
- );
205
+ promises.push(processImageNode([node, index, parent!], context));
204
206
  });
205
207
  await Promise.all(promises);
206
208
  };