UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

303 lines (286 loc) 12.8 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 _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; import { visit } from 'unist-util-visit'; import { loadVariant } from "../../CodeHighlighter/loadVariant.js"; import { createParseSource } from "../parseSource/index.js"; import { TypescriptToJavascriptTransformer } from "../transformTypescriptToJavascript/index.js"; /** * Maps common language class names to file extensions * Only includes languages that have corresponding grammars in parseSource/grammars.ts */ var LANGUAGE_TO_EXTENSION = { // JavaScript javascript: 'js', js: 'js', // TypeScript typescript: 'ts', ts: 'ts', // TSX/JSX tsx: 'tsx', jsx: 'jsx', // Maps to .jsx but uses tsx grammar // JSON json: 'json', // Markdown markdown: 'md', md: 'md', // MDX mdx: 'mdx', // HTML html: 'html', // CSS css: 'css', // Shell shell: 'sh', bash: 'sh', sh: 'sh', // YAML yaml: 'yaml', yml: 'yaml' }; /** * Extracts the language from className attribute */ function extractLanguageFromClassName(className) { if (!className) { return null; } // Handle array of class names (HAST format) var classString = Array.isArray(className) ? className.join(' ') : className; var match = classString.match(/(?:^|\s)language-(\w+)(?:\s|$)/); return match ? match[1] : null; } /** * Gets the filename from data-filename attribute or derives it from language * Returns undefined if no explicit filename and no recognizable language */ function getFileName(codeElement) { var _codeElement$properti, _codeElement$properti2; // Check for explicit data-filename attribute var dataFilename = (_codeElement$properti = codeElement.properties) == null ? void 0 : _codeElement$properti.dataFilename; if (dataFilename && typeof dataFilename === 'string') { return dataFilename; } // Extract language from className var className = (_codeElement$properti2 = codeElement.properties) == null ? void 0 : _codeElement$properti2.className; var language = extractLanguageFromClassName(className); if (language && LANGUAGE_TO_EXTENSION[language]) { return "index.".concat(LANGUAGE_TO_EXTENSION[language]); } // Return undefined instead of a fallback - let the system handle gracefully return undefined; } /** * Extracts text content from HAST nodes */ function extractTextContent(node) { if (node.type === 'text') { return node.value; } if (node.type === 'element' && node.children) { return node.children.map(function (child) { return extractTextContent(child); }).join(''); } return ''; } /** * Creates variants from multiple code elements within a pre element */ function createVariantsFromCodeElements(codeElements) { var variants = {}; if (codeElements.length === 1) { var _codeElement$properti3; // Single code element - use "Default" as variant name var codeElement = codeElements[0]; var sourceCode = extractTextContent(codeElement); var fileName = getFileName(codeElement); var variant = { source: sourceCode, skipTransforms: !((_codeElement$properti3 = codeElement.properties) != null && _codeElement$properti3.dataTransform) }; // Only add fileName if we have one if (fileName) { variant.fileName = fileName; } variants.Default = variant; } else { // Multiple code elements - create appropriate variant names var languages = codeElements.map(function (element) { var _element$properties; var className = (_element$properties = element.properties) == null ? void 0 : _element$properties.className; return extractLanguageFromClassName(className); }); // Check if all languages are the same (or all null) var uniqueLanguages = Array.from(new Set(languages.filter(Boolean))); var shouldUseLanguageNames = uniqueLanguages.length > 1; codeElements.forEach(function (codeElement, index) { var _codeElement$properti4, _codeElement$properti5; var sourceCode = extractTextContent(codeElement); var fileName = getFileName(codeElement); // Check for explicit variant name var dataVariant = (_codeElement$properti4 = codeElement.properties) == null ? void 0 : _codeElement$properti4.dataVariant; var variantName; if (dataVariant && typeof dataVariant === 'string') { variantName = dataVariant; } else if (shouldUseLanguageNames && languages[index]) { // Use language name if languages differ variantName = languages[index].charAt(0).toUpperCase() + languages[index].slice(1); } else { // Use numbered variants if languages are the same or unknown variantName = "Variant ".concat(index + 1); } var variant = { source: sourceCode, skipTransforms: !((_codeElement$properti5 = codeElement.properties) != null && _codeElement$properti5.dataTransform) }; // Only add fileName if we have one if (fileName) { variant.fileName = fileName; } variants[variantName] = variant; }); } return variants; } /** * Rehype plugin that transforms pre > code elements to use loadVariant * * This plugin: * 1. Finds pre elements in the HTML AST * 2. Collects all code children within each pre element * 3. Creates variants from multiple code elements or single Default variant * 4. Uses loadVariant to process each variant * 5. Stores the combined precompute data on the pre element * 6. Clears all code element contents */ export var transformHtmlCode = function transformHtmlCode() { return /*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3(tree) { var transformPromises, sourceParser, sourceTransformers; return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: transformPromises = []; // Get the source parser and transformers sourceParser = createParseSource(); sourceTransformers = [TypescriptToJavascriptTransformer]; visit(tree, 'element', function (node) { // Look for pre elements if (node.tagName === 'pre' && node.children && node.children.length > 0) { // Find all code elements within this pre var codeElements = node.children.filter(function (child) { return child.type === 'element' && child.tagName === 'code'; }); if (codeElements.length > 0) { var transformPromise = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2() { var variants, processedCode, variantPromises, variantResults, _iterator, _step, result; return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; // Create variants from all code elements variants = createVariantsFromCodeElements(codeElements); // Process each variant with loadVariant processedCode = {}; variantPromises = Object.entries(variants).map(/*#__PURE__*/function () { var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref3) { var _ref5, variantName, variantData, result; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _ref5 = _slicedToArray(_ref3, 2), variantName = _ref5[0], variantData = _ref5[1]; if (!(variantData && _typeof(variantData) === 'object')) { _context.next = 6; break; } _context.next = 4; return loadVariant(undefined, // url - not needed for inline code variantName, variantData, sourceParser, undefined, // loadSource - not needed since we have the data undefined, // loadVariantMeta - not needed since we have the data sourceTransformers, { disableTransforms: variantData.skipTransforms || false }); case 4: result = _context.sent; return _context.abrupt("return", { variantName: variantName, processedVariant: result.code }); case 6: return _context.abrupt("return", null); case 7: case "end": return _context.stop(); } }, _callee); })); return function (_x2) { return _ref4.apply(this, arguments); }; }()); _context2.next = 6; return Promise.all(variantPromises); case 6: variantResults = _context2.sent; _iterator = _createForOfIteratorHelper(variantResults); try { for (_iterator.s(); !(_step = _iterator.n()).done;) { result = _step.value; if (result) { processedCode[result.variantName] = result.processedVariant; } } // Clear all code element contents and replace with error message } catch (err) { _iterator.e(err); } finally { _iterator.f(); } codeElements.forEach(function (codeElement) { codeElement.children = []; }); // Replace pre element children with error message for CodeHighlighter node.children = [{ type: 'text', value: 'Error: expected pre tag to be handled by CodeHighlighter' }]; // Set the precompute data on the pre element directly on properties for immediate HTML serialization if (!node.properties) { node.properties = {}; } node.properties.dataPrecompute = JSON.stringify(processedCode); _context2.next = 18; break; case 15: _context2.prev = 15; _context2.t0 = _context2["catch"](0); console.warn('Failed to transform code block:', _context2.t0); case 18: case "end": return _context2.stop(); } }, _callee2, null, [[0, 15]]); }))(); transformPromises.push(transformPromise); } } }); // Wait for all transformations to complete _context3.next = 6; return Promise.all(transformPromises); case 6: case "end": return _context3.stop(); } }, _callee3); })); return function (_x) { return _ref.apply(this, arguments); }; }(); };