@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
253 lines (226 loc) • 9.92 kB
JavaScript
import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
/**
* Generates a unique import name based on module path and original name
*/
function generateUniqueImportName(originalName, modulePath, type, usedNames) {
// If no conflict, use original name
if (!usedNames.has(originalName)) {
return originalName;
}
// For conflicts, strategy depends on type and context:
// - Namespace imports: always use numbered suffixes
// - Named imports from simple test cases (lib1, lib2, etc.): use numbered suffixes
// - Other cases: try module-based names first
var wantsNumberedSuffix = type === 'namespace' || modulePath.startsWith('lib') && /^lib\d+$/.test(modulePath);
if (wantsNumberedSuffix) {
// Use numbered suffixes
var attempt = 1;
var _uniqueName = "".concat(originalName).concat(attempt);
while (usedNames.has(_uniqueName)) {
attempt += 1;
_uniqueName = "".concat(originalName).concat(attempt);
}
return _uniqueName;
}
// For real modules, try module-based names first
var moduleKey = modulePath.replace(/[@/.-]/g, '') // Remove special characters
.toLowerCase().slice(0, 20); // Limit length
var uniqueName = "".concat(originalName).concat(moduleKey);
// If that's still taken, try numbered suffixes
if (usedNames.has(uniqueName)) {
var _attempt = 1;
do {
uniqueName = "".concat(originalName).concat(_attempt);
_attempt += 1;
} while (usedNames.has(uniqueName));
}
return uniqueName;
}
/**
* Resolves import conflicts and generates import statements with proper deduplication
*/
function resolveImportConflicts(externals) {
var moduleImports = {};
var usedNames = new Set();
var nameMapping = {};
var seenImports = new Set();
// First pass: collect all imports and resolve naming conflicts
for (var _i = 0, _Object$entries = Object.entries(externals); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
modulePath = _Object$entries$_i[0],
importItems = _Object$entries$_i[1];
if (!moduleImports[modulePath]) {
moduleImports[modulePath] = {
named: [],
namespace: []
};
}
var _iterator = _createForOfIteratorHelper(importItems),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _step.value,
originalName = _step$value.name,
type = _step$value.type,
isType = _step$value.isType;
// Skip type-only imports and empty names
if (isType || !originalName.trim()) {
continue;
}
var importKey = "".concat(modulePath, ":").concat(originalName, ":").concat(type);
// Skip duplicates (this handles the duplicate React namespace import issue)
if (seenImports.has(importKey)) {
continue;
}
seenImports.add(importKey);
var uniqueName = generateUniqueImportName(originalName, modulePath, type, usedNames);
nameMapping[importKey] = uniqueName;
usedNames.add(uniqueName);
if (type === 'default') {
moduleImports[modulePath]["default"] = uniqueName;
} else if (type === 'named') {
moduleImports[modulePath].named.push({
original: originalName,
unique: uniqueName
});
} else if (type === 'namespace') {
moduleImports[modulePath].namespace.push(uniqueName);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
// Second pass: generate consolidated import statements and externals mappings
var imports = [];
var exportMappings = [];
var _loop = function _loop() {
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
modulePath = _Object$entries2$_i[0],
moduleImport = _Object$entries2$_i[1];
var importParts = [];
// Add default import
if (moduleImport["default"]) {
importParts.push(moduleImport["default"]);
}
// Add named imports (consolidated into one statement)
if (moduleImport.named.length > 0) {
var namedImports = moduleImport.named.map(function (_ref) {
var original = _ref.original,
unique = _ref.unique;
return original === unique ? original : "".concat(original, " as ").concat(unique);
}).join(', ');
importParts.push("{ ".concat(namedImports, " }"));
}
// Generate import statement
if (importParts.length > 0) {
imports.push("import ".concat(importParts.join(', '), " from '").concat(modulePath, "';"));
}
// Add namespace imports (separate statements as they can't be combined)
var _iterator2 = _createForOfIteratorHelper(moduleImport.namespace),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var namespaceName = _step2.value;
imports.push("import * as ".concat(namespaceName, " from '").concat(modulePath, "';"));
}
// Generate single consolidated export mapping for this module
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var exportParts = [];
// Add default export
if (moduleImport["default"]) {
exportParts.push("default: ".concat(moduleImport["default"]));
}
// Add named exports
moduleImport.named.forEach(function (_ref2) {
var original = _ref2.original,
unique = _ref2.unique;
if (original === unique) {
exportParts.push(original);
} else {
exportParts.push("".concat(original, ": ").concat(unique));
}
});
// Add namespace exports
moduleImport.namespace.forEach(function (namespaceName) {
exportParts.push(namespaceName);
});
// Only add export mapping if there are exports
if (exportParts.length > 0) {
// Check if we have only a single namespace export (no default, no named)
var isOnlyNamespaceExport = exportParts.length === 1 && moduleImport.namespace.length === 1 && moduleImport["default"] === undefined && moduleImport.named.length === 0;
if (isOnlyNamespaceExport) {
// Single namespace export - use direct assignment (e.g., 'react': React)
exportMappings.push("'".concat(modulePath, "': ").concat(exportParts[0]));
} else {
// All other cases - use object syntax to ensure consistency
exportMappings.push("'".concat(modulePath, "': { ").concat(exportParts.join(', '), " }"));
}
}
};
for (var _i2 = 0, _Object$entries2 = Object.entries(moduleImports); _i2 < _Object$entries2.length; _i2++) {
_loop();
}
return {
imports: imports,
exportMappings: exportMappings,
nameMapping: nameMapping
};
}
/**
* Generates the content for the CodeExternalsProvider file
*/
export function generateExternalsProviderContent(externals) {
var _resolveImportConflic = resolveImportConflicts(externals),
imports = _resolveImportConflic.imports,
exportMappings = _resolveImportConflic.exportMappings;
var importStatements = imports.length > 0 ? "".concat(imports.join('\n'), "\n") : '';
// Handle empty externals case
var externalsContent = exportMappings.length > 0 ? "\n".concat(exportMappings.map(function (mapping) {
return " ".concat(mapping);
}).join(',\n'), "\n ") : ' '; // Two spaces for empty object formatting (to match test expectation: "{ }")
// Only import React namespace when there are actually imports AND React is not already imported
var hasReactImport = imports.some(function (imp) {
return imp.includes("from 'react'");
});
var reactImport = imports.length > 0 && !hasReactImport ? "import * as React from 'react';\n" : '';
// Only import CodeExternalsContext if it's not already specifically imported as a named import
var hasCodeExternalsContextImport = imports.some(function (imp) {
return imp.includes("from '@mui/internal-docs-infra/CodeExternalsContext'") && imp.includes('{ CodeExternalsContext');
});
var codeExternalsContextImport = !hasCodeExternalsContextImport ? "import { CodeExternalsContext } from '@mui/internal-docs-infra/CodeExternalsContext';\n" : '';
return "'use client';\n\n".concat(reactImport).concat(importStatements).concat(codeExternalsContextImport, "\nconst externals = {").concat(externalsContent, "};\n\nexport function CodeExternalsProvider({ children }: { children: React.ReactNode }) {\n return (\n <CodeExternalsContext.Provider value={{ externals }}>\n {children}\n </CodeExternalsContext.Provider>\n );\n}\n");
}
/**
* Creates externals provider file information.
* Returns the filename, content, and relative path for import.
* If no externals exist, returns undefined.
*/
export function createExternalsProvider(externals, resourcePath) {
var _resourcePath$split$p;
// If no externals exist, don't create a provider
if (Object.keys(externals).length === 0) {
return undefined;
}
// Generate a unique filename for the externals provider
var resourceName = resourcePath.replace(/\.[^/.]+$/, ''); // Remove extension
var externalsFileName = "".concat(resourceName, ".externals.tsx");
// Extract just the filename from the full path for the relative import
var basename = ((_resourcePath$split$p = resourcePath.split('/').pop()) == null ? void 0 : _resourcePath$split$p.replace(/\.[^/.]+$/, '')) || 'demo';
var relativeFileName = "".concat(basename, ".externals.tsx");
// Generate the externals provider content using combined externals
var externalsProviderContent = generateExternalsProviderContent(externals);
// Return the file information for the caller to handle emission
return {
fileName: externalsFileName,
content: externalsProviderContent,
relativePath: "./".concat(relativeFileName)
};
}