@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
303 lines (286 loc) • 12.8 kB
JavaScript
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);
};
}();
};