UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

179 lines (162 loc) 6.47 kB
/** * 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 const wantsNumberedSuffix = type === 'namespace' || modulePath.startsWith('lib') && /^lib\d+$/.test(modulePath); if (wantsNumberedSuffix) { // Use numbered suffixes let attempt = 1; let uniqueName = `${originalName}${attempt}`; while (usedNames.has(uniqueName)) { attempt += 1; uniqueName = `${originalName}${attempt}`; } return uniqueName; } // For real modules, try module-based names first const moduleKey = modulePath.replace(/[@/.-]/g, '') // Remove special characters .toLowerCase().slice(0, 20); // Limit length let uniqueName = `${originalName}${moduleKey}`; // If that's still taken, try numbered suffixes if (usedNames.has(uniqueName)) { let attempt = 1; do { uniqueName = `${originalName}${attempt}`; attempt += 1; } while (usedNames.has(uniqueName)); } return uniqueName; } /** * Helper function to check if a string is a valid JavaScript identifier */ function isValidIdentifier(str) { // JavaScript identifier rules: must start with letter, $, or _, followed by letters, digits, $, or _ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str); } /** * Generates both import statements and resolved externals object * Returns the import statements and the externals as a JavaScript object * * `existingNames` are identifier names already in scope at the injection site * (e.g. existing imports or top-level declarations in the destination file). * They are seeded into the conflict-resolution set so injected imports get * aliased to a unique name when they would collide. */ export function generateResolvedExternals(externals, existingNames) { const moduleImports = {}; const usedNames = new Set(existingNames); const seenImports = new Set(); // First pass: collect all imports and resolve naming conflicts for (const [modulePath, importItems] of Object.entries(externals)) { if (!moduleImports[modulePath]) { moduleImports[modulePath] = { named: [], namespace: [] }; } for (const { name: originalName, type, isType } of importItems) { // Skip type-only imports and empty names if (isType || !originalName.trim()) { continue; } const importKey = `${modulePath}:${originalName}:${type}`; // Skip duplicates if (seenImports.has(importKey)) { continue; } seenImports.add(importKey); const uniqueName = generateUniqueImportName(originalName, modulePath, type, usedNames); 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); } } } // Second pass: generate consolidated import statements and resolved externals const imports = []; const resolvedExternalsObject = {}; for (const [modulePath, moduleImport] of Object.entries(moduleImports)) { const hasDefault = moduleImport.default !== undefined; const hasNamed = moduleImport.named.length > 0; const hasNamespace = moduleImport.namespace.length > 0; // Skip modules that have no valid imports if (!hasDefault && !hasNamed && !hasNamespace) { continue; } const importParts = []; // Add default import if (moduleImport.default) { importParts.push(moduleImport.default); } // Add named imports (consolidated into one statement) if (moduleImport.named.length > 0) { const namedImports = moduleImport.named.map(({ original, unique }) => original === unique ? original : `${original} as ${unique}`).join(', '); importParts.push(`{ ${namedImports} }`); } // Generate import statement if (importParts.length > 0) { imports.push(`import ${importParts.join(', ')} from '${modulePath}';`); } // Add namespace imports (separate statements as they can't be combined) for (const namespaceName of moduleImport.namespace) { imports.push(`import * as ${namespaceName} from '${modulePath}';`); } // Generate resolved externals entry for this module // For invalid JavaScript identifiers, use the quoted version as the key const objectKey = isValidIdentifier(modulePath) ? modulePath : `"${modulePath}"`; let resolvedValue; if (hasDefault && !hasNamed && !hasNamespace) { // Single default export - use direct assignment (e.g., 'react': React) resolvedValue = moduleImport.default; } else if (!hasDefault && hasNamed && !hasNamespace) { // Named exports only - use object syntax (e.g., '@mui/material': { Button, TextField }). // When a name had to be aliased due to a collision, emit `{ original: unique }` // so the resolved value still references the renamed local binding. const namedExports = moduleImport.named.map(({ original, unique }) => original === unique ? original : `${original}: ${unique}`).join(', '); resolvedValue = `{ ${namedExports} }`; } else if (!hasDefault && !hasNamed && hasNamespace) { // Single namespace export - use direct assignment (e.g., 'lodash': lodash) resolvedValue = moduleImport.namespace[0]; } else if (hasDefault) { // Mixed imports - prefer default for the resolved externals resolvedValue = moduleImport.default; } else if (hasNamespace) { // Mixed imports - use namespace if no default resolvedValue = moduleImport.namespace[0]; } else { continue; // Should not happen, but safety check } // Add to the resolved externals object using the object key resolvedExternalsObject[objectKey] = resolvedValue; } return { imports, resolvedExternals: resolvedExternalsObject }; }