"use strict"; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.mdxLoader = void 0; const tslib_1 = require("tslib"); const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger")); const utils_1 = require("@docusaurus/utils"); const stringify_object_1 = tslib_1.__importDefault(require("stringify-object")); const preprocessor_1 = tslib_1.__importDefault(require("./preprocessor")); const frontMatter_1 = require("./frontMatter"); const processor_1 = require("./processor"); const { loaders: { inlineMarkdownAssetImageFileLoader }, } = (0, utils_1.getFileLoaderUtils)(); /** * When this throws, it generally means that there's no metadata file associated * with this MDX document. It can happen when using MDX partials (usually * starting with _). That's why it's important to provide the `isMDXPartial` * function in config */ async function readMetadataPath(metadataPath) { try { return await fs_extra_1.default.readFile(metadataPath, 'utf8'); } catch (err) { logger_1.default.error `MDX loader can't read MDX metadata file path=${metadataPath}. Maybe the isMDXPartial option function was not provided?`; throw err; } } /** * Converts assets an object with Webpack require calls code. * This is useful for mdx files to reference co-located assets using relative * paths. Those assets should enter the Webpack assets pipeline and be hashed. * For now, we only handle that for images and paths starting with `./`: * * `{image: "./myImage.png"}` => `{image: require("./myImage.png")}` */ function createAssetsExportCode(assets) { if (typeof assets !== 'object' || !assets || Object.keys(assets).length === 0) { return 'undefined'; } // TODO implementation can be completed/enhanced function createAssetValueCode(assetValue) { if (Array.isArray(assetValue)) { const arrayItemCodes = assetValue.map((item) => createAssetValueCode(item) ?? 'undefined'); return `[${arrayItemCodes.join(', ')}]`; } // Only process string values starting with ./ // We could enhance this logic and check if file exists on disc? if (typeof assetValue === 'string' && assetValue.startsWith('./')) { // TODO do we have other use-cases than image assets? // Probably not worth adding more support, as we want to move to Webpack 5 new asset system (https://github.com/facebook/docusaurus/pull/4708) return `require("${inlineMarkdownAssetImageFileLoader}${(0, utils_1.escapePath)(assetValue)}").default`; } return undefined; } const assetEntries = Object.entries(assets); const codeLines = assetEntries .map(([key, value]) => { const assetRequireCode = createAssetValueCode(value); return assetRequireCode ? `"${key}": ${assetRequireCode},` : undefined; }) .filter(Boolean); return `{\n${codeLines.join('\n')}\n}`; } // TODO temporary, remove this after v3.1? // Some plugin authors use our mdx-loader, despite it not being public API // see https://github.com/facebook/docusaurus/issues/8298 function ensureMarkdownConfig(reqOptions) { if (!reqOptions.markdownConfig) { throw new Error('Docusaurus v3+ requires MDX loader options.markdownConfig - plugin authors using the MDX loader should make sure to provide that option'); } } /** * data.contentTitle is set by the remark contentTitle plugin */ function extractContentTitleData(data) { return data.contentTitle; } async function mdxLoader(fileContent) { const compilerName = (0, utils_1.getWebpackLoaderCompilerName)(this); const callback = this.async(); const filePath = this.resourcePath; const reqOptions = this.getOptions(); const { query } = this; ensureMarkdownConfig(reqOptions); const { frontMatter } = await reqOptions.markdownConfig.parseFrontMatter({ filePath, fileContent, defaultParseFrontMatter: utils_1.DEFAULT_PARSE_FRONT_MATTER, }); const mdxFrontMatter = (0, frontMatter_1.validateMDXFrontMatter)(frontMatter.mdx); const preprocessedContent = (0, preprocessor_1.default)({ fileContent, filePath, admonitions: reqOptions.admonitions, markdownConfig: reqOptions.markdownConfig, }); const hasFrontMatter = Object.keys(frontMatter).length > 0; const processor = await (0, processor_1.createProcessorCached)({ filePath, reqOptions, query, mdxFrontMatter, }); let result; try { result = await processor.process({ content: preprocessedContent, filePath, frontMatter, compilerName, }); } catch (errorUnknown) { const error = errorUnknown; // MDX can emit errors that have useful extra attributes const errorJSON = JSON.stringify(error, null, 2); const errorDetails = errorJSON === '{}' ? // regular JS error case: print stacktrace error.stack ?? 'N/A' : // MDX error: print extra attributes + stacktrace `${errorJSON}\n${error.stack}`; return callback(new Error(`MDX compilation failed for file ${logger_1.default.path(filePath)}\nCause: ${error.message}\nDetails:\n${errorDetails}`, // TODO error cause doesn't seem to be used by Webpack stats.errors :s { cause: error })); } const contentTitle = extractContentTitleData(result.data); // MDX partials are MDX files starting with _ or in a folder starting with _ // Partial are not expected to have associated metadata files or front matter const isMDXPartial = reqOptions.isMDXPartial?.(filePath); if (isMDXPartial && hasFrontMatter) { const errorMessage = `Docusaurus MDX partial files should not contain front matter. Those partial files use the _ prefix as a convention by default, but this is configurable. File at ${filePath} contains front matter that will be ignored: ${JSON.stringify(frontMatter, null, 2)}`; if (!reqOptions.isMDXPartialFrontMatterWarningDisabled) { const shouldError = process.env.NODE_ENV === 'test' || process.env.CI; if (shouldError) { return callback(new Error(errorMessage)); } logger_1.default.warn(errorMessage); } } function getMetadataPath() { if (!isMDXPartial) { // Read metadata for this MDX and export it. if (reqOptions.metadataPath && typeof reqOptions.metadataPath === 'function') { return reqOptions.metadataPath(filePath); } } return undefined; } const metadataPath = getMetadataPath(); if (metadataPath) { this.addDependency(metadataPath); } const metadataJsonString = metadataPath ? await readMetadataPath(metadataPath) : undefined; const metadata = metadataJsonString ? JSON.parse(metadataJsonString) : undefined; const assets = reqOptions.createAssets && metadata ? reqOptions.createAssets({ frontMatter, metadata }) : undefined; // TODO use remark plugins to insert extra exports instead of string concat? // cf how the toc is exported const exportsCode = ` export const frontMatter = ${(0, stringify_object_1.default)(frontMatter)}; export const contentTitle = ${(0, stringify_object_1.default)(contentTitle)}; ${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''} ${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''} `; const code = ` ${exportsCode} ${result.content} `; return callback(null, code); } exports.mdxLoader = mdxLoader; //# sourceMappingURL=loader.js.map