UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

469 lines (459 loc) 20 kB
import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper"; import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime"; import _typeof from "@babel/runtime/helpers/esm/typeof"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import * as React from 'react'; import { loadVariant } from "./loadVariant.js"; import { loadFallbackCode } from "./loadFallbackCode.js"; import { stringOrHastToJsx } from "../pipeline/hastUtils/index.js"; import { CodeHighlighterClient } from "./CodeHighlighterClient.js"; import { maybeInitialData } from "./maybeInitialData.js"; import { hasAllVariants } from "./hasAllVariants.js"; import { getFileNameFromUrl } from "../pipeline/loaderUtils/getFileNameFromUrl.js"; import { CodeErrorHandler } from "./CodeErrorHandler.js"; // Common props shared across helper functions import { jsx as _jsx } from "react/jsx-runtime"; var DEFAULT_HIGHLIGHT_AT = 'stream'; var DEBUG = false; // Set to true for debugging purposes function createClientProps(props) { var highlightAt = props.highlightAt === 'stream' ? 'init' : props.highlightAt; var contentProps = _objectSpread({ code: props.code || props.precompute, components: props.components, name: props.name, slug: props.slug, url: props.url, variantType: props.variantType }, props.contentProps || {}); var ErrorHandler = props.ErrorHandler || CodeErrorHandler; return { url: props.url, code: props.code, precompute: props.precompute, components: props.components, variants: props.variants, variant: props.variant, fileName: props.fileName, initialVariant: props.initialVariant, defaultVariant: props.defaultVariant, highlightAt: highlightAt || 'init', skipFallback: props.skipFallback, controlled: props.controlled, name: props.name, slug: props.slug, // Use processedGlobalsCode if available, otherwise fall back to raw globalsCode globalsCode: props.processedGlobalsCode || props.globalsCode, // Note: it is important that we render components before passing them to the client // otherwise we will get an error because functions can't be serialized // On the client, in order to send data to these components, we have to set context fallback: props.fallback, errorHandler: /*#__PURE__*/_jsx(ErrorHandler, {}), children: /*#__PURE__*/_jsx(props.Content, _objectSpread({}, contentProps)) }; } function CodeSourceLoader(_x) { return _CodeSourceLoader.apply(this, arguments); } function _CodeSourceLoader() { _CodeSourceLoader = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(props) { var ErrorHandler, loadedCode, processedGlobalsCode, hasStringUrls, globalsPromises, variantNames, variantCodes, processedCode, errors, _iterator2, _step2, item, clientProps; return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: ErrorHandler = props.ErrorHandler || CodeErrorHandler; // Start with the loaded code from precompute, or load it if needed loadedCode = props.code || props.precompute; if (loadedCode) { _context2.next = 16; break; } if (props.loadCodeMeta) { _context2.next = 5; break; } return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('No code provided and "loadCodeMeta" function is not defined')] })); case 5: if (props.url) { _context2.next = 7; break; } return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('URL is required when loading code with "loadCodeMeta"')] })); case 7: _context2.prev = 7; _context2.next = 10; return props.loadCodeMeta(props.url); case 10: loadedCode = _context2.sent; _context2.next = 16; break; case 13: _context2.prev = 13; _context2.t0 = _context2["catch"](7); return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error("Failed to load code from URL: ".concat(props.url, ". Error: ").concat(JSON.stringify(_context2.t0)))] })); case 16: // TODO: if props.variant is provided, we should only load that variant // Process globalsCode: use already processed version if available, otherwise convert string URLs to Code objects processedGlobalsCode = props.processedGlobalsCode; if (!(!processedGlobalsCode && props.globalsCode && props.globalsCode.length > 0)) { _context2.next = 31; break; } hasStringUrls = props.globalsCode.some(function (item) { return typeof item === 'string'; }); if (!(hasStringUrls && !props.loadCodeMeta)) { _context2.next = 21; break; } return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('loadCodeMeta function is required when globalsCode contains string URLs')] })); case 21: // Load all string URLs in parallel, keep Code objects as-is globalsPromises = props.globalsCode.map(/*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(globalItem) { return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!(typeof globalItem === 'string')) { _context.next = 12; break; } _context.prev = 1; _context.next = 4; return props.loadCodeMeta(globalItem); case 4: return _context.abrupt("return", _context.sent); case 7: _context.prev = 7; _context.t0 = _context["catch"](1); throw new Error("Failed to load globalsCode from URL: ".concat(globalItem, ". Error: ").concat(JSON.stringify(_context.t0))); case 10: _context.next = 13; break; case 12: return _context.abrupt("return", globalItem); case 13: case "end": return _context.stop(); } }, _callee, null, [[1, 7]]); })); return function (_x4) { return _ref.apply(this, arguments); }; }()); _context2.prev = 22; _context2.next = 25; return Promise.all(globalsPromises); case 25: processedGlobalsCode = _context2.sent; _context2.next = 31; break; case 28: _context2.prev = 28; _context2.t1 = _context2["catch"](22); return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [_context2.t1] })); case 31: variantNames = Object.keys(props.components || loadedCode || {}); _context2.next = 34; return Promise.all(variantNames.map(function (variantName) { var variantCode = loadedCode[variantName]; var variantUrl = _typeof(variantCode) === 'object' && variantCode != null && variantCode.url ? variantCode.url : props.url; // Convert processedGlobalsCode to VariantCode | string for this specific variant var resolvedGlobalsCode; if (processedGlobalsCode && processedGlobalsCode.length > 0) { resolvedGlobalsCode = []; var _iterator = _createForOfIteratorHelper(processedGlobalsCode), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var codeObj = _step.value; // Only include if this variant exists in the globalsCode var targetVariant = codeObj[variantName]; if (targetVariant) { resolvedGlobalsCode.push(targetVariant); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } return loadVariant(variantUrl, variantName, variantCode, props.sourceParser, props.loadSource, props.loadVariantMeta, props.sourceTransformers, { globalsCode: resolvedGlobalsCode }).then(function (variant) { return { name: variantName, variant: variant }; })["catch"](function (error) { return { error: error }; }); })); case 34: variantCodes = _context2.sent; processedCode = {}; errors = []; _iterator2 = _createForOfIteratorHelper(variantCodes); try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { item = _step2.value; if ('error' in item) { errors.push(item.error); } else { processedCode[item.name] = item.variant.code; } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } if (!(errors.length > 0)) { _context2.next = 41; break; } return _context2.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: errors })); case 41: clientProps = createClientProps(_objectSpread(_objectSpread({}, props), {}, { code: processedCode, processedGlobalsCode: processedGlobalsCode })); return _context2.abrupt("return", /*#__PURE__*/_jsx(CodeHighlighterClient, _objectSpread({}, clientProps))); case 43: case "end": return _context2.stop(); } }, _callee2, null, [[7, 13], [22, 28]]); })); return _CodeSourceLoader.apply(this, arguments); } function renderCodeHighlighter(props) { var ErrorHandler = props.ErrorHandler || CodeErrorHandler; var code = props.code || props.precompute; var variants = props.variants || Object.keys(props.components || code || {}); var allCodeVariantsLoaded = code && hasAllVariants(variants, code, true); if (!allCodeVariantsLoaded) { if (props.forceClient) { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('Client only mode requires precomputed source code')] }); } return /*#__PURE__*/_jsx(CodeSourceLoader, _objectSpread({}, props)); } var clientProps = createClientProps(_objectSpread({}, props)); return /*#__PURE__*/_jsx(CodeHighlighterClient, _objectSpread({}, clientProps)); // TODO: we might not need the client if hydrateAt is 'init' or 'stream' and there is no props.controlled } /** * Ensures that the suspense boundary is always rendered, even if none of the children have async operations. */ function CodeHighlighterSuspense(_x2) { return _CodeHighlighterSuspense.apply(this, arguments); } function _CodeHighlighterSuspense() { _CodeHighlighterSuspense = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3(props) { return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: return _context3.abrupt("return", props.children); case 1: case "end": return _context3.stop(); } }, _callee3); })); return _CodeHighlighterSuspense.apply(this, arguments); } function renderWithInitialSource(props) { var fileNames = [].concat(_toConsumableArray(props.initialFilename ? [props.initialFilename] : []), _toConsumableArray(Object.keys(props.initialExtraFiles || {}))); var source = stringOrHastToJsx(props.initialSource, props.highlightAt === 'init'); var contentProps = _objectSpread({ name: props.name, slug: props.slug, url: props.url, fileNames: fileNames, source: source }, props.contentProps || {}); var ContentLoading = props.ContentLoading; var fallback = /*#__PURE__*/_jsx(ContentLoading, _objectSpread({}, contentProps)); if (props.forceClient) { return renderCodeHighlighter(_objectSpread(_objectSpread({}, props), {}, { fallback: fallback, processedGlobalsCode: props.processedGlobalsCode })); } return /*#__PURE__*/_jsx(React.Suspense, { fallback: fallback, children: /*#__PURE__*/_jsx(CodeHighlighterSuspense, { children: renderCodeHighlighter(_objectSpread(_objectSpread({}, props), {}, { fallback: fallback, skipFallback: true, processedGlobalsCode: props.processedGlobalsCode })) }) }); } function CodeInitialSourceLoader(_x3) { return _CodeInitialSourceLoader.apply(this, arguments); } function _CodeInitialSourceLoader() { _CodeInitialSourceLoader = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4(props) { var ErrorHandler, loaded, code, initialFilename, initialSource, initialExtraFiles, processedGlobalsCode; return _regeneratorRuntime().wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: ErrorHandler = props.ErrorHandler || CodeErrorHandler; if (props.url) { _context4.next = 3; break; } return _context4.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('URL is required for loading initial source')] })); case 3: _context4.next = 5; return loadFallbackCode(props.url, props.initialVariant, props.code, props.highlightAt === 'init', props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants, props.sourceParser, props.loadSource, props.loadVariantMeta, props.loadCodeMeta, props.fileName, props.variants, props.globalsCode)["catch"](function (error) { return { error: error }; }); case 5: loaded = _context4.sent; if (!('error' in loaded)) { _context4.next = 8; break; } return _context4.abrupt("return", /*#__PURE__*/_jsx(ErrorHandler, { errors: [loaded.error] })); case 8: code = loaded.code, initialFilename = loaded.initialFilename, initialSource = loaded.initialSource, initialExtraFiles = loaded.initialExtraFiles, processedGlobalsCode = loaded.processedGlobalsCode; return _context4.abrupt("return", renderWithInitialSource(_objectSpread(_objectSpread({}, props), {}, { code: code, initialFilename: initialFilename, initialSource: initialSource, initialExtraFiles: initialExtraFiles, ContentLoading: props.ContentLoading, processedGlobalsCode: processedGlobalsCode }))); case 10: case "end": return _context4.stop(); } }, _callee4); })); return _CodeInitialSourceLoader.apply(this, arguments); } export function CodeHighlighter(props) { var _code, _props$components; var ErrorHandler = props.ErrorHandler || CodeErrorHandler; // Validate mutually exclusive props if (props.children && (props.code || props.precompute)) { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('Cannot provide both "children" and "code" or "precompute" props')] }); } // Handle children as string -> Default variant var code = props.code; if (props.children && typeof props.children === 'string') { var fileName = props.fileName || (props.url ? getFileNameFromUrl(props.url).fileName : undefined); code = { Default: { fileName: fileName, source: props.children, url: props.url } }; } var variants = props.variants || Object.keys(props.components || code || props.precompute || {}); if (variants.length === 0) { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('No code or components provided')] }); } // Validate fileName is provided when extraFiles are present if (code) { for (var _i = 0, _Object$entries = Object.entries(code); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), variantName = _Object$entries$_i[0], variantCode = _Object$entries$_i[1]; if (_typeof(variantCode) === 'object' && variantCode != null && variantCode.extraFiles && Object.keys(variantCode.extraFiles).length > 0 && !variantCode.fileName && !variantCode.url) { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error("fileName or url is required for variant \"".concat(variantName, "\" when extraFiles are provided"))] }); } } } var ContentLoading = props.ContentLoading; if (!ContentLoading) { if (props.highlightAt === 'stream') { // if the user explicitly sets highlightAt to 'stream', we need a ContentLoading component return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('ContentLoading component is required for stream highlighting')] }); } return renderCodeHighlighter(_objectSpread(_objectSpread({}, props), {}, { code: code })); } var initialKey = props.initialVariant || props.variant || props.defaultVariant || variants[0]; var initial = (_code = code) == null ? void 0 : _code[initialKey]; if (!initial && !((_props$components = props.components) != null && _props$components[initialKey])) { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error("No code or component for variant \"".concat(initialKey, "\""))] }); } // TODO: use initial.filesOrder to determing which source to use var highlightAt = props.highlightAt || DEFAULT_HIGHLIGHT_AT; var _maybeInitialData = maybeInitialData(variants, initialKey, code || props.precompute, undefined, // TODO: use initial.filesOrder if provided? highlightAt === 'init', props.fallbackUsesExtraFiles, props.fallbackUsesAllVariants), initialData = _maybeInitialData.initialData, reason = _maybeInitialData.reason; if (!initialData) { if (DEBUG) { // eslint-disable-next-line no-console console.log('Initial data not found:', reason); } if (props.forceClient) { if (highlightAt === 'init') { return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('Client only mode with highlightAt: init requires precomputed and parsed source code')] }); } // TODO: send directly to client component? return /*#__PURE__*/_jsx(ErrorHandler, { errors: [new Error('Client only mode requires precomputed source code')] }); } return /*#__PURE__*/_jsx(CodeInitialSourceLoader, _objectSpread(_objectSpread({}, props), {}, { ContentLoading: ContentLoading, initialVariant: initialKey })); } return renderWithInitialSource(_objectSpread(_objectSpread({}, props), {}, { code: initialData.code, ContentLoading: ContentLoading, initialVariant: initialKey, initialFilename: initialData.initialFilename, initialSource: initialData.initialSource, initialExtraFiles: initialData.initialExtraFiles })); }